devicetree/
types.rs

1// Copyright 2025 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use zerocopy::{BigEndian, FromBytes, Immutable, KnownLayout, U32, U64};
6
7/// The devictree header.
8///
9/// See https://devicetree-specification.readthedocs.io/en/v0.3/flattened-format.html#header
10#[derive(FromBytes, Debug, KnownLayout, Immutable)]
11#[repr(C)]
12pub struct Header {
13    /// The value 0xd00dfeed.
14    pub magic: U32<BigEndian>,
15
16    /// The total size in bytes of the devicetree data structure.
17    ///
18    /// This includes the header, the memory reservation block, structure block, as well
19    /// as any gaps between or after the blocks.
20    pub totalsize: U32<BigEndian>,
21
22    /// The offset, in bytes, of the structure block.
23    pub off_dt_struct: U32<BigEndian>,
24
25    /// The offset, in bytes, of the strings block.
26    pub off_dt_strings: U32<BigEndian>,
27
28    /// The offset, in bytes, of the memory reservation block.
29    pub off_mem_rsvmap: U32<BigEndian>,
30
31    /// The version of the devicetree structure.
32    pub version: U32<BigEndian>,
33
34    /// The lowest version of the devicetree data structure with which this version is
35    /// backwards compatible.
36    pub last_comp_version: U32<BigEndian>,
37
38    /// The physical ID of the system's boot CPU.
39    pub boot_cpuid_phys: U32<BigEndian>,
40
41    /// The size, in bytes, of the strings block section.
42    pub size_dt_strings: U32<BigEndian>,
43
44    /// The size, in bytes, of the structure block section.
45    pub size_dt_struct: U32<BigEndian>,
46}
47
48#[derive(FromBytes, Debug, KnownLayout, Immutable)]
49#[repr(C)]
50pub struct ReserveEntry {
51    /// The physical address of the reserved memory region.
52    pub address: U64<BigEndian>,
53
54    /// The size, in bytes, of the reserved memory region.
55    pub size: U64<BigEndian>,
56}
57
58#[derive(Debug)]
59pub struct Property<'a> {
60    /// The name of the property, which is stored as a null-terminated string in the strings
61    /// section.
62    pub name: &'a str,
63
64    /// The value of the property.
65    pub value: &'a [u8],
66}
67
68#[derive(Debug)]
69pub struct Node<'a> {
70    pub name: &'a str,
71    pub properties: Vec<Property<'a>>,
72    pub children: Vec<Node<'a>>,
73}
74
75impl<'a> Node<'a> {
76    pub(crate) fn new(name: &'a str) -> Self {
77        Node { name, properties: vec![], children: vec![] }
78    }
79
80    // Finds the first node with a name matching `prefix`, using a depth first search.
81    pub fn find(&self, prefix: &str) -> Option<&Node<'a>> {
82        if self.name.starts_with(prefix) {
83            return Some(self);
84        }
85
86        for child in &self.children {
87            let found_child = child.find(prefix);
88            if found_child.is_some() {
89                return found_child;
90            }
91        }
92
93        return None;
94    }
95
96    pub fn get_property(&self, name: &str) -> Option<&Property<'a>> {
97        self.properties.iter().find(|p| p.name == name)
98    }
99}
100
101#[derive(Debug)]
102pub struct Devicetree<'a> {
103    pub header: &'a Header,
104    pub reserve_entries: &'a [ReserveEntry],
105    pub root_node: Node<'a>,
106}
107
108#[cfg(test)]
109mod test {
110    use super::*;
111
112    #[test]
113    fn test_node_find() {
114        let child1 = Node { name: "child1", properties: vec![], children: vec![] };
115        let child2 = Node { name: "child2", properties: vec![], children: vec![] };
116        let root = Node { name: "root", properties: vec![], children: vec![child1, child2] };
117
118        assert_eq!(root.find("root").unwrap().name, "root");
119        assert_eq!(root.find("child1").unwrap().name, "child1");
120        assert_eq!(root.find("child2").unwrap().name, "child2");
121        assert!(root.find("nonexistent").is_none());
122    }
123
124    #[test]
125    fn test_node_find_nested() {
126        let grandchild = Node { name: "grandchild", properties: vec![], children: vec![] };
127        let child = Node { name: "child", properties: vec![], children: vec![grandchild] };
128        let root = Node { name: "root", properties: vec![], children: vec![child] };
129
130        assert_eq!(root.find("root").unwrap().name, "root");
131        assert_eq!(root.find("child").unwrap().name, "child");
132        assert_eq!(root.find("grandchild").unwrap().name, "grandchild");
133        assert!(root.find("nonexistent").is_none());
134    }
135
136    #[test]
137    fn test_node_find_duplicate() {
138        let property_value = vec![];
139        let property = Property { name: "p1", value: &property_value };
140        let child1 = Node { name: "child", properties: vec![property], children: vec![] };
141        let child2 = Node { name: "child", properties: vec![], children: vec![] };
142        let root = Node { name: "root", properties: vec![], children: vec![child1, child2] };
143
144        assert_eq!(root.find("child").unwrap().name, "child");
145        assert!(root.find("child").unwrap().get_property("p1").is_some());
146    }
147}