fdt_raw/node/mod.rs
1//! Device tree node types and parsing.
2//!
3//! This module provides types for representing device tree nodes,
4//! including the base node type and specialized variants like Chosen
5//! and Memory nodes. It also contains the iterator logic for parsing
6//! nodes from the FDT structure block.
7
8use core::fmt;
9use core::ops::Deref;
10use core::{ffi::CStr, fmt::Debug};
11
12use crate::Fdt;
13use crate::fmt_utils;
14use crate::{
15 FdtError, Token,
16 data::{Bytes, Reader, U32_SIZE},
17};
18
19mod chosen;
20mod memory;
21mod prop;
22
23pub use chosen::Chosen;
24pub use memory::{Memory, MemoryRegion};
25pub use prop::{PropIter, Property, RangeInfo, RegInfo, RegIter, VecRange};
26
27/// Context inherited from a node's parent.
28///
29/// Contains the `#address-cells` and `#size-cells` values that should
30/// be used when parsing properties of the current node. These values
31/// are inherited from the parent node unless overridden.
32///
33/// # Default Values
34///
35/// The root node defaults to `#address-cells = 2` and `#size-cells = 1`
36/// per the Device Tree specification.
37#[derive(Clone)]
38pub(crate) struct NodeContext {
39 /// Parent node's #address-cells (used for parsing current node's reg)
40 pub address_cells: u8,
41 /// Parent node's #size-cells (used for parsing current node's reg)
42 pub size_cells: u8,
43}
44
45impl Default for NodeContext {
46 fn default() -> Self {
47 NodeContext {
48 address_cells: 2,
49 size_cells: 1,
50 }
51 }
52}
53
54/// Base device tree node structure.
55///
56/// Contains the common data and methods available on all nodes,
57/// including name, level, properties, and cell values.
58#[derive(Clone)]
59pub struct NodeBase<'a> {
60 name: &'a str,
61 data: Bytes<'a>,
62 strings: Bytes<'a>,
63 level: usize,
64 _fdt: Fdt<'a>,
65 /// Current node's #address-cells (used for child nodes)
66 pub address_cells: u8,
67 /// Current node's #size-cells (used for child nodes)
68 pub size_cells: u8,
69 /// Inherited context (contains parent's cells)
70 context: NodeContext,
71 /// Path components from root to this node
72 path_components: heapless::Vec<&'a str, 16>,
73}
74
75impl<'a> NodeBase<'a> {
76 /// Returns the node's name.
77 pub fn name(&self) -> &'a str {
78 self.name
79 }
80
81 /// Returns the depth/level of this node in the tree.
82 pub fn level(&self) -> usize {
83 self.level
84 }
85
86 /// Returns an iterator over this node's properties.
87 pub fn properties(&self) -> PropIter<'a> {
88 PropIter::new(self.data.reader(), self.strings.clone())
89 }
90
91 /// Finds a property by name.
92 pub fn find_property(&self, name: &str) -> Option<Property<'a>> {
93 self.properties().find(|p| p.name() == name)
94 }
95
96 /// Finds a string property by name.
97 pub fn find_property_str(&self, name: &str) -> Option<&'a str> {
98 let prop = self.find_property(name)?;
99 prop.as_str()
100 }
101
102 /// Finds and parses the `reg` property, returning a Reg iterator.
103 pub fn reg(&self) -> Option<RegIter<'a>> {
104 let prop = self.find_property("reg")?;
105 Some(RegIter::new(
106 prop.data().reader(),
107 self.context.address_cells,
108 self.context.size_cells,
109 ))
110 }
111
112 /// Finds and parses the `reg` property, returning all RegInfo entries.
113 pub fn reg_array<const N: usize>(&self) -> heapless::Vec<RegInfo, N> {
114 let mut result = heapless::Vec::new();
115 if let Some(reg) = self.reg() {
116 for info in reg {
117 if result.push(info).is_err() {
118 break; // Array is full
119 }
120 }
121 }
122 result
123 }
124
125 /// Checks if this is the chosen node.
126 fn is_chosen(&self) -> bool {
127 self.name == "chosen"
128 }
129
130 /// Checks if this is a memory node.
131 fn is_memory(&self) -> bool {
132 self.name.starts_with("memory")
133 }
134
135 /// Returns the `ranges` property if present.
136 pub fn ranges(&self) -> Option<VecRange<'a>> {
137 let prop = self.find_property("ranges")?;
138 Some(VecRange::new(
139 self.address_cells as usize,
140 self.context.address_cells as usize,
141 self.context.size_cells as usize,
142 prop.data(),
143 ))
144 }
145
146 /// Returns an iterator over compatible strings.
147 pub fn compatibles(&self) -> impl Iterator<Item = &'a str> {
148 self.find_property("compatible")
149 .into_iter()
150 .flat_map(|p| p.as_str_iter())
151 }
152
153 /// Returns the full path of this node as a string.
154 ///
155 /// For the root node, returns "/". For other nodes, returns the
156 /// absolute path like "/soc/serial@0".
157 pub fn path(&self) -> heapless::String<256> {
158 let mut result = heapless::String::new();
159 if self.path_components.is_empty() {
160 let _ = result.push('/');
161 return result;
162 }
163 for component in &self.path_components {
164 let _ = result.push('/');
165 let _ = result.push_str(component);
166 }
167 result
168 }
169}
170
171impl fmt::Display for NodeBase<'_> {
172 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
173 fmt_utils::write_indent(f, self.level, " ")?;
174 let name = if self.name.is_empty() { "/" } else { self.name };
175
176 writeln!(f, "{} {{", name)?;
177 for prop in self.properties() {
178 fmt_utils::write_indent(f, self.level + 1, " ")?;
179 writeln!(f, "{};", prop)?;
180 }
181 fmt_utils::write_indent(f, self.level, " ")?;
182 write!(f, "}}")
183 }
184}
185
186// ============================================================================
187// Node enum: supports specialized node types
188// ============================================================================
189
190/// Device tree node enum supporting specialized node types.
191///
192/// Nodes are automatically classified into General, Chosen, or Memory
193/// variants based on their name and properties.
194#[derive(Clone)]
195pub enum Node<'a> {
196 /// A general-purpose node without special handling
197 General(NodeBase<'a>),
198 /// The /chosen node containing boot parameters
199 Chosen(Chosen<'a>),
200 /// A memory node describing physical memory layout
201 Memory(Memory<'a>),
202}
203
204impl<'a> From<NodeBase<'a>> for Node<'a> {
205 fn from(node: NodeBase<'a>) -> Self {
206 if node.is_chosen() {
207 Node::Chosen(Chosen::new(node))
208 } else if node.is_memory() {
209 Node::Memory(Memory::new(node))
210 } else {
211 Node::General(node)
212 }
213 }
214}
215
216impl<'a> Deref for Node<'a> {
217 type Target = NodeBase<'a>;
218
219 fn deref(&self) -> &Self::Target {
220 match self {
221 Node::General(n) => n,
222 Node::Chosen(c) => c.deref(),
223 Node::Memory(m) => m.deref(),
224 }
225 }
226}
227
228impl fmt::Display for Node<'_> {
229 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
230 Debug::fmt(self, f)
231 }
232}
233
234impl fmt::Debug for Node<'_> {
235 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
236 match self {
237 Node::General(n) => f.debug_tuple("General").field(&n.name()).finish(),
238 Node::Chosen(c) => c.fmt(f),
239 Node::Memory(m) => m.fmt(f),
240 }
241 }
242}
243
244/// Key information extracted when parsing properties.
245#[derive(Debug, Clone, Default)]
246pub(crate) struct ParsedProps {
247 pub address_cells: Option<u8>,
248 pub size_cells: Option<u8>,
249}
250
251/// State of a single node iteration.
252///
253/// Tracks the current state while parsing a single node's content.
254/// Used internally by `OneNodeIter` to communicate with `FdtIter`.
255#[derive(Debug, Clone, Copy, PartialEq, Eq)]
256pub(crate) enum OneNodeState {
257 /// Currently processing the node (reading properties)
258 Processing,
259 /// Encountered a child's BeginNode token, needs to backtrack
260 ChildBegin,
261 /// Encountered EndNode token, current node processing complete
262 End,
263}
264
265/// An iterator over a single node's content.
266///
267/// When encountering a child's BeginNode token, it backtracks and signals
268/// FdtIter to handle the child node. This allows FdtIter to maintain
269/// proper tree traversal state.
270///
271/// # Implementation Notes
272///
273/// This iterator is `pub(crate)` because it's an internal implementation
274/// detail of the FDT parsing machinery. External consumers should use
275/// `FdtIter` or `NodeBase::properties()` instead.
276pub(crate) struct OneNodeIter<'a> {
277 /// Reader for the node's property data
278 reader: Reader<'a>,
279 /// Strings block for looking up property names
280 strings: Bytes<'a>,
281 /// Current iteration state
282 state: OneNodeState,
283 /// Depth level of this node in the tree
284 level: usize,
285 /// Inherited context from parent (address_cells, size_cells)
286 context: NodeContext,
287 /// Extracted properties (#address-cells, #size-cells)
288 parsed_props: ParsedProps,
289 /// Reference to the containing FDT for path resolution
290 fdt: Fdt<'a>,
291}
292
293impl<'a> OneNodeIter<'a> {
294 /// Creates a new single node iterator.
295 pub fn new(
296 reader: Reader<'a>,
297 strings: Bytes<'a>,
298 level: usize,
299 context: NodeContext,
300 fdt: Fdt<'a>,
301 ) -> Self {
302 Self {
303 reader,
304 strings,
305 state: OneNodeState::Processing,
306 level,
307 context,
308 parsed_props: ParsedProps::default(),
309 fdt,
310 }
311 }
312
313 /// Returns a reference to the reader.
314 pub fn reader(&self) -> &Reader<'a> {
315 &self.reader
316 }
317
318 /// Returns the parsed properties extracted from this node.
319 pub fn parsed_props(&self) -> &ParsedProps {
320 &self.parsed_props
321 }
322
323 /// Reads the node name (called after BeginNode token).
324 ///
325 /// Reads the null-terminated node name and aligns to a 4-byte boundary.
326 /// Returns a partially-constructed `NodeBase` with default cell values
327 /// that will be updated by `process()`.
328 pub fn read_node_name(
329 &mut self,
330 parent_path: &heapless::Vec<&'a str, 16>,
331 ) -> Result<NodeBase<'a>, FdtError> {
332 // Read null-terminated name string
333 let name = self.read_cstr()?;
334
335 // Align to 4-byte boundary
336 self.align4();
337
338 let data = self.reader.remain();
339
340 // Build path components: parent path + current node name
341 let mut path_components = parent_path.clone();
342 if !name.is_empty() {
343 let _ = path_components.push(name);
344 }
345
346 Ok(NodeBase {
347 name,
348 data,
349 strings: self.strings.clone(),
350 level: self.level,
351 // Default values, will be updated in process()
352 address_cells: 2,
353 size_cells: 1,
354 context: self.context.clone(),
355 _fdt: self.fdt.clone(),
356 path_components,
357 })
358 }
359
360 /// Reads a null-terminated string from the current position.
361 fn read_cstr(&mut self) -> Result<&'a str, FdtError> {
362 let bytes = self.reader.remain();
363 let cstr = CStr::from_bytes_until_nul(bytes.as_slice())?;
364 let s = cstr.to_str()?;
365 // Skip string content + null terminator
366 let _ = self.reader.read_bytes(s.len() + 1);
367 Ok(s)
368 }
369
370 /// Aligns the reader to a 4-byte boundary.
371 ///
372 /// FDT structures are 4-byte aligned, so after reading variable-length
373 /// data (like node names), we need to pad to the next 4-byte boundary.
374 fn align4(&mut self) {
375 let pos = self.reader.position();
376 let aligned = (pos + U32_SIZE - 1) & !(U32_SIZE - 1);
377 let skip = aligned - pos;
378 if skip > 0 {
379 let _ = self.reader.read_bytes(skip);
380 }
381 }
382
383 /// Reads a property name from the strings block.
384 ///
385 /// Property names are stored as offsets into the strings block,
386 /// not inline with the property data.
387 fn read_prop_name(&self, nameoff: u32) -> Result<&'a str, FdtError> {
388 let bytes = self.strings.slice(nameoff as usize..self.strings.len());
389 let cstr = CStr::from_bytes_until_nul(bytes.as_slice())?;
390 Ok(cstr.to_str()?)
391 }
392
393 /// Reads a u32 value from big-endian bytes at the given offset.
394 fn read_u32_be(data: &[u8], offset: usize) -> u64 {
395 u32::from_be_bytes(data[offset..offset + U32_SIZE].try_into().unwrap()) as u64
396 }
397
398 /// Processes node content, parsing properties until child node or end.
399 ///
400 /// This is the core parsing loop for a node. It reads tokens sequentially:
401 /// - Properties are parsed and `#address-cells`/`#size-cells` are extracted
402 /// - Child nodes cause backtracking and return `ChildBegin`
403 /// - EndNode terminates processing and returns `End`
404 ///
405 /// # Returns
406 ///
407 /// - `Ok(OneNodeState::ChildBegin)` if a child node was found
408 /// - `Ok(OneNodeState::End)` if the node ended
409 /// - `Err(FdtError)` if parsing failed
410 pub fn process(&mut self) -> Result<OneNodeState, FdtError> {
411 loop {
412 let token = self.reader.read_token()?;
413 match token {
414 Token::BeginNode => {
415 // Child node encountered, backtrack token and return
416 self.reader.backtrack(U32_SIZE);
417 self.state = OneNodeState::ChildBegin;
418 return Ok(OneNodeState::ChildBegin);
419 }
420 Token::EndNode => {
421 self.state = OneNodeState::End;
422 return Ok(OneNodeState::End);
423 }
424 Token::Prop => {
425 // Read property: len and nameoff
426 let len = self.reader.read_u32().ok_or(FdtError::BufferTooSmall {
427 pos: self.reader.position(),
428 })? as usize;
429
430 let nameoff = self.reader.read_u32().ok_or(FdtError::BufferTooSmall {
431 pos: self.reader.position(),
432 })?;
433
434 // Read property data
435 let prop_data = if len > 0 {
436 self.reader
437 .read_bytes(len)
438 .ok_or(FdtError::BufferTooSmall {
439 pos: self.reader.position(),
440 })?
441 } else {
442 Bytes::new(&[])
443 };
444
445 // Parse key properties
446 if let Ok(prop_name) = self.read_prop_name(nameoff) {
447 match prop_name {
448 "#address-cells" if len == 4 => {
449 self.parsed_props.address_cells =
450 Some(Self::read_u32_be(&prop_data, 0) as u8);
451 }
452 "#size-cells" if len == 4 => {
453 self.parsed_props.size_cells =
454 Some(Self::read_u32_be(&prop_data, 0) as u8);
455 }
456 _ => {}
457 }
458 }
459
460 // Align to 4-byte boundary
461 self.align4();
462 }
463 Token::Nop => {
464 // Ignore NOP tokens
465 }
466 Token::End => {
467 // Structure block ended
468 self.state = OneNodeState::End;
469 return Ok(OneNodeState::End);
470 }
471 Token::Data(_) => {
472 // Invalid token
473 return Err(FdtError::BufferTooSmall {
474 pos: self.reader.position(),
475 });
476 }
477 }
478 }
479 }
480}