Skip to content

Commit 4380a93

Browse files
committed
Add type and getters for memory node.
1 parent cc2e597 commit 4380a93

8 files changed

Lines changed: 166 additions & 5 deletions

File tree

src/error.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,20 @@ pub enum FdtError {
2121
/// The `status` property of a node had an invalid value.
2222
#[error("Invalid status value")]
2323
InvalidStatus,
24+
/// The required `/memory` node wasn't found.
25+
#[error("/memory node missing")]
26+
MemoryMissing,
27+
/// The size of a prop-encoded-array property wasn't a multiple of the
28+
/// expected element size.
29+
#[error(
30+
"prop-encoded-array property was {size} bytes, but should have been a multiple of {chunk} cells"
31+
)]
32+
PropEncodedArraySizeMismatch {
33+
/// The size in bytes of the prop-encoded-array property.
34+
size: usize,
35+
/// The number of 4 byte cells expected in each element of the array.
36+
chunk: usize,
37+
},
2438
}
2539

2640
/// An error that can occur when parsing a device tree.

src/fdt/property.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use core::fmt;
1414
use zerocopy::{FromBytes, big_endian};
1515

1616
use super::{FDT_TAGSIZE, Fdt, FdtToken};
17-
use crate::error::{FdtErrorKind, FdtParseError};
17+
use crate::error::{FdtError, FdtErrorKind, FdtParseError};
1818

1919
/// A property of a device tree node.
2020
#[derive(Debug, PartialEq)]
@@ -127,6 +127,26 @@ impl<'a> FdtProperty<'a> {
127127
FdtStringListIterator { value: self.value }
128128
}
129129

130+
pub(crate) fn as_prop_encoded_array(
131+
&self,
132+
chunk_cells: usize,
133+
) -> Result<impl Iterator<Item = &'a [big_endian::U32]> + use<'a>, FdtError> {
134+
let chunk_bytes = chunk_cells * size_of::<u32>();
135+
if !self.value.len().is_multiple_of(chunk_bytes) {
136+
return Err(FdtError::PropEncodedArraySizeMismatch {
137+
size: self.value.len(),
138+
chunk: chunk_cells,
139+
});
140+
}
141+
// `ref_from_bytes` shouldn't panic because each chunk will always be a multiple
142+
// of 4 bytes because of `chunks_exact`.
143+
#[allow(clippy::unwrap_used)]
144+
Ok(self
145+
.value
146+
.chunks_exact(chunk_bytes)
147+
.map(|chunk| <[big_endian::U32]>::ref_from_bytes(chunk).unwrap()))
148+
}
149+
130150
pub(crate) fn fmt(&self, f: &mut fmt::Formatter<'_>, indent: usize) -> fmt::Result {
131151
write!(f, "{:indent$}{}", "", self.name, indent = indent)?;
132152

src/model/property.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use alloc::string::{String, ToString};
1010
use alloc::vec::Vec;
1111
use core::{fmt, str};
1212

13-
use crate::error::FdtParseError;
13+
use crate::error::{FdtError, FdtParseError};
1414
use crate::fdt::FdtProperty;
1515

1616
/// An error that can occur when parsing a property.

src/standard.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@
88

99
//! Standard nodes and properties.
1010
11+
mod memory;
1112
mod status;
1213

13-
pub use status::Status;
14-
14+
pub use self::memory::{InitialMappedArea, Memory};
15+
pub use self::status::Status;
1516
use crate::error::{FdtError, FdtParseError};
1617
use crate::fdt::FdtNode;
1718

src/standard/memory.rs

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4+
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5+
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6+
// option. This file may not be copied, modified, or distributed
7+
// except according to those terms.
8+
9+
use core::ops::Deref;
10+
11+
use zerocopy::big_endian;
12+
13+
use crate::error::FdtError;
14+
use crate::fdt::{Fdt, FdtNode};
15+
16+
impl Fdt<'_> {
17+
/// Returns the /memory node.
18+
///
19+
/// This should always be included in a valid device tree.
20+
///
21+
/// # Errors
22+
///
23+
/// Returns a parse error if there was a problem reading the FDT structure
24+
/// to find the node, or `FdtError::MemoryMissing` if the memory node is
25+
/// missing.
26+
pub fn memory(&self) -> Result<Memory<'_>, FdtError> {
27+
let node = self.find_node("/memory")?.ok_or(FdtError::MemoryMissing)?;
28+
Ok(Memory { node })
29+
}
30+
}
31+
32+
/// Typed wrapper for a "/memory" node.
33+
#[derive(Clone, Copy, Debug)]
34+
pub struct Memory<'a> {
35+
node: FdtNode<'a>,
36+
}
37+
38+
impl<'a> Deref for Memory<'a> {
39+
type Target = FdtNode<'a>;
40+
41+
fn deref(&self) -> &Self::Target {
42+
&self.node
43+
}
44+
}
45+
46+
impl<'a> Memory<'a> {
47+
/// Returns the value of the standard `initial-mapped-area` property of the
48+
/// memory node.
49+
///
50+
/// # Errors
51+
///
52+
/// Returns an error if a property's name or value cannot be read.
53+
#[allow(clippy::missing_panics_doc)]
54+
pub fn initial_mapped_area(
55+
&self,
56+
) -> Result<Option<impl Iterator<Item = InitialMappedArea> + use<'a>>, FdtError> {
57+
Ok(
58+
if let Some(property) = self.node.property("initial-mapped-area")? {
59+
// try_into can't return an error, because we passed a chunk size matching what
60+
// `InitialMappedArea::from_cells` expects.
61+
#[allow(clippy::unwrap_used)]
62+
Some(
63+
property
64+
.as_prop_encoded_array(5)?
65+
.map(|chunk| InitialMappedArea::from_cells(chunk.try_into().unwrap())),
66+
)
67+
} else {
68+
None
69+
},
70+
)
71+
}
72+
73+
/// Returns the value of the standard `hotpluggable` property of the memory
74+
/// node.
75+
///
76+
/// # Errors
77+
///
78+
/// Returns an error if a property's name or value cannot be read.
79+
pub fn hotpluggable(&self) -> Result<bool, FdtError> {
80+
Ok(self.node.property("hotpluggable")?.is_some())
81+
}
82+
}
83+
84+
/// The value of an `initial-mapped-area` property.
85+
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
86+
pub struct InitialMappedArea {
87+
/// The effective address.
88+
pub effective_address: u64,
89+
/// The physical address.
90+
pub physical_address: u64,
91+
/// The size of the area.
92+
pub size: u32,
93+
}
94+
95+
impl InitialMappedArea {
96+
fn from_cells([ea_high, ea_low, pa_high, pa_low, size]: [big_endian::U32; 5]) -> Self {
97+
Self {
98+
effective_address: u64::from(ea_high.get()) << 32 | u64::from(ea_low.get()),
99+
physical_address: u64::from(pa_high.get()) << 32 | u64::from(pa_low.get()),
100+
size: size.get(),
101+
}
102+
}
103+
}

tests/dtb/test_pretty_print.dtb

77 Bytes
Binary file not shown.

tests/dts/test_pretty_print.dts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,7 @@
2323
memory@80000000 {
2424
device_type = "memory";
2525
reg = <0x80000000 0x20000000>;
26+
initial-mapped-area = <0x00 0x1234 0x00 0x4321 0x1000>;
27+
hotpluggable;
2628
};
2729
};

tests/fdt.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
// except according to those terms.
88

99
use dtoolkit::fdt::Fdt;
10-
use dtoolkit::standard::Status;
10+
use dtoolkit::standard::{InitialMappedArea, Status};
1111

1212
#[test]
1313
fn read_child_nodes() {
@@ -183,6 +183,27 @@ fn find_node_by_path() {
183183
assert!(fdt.find_node("").unwrap().is_none());
184184
}
185185

186+
#[test]
187+
fn memory() {
188+
let dtb = include_bytes!("dtb/test_pretty_print.dtb");
189+
let fdt = Fdt::new(dtb).unwrap();
190+
191+
let memory = fdt.memory().unwrap();
192+
assert!(memory.hotpluggable().unwrap());
193+
assert_eq!(
194+
memory
195+
.initial_mapped_area()
196+
.unwrap()
197+
.unwrap()
198+
.collect::<Vec<_>>(),
199+
vec![InitialMappedArea {
200+
effective_address: 0x1234,
201+
physical_address: 0x4321,
202+
size: 0x1000,
203+
}]
204+
);
205+
}
206+
186207
#[macro_export]
187208
macro_rules! load_dtb_dts_pair {
188209
($name:expr) => {

0 commit comments

Comments
 (0)