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