Skip to main content

fdt_raw/
fdt.rs

1//! Main Flattened Device Tree (FDT) parser.
2//!
3//! This module provides the primary `Fdt` type that represents a parsed
4//! device tree blob. It offers methods for traversing nodes, resolving
5//! paths, translating addresses, and accessing special nodes like
6//! /chosen and /memory.
7
8use core::fmt;
9
10use crate::{
11    Chosen, FdtError, Memory, MemoryReservation, Node, Property, VecRange, data, data::Bytes,
12    fmt_utils, header::Header, iter::FdtIter,
13};
14
15/// Iterator over memory reservation entries.
16///
17/// The memory reservation block contains a list of physical memory regions
18/// that must be preserved during boot. This iterator yields each reservation
19/// entry until it reaches the terminating entry (address=0, size=0).
20pub struct MemoryReservationIter<'a> {
21    data: &'a [u8],
22    offset: usize,
23}
24
25impl<'a> Iterator for MemoryReservationIter<'a> {
26    type Item = MemoryReservation;
27
28    fn next(&mut self) -> Option<Self::Item> {
29        // Ensure we have enough data to read a complete entry
30        if self.offset + data::MEM_RSV_ENTRY_SIZE > self.data.len() {
31            return None;
32        }
33
34        // Read address (8 bytes, big-endian)
35        let address_bytes = &self.data[self.offset..self.offset + 8];
36        let address = u64::from_be_bytes(address_bytes.try_into().unwrap());
37        self.offset += 8;
38
39        // Read size (8 bytes, big-endian)
40        let size_bytes = &self.data[self.offset..self.offset + 8];
41        let size = u64::from_be_bytes(size_bytes.try_into().unwrap());
42        self.offset += 8;
43
44        // Check for terminator (both address and size are zero)
45        if address == 0 && size == 0 {
46            return None;
47        }
48
49        Some(MemoryReservation { address, size })
50    }
51}
52
53/// A parsed Flattened Device Tree (FDT).
54///
55/// This is the main type for working with device tree blobs. It provides
56/// methods for traversing the tree, finding nodes by path, translating
57/// addresses, and accessing special nodes like /chosen and /memory.
58///
59/// The `Fdt` holds a reference to the underlying device tree data and
60/// performs zero-copy parsing where possible.
61#[derive(Clone)]
62pub struct Fdt<'a> {
63    header: Header,
64    pub(crate) data: Bytes<'a>,
65}
66
67impl<'a> Fdt<'a> {
68    /// Create a new `Fdt` from a byte slice.
69    ///
70    /// Parses the FDT header and validates the magic number. The slice
71    /// must contain a complete, valid device tree blob.
72    ///
73    /// # Errors
74    ///
75    /// Returns `FdtError` if the header is invalid, the magic number
76    /// doesn't match, or the buffer is too small.
77    pub fn from_bytes(data: &'a [u8]) -> Result<Fdt<'a>, FdtError> {
78        let header = Header::from_bytes(data)?;
79        if data.len() < header.totalsize as usize {
80            return Err(FdtError::BufferTooSmall {
81                pos: header.totalsize as usize,
82            });
83        }
84        let buffer = Bytes::new(data);
85
86        Ok(Fdt {
87            header,
88            data: buffer,
89        })
90    }
91
92    /// Create a new `Fdt` from a raw pointer.
93    ///
94    /// Parses an FDT from the memory location pointed to by `ptr`.
95    /// This is useful when working with device trees loaded by bootloaders.
96    ///
97    /// # Safety
98    ///
99    /// The caller must ensure that the pointer is valid and points to a
100    /// memory region of at least `totalsize` bytes that contains a valid
101    /// device tree blob. The memory must remain valid for the lifetime `'a`.
102    ///
103    /// # Errors
104    ///
105    /// Returns `FdtError` if the header is invalid or the magic number
106    /// doesn't match.
107    pub unsafe fn from_ptr(ptr: *mut u8) -> Result<Fdt<'a>, FdtError> {
108        let header = unsafe { Header::from_ptr(ptr)? };
109
110        let data_slice = unsafe { core::slice::from_raw_parts(ptr, header.totalsize as _) };
111        let data = Bytes::new(data_slice);
112
113        Ok(Fdt { header, data })
114    }
115
116    /// Returns a reference to the FDT header.
117    pub fn header(&self) -> &Header {
118        &self.header
119    }
120
121    /// Returns the underlying byte slice.
122    pub fn as_slice(&self) -> &'a [u8] {
123        self.data.as_slice()
124    }
125
126    /// Returns an iterator over all nodes in the device tree.
127    pub fn all_nodes(&self) -> FdtIter<'a> {
128        FdtIter::new(self.clone())
129    }
130
131    /// Find a node by its absolute path or alias.
132    ///
133    /// The path can be an absolute path starting with '/', or an alias
134    /// defined in the /aliases node. Returns `None` if the node is not found.
135    ///
136    /// # Example
137    ///
138    /// ```ignore
139    /// let node = fdt.find_by_path("/soc@30000000/serial@10000");
140    /// let uart = fdt.find_by_path("serial0");  // Using alias
141    /// ```
142    pub fn find_by_path(&self, path: &str) -> Option<Node<'a>> {
143        let path = self.normalize_path(path)?;
144        let split = path.trim_matches('/').split('/');
145
146        let mut current_iter = self.all_nodes();
147        let mut found_node: Option<Node<'a>> = None;
148
149        for part in split {
150            let mut found = false;
151            for node in current_iter.by_ref() {
152                let node_name = node.name();
153                if node_name == part {
154                    found = true;
155                    found_node = Some(node);
156                    break;
157                }
158            }
159            if !found {
160                return None;
161            }
162        }
163
164        found_node
165    }
166
167    /// Find all direct children of a node at the given path.
168    ///
169    /// Returns an iterator over all direct child nodes (one level deeper)
170    /// of the node at the specified path. Returns `None` if the node is
171    /// not found.
172    ///
173    /// Only direct children are yielded — grandchildren and deeper
174    /// descendants are skipped.
175    ///
176    /// # Example
177    ///
178    /// ```ignore
179    /// // List all direct children of /soc
180    /// if let Some(children) = fdt.find_children_by_path("/soc") {
181    ///     for child in children {
182    ///         println!("{}", child.name());
183    ///     }
184    /// }
185    /// ```
186    pub fn find_children_by_path(&self, path: &str) -> ChildrenIter<'a> {
187        let Some(path) = self.normalize_path(path) else {
188            return ChildrenIter {
189                node_iter: self.all_nodes(),
190                child_level: 0,
191                done: true,
192            };
193        };
194        let split = path.trim_matches('/').split('/');
195
196        let mut iter = self.all_nodes();
197        let mut target_level = 0usize;
198
199        for part in split {
200            if part.is_empty() {
201                // Root path "/" — skip the root node itself
202                iter.next();
203                break;
204            }
205            let mut found = false;
206            for node in iter.by_ref() {
207                if node.name() == part {
208                    found = true;
209                    target_level = node.level();
210                    break;
211                }
212            }
213            if !found {
214                return ChildrenIter {
215                    node_iter: self.all_nodes(),
216                    child_level: 0,
217                    done: true,
218                };
219            }
220        }
221
222        let child_level = target_level + 1;
223        ChildrenIter {
224            node_iter: iter,
225            child_level,
226            done: false,
227        }
228    }
229
230    /// Resolve an alias to its full path.
231    ///
232    /// Looks up the alias in the /aliases node and returns the corresponding
233    /// path string.
234    fn resolve_alias(&self, alias: &str) -> Option<&'a str> {
235        let aliases_node = self.find_by_path("/aliases")?;
236        aliases_node.find_property_str(alias)
237    }
238
239    /// Normalize a path to an absolute path.
240    ///
241    /// If the path starts with '/', it's returned as-is. Otherwise,
242    /// it's treated as an alias and resolved.
243    fn normalize_path(&self, path: &'a str) -> Option<&'a str> {
244        if path.starts_with('/') {
245            Some(path)
246        } else {
247            self.resolve_alias(path)
248        }
249    }
250
251    /// Translate a device address to a CPU physical address.
252    ///
253    /// This function implements address translation similar to Linux's
254    /// `of_translate_address`. It walks up the device tree hierarchy,
255    /// applying each parent's `ranges` property to translate the child
256    /// address space to the parent address space.
257    ///
258    /// The translation starts from the node at `path` and walks up through
259    /// each parent, applying the `ranges` property until reaching the root.
260    ///
261    /// # Arguments
262    ///
263    /// * `path` - Node path (absolute path starting with '/' or alias name)
264    /// * `address` - Device address from the node's `reg` property
265    ///
266    /// # Returns
267    ///
268    /// The translated CPU physical address. If translation fails at any
269    /// point (e.g., a parent node has no `ranges` property), the original
270    /// address is returned.
271    pub fn translate_address(&self, path: &'a str, address: u64) -> u64 {
272        let mut addresses = [address];
273        self.translate_addresses(path, &mut addresses);
274        addresses[0]
275    }
276
277    /// Translate multiple device addresses to CPU physical addresses in a single pass.
278    ///
279    /// This is more efficient than calling `translate_address` multiple times
280    /// for the same node path, because the tree is walked only once. Each
281    /// parent node's `ranges` property is looked up once and applied to all
282    /// addresses in the batch.
283    ///
284    /// # Arguments
285    ///
286    /// * `path` - Node path (absolute path starting with '/' or alias name)
287    /// * `addresses` - Mutable slice of device addresses to translate in place.
288    ///   The addresses are modified with the translated CPU physical addresses.
289    ///
290    /// If translation fails for any address at any level, the original address
291    /// value is preserved for that address.
292    pub fn translate_addresses(&self, path: &'a str, addresses: &mut [u64]) {
293        let path = match self.normalize_path(path) {
294            Some(p) => p,
295            None => return,
296        };
297
298        let path_parts = Self::split_path(path);
299        if path_parts.is_empty() {
300            return;
301        }
302
303        self.translate_addresses_with_parts(&path_parts, addresses);
304    }
305
306    /// Splits an absolute path into its component parts.
307    ///
308    /// Takes a path like "/soc/serial@0" and returns ["soc", "serial@0"].
309    fn split_path(path: &str) -> heapless::Vec<&str, 16> {
310        path.trim_matches('/')
311            .split('/')
312            .filter(|s| !s.is_empty())
313            .collect()
314    }
315
316    /// Performs batch address translation using pre-split path components.
317    ///
318    /// Walks up the tree from the deepest node, applying `ranges` at each level
319    /// to all addresses in the slice. Each parent node is looked up only once.
320    fn translate_addresses_with_parts(&self, path_parts: &[&str], addresses: &mut [u64]) {
321        // Walk up from the deepest node, applying ranges at each level
322        // We start from the second-to-last level (the target node itself is skipped)
323        for depth in (0..path_parts.len()).rev() {
324            let parent_parts = &path_parts[..depth];
325
326            if parent_parts.is_empty() {
327                // Reached root node, no more translation needed
328                break;
329            }
330
331            if let Some(parent_node) = self.find_node_by_parts(parent_parts) {
332                let ranges = match parent_node.ranges() {
333                    Some(r) => r,
334                    None => break, // No ranges property, stop translation
335                };
336
337                // Apply ranges to all addresses in batch
338                for addr in addresses.iter_mut() {
339                    *addr = Self::apply_ranges_one(&ranges, *addr);
340                }
341            }
342        }
343    }
344
345    /// Finds a node by its path component parts.
346    fn find_node_by_parts(&self, parts: &[&str]) -> Option<Node<'a>> {
347        let mut path = heapless::String::<256>::new();
348        path.push('/').ok();
349        for (i, part) in parts.iter().enumerate() {
350            if i > 0 {
351                path.push('/').ok();
352            }
353            path.push_str(part).ok();
354        }
355        self.find_by_path(path.as_str())
356    }
357
358    /// Translates a single address using the given ranges.
359    ///
360    /// If the address falls within a range, it is translated. Otherwise,
361    /// the original address is returned unchanged.
362    fn apply_ranges_one(ranges: &VecRange<'_>, address: u64) -> u64 {
363        for range in ranges.iter() {
364            // Check if the address falls within this range
365            if address >= range.child_address && address < range.child_address + range.length {
366                let offset = address - range.child_address;
367                return range.parent_address + offset;
368            }
369        }
370
371        // No matching range found, return as-is
372        address
373    }
374
375    /// Returns an iterator over memory reservation entries.
376    pub fn memory_reservations(&self) -> MemoryReservationIter<'a> {
377        MemoryReservationIter {
378            data: self.data.as_slice(),
379            offset: self.header.off_mem_rsvmap as usize,
380        }
381    }
382
383    /// Returns the /chosen node if it exists.
384    pub fn chosen(&self) -> Option<Chosen<'a>> {
385        for node in self.all_nodes() {
386            if let Node::Chosen(c) = node {
387                return Some(c);
388            }
389        }
390        None
391    }
392
393    /// Returns an iterator over all memory nodes.
394    pub fn memory(&self) -> impl Iterator<Item = Memory<'a>> + 'a {
395        self.all_nodes().filter_map(|node| {
396            if let Node::Memory(mem) = node {
397                Some(mem)
398            } else {
399                None
400            }
401        })
402    }
403
404    /// Returns an iterator over nodes in the /reserved-memory region.
405    pub fn reserved_memory(&self) -> impl Iterator<Item = Node<'a>> + 'a {
406        ReservedMemoryIter {
407            node_iter: self.all_nodes(),
408            in_reserved_memory: false,
409            reserved_level: 0,
410        }
411    }
412}
413
414/// Iterator over nodes in the /reserved-memory region.
415///
416/// Yields all child nodes of the /reserved-memory node, which describe
417/// memory regions that are reserved for specific purposes.
418struct ReservedMemoryIter<'a> {
419    node_iter: FdtIter<'a>,
420    in_reserved_memory: bool,
421    reserved_level: usize,
422}
423
424impl<'a> Iterator for ReservedMemoryIter<'a> {
425    type Item = Node<'a>;
426
427    fn next(&mut self) -> Option<Self::Item> {
428        for node in self.node_iter.by_ref() {
429            if node.name() == "reserved-memory" {
430                self.in_reserved_memory = true;
431                self.reserved_level = node.level();
432                continue;
433            }
434
435            if self.in_reserved_memory {
436                if node.level() <= self.reserved_level {
437                    // Left the reserved-memory node
438                    self.in_reserved_memory = false;
439                    return None;
440                } else {
441                    return Some(node);
442                }
443            }
444        }
445        None
446    }
447}
448
449/// Iterator over direct children of a specific node.
450///
451/// Yields only nodes whose level equals `child_level`. Nodes deeper
452/// than `child_level` (grandchildren) are skipped, and iteration stops
453/// when leaving the parent's subtree (level < child_level).
454pub struct ChildrenIter<'a> {
455    node_iter: FdtIter<'a>,
456    child_level: usize,
457    done: bool,
458}
459
460impl<'a> Iterator for ChildrenIter<'a> {
461    type Item = Node<'a>;
462
463    fn next(&mut self) -> Option<Self::Item> {
464        if self.done {
465            return None;
466        }
467        for node in self.node_iter.by_ref() {
468            if node.level() == self.child_level {
469                return Some(node);
470            }
471            if node.level() < self.child_level {
472                // Left the parent's subtree
473                self.done = true;
474                return None;
475            }
476            // node.level() > self.child_level: grandchild, skip
477        }
478        None
479    }
480}
481
482impl fmt::Display for Fdt<'_> {
483    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
484        writeln!(f, "/dts-v1/;")?;
485        writeln!(f)?;
486
487        let mut state = DisplayState::new();
488
489        for node in self.all_nodes() {
490            self.close_open_nodes(f, &mut state, node.level())?;
491            self.write_node(f, &node)?;
492            state.prev_level = node.level() + 1;
493        }
494
495        self.close_all_nodes(f, &mut state)
496    }
497}
498
499/// State for tracking the display output during tree traversal.
500struct DisplayState {
501    prev_level: usize,
502}
503
504impl DisplayState {
505    fn new() -> Self {
506        Self { prev_level: 0 }
507    }
508}
509
510impl Fdt<'_> {
511    /// Writes a single node in DTS format.
512    fn write_node(&self, f: &mut fmt::Formatter<'_>, node: &Node<'_>) -> fmt::Result {
513        fmt_utils::write_indent(f, node.level(), "    ")?;
514        let name = Self::format_node_name(node.name());
515        writeln!(f, "{} {{", name)?;
516
517        for prop in node.properties() {
518            fmt_utils::write_indent(f, node.level() + 1, "    ")?;
519            writeln!(f, "{};", prop)?;
520        }
521        Ok(())
522    }
523
524    /// Formats a node name, replacing empty names with "/".
525    fn format_node_name(name: &str) -> &str {
526        if name.is_empty() { "/" } else { name }
527    }
528
529    /// Closes nodes that are no longer open in the tree traversal.
530    fn close_open_nodes(
531        &self,
532        f: &mut fmt::Formatter<'_>,
533        state: &mut DisplayState,
534        current_level: usize,
535    ) -> fmt::Result {
536        while state.prev_level > current_level {
537            state.prev_level -= 1;
538            fmt_utils::write_indent(f, state.prev_level, "    ")?;
539            writeln!(f, "}};\n")?;
540        }
541        Ok(())
542    }
543
544    /// Closes all remaining open nodes at the end of output.
545    fn close_all_nodes(&self, f: &mut fmt::Formatter<'_>, state: &mut DisplayState) -> fmt::Result {
546        while state.prev_level > 0 {
547            state.prev_level -= 1;
548            fmt_utils::write_indent(f, state.prev_level, "    ")?;
549            writeln!(f, "}};\n")?;
550        }
551        Ok(())
552    }
553}
554
555impl fmt::Debug for Fdt<'_> {
556    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
557        writeln!(f, "Fdt {{")?;
558        writeln!(f, "\theader: {:?}", self.header)?;
559        writeln!(f, "\tnodes:")?;
560
561        for node in self.all_nodes() {
562            self.debug_node(f, &node)?;
563        }
564
565        writeln!(f, "}}")
566    }
567}
568
569impl Fdt<'_> {
570    /// Writes a node in debug format.
571    fn debug_node(&self, f: &mut fmt::Formatter<'_>, node: &Node<'_>) -> fmt::Result {
572        let level = node.level();
573        fmt_utils::write_indent(f, level + 2, "\t")?;
574
575        let name = Self::format_node_name(node.name());
576        writeln!(
577            f,
578            "[{}] address_cells={}, size_cells={}",
579            name, node.address_cells, node.size_cells
580        )?;
581
582        for prop in node.properties() {
583            self.debug_property(f, level, &prop)?;
584        }
585        Ok(())
586    }
587
588    /// Writes a property in debug format.
589    fn debug_property(
590        &self,
591        f: &mut fmt::Formatter<'_>,
592        level: usize,
593        prop: &Property<'_>,
594    ) -> fmt::Result {
595        fmt_utils::write_indent(f, level + 3, "\t")?;
596
597        match () {
598            () if prop.as_address_cells().is_some() => {
599                writeln!(f, "#address-cells: {}", prop.as_address_cells().unwrap())?
600            }
601            () if prop.as_size_cells().is_some() => {
602                writeln!(f, "#size-cells: {}", prop.as_size_cells().unwrap())?
603            }
604            () if prop.as_interrupt_cells().is_some() => writeln!(
605                f,
606                "#interrupt-cells: {}",
607                prop.as_interrupt_cells().unwrap()
608            )?,
609            () if prop.as_status().is_some() => {
610                writeln!(f, "status: {:?}", prop.as_status().unwrap())?
611            }
612            () if prop.as_phandle().is_some() => {
613                writeln!(f, "phandle: {}", prop.as_phandle().unwrap())?
614            }
615            () if prop.is_empty() => writeln!(f, "{}", prop.name())?,
616            () if prop.as_str().is_some() => {
617                writeln!(f, "{}: \"{}\"", prop.name(), prop.as_str().unwrap())?
618            }
619            () if prop.len() == 4 => {
620                let v = u32::from_be_bytes(prop.data().as_slice().try_into().unwrap());
621                writeln!(f, "{}: {:#x}", prop.name(), v)?
622            }
623            () => writeln!(f, "{}: <{} bytes>", prop.name(), prop.len())?,
624        }
625        Ok(())
626    }
627}
628
629#[cfg(test)]
630mod tests {
631    use super::*;
632    use heapless::Vec;
633
634    #[test]
635    fn test_memory_reservation_iterator() {
636        // Create simple test data: one memory reservation entry + terminator
637        let mut test_data = [0u8; data::MEM_RSV_ENTRY_SIZE * 2];
638
639        // Address: 0x80000000, Size: 0x10000000 (256MB)
640        test_data[0..8].copy_from_slice(&0x80000000u64.to_be_bytes());
641        test_data[8..16].copy_from_slice(&0x10000000u64.to_be_bytes());
642        // Terminator: address=0, size=0
643        test_data[16..24].copy_from_slice(&0u64.to_be_bytes());
644        test_data[24..32].copy_from_slice(&0u64.to_be_bytes());
645
646        let iter = MemoryReservationIter {
647            data: &test_data,
648            offset: 0,
649        };
650
651        let reservations: Vec<MemoryReservation, 4> = iter.collect();
652        assert_eq!(reservations.len(), 1);
653        assert_eq!(reservations[0].address, 0x80000000);
654        assert_eq!(reservations[0].size, 0x10000000);
655    }
656
657    #[test]
658    fn test_empty_memory_reservation_iterator() {
659        // Only terminator
660        let mut test_data = [0u8; data::MEM_RSV_ENTRY_SIZE];
661        test_data[0..8].copy_from_slice(&0u64.to_be_bytes());
662        test_data[8..16].copy_from_slice(&0u64.to_be_bytes());
663
664        let iter = MemoryReservationIter {
665            data: &test_data,
666            offset: 0,
667        };
668
669        let reservations: Vec<MemoryReservation, 4> = iter.collect();
670        assert_eq!(reservations.len(), 0);
671    }
672}