Skip to main content

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}