diff --git a/src/error.rs b/src/error.rs index 47f3194..5c63789 100644 --- a/src/error.rs +++ b/src/error.rs @@ -8,16 +8,14 @@ //! Error types for the `dtoolkit` crate. -use core::fmt::{self, Display, Formatter}; - use thiserror::Error; -/// An error that can occur when parsing or accessing a device tree. -#[derive(Clone, Debug, Eq, Error, PartialEq)] -pub enum FdtError { - /// There was an error parsing the device tree. - #[error("{0}")] - Parse(#[from] FdtParseError), +/// An error that can occur when accessing a standard node or property. +#[derive(Copy, Clone, Debug, Eq, Error, PartialEq)] +pub enum StandardError { + /// There was an error when converting the property value. + #[error("error occurred when converting the property value: {0}")] + PropertyConversion(#[from] PropertyError), /// The `status` property of a node had an invalid value. #[error("Invalid status value")] InvalidStatus, @@ -53,6 +51,7 @@ pub enum FdtError { /// An error that can occur when parsing a device tree. #[derive(Clone, Debug, Eq, Error, PartialEq)] #[non_exhaustive] +#[error("{kind} at offset {offset}")] pub struct FdtParseError { offset: usize, /// The type of the error that has occurred. @@ -65,11 +64,6 @@ impl FdtParseError { } } -impl Display for FdtParseError { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - write!(f, "{} at offset {}", self.kind, self.offset) - } -} /// The kind of an error that can occur when parsing a device tree. #[derive(Clone, Debug, Eq, Error, PartialEq)] #[non_exhaustive] @@ -103,3 +97,15 @@ pub enum FdtErrorKind { #[error("Memory reservation block has an entry that is unaligned or has invalid size")] MemReserveInvalid, } + +/// An error that can occur when parsing a property. +#[derive(Debug, Clone, Copy, Error, PartialEq, Eq)] +#[non_exhaustive] +pub enum PropertyError { + /// The property's value has an invalid length for the requested conversion. + #[error("property has an invalid length")] + InvalidLength, + /// The property's value is not a valid string. + #[error("property is not a valid string")] + InvalidString, +} diff --git a/src/fdt/mod.rs b/src/fdt/mod.rs index 0e1b607..0e374ad 100644 --- a/src/fdt/mod.rs +++ b/src/fdt/mod.rs @@ -176,36 +176,52 @@ impl<'a> Fdt<'a> { /// let fdt = Fdt::new(dtb).unwrap(); /// ``` pub fn new(data: &'a [u8]) -> Result { - if data.len() < size_of::() { - return Err(FdtParseError::new(FdtErrorKind::InvalidLength, 0)); - } - - let fdt = Fdt { data }; - let header = fdt.header(); - - if header.magic() != FDT_MAGIC { - return Err(FdtParseError::new( - FdtErrorKind::InvalidMagic, - offset_of!(FdtHeader, magic), - )); - } - if !(header.last_comp_version()..=header.version()).contains(&FDT_VERSION) { - return Err(FdtParseError::new( - FdtErrorKind::UnsupportedVersion(header.version()), - offset_of!(FdtHeader, version), - )); - } - - if header.totalsize() as usize != data.len() { - return Err(FdtParseError::new( - FdtErrorKind::InvalidLength, - offset_of!(FdtHeader, totalsize), - )); - } + let fdt = Self::new_unchecked(data); + fdt.validate()?; + Ok(fdt) + } - fdt.validate_header()?; + /// Creates a new `Fdt` from the given byte slice without validation. + /// + /// # Safety + /// + /// The caller must ensure that `data` contains a valid Flattened Device + /// Tree (FDT) blob. If the blob is invalid, methods on `Fdt` and + /// related types may panic. + #[must_use] + pub fn new_unchecked(data: &'a [u8]) -> Self { + Self { data } + } - Ok(fdt) + /// Creates a new `Fdt` from the given pointer without validation. + /// + /// # Safety + /// + /// The `data` pointer must be a valid pointer to a Flattened Device Tree + /// (FDT) blob. The memory region starting at `data` and spanning + /// `totalsize` bytes (as specified in the FDT header) must be valid and + /// accessible for reading. + #[expect( + unsafe_code, + reason = "Having a methods that reads a Device Tree from a raw pointer is useful for \ + embedded applications, where the binary only gets a pointer to DT from the firmware or \ + a bootloader. The user must ensure it trusts the data." + )] + #[must_use] + pub unsafe fn from_raw_unchecked(data: *const u8) -> Self { + // SAFETY: The caller guarantees that `data` is a valid pointer to a Flattened + // Device Tree (FDT) blob. We are reading an `FdtHeader` from this + // pointer, which is a `#[repr(C, packed)]` struct. The `totalsize` + // field of this header is then used to determine the total size of the FDT + // blob. The caller must ensure that the memory at `data` is valid for + // at least `size_of::()` bytes. + let header = unsafe { ptr::read_unaligned(data.cast::()) }; + let size = header.totalsize(); + // SAFETY: The caller must ensure that `data` is a valid pointer to a Flattened + // Device Tree (FDT) blob. The caller must ensure the `data` spans + // `totalsize` bytes (as specified in the FDT header). + let slice = unsafe { core::slice::from_raw_parts(data, size as usize) }; + Self::new_unchecked(slice) } /// Creates a new `Fdt` from the given pointer. @@ -238,18 +254,124 @@ impl<'a> Fdt<'a> { )] pub unsafe fn from_raw(data: *const u8) -> Result { // SAFETY: The caller guarantees that `data` is a valid pointer to a Flattened - // Device Tree (FDT) blob. We are reading an `FdtHeader` from this - // pointer, which is a `#[repr(C, packed)]` struct. The `totalsize` - // field of this header is then used to determine the total size of the FDT - // blob. The caller must ensure that the memory at `data` is valid for - // at least `size_of::()` bytes. - let header = unsafe { ptr::read_unaligned(data.cast::()) }; - let size = header.totalsize(); - // SAFETY: The caller must ensure that `data` is a valid pointer to a Flattened - // Device Tree (FDT) blob. The caller must ensure the `data` spans - // `totalsize` bytes (as specified in the FDT header). - let slice = unsafe { core::slice::from_raw_parts(data, size as usize) }; - Fdt::new(slice) + // Device Tree (FDT) blob. + let fdt = unsafe { Self::from_raw_unchecked(data) }; + fdt.validate()?; + Ok(fdt) + } + + fn validate(self) -> Result<(), FdtParseError> { + if self.data.len() < size_of::() { + return Err(FdtParseError::new(FdtErrorKind::InvalidLength, 0)); + } + + let header = self.header(); + + if header.magic() != FDT_MAGIC { + return Err(FdtParseError::new( + FdtErrorKind::InvalidMagic, + offset_of!(FdtHeader, magic), + )); + } + if !(header.last_comp_version()..=header.version()).contains(&FDT_VERSION) { + return Err(FdtParseError::new( + FdtErrorKind::UnsupportedVersion(header.version()), + offset_of!(FdtHeader, version), + )); + } + + if header.totalsize() as usize != self.data.len() { + return Err(FdtParseError::new( + FdtErrorKind::InvalidLength, + offset_of!(FdtHeader, totalsize), + )); + } + + self.validate_header()?; + self.validate_mem_reservations()?; + + // Validate structure block + let offset = header.off_dt_struct() as usize; + // Check root node + if self.read_token(offset)? != FdtToken::BeginNode { + return Err(FdtParseError::new( + FdtErrorKind::BadToken(FDT_BEGIN_NODE), + offset, + )); + } + + let end_offset = self.traverse_node(offset, true)?; + + // Check FDT_END + if self.read_token(end_offset)? != FdtToken::End { + return Err(FdtParseError::new( + FdtErrorKind::BadToken(FDT_END), + end_offset, + )); + } + + Ok(()) + } + + fn validate_mem_reservations(self) -> Result<(), FdtParseError> { + let header = self.header(); + let mut offset = header.off_mem_rsvmap() as usize; + loop { + if offset >= header.off_dt_struct() as usize { + return Err(FdtParseError::new( + FdtErrorKind::MemReserveNotTerminated, + offset, + )); + } + + let (reservation, _) = MemoryReservation::ref_from_prefix(&self.data[offset..]) + .map_err(|_| FdtParseError::new(FdtErrorKind::MemReserveInvalid, offset))?; + offset += size_of::(); + + if *reservation == MemoryReservation::TERMINATOR { + break; + } + } + Ok(()) + } + + fn traverse_node(self, offset: usize, check_strings: bool) -> Result { + let mut offset = offset; + let mut depth = 0; + loop { + let token = self.read_token(offset)?; + match token { + FdtToken::BeginNode => { + depth += 1; + offset += FDT_TAGSIZE; + // Validate name + offset = self.find_string_end(offset)?; + offset = Self::align_tag_offset(offset); + } + FdtToken::EndNode => { + if depth == 0 { + return Err(FdtParseError::new( + FdtErrorKind::BadToken(FDT_END_NODE), + offset, + )); + } + depth -= 1; + offset += FDT_TAGSIZE; + if depth == 0 { + // End of root node + return Ok(offset); + } + } + FdtToken::Prop => { + offset += FDT_TAGSIZE; + offset = self.next_property_offset(offset, check_strings)?; + } + FdtToken::Nop => offset += FDT_TAGSIZE, + FdtToken::End => { + return Err(FdtParseError::new(FdtErrorKind::BadToken(FDT_END), offset)); + } + } + } } fn validate_header(self) -> Result<(), FdtParseError> { @@ -334,40 +456,33 @@ impl<'a> Fdt<'a> { } /// Returns an iterator over the memory reservation block. - pub fn memory_reservations( - self, - ) -> impl Iterator> + 'a { + /// + /// # Panics + /// + /// Panics if the [`Fdt`] structure was constructed using + /// [`Fdt::new_unchecked`] or [`Fdt::from_raw_unchecked`] and the FDT is not + /// valid. + pub fn memory_reservations(self) -> impl Iterator + 'a { let mut offset = self.header().off_mem_rsvmap() as usize; core::iter::from_fn(move || { - if offset >= self.header().off_dt_struct() as usize { - return Some(Err(FdtParseError::new( - FdtErrorKind::MemReserveNotTerminated, - offset, - ))); - } - - let reservation = match MemoryReservation::ref_from_prefix(&self.data[offset..]) - .map_err(|_| FdtParseError::new(FdtErrorKind::MemReserveInvalid, offset)) - { - Ok((reservation, _)) => *reservation, - Err(e) => return Some(Err(e)), - }; + let (reservation, _) = MemoryReservation::ref_from_prefix(&self.data[offset..]) + .expect("Fdt should be valid"); offset += size_of::(); - if reservation == MemoryReservation::TERMINATOR { + if *reservation == MemoryReservation::TERMINATOR { return None; } - Some(Ok(reservation)) + Some(*reservation) }) } /// Returns the root node of the device tree. /// - /// # Errors + /// # Panics /// - /// Returns an [`FdtErrorKind::InvalidLength`] if the FDT structure is - /// truncated or an [`FdtErrorKind::BadToken`] if the first token is not - /// `FDT_BEGIN_NODE`. + /// Panics if the [`Fdt`] structure was constructed using + /// [`Fdt::new_unchecked`] or [`Fdt::from_raw_unchecked`] and the FDT is not + /// valid. /// /// # Examples /// @@ -375,19 +490,13 @@ impl<'a> Fdt<'a> { /// # use dtoolkit::fdt::Fdt; /// # let dtb = include_bytes!("../../tests/dtb/test.dtb"); /// let fdt = Fdt::new(dtb).unwrap(); - /// let root = fdt.root().unwrap(); - /// assert_eq!(root.name().unwrap(), ""); + /// let root = fdt.root(); + /// assert_eq!(root.name(), ""); /// ``` - pub fn root(self) -> Result, FdtParseError> { + #[must_use] + pub fn root(self) -> FdtNode<'a> { let offset = self.header().off_dt_struct() as usize; - let token = self.read_token(offset)?; - if token != FdtToken::BeginNode { - return Err(FdtParseError::new( - FdtErrorKind::BadToken(FDT_BEGIN_NODE), - offset, - )); - } - Ok(FdtNode::new(self, offset)) + FdtNode::new(self, offset) } /// Finds a node by its path. @@ -410,12 +519,11 @@ impl<'a> Fdt<'a> { /// first. [`DeviceTree`](crate::model::DeviceTree) stores the nodes in a /// hash map for constant-time lookup. /// - /// # Errors + /// # Panics /// - /// Returns an [`FdtErrorKind::InvalidLength`] if the FDT structure is - /// truncated or, an [`FdtErrorKind::BadToken`] if an unexpected token is - /// encountered while searching, or an [`FdtErrorKind::InvalidString`] if a - /// node has an invalid name. + /// Panics if the [`Fdt`] structure was constructed using + /// [`Fdt::new_unchecked`] or [`Fdt::from_raw_unchecked`] and the FDT is not + /// valid. /// /// # Examples /// @@ -423,34 +531,35 @@ impl<'a> Fdt<'a> { /// # use dtoolkit::fdt::Fdt; /// # let dtb = include_bytes!("../../tests/dtb/test_traversal.dtb"); /// let fdt = Fdt::new(dtb).unwrap(); - /// let node = fdt.find_node("/a/b/c").unwrap().unwrap(); - /// assert_eq!(node.name().unwrap(), "c"); + /// let node = fdt.find_node("/a/b/c").unwrap(); + /// assert_eq!(node.name(), "c"); /// ``` /// /// ``` /// # use dtoolkit::fdt::Fdt; /// # let dtb = include_bytes!("../../tests/dtb/test_children.dtb"); /// let fdt = Fdt::new(dtb).unwrap(); - /// let node = fdt.find_node("/child2").unwrap().unwrap(); - /// assert_eq!(node.name().unwrap(), "child2@42"); - /// let node = fdt.find_node("/child2@42").unwrap().unwrap(); - /// assert_eq!(node.name().unwrap(), "child2@42"); + /// let node = fdt.find_node("/child2").unwrap(); + /// assert_eq!(node.name(), "child2@42"); + /// let node = fdt.find_node("/child2@42").unwrap(); + /// assert_eq!(node.name(), "child2@42"); /// ``` - pub fn find_node(self, path: &str) -> Result>, FdtParseError> { + #[must_use] + pub fn find_node(self, path: &str) -> Option> { if !path.starts_with('/') { - return Ok(None); + return None; } - let mut current_node = self.root()?; + let mut current_node = self.root(); if path == "/" { - return Ok(Some(current_node)); + return Some(current_node); } for component in path.split('/').filter(|s| !s.is_empty()) { - match current_node.child(component)? { + match current_node.child(component) { Some(node) => current_node = node, - None => return Ok(None), + None => return None, } } - Ok(Some(current_node)) + Some(current_node) } pub(crate) fn read_token(self, offset: usize) -> Result { @@ -505,55 +614,35 @@ impl<'a> Fdt<'a> { } } - pub(crate) fn next_sibling_offset(self, mut offset: usize) -> Result { - offset += FDT_TAGSIZE; // Skip FDT_BEGIN_NODE - - // Skip node name - offset = self.find_string_end(offset)?; - offset = Self::align_tag_offset(offset); - - // Skip properties - loop { - let token = self.read_token(offset)?; - match token { - FdtToken::Prop => { - offset += FDT_TAGSIZE; // skip FDT_PROP - offset = self.next_property_offset(offset)?; - } - FdtToken::Nop => offset += FDT_TAGSIZE, - _ => break, - } - } - - // Skip child nodes - loop { - let token = self.read_token(offset)?; - match token { - FdtToken::BeginNode => { - offset = self.next_sibling_offset(offset)?; - } - FdtToken::EndNode => { - offset += FDT_TAGSIZE; - break; - } - FdtToken::Nop => offset += FDT_TAGSIZE, - _ => {} - } - } - - Ok(offset) + pub(crate) fn next_sibling_offset(self, offset: usize) -> Result { + self.traverse_node(offset, false) } - pub(crate) fn next_property_offset(self, mut offset: usize) -> Result { + pub(crate) fn next_property_offset( + self, + offset: usize, + check_name: bool, + ) -> Result { let len = big_endian::U32::ref_from_prefix(&self.data[offset..]) .map(|(val, _)| val.get()) .map_err(|_e| FdtParseError::new(FdtErrorKind::InvalidLength, offset))? as usize; - offset += FDT_TAGSIZE; // skip value length - offset += FDT_TAGSIZE; // skip name offset - offset += len; // skip property value + let nameoff = big_endian::U32::ref_from_prefix(&self.data[offset + FDT_TAGSIZE..]) + .map(|(val, _)| val.get()) + .map_err(|_e| FdtParseError::new(FdtErrorKind::InvalidLength, offset))? + as usize; + + if check_name { + self.string(nameoff)?; + } + + let prop_offset = offset + 2 * FDT_TAGSIZE; + let end_offset = prop_offset + len; + if end_offset > self.data.len() { + return Err(FdtParseError::new(FdtErrorKind::InvalidLength, prop_offset)); + } - Ok(Self::align_tag_offset(offset)) + Ok(Self::align_tag_offset(end_offset)) } pub(crate) fn align_tag_offset(offset: usize) -> usize { @@ -565,7 +654,6 @@ impl Display for Fdt<'_> { fn fmt(&self, f: &mut Formatter) -> fmt::Result { writeln!(f, "/dts-v1/;")?; for reservation in self.memory_reservations() { - let reservation = reservation.map_err(|_| fmt::Error)?; writeln!( f, "/memreserve/ {:#x} {:#x};", @@ -574,7 +662,7 @@ impl Display for Fdt<'_> { )?; } writeln!(f)?; - let root = self.root().map_err(|_| fmt::Error)?; + let root = self.root(); root.fmt_recursive(f, 0) } } @@ -586,20 +674,23 @@ mod tests { const FDT_HEADER_OK: &[u8] = &[ 0xd0, 0x0d, 0xfe, 0xed, // magic - 0x00, 0x00, 0x00, 0x3c, // totalsize = 60 + 0x00, 0x00, 0x00, 0x48, // totalsize = 72 0x00, 0x00, 0x00, 0x38, // off_dt_struct = 56 - 0x00, 0x00, 0x00, 0x3c, // off_dt_strings = 60 + 0x00, 0x00, 0x00, 0x48, // off_dt_strings = 72 0x00, 0x00, 0x00, 0x28, // off_mem_rsvmap = 40 0x00, 0x00, 0x00, 0x11, // version = 17 0x00, 0x00, 0x00, 0x10, // last_comp_version = 16 0x00, 0x00, 0x00, 0x00, // boot_cpuid_phys = 0 0x00, 0x00, 0x00, 0x00, // size_dt_strings = 0 - 0x00, 0x00, 0x00, 0x04, // size_dt_struct = 4 + 0x00, 0x00, 0x00, 0x10, // size_dt_struct = 16 0x00, 0x00, 0x00, 0x00, // memory reservation 0x00, 0x00, 0x00, 0x00, // ... 0x00, 0x00, 0x00, 0x00, // ... 0x00, 0x00, 0x00, 0x00, // ... - 0x00, 0x00, 0x00, 0x09, // dt struct + 0x00, 0x00, 0x00, 0x01, // FDT_BEGIN_NODE + 0x00, 0x00, 0x00, 0x00, // "" + 0x00, 0x00, 0x00, 0x02, // FDT_END_NODE + 0x00, 0x00, 0x00, 0x09, // FDT_END ]; #[test] @@ -607,15 +698,15 @@ mod tests { let fdt = Fdt::new(FDT_HEADER_OK).unwrap(); let header = fdt.header(); - assert_eq!(header.totalsize(), 60); + assert_eq!(header.totalsize(), 72); assert_eq!(header.off_dt_struct(), 56); - assert_eq!(header.off_dt_strings(), 60); + assert_eq!(header.off_dt_strings(), 72); assert_eq!(header.off_mem_rsvmap(), 40); assert_eq!(header.version(), 17); assert_eq!(header.last_comp_version(), 16); assert_eq!(header.boot_cpuid_phys(), 0); assert_eq!(header.size_dt_strings(), 0); - assert_eq!(header.size_dt_struct(), 4); + assert_eq!(header.size_dt_struct(), 16); } #[test] diff --git a/src/fdt/node.rs b/src/fdt/node.rs index 7031c06..9536d36 100644 --- a/src/fdt/node.rs +++ b/src/fdt/node.rs @@ -11,7 +11,6 @@ use core::fmt::{self, Display, Formatter}; use super::{FDT_TAGSIZE, Fdt, FdtToken}; -use crate::error::FdtParseError; use crate::fdt::property::{FdtPropIter, FdtProperty}; use crate::standard::AddressSpaceProperties; @@ -36,13 +35,11 @@ impl<'a> FdtNode<'a> { /// Returns the name of this node. /// - /// # Errors + /// # Panics /// - /// Returns an - /// [`FdtErrorKind::InvalidOffset`](crate::error::FdtErrorKind::InvalidOffset) - /// if the name offset is invalid or an - /// [`FdtErrorKind::InvalidString`](crate::error::FdtErrorKind::InvalidString) if the string at the offset is not null-terminated - /// or contains invalid UTF-8. + /// Panics if the [`Fdt`] structure was constructed using + /// [`Fdt::new_unchecked`] or [`Fdt::from_raw_unchecked`] and the FDT is not + /// valid. /// /// # Examples /// @@ -50,30 +47,32 @@ impl<'a> FdtNode<'a> { /// # use dtoolkit::fdt::Fdt; /// # let dtb = include_bytes!("../../tests/dtb/test_children.dtb"); /// let fdt = Fdt::new(dtb).unwrap(); - /// let root = fdt.root().unwrap(); - /// let child = root.child("child1").unwrap().unwrap(); - /// assert_eq!(child.name().unwrap(), "child1"); + /// let root = fdt.root(); + /// let child = root.child("child1").unwrap(); + /// assert_eq!(child.name(), "child1"); /// ``` - pub fn name(&self) -> Result<&'a str, FdtParseError> { + #[must_use] + pub fn name(&self) -> &'a str { let name_offset = self.offset + FDT_TAGSIZE; - self.fdt.string_at_offset(name_offset, None) + self.fdt + .string_at_offset(name_offset, None) + .expect("Fdt should be valid") } /// Returns the name of this node without the unit address, if any. /// - /// # Errors + /// # Panics /// - /// Returns an - /// [`FdtErrorKind::InvalidOffset`](crate::error::FdtErrorKind::InvalidOffset) - /// if the name offset is invalid or an - /// [`FdtErrorKind::InvalidString`](crate::error::FdtErrorKind::InvalidString) if the string at the offset is not null-terminated - /// or contains invalid UTF-8. - pub fn name_without_address(&self) -> Result<&'a str, FdtParseError> { - let name = self.name()?; + /// Panics if the [`Fdt`] structure was constructed using + /// [`Fdt::new_unchecked`] or [`Fdt::from_raw_unchecked`] and the FDT is not + /// valid. + #[must_use] + pub fn name_without_address(&self) -> &'a str { + let name = self.name(); if let Some((name, _)) = name.split_once('@') { - Ok(name) + name } else { - Ok(name) + name } } @@ -89,22 +88,19 @@ impl<'a> FdtNode<'a> { /// # use dtoolkit::fdt::Fdt; /// # let dtb = include_bytes!("../../tests/dtb/test_props.dtb"); /// let fdt = Fdt::new(dtb).unwrap(); - /// let node = fdt.find_node("/test-props").unwrap().unwrap(); - /// let prop = node.property("u32-prop").unwrap().unwrap(); + /// let node = fdt.find_node("/test-props").unwrap(); + /// let prop = node.property("u32-prop").unwrap(); /// assert_eq!(prop.name(), "u32-prop"); /// ``` /// - /// # Errors + /// # Panics /// - /// Returns an error if a property's name or value cannot be read. - pub fn property(&self, name: &str) -> Result>, FdtParseError> { - for property in self.properties() { - let property = property?; - if property.name() == name { - return Ok(Some(property)); - } - } - Ok(None) + /// Panics if the [`Fdt`] structure was constructed using + /// [`Fdt::new_unchecked`] or [`Fdt::from_raw_unchecked`] and the FDT is not + /// valid. + #[must_use] + pub fn property(&self, name: &str) -> Option> { + self.properties().find(|property| property.name() == name) } /// Returns an iterator over the properties of this node. @@ -115,15 +111,13 @@ impl<'a> FdtNode<'a> { /// # use dtoolkit::fdt::Fdt; /// # let dtb = include_bytes!("../../tests/dtb/test_props.dtb"); /// let fdt = Fdt::new(dtb).unwrap(); - /// let node = fdt.find_node("/test-props").unwrap().unwrap(); + /// let node = fdt.find_node("/test-props").unwrap(); /// let mut props = node.properties(); - /// assert_eq!(props.next().unwrap().unwrap().name(), "u32-prop"); - /// assert_eq!(props.next().unwrap().unwrap().name(), "u64-prop"); - /// assert_eq!(props.next().unwrap().unwrap().name(), "str-prop"); + /// assert_eq!(props.next().unwrap().name(), "u32-prop"); + /// assert_eq!(props.next().unwrap().name(), "u64-prop"); + /// assert_eq!(props.next().unwrap().name(), "str-prop"); /// ``` - pub fn properties( - &self, - ) -> impl Iterator, FdtParseError>> + use<'a> { + pub fn properties(&self) -> impl Iterator> + use<'a> { FdtPropIter::Start { fdt: self.fdt, offset: self.offset, @@ -150,9 +144,11 @@ impl<'a> FdtNode<'a> { /// on a [`DeviceTreeNode`](crate::model::DeviceTreeNode) is a /// constant-time operation. /// - /// # Errors + /// # Panics /// - /// Returns an error if a child node's name cannot be read. + /// Panics if the [`Fdt`] structure was constructed using + /// [`Fdt::new_unchecked`] or [`Fdt::from_raw_unchecked`] and the FDT is not + /// valid. /// /// # Examples /// @@ -160,34 +156,31 @@ impl<'a> FdtNode<'a> { /// # use dtoolkit::fdt::Fdt; /// # let dtb = include_bytes!("../../tests/dtb/test_children.dtb"); /// let fdt = Fdt::new(dtb).unwrap(); - /// let root = fdt.root().unwrap(); - /// let child = root.child("child1").unwrap().unwrap(); - /// assert_eq!(child.name().unwrap(), "child1"); + /// let root = fdt.root(); + /// let child = root.child("child1").unwrap(); + /// assert_eq!(child.name(), "child1"); /// ``` /// /// ``` /// # use dtoolkit::fdt::Fdt; /// # let dtb = include_bytes!("../../tests/dtb/test_children.dtb"); /// let fdt = Fdt::new(dtb).unwrap(); - /// let root = fdt.root().unwrap(); - /// let child = root.child("child2").unwrap().unwrap(); - /// assert_eq!(child.name().unwrap(), "child2@42"); - /// let child = root.child("child2@42").unwrap().unwrap(); - /// assert_eq!(child.name().unwrap(), "child2@42"); + /// let root = fdt.root(); + /// let child = root.child("child2").unwrap(); + /// assert_eq!(child.name(), "child2@42"); + /// let child = root.child("child2@42").unwrap(); + /// assert_eq!(child.name(), "child2@42"); /// ``` - pub fn child(&self, name: &str) -> Result>, FdtParseError> { + #[must_use] + pub fn child(&self, name: &str) -> Option> { let include_address = name.contains('@'); - for child in self.children() { - let child = child?; - if if include_address { - child.name()? == name + self.children().find(|&child| { + if include_address { + child.name() == name } else { - child.name_without_address()? == name - } { - return Ok(Some(child)); + child.name_without_address() == name } - } - Ok(None) + }) } /// Returns an iterator over the children of this node. @@ -198,21 +191,18 @@ impl<'a> FdtNode<'a> { /// # use dtoolkit::fdt::Fdt; /// # let dtb = include_bytes!("../../tests/dtb/test_children.dtb"); /// let fdt = Fdt::new(dtb).unwrap(); - /// let root = fdt.root().unwrap(); + /// let root = fdt.root(); /// let mut children = root.children(); - /// assert_eq!(children.next().unwrap().unwrap().name().unwrap(), "child1"); - /// assert_eq!( - /// children.next().unwrap().unwrap().name().unwrap(), - /// "child2@42" - /// ); + /// assert_eq!(children.next().unwrap().name(), "child1"); + /// assert_eq!(children.next().unwrap().name(), "child2@42"); /// assert!(children.next().is_none()); /// ``` - pub fn children(&self) -> impl Iterator, FdtParseError>> + use<'a> { + pub fn children(&self) -> impl Iterator> + use<'a> { FdtChildIter::Start { node: *self } } pub(crate) fn fmt_recursive(&self, f: &mut Formatter, indent: usize) -> fmt::Result { - let name = self.name().map_err(|_| fmt::Error)?; + let name = self.name(); if name.is_empty() { writeln!(f, "{:indent$}/ {{", "", indent = indent)?; } else { @@ -222,12 +212,7 @@ impl<'a> FdtNode<'a> { let mut has_properties = false; for prop in self.properties() { has_properties = true; - match prop { - Ok(prop) => prop.fmt(f, indent + 4)?, - Err(_e) => { - writeln!(f, "")?; - } - } + prop.fmt(f, indent + 4)?; } let mut first_child = true; @@ -237,12 +222,7 @@ impl<'a> FdtNode<'a> { } first_child = false; - match child { - Ok(child) => child.fmt_recursive(f, indent + 4)?, - Err(_e) => { - writeln!(f, "")?; - } - } + child.fmt_recursive(f, indent + 4)?; } writeln!(f, "{:indent$}}};", "", indent = indent) @@ -265,28 +245,21 @@ enum FdtChildIter<'a> { offset: usize, address_space: AddressSpaceProperties, }, - Error, } impl<'a> Iterator for FdtChildIter<'a> { - type Item = Result, FdtParseError>; + type Item = FdtNode<'a>; fn next(&mut self) -> Option { match self { Self::Start { node } => { - let address_space = match node.address_space() { - Ok(value) => value, - Err(e) => return Some(Err(e)), - }; + let address_space = node.address_space(); let mut offset = node.offset; offset += FDT_TAGSIZE; // Skip FDT_BEGIN_NODE - offset = match node.fdt.find_string_end(offset) { - Ok(offset) => offset, - Err(e) => { - *self = Self::Error; - return Some(Err(e)); - } - }; + offset = node + .fdt + .find_string_end(offset) + .expect("Fdt should be valid"); offset = Fdt::align_tag_offset(offset); *self = Self::Running { fdt: node.fdt, @@ -299,15 +272,7 @@ impl<'a> Iterator for FdtChildIter<'a> { fdt, offset, address_space, - } => match Self::try_next(*fdt, offset, *address_space) { - Some(Ok(val)) => Some(Ok(val)), - Some(Err(e)) => { - *self = Self::Error; - Some(Err(e)) - } - None => None, - }, - Self::Error => None, + } => Self::try_next(*fdt, offset, *address_space), } } } @@ -317,30 +282,25 @@ impl<'a> FdtChildIter<'a> { fdt: Fdt<'a>, offset: &mut usize, parent_address_space: AddressSpaceProperties, - ) -> Option, FdtParseError>> { + ) -> Option> { loop { - let token = match fdt.read_token(*offset) { - Ok(token) => token, - Err(e) => return Some(Err(e)), - }; + let token = fdt.read_token(*offset).expect("Fdt should be valid"); match token { FdtToken::BeginNode => { let node_offset = *offset; - *offset = match fdt.next_sibling_offset(*offset) { - Ok(offset) => offset, - Err(e) => return Some(Err(e)), - }; - return Some(Ok(FdtNode { + *offset = fdt + .next_sibling_offset(*offset) + .expect("Fdt should be valid"); + return Some(FdtNode { fdt, offset: node_offset, parent_address_space, - })); + }); } FdtToken::Prop => { - *offset = match fdt.next_property_offset(*offset + FDT_TAGSIZE) { - Ok(offset) => offset, - Err(e) => return Some(Err(e)), - }; + *offset = fdt + .next_property_offset(*offset + FDT_TAGSIZE, false) + .expect("Fdt should be valid"); } FdtToken::EndNode | FdtToken::End => return None, FdtToken::Nop => *offset += FDT_TAGSIZE, diff --git a/src/fdt/property.rs b/src/fdt/property.rs index 5d2fc52..83214ed 100644 --- a/src/fdt/property.rs +++ b/src/fdt/property.rs @@ -15,7 +15,7 @@ use core::ops::{BitOr, Shl}; use zerocopy::{FromBytes, big_endian}; use super::{FDT_TAGSIZE, Fdt, FdtToken}; -use crate::error::{FdtError, FdtErrorKind, FdtParseError}; +use crate::error::{PropertyError, StandardError}; /// A property of a device tree node. #[derive(Debug, PartialEq)] @@ -42,7 +42,7 @@ impl<'a> FdtProperty<'a> { /// /// # Errors /// - /// Returns an [`FdtErrorKind::InvalidLength`] if the property's value is + /// Returns an [`PropertyError::InvalidLength`] if the property's value is /// not 4 bytes long. /// /// # Examples @@ -51,21 +51,21 @@ impl<'a> FdtProperty<'a> { /// # use dtoolkit::fdt::Fdt; /// # let dtb = include_bytes!("../../tests/dtb/test_props.dtb"); /// let fdt = Fdt::new(dtb).unwrap(); - /// let node = fdt.find_node("/test-props").unwrap().unwrap(); - /// let prop = node.property("u32-prop").unwrap().unwrap(); + /// let node = fdt.find_node("/test-props").unwrap(); + /// let prop = node.property("u32-prop").unwrap(); /// assert_eq!(prop.as_u32().unwrap(), 0x12345678); /// ``` - pub fn as_u32(&self) -> Result { + pub fn as_u32(&self) -> Result { big_endian::U32::ref_from_bytes(self.value) .map(|val| val.get()) - .map_err(|_e| FdtParseError::new(FdtErrorKind::InvalidLength, self.value_offset)) + .map_err(|_e| PropertyError::InvalidLength) } /// Returns the value of this property as a `u64`. /// /// # Errors /// - /// Returns an [`FdtErrorKind::InvalidLength`] if the property's value is + /// Returns an [`PropertyError::InvalidLength`] if the property's value is /// not 8 bytes long. /// /// # Examples @@ -74,21 +74,21 @@ impl<'a> FdtProperty<'a> { /// # use dtoolkit::fdt::Fdt; /// # let dtb = include_bytes!("../../tests/dtb/test_props.dtb"); /// let fdt = Fdt::new(dtb).unwrap(); - /// let node = fdt.find_node("/test-props").unwrap().unwrap(); - /// let prop = node.property("u64-prop").unwrap().unwrap(); + /// let node = fdt.find_node("/test-props").unwrap(); + /// let prop = node.property("u64-prop").unwrap(); /// assert_eq!(prop.as_u64().unwrap(), 0x1122334455667788); /// ``` - pub fn as_u64(&self) -> Result { + pub fn as_u64(&self) -> Result { big_endian::U64::ref_from_bytes(self.value) .map(|val| val.get()) - .map_err(|_e| FdtParseError::new(FdtErrorKind::InvalidLength, self.value_offset)) + .map_err(|_e| PropertyError::InvalidLength) } /// Returns the value of this property as a string. /// /// # Errors /// - /// Returns an [`FdtErrorKind::InvalidString`] if the property's value is + /// Returns an [`PropertyError::InvalidString`] if the property's value is /// not a null-terminated string or contains invalid UTF-8. /// /// # Examples @@ -97,15 +97,14 @@ impl<'a> FdtProperty<'a> { /// # use dtoolkit::fdt::Fdt; /// # let dtb = include_bytes!("../../tests/dtb/test_props.dtb"); /// let fdt = Fdt::new(dtb).unwrap(); - /// let node = fdt.find_node("/test-props").unwrap().unwrap(); - /// let prop = node.property("str-prop").unwrap().unwrap(); + /// let node = fdt.find_node("/test-props").unwrap(); + /// let prop = node.property("str-prop").unwrap(); /// assert_eq!(prop.as_str().unwrap(), "hello world"); /// ``` - pub fn as_str(&self) -> Result<&'a str, FdtParseError> { - let cstr = CStr::from_bytes_with_nul(self.value) - .map_err(|_| FdtParseError::new(FdtErrorKind::InvalidString, self.value_offset))?; - cstr.to_str() - .map_err(|_| FdtParseError::new(FdtErrorKind::InvalidString, self.value_offset)) + pub fn as_str(&self) -> Result<&'a str, PropertyError> { + let cstr = + CStr::from_bytes_with_nul(self.value).map_err(|_| PropertyError::InvalidString)?; + cstr.to_str().map_err(|_| PropertyError::InvalidString) } /// Returns an iterator over the strings in this property. @@ -116,8 +115,8 @@ impl<'a> FdtProperty<'a> { /// # use dtoolkit::fdt::Fdt; /// # let dtb = include_bytes!("../../tests/dtb/test_props.dtb"); /// let fdt = Fdt::new(dtb).unwrap(); - /// let node = fdt.find_node("/test-props").unwrap().unwrap(); - /// let prop = node.property("str-list-prop").unwrap().unwrap(); + /// let node = fdt.find_node("/test-props").unwrap(); + /// let prop = node.property("str-list-prop").unwrap(); /// let mut str_list = prop.as_str_list(); /// assert_eq!(str_list.next(), Some("first")); /// assert_eq!(str_list.next(), Some("second")); @@ -131,11 +130,11 @@ impl<'a> FdtProperty<'a> { pub(crate) fn as_prop_encoded_array( &self, fields_cells: [usize; N], - ) -> Result; N]> + use<'a, N>, FdtError> { + ) -> Result; N]> + use<'a, N>, StandardError> { let chunk_cells = fields_cells.iter().sum(); let chunk_bytes = chunk_cells * size_of::(); if !self.value.len().is_multiple_of(chunk_bytes) { - return Err(FdtError::PropEncodedArraySizeMismatch { + return Err(StandardError::PropEncodedArraySizeMismatch { size: self.value.len(), chunk: chunk_cells, }); @@ -215,87 +214,53 @@ impl Display for FdtProperty<'_> { pub(crate) enum FdtPropIter<'a> { Start { fdt: Fdt<'a>, offset: usize }, Running { fdt: Fdt<'a>, offset: usize }, - Error, } impl<'a> Iterator for FdtPropIter<'a> { - type Item = Result, FdtParseError>; + type Item = FdtProperty<'a>; fn next(&mut self) -> Option { match self { Self::Start { fdt, offset } => { let mut offset = *offset; offset += FDT_TAGSIZE; // Skip FDT_BEGIN_NODE - offset = match fdt.find_string_end(offset) { - Ok(offset) => offset, - Err(e) => { - *self = Self::Error; - return Some(Err(e)); - } - }; + offset = fdt.find_string_end(offset).expect("Fdt should be valid"); offset = Fdt::align_tag_offset(offset); *self = Self::Running { fdt: *fdt, offset }; self.next() } - Self::Running { fdt, offset } => match Self::try_next(*fdt, offset) { - Some(Ok(val)) => Some(Ok(val)), - Some(Err(e)) => { - *self = Self::Error; - Some(Err(e)) - } - None => None, - }, - Self::Error => None, + Self::Running { fdt, offset } => Self::next_prop(*fdt, offset), } } } impl<'a> FdtPropIter<'a> { - fn try_next( - fdt: Fdt<'a>, - offset: &mut usize, - ) -> Option, FdtParseError>> { + fn next_prop(fdt: Fdt<'a>, offset: &mut usize) -> Option> { loop { - let token = match fdt.read_token(*offset) { - Ok(token) => token, - Err(e) => return Some(Err(e)), - }; + let token = fdt.read_token(*offset).expect("Fdt should be valid"); match token { FdtToken::Prop => { - let len = match big_endian::U32::ref_from_prefix( - &fdt.data[*offset + FDT_TAGSIZE..], - ) { - Ok((val, _)) => val.get() as usize, - Err(_) => { - return Some(Err(FdtParseError::new( - FdtErrorKind::InvalidLength, - *offset, - ))); - } - }; - let nameoff = match big_endian::U32::ref_from_prefix( - &fdt.data[*offset + 2 * FDT_TAGSIZE..], - ) { - Ok((val, _)) => val.get() as usize, - Err(_) => { - return Some(Err(FdtParseError::new( - FdtErrorKind::InvalidLength, - *offset, - ))); - } - }; + let len = big_endian::U32::ref_from_prefix(&fdt.data[*offset + FDT_TAGSIZE..]) + .expect("Fdt should be valid") + .0 + .get() as usize; + let nameoff = + big_endian::U32::ref_from_prefix(&fdt.data[*offset + 2 * FDT_TAGSIZE..]) + .expect("Fdt should be valid") + .0 + .get() as usize; let prop_offset = *offset + 3 * FDT_TAGSIZE; *offset = Fdt::align_tag_offset(prop_offset + len); - let name = match fdt.string(nameoff) { - Ok(name) => name, - Err(e) => return Some(Err(e)), - }; - let value = fdt.data.get(prop_offset..prop_offset + len)?; - return Some(Ok(FdtProperty { + let name = fdt.string(nameoff).expect("Fdt should be valid"); + let value = fdt + .data + .get(prop_offset..prop_offset + len) + .expect("Fdt should be valid"); + return Some(FdtProperty { name, value, value_offset: prop_offset, - })); + }); } FdtToken::Nop => *offset += FDT_TAGSIZE, _ => return None, @@ -337,9 +302,9 @@ impl Cells<'_> { /// in the given type. pub fn to_int + Shl + BitOr>( self, - ) -> Result { + ) -> Result { if size_of::() < self.0.len() * size_of::() { - Err(FdtError::TooManyCells { + Err(StandardError::TooManyCells { cells: self.0.len(), }) } else if let [size] = self.0 { diff --git a/src/lib.rs b/src/lib.rs index c354f69..f5fd81f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -64,8 +64,8 @@ //! let fdt = Fdt::new(&dtb).unwrap(); //! //! // Find the child node and read its property. -//! let child_node = fdt.find_node("/child").unwrap().unwrap(); -//! let prop = child_node.property("my-property").unwrap().unwrap(); +//! let child_node = fdt.find_node("/child").unwrap(); +//! let prop = child_node.property("my-property").unwrap(); //! assert_eq!(prop.as_str().unwrap(), "hello"); //! //! // Display the DTS diff --git a/src/model/mod.rs b/src/model/mod.rs index 97e8e42..f721fe1 100644 --- a/src/model/mod.rs +++ b/src/model/mod.rs @@ -79,11 +79,11 @@ impl DeviceTree { /// /// Returns an error if the root node of the `Fdt` cannot be parsed. pub fn from_fdt(fdt: &Fdt<'_>) -> Result { - let root = DeviceTreeNode::try_from(fdt.root()?)?; - let memory_reservations: Result, _> = fdt.memory_reservations().collect(); + let root = DeviceTreeNode::try_from(fdt.root())?; + let memory_reservations: Vec<_> = fdt.memory_reservations().collect(); Ok(DeviceTree { root, - memory_reservations: memory_reservations?, + memory_reservations, }) } diff --git a/src/model/node.rs b/src/model/node.rs index c5d08e2..97075da 100644 --- a/src/model/node.rs +++ b/src/model/node.rs @@ -261,10 +261,10 @@ impl<'a> TryFrom> for DeviceTreeNode { type Error = FdtParseError; fn try_from(node: FdtNode<'a>) -> Result { - let name = node.name()?.to_string(); + let name = node.name().to_string(); let properties = node .properties() - .map(|property| property?.try_into()) + .map(TryInto::try_into) .collect::, _>>()?; let mut property_map = IndexMap::with_capacity_and_hasher(properties.len(), default_hash_state()); @@ -274,7 +274,7 @@ impl<'a> TryFrom> for DeviceTreeNode { let children_vec: Vec = node .children() - .map(|child| child?.try_into()) + .map(TryInto::try_into) .collect::, _>>()?; let mut children = IndexMap::with_capacity_and_hasher(children_vec.len(), default_hash_state()); diff --git a/src/model/property.rs b/src/model/property.rs index 6aab40d..6f69da3 100644 --- a/src/model/property.rs +++ b/src/model/property.rs @@ -8,31 +8,11 @@ use alloc::string::{String, ToString}; use alloc::vec::Vec; -use core::{fmt, str}; +use core::str; -use crate::error::FdtParseError; +use crate::error::{FdtParseError, PropertyError}; use crate::fdt::FdtProperty; -/// An error that can occur when parsing a property. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum PropertyError { - /// The property's value has an invalid length for the requested conversion. - InvalidLength, - /// The property's value is not a valid string. - InvalidString, -} - -impl fmt::Display for PropertyError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - PropertyError::InvalidLength => write!(f, "property has an invalid length"), - PropertyError::InvalidString => write!(f, "property is not a valid string"), - } - } -} - -impl core::error::Error for PropertyError {} - /// A mutable, in-memory representation of a device tree property. #[derive(Debug, Clone, PartialEq, Eq)] pub struct DeviceTreeProperty { diff --git a/src/standard.rs b/src/standard.rs deleted file mode 100644 index cea8e85..0000000 --- a/src/standard.rs +++ /dev/null @@ -1,265 +0,0 @@ -// Copyright 2025 Google LLC -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Standard nodes and properties. - -mod cpus; -mod memory; -mod ranges; -mod reg; -mod status; - -pub use self::cpus::{Cpu, Cpus}; -pub use self::memory::{InitialMappedArea, Memory}; -pub use self::ranges::Range; -pub use self::reg::Reg; -pub use self::status::Status; -use crate::error::{FdtError, FdtParseError}; -use crate::fdt::FdtNode; - -pub(crate) const DEFAULT_ADDRESS_CELLS: u32 = 2; -pub(crate) const DEFAULT_SIZE_CELLS: u32 = 1; - -impl<'a> FdtNode<'a> { - /// Returns the value of the standard `compatible` property. - /// - /// # Errors - /// - /// Returns an error if a property's name or value cannot be read. - pub fn compatible( - &self, - ) -> Result + use<'a>>, FdtParseError> { - Ok(self - .property("compatible")? - .map(|property| property.as_str_list())) - } - - /// Returns whether this node has a `compatible` properties containing the - /// given string. - /// - /// # Errors - /// - /// Returns an error if a property's name or value cannot be read. - pub fn is_compatible(&self, compatible_filter: &str) -> Result { - Ok(if let Some(mut compatible) = self.compatible()? { - compatible.any(|c| c == compatible_filter) - } else { - false - }) - } - - /// Finds all child nodes with a `compatible` property containing the given - /// string. - pub fn find_compatible<'f>( - &self, - compatible_filter: &'f str, - ) -> impl Iterator, FdtParseError>> + use<'a, 'f> { - self.children().filter_map(|child| match child { - Ok(child) => match child.is_compatible(compatible_filter) { - Ok(true) => Some(Ok(child)), - Ok(false) => None, - Err(e) => Some(Err(e)), - }, - Err(e) => Some(Err(e)), - }) - } - - /// Returns the value of the standard `model` property. - /// - /// # Errors - /// - /// Returns an error if a property's name or value cannot be read, or the - /// value isn't a valid UTF-8 string. - pub fn model(&self) -> Result, FdtParseError> { - Ok(if let Some(model) = self.property("model")? { - Some(model.as_str()?) - } else { - None - }) - } - - /// Returns the value of the standard `phandle` property. - /// - /// # Errors - /// - /// Returns an error if a property's name or value cannot be read, or the - /// value isn't a valid u32. - pub fn phandle(&self) -> Result, FdtParseError> { - Ok(if let Some(property) = self.property("phandle")? { - Some(property.as_u32()?) - } else { - None - }) - } - - /// Returns the value of the standard `status` property. - /// - /// If there is no `status` property then `okay` is assumed. - /// - /// # Errors - /// - /// Returns an error if a property's name or value cannot be read, or the - /// value isn't a valid status. - pub fn status(&self) -> Result { - Ok(if let Some(status) = self.property("status")? { - status.as_str()?.parse()? - } else { - Status::Okay - }) - } - - /// Returns the value of the standard `#address-cells` property. - /// - /// # Errors - /// - /// Returns an error if a property's name or value cannot be read, or the - /// value isn't a valid u32. - pub fn address_cells(&self) -> Result { - Ok(if let Some(property) = self.property("#address-cells")? { - property.as_u32()? - } else { - DEFAULT_ADDRESS_CELLS - }) - } - - /// Returns the value of the standard `#size-cells` property. - /// - /// # Errors - /// - /// Returns an error if a property's name or value cannot be read, or the - /// value isn't a valid u32. - pub fn size_cells(&self) -> Result { - Ok(if let Some(model) = self.property("#size-cells")? { - model.as_u32()? - } else { - DEFAULT_SIZE_CELLS - }) - } - - /// Returns the values of the standard `#address-cells` and `#size_cells` - /// properties. - /// - /// # Errors - /// - /// Returns an error if a property's name or value cannot be read, or the - /// values aren't valid u32s. - pub fn address_space(&self) -> Result { - Ok(AddressSpaceProperties { - address_cells: self.address_cells()?, - size_cells: self.size_cells()?, - }) - } - - /// Returns the value of the standard `reg` property. - /// - /// # Errors - /// - /// Returns an error if a property's name or value cannot be read, or the - /// size of the value isn't a multiple of the expected number of address and - /// size cells. - pub fn reg(&self) -> Result> + use<'a>>, FdtError> { - let address_cells = self.parent_address_space.address_cells as usize; - let size_cells = self.parent_address_space.size_cells as usize; - Ok(if let Some(property) = self.property("reg")? { - Some( - property - .as_prop_encoded_array([address_cells, size_cells])? - .map(Reg::from_cells), - ) - } else { - None - }) - } - - /// Returns the value of the standard `virtual-reg` property. - /// - /// # Errors - /// - /// Returns an error if a property's name or value cannot be read, or the - /// value isn't a valid u32. - pub fn virtual_reg(&self) -> Result, FdtParseError> { - Ok(if let Some(property) = self.property("virtual-reg")? { - Some(property.as_u32()?) - } else { - None - }) - } - - /// Returns the value of the standard `ranges` property. - /// - /// # Errors - /// - /// Returns an error if a property's name or value cannot be read, or the - /// size of the value isn't a multiple of the expected number of cells. - pub fn ranges(&self) -> Result> + use<'a>>, FdtError> { - Ok(if let Some(property) = self.property("ranges")? { - Some( - property - .as_prop_encoded_array([ - self.address_cells()? as usize, - self.parent_address_space.address_cells as usize, - self.size_cells()? as usize, - ])? - .map(Range::from_cells), - ) - } else { - None - }) - } - - /// Returns the value of the standard `dma-ranges` property. - /// - /// # Errors - /// - /// Returns an error if a property's name or value cannot be read, or the - /// size of the value isn't a multiple of the expected number of cells. - pub fn dma_ranges( - &self, - ) -> Result> + use<'a>>, FdtError> { - Ok(if let Some(property) = self.property("dma-ranges")? { - Some( - property - .as_prop_encoded_array([ - self.address_cells()? as usize, - self.parent_address_space.address_cells as usize, - self.size_cells()? as usize, - ])? - .map(Range::from_cells), - ) - } else { - None - }) - } - - /// Returns whether the standard `dma-coherent` property is present. - /// - /// # Errors - /// - /// Returns an error if a property can't be read. - pub fn dma_coherent(&self) -> Result { - Ok(self.property("dma-coherent")?.is_some()) - } -} - -/// The `#address-cells` and `#size-cells` properties of a node. -#[derive(Debug, Clone, Copy)] -pub struct AddressSpaceProperties { - /// The `#address-cells` property. - pub address_cells: u32, - /// The `#size-cells` property. - pub size_cells: u32, -} - -impl Default for AddressSpaceProperties { - fn default() -> Self { - Self { - address_cells: DEFAULT_ADDRESS_CELLS, - size_cells: DEFAULT_SIZE_CELLS, - } - } -} diff --git a/src/standard/cpus.rs b/src/standard/cpus.rs index 45d32c2..af7c512 100644 --- a/src/standard/cpus.rs +++ b/src/standard/cpus.rs @@ -9,7 +9,7 @@ use core::fmt::{self, Display, Formatter}; use core::ops::Deref; -use crate::error::{FdtError, FdtParseError}; +use crate::error::StandardError; use crate::fdt::{Cells, Fdt, FdtNode}; impl<'a> Fdt<'a> { @@ -22,8 +22,8 @@ impl<'a> Fdt<'a> { /// Returns a parse error if there was a problem reading the FDT structure /// to find the node, or `FdtError::CpusMissing` if the CPUs node is /// missing. - pub fn cpus(self) -> Result, FdtError> { - let node = self.find_node("/cpus")?.ok_or(FdtError::CpusMissing)?; + pub fn cpus(self) -> Result, StandardError> { + let node = self.find_node("/cpus").ok_or(StandardError::CpusMissing)?; Ok(Cpus { node }) } } @@ -50,18 +50,10 @@ impl Display for Cpus<'_> { impl<'a> Cpus<'a> { /// Returns an iterator over the `/cpus/cpu@*` nodes. - pub fn cpus(&self) -> impl Iterator, FdtParseError>> + use<'a> { + pub fn cpus(&self) -> impl Iterator> + use<'a> { self.node.children().filter_map(|child| { - let child = match child { - Ok(child) => child, - Err(e) => return Some(Err(e)), - }; - let name = match child.name_without_address() { - Ok(name) => name, - Err(e) => return Some(Err(e)), - }; - if name == "cpu" { - Some(Ok(Cpu { node: child })) + if child.name_without_address() == "cpu" { + Some(Cpu { node: child }) } else { None } @@ -98,10 +90,10 @@ impl<'a> Cpu<'a> { /// Returns an error if a property's name or value cannot be read, or the /// `reg` property is missing, or the size of the value isn't a multiple of /// the expected number of address and size cells. - pub fn ids(&self) -> Result> + use<'a>, FdtError> { + pub fn ids(&self) -> Result> + use<'a>, StandardError> { Ok(self .reg()? - .ok_or(FdtError::CpuMissingReg)? + .ok_or(StandardError::CpuMissingReg)? .map(|reg| reg.address)) } } diff --git a/src/standard/memory.rs b/src/standard/memory.rs index 5281655..804dcf3 100644 --- a/src/standard/memory.rs +++ b/src/standard/memory.rs @@ -9,7 +9,7 @@ use core::fmt::{self, Display, Formatter}; use core::ops::Deref; -use crate::error::FdtError; +use crate::error::StandardError; use crate::fdt::{Cells, Fdt, FdtNode}; impl<'a> Fdt<'a> { @@ -22,8 +22,10 @@ impl<'a> Fdt<'a> { /// Returns a parse error if there was a problem reading the FDT structure /// to find the node, or `FdtError::MemoryMissing` if the memory node is /// missing. - pub fn memory(self) -> Result, FdtError> { - let node = self.find_node("/memory")?.ok_or(FdtError::MemoryMissing)?; + pub fn memory(self) -> Result, StandardError> { + let node = self + .find_node("/memory") + .ok_or(StandardError::MemoryMissing)?; Ok(Memory { node }) } } @@ -58,28 +60,23 @@ impl<'a> Memory<'a> { /// size of the value isn't a multiple of 5 cells. pub fn initial_mapped_area( &self, - ) -> Result + use<'a>>, FdtError> { - Ok( - if let Some(property) = self.node.property("initial-mapped-area")? { - Some( - property - .as_prop_encoded_array([2, 2, 1])? - .map(|chunk| InitialMappedArea::from_cells(chunk)), - ) - } else { - None - }, - ) + ) -> Result + use<'a>>, StandardError> { + if let Some(property) = self.node.property("initial-mapped-area") { + Ok(Some( + property + .as_prop_encoded_array([2, 2, 1])? + .map(|chunk| InitialMappedArea::from_cells(chunk)), + )) + } else { + Ok(None) + } } /// Returns the value of the standard `hotpluggable` property of the memory /// node. - /// - /// # Errors - /// - /// Returns an error if a property's name or value cannot be read. - pub fn hotpluggable(&self) -> Result { - Ok(self.node.property("hotpluggable")?.is_some()) + #[must_use] + pub fn hotpluggable(&self) -> bool { + self.node.property("hotpluggable").is_some() } } diff --git a/src/standard/mod.rs b/src/standard/mod.rs new file mode 100644 index 0000000..eacad83 --- /dev/null +++ b/src/standard/mod.rs @@ -0,0 +1,238 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Standard nodes and properties. + +mod cpus; +mod memory; +mod ranges; +mod reg; +mod status; + +pub use self::cpus::{Cpu, Cpus}; +pub use self::memory::{InitialMappedArea, Memory}; +pub use self::ranges::Range; +pub use self::reg::Reg; +pub use self::status::Status; +use crate::error::StandardError; +use crate::fdt::FdtNode; + +pub(crate) const DEFAULT_ADDRESS_CELLS: u32 = 2; +pub(crate) const DEFAULT_SIZE_CELLS: u32 = 1; + +impl<'a> FdtNode<'a> { + /// Returns the value of the standard `compatible` property. + #[must_use] + pub fn compatible(&self) -> Option + use<'a>> { + self.property("compatible") + .map(|property| property.as_str_list()) + } + + /// Returns whether this node has a `compatible` properties containing the + /// given string. + #[must_use] + pub fn is_compatible(&self, compatible_filter: &str) -> bool { + if let Some(mut compatible) = self.compatible() { + compatible.any(|c| c == compatible_filter) + } else { + false + } + } + + /// Finds all child nodes with a `compatible` property containing the given + /// string. + pub fn find_compatible<'f>( + &self, + compatible_filter: &'f str, + ) -> impl Iterator> + use<'a, 'f> { + self.children() + .filter(move |child| child.is_compatible(compatible_filter)) + } + + /// Returns the value of the standard `model` property. + /// + /// # Errors + /// + /// Returns an error if the value isn't a valid UTF-8 string. + pub fn model(&self) -> Result, StandardError> { + if let Some(model) = self.property("model") { + Ok(Some(model.as_str()?)) + } else { + Ok(None) + } + } + + /// Returns the value of the standard `phandle` property. + /// + /// # Errors + /// + /// Returns an error if the value isn't a valid u32. + pub fn phandle(&self) -> Result, StandardError> { + if let Some(property) = self.property("phandle") { + Ok(Some(property.as_u32()?)) + } else { + Ok(None) + } + } + + /// Returns the value of the standard `status` property. + /// + /// If there is no `status` property then `okay` is assumed. + /// + /// # Errors + /// + /// Returns an error if the value isn't a valid status. + pub fn status(&self) -> Result { + if let Some(status) = self.property("status") { + Ok(status.as_str()?.parse()?) + } else { + Ok(Status::Okay) + } + } + + /// Returns the value of the standard `#address-cells` property. + /// + /// # Errors + /// + /// Returns an error if the value isn't a valid u32. + pub fn address_cells(&self) -> Result { + if let Some(property) = self.property("#address-cells") { + Ok(property.as_u32()?) + } else { + Ok(DEFAULT_ADDRESS_CELLS) + } + } + + /// Returns the value of the standard `#size-cells` property. + /// + /// # Errors + /// + /// Returns an error if the value isn't a valid u32. + pub fn size_cells(&self) -> Result { + if let Some(model) = self.property("#size-cells") { + Ok(model.as_u32()?) + } else { + Ok(DEFAULT_SIZE_CELLS) + } + } + + /// Returns the values of the standard `#address-cells` and `#size_cells` + /// properties. + #[must_use] + pub fn address_space(&self) -> AddressSpaceProperties { + AddressSpaceProperties { + address_cells: self.address_cells().unwrap_or(DEFAULT_ADDRESS_CELLS), + size_cells: self.size_cells().unwrap_or(DEFAULT_SIZE_CELLS), + } + } + + /// Returns the value of the standard `reg` property. + /// + /// # Errors + /// + /// Returns an error if the size of the value isn't a multiple of the + /// expected number of address and size cells. + pub fn reg(&self) -> Result> + use<'a>>, StandardError> { + let address_cells = self.parent_address_space.address_cells as usize; + let size_cells = self.parent_address_space.size_cells as usize; + if let Some(property) = self.property("reg") { + Ok(Some( + property + .as_prop_encoded_array([address_cells, size_cells])? + .map(Reg::from_cells), + )) + } else { + Ok(None) + } + } + + /// Returns the value of the standard `virtual-reg` property. + /// + /// # Errors + /// + /// Returns an error if the value isn't a valid u32. + pub fn virtual_reg(&self) -> Result, StandardError> { + if let Some(property) = self.property("virtual-reg") { + Ok(Some(property.as_u32()?)) + } else { + Ok(None) + } + } + + /// Returns the value of the standard `ranges` property. + /// + /// # Errors + /// + /// Returns an error if the size of the value isn't a multiple of the + /// expected number of cells. + pub fn ranges( + &self, + ) -> Result> + use<'a>>, StandardError> { + if let Some(property) = self.property("ranges") { + Ok(Some( + property + .as_prop_encoded_array([ + self.address_cells().unwrap_or(DEFAULT_ADDRESS_CELLS) as usize, + self.parent_address_space.address_cells as usize, + self.size_cells().unwrap_or(DEFAULT_SIZE_CELLS) as usize, + ])? + .map(Range::from_cells), + )) + } else { + Ok(None) + } + } + + /// Returns the value of the standard `dma-ranges` property. + /// + /// # Errors + /// + /// Returns an error if the size of the value isn't a multiple of the + /// expected number of cells. + pub fn dma_ranges( + &self, + ) -> Result> + use<'a>>, StandardError> { + if let Some(property) = self.property("dma-ranges") { + Ok(Some( + property + .as_prop_encoded_array([ + self.address_cells().unwrap_or(DEFAULT_ADDRESS_CELLS) as usize, + self.parent_address_space.address_cells as usize, + self.size_cells().unwrap_or(DEFAULT_SIZE_CELLS) as usize, + ])? + .map(Range::from_cells), + )) + } else { + Ok(None) + } + } + + /// Returns whether the standard `dma-coherent` property is present. + #[must_use] + pub fn dma_coherent(&self) -> bool { + self.property("dma-coherent").is_some() + } +} + +/// The `#address-cells` and `#size-cells` properties of a node. +#[derive(Debug, Clone, Copy)] +pub struct AddressSpaceProperties { + /// The `#address-cells` property. + pub address_cells: u32, + /// The `#size-cells` property. + pub size_cells: u32, +} + +impl Default for AddressSpaceProperties { + fn default() -> Self { + Self { + address_cells: DEFAULT_ADDRESS_CELLS, + size_cells: DEFAULT_SIZE_CELLS, + } + } +} diff --git a/src/standard/ranges.rs b/src/standard/ranges.rs index 14b000b..a1adbd4 100644 --- a/src/standard/ranges.rs +++ b/src/standard/ranges.rs @@ -9,7 +9,7 @@ use core::fmt::{self, Display, Formatter}; use core::ops::{BitOr, Shl}; -use crate::error::FdtError; +use crate::error::StandardError; use crate::fdt::Cells; /// One of the values of a `ranges` property. @@ -45,7 +45,7 @@ impl<'a> Range<'a> { T: Default + From + Shl + BitOr, >( &self, - ) -> Result { + ) -> Result { self.child_bus_address.to_int() } @@ -60,7 +60,7 @@ impl<'a> Range<'a> { T: Default + From + Shl + BitOr, >( &self, - ) -> Result { + ) -> Result { self.parent_bus_address.to_int() } @@ -71,7 +71,7 @@ impl<'a> Range<'a> { /// Returns `FdtError::TooManyCells` if the length doesn't fit in `T`. pub fn length + Shl + BitOr>( &self, - ) -> Result { + ) -> Result { self.length.to_int() } } diff --git a/src/standard/reg.rs b/src/standard/reg.rs index 5d24390..5c223ba 100644 --- a/src/standard/reg.rs +++ b/src/standard/reg.rs @@ -9,7 +9,7 @@ use core::fmt::{self, Debug, Display, Formatter}; use core::ops::{BitOr, Shl}; -use crate::error::FdtError; +use crate::error::StandardError; use crate::fdt::Cells; /// The value of a `reg` property. @@ -33,7 +33,7 @@ impl<'a> Reg<'a> { /// Returns `FdtError::TooManyCells` if the address doesn't fit in `T`. pub fn address + Shl + BitOr>( self, - ) -> Result { + ) -> Result { self.address.to_int() } @@ -44,7 +44,7 @@ impl<'a> Reg<'a> { /// Returns `FdtError::TooManyCells` if the size doesn't fit in `T`. pub fn size + Shl + BitOr>( self, - ) -> Result { + ) -> Result { self.size.to_int() } } @@ -90,7 +90,7 @@ mod tests { }; assert_eq!( reg.address::(), - Err(FdtError::TooManyCells { cells: 2 }) + Err(StandardError::TooManyCells { cells: 2 }) ); assert_eq!(reg.address::(), Ok(0x1234_5678_abcd_0000)); assert_eq!(reg.size::(), Ok(0x1122_3344)); diff --git a/src/standard/status.rs b/src/standard/status.rs index cc7ab6d..a9f356f 100644 --- a/src/standard/status.rs +++ b/src/standard/status.rs @@ -9,7 +9,7 @@ use core::fmt::{self, Display, Formatter}; use core::str::FromStr; -use crate::error::FdtError; +use crate::error::StandardError; /// The value of a `status` property. #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] @@ -47,7 +47,7 @@ impl Display for Status { } impl FromStr for Status { - type Err = FdtError; + type Err = StandardError; fn from_str(s: &str) -> Result { match s { @@ -56,7 +56,7 @@ impl FromStr for Status { "reserved" => Ok(Self::Reserved), "fail" => Ok(Self::Fail), "fail-sss" => Ok(Self::FailSss), - _ => Err(FdtError::InvalidStatus), + _ => Err(StandardError::InvalidStatus), } } } diff --git a/tests/fdt.rs b/tests/fdt.rs index 1c29193..39bdb57 100644 --- a/tests/fdt.rs +++ b/tests/fdt.rs @@ -15,16 +15,16 @@ use dtoolkit::standard::{InitialMappedArea, Status}; fn read_child_nodes() { let dtb = include_bytes!("dtb/test_children.dtb"); let fdt = Fdt::new(dtb).unwrap(); - let root = fdt.root().unwrap(); + let root = fdt.root(); let mut children = root.children(); - let child1 = children.next().unwrap().unwrap(); - assert_eq!(child1.name().unwrap(), "child1"); - assert_eq!(child1.name_without_address().unwrap(), "child1"); + let child1 = children.next().unwrap(); + assert_eq!(child1.name(), "child1"); + assert_eq!(child1.name_without_address(), "child1"); - let child3 = children.next().unwrap().unwrap(); - assert_eq!(child3.name().unwrap(), "child2@42"); - assert_eq!(child3.name_without_address().unwrap(), "child2"); + let child3 = children.next().unwrap(); + assert_eq!(child3.name(), "child2@42"); + assert_eq!(child3.name_without_address(), "child2"); assert!(children.next().is_none()); } @@ -34,8 +34,8 @@ fn name_outlives_fdt_and_node() { let dtb = include_bytes!("dtb/test_children.dtb"); let name = { let fdt = Fdt::new(dtb).unwrap(); - let child1 = fdt.find_node("/child1").unwrap().unwrap(); - child1.name().unwrap() + let child1 = fdt.find_node("/child1").unwrap(); + child1.name() }; assert_eq!(name, "child1"); @@ -45,26 +45,26 @@ fn name_outlives_fdt_and_node() { fn read_prop_values() { let dtb = include_bytes!("dtb/test_props.dtb"); let fdt = Fdt::new(dtb).unwrap(); - let root = fdt.root().unwrap(); + let root = fdt.root(); let mut children = root.children(); - let node = children.next().unwrap().unwrap(); - assert_eq!(node.name().unwrap(), "test-props"); + let node = children.next().unwrap(); + assert_eq!(node.name(), "test-props"); let mut props = node.properties(); - let prop = props.next().unwrap().unwrap(); + let prop = props.next().unwrap(); assert_eq!(prop.name(), "u32-prop"); assert_eq!(prop.as_u32().unwrap(), 0x1234_5678); - let prop = props.next().unwrap().unwrap(); + let prop = props.next().unwrap(); assert_eq!(prop.name(), "u64-prop"); assert_eq!(prop.as_u64().unwrap(), 0x1122_3344_5566_7788); - let prop = props.next().unwrap().unwrap(); + let prop = props.next().unwrap(); assert_eq!(prop.name(), "str-prop"); assert_eq!(prop.as_str().unwrap(), "hello world"); - let prop = props.next().unwrap().unwrap(); + let prop = props.next().unwrap(); assert_eq!(prop.name(), "str-list-prop"); let mut str_list = prop.as_str_list(); assert_eq!(str_list.next(), Some("first")); @@ -79,53 +79,52 @@ fn read_prop_values() { fn get_property_by_name() { let dtb = include_bytes!("dtb/test_props.dtb"); let fdt = Fdt::new(dtb).unwrap(); - let root = fdt.root().unwrap(); - let node = root.child("test-props").unwrap().unwrap(); + let root = fdt.root(); + let node = root.child("test-props").unwrap(); - let prop = node.property("u32-prop").unwrap().unwrap(); + let prop = node.property("u32-prop").unwrap(); assert_eq!(prop.name(), "u32-prop"); assert_eq!(prop.as_u32().unwrap(), 0x1234_5678); - let prop = node.property("str-prop").unwrap().unwrap(); + let prop = node.property("str-prop").unwrap(); assert_eq!(prop.name(), "str-prop"); assert_eq!(prop.as_str().unwrap(), "hello world"); - assert!(node.property("non-existent-prop").unwrap().is_none()); + assert!(node.property("non-existent-prop").is_none()); } #[test] fn standard_properties() { let dtb = include_bytes!("dtb/test_props.dtb"); let fdt = Fdt::new(dtb).unwrap(); - let root = fdt.root().unwrap(); - let test_props_node = root.child("test-props").unwrap().unwrap(); - let standard_props_node = root.child("standard-props").unwrap().unwrap(); + let root = fdt.root(); + let test_props_node = root.child("test-props").unwrap(); + let standard_props_node = root.child("standard-props").unwrap(); // Default values. assert_eq!(test_props_node.address_cells().unwrap(), 2); assert_eq!(test_props_node.size_cells().unwrap(), 1); assert_eq!(test_props_node.status().unwrap(), Status::Okay); assert_eq!(test_props_node.model().unwrap(), None); - assert!(!test_props_node.dma_coherent().unwrap()); + assert!(!test_props_node.dma_coherent()); assert_eq!(test_props_node.phandle().unwrap(), None); assert_eq!(test_props_node.virtual_reg().unwrap(), None); assert!(test_props_node.ranges().unwrap().is_none()); assert!(test_props_node.dma_ranges().unwrap().is_none()); - assert!(test_props_node.compatible().unwrap().is_none()); + assert!(test_props_node.compatible().is_none()); // Explicit values. assert_eq!(standard_props_node.address_cells().unwrap(), 1); assert_eq!(standard_props_node.size_cells().unwrap(), 1); assert_eq!(standard_props_node.status().unwrap(), Status::Fail); assert_eq!(standard_props_node.model().unwrap(), Some("Some Model")); - assert!(standard_props_node.dma_coherent().unwrap()); + assert!(standard_props_node.dma_coherent()); assert_eq!(standard_props_node.phandle().unwrap(), Some(0x1234)); assert_eq!(standard_props_node.virtual_reg().unwrap(), Some(0xabcd)); assert_eq!( standard_props_node .compatible() .unwrap() - .unwrap() .collect::>(), vec!["abc,def", "some,other"] ); @@ -172,41 +171,38 @@ fn standard_properties() { fn get_child_by_name() { let dtb = include_bytes!("dtb/test_children.dtb"); let fdt = Fdt::new(dtb).unwrap(); - let root = fdt.root().unwrap(); + let root = fdt.root(); - let child1 = root.child("child1").unwrap().unwrap(); - assert_eq!(child1.name().unwrap(), "child1"); + let child1 = root.child("child1").unwrap(); + assert_eq!(child1.name(), "child1"); - let child2 = root.child("child2").unwrap().unwrap(); - assert_eq!(child2.name().unwrap(), "child2@42"); + let child2 = root.child("child2").unwrap(); + assert_eq!(child2.name(), "child2@42"); - let child2_with_address = root.child("child2@42").unwrap().unwrap(); - assert_eq!(child2_with_address.name().unwrap(), "child2@42"); + let child2_with_address = root.child("child2@42").unwrap(); + assert_eq!(child2_with_address.name(), "child2@42"); - assert!(root.child("non-existent-child").unwrap().is_none()); + assert!(root.child("non-existent-child").is_none()); } #[test] fn children_nested() { let dtb = include_bytes!("dtb/test_children_nested.dtb"); let fdt = Fdt::new(dtb).unwrap(); - let root = fdt.root().unwrap(); + let root = fdt.root(); for child in root.children() { - println!("{}", child.unwrap().name().unwrap()); + println!("{}", child.name()); } - let children_names: Vec<_> = root - .children() - .map(|child| child.unwrap().name().unwrap()) - .collect(); + let children_names: Vec<_> = root.children().map(|child| child.name()).collect(); assert_eq!(children_names, vec!["child1", "child3"]); - let child1 = root.child("child1").unwrap().unwrap(); - let child2 = child1.child("child2").unwrap().unwrap(); + let child1 = root.child("child1").unwrap(); + let child2 = child1.child("child2").unwrap(); let nested_properties: Vec<_> = child2 .properties() - .map(|prop| prop.unwrap().name().to_owned()) + .map(|prop| prop.name().to_owned()) .collect(); assert_eq!(nested_properties, vec!["prop2"]); } @@ -216,24 +212,24 @@ fn find_node_by_path() { let dtb = include_bytes!("dtb/test_traversal.dtb"); let fdt = Fdt::new(dtb).unwrap(); - let root = fdt.find_node("/").unwrap().unwrap(); - assert_eq!(root.name().unwrap(), ""); + let root = fdt.find_node("/").unwrap(); + assert_eq!(root.name(), ""); - let a = fdt.find_node("/a").unwrap().unwrap(); - assert_eq!(a.name().unwrap(), "a"); + let a = fdt.find_node("/a").unwrap(); + assert_eq!(a.name(), "a"); - let b = fdt.find_node("/a/b").unwrap().unwrap(); - assert_eq!(b.name().unwrap(), "b"); + let b = fdt.find_node("/a/b").unwrap(); + assert_eq!(b.name(), "b"); - let c = fdt.find_node("/a/b/c").unwrap().unwrap(); - assert_eq!(c.name().unwrap(), "c"); + let c = fdt.find_node("/a/b/c").unwrap(); + assert_eq!(c.name(), "c"); - let d = fdt.find_node("/d").unwrap().unwrap(); - assert_eq!(d.name().unwrap(), "d"); + let d = fdt.find_node("/d").unwrap(); + assert_eq!(d.name(), "d"); - assert!(fdt.find_node("/a/c").unwrap().is_none()); - assert!(fdt.find_node("/x").unwrap().is_none()); - assert!(fdt.find_node("").unwrap().is_none()); + assert!(fdt.find_node("/a/c").is_none()); + assert!(fdt.find_node("/x").is_none()); + assert!(fdt.find_node("").is_none()); } #[test] @@ -246,7 +242,7 @@ fn memory() { assert_eq!(reg.len(), 1); assert_eq!(reg[0].address::().unwrap(), 0x8000_0000); assert_eq!(reg[0].size::().unwrap(), 0x2000_0000); - assert!(memory.hotpluggable().unwrap()); + assert!(memory.hotpluggable()); assert_eq!( memory .initial_mapped_area() @@ -297,19 +293,31 @@ fn pretty_print() { #[test] #[cfg(feature = "write")] fn round_trip() { - for (dtb, _dts, name) in ALL_DT_FILES { - let fdt = Fdt::new(dtb).unwrap(); - let ir = DeviceTree::from_fdt(&fdt).unwrap(); - let new_dtb = ir.to_dtb(); - assert_eq!(dtb.to_vec(), new_dtb, "Mismatch for {name}"); - } + round_trip_impl(|dtb| Fdt::new(dtb).unwrap()); +} + +#[test] +#[cfg(feature = "write")] +fn round_trip_unchecked() { + round_trip_impl(|dtb| Fdt::new_unchecked(dtb)); } #[test] #[cfg(feature = "write")] fn round_trip_raw() { + round_trip_impl(|dtb| unsafe { Fdt::from_raw(dtb.as_ptr()).unwrap() }); +} + +#[test] +#[cfg(feature = "write")] +fn round_trip_raw_unchecked() { + round_trip_impl(|dtb| unsafe { Fdt::from_raw_unchecked(dtb.as_ptr()) }); +} + +#[cfg(feature = "write")] +fn round_trip_impl(construct_fdt: impl Fn(&[u8]) -> Fdt) { for (dtb, _dts, name) in ALL_DT_FILES { - let fdt = unsafe { Fdt::from_raw(dtb.as_ptr()).unwrap() }; + let fdt = construct_fdt(*dtb); let ir = DeviceTree::from_fdt(&fdt).unwrap(); let new_dtb = ir.to_dtb(); assert_eq!(dtb.to_vec(), new_dtb, "Mismatch for {name}"); diff --git a/tests/memreserve.rs b/tests/memreserve.rs index 4409a5c..40d40ab 100644 --- a/tests/memreserve.rs +++ b/tests/memreserve.rs @@ -7,22 +7,18 @@ // except according to those terms. use dtoolkit::fdt::Fdt; -use dtoolkit::memreserve::MemoryReservation; #[test] fn memreserve() { let dtb = include_bytes!("dtb/test_memreserve.dtb"); let fdt = Fdt::new(dtb).unwrap(); - let reservations: Result, _> = fdt.memory_reservations().collect(); - let reservations = reservations.unwrap(); - assert_eq!( - reservations, - &[ - MemoryReservation::new(0x1000, 0x100), - MemoryReservation::new(0x2000, 0x200) - ] - ); + let reservations: Vec<_> = fdt.memory_reservations().collect(); + assert_eq!(reservations.len(), 2); + assert_eq!(reservations[0].address(), 0x1000); + assert_eq!(reservations[0].size(), 0x100); + assert_eq!(reservations[1].address(), 0x2000); + assert_eq!(reservations[1].size(), 0x200); let dts = fdt.to_string(); assert!(dts.contains("/memreserve/ 0x1000 0x100;"));