diff --git a/src/lib.rs b/src/lib.rs index 140d673..290098f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -258,6 +258,19 @@ pub trait Property<'a>: Sized { .map_err(|_| PropertyError::InvalidLength) } + /// Returns the value of this property as a slide of 32-bit cells. + /// + /// # Errors + /// + /// Returns an error if the value of the property isn't a multiple of 4 + /// bytes long. + fn as_cells(&self) -> Result, PropertyError> { + Ok(Cells( + <[big_endian::U32]>::ref_from_bytes(self.value()) + .map_err(|_| PropertyError::InvalidLength)?, + )) + } + /// Returns the value of this property as a string. /// /// # Errors diff --git a/src/standard.rs b/src/standard.rs index 72eb9ed..6834226 100644 --- a/src/standard.rs +++ b/src/standard.rs @@ -8,14 +8,16 @@ //! Standard nodes and properties. +mod chosen; mod cpus; mod memory; mod ranges; mod reg; mod status; +pub use self::chosen::Chosen; pub use self::cpus::{Cpu, Cpus}; -pub use self::memory::{InitialMappedArea, Memory}; +pub use self::memory::{InitialMappedArea, Memory, ReservedMemory}; pub use self::ranges::Range; pub use self::reg::Reg; pub use self::status::Status; diff --git a/src/standard/chosen.rs b/src/standard/chosen.rs new file mode 100644 index 0000000..1410f56 --- /dev/null +++ b/src/standard/chosen.rs @@ -0,0 +1,84 @@ +// Copyright 2026 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. + +use core::fmt::{self, Display, Formatter}; +use core::ops::Deref; + +use crate::error::PropertyError; +use crate::fdt::{Fdt, FdtNode}; +use crate::{Node, Property}; + +impl<'a> Fdt<'a> { + /// Returns the `/chosen` node, if it exists. + #[must_use] + pub fn chosen(self) -> Option>> { + let node = self.find_node("/chosen")?; + Some(Chosen { node }) + } +} + +/// Typed wrapper for a `/chosen` node. +#[derive(Clone, Copy, Debug)] +pub struct Chosen { + node: N, +} + +impl Deref for Chosen { + type Target = N; + + fn deref(&self) -> &Self::Target { + &self.node + } +} + +impl Display for Chosen { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + self.node.fmt(f) + } +} + +impl<'a, N: Node<'a>> Chosen { + /// Returns the value of the standard `bootargs` property. + /// + /// # Errors + /// + /// Returns an [`PropertyError::InvalidString`] if the property's value is + /// not a null-terminated string or contains invalid UTF-8. + pub fn bootargs(&self) -> Result, PropertyError> { + self.node + .property("bootargs") + .map(|value| value.as_str()) + .transpose() + } + + /// Returns the value of the standard `stdout-path` property. + /// + /// # Errors + /// + /// Returns an [`PropertyError::InvalidString`] if the property's value is + /// not a null-terminated string or contains invalid UTF-8. + pub fn stdout_path(&self) -> Result, PropertyError> { + self.node + .property("stdout-path") + .map(|value| value.as_str()) + .transpose() + } + + /// Returns the value of the standard `stdin-path` property. + /// + /// # Errors + /// + /// Returns an [`PropertyError::InvalidString`] if the property's value is + /// not a null-terminated string or contains invalid UTF-8. + pub fn stdin_path(&self) -> Result, PropertyError> { + self.node + .property("stdin-path") + .map(|value| value.as_str()) + .transpose() + } +} diff --git a/src/standard/cpus.rs b/src/standard/cpus.rs index 3a4e355..c659f08 100644 --- a/src/standard/cpus.rs +++ b/src/standard/cpus.rs @@ -9,9 +9,9 @@ use core::fmt::{self, Display, Formatter}; use core::ops::Deref; -use crate::error::StandardError; +use crate::error::{PropertyError, StandardError}; use crate::fdt::{Fdt, FdtNode}; -use crate::{Cells, Node}; +use crate::{Cells, Node, Property}; impl<'a> Fdt<'a> { /// Returns the `/cpus` node. @@ -82,6 +82,27 @@ impl Display for Cpu { } } +impl<'a, N: Node<'a>> Cpu { + /// Returns the value of the standard `enable-method` property if it is + /// present. + pub fn enable_method(&self) -> Option> { + Some(self.node.property("enable-method")?.as_str_list()) + } + + /// Returns the value of the standard `cpu-release-addr` property if it is + /// present. + /// + /// # Errors + /// + /// Returns an error if the value of the property isn't 8 bytes long. + pub fn cpu_release_addr(&self) -> Result, PropertyError> { + self.node + .property("cpu-release-addr") + .map(|value| value.as_u64()) + .transpose() + } +} + impl<'a> Cpu> { /// Returns an iterator over the IDs of the CPU, from the standard `reg` /// property. diff --git a/src/standard/memory.rs b/src/standard/memory.rs index e554a66..b9e74b6 100644 --- a/src/standard/memory.rs +++ b/src/standard/memory.rs @@ -11,6 +11,7 @@ use core::ops::Deref; use crate::error::{PropertyError, StandardError}; use crate::fdt::{Fdt, FdtNode}; +use crate::standard::Reg; use crate::{Cells, Node, Property}; impl<'a> Fdt<'a> { @@ -27,6 +28,16 @@ impl<'a> Fdt<'a> { .ok_or(StandardError::MemoryMissing)?; Ok(Memory { node }) } + + /// Returns the `/reserved-memory/*` nodes, if any. + #[must_use] + pub fn reserved_memory(self) -> Option>>> { + Some( + self.find_node("/reserved-memory")? + .children() + .map(|node| ReservedMemory { node }), + ) + } } /// Typed wrapper for a `/memory` node. @@ -107,3 +118,92 @@ impl InitialMappedArea { } } } + +/// Typed wrapper for a `/reserved-memory/*` node. +#[derive(Clone, Copy, Debug)] +pub struct ReservedMemory { + node: N, +} + +impl Deref for ReservedMemory { + type Target = N; + + fn deref(&self) -> &Self::Target { + &self.node + } +} + +impl Display for ReservedMemory { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + self.node.fmt(f) + } +} + +impl<'a, N: Node<'a>> ReservedMemory { + /// Returns the value of the standard `size` property of the reserved memory + /// node, if it is present. + /// + /// # Errors + /// + /// Returns an error if the value of the property isn't a multiple of 4 + /// bytes long. + pub fn size(&self) -> Result>, PropertyError> { + self.node + .property("size") + .map(|value| value.as_cells()) + .transpose() + } + + /// Returns the value of the standard `alignment` property of the reserved + /// memory node, if it is present. + /// + /// # Errors + /// + /// Returns an error if the value of the property isn't a multiple of 4 + /// bytes long. + pub fn alignment(&self) -> Result>, PropertyError> { + self.node + .property("alignment") + .map(|value| value.as_cells()) + .transpose() + } + + /// Returns whether the standard `no-map` property is present. + pub fn no_map(&self) -> bool { + self.node.property("no-map").is_some() + } + + /// Returns whether the standard `no-map-fixup` property is present. + pub fn no_map_fixup(&self) -> bool { + self.node.property("no-map-fixup").is_some() + } + + /// Returns whether the standard `reusable` property is present. + pub fn reusable(&self) -> bool { + self.node.property("reusable").is_some() + } +} + +impl<'a> ReservedMemory> { + /// Returns the value of the standard `alloc-ranges` 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 alloc_ranges( + &self, + ) -> Result> + use<'a>>, StandardError> { + let address_cells = self.node.parent_address_space.address_cells as usize; + let size_cells = self.node.parent_address_space.size_cells as usize; + if let Some(property) = self.property("alloc_ranges") { + Ok(Some( + property + .as_prop_encoded_array([address_cells, size_cells])? + .map(Reg::from_cells), + )) + } else { + Ok(None) + } + } +} diff --git a/tests/dtb/test_pretty_print.dtb b/tests/dtb/test_pretty_print.dtb index 31ac05c..c105688 100644 Binary files a/tests/dtb/test_pretty_print.dtb and b/tests/dtb/test_pretty_print.dtb differ diff --git a/tests/dts/test_pretty_print.dts b/tests/dts/test_pretty_print.dts index e60ad04..32cf8f3 100644 --- a/tests/dts/test_pretty_print.dts +++ b/tests/dts/test_pretty_print.dts @@ -26,4 +26,20 @@ initial-mapped-area = <0x00 0x1234 0x00 0x4321 0x1000>; hotpluggable; }; + + reserved-memory { + #address-cells = <0x01>; + #size-cells = <0x01>; + ranges; + + shared { + reusable; + size = <0x4000000>; + alignment = <0x2000>; + }; + + foo@78000000 { + reg = <0x78000000 0x800000>; + }; + }; }; diff --git a/tests/fdt.rs b/tests/fdt.rs index 3038eae..bb9d1cc 100644 --- a/tests/fdt.rs +++ b/tests/fdt.rs @@ -258,6 +258,43 @@ fn memory() { ); } +#[test] +fn reserved_memory() { + let dtb = include_bytes!("dtb/test_pretty_print.dtb"); + let fdt = Fdt::new(dtb).unwrap(); + + let reserved = fdt.reserved_memory().unwrap().collect::>(); + + assert!(reserved[0].reg().unwrap().is_none()); + assert_eq!( + reserved[0] + .size() + .unwrap() + .unwrap() + .to_int::() + .unwrap(), + 0x400_0000 + ); + assert_eq!( + reserved[0] + .alignment() + .unwrap() + .unwrap() + .to_int::() + .unwrap(), + 0x2000 + ); + assert!(reserved[0].reusable()); + + assert!(reserved[1].size().unwrap().is_none()); + assert!(reserved[1].alignment().unwrap().is_none()); + assert!(!reserved[1].reusable()); + let reg = reserved[1].reg().unwrap().unwrap().collect::>(); + assert_eq!(reg.len(), 1); + assert_eq!(reg[0].address::().unwrap(), 0x7800_0000); + assert_eq!(reg[0].size::().unwrap(), 0x80_0000); +} + #[macro_export] macro_rules! load_dtb_dts_pair { ($name:expr) => {