use core::fmt;
use crate::{
Chosen, FdtError, Memory, MemoryReservation, Node, Property, VecRange, data, data::Bytes,
fmt_utils, header::Header, iter::FdtIter,
};
pub struct MemoryReservationIter<'a> {
data: &'a [u8],
offset: usize,
}
impl<'a> Iterator for MemoryReservationIter<'a> {
type Item = MemoryReservation;
fn next(&mut self) -> Option<Self::Item> {
if self.offset + data::MEM_RSV_ENTRY_SIZE > self.data.len() {
return None;
}
let address_bytes = &self.data[self.offset..self.offset + 8];
let address = u64::from_be_bytes(address_bytes.try_into().unwrap());
self.offset += 8;
let size_bytes = &self.data[self.offset..self.offset + 8];
let size = u64::from_be_bytes(size_bytes.try_into().unwrap());
self.offset += 8;
if address == 0 && size == 0 {
return None;
}
Some(MemoryReservation { address, size })
}
}
#[derive(Clone)]
pub struct Fdt<'a> {
header: Header,
pub(crate) data: Bytes<'a>,
}
impl<'a> Fdt<'a> {
pub fn from_bytes(data: &'a [u8]) -> Result<Fdt<'a>, FdtError> {
let header = Header::from_bytes(data)?;
if data.len() < header.totalsize as usize {
return Err(FdtError::BufferTooSmall {
pos: header.totalsize as usize,
});
}
let buffer = Bytes::new(data);
Ok(Fdt {
header,
data: buffer,
})
}
pub unsafe fn from_ptr(ptr: *mut u8) -> Result<Fdt<'a>, FdtError> {
let header = unsafe { Header::from_ptr(ptr)? };
let data_slice = unsafe { core::slice::from_raw_parts(ptr, header.totalsize as _) };
let data = Bytes::new(data_slice);
Ok(Fdt { header, data })
}
pub fn header(&self) -> &Header {
&self.header
}
pub fn as_slice(&self) -> &'a [u8] {
self.data.as_slice()
}
pub fn all_nodes(&self) -> FdtIter<'a> {
FdtIter::new(self.clone())
}
pub fn find_by_path(&self, path: &str) -> Option<Node<'a>> {
let path = self.normalize_path(path)?;
let split = path.trim_matches('/').split('/');
let mut current_iter = self.all_nodes();
let mut found_node: Option<Node<'a>> = None;
for part in split {
let mut found = false;
for node in current_iter.by_ref() {
let node_name = node.name();
if node_name == part {
found = true;
found_node = Some(node);
break;
}
}
if !found {
return None;
}
}
found_node
}
pub fn find_children_by_path(&self, path: &str) -> ChildrenIter<'a> {
let Some(path) = self.normalize_path(path) else {
return ChildrenIter {
node_iter: self.all_nodes(),
child_level: 0,
done: true,
};
};
let split = path.trim_matches('/').split('/');
let mut iter = self.all_nodes();
let mut target_level = 0usize;
for part in split {
if part.is_empty() {
iter.next();
break;
}
let mut found = false;
for node in iter.by_ref() {
if node.name() == part {
found = true;
target_level = node.level();
break;
}
}
if !found {
return ChildrenIter {
node_iter: self.all_nodes(),
child_level: 0,
done: true,
};
}
}
let child_level = target_level + 1;
ChildrenIter {
node_iter: iter,
child_level,
done: false,
}
}
fn resolve_alias(&self, alias: &str) -> Option<&'a str> {
let aliases_node = self.find_by_path("/aliases")?;
aliases_node.find_property_str(alias)
}
fn normalize_path(&self, path: &'a str) -> Option<&'a str> {
if path.starts_with('/') {
Some(path)
} else {
self.resolve_alias(path)
}
}
pub fn translate_address(&self, path: &'a str, address: u64) -> u64 {
let mut addresses = [address];
self.translate_addresses(path, &mut addresses);
addresses[0]
}
pub fn translate_addresses(&self, path: &'a str, addresses: &mut [u64]) {
let path = match self.normalize_path(path) {
Some(p) => p,
None => return,
};
let path_parts = Self::split_path(path);
if path_parts.is_empty() {
return;
}
self.translate_addresses_with_parts(&path_parts, addresses);
}
fn split_path(path: &str) -> heapless::Vec<&str, 16> {
path.trim_matches('/')
.split('/')
.filter(|s| !s.is_empty())
.collect()
}
fn translate_addresses_with_parts(&self, path_parts: &[&str], addresses: &mut [u64]) {
for depth in (0..path_parts.len()).rev() {
let parent_parts = &path_parts[..depth];
if parent_parts.is_empty() {
break;
}
if let Some(parent_node) = self.find_node_by_parts(parent_parts) {
let ranges = match parent_node.ranges() {
Some(r) => r,
None => break, };
for addr in addresses.iter_mut() {
*addr = Self::apply_ranges_one(&ranges, *addr);
}
}
}
}
fn find_node_by_parts(&self, parts: &[&str]) -> Option<Node<'a>> {
let mut path = heapless::String::<256>::new();
path.push('/').ok();
for (i, part) in parts.iter().enumerate() {
if i > 0 {
path.push('/').ok();
}
path.push_str(part).ok();
}
self.find_by_path(path.as_str())
}
fn apply_ranges_one(ranges: &VecRange<'_>, address: u64) -> u64 {
for range in ranges.iter() {
if address >= range.child_address && address < range.child_address + range.length {
let offset = address - range.child_address;
return range.parent_address + offset;
}
}
address
}
pub fn memory_reservations(&self) -> MemoryReservationIter<'a> {
MemoryReservationIter {
data: self.data.as_slice(),
offset: self.header.off_mem_rsvmap as usize,
}
}
pub fn chosen(&self) -> Option<Chosen<'a>> {
for node in self.all_nodes() {
if let Node::Chosen(c) = node {
return Some(c);
}
}
None
}
pub fn memory(&self) -> impl Iterator<Item = Memory<'a>> + 'a {
self.all_nodes().filter_map(|node| {
if let Node::Memory(mem) = node {
Some(mem)
} else {
None
}
})
}
pub fn reserved_memory(&self) -> impl Iterator<Item = Node<'a>> + 'a {
ReservedMemoryIter {
node_iter: self.all_nodes(),
in_reserved_memory: false,
reserved_level: 0,
}
}
}
struct ReservedMemoryIter<'a> {
node_iter: FdtIter<'a>,
in_reserved_memory: bool,
reserved_level: usize,
}
impl<'a> Iterator for ReservedMemoryIter<'a> {
type Item = Node<'a>;
fn next(&mut self) -> Option<Self::Item> {
for node in self.node_iter.by_ref() {
if node.name() == "reserved-memory" {
self.in_reserved_memory = true;
self.reserved_level = node.level();
continue;
}
if self.in_reserved_memory {
if node.level() <= self.reserved_level {
self.in_reserved_memory = false;
return None;
} else {
return Some(node);
}
}
}
None
}
}
pub struct ChildrenIter<'a> {
node_iter: FdtIter<'a>,
child_level: usize,
done: bool,
}
impl<'a> Iterator for ChildrenIter<'a> {
type Item = Node<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.done {
return None;
}
for node in self.node_iter.by_ref() {
if node.level() == self.child_level {
return Some(node);
}
if node.level() < self.child_level {
self.done = true;
return None;
}
}
None
}
}
impl fmt::Display for Fdt<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "/dts-v1/;")?;
writeln!(f)?;
let mut state = DisplayState::new();
for node in self.all_nodes() {
self.close_open_nodes(f, &mut state, node.level())?;
self.write_node(f, &node)?;
state.prev_level = node.level() + 1;
}
self.close_all_nodes(f, &mut state)
}
}
struct DisplayState {
prev_level: usize,
}
impl DisplayState {
fn new() -> Self {
Self { prev_level: 0 }
}
}
impl Fdt<'_> {
fn write_node(&self, f: &mut fmt::Formatter<'_>, node: &Node<'_>) -> fmt::Result {
fmt_utils::write_indent(f, node.level(), " ")?;
let name = Self::format_node_name(node.name());
writeln!(f, "{} {{", name)?;
for prop in node.properties() {
fmt_utils::write_indent(f, node.level() + 1, " ")?;
writeln!(f, "{};", prop)?;
}
Ok(())
}
fn format_node_name(name: &str) -> &str {
if name.is_empty() { "/" } else { name }
}
fn close_open_nodes(
&self,
f: &mut fmt::Formatter<'_>,
state: &mut DisplayState,
current_level: usize,
) -> fmt::Result {
while state.prev_level > current_level {
state.prev_level -= 1;
fmt_utils::write_indent(f, state.prev_level, " ")?;
writeln!(f, "}};\n")?;
}
Ok(())
}
fn close_all_nodes(&self, f: &mut fmt::Formatter<'_>, state: &mut DisplayState) -> fmt::Result {
while state.prev_level > 0 {
state.prev_level -= 1;
fmt_utils::write_indent(f, state.prev_level, " ")?;
writeln!(f, "}};\n")?;
}
Ok(())
}
}
impl fmt::Debug for Fdt<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "Fdt {{")?;
writeln!(f, "\theader: {:?}", self.header)?;
writeln!(f, "\tnodes:")?;
for node in self.all_nodes() {
self.debug_node(f, &node)?;
}
writeln!(f, "}}")
}
}
impl Fdt<'_> {
fn debug_node(&self, f: &mut fmt::Formatter<'_>, node: &Node<'_>) -> fmt::Result {
let level = node.level();
fmt_utils::write_indent(f, level + 2, "\t")?;
let name = Self::format_node_name(node.name());
writeln!(
f,
"[{}] address_cells={}, size_cells={}",
name, node.address_cells, node.size_cells
)?;
for prop in node.properties() {
self.debug_property(f, level, &prop)?;
}
Ok(())
}
fn debug_property(
&self,
f: &mut fmt::Formatter<'_>,
level: usize,
prop: &Property<'_>,
) -> fmt::Result {
fmt_utils::write_indent(f, level + 3, "\t")?;
match () {
() if prop.as_address_cells().is_some() => {
writeln!(f, "#address-cells: {}", prop.as_address_cells().unwrap())?
}
() if prop.as_size_cells().is_some() => {
writeln!(f, "#size-cells: {}", prop.as_size_cells().unwrap())?
}
() if prop.as_interrupt_cells().is_some() => writeln!(
f,
"#interrupt-cells: {}",
prop.as_interrupt_cells().unwrap()
)?,
() if prop.as_status().is_some() => {
writeln!(f, "status: {:?}", prop.as_status().unwrap())?
}
() if prop.as_phandle().is_some() => {
writeln!(f, "phandle: {}", prop.as_phandle().unwrap())?
}
() if prop.is_empty() => writeln!(f, "{}", prop.name())?,
() if prop.as_str().is_some() => {
writeln!(f, "{}: \"{}\"", prop.name(), prop.as_str().unwrap())?
}
() if prop.len() == 4 => {
let v = u32::from_be_bytes(prop.data().as_slice().try_into().unwrap());
writeln!(f, "{}: {:#x}", prop.name(), v)?
}
() => writeln!(f, "{}: <{} bytes>", prop.name(), prop.len())?,
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use heapless::Vec;
#[test]
fn test_memory_reservation_iterator() {
let mut test_data = [0u8; data::MEM_RSV_ENTRY_SIZE * 2];
test_data[0..8].copy_from_slice(&0x80000000u64.to_be_bytes());
test_data[8..16].copy_from_slice(&0x10000000u64.to_be_bytes());
test_data[16..24].copy_from_slice(&0u64.to_be_bytes());
test_data[24..32].copy_from_slice(&0u64.to_be_bytes());
let iter = MemoryReservationIter {
data: &test_data,
offset: 0,
};
let reservations: Vec<MemoryReservation, 4> = iter.collect();
assert_eq!(reservations.len(), 1);
assert_eq!(reservations[0].address, 0x80000000);
assert_eq!(reservations[0].size, 0x10000000);
}
#[test]
fn test_empty_memory_reservation_iterator() {
let mut test_data = [0u8; data::MEM_RSV_ENTRY_SIZE];
test_data[0..8].copy_from_slice(&0u64.to_be_bytes());
test_data[8..16].copy_from_slice(&0u64.to_be_bytes());
let iter = MemoryReservationIter {
data: &test_data,
offset: 0,
};
let reservations: Vec<MemoryReservation, 4> = iter.collect();
assert_eq!(reservations.len(), 0);
}
}