use super::error::DtbError;
use super::tokens::DtbToken;
use alloc::{vec, vec::Vec};
use core::convert::TryFrom;
use core::fmt::{self, Display, Formatter};
use core::ops::Index;
#[derive(Debug, Clone, PartialEq)]
pub enum PropertyValue<'a> {
Empty,
String(&'a str),
StringList(Vec<&'a str>),
U32(u32),
U32Array(&'a [u8]),
U64(u64),
U64Array(&'a [u8]),
Bytes(&'a [u8]),
}
#[derive(Debug, Clone)]
pub struct Property<'a> {
pub name: &'a str,
pub value: PropertyValue<'a>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AddressSpec {
address_cells: u32,
size_cells: u32,
}
impl AddressSpec {
pub const DEFAULT_ADDRESS_CELLS: u32 = 2;
pub const DEFAULT_SIZE_CELLS: u32 = 1;
pub const MAX_ADDRESS_CELLS: u32 = 4;
pub const MAX_SIZE_CELLS: u32 = 4;
pub fn new(address_cells: u32, size_cells: u32) -> Result<Self, DtbError> {
if address_cells == 0 || address_cells > Self::MAX_ADDRESS_CELLS {
return Err(DtbError::InvalidAddressCells(address_cells));
}
if size_cells > Self::MAX_SIZE_CELLS {
return Err(DtbError::InvalidSizeCells(size_cells));
}
Ok(Self {
address_cells,
size_cells,
})
}
#[must_use]
pub const fn address_cells(&self) -> u32 {
self.address_cells
}
#[must_use]
pub const fn size_cells(&self) -> u32 {
self.size_cells
}
#[must_use]
pub const fn total_cells(&self) -> u32 {
self.address_cells + self.size_cells
}
#[must_use]
pub const fn address_size_bytes(&self) -> usize {
(self.address_cells * 4) as usize
}
#[must_use]
pub const fn size_size_bytes(&self) -> usize {
(self.size_cells * 4) as usize
}
#[must_use]
pub const fn total_size_bytes(&self) -> usize {
(self.total_cells() * 4) as usize
}
}
impl Default for AddressSpec {
fn default() -> Self {
Self {
address_cells: Self::DEFAULT_ADDRESS_CELLS,
size_cells: Self::DEFAULT_SIZE_CELLS,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AddressRange {
child_address: u64,
parent_address: u64,
size: u64,
}
impl AddressRange {
pub fn new(child_address: u64, parent_address: u64, size: u64) -> Result<Self, DtbError> {
if child_address.checked_add(size).is_none() {
return Err(DtbError::AddressTranslationError(child_address));
}
if parent_address.checked_add(size).is_none() {
return Err(DtbError::AddressTranslationError(parent_address));
}
Ok(Self {
child_address,
parent_address,
size,
})
}
#[must_use]
pub const fn child_address(&self) -> u64 {
self.child_address
}
#[must_use]
pub const fn parent_address(&self) -> u64 {
self.parent_address
}
#[must_use]
pub const fn size(&self) -> u64 {
self.size
}
#[must_use]
pub const fn child_end(&self) -> u64 {
self.child_address + self.size
}
#[must_use]
pub const fn parent_end(&self) -> u64 {
self.parent_address + self.size
}
#[must_use]
pub const fn contains(&self, address: u64) -> bool {
address >= self.child_address && address < self.child_end()
}
pub fn translate(&self, child_addr: u64) -> Result<u64, DtbError> {
if !self.contains(child_addr) {
return Err(DtbError::AddressTranslationError(child_addr));
}
let offset = child_addr - self.child_address;
self.parent_address
.checked_add(offset)
.ok_or(DtbError::AddressTranslationError(child_addr))
}
}
#[derive(Debug, Clone)]
pub struct DeviceTreeNode<'a> {
pub name: &'a str,
pub properties: Vec<Property<'a>>,
pub children: Vec<DeviceTreeNode<'a>>,
}
impl<'a> DeviceTreeNode<'a> {
#[must_use]
pub fn new(name: &'a str) -> Self {
Self {
name,
properties: Vec::new(),
children: Vec::new(),
}
}
pub fn add_property(&mut self, property: Property<'a>) {
self.properties.push(property);
}
pub fn add_child(&mut self, child: DeviceTreeNode<'a>) {
self.children.push(child);
}
#[must_use]
pub fn find_property(&self, name: &str) -> Option<&Property<'a>> {
self.properties.iter().find(|p| p.name == name)
}
#[must_use]
pub fn find_child(&self, name: &str) -> Option<&DeviceTreeNode<'a>> {
self.children.iter().find(|c| c.name == name)
}
#[must_use]
pub fn find_node(&self, path: &str) -> Option<&DeviceTreeNode<'a>> {
if path.is_empty() || path == "/" {
return Some(self);
}
let path = path.strip_prefix('/').unwrap_or(path);
let parts: Vec<&str> = path.split('/').collect();
self.find_node_by_parts(&parts)
}
fn find_node_by_parts(&self, parts: &[&str]) -> Option<&DeviceTreeNode<'a>> {
if parts.is_empty() {
return Some(self);
}
let current_part = parts[0];
let remaining_parts = &parts[1..];
if let Some(child) = self.find_child(current_part) {
return child.find_node_by_parts(remaining_parts);
}
for child in &self.children {
if child.name.starts_with(current_part)
&& let Some(at_pos) = child.name.find('@')
{
let base_name = &child.name[..at_pos];
if base_name == current_part {
return child.find_node_by_parts(remaining_parts);
}
}
}
None
}
#[must_use]
pub fn prop_u32(&self, name: &str) -> Option<u32> {
self.find_property(name).and_then(|p| match &p.value {
PropertyValue::U32(val) => Some(*val),
PropertyValue::U32Array(bytes) if bytes.len() >= 4 => {
Some(u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
}
_ => None,
})
}
#[must_use]
pub fn prop_string(&self, name: &str) -> Option<&str> {
self.find_property(name).and_then(|p| match &p.value {
PropertyValue::String(s) => Some(*s),
PropertyValue::StringList(list) if !list.is_empty() => Some(list[0]),
_ => None,
})
}
#[must_use]
pub fn prop_u32_array(&self, name: &str) -> Option<Vec<u32>> {
self.find_property(name).and_then(|p| match &p.value {
PropertyValue::U32Array(bytes) => {
let mut values = Vec::new();
for chunk in bytes.chunks_exact(4) {
values.push(u32::from_be_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]));
}
Some(values)
}
PropertyValue::U32(val) => Some(vec![*val]),
_ => None,
})
}
#[must_use]
pub fn prop_u64(&self, name: &str) -> Option<u64> {
self.find_property(name).and_then(|p| match &p.value {
PropertyValue::U64(val) => Some(*val),
PropertyValue::U64Array(bytes) if bytes.len() >= 8 => Some(u64::from_be_bytes([
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
])),
_ => None,
})
}
#[must_use]
pub fn prop_bytes(&self, name: &str) -> Option<&[u8]> {
self.find_property(name).and_then(|p| match &p.value {
PropertyValue::Bytes(bytes) => Some(*bytes),
_ => None,
})
}
#[must_use]
pub fn has_property(&self, name: &str) -> bool {
self.find_property(name).is_some()
}
pub fn address_cells(&self) -> Result<u32, DtbError> {
match self.prop_u32("#address-cells") {
Some(cells) => {
if cells == 0 || cells > AddressSpec::MAX_ADDRESS_CELLS {
Err(DtbError::InvalidAddressCells(cells))
} else {
Ok(cells)
}
}
None => Ok(AddressSpec::DEFAULT_ADDRESS_CELLS), }
}
pub fn size_cells(&self) -> Result<u32, DtbError> {
match self.prop_u32("#size-cells") {
Some(cells) => {
if cells > AddressSpec::MAX_SIZE_CELLS {
Err(DtbError::InvalidSizeCells(cells))
} else {
Ok(cells)
}
}
None => Ok(AddressSpec::DEFAULT_SIZE_CELLS), }
}
pub fn address_cells_with_parent(
&self,
parent: Option<&DeviceTreeNode<'a>>,
) -> Result<u32, DtbError> {
if let Some(cells) = self.prop_u32("#address-cells") {
if cells == 0 || cells > AddressSpec::MAX_ADDRESS_CELLS {
return Err(DtbError::InvalidAddressCells(cells));
}
return Ok(cells);
}
if let Some(parent_node) = parent {
if let Some(cells) = parent_node.prop_u32("#address-cells") {
if cells == 0 || cells > AddressSpec::MAX_ADDRESS_CELLS {
return Err(DtbError::InvalidAddressCells(cells));
}
return Ok(cells);
}
}
Ok(AddressSpec::DEFAULT_ADDRESS_CELLS)
}
pub fn size_cells_with_parent(
&self,
parent: Option<&DeviceTreeNode<'a>>,
) -> Result<u32, DtbError> {
if let Some(cells) = self.prop_u32("#size-cells") {
if cells > AddressSpec::MAX_SIZE_CELLS {
return Err(DtbError::InvalidSizeCells(cells));
}
return Ok(cells);
}
if let Some(parent_node) = parent {
if let Some(cells) = parent_node.prop_u32("#size-cells") {
if cells > AddressSpec::MAX_SIZE_CELLS {
return Err(DtbError::InvalidSizeCells(cells));
}
return Ok(cells);
}
}
Ok(AddressSpec::DEFAULT_SIZE_CELLS)
}
pub fn create_address_spec(
&self,
parent: Option<&DeviceTreeNode<'a>>,
) -> Result<AddressSpec, DtbError> {
let address_cells = self.address_cells_with_parent(parent)?;
let size_cells = self.size_cells_with_parent(parent)?;
AddressSpec::new(address_cells, size_cells)
}
pub fn ranges(
&self,
parent: Option<&DeviceTreeNode<'a>>,
child_address_cells: u32,
) -> Result<Vec<AddressRange>, DtbError> {
let ranges_data = match self.find_property("ranges") {
Some(prop) => match &prop.value {
PropertyValue::Bytes(data) | PropertyValue::U32Array(data) => *data,
PropertyValue::Empty => {
return Ok(Vec::new());
}
_ => return Err(DtbError::InvalidRangesFormat),
},
None => {
return Ok(Vec::new());
}
};
let parent_address_cells = self.address_cells_with_parent(parent)?;
let parent_size_cells = self.size_cells_with_parent(parent)?;
let child_addr_bytes = (child_address_cells * 4) as usize;
let parent_addr_bytes = (parent_address_cells * 4) as usize;
let size_bytes = (parent_size_cells * 4) as usize;
let entry_size = child_addr_bytes + parent_addr_bytes + size_bytes;
if ranges_data.len() % entry_size != 0 {
return Err(DtbError::InvalidRangesFormat);
}
let mut ranges = Vec::new();
let mut offset = 0;
while offset + entry_size <= ranges_data.len() {
let child_address = parse_address_from_bytes(
&ranges_data[offset..offset + child_addr_bytes],
child_address_cells,
)?;
offset += child_addr_bytes;
let parent_address = parse_address_from_bytes(
&ranges_data[offset..offset + parent_addr_bytes],
parent_address_cells,
)?;
offset += parent_addr_bytes;
let size = parse_address_from_bytes(
&ranges_data[offset..offset + size_bytes],
parent_size_cells,
)?;
offset += size_bytes;
let range = AddressRange::new(child_address, parent_address, size)?;
ranges.push(range);
}
Ok(ranges)
}
pub fn translate_address(
&self,
child_address: u64,
parent: Option<&DeviceTreeNode<'a>>,
child_address_cells: u32,
) -> Result<u64, DtbError> {
let ranges = self.ranges(parent, child_address_cells)?;
if ranges.is_empty() {
if self.has_property("ranges") {
return Ok(child_address);
}
return Err(DtbError::AddressTranslationError(child_address));
}
for range in &ranges {
if range.contains(child_address) {
return range.translate(child_address);
}
}
Err(DtbError::AddressTranslationError(child_address))
}
pub fn translate_address_recursive(
&self,
child_address: u64,
child_address_cells: u32,
max_depth: u32,
) -> Result<u64, DtbError> {
self.translate_address_recursive_internal(
child_address,
child_address_cells,
max_depth,
&mut Vec::new(),
0,
)
}
fn translate_address_recursive_internal(
&self,
mut current_address: u64,
child_address_cells: u32,
max_depth: u32,
visited_nodes: &mut Vec<*const DeviceTreeNode<'a>>,
current_depth: u32,
) -> Result<u64, DtbError> {
if current_depth >= max_depth {
return Err(DtbError::MaxTranslationDepthExceeded);
}
let self_ptr = self as *const DeviceTreeNode<'a>;
if visited_nodes.contains(&self_ptr) {
return Err(DtbError::TranslationCycle);
}
visited_nodes.push(self_ptr);
if !self.has_property("ranges") {
visited_nodes.pop();
return Ok(current_address);
}
let parent_node: Option<&DeviceTreeNode<'a>> = None; match self.translate_address(current_address, parent_node, child_address_cells) {
Ok(translated_address) => {
current_address = translated_address;
visited_nodes.pop();
Ok(current_address)
}
Err(DtbError::AddressTranslationError(_)) => {
if self.has_property("ranges") {
if let Some(ranges_prop) = self.find_property("ranges") {
if matches!(ranges_prop.value, PropertyValue::Empty) {
visited_nodes.pop();
return Ok(current_address);
}
}
}
visited_nodes.pop();
Err(DtbError::AddressTranslationError(current_address))
}
Err(e) => {
visited_nodes.pop();
Err(e)
}
}
}
pub fn translate_reg_addresses(
&self,
parent: Option<&DeviceTreeNode<'a>>,
) -> Result<Vec<(u64, u64)>, DtbError> {
let mut addresses = Vec::new();
if let Some(reg) = self.prop_u32_array("reg") {
let address_cells = self.address_cells_with_parent(parent)?;
let size_cells = self.size_cells_with_parent(parent)?;
let entry_size = (address_cells + size_cells) as usize;
let mut i = 0;
while i + entry_size <= reg.len() {
let mut address = 0u64;
for j in 0..address_cells as usize {
address = (address << 32) | u64::from(reg[i + j]);
}
let mut size = 0u64;
for j in 0..size_cells as usize {
size = (size << 32) | u64::from(reg[i + address_cells as usize + j]);
}
let translated_address = self
.translate_address(address, parent, address_cells)
.unwrap_or(address);
addresses.push((translated_address, size));
i += entry_size;
}
}
Ok(addresses)
}
pub fn mmio_regions(
&self,
parent: Option<&DeviceTreeNode<'a>>,
) -> Result<Vec<(u64, u64)>, DtbError> {
self.translate_reg_addresses(parent)
}
#[must_use]
pub fn find_nodes_with_property(&self, property_name: &str) -> Vec<&DeviceTreeNode<'a>> {
let mut nodes = Vec::new();
self.collect_nodes_with_property(property_name, &mut nodes);
nodes
}
fn collect_nodes_with_property<'b>(
&'b self,
property_name: &str,
nodes: &mut Vec<&'b DeviceTreeNode<'a>>,
) {
if self.has_property(property_name) {
nodes.push(self);
}
for child in &self.children {
child.collect_nodes_with_property(property_name, nodes);
}
}
#[must_use]
pub fn find_compatible_nodes(&self, compatible: &str) -> Vec<&DeviceTreeNode<'a>> {
let mut nodes = Vec::new();
self.collect_compatible_nodes(compatible, &mut nodes);
nodes
}
fn collect_compatible_nodes<'b>(
&'b self,
compatible: &str,
nodes: &mut Vec<&'b DeviceTreeNode<'a>>,
) {
if let Some(compat_prop) = self.find_property("compatible") {
match &compat_prop.value {
PropertyValue::String(s) if *s == compatible => {
nodes.push(self);
}
PropertyValue::StringList(list) if list.contains(&compatible) => {
nodes.push(self);
}
_ => {}
}
}
for child in &self.children {
child.collect_compatible_nodes(compatible, nodes);
}
}
#[must_use]
pub fn iter_nodes(&self) -> NodeIterator<'a, '_> {
NodeIterator::new(self)
}
pub fn iter_properties(&self) -> core::slice::Iter<'_, Property<'a>> {
self.properties.iter()
}
pub fn iter_children(&self) -> core::slice::Iter<'_, DeviceTreeNode<'a>> {
self.children.iter()
}
}
impl<'a> Index<&str> for DeviceTreeNode<'a> {
type Output = Property<'a>;
fn index(&self, property_name: &str) -> &Self::Output {
self.find_property(property_name)
.unwrap_or_else(|| panic!("Property '{property_name}' not found"))
}
}
impl<'a> Index<usize> for DeviceTreeNode<'a> {
type Output = DeviceTreeNode<'a>;
fn index(&self, index: usize) -> &Self::Output {
&self.children[index]
}
}
impl<'a> IntoIterator for &'a DeviceTreeNode<'a> {
type Item = &'a DeviceTreeNode<'a>;
type IntoIter = core::slice::Iter<'a, DeviceTreeNode<'a>>;
fn into_iter(self) -> Self::IntoIter {
self.children.iter()
}
}
impl Display for PropertyValue<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
PropertyValue::Empty => write!(f, "<empty>"),
PropertyValue::String(s) => write!(f, "\"{s}\""),
PropertyValue::StringList(list) => {
write!(f, "[")?;
for (i, s) in list.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "\"{s}\"")?;
}
write!(f, "]")
}
PropertyValue::U32(val) => write!(f, "0x{val:x}"),
PropertyValue::U32Array(bytes) => {
write!(f, "[")?;
for (i, chunk) in bytes.chunks_exact(4).enumerate() {
if i > 0 {
write!(f, ", ")?;
}
let val = u32::from_be_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]);
write!(f, "0x{val:x}")?;
}
write!(f, "]")
}
PropertyValue::U64(val) => write!(f, "0x{val:x}"),
PropertyValue::U64Array(bytes) => {
write!(f, "[")?;
for (i, chunk) in bytes.chunks_exact(8).enumerate() {
if i > 0 {
write!(f, ", ")?;
}
let val = u64::from_be_bytes([
chunk[0], chunk[1], chunk[2], chunk[3], chunk[4], chunk[5], chunk[6],
chunk[7],
]);
write!(f, "0x{val:x}")?;
}
write!(f, "]")
}
PropertyValue::Bytes(bytes) => {
write!(f, "[")?;
for (i, byte) in bytes.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "0x{byte:02x}")?;
}
write!(f, "]")
}
}
}
}
impl Display for Property<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{} = {}", self.name, self.value)
}
}
impl Display for DeviceTreeNode<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
self.fmt_with_indent(f, 0)
}
}
impl DeviceTreeNode<'_> {
fn fmt_with_indent(&self, f: &mut Formatter<'_>, indent: usize) -> fmt::Result {
let indent_str = " ".repeat(indent);
if self.name.is_empty() {
writeln!(f, "{indent_str}/ {{")?;
} else {
writeln!(f, "{indent_str}{} {{", self.name)?;
}
for property in &self.properties {
writeln!(f, "{indent_str} {property}")?;
}
for child in &self.children {
child.fmt_with_indent(f, indent + 1)?;
}
writeln!(f, "{indent_str}}}")
}
}
impl Default for DeviceTreeNode<'_> {
fn default() -> Self {
Self {
name: "",
properties: Vec::new(),
children: Vec::new(),
}
}
}
impl Default for PropertyValue<'_> {
fn default() -> Self {
PropertyValue::Empty
}
}
impl<'a> TryFrom<&PropertyValue<'a>> for u32 {
type Error = DtbError;
fn try_from(value: &PropertyValue<'a>) -> Result<Self, Self::Error> {
match value {
PropertyValue::U32(val) => Ok(*val),
PropertyValue::U32Array(bytes) if bytes.len() >= 4 => {
Ok(u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
}
_ => Err(DtbError::InvalidToken),
}
}
}
impl<'a> TryFrom<&PropertyValue<'a>> for u64 {
type Error = DtbError;
fn try_from(value: &PropertyValue<'a>) -> Result<Self, Self::Error> {
match value {
PropertyValue::U64(val) => Ok(*val),
PropertyValue::U64Array(bytes) if bytes.len() >= 8 => Ok(u64::from_be_bytes([
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
])),
PropertyValue::U32(val) => Ok(u64::from(*val)),
PropertyValue::U32Array(bytes) if bytes.len() >= 4 => {
let val = u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
Ok(u64::from(val))
}
_ => Err(DtbError::InvalidToken),
}
}
}
impl<'a> TryFrom<&PropertyValue<'a>> for &'a str {
type Error = DtbError;
fn try_from(value: &PropertyValue<'a>) -> Result<Self, Self::Error> {
match value {
PropertyValue::String(s) => Ok(*s),
PropertyValue::StringList(list) if !list.is_empty() => Ok(list[0]),
_ => Err(DtbError::InvalidToken),
}
}
}
impl<'a> TryFrom<&PropertyValue<'a>> for Vec<u32> {
type Error = DtbError;
fn try_from(value: &PropertyValue<'a>) -> Result<Self, Self::Error> {
match value {
PropertyValue::U32Array(bytes) => {
let mut values = Vec::new();
for chunk in bytes.chunks_exact(4) {
values.push(u32::from_be_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]));
}
Ok(values)
}
PropertyValue::U32(val) => Ok(vec![*val]),
_ => Err(DtbError::InvalidToken),
}
}
}
impl<'a> TryFrom<&PropertyValue<'a>> for &'a [u8] {
type Error = DtbError;
fn try_from(value: &PropertyValue<'a>) -> Result<Self, Self::Error> {
match value {
PropertyValue::Bytes(bytes)
| PropertyValue::U32Array(bytes)
| PropertyValue::U64Array(bytes) => Ok(*bytes),
_ => Err(DtbError::InvalidToken),
}
}
}
pub struct NodeIterator<'a, 'b> {
stack: Vec<&'b DeviceTreeNode<'a>>,
}
impl<'a, 'b> NodeIterator<'a, 'b> {
fn new(root: &'b DeviceTreeNode<'a>) -> Self {
Self { stack: vec![root] }
}
}
impl<'a, 'b> Iterator for NodeIterator<'a, 'b> {
type Item = &'b DeviceTreeNode<'a>;
fn next(&mut self) -> Option<Self::Item> {
if let Some(node) = self.stack.pop() {
for child in node.children.iter().rev() {
self.stack.push(child);
}
Some(node)
} else {
None
}
}
}
pub fn parse_address_from_bytes(bytes: &[u8], cells: u32) -> Result<u64, DtbError> {
let expected_len = (cells * 4) as usize;
if bytes.len() != expected_len {
return Err(DtbError::MalformedHeader);
}
match cells {
1 => {
let addr = u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
Ok(u64::from(addr))
}
2 => {
Ok(u64::from_be_bytes([
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
]))
}
3 => {
Ok(u64::from_be_bytes([
bytes[4], bytes[5], bytes[6], bytes[7], bytes[8], bytes[9], bytes[10], bytes[11],
]))
}
4 => {
Ok(u64::from_be_bytes([
bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14],
bytes[15],
]))
}
_ => Err(DtbError::InvalidAddressCells(cells)),
}
}
pub fn parse_null_terminated_string(input: &[u8]) -> Result<(&[u8], &str), DtbError> {
let null_pos = input
.iter()
.position(|&b| b == 0)
.ok_or(DtbError::MalformedHeader)?;
let string_bytes = &input[..null_pos];
let string = core::str::from_utf8(string_bytes).map_err(|_| DtbError::MalformedHeader)?;
Ok((&input[null_pos + 1..], string))
}
pub fn parse_node_name(input: &[u8]) -> Result<(&[u8], &str), DtbError> {
let (remaining, name) = parse_null_terminated_string(input)?;
let name_len = input.len() - remaining.len();
let padding = DtbToken::calculate_padding(name_len);
if remaining.len() < padding {
return Err(DtbError::MalformedHeader);
}
Ok((&remaining[padding..], name))
}
pub fn parse_property_data<'a>(
input: &'a [u8],
strings_block: &'a [u8],
) -> Result<(&'a [u8], Property<'a>), DtbError> {
if input.len() < 8 {
return Err(DtbError::MalformedHeader);
}
let prop_len = u32::from_be_bytes([input[0], input[1], input[2], input[3]]) as usize;
let name_offset = u32::from_be_bytes([input[4], input[5], input[6], input[7]]) as usize;
let remaining = &input[8..];
if remaining.len() < prop_len {
return Err(DtbError::MalformedHeader);
}
let prop_data = &remaining[..prop_len];
let padding = DtbToken::calculate_padding(prop_len);
let next_input = &remaining[prop_len + padding..];
let name = resolve_property_name(strings_block, name_offset)?;
let value = parse_property_value(prop_data);
let property = Property { name, value };
Ok((next_input, property))
}
fn resolve_property_name(strings_block: &[u8], offset: usize) -> Result<&str, DtbError> {
if offset >= strings_block.len() {
return Err(DtbError::MalformedHeader);
}
let string_data = &strings_block[offset..];
let (_remaining, name) = parse_null_terminated_string(string_data)?;
Ok(name)
}
fn parse_property_value(data: &[u8]) -> PropertyValue<'_> {
if data.is_empty() {
return PropertyValue::Empty;
}
if let Ok(string_value) = parse_as_strings(data) {
return string_value;
}
if data.len() % 4 == 0 && !data.is_empty() {
if data.len() == 4 {
let value = u32::from_be_bytes([data[0], data[1], data[2], data[3]]);
return PropertyValue::U32(value);
}
return PropertyValue::U32Array(data);
}
if data.len() % 8 == 0 && !data.is_empty() {
if data.len() == 8 {
let value = u64::from_be_bytes([
data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
]);
return PropertyValue::U64(value);
}
return PropertyValue::U64Array(data);
}
PropertyValue::Bytes(data)
}
fn parse_as_strings(data: &[u8]) -> Result<PropertyValue<'_>, ()> {
if !data
.iter()
.all(|&b| b == 0 || (32..=126).contains(&b) || b == 9 || b == 10 || b == 13)
{
return Err(());
}
let mut strings = Vec::new();
let mut start = 0;
for (i, &byte) in data.iter().enumerate() {
if byte == 0 {
if start < i {
let string_bytes = &data[start..i];
if let Ok(s) = core::str::from_utf8(string_bytes) {
strings.push(s);
} else {
return Err(());
}
}
start = i + 1;
}
}
if start < data.len() {
let string_bytes = &data[start..];
if let Ok(s) = core::str::from_utf8(string_bytes) {
strings.push(s);
} else {
return Err(());
}
}
match strings.len() {
0 => Ok(PropertyValue::Empty),
1 => Ok(PropertyValue::String(strings[0])),
_ => Ok(PropertyValue::StringList(strings)),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_device_tree_node_creation() {
let node = DeviceTreeNode::new("test");
assert_eq!(node.name, "test");
assert!(node.properties.is_empty());
assert!(node.children.is_empty());
}
#[test]
fn test_parse_null_terminated_string() {
let data = b"hello\0world";
let result = parse_null_terminated_string(data);
assert!(result.is_ok());
let (remaining, string) = result.unwrap();
assert_eq!(string, "hello");
assert_eq!(remaining, b"world");
}
#[test]
fn test_address_spec_creation() {
let spec1 = AddressSpec::new(2, 1).unwrap();
assert_eq!(spec1.address_cells(), 2);
assert_eq!(spec1.size_cells(), 1);
assert_eq!(spec1.total_cells(), 3);
let spec2 = AddressSpec::new(1, 2).unwrap();
assert_eq!(spec2.address_cells(), 1);
assert_eq!(spec2.size_cells(), 2);
let spec_min = AddressSpec::new(1, 0).unwrap();
assert_eq!(spec_min.address_cells(), 1);
assert_eq!(spec_min.size_cells(), 0);
let spec_max = AddressSpec::new(4, 4).unwrap();
assert_eq!(spec_max.address_cells(), 4);
assert_eq!(spec_max.size_cells(), 4);
}
#[test]
fn test_address_spec_validation() {
assert!(matches!(
AddressSpec::new(0, 1),
Err(DtbError::InvalidAddressCells(0))
));
assert!(matches!(
AddressSpec::new(5, 1),
Err(DtbError::InvalidAddressCells(5))
));
assert!(matches!(
AddressSpec::new(2, 5),
Err(DtbError::InvalidSizeCells(5))
));
}
#[test]
fn test_address_spec_defaults() {
let default_spec = AddressSpec::default();
assert_eq!(default_spec.address_cells(), 2);
assert_eq!(default_spec.size_cells(), 1);
assert_eq!(default_spec.address_size_bytes(), 8);
assert_eq!(default_spec.size_size_bytes(), 4);
assert_eq!(default_spec.total_size_bytes(), 12);
}
#[test]
fn test_address_spec_byte_calculations() {
let spec = AddressSpec::new(3, 2).unwrap();
assert_eq!(spec.address_size_bytes(), 12); assert_eq!(spec.size_size_bytes(), 8); assert_eq!(spec.total_size_bytes(), 20); }
#[test]
fn test_parse_node_name() {
let data = b"root\0\0\0\0next";
let result = parse_node_name(data);
assert!(result.is_ok());
let (remaining, name) = result.unwrap();
assert_eq!(name, "root");
assert_eq!(remaining, b"next");
}
#[test]
fn test_parse_property_value_u32() {
let data = [0x12, 0x34, 0x56, 0x78];
let value = parse_property_value(&data);
assert_eq!(value, PropertyValue::U32(0x12345678));
}
#[test]
fn test_parse_property_value_string() {
let data = b"hello\0";
let value = parse_property_value(data);
match value {
PropertyValue::String(s) => assert_eq!(s, "hello"),
_ => panic!("Expected String value"),
}
}
#[test]
fn test_parse_property_value_empty() {
let data = [];
let value = parse_property_value(&data);
assert_eq!(value, PropertyValue::Empty);
}
#[test]
fn test_node_property_accessors() {
let name1 = "test-u32";
let name2 = "test-string";
let value_str = "hello";
let mut node = DeviceTreeNode::new("test");
node.add_property(Property {
name: name1,
value: PropertyValue::U32(42),
});
node.add_property(Property {
name: name2,
value: PropertyValue::String(value_str),
});
assert_eq!(node.prop_u32("test-u32"), Some(42));
assert_eq!(node.prop_string("test-string"), Some("hello"));
assert_eq!(node.prop_u32("nonexistent"), None);
}
#[test]
fn test_node_path_lookup() {
let device_type = "device_type";
let cpu_str = "cpu";
let mut root = DeviceTreeNode::new("");
let mut cpus = DeviceTreeNode::new("cpus");
let mut cpu0 = DeviceTreeNode::new("cpu@0");
cpu0.add_property(Property {
name: device_type,
value: PropertyValue::String(cpu_str),
});
cpus.add_child(cpu0);
root.add_child(cpus);
assert!(root.find_node("/").is_some());
assert!(root.find_node("").is_some());
assert!(root.find_node("/cpus").is_some());
assert!(root.find_node("/cpus/cpu@0").is_some());
assert!(root.find_node("/cpus/cpu").is_some());
assert!(root.find_node("/nonexistent").is_none());
}
#[test]
fn test_compatible_node_search() {
let compatible = "compatible";
let ns16550a = "ns16550a";
let ns16550 = "ns16550";
let mut root = DeviceTreeNode::new("");
let mut uart1 = DeviceTreeNode::new("uart@1000");
let mut uart2 = DeviceTreeNode::new("uart@2000");
uart1.add_property(Property {
name: compatible,
value: PropertyValue::String(ns16550a),
});
uart2.add_property(Property {
name: compatible,
value: PropertyValue::StringList(vec![ns16550a, ns16550]),
});
root.add_child(uart1);
root.add_child(uart2);
let ns16550a_nodes = root.find_compatible_nodes("ns16550a");
assert_eq!(ns16550a_nodes.len(), 2);
let ns16550_nodes = root.find_compatible_nodes("ns16550");
assert_eq!(ns16550_nodes.len(), 1);
}
#[test]
fn test_node_iterator() {
let mut root = DeviceTreeNode::new("");
let mut child1 = DeviceTreeNode::new("child1");
let child2 = DeviceTreeNode::new("child2");
let grandchild = DeviceTreeNode::new("grandchild");
child1.add_child(grandchild);
root.add_child(child1);
root.add_child(child2);
let nodes: Vec<_> = root.iter_nodes().collect();
assert_eq!(nodes.len(), 4);
assert_eq!(nodes[0].name, "");
assert_eq!(nodes[1].name, "child1");
assert_eq!(nodes[2].name, "grandchild");
assert_eq!(nodes[3].name, "child2");
}
#[test]
fn test_property_types() {
let u32_prop = "u32-prop";
let u64_prop = "u64-prop";
let bytes_prop = "bytes-prop";
let empty_prop = "empty-prop";
let bytes_data = &[1u8, 2, 3, 4];
let mut node = DeviceTreeNode::new("test");
node.add_property(Property {
name: u32_prop,
value: PropertyValue::U32(42),
});
node.add_property(Property {
name: u64_prop,
value: PropertyValue::U64(0x123456789),
});
node.add_property(Property {
name: bytes_prop,
value: PropertyValue::Bytes(bytes_data),
});
node.add_property(Property {
name: empty_prop,
value: PropertyValue::Empty,
});
assert_eq!(node.prop_u32("u32-prop"), Some(42));
assert_eq!(node.prop_u64("u64-prop"), Some(0x123456789));
assert_eq!(node.prop_bytes("bytes-prop"), Some(&[1, 2, 3, 4][..]));
assert!(node.has_property("empty-prop"));
assert!(!node.has_property("nonexistent"));
}
#[test]
fn test_ergonomic_traits() {
use core::convert::TryFrom;
let mut node = DeviceTreeNode::new("test");
let mut child = DeviceTreeNode::new("child");
node.add_property(Property {
name: "test-u32",
value: PropertyValue::U32(42),
});
node.add_property(Property {
name: "test-string",
value: PropertyValue::String("hello"),
});
child.add_property(Property {
name: "child-prop",
value: PropertyValue::U32(100),
});
node.add_child(child);
assert_eq!(node["test-u32"].name, "test-u32");
assert_eq!(node["test-string"].name, "test-string");
assert_eq!(node[0].name, "child");
let mut child_count = 0;
for child in &node {
child_count += 1;
assert_eq!(child.name, "child");
}
assert_eq!(child_count, 1);
let u32_val: u32 = u32::try_from(&node["test-u32"].value).unwrap();
assert_eq!(u32_val, 42);
let str_val: &str = <&str>::try_from(&node["test-string"].value).unwrap();
assert_eq!(str_val, "hello");
let default_node = DeviceTreeNode::default();
assert_eq!(default_node.name, "");
assert!(default_node.properties.is_empty());
assert!(default_node.children.is_empty());
let default_value = PropertyValue::default();
assert_eq!(default_value, PropertyValue::Empty);
}
#[test]
fn test_address_cells_parsing() {
let mut node = DeviceTreeNode::new("test");
node.add_property(Property {
name: "#address-cells",
value: PropertyValue::U32(2),
});
assert_eq!(node.address_cells().unwrap(), 2);
let mut invalid_node = DeviceTreeNode::new("test");
invalid_node.add_property(Property {
name: "#address-cells",
value: PropertyValue::U32(0),
});
assert!(matches!(
invalid_node.address_cells(),
Err(DtbError::InvalidAddressCells(0))
));
let mut invalid_node2 = DeviceTreeNode::new("test");
invalid_node2.add_property(Property {
name: "#address-cells",
value: PropertyValue::U32(5),
});
assert!(matches!(
invalid_node2.address_cells(),
Err(DtbError::InvalidAddressCells(5))
));
let empty_node = DeviceTreeNode::new("test");
assert_eq!(
empty_node.address_cells().unwrap(),
AddressSpec::DEFAULT_ADDRESS_CELLS
);
}
#[test]
fn test_size_cells_parsing() {
let mut node = DeviceTreeNode::new("test");
node.add_property(Property {
name: "#size-cells",
value: PropertyValue::U32(1),
});
assert_eq!(node.size_cells().unwrap(), 1);
let mut zero_size_node = DeviceTreeNode::new("test");
zero_size_node.add_property(Property {
name: "#size-cells",
value: PropertyValue::U32(0),
});
assert_eq!(zero_size_node.size_cells().unwrap(), 0);
let mut invalid_node = DeviceTreeNode::new("test");
invalid_node.add_property(Property {
name: "#size-cells",
value: PropertyValue::U32(5),
});
assert!(matches!(
invalid_node.size_cells(),
Err(DtbError::InvalidSizeCells(5))
));
let empty_node = DeviceTreeNode::new("test");
assert_eq!(
empty_node.size_cells().unwrap(),
AddressSpec::DEFAULT_SIZE_CELLS
);
}
#[test]
fn test_address_cells_with_parent_inheritance() {
let mut parent = DeviceTreeNode::new("parent");
parent.add_property(Property {
name: "#address-cells",
value: PropertyValue::U32(3),
});
let child = DeviceTreeNode::new("child");
assert_eq!(child.address_cells_with_parent(Some(&parent)).unwrap(), 3);
let mut child_with_prop = DeviceTreeNode::new("child");
child_with_prop.add_property(Property {
name: "#address-cells",
value: PropertyValue::U32(1),
});
assert_eq!(
child_with_prop
.address_cells_with_parent(Some(&parent))
.unwrap(),
1
);
assert_eq!(
child.address_cells_with_parent(None).unwrap(),
AddressSpec::DEFAULT_ADDRESS_CELLS
);
let mut invalid_parent = DeviceTreeNode::new("parent");
invalid_parent.add_property(Property {
name: "#address-cells",
value: PropertyValue::U32(0),
});
assert!(matches!(
child.address_cells_with_parent(Some(&invalid_parent)),
Err(DtbError::InvalidAddressCells(0))
));
}
#[test]
fn test_size_cells_with_parent_inheritance() {
let mut parent = DeviceTreeNode::new("parent");
parent.add_property(Property {
name: "#size-cells",
value: PropertyValue::U32(2),
});
let child = DeviceTreeNode::new("child");
assert_eq!(child.size_cells_with_parent(Some(&parent)).unwrap(), 2);
let mut child_with_prop = DeviceTreeNode::new("child");
child_with_prop.add_property(Property {
name: "#size-cells",
value: PropertyValue::U32(0),
});
assert_eq!(
child_with_prop
.size_cells_with_parent(Some(&parent))
.unwrap(),
0
);
assert_eq!(
child.size_cells_with_parent(None).unwrap(),
AddressSpec::DEFAULT_SIZE_CELLS
);
}
#[test]
fn test_create_address_spec() {
let mut node = DeviceTreeNode::new("test");
node.add_property(Property {
name: "#address-cells",
value: PropertyValue::U32(2),
});
node.add_property(Property {
name: "#size-cells",
value: PropertyValue::U32(1),
});
let spec = node.create_address_spec(None).unwrap();
assert_eq!(spec.address_cells(), 2);
assert_eq!(spec.size_cells(), 1);
assert_eq!(spec.total_cells(), 3);
let mut parent = DeviceTreeNode::new("parent");
parent.add_property(Property {
name: "#address-cells",
value: PropertyValue::U32(1),
});
parent.add_property(Property {
name: "#size-cells",
value: PropertyValue::U32(2),
});
let child = DeviceTreeNode::new("child");
let spec_with_parent = child.create_address_spec(Some(&parent)).unwrap();
assert_eq!(spec_with_parent.address_cells(), 1);
assert_eq!(spec_with_parent.size_cells(), 2);
let empty_node = DeviceTreeNode::new("empty");
let default_spec = empty_node.create_address_spec(None).unwrap();
assert_eq!(
default_spec.address_cells(),
AddressSpec::DEFAULT_ADDRESS_CELLS
);
assert_eq!(default_spec.size_cells(), AddressSpec::DEFAULT_SIZE_CELLS);
}
#[test]
fn test_address_range_creation() {
let range = AddressRange::new(0x1000, 0x80001000, 0x1000).unwrap();
assert_eq!(range.child_address(), 0x1000);
assert_eq!(range.parent_address(), 0x80001000);
assert_eq!(range.size(), 0x1000);
assert_eq!(range.child_end(), 0x2000);
assert_eq!(range.parent_end(), 0x80002000);
assert!(matches!(
AddressRange::new(u64::MAX, 0x80000000, 1),
Err(DtbError::AddressTranslationError(_))
));
assert!(matches!(
AddressRange::new(0x1000, u64::MAX, 1),
Err(DtbError::AddressTranslationError(_))
));
}
#[test]
fn test_address_range_contains() {
let range = AddressRange::new(0x1000, 0x80001000, 0x1000).unwrap();
assert!(range.contains(0x1000)); assert!(range.contains(0x1500)); assert!(range.contains(0x1FFF));
assert!(!range.contains(0x2000)); assert!(!range.contains(0x500)); assert!(!range.contains(0x3000)); }
#[test]
fn test_address_range_translation() {
let range = AddressRange::new(0x1000, 0x80001000, 0x1000).unwrap();
assert_eq!(range.translate(0x1000).unwrap(), 0x80001000); assert_eq!(range.translate(0x1500).unwrap(), 0x80001500); assert_eq!(range.translate(0x1FFF).unwrap(), 0x80001FFF);
assert!(matches!(
range.translate(0x500),
Err(DtbError::AddressTranslationError(0x500))
));
assert!(matches!(
range.translate(0x2000),
Err(DtbError::AddressTranslationError(0x2000))
));
let max_range = AddressRange::new(0x0, u64::MAX - 10, 10).unwrap();
assert_eq!(max_range.translate(0x5).unwrap(), u64::MAX - 5);
}
#[test]
fn test_parse_address_from_bytes() {
let bytes1 = [0x12, 0x34, 0x56, 0x78];
let addr1 = parse_address_from_bytes(&bytes1, 1).unwrap();
assert_eq!(addr1, 0x12345678);
let bytes2 = [0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0];
let addr2 = parse_address_from_bytes(&bytes2, 2).unwrap();
assert_eq!(addr2, 0x123456789ABCDEF0);
let bytes3 = [
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, ];
let addr3 = parse_address_from_bytes(&bytes3, 3).unwrap();
assert_eq!(addr3, 0x445566778899AABB);
let bytes4 = [
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, ];
let addr4 = parse_address_from_bytes(&bytes4, 4).unwrap();
assert_eq!(addr4, 0x8899AABBCCDDEEFF);
assert!(matches!(
parse_address_from_bytes(&bytes1, 0),
Err(DtbError::MalformedHeader)
));
let bytes5 = [0u8; 20]; assert!(matches!(
parse_address_from_bytes(&bytes5, 5),
Err(DtbError::InvalidAddressCells(5))
));
assert!(matches!(
parse_address_from_bytes(&bytes1[..3], 1),
Err(DtbError::MalformedHeader)
));
}
#[test]
fn test_ranges_parsing_empty_property() {
let mut node = DeviceTreeNode::new("test");
node.add_property(Property {
name: "ranges",
value: PropertyValue::Empty,
});
let ranges = node.ranges(None, 2).unwrap();
assert!(ranges.is_empty());
}
#[test]
fn test_ranges_parsing_no_property() {
let node = DeviceTreeNode::new("test");
let ranges = node.ranges(None, 2).unwrap();
assert!(ranges.is_empty());
}
#[test]
fn test_ranges_parsing_with_data() {
let mut node = DeviceTreeNode::new("test");
node.add_property(Property {
name: "#address-cells",
value: PropertyValue::U32(2),
});
node.add_property(Property {
name: "#size-cells",
value: PropertyValue::U32(1),
});
let ranges_data = vec![
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00,
0x00, 0x00, 0x80, 0x00,
];
node.add_property(Property {
name: "ranges",
value: PropertyValue::Bytes(&ranges_data),
});
let ranges = node.ranges(None, 2).unwrap();
assert_eq!(ranges.len(), 2);
let range1 = &ranges[0];
assert_eq!(range1.child_address(), 0x0);
assert_eq!(range1.parent_address(), 0x80000000);
assert_eq!(range1.size(), 0x10000);
let range2 = &ranges[1];
assert_eq!(range2.child_address(), 0x20000);
assert_eq!(range2.parent_address(), 0x90000000);
assert_eq!(range2.size(), 0x8000);
}
#[test]
fn test_ranges_parsing_invalid_format() {
let mut node = DeviceTreeNode::new("test");
node.add_property(Property {
name: "#address-cells",
value: PropertyValue::U32(2),
});
node.add_property(Property {
name: "#size-cells",
value: PropertyValue::U32(1),
});
let invalid_data = vec![0u8; 19]; node.add_property(Property {
name: "ranges",
value: PropertyValue::Bytes(&invalid_data),
});
assert!(matches!(
node.ranges(None, 2),
Err(DtbError::InvalidRangesFormat)
));
}
#[test]
fn test_ranges_parsing_with_inheritance() {
let mut parent = DeviceTreeNode::new("parent");
parent.add_property(Property {
name: "#address-cells",
value: PropertyValue::U32(1),
});
parent.add_property(Property {
name: "#size-cells",
value: PropertyValue::U32(1),
});
let mut child = DeviceTreeNode::new("child");
let ranges_data = vec![
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
];
child.add_property(Property {
name: "ranges",
value: PropertyValue::Bytes(&ranges_data),
});
let ranges = child.ranges(Some(&parent), 2).unwrap();
assert_eq!(ranges.len(), 1);
let range = &ranges[0];
assert_eq!(range.child_address(), 0x1000);
assert_eq!(range.parent_address(), 0x80000000);
assert_eq!(range.size(), 0x1000);
}
#[test]
fn test_translate_address_successful() {
let mut node = DeviceTreeNode::new("test");
node.add_property(Property {
name: "#address-cells",
value: PropertyValue::U32(2),
});
node.add_property(Property {
name: "#size-cells",
value: PropertyValue::U32(1),
});
let ranges_data = vec![
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00,
];
node.add_property(Property {
name: "ranges",
value: PropertyValue::Bytes(&ranges_data),
});
let translated = node.translate_address(0x1500, None, 2).unwrap();
assert_eq!(translated, 0x80001500);
let translated = node.translate_address(0x1000, None, 2).unwrap();
assert_eq!(translated, 0x80001000);
let translated = node.translate_address(0x1FFF, None, 2).unwrap();
assert_eq!(translated, 0x80001FFF);
}
#[test]
fn test_translate_address_no_matching_range() {
let mut node = DeviceTreeNode::new("test");
node.add_property(Property {
name: "#address-cells",
value: PropertyValue::U32(2),
});
node.add_property(Property {
name: "#size-cells",
value: PropertyValue::U32(1),
});
let ranges_data = vec![
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, ];
node.add_property(Property {
name: "ranges",
value: PropertyValue::Bytes(&ranges_data),
});
assert!(matches!(
node.translate_address(0x500, None, 2),
Err(DtbError::AddressTranslationError(0x500))
));
assert!(matches!(
node.translate_address(0x3000, None, 2),
Err(DtbError::AddressTranslationError(0x3000))
));
}
#[test]
fn test_translate_address_empty_ranges() {
let mut node = DeviceTreeNode::new("test");
node.add_property(Property {
name: "ranges",
value: PropertyValue::Empty,
});
let translated = node.translate_address(0x1234, None, 2).unwrap();
assert_eq!(translated, 0x1234);
let translated = node.translate_address(0x0, None, 2).unwrap();
assert_eq!(translated, 0x0);
}
#[test]
fn test_translate_address_no_ranges_property() {
let node = DeviceTreeNode::new("test");
assert!(matches!(
node.translate_address(0x1000, None, 2),
Err(DtbError::AddressTranslationError(0x1000))
));
}
#[test]
fn test_translate_address_multiple_ranges() {
let mut node = DeviceTreeNode::new("test");
node.add_property(Property {
name: "#address-cells",
value: PropertyValue::U32(2),
});
node.add_property(Property {
name: "#size-cells",
value: PropertyValue::U32(1),
});
let ranges_data = vec![
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00,
0x00, 0x00, 0x80, 0x00,
];
node.add_property(Property {
name: "ranges",
value: PropertyValue::Bytes(&ranges_data),
});
let translated = node.translate_address(0x5000, None, 2).unwrap();
assert_eq!(translated, 0x80005000);
let translated = node.translate_address(0x24000, None, 2).unwrap();
assert_eq!(translated, 0x90004000);
assert!(matches!(
node.translate_address(0x15000, None, 2),
Err(DtbError::AddressTranslationError(0x15000))
));
}
#[test]
fn test_translate_address_with_parent_inheritance() {
let mut parent = DeviceTreeNode::new("parent");
parent.add_property(Property {
name: "#address-cells",
value: PropertyValue::U32(1),
});
parent.add_property(Property {
name: "#size-cells",
value: PropertyValue::U32(1),
});
let mut child = DeviceTreeNode::new("child");
let ranges_data = vec![
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
];
child.add_property(Property {
name: "ranges",
value: PropertyValue::Bytes(&ranges_data),
});
let translated = child.translate_address(0x1500, Some(&parent), 2).unwrap();
assert_eq!(translated, 0x80000500);
}
#[test]
fn test_translate_address_boundary_conditions() {
let mut node = DeviceTreeNode::new("test");
node.add_property(Property {
name: "#address-cells",
value: PropertyValue::U32(1),
});
node.add_property(Property {
name: "#size-cells",
value: PropertyValue::U32(1),
});
let ranges_data = vec![
0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x10, 0x00,
];
node.add_property(Property {
name: "ranges",
value: PropertyValue::Bytes(&ranges_data),
});
let translated = node.translate_address(0x1000, None, 1).unwrap();
assert_eq!(translated, 0x2000);
let translated = node.translate_address(0x1FFF, None, 1).unwrap();
assert_eq!(translated, 0x2FFF);
assert!(matches!(
node.translate_address(0xFFF, None, 1),
Err(DtbError::AddressTranslationError(0xFFF))
));
assert!(matches!(
node.translate_address(0x2000, None, 1),
Err(DtbError::AddressTranslationError(0x2000))
));
}
#[test]
fn test_translate_address_zero_offset() {
let mut node = DeviceTreeNode::new("test");
node.add_property(Property {
name: "#address-cells",
value: PropertyValue::U32(1),
});
node.add_property(Property {
name: "#size-cells",
value: PropertyValue::U32(1),
});
let ranges_data = vec![
0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, ];
node.add_property(Property {
name: "ranges",
value: PropertyValue::Bytes(&ranges_data),
});
let translated = node.translate_address(0x1500, None, 1).unwrap();
assert_eq!(translated, 0x1500); }
#[test]
fn test_translate_address_large_addresses() {
let mut node = DeviceTreeNode::new("test");
node.add_property(Property {
name: "#address-cells",
value: PropertyValue::U32(2),
});
node.add_property(Property {
name: "#size-cells",
value: PropertyValue::U32(2),
});
let ranges_data = vec![
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
];
node.add_property(Property {
name: "ranges",
value: PropertyValue::Bytes(&ranges_data),
});
let translated = node.translate_address(0x150000000, None, 2).unwrap();
assert_eq!(translated, 0x250000000);
}
#[test]
fn test_translate_address_recursive_basic() {
let mut node = DeviceTreeNode::new("test");
node.add_property(Property {
name: "#address-cells",
value: PropertyValue::U32(2),
});
node.add_property(Property {
name: "#size-cells",
value: PropertyValue::U32(1),
});
let ranges_data = vec![
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, ];
node.add_property(Property {
name: "ranges",
value: PropertyValue::Bytes(&ranges_data),
});
let translated = node.translate_address_recursive(0x1500, 2, 10).unwrap();
assert_eq!(translated, 0x80001500);
}
#[test]
fn test_translate_address_recursive_no_ranges() {
let node = DeviceTreeNode::new("root");
let translated = node.translate_address_recursive(0x1000, 2, 10).unwrap();
assert_eq!(translated, 0x1000);
}
#[test]
fn test_translate_address_recursive_empty_ranges() {
let mut node = DeviceTreeNode::new("test");
node.add_property(Property {
name: "ranges",
value: PropertyValue::Empty,
});
let translated = node.translate_address_recursive(0x1234, 2, 10).unwrap();
assert_eq!(translated, 0x1234);
}
#[test]
fn test_translate_address_recursive_max_depth() {
let mut node = DeviceTreeNode::new("test");
node.add_property(Property {
name: "#address-cells",
value: PropertyValue::U32(1),
});
node.add_property(Property {
name: "#size-cells",
value: PropertyValue::U32(1),
});
let ranges_data = vec![
0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x10, 0x00, ];
node.add_property(Property {
name: "ranges",
value: PropertyValue::Bytes(&ranges_data),
});
assert!(matches!(
node.translate_address_recursive(0x1500, 1, 0),
Err(DtbError::MaxTranslationDepthExceeded)
));
}
#[test]
fn test_translate_address_recursive_cycle_detection() {
let mut node = DeviceTreeNode::new("self-referencing");
node.add_property(Property {
name: "#address-cells",
value: PropertyValue::U32(1),
});
node.add_property(Property {
name: "#size-cells",
value: PropertyValue::U32(1),
});
let ranges_data = vec![
0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x10, 0x00, ];
node.add_property(Property {
name: "ranges",
value: PropertyValue::Bytes(&ranges_data),
});
assert!(matches!(
node.translate_address_recursive(0x1000, 1, 10),
Err(DtbError::AddressTranslationError(0x1000))
));
}
#[test]
fn test_translate_address_recursive_invalid_ranges() {
let mut node = DeviceTreeNode::new("test");
node.add_property(Property {
name: "#address-cells",
value: PropertyValue::U32(1),
});
node.add_property(Property {
name: "#size-cells",
value: PropertyValue::U32(1),
});
let invalid_ranges_data = vec![0x00, 0x00, 0x10];
node.add_property(Property {
name: "ranges",
value: PropertyValue::Bytes(&invalid_ranges_data),
});
assert!(matches!(
node.translate_address_recursive(0x1000, 1, 10),
Err(DtbError::InvalidRangesFormat)
));
}
#[test]
fn test_translate_address_recursive_complex_scenario() {
let mut bus_node = DeviceTreeNode::new("bus");
bus_node.add_property(Property {
name: "#address-cells",
value: PropertyValue::U32(2),
});
bus_node.add_property(Property {
name: "#size-cells",
value: PropertyValue::U32(1),
});
let ranges_data = vec![
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00,
];
bus_node.add_property(Property {
name: "ranges",
value: PropertyValue::Bytes(&ranges_data),
});
let translated = bus_node.translate_address_recursive(0x1800, 2, 10).unwrap();
assert_eq!(translated, 0x90001800);
assert!(matches!(
bus_node.translate_address_recursive(0x3000, 2, 10),
Err(DtbError::AddressTranslationError(0x3000))
));
}
#[test]
fn test_translate_reg_addresses() {
let mut device = DeviceTreeNode::new("device");
device.add_property(Property {
name: "#address-cells",
value: PropertyValue::U32(2),
});
device.add_property(Property {
name: "#size-cells",
value: PropertyValue::U32(1),
});
let reg_data = vec![
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x02, 0x00, ];
device.add_property(Property {
name: "reg",
value: PropertyValue::U32Array(®_data),
});
let ranges_data = vec![
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, ];
device.add_property(Property {
name: "ranges",
value: PropertyValue::Bytes(&ranges_data),
});
let addresses = device.translate_reg_addresses(None).unwrap();
assert_eq!(addresses.len(), 2);
assert_eq!(addresses[0].0, 0x80001000); assert_eq!(addresses[0].1, 0x100);
assert_eq!(addresses[1].0, 0x80002000); assert_eq!(addresses[1].1, 0x200); }
#[test]
fn test_mmio_regions() {
let mut device = DeviceTreeNode::new("uart");
device.add_property(Property {
name: "#address-cells",
value: PropertyValue::U32(1),
});
device.add_property(Property {
name: "#size-cells",
value: PropertyValue::U32(1),
});
let reg_data = [
0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, ];
device.add_property(Property {
name: "reg",
value: PropertyValue::U32Array(®_data),
});
let mmio = device.mmio_regions(None).unwrap();
assert_eq!(mmio.len(), 1);
assert_eq!(mmio[0].0, 0x1000);
assert_eq!(mmio[0].1, 0x100);
}
#[test]
fn test_translate_reg_addresses_no_reg() {
let device = DeviceTreeNode::new("device");
let addresses = device.translate_reg_addresses(None).unwrap();
assert!(addresses.is_empty());
}
}