diff --git a/Cargo.toml b/Cargo.toml index fce3086..7c37c21 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ alloc = [] std = ["alloc"] [dependencies] -zerocopy = { version = "0.8", features = ["derive"] } +zerocopy = { version = "0.8.28", features = ["derive"] } [lints.rust] deprecated-safe = "warn" diff --git a/src/error.rs b/src/error.rs index 587b273..cd07dde 100644 --- a/src/error.rs +++ b/src/error.rs @@ -43,6 +43,11 @@ pub enum FdtErrorKind { InvalidOffset, /// An invalid string was encountered. InvalidString, + /// Memory reservation block has not been terminated with a null entry. + MemReserveNotTerminated, + /// Memory reservation block has an entry that is unaligned or has invalid + /// size. + MemReserveInvalid, } impl fmt::Display for FdtError { @@ -65,6 +70,14 @@ impl fmt::Display for FdtErrorKind { FdtErrorKind::BadToken(token) => write!(f, "bad FDT token: 0x{token:x}"), FdtErrorKind::InvalidOffset => write!(f, "invalid offset in FDT"), FdtErrorKind::InvalidString => write!(f, "invalid string in FDT"), + FdtErrorKind::MemReserveNotTerminated => write!( + f, + "memory reservation block not terminated with a null entry" + ), + FdtErrorKind::MemReserveInvalid => write!( + f, + "memory reservation block has an entry that is unaligned or has invalid size" + ), } } } diff --git a/src/fdt/mod.rs b/src/fdt/mod.rs index a0d71f5..00e1893 100644 --- a/src/fdt/mod.rs +++ b/src/fdt/mod.rs @@ -16,6 +16,7 @@ //! [Flattened Device Tree (FDT)]: https://devicetree-specification.readthedocs.io/en/latest/chapter5-flattened-format.html use crate::error::{FdtError, FdtErrorKind}; +use crate::memreserve::MemoryReservation; mod node; mod property; use core::ffi::CStr; @@ -312,6 +313,34 @@ impl<'a> Fdt<'a> { self.header().boot_cpuid_phys() } + /// Returns an iterator over the memory reservation block. + pub fn memory_reservations( + &self, + ) -> impl Iterator> + '_ { + 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(FdtError::new( + FdtErrorKind::MemReserveNotTerminated, + offset, + ))); + } + + let reservation = match MemoryReservation::ref_from_prefix(&self.data[offset..]) + .map_err(|_| FdtError::new(FdtErrorKind::MemReserveInvalid, offset)) + { + Ok((reservation, _)) => *reservation, + Err(e) => return Some(Err(e)), + }; + offset += size_of::(); + + if reservation == MemoryReservation::TERMINATOR { + return None; + } + Some(Ok(reservation)) + }) + } + /// Returns the root node of the device tree. /// /// # Errors @@ -494,6 +523,15 @@ impl<'a> Fdt<'a> { impl fmt::Display for Fdt<'_> { fn fmt(&self, f: &mut fmt::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};", + reservation.address(), + reservation.size() + )?; + } writeln!(f)?; let root = self.root().map_err(|_| fmt::Error)?; root.fmt_recursive(f, 0) diff --git a/src/lib.rs b/src/lib.rs index 4015745..0bf60bd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,3 +25,4 @@ pub mod error; pub mod fdt; +pub mod memreserve; diff --git a/src/memreserve.rs b/src/memreserve.rs new file mode 100644 index 0000000..53fc489 --- /dev/null +++ b/src/memreserve.rs @@ -0,0 +1,57 @@ +// 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. + +//! Device tree memory reservations. + +use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, big_endian}; + +/// A 64-bit memory reservation. +#[derive( + Debug, + Clone, + Copy, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + FromBytes, + IntoBytes, + Immutable, + KnownLayout, +)] +#[repr(C)] +pub struct MemoryReservation { + address: big_endian::U64, + size: big_endian::U64, +} + +impl MemoryReservation { + pub(crate) const TERMINATOR: Self = Self::new(0, 0); + + /// Creates a new [`MemoryReservation`]. + #[must_use] + pub const fn new(address: u64, size: u64) -> Self { + Self { + address: big_endian::U64::new(address), + size: big_endian::U64::new(size), + } + } + + /// Returns the physical address of the reserved memory region. + #[must_use] + pub const fn address(&self) -> u64 { + self.address.get() + } + + /// Returns the size of the reserved memory region. + #[must_use] + pub const fn size(&self) -> u64 { + self.size.get() + } +} diff --git a/tests/dtb/test_memreserve.dtb b/tests/dtb/test_memreserve.dtb new file mode 100644 index 0000000..e04171b Binary files /dev/null and b/tests/dtb/test_memreserve.dtb differ diff --git a/tests/dts/test_memreserve.dts b/tests/dts/test_memreserve.dts new file mode 100644 index 0000000..5d421e7 --- /dev/null +++ b/tests/dts/test_memreserve.dts @@ -0,0 +1,6 @@ +/dts-v1/; +/memreserve/ 0x1000 0x100; +/memreserve/ 0x2000 0x200; + +/ { +}; diff --git a/tests/fdt.rs b/tests/fdt.rs index 8cb5de4..5f753ad 100644 --- a/tests/fdt.rs +++ b/tests/fdt.rs @@ -155,6 +155,7 @@ macro_rules! load_dtb_dts_pair { const ALL_DT_FILES: &[(&[u8], &str, &str)] = &[ load_dtb_dts_pair!("test_children_nested"), load_dtb_dts_pair!("test_children"), + load_dtb_dts_pair!("test_memreserve"), load_dtb_dts_pair!("test_pretty_print"), load_dtb_dts_pair!("test_props"), load_dtb_dts_pair!("test_traversal"), diff --git a/tests/memreserve.rs b/tests/memreserve.rs new file mode 100644 index 0000000..4409a5c --- /dev/null +++ b/tests/memreserve.rs @@ -0,0 +1,30 @@ +// 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. + +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 dts = fdt.to_string(); + assert!(dts.contains("/memreserve/ 0x1000 0x100;")); + assert!(dts.contains("/memreserve/ 0x2000 0x200;")); +}