Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Cells<'a>, 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
Expand Down
4 changes: 3 additions & 1 deletion src/standard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
84 changes: 84 additions & 0 deletions src/standard/chosen.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, 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<Chosen<FdtNode<'a>>> {
let node = self.find_node("/chosen")?;
Some(Chosen { node })
}
}

/// Typed wrapper for a `/chosen` node.
#[derive(Clone, Copy, Debug)]
pub struct Chosen<N> {
node: N,
}

impl<N> Deref for Chosen<N> {
type Target = N;

fn deref(&self) -> &Self::Target {
&self.node
}
}

impl<N: Display> Display for Chosen<N> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
self.node.fmt(f)
}
}

impl<'a, N: Node<'a>> Chosen<N> {
/// 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<Option<&'a str>, 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<Option<&'a str>, 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<Option<&'a str>, PropertyError> {
self.node
.property("stdin-path")
.map(|value| value.as_str())
.transpose()
}
}
25 changes: 23 additions & 2 deletions src/standard/cpus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -82,6 +82,27 @@ impl<N: Display> Display for Cpu<N> {
}
}

impl<'a, N: Node<'a>> Cpu<N> {
/// Returns the value of the standard `enable-method` property if it is
/// present.
pub fn enable_method(&self) -> Option<impl Iterator<Item = &'a str>> {
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<Option<u64>, PropertyError> {
self.node
.property("cpu-release-addr")
.map(|value| value.as_u64())
.transpose()
}
}

impl<'a> Cpu<FdtNode<'a>> {
/// Returns an iterator over the IDs of the CPU, from the standard `reg`
/// property.
Expand Down
100 changes: 100 additions & 0 deletions src/standard/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand All @@ -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<impl Iterator<Item = ReservedMemory<FdtNode<'a>>>> {
Some(
self.find_node("/reserved-memory")?
.children()
.map(|node| ReservedMemory { node }),
)
}
}

/// Typed wrapper for a `/memory` node.
Expand Down Expand Up @@ -107,3 +118,92 @@ impl InitialMappedArea {
}
}
}

/// Typed wrapper for a `/reserved-memory/*` node.
#[derive(Clone, Copy, Debug)]
pub struct ReservedMemory<N> {
node: N,
}

impl<N> Deref for ReservedMemory<N> {
type Target = N;

fn deref(&self) -> &Self::Target {
&self.node
}
}

impl<N: Display> Display for ReservedMemory<N> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
self.node.fmt(f)
}
}

impl<'a, N: Node<'a>> ReservedMemory<N> {
/// 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<Option<Cells<'a>>, 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<Option<Cells<'a>>, 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<FdtNode<'a>> {
/// 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<Option<impl Iterator<Item = Reg<'a>> + 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)
}
}
}
Binary file modified tests/dtb/test_pretty_print.dtb
Binary file not shown.
16 changes: 16 additions & 0 deletions tests/dts/test_pretty_print.dts
Original file line number Diff line number Diff line change
Expand Up @@ -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>;
};
};
};
37 changes: 37 additions & 0 deletions tests/fdt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<Vec<_>>();

assert!(reserved[0].reg().unwrap().is_none());
assert_eq!(
reserved[0]
.size()
.unwrap()
.unwrap()
.to_int::<u32>()
.unwrap(),
0x400_0000
);
assert_eq!(
reserved[0]
.alignment()
.unwrap()
.unwrap()
.to_int::<u32>()
.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::<Vec<_>>();
assert_eq!(reg.len(), 1);
assert_eq!(reg[0].address::<u32>().unwrap(), 0x7800_0000);
assert_eq!(reg[0].size::<u32>().unwrap(), 0x80_0000);
}

#[macro_export]
macro_rules! load_dtb_dts_pair {
($name:expr) => {
Expand Down
Loading