//! A small reader for the **device tree** (a "DTB", flattened device tree).
//!
//! Arm boards have no PCI-style "ask the hardware what's there", so the
//! firmware (here QEMU) hands the kernel a description: a tree of nodes
//! like `pl011@9000000` with properties like `compatible = "arm,pl011"`
//! and `reg =
`. The format is simple (devicetree.org spec,
//! chapter 5): a header, then a list of tokens (begin node, property, end
//! node), then a table of property names. Every number in it is big-endian.
//!
//! This reader only does what the bring-up needs: walk every node and hand
//! over its name, `compatible`, `reg`, `interrupts` and `device_type`. It
//! assumes `#address-cells = 2` and `#size-cells = 2` (64-bit addresses
//! and sizes), which is what QEMU's virt uses for every node we read.
const MAGIC: u32 = 0xd00d_feed;
const BEGIN_NODE: u32 = 1;
const END_NODE: u32 = 2;
const PROP: u32 = 3;
const NOP: u32 = 4;
#[allow(dead_code)]
const END: u32 = 9; // (the walk stops at anything it doesn't know, END included)
const MAX_DEPTH: usize = 16;
pub struct Fdt {
data: &'static [u8],
structs: usize,
strings: usize,
}
/// One node, as `for_each_node` hands it over.
#[derive(Clone, Copy, Default)]
pub struct Node<'a> {
pub name: &'a [u8],
pub compatible: &'a [u8],
pub reg: &'a [u8],
pub interrupts: &'a [u8],
pub device_type: &'a [u8],
pub depth: usize,
}
fn be32(d: &[u8], at: usize) -> Option {
let b = d.get(at..at + 4)?;
Some(u32::from_be_bytes([b[0], b[1], b[2], b[3]]))
}
fn cstr(d: &[u8], at: usize) -> &[u8] {
let rest = d.get(at..).unwrap_or(&[]);
let n = rest.iter().position(|&b| b == 0).unwrap_or(rest.len());
&rest[..n]
}
impl Fdt {
/// The device tree at physical address `addr`, if there is one there.
///
/// # Safety
/// `addr` must be readable memory (with the MMU on: mapped).
pub unsafe fn at(addr: usize) -> Option {
let head = unsafe { core::slice::from_raw_parts(addr as *const u8, 40) };
if be32(head, 0)? != MAGIC {
return None;
}
let size = be32(head, 4)? as usize;
if !(40..=(2 << 20)).contains(&size) {
return None;
}
let data = unsafe { core::slice::from_raw_parts(addr as *const u8, size) };
Some(Fdt { data, structs: be32(data, 8)? as usize, strings: be32(data, 12)? as usize })
}
pub fn size(&self) -> usize {
self.data.len()
}
/// Call `f` for every node (children before their parents finish).
pub fn for_each_node(&self, mut f: impl FnMut(&Node)) {
let d = self.data;
let mut stack = [Node::default(); MAX_DEPTH];
let mut depth = 0usize;
let mut at = self.structs;
while let Some(token) = be32(d, at) {
at += 4;
match token {
BEGIN_NODE => {
let name = cstr(d, at);
at = (at + name.len() + 1 + 3) & !3;
if depth < MAX_DEPTH {
stack[depth] = Node { name, depth, ..Node::default() };
}
depth += 1;
}
END_NODE => {
if depth == 0 {
return;
}
depth -= 1;
if depth < MAX_DEPTH {
f(&stack[depth]);
}
}
PROP => {
let (Some(len), Some(nameoff)) = (be32(d, at), be32(d, at + 4)) else { return };
let value = d.get(at + 8..at + 8 + len as usize).unwrap_or(&[]);
at = (at + 8 + len as usize + 3) & !3;
if depth == 0 || depth > MAX_DEPTH {
continue;
}
let node = &mut stack[depth - 1];
match cstr(d, self.strings + nameoff as usize) {
b"compatible" => node.compatible = value,
b"reg" => node.reg = value,
b"interrupts" => node.interrupts = value,
b"device_type" => node.device_type = value,
_ => {}
}
}
NOP => {}
_ => return, // END (9), or something we don't understand
}
}
}
}
impl Node<'_> {
/// Is one of the strings in `compatible` exactly `what`?
pub fn is_compatible(&self, what: &str) -> bool {
self.compatible.split(|&b| b == 0).any(|s| s == what.as_bytes())
}
/// The `i`th (address, size) pair in `reg` (2 + 2 cells).
pub fn reg(&self, i: usize) -> Option<(u64, u64)> {
let at = i * 16;
let cell = |k: usize| be32(self.reg, at + 4 * k).map(|v| v as u64);
Some(((cell(0)? << 32) | cell(1)?, (cell(2)? << 32) | cell(3)?))
}
/// The `i`th 32-bit cell of `interrupts`.
pub fn interrupt_cell(&self, i: usize) -> Option {
be32(self.interrupts, 4 * i)
}
pub fn name(&self) -> &str {
core::str::from_utf8(self.name).unwrap_or("?")
}
}