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, 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}