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}