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