flat_device_tree/
node.rs

1// This Source Code Form is subject to the terms of the Mozilla Public License,
2// v. 2.0. If a copy of the MPL was not distributed with this file, You can
3// obtain one at https://mozilla.org/MPL/2.0/.
4
5//! Device Tree node parsing and manipulation.
6
7use core::ffi::CStr;
8
9use crate::parsing::{BigEndianU32, BigEndianU64, FdtData};
10use crate::standard_nodes::{Compatible, MemoryRange, MemoryRegion};
11use crate::{Error, Fdt, Result};
12
13mod cell_size;
14
15pub use cell_size::*;
16
17const FDT_BEGIN_NODE: u32 = 1;
18const FDT_END_NODE: u32 = 2;
19const FDT_PROP: u32 = 3;
20pub(crate) const FDT_NOP: u32 = 4;
21const FDT_END: u32 = 5;
22
23#[derive(Debug, Clone, Copy)]
24#[repr(C)]
25struct FdtProperty {
26    len: BigEndianU32,
27    name_offset: BigEndianU32,
28}
29
30impl FdtProperty {
31    fn from_bytes(bytes: &mut FdtData<'_>) -> Option<Self> {
32        let len = bytes.u32()?;
33        let name_offset = bytes.u32()?;
34
35        Some(Self { len, name_offset })
36    }
37}
38
39/// A devicetree node
40#[derive(Debug, Clone, Copy)]
41pub struct FdtNode<'b, 'a: 'b> {
42    pub name: &'a str,
43    pub(crate) header: &'b Fdt<'a>,
44    props: &'a [u8],
45    parent_props: Option<&'a [u8]>,
46}
47
48#[cfg(feature = "pretty-printing")]
49impl core::fmt::Display for FdtNode<'_, '_> {
50    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
51        crate::pretty_print::print_node(f, *self, 0)?;
52        Ok(())
53    }
54}
55
56impl<'b, 'a: 'b> FdtNode<'b, 'a> {
57    fn new(
58        name: &'a str,
59        header: &'b Fdt<'a>,
60        props: &'a [u8],
61        parent_props: Option<&'a [u8]>,
62    ) -> Self {
63        Self { name, header, props, parent_props }
64    }
65
66    /// Returns an iterator over the available properties of the node
67    pub fn properties(self) -> impl Iterator<Item = NodeProperty<'a>> + 'b {
68        let mut stream = FdtData::new(self.props);
69        let mut done = false;
70
71        core::iter::from_fn(move || {
72            if stream.is_empty() || done {
73                return None;
74            }
75
76            while stream.peek_u32()?.get() == FDT_NOP {
77                stream.skip(4);
78            }
79
80            if stream.peek_u32()?.get() == FDT_PROP {
81                NodeProperty::parse(&mut stream, self.header).ok()
82            } else {
83                done = true;
84                None
85            }
86        })
87    }
88
89    /// Attempts to find the a property by its name
90    pub fn property(self, name: &str) -> Option<NodeProperty<'a>> {
91        self.properties().find(|p| p.name == name)
92    }
93
94    /// Returns an iterator over the children of the current node
95    pub fn children(self) -> impl Iterator<Item = FdtNode<'b, 'a>> {
96        let mut stream = FdtData::new(self.props);
97
98        while stream.peek_u32().unwrap_or_default().get() == FDT_NOP {
99            stream.skip(4);
100        }
101
102        while stream.peek_u32().unwrap_or_default().get() == FDT_PROP {
103            NodeProperty::parse(&mut stream, self.header).ok();
104        }
105
106        let mut done = false;
107
108        core::iter::from_fn(move || {
109            if stream.is_empty() || done {
110                return None;
111            }
112
113            while stream.peek_u32()?.get() == FDT_NOP {
114                stream.skip(4);
115            }
116
117            if stream.peek_u32()?.get() == FDT_BEGIN_NODE {
118                let origin = stream.remaining();
119                let ret = {
120                    stream.skip(4);
121                    let unit_name =
122                        CStr::from_bytes_until_nul(stream.remaining()).ok()?.to_str().ok()?;
123                    let full_name_len = unit_name.len() + 1;
124                    stream.skip(full_name_len);
125
126                    if full_name_len % 4 != 0 {
127                        stream.skip(4 - (full_name_len % 4));
128                    }
129
130                    Some(Self::new(unit_name, self.header, stream.remaining(), Some(self.props)))
131                };
132
133                stream = FdtData::new(origin);
134
135                skip_current_node(&mut stream, self.header);
136
137                ret
138            } else {
139                done = true;
140                None
141            }
142        })
143    }
144
145    /// `reg` property
146    ///
147    /// Important: this method assumes that the value(s) inside the `reg`
148    /// property represent CPU-addressable addresses that are able to fit within
149    /// the platform's pointer size (e.g. `#address-cells` and `#size-cells` are
150    /// less than or equal to 2 for a 64-bit platform). If this is not the case
151    /// or you're unsure of whether this applies to the node, it is recommended
152    /// to use the [`FdtNode::property`] method to extract the raw value slice
153    /// or use the provided [`FdtNode::raw_reg`] helper method to give you an
154    /// iterator over the address and size slices. One example of where this
155    /// would return `None` for a node is a `pci` child node which contains the
156    /// PCI address information in the `reg` property, of which the address has
157    /// an `#address-cells` value of 3.
158    pub fn reg(self) -> impl Iterator<Item = MemoryRegion> + 'a {
159        let sizes = self.parent_cell_sizes();
160
161        let mut reg = self.properties().find(|p| p.name == "reg").map(|p| FdtData::new(p.value));
162
163        core::iter::from_fn(move || match reg.as_mut() {
164            Some(stream) => {
165                let starting_address = match sizes.address_cells {
166                    CellSize::One => Some(stream.u32()?.get() as usize),
167                    CellSize::Two => Some(stream.u64()?.get() as usize),
168                    _ => None,
169                }? as *const u8;
170
171                let size = match sizes.size_cells {
172                    CellSize::None => Some(None),
173                    CellSize::One => Some(Some(stream.u32()?.get() as usize)),
174                    CellSize::Two => Some(Some(stream.u64()?.get() as usize)),
175                    _ => None,
176                }?;
177
178                Some(MemoryRegion { starting_address, size })
179            }
180            None => None,
181        })
182    }
183
184    /// Parses the `ranges` property as an iterator over [MemoryRange].
185    pub fn ranges(self) -> impl Iterator<Item = MemoryRange> + 'a {
186        let sizes = self.cell_sizes();
187        let parent_sizes = self.parent_cell_sizes();
188
189        let mut ranges =
190            self.properties().find(|p| p.name == "ranges").map(|p| FdtData::new(p.value));
191
192        core::iter::from_fn(move || match ranges.as_mut() {
193            Some(stream) => {
194                let (child_bus_address_hi, child_bus_address) = match sizes.address_cells {
195                    CellSize::One => Some((0, stream.u32()?.get() as usize)),
196                    CellSize::Two => Some((0, stream.u64()?.get() as usize)),
197                    CellSize::Three => Some((stream.u32()?.get(), stream.u64()?.get() as usize)),
198                    _ => None,
199                }?;
200
201                let parent_bus_address = match parent_sizes.address_cells {
202                    CellSize::One => Some(stream.u32()?.get() as usize),
203                    CellSize::Two => Some(stream.u64()?.get() as usize),
204                    _ => None,
205                }?;
206
207                let size = match sizes.size_cells {
208                    CellSize::One => Some(stream.u32()?.get() as usize),
209                    CellSize::Two => Some(stream.u64()?.get() as usize),
210                    _ => None,
211                }?;
212
213                Some(MemoryRange {
214                    child_bus_address,
215                    child_bus_address_hi,
216                    parent_bus_address,
217                    size,
218                })
219            }
220            None => None,
221        })
222    }
223
224    /// Convenience method that provides an iterator over the raw bytes for the
225    /// address and size values inside of the `reg` property
226    pub fn raw_reg(self) -> impl Iterator<Item = RawReg<'a>> + 'a {
227        let sizes = self.parent_cell_sizes();
228
229        let mut reg = self.property("reg").map(|p| FdtData::new(p.value));
230        core::iter::from_fn(move || match reg.as_mut() {
231            Some(stream) => Some(RawReg {
232                address: stream.take(sizes.address_cells.to_usize() * 4)?,
233                size: stream.take(sizes.size_cells.to_usize() * 4)?,
234            }),
235            None => None,
236        })
237    }
238
239    /// `compatible` property
240    pub fn compatible(self) -> Option<Compatible<'a>> {
241        let mut s = None;
242        for prop in self.properties() {
243            if prop.name == "compatible" {
244                s = Some(Compatible { data: prop.value });
245            }
246        }
247
248        s
249    }
250
251    /// Cell sizes for child nodes
252    pub fn cell_sizes(self) -> CellSizes {
253        let mut cell_sizes = CellSizes::default();
254
255        for property in self.properties() {
256            match property.name {
257                "#address-cells" => {
258                    cell_sizes.address_cells =
259                        BigEndianU32::from_bytes(property.value).unwrap_or_default().get().into();
260                }
261                "#size-cells" => {
262                    cell_sizes.size_cells =
263                        BigEndianU32::from_bytes(property.value).unwrap_or_default().get().into();
264                }
265                _ => {}
266            }
267        }
268
269        cell_sizes
270    }
271
272    /// Searches for the interrupt parent, if the node contains one
273    pub fn interrupt_parent(self) -> Option<FdtNode<'b, 'a>> {
274        self.properties()
275            .find(|p| p.name == "interrupt-parent")
276            .and_then(|p| self.header.find_phandle(BigEndianU32::from_bytes(p.value)?.get()))
277    }
278
279    /// `#interrupt-cells` property
280    pub fn interrupt_cells(self) -> Option<usize> {
281        let mut interrupt_cells = None;
282
283        if let Some(prop) = self.property("#interrupt-cells") {
284            interrupt_cells = BigEndianU32::from_bytes(prop.value).map(|n| n.get() as usize)
285        }
286
287        interrupt_cells
288    }
289
290    /// Searches for the `interrupts` property.
291    pub fn interrupts(self) -> impl Iterator<Item = usize> + 'a {
292        let sizes = self.parent_interrupt_cells();
293
294        let mut interrupt =
295            self.properties().find(|p| p.name == "interrupts").map(|p| FdtData::new(p.value));
296
297        core::iter::from_fn(move || match interrupt.as_mut() {
298            Some(stream) => match sizes {
299                Some(1) => Some(stream.u32()?.get() as usize),
300                Some(2) => Some(stream.u64()?.get() as usize),
301                _ => None,
302            },
303            None => None,
304        })
305    }
306
307    pub(crate) fn parent_cell_sizes(self) -> CellSizes {
308        let mut cell_sizes = CellSizes::default();
309
310        if let Some(parent) = self.parent_props {
311            let parent =
312                FdtNode { name: "", props: parent, header: self.header, parent_props: None };
313            cell_sizes = parent.cell_sizes();
314        }
315
316        cell_sizes
317    }
318
319    pub(crate) fn parent_interrupt_cells(self) -> Option<usize> {
320        let mut interrupt_cells = None;
321        let parent = self
322            .property("interrupt-parent")
323            .and_then(|p| self.header.find_phandle(BigEndianU32::from_bytes(p.value)?.get()))
324            .or_else(|| {
325                // attempt to find the `interrupt-parent` property in the parent node
326                let p = FdtNode::new("", self.header, self.parent_props?, None)
327                    .property("interrupt-parent")?;
328                self.header.find_phandle(BigEndianU32::from_bytes(p.value)?.get())
329            });
330
331        if let Some(size) = parent.and_then(|parent| parent.interrupt_cells()) {
332            interrupt_cells = Some(size);
333        }
334
335        interrupt_cells
336    }
337}
338
339/// The number of cells (big endian u32s) that addresses and sizes take
340#[derive(Debug, Clone, Copy, Eq, PartialEq)]
341pub struct CellSizes {
342    /// Size of values representing an address
343    pub address_cells: CellSize,
344    /// Size of values representing a size
345    pub size_cells: CellSize,
346}
347
348impl CellSizes {
349    /// Creates a new [CellSizes].
350    pub const fn new() -> Self {
351        Self { address_cells: CellSize::Two, size_cells: CellSize::One }
352    }
353}
354
355impl Default for CellSizes {
356    fn default() -> Self {
357        Self::new()
358    }
359}
360
361/// A raw `reg` property value set
362#[derive(Debug, Clone, Copy, PartialEq)]
363pub struct RawReg<'a> {
364    /// Big-endian encoded bytes making up the address portion of the property.
365    /// Length will always be a multiple of 4 bytes.
366    pub address: &'a [u8],
367    /// Big-endian encoded bytes making up the size portion of the property.
368    /// Length will always be a multiple of 4 bytes.
369    pub size: &'a [u8],
370}
371
372pub(crate) fn find_node<'b, 'a: 'b>(
373    stream: &mut FdtData<'a>,
374    name: &str,
375    header: &'b Fdt<'a>,
376    parent_props: Option<&'a [u8]>,
377) -> Option<FdtNode<'b, 'a>> {
378    let mut parts = name.splitn(2, '/');
379    let looking_for = parts.next()?;
380
381    stream.skip_nops();
382
383    let curr_data = stream.remaining();
384
385    match stream.u32()?.get() {
386        FDT_BEGIN_NODE => {}
387        _ => return None,
388    }
389
390    let unit_name = CStr::from_bytes_until_nul(stream.remaining()).ok()?.to_str().ok()?;
391
392    let full_name_len = unit_name.len() + 1;
393    skip_4_aligned(stream, full_name_len);
394
395    let looking_contains_addr = looking_for.contains('@');
396    let addr_name_same = unit_name == looking_for;
397    let base_name_same = unit_name.split('@').next()? == looking_for;
398
399    if (looking_contains_addr && !addr_name_same) || (!looking_contains_addr && !base_name_same) {
400        *stream = FdtData::new(curr_data);
401        skip_current_node(stream, header)?;
402
403        return None;
404    }
405
406    let next_part = match parts.next() {
407        None | Some("") => {
408            return Some(FdtNode::new(unit_name, header, stream.remaining(), parent_props))
409        }
410        Some(part) => part,
411    };
412
413    stream.skip_nops();
414
415    let parent_props = Some(stream.remaining());
416
417    while stream.peek_u32()?.get() == FDT_PROP {
418        let _ = NodeProperty::parse(stream, header);
419    }
420
421    while stream.peek_u32()?.get() == FDT_BEGIN_NODE {
422        if let Some(p) = find_node(stream, next_part, header, parent_props) {
423            return Some(p);
424        }
425    }
426
427    stream.skip_nops();
428
429    if stream.u32()?.get() != FDT_END_NODE {
430        return None;
431    }
432
433    None
434}
435
436// FIXME: this probably needs refactored
437pub(crate) fn all_nodes<'b, 'a: 'b>(header: &'b Fdt<'a>) -> impl Iterator<Item = FdtNode<'b, 'a>> {
438    let mut stream = FdtData::new(header.structs_block());
439    let mut done = false;
440    let mut parents: [&[u8]; 64] = [&[]; 64];
441    let mut parent_index = 0;
442
443    core::iter::from_fn(move || {
444        if stream.is_empty() || done {
445            return None;
446        }
447
448        while stream.peek_u32()?.get() == FDT_END_NODE {
449            parent_index -= 1;
450            stream.skip(4);
451        }
452
453        if stream.peek_u32()?.get() == FDT_END {
454            done = true;
455            return None;
456        }
457
458        while stream.peek_u32()?.get() == FDT_NOP {
459            stream.skip(4);
460        }
461
462        match stream.u32()?.get() {
463            FDT_BEGIN_NODE => {}
464            _ => return None,
465        }
466
467        let unit_name = CStr::from_bytes_until_nul(stream.remaining()).ok()?.to_str().ok()?;
468        let full_name_len = unit_name.len() + 1;
469        skip_4_aligned(&mut stream, full_name_len);
470
471        let curr_node = stream.remaining();
472
473        parent_index += 1;
474        parents[parent_index] = curr_node;
475
476        while stream.peek_u32()?.get() == FDT_NOP {
477            stream.skip(4);
478        }
479
480        while stream.peek_u32()?.get() == FDT_PROP {
481            NodeProperty::parse(&mut stream, header).ok()?;
482        }
483
484        Some(FdtNode {
485            name: if unit_name.is_empty() { "/" } else { unit_name },
486            header,
487            parent_props: match parent_index {
488                1 => None,
489                _ => parents.get(parent_index.saturating_sub(1)).copied(),
490            },
491            props: curr_node,
492        })
493    })
494}
495
496pub(crate) fn skip_current_node<'a>(stream: &mut FdtData<'a>, header: &Fdt<'a>) -> Option<()> {
497    if stream.u32()?.get() == FDT_BEGIN_NODE { Some(()) } else { None }?;
498
499    let unit_name = CStr::from_bytes_until_nul(stream.remaining()).ok()?.to_str().ok()?;
500    let full_name_len = unit_name.len().saturating_add(1);
501    skip_4_aligned(stream, full_name_len);
502
503    while stream.peek_u32()?.get() == FDT_PROP {
504        NodeProperty::parse(stream, header).ok()?;
505    }
506
507    while stream.peek_u32()?.get() == FDT_BEGIN_NODE {
508        skip_current_node(stream, header);
509    }
510
511    stream.skip_nops();
512
513    if stream.u32()?.get() == FDT_END_NODE {
514        Some(())
515    } else {
516        None
517    }
518}
519
520/// A node property
521#[derive(Debug, Clone, Copy)]
522pub struct NodeProperty<'a> {
523    /// Property name
524    pub name: &'a str,
525    /// Property value
526    pub value: &'a [u8],
527}
528
529impl<'a> NodeProperty<'a> {
530    /// Attempt to parse the property value as a `usize`
531    pub fn as_usize(self) -> Option<usize> {
532        match self.value.len() {
533            4 => BigEndianU32::from_bytes(self.value).map(|i| i.get() as usize),
534            8 => BigEndianU64::from_bytes(self.value).map(|i| i.get() as usize),
535            _ => None,
536        }
537    }
538
539    /// Attempts to parse the property value as a list of [`u64`].
540    ///
541    /// Only handles property values with uniform cell sizes.
542    ///
543    /// For `prop-encoded-array` property values use [iter_prop_encoded](Self::iter_prop_encoded).
544    pub fn iter_cell_size(self, cell_size: CellSize) -> impl Iterator<Item = u64> + 'a {
545        let mut cells = FdtData::new(self.value);
546
547        core::iter::from_fn(move || match cell_size {
548            CellSize::One => Some(cells.u32()?.get() as u64),
549            CellSize::Two => Some(cells.u64()?.get()),
550            _ => None,
551        })
552    }
553
554    /// Attempts to parse the property value as a `prop-encoded-array` list of [`u64`] tuples.
555    pub fn iter_prop_encoded(self, cell_sizes: CellSizes) -> impl Iterator<Item = (u64, u64)> + 'a {
556        let mut cells = FdtData::new(self.value);
557
558        core::iter::from_fn(move || {
559            let addr = match cell_sizes.address_cells {
560                CellSize::One => Some(cells.u32()?.get() as u64),
561                CellSize::Two => Some(cells.u64()?.get()),
562                _ => None,
563            }?;
564
565            let size = match cell_sizes.size_cells {
566                CellSize::One => Some(cells.u32()?.get() as u64),
567                CellSize::Two => Some(cells.u64()?.get()),
568                _ => None,
569            }?;
570
571            Some((addr, size))
572        })
573    }
574
575    /// Attempt to parse the property value as a `&str`
576    pub fn as_str(self) -> Option<&'a str> {
577        core::str::from_utf8(self.value).map(|s| s.trim_end_matches('\0')).ok()
578    }
579
580    /// Attempts to parse the property value as a list of [`&str`].
581    pub fn iter_str(self) -> impl Iterator<Item = &'a str> + 'a {
582        let mut strs = self.as_str().map(|s| s.split('\0'));
583
584        core::iter::from_fn(move || match strs.as_mut() {
585            Some(s) => s.next(),
586            None => None,
587        })
588    }
589
590    fn parse(stream: &mut FdtData<'a>, header: &Fdt<'a>) -> Result<Self> {
591        match stream.u32() {
592            Some(p) if p.get() == FDT_PROP => Ok(()),
593            Some(other) => Err(Error::BadPropTag((other.get(), FDT_PROP))),
594            None => Err(Error::BadPropTag((0, FDT_PROP))),
595        }?;
596
597        let prop = FdtProperty::from_bytes(stream).ok_or(Error::MissingProperty)?;
598        let data_len = prop.len.get() as usize;
599
600        let data = stream.remaining().get(..data_len).unwrap_or_default();
601
602        skip_4_aligned(stream, data_len);
603
604        Ok(NodeProperty {
605            name: header.str_at_offset(prop.name_offset.get() as usize),
606            value: data,
607        })
608    }
609}
610
611/// A memory reservation
612#[derive(Debug)]
613#[repr(C)]
614pub struct MemoryReservation {
615    pub(crate) address: BigEndianU64,
616    pub(crate) size: BigEndianU64,
617}
618
619impl MemoryReservation {
620    /// Pointer representing the memory reservation address
621    pub fn address(&self) -> *const u8 {
622        self.address.get() as usize as *const u8
623    }
624
625    /// Size of the memory reservation
626    pub fn size(&self) -> usize {
627        self.size.get() as usize
628    }
629
630    pub(crate) fn from_bytes(bytes: &mut FdtData<'_>) -> Option<Self> {
631        let address = bytes.u64()?;
632        let size = bytes.u64()?;
633
634        Some(Self { address, size })
635    }
636}
637
638fn skip_4_aligned(stream: &mut FdtData<'_>, len: usize) {
639    stream.skip((len + 3) & !0x3);
640}