Skip to content

Commit 5fcdfc0

Browse files
committed
rust: zephyr-build: Conversion of Device Tree
This code parses the DTS file generated by the Zephyr build, along with a few entries from the generated header file, to build a representation of the device tree. There is a notion of "augments" that add various methods. This is currently just hard-coded. Signed-off-by: David Brown <[email protected]>
1 parent 3d91c8d commit 5fcdfc0

File tree

13 files changed

+1193
-3
lines changed

13 files changed

+1193
-3
lines changed

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ ZEPHYR_DTS = \"${ZEPHYR_DTS}\"
133133
INCLUDE_DIRS = \"${include_dirs}\"
134134
INCLUDE_DEFINES = \"${include_defines}\"
135135
WRAPPER_FILE = \"${WRAPPER_FILE}\"
136+
BINARY_DIR_INCLUDE_GENERATED = \"${BINARY_DIR_INCLUDE_GENERATED}\"
136137
137138
[patch.crates-io]
138139
${config_paths}
@@ -151,6 +152,7 @@ ${config_paths}
151152
INCLUDE_DIRS="${include_dirs}"
152153
INCLUDE_DEFINES="${include_defines}"
153154
WRAPPER_FILE="${WRAPPER_FILE}"
155+
BINARY_DIR_INCLUDE_GENERATED="${BINARY_DIR_INCLUDE_GENERATED}"
154156
cargo build
155157
# TODO: release flag if release build
156158
# --release

zephyr-build/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,7 @@ Provides utilities for accessing Kconfig and devicetree information.
1515
# used by the core Zephyr tree, but are needed by zephyr applications.
1616
[dependencies]
1717
regex = "1.10.3"
18+
pest = "2.6"
19+
pest_derive = "2.6"
20+
quote = "1.0"
21+
proc-macro2 = "1.0.86"

zephyr-build/src/devicetree.rs

Lines changed: 304 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,304 @@
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+
23+
use ordmap::OrdMap;
24+
use std::{cell::RefCell, collections::BTreeMap, path::Path, rc::Rc};
25+
26+
mod augment;
27+
mod ordmap;
28+
mod output;
29+
mod parse;
30+
31+
pub struct DeviceTree {
32+
/// The root of the tree.
33+
root: Rc<Node>,
34+
/// All of the labels.
35+
labels: BTreeMap<String, Rc<Node>>,
36+
}
37+
38+
// This is a single node in the devicetree.
39+
pub struct Node {
40+
// The name of the node itself.
41+
name: String,
42+
// The full path of this node in the tree.
43+
path: String,
44+
// The "route" is the path, but still as separate entries.
45+
route: Vec<String>,
46+
// The ord index in this particular Zephyr build.
47+
ord: usize,
48+
// Labels attached to this node.
49+
labels: Vec<String>,
50+
// Any properties set in this node.
51+
properties: Vec<Property>,
52+
// Children nodes.
53+
children: Vec<Rc<Node>>,
54+
// The parent. Should be non-null except at the root node.
55+
parent: RefCell<Option<Rc<Node>>>,
56+
}
57+
58+
#[derive(Debug)]
59+
pub struct Property {
60+
pub name: String,
61+
pub value: Vec<Value>,
62+
}
63+
64+
// Although the real device flattends all of these into bytes, Zephyr takes advantage of them at a
65+
// slightly higher level.
66+
#[derive(Debug)]
67+
pub enum Value {
68+
Words(Vec<Word>),
69+
Bytes(Vec<u8>),
70+
Phandle(Phandle), // TODO
71+
String(String),
72+
}
73+
74+
/// A phandle is a named reference to a labeled part of the DT. We resolve this by making the
75+
/// reference optional, and filling them in afterwards.
76+
pub struct Phandle {
77+
/// The label of our target. Keep this because it may be useful to know which label was used,
78+
/// as nodes often have multiple labels.
79+
name: String,
80+
/// The inside of the node, inner mutability so this can be looked up and cached.
81+
node: RefCell<Option<Rc<Node>>>,
82+
}
83+
84+
#[derive(Debug)]
85+
pub enum Word {
86+
Number(u32),
87+
Phandle(Phandle),
88+
}
89+
90+
impl DeviceTree {
91+
/// Decode the zephyr.dts and devicetree_generated.h files from the build and build an internal
92+
/// representation of the devicetree itself.
93+
pub fn new<P1: AsRef<Path>, P2: AsRef<Path>>(dts_path: P1, dt_gen: P2) -> DeviceTree {
94+
let ords = OrdMap::new(dt_gen);
95+
96+
let dts = std::fs::read_to_string(dts_path)
97+
.expect("Reading zephyr.dts file");
98+
let dt = parse::parse(&dts, &ords);
99+
dt.resolve_phandles();
100+
dt.set_parents();
101+
dt
102+
}
103+
104+
/// Walk the node tree, fixing any phandles to include their reference.
105+
fn resolve_phandles(&self) {
106+
self.root.phandle_walk(&self.labels);
107+
}
108+
109+
/// Walk the node tree, setting each node's parent appropriately.
110+
fn set_parents(&self) {
111+
self.root.parent_walk();
112+
}
113+
}
114+
115+
impl Node {
116+
fn phandle_walk(&self, labels: &BTreeMap<String, Rc<Node>>) {
117+
for prop in &self.properties {
118+
for value in &prop.value {
119+
value.phandle_walk(labels);
120+
}
121+
}
122+
for child in &self.children {
123+
child.phandle_walk(labels);
124+
}
125+
}
126+
127+
fn parent_walk(self: &Rc<Self>) {
128+
// *(self.parent.borrow_mut()) = Some(parent.clone());
129+
130+
for child in &self.children {
131+
*(child.parent.borrow_mut()) = Some(self.clone());
132+
child.parent_walk()
133+
}
134+
}
135+
136+
fn is_compatible(&self, name: &str) -> bool {
137+
if let Some(prop) = self.properties.iter().find(|p| p.name == "compatible") {
138+
prop.value.iter().any(|v| {
139+
match v {
140+
Value::String(vn) if name == vn => true,
141+
_ => false,
142+
}
143+
})
144+
} else {
145+
// If there is no compatible field, we are clearly not compatible.
146+
false
147+
}
148+
}
149+
150+
/// A richer compatible test. Walks a series of names, in reverse. Any that are "Some(x)" must
151+
/// be compatible with "x" at that level.
152+
fn compatible_path(&self, path: &[Option<&str>]) -> bool {
153+
let res = self.path_walk(path, 0);
154+
// println!("compatible? {}: {} {:?}", res, self.path, path);
155+
res
156+
}
157+
158+
/// Recursive path walk, to make borrowing simpler.
159+
fn path_walk(&self, path: &[Option<&str>], pos: usize) -> bool {
160+
if pos >= path.len() {
161+
// Once past the end, we consider everything a match.
162+
return true;
163+
}
164+
165+
// Check the failure condition, where this node isn't compatible with this section of the path.
166+
if let Some(name) = path[pos] {
167+
if !self.is_compatible(name) {
168+
return false;
169+
}
170+
}
171+
172+
// Walk down the tree. We have to check for None here, as we can't recurse on the none
173+
// case.
174+
if let Some(child) = self.parent.borrow().as_ref() {
175+
child.path_walk(path, pos + 1)
176+
} else {
177+
// We've run out of nodes, so this is considered not matching.
178+
false
179+
}
180+
}
181+
182+
/// Is the named property present?
183+
fn has_prop(&self, name: &str) -> bool {
184+
self.properties.iter().any(|p| p.name == name)
185+
}
186+
187+
/// Get this property in its entirety.
188+
fn get_property(&self, name: &str) -> Option<&[Value]> {
189+
for p in &self.properties {
190+
if p.name == name {
191+
return Some(&p.value);
192+
}
193+
}
194+
return None;
195+
}
196+
197+
/// Attempt to retrieve the named property, as a single entry of Words.
198+
fn get_words(&self, name: &str) -> Option<&[Word]> {
199+
self.get_property(name)
200+
.and_then(|p| {
201+
match p {
202+
&[Value::Words(ref w)] => Some(w.as_ref()),
203+
_ => None,
204+
}
205+
})
206+
}
207+
208+
/// Get a property that consists of a single number.
209+
fn get_number(&self, name: &str) -> Option<u32> {
210+
self.get_words(name)
211+
.and_then(|p| {
212+
if let &[Word::Number(n)] = p {
213+
Some(n)
214+
} else {
215+
None
216+
}
217+
})
218+
}
219+
220+
/// Get a property that consists of multiple numbers.
221+
fn get_numbers(&self, name: &str) -> Option<Vec<u32>> {
222+
let mut result = vec![];
223+
for word in self.get_words(name)? {
224+
if let Word::Number(n) = word {
225+
result.push(*n);
226+
} else {
227+
return None;
228+
}
229+
}
230+
Some(result)
231+
}
232+
233+
/// Get a property that is a single string.
234+
fn get_single_string(&self, name: &str) -> Option<&str> {
235+
self.get_property(name)
236+
.and_then(|p| {
237+
if let &[Value::String(ref text)] = p {
238+
Some(text.as_ref())
239+
} else {
240+
None
241+
}
242+
})
243+
}
244+
}
245+
246+
impl Value {
247+
fn phandle_walk(&self, labels: &BTreeMap<String, Rc<Node>>) {
248+
match self {
249+
Value::Phandle(ph) => ph.phandle_resolve(labels),
250+
Value::Words(words) => {
251+
for w in words {
252+
match w {
253+
Word::Phandle(ph) => ph.phandle_resolve(labels),
254+
_ => (),
255+
}
256+
}
257+
}
258+
_ => (),
259+
}
260+
}
261+
}
262+
263+
impl Phandle {
264+
/// Construct a phandle that is unresolved.
265+
pub fn new(name: String) -> Phandle {
266+
Phandle {
267+
name,
268+
node: RefCell::new(None),
269+
}
270+
}
271+
272+
/// Resolve this phandle, with the given label for lookup.
273+
fn phandle_resolve(&self, labels: &BTreeMap<String, Rc<Node>>) {
274+
// If already resolve, just return.
275+
if self.node.borrow().is_some() {
276+
return;
277+
}
278+
279+
let node = labels.get(&self.name).cloned()
280+
.expect("Missing phandle");
281+
*self.node.borrow_mut() = Some(node);
282+
}
283+
284+
/// Get the child node, panicing if it wasn't resolved properly.
285+
fn node_ref(&self) -> Rc<Node> {
286+
self.node.borrow().as_ref().unwrap().clone()
287+
}
288+
}
289+
290+
impl Word {
291+
pub fn get_number(&self) -> Option<u32> {
292+
match self {
293+
Word::Number(n) => Some(*n),
294+
_ => None,
295+
}
296+
}
297+
}
298+
299+
// To avoid recursion, the debug printer for Phandle just prints the name.
300+
impl std::fmt::Debug for Phandle {
301+
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
302+
write!(fmt, "Phandle({:?})", self.name)
303+
}
304+
}

0 commit comments

Comments
 (0)