use crate::{
node::{CellSizes, FdtNode, NodeProperty},
parsing::{BigEndianU32, BigEndianU64, CStr, FdtData},
Fdt,
};
#[derive(Debug, Clone, Copy)]
pub struct Chosen<'b, 'a: 'b> {
pub(crate) node: FdtNode<'b, 'a>,
}
impl<'b, 'a: 'b> Chosen<'b, 'a> {
pub fn bootargs(self) -> Option<&'a str> {
self.node
.properties()
.find(|n| n.name == "bootargs")
.and_then(|n| core::str::from_utf8(&n.value[..n.value.len() - 1]).ok())
}
pub fn stdout(self) -> Option<StdInOutPath<'b, 'a>> {
self.node
.properties()
.find(|n| n.name == "stdout-path")
.and_then(|n| core::str::from_utf8(&n.value[..n.value.len() - 1]).ok())
.map(Self::split_stdinout_property)
.and_then(|(name, params)| {
self.node.header.find_node(name).map(|node| StdInOutPath::new(node, params))
})
}
pub fn stdin(self) -> Option<StdInOutPath<'b, 'a>> {
self.node
.properties()
.find(|n| n.name == "stdin-path")
.and_then(|n| core::str::from_utf8(&n.value[..n.value.len() - 1]).ok())
.map(Self::split_stdinout_property)
.and_then(|(name, params)| {
self.node.header.find_node(name).map(|node| StdInOutPath::new(node, params))
})
.or_else(|| self.stdout())
}
fn split_stdinout_property(property: &str) -> (&str, Option<&str>) {
property
.split_once(':')
.map_or_else(|| (property, None), |(name, params)| (name, Some(params)))
}
}
pub struct StdInOutPath<'b, 'a> {
pub(crate) node: FdtNode<'b, 'a>,
pub(crate) params: Option<&'a str>,
}
impl<'b, 'a> StdInOutPath<'b, 'a> {
fn new(node: FdtNode<'b, 'a>, params: Option<&'a str>) -> Self {
Self { node, params }
}
pub fn node(&self) -> FdtNode<'b, 'a> {
self.node
}
pub fn params(&self) -> Option<&'a str> {
self.params
}
}
#[derive(Debug, Clone, Copy)]
pub struct Root<'b, 'a: 'b> {
pub(crate) node: FdtNode<'b, 'a>,
}
impl<'b, 'a: 'b> Root<'b, 'a> {
pub fn cell_sizes(self) -> CellSizes {
self.node.cell_sizes()
}
pub fn model(self) -> &'a str {
self.node
.properties()
.find(|p| p.name == "model")
.and_then(|p| core::str::from_utf8(p.value).map(|s| s.trim_end_matches('\0')).ok())
.unwrap()
}
pub fn compatible(self) -> Compatible<'a> {
self.node.compatible().unwrap()
}
pub fn properties(self) -> impl Iterator<Item = NodeProperty<'a>> + 'b {
self.node.properties()
}
pub fn property(self, name: &str) -> Option<NodeProperty<'a>> {
self.node.properties().find(|p| p.name == name)
}
}
#[derive(Debug, Clone, Copy)]
pub struct Aliases<'b, 'a: 'b> {
pub(crate) header: &'b Fdt<'a>,
pub(crate) node: FdtNode<'b, 'a>,
}
impl<'b, 'a: 'b> Aliases<'b, 'a> {
pub fn resolve(self, alias: &str) -> Option<&'a str> {
self.node
.properties()
.find(|p| p.name == alias)
.and_then(|p| core::str::from_utf8(p.value).map(|s| s.trim_end_matches('\0')).ok())
}
pub fn resolve_node(self, alias: &str) -> Option<FdtNode<'b, 'a>> {
self.resolve(alias).and_then(|name| self.header.find_node(name))
}
pub fn all(self) -> impl Iterator<Item = (&'a str, &'a str)> + 'b {
self.node.properties().filter_map(|p| {
Some((p.name, core::str::from_utf8(p.value).map(|s| s.trim_end_matches('\0')).ok()?))
})
}
}
#[derive(Debug, Clone, Copy)]
pub struct Cpu<'b, 'a: 'b> {
pub(crate) parent: FdtNode<'b, 'a>,
pub(crate) node: FdtNode<'b, 'a>,
}
impl<'b, 'a: 'b> Cpu<'b, 'a> {
pub fn ids(self) -> CpuIds<'a> {
let address_cells = self.node.parent_cell_sizes().address_cells;
CpuIds {
reg: self
.node
.properties()
.find(|p| p.name == "reg")
.expect("reg is a required property of cpu nodes"),
address_cells,
}
}
pub fn clock_frequency(self) -> usize {
self.node
.properties()
.find(|p| p.name == "clock-frequency")
.or_else(|| self.parent.property("clock-frequency"))
.map(|p| match p.value.len() {
4 => BigEndianU32::from_bytes(p.value).unwrap().get() as usize,
8 => BigEndianU64::from_bytes(p.value).unwrap().get() as usize,
_ => unreachable!(),
})
.expect("clock-frequency is a required property of cpu nodes")
}
pub fn timebase_frequency(self) -> usize {
self.node
.properties()
.find(|p| p.name == "timebase-frequency")
.or_else(|| self.parent.property("timebase-frequency"))
.map(|p| match p.value.len() {
4 => BigEndianU32::from_bytes(p.value).unwrap().get() as usize,
8 => BigEndianU64::from_bytes(p.value).unwrap().get() as usize,
_ => unreachable!(),
})
.expect("timebase-frequency is a required property of cpu nodes")
}
pub fn properties(self) -> impl Iterator<Item = NodeProperty<'a>> + 'b {
self.node.properties()
}
pub fn property(self, name: &str) -> Option<NodeProperty<'a>> {
self.node.properties().find(|p| p.name == name)
}
}
#[derive(Debug, Clone, Copy)]
pub struct CpuIds<'a> {
pub(crate) reg: NodeProperty<'a>,
pub(crate) address_cells: usize,
}
impl<'a> CpuIds<'a> {
pub fn first(self) -> usize {
match self.address_cells {
1 => BigEndianU32::from_bytes(self.reg.value).unwrap().get() as usize,
2 => BigEndianU64::from_bytes(self.reg.value).unwrap().get() as usize,
n => panic!("address-cells of size {} is currently not supported", n),
}
}
pub fn all(self) -> impl Iterator<Item = usize> + 'a {
let mut vals = FdtData::new(self.reg.value);
core::iter::from_fn(move || match vals.remaining() {
[] => None,
_ => Some(match self.address_cells {
1 => vals.u32()?.get() as usize,
2 => vals.u64()?.get() as usize,
n => panic!("address-cells of size {} is currently not supported", n),
}),
})
}
}
#[derive(Clone, Copy)]
pub struct Compatible<'a> {
pub(crate) data: &'a [u8],
}
impl<'a> Compatible<'a> {
pub fn first(self) -> &'a str {
CStr::new(self.data).expect("expected C str").as_str().unwrap()
}
pub fn all(self) -> impl Iterator<Item = &'a str> {
let mut data = self.data;
core::iter::from_fn(move || {
if data.is_empty() {
return None;
}
match data.iter().position(|b| *b == b'\0') {
Some(idx) => {
let ret = Some(core::str::from_utf8(&data[..idx]).ok()?);
data = &data[idx + 1..];
ret
}
None => {
let ret = Some(core::str::from_utf8(data).ok()?);
data = &[];
ret
}
}
})
}
}
#[derive(Debug, Clone, Copy)]
pub struct Memory<'b, 'a: 'b> {
pub(crate) node: FdtNode<'b, 'a>,
}
impl<'a> Memory<'_, 'a> {
pub fn regions(&self) -> impl Iterator<Item = MemoryRegion> + 'a {
self.node.reg().unwrap()
}
pub fn initial_mapped_area(&self) -> Option<MappedArea> {
let mut mapped_area = None;
if let Some(init_mapped_area) = self.node.property("initial_mapped_area") {
let mut stream = FdtData::new(init_mapped_area.value);
let effective_address = stream.u64().expect("effective address");
let physical_address = stream.u64().expect("physical address");
let size = stream.u32().expect("size");
mapped_area = Some(MappedArea {
effective_address: effective_address.get() as usize,
physical_address: physical_address.get() as usize,
size: size.get() as usize,
});
}
mapped_area
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[repr(C)]
pub struct MappedArea {
pub effective_address: usize,
pub physical_address: usize,
pub size: usize,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct MemoryRegion {
pub starting_address: *const u8,
pub size: Option<usize>,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct MemoryRange {
pub child_bus_address: usize,
pub child_bus_address_hi: u32,
pub parent_bus_address: usize,
pub size: usize,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct InterruptMapping {
pub child_unit_address: usize,
pub child_unit_address_hi: u32,
pub child_interrupt_specifier: usize,
pub parent_phandle: u32,
pub parent_unit_address: usize,
pub parent_unit_address_hi: u32,
pub parent_interrupt_specifier: usize,
}
pub struct InterruptMapMask {
pub address_mask: usize,
pub address_mask_hi: u32,
pub interrupt_mask: usize,
}