zephyr_build/
devicetree.rs

1//! Incorporating Zephyr's devicetree into Rust.
2//!
3//! Zephyr depends fairly heavily on the devicetree for configuration.  The build system reads
4//! multiple DTS files, and coalesces this into a single devicetree.  This tree is output in a few
5//! different ways:
6//!
7//! - Canonical DTS.  There is a single DTS file (`build/zephyr/zephyr.dts`) that contains the final
8//!   tree, but still in DTS format (the DTB file would have information discarded).
9//!
10//! - Generated.  The C header `devicetree_generated.h` contains all of the definitions.  This isn't
11//!   a particularly friendly file to read or parse, but it does have one piece of information that is
12//!   not represented anywhere else: the mapping between devicetree nodes and their "ORD" index.  The
13//!   device nodes in the system are indexed by this number, and we need this in order to be able to
14//!   reference the nodes from Rust.
15//!
16//! Beyond the ORD field, it seems easier to deal with the DTS file itself.  Parsing is fairly
17//! straightforward, as it is a subset of the DTS format, and we only have to be able to deal with
18//! the files that are generated by the Zephyr build process.
19
20// TODO: Turn this off.
21#![allow(dead_code)]
22
23use ordmap::OrdMap;
24use std::{cell::RefCell, collections::BTreeMap, path::Path, rc::Rc};
25
26mod augment;
27mod ordmap;
28mod output;
29mod parse;
30
31pub use augment::{load_augments, Augment};
32
33/// Representation of a parsed device tree.
34pub struct DeviceTree {
35    /// The root of the tree.
36    root: Rc<Node>,
37    /// All of the labels.
38    /// Note that this is a BTree  so that the output will be deterministic.
39    labels: BTreeMap<String, Rc<Node>>,
40}
41
42// A single node in a [`DeviceTree`].
43pub struct Node {
44    // The name of the node itself.
45    name: String,
46    // The full path of this node in the tree.
47    path: String,
48    // The "route" is the path, but still as separate entries.
49    route: Vec<String>,
50    // The ord index in this particular Zephyr build.
51    ord: usize,
52    // Labels attached to this node.
53    labels: Vec<String>,
54    // Any properties set in this node.
55    properties: Vec<Property>,
56    // Children nodes.
57    children: Vec<Rc<Node>>,
58    // The parent. Should be non-null except at the root node.
59    parent: RefCell<Option<Rc<Node>>>,
60}
61
62#[derive(Debug)]
63pub struct Property {
64    pub name: String,
65    pub value: Vec<Value>,
66}
67
68// Although the real device flattends all of these into bytes, Zephyr takes advantage of them at a
69// slightly higher level.
70#[derive(Debug)]
71pub enum Value {
72    Words(Vec<Word>),
73    Bytes(Vec<u8>),
74    Phandle(Phandle),
75    String(String),
76}
77
78/// A phandle is a named reference to a labeled part of the DT.  We resolve this by making the
79/// reference optional, and filling them in afterwards.
80pub struct Phandle {
81    /// The label of our target.  Keep this because it may be useful to know which label was used,
82    /// as nodes often have multiple labels.
83    name: String,
84    /// The inside of the node, inner mutability so this can be looked up and cached.
85    node: RefCell<Option<Rc<Node>>>,
86}
87
88#[derive(Debug)]
89pub enum Word {
90    Number(u32),
91    Phandle(Phandle),
92}
93
94impl DeviceTree {
95    /// Decode the `zephyr.dts` and `devicetree_generated.h` files from the build and build an internal
96    /// representation of the devicetree itself.
97    pub fn new<P1: AsRef<Path>, P2: AsRef<Path>>(dts_path: P1, dt_gen: P2) -> DeviceTree {
98        let ords = OrdMap::new(dt_gen);
99
100        let dts = std::fs::read_to_string(dts_path).expect("Reading zephyr.dts file");
101        let dt = parse::parse(&dts, &ords);
102
103        // Walk the node tree, fixing any phandles to include their reference.
104        dt.root.phandle_walk(&dt.labels);
105
106        // Walk the node tree, setting each node's parent appropriately.
107        dt.root.parent_walk();
108
109        dt
110    }
111}
112
113impl Node {
114    fn phandle_walk(&self, labels: &BTreeMap<String, Rc<Node>>) {
115        for prop in &self.properties {
116            for value in &prop.value {
117                value.phandle_walk(labels);
118            }
119        }
120        for child in &self.children {
121            child.phandle_walk(labels);
122        }
123    }
124
125    fn parent_walk(self: &Rc<Self>) {
126        for child in &self.children {
127            *(child.parent.borrow_mut()) = Some(self.clone());
128            child.parent_walk()
129        }
130    }
131
132    fn is_compatible(&self, name: &str) -> bool {
133        self.properties
134            .iter()
135            .filter(|p| p.name == "compatible")
136            .flat_map(|prop| prop.value.iter())
137            .any(|v| matches!(v, Value::String(vn) if name == vn))
138    }
139
140    /// A richer compatible test.  Walks a series of names, in reverse.  Any that are "Some(x)" must
141    /// be compatible with "x" at that level.
142    fn compatible_path(&self, path: &[Option<&str>]) -> bool {
143        if let Some(first) = path.first() {
144            if !matches!(first, Some(name) if !self.is_compatible(name)) {
145                return false;
146            }
147
148            // Walk down the tree with the remainder of the path.
149            if let Some(child) = self.parent.borrow().as_ref() {
150                child.compatible_path(&path[1..])
151            } else {
152                // We've run out of nodes, so this is considered not matching.
153                false
154            }
155        } else {
156            // The empty path always matches.
157            true
158        }
159    }
160
161    /// Returns `true` if there is a property with this name.
162    fn has_prop(&self, name: &str) -> bool {
163        self.properties.iter().any(|p| p.name == name)
164    }
165
166    /// Returns the slice of values of a property with this name as `Some` or `None` if the property
167    /// does not exist.
168    fn get_property(&self, name: &str) -> Option<&[Value]> {
169        self.properties.iter().find_map(|p| {
170            if p.name == name {
171                Some(p.value.as_slice())
172            } else {
173                None
174            }
175        })
176    }
177
178    /// Attempt to retrieve the named property, as a single entry of Words.
179    fn get_words(&self, name: &str) -> Option<&[Word]> {
180        self.get_property(name).and_then(|p| match p {
181            &[Value::Words(ref w)] => Some(w.as_ref()),
182            _ => None,
183        })
184    }
185
186    /// Get a property that consists of a single number.
187    fn get_number(&self, name: &str) -> Option<u32> {
188        self.get_words(name).and_then(|p| {
189            if let &[Word::Number(n)] = p {
190                Some(n)
191            } else {
192                None
193            }
194        })
195    }
196
197    /// Get a property that consists of multiple numbers.
198    fn get_numbers(&self, name: &str) -> Option<Vec<u32>> {
199        let mut result = vec![];
200        for word in self.get_words(name)? {
201            if let Word::Number(n) = word {
202                result.push(*n);
203            } else {
204                return None;
205            }
206        }
207        Some(result)
208    }
209
210    /// Get a property that is a single string.
211    fn get_single_string(&self, name: &str) -> Option<&str> {
212        self.get_property(name).and_then(|p| {
213            if let &[Value::String(ref text)] = p {
214                Some(text.as_ref())
215            } else {
216                None
217            }
218        })
219    }
220}
221
222impl Value {
223    fn phandle_walk(&self, labels: &BTreeMap<String, Rc<Node>>) {
224        match self {
225            Self::Phandle(ph) => ph.phandle_resolve(labels),
226            Self::Words(words) => {
227                for w in words {
228                    if let Word::Phandle(ph) = w {
229                        ph.phandle_resolve(labels);
230                    }
231                }
232            }
233            _ => (),
234        }
235    }
236}
237
238impl Phandle {
239    /// Construct a phandle that is unresolved.
240    pub fn new(name: String) -> Self {
241        Self {
242            name,
243            node: RefCell::new(None),
244        }
245    }
246
247    /// Resolve this phandle, with the given label for lookup.
248    fn phandle_resolve(&self, labels: &BTreeMap<String, Rc<Node>>) {
249        // If already resolve, just return.
250        if self.node.borrow().is_some() {
251            return;
252        }
253
254        let node = labels.get(&self.name).cloned().expect("Missing phandle");
255        *self.node.borrow_mut() = Some(node);
256    }
257
258    /// Get the child node, panicing if it wasn't resolved properly.
259    fn node_ref(&self) -> Rc<Node> {
260        self.node.borrow().as_ref().unwrap().clone()
261    }
262}
263
264impl Word {
265    pub fn as_number(&self) -> Option<u32> {
266        match self {
267            Self::Number(n) => Some(*n),
268            _ => None,
269        }
270    }
271}
272
273// To avoid recursion, the debug printer for Phandle just prints the name.
274impl std::fmt::Debug for Phandle {
275    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
276        fmt.debug_struct("Phandle")
277            .field("name", &self.name)
278            .finish_non_exhaustive()
279    }
280}