Next Generation WASM Microkernel Operating System
1// Copyright 2025 Jonas Kruckenberg
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8use bumpalo::Bump;
9use core::ffi::CStr;
10use core::ptr::NonNull;
11use core::{fmt, iter, mem, slice};
12use fallible_iterator::FallibleIterator;
13use fdt::{CellSizes, Error, Fdt, NodeName, StringList};
14use hashbrown::HashMap;
15use smallvec::{SmallVec, smallvec};
16
17type Link<T> = Option<NonNull<T>>;
18
19/// A device tree describing the hardware configuration of the system.
20#[ouroboros::self_referencing] // `root` and all other nodes & data borrows from `alloc`
21pub struct DeviceTree {
22 alloc: Bump,
23 #[borrows(alloc)]
24 #[covariant]
25 inner: DeviceTreeInner<'this>,
26}
27
28struct DeviceTreeInner<'devtree> {
29 phandle2ptr: HashMap<u32, NonNull<Device<'devtree>>>,
30 root: NonNull<Device<'devtree>>,
31}
32
33/// Tree of the following shape:
34///
35///
36/// root
37/// /
38/// /
39/// node - node - node
40/// / /
41/// / /
42/// node - node node
43///
44/// where each node has a pointer to its first child, which in turn form a linked list of siblings.
45/// additionally each node has a pointer to back its parent.
46pub struct Device<'a> {
47 /// The name of the device
48 pub name: NodeName<'a>,
49 pub compatible: &'a str,
50 pub phandle: Option<u32>,
51
52 // linked list of device properties
53 properties: Link<Property<'a>>,
54 // links to other devices in the tree
55 parent: Link<Device<'a>>,
56 first_child: Link<Device<'a>>,
57 next_sibling: Link<Device<'a>>,
58}
59
60/// A property of a device.
61pub struct Property<'a> {
62 inner: fdt::Property<'a>,
63 next: Link<Property<'a>>,
64}
65
66// Safety: `DeviceTree`s accessor methods allow non-mutable access.
67unsafe impl Send for DeviceTree {}
68// Safety: `DeviceTree`s accessor methods allow non-mutable access.
69unsafe impl Sync for DeviceTree {}
70
71impl fmt::Debug for DeviceTree {
72 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73 f.debug_struct("DeviceTree")
74 .field("root", &self.root())
75 .finish()
76 }
77}
78
79impl DeviceTree {
80 pub fn parse(fdt: &[u8]) -> crate::Result<Self> {
81 // Safety: u32 has no invalid bit patterns
82 let (left, aligned, _) = unsafe { fdt.align_to::<u32>() };
83 assert!(left.is_empty()); // TODO decide what to do with unaligned slices
84 let fdt = Fdt::new(aligned)?;
85
86 let alloc = Bump::new();
87
88 DeviceTree::try_new(alloc, |alloc| {
89 let mut phandle2ptr = HashMap::new();
90
91 let mut stack: [Link<Device>; 16] = [const { None }; 16];
92
93 let root = unflatten_root(&fdt, alloc)?;
94 stack[0] = Some(root);
95
96 let mut iter = fdt.nodes()?;
97 while let Some((depth, node)) = iter.next()? {
98 let ptr = unflatten_node(
99 node,
100 &mut phandle2ptr,
101 stack[depth - 1].unwrap(),
102 stack[depth],
103 alloc,
104 )?;
105
106 // insert ourselves into the stack so we will become the new previous sibling in the next iteration
107 stack[depth] = Some(ptr);
108 }
109
110 Ok(DeviceTreeInner { phandle2ptr, root })
111 })
112 }
113
114 /// Matches the root device tree `compatible` string against the given list of strings.
115 #[inline]
116 pub fn is_compatible<'b>(&self, compats: impl IntoIterator<Item = &'b str>) -> bool {
117 self.root().is_compatible(compats)
118 }
119
120 /// Returns an iterator over all top-level devices in the tree.
121 #[inline]
122 pub fn children(&self) -> Children {
123 self.root().children()
124 }
125
126 /// Returns an iterator over all nodes in the tree in depth-first order.
127 #[inline]
128 pub fn descendants(&self) -> Descendants {
129 self.root().descendants()
130 }
131
132 /// Returns an iterator over all top-level properties in the tree.
133 #[inline]
134 pub fn properties(&self) -> Properties {
135 self.root().properties()
136 }
137
138 /// Returns the top-level property with the given name.
139 #[inline]
140 pub fn property(&self, name: &str) -> Option<&Property> {
141 self.root().property(name)
142 }
143
144 /// Returns the device with the given path.
145 #[inline]
146 pub fn find_by_path(&self, path: &str) -> Option<&Device> {
147 self.root().find_by_path(path)
148 }
149
150 pub fn find_by_phandle(&self, phandle: u32) -> Option<&Device> {
151 // Safety: we only inserted valid pointers into the map, so we should only get valid pointers out...
152 self.with_inner(|inner| unsafe { Some(inner.phandle2ptr.get(&phandle)?.as_ref()) })
153 }
154
155 #[inline]
156 fn root(&self) -> &Device {
157 // Safety: `init` guarantees the root node always exists and is correctly initialized
158 unsafe { self.borrow_inner().root.as_ref() }
159 }
160}
161
162impl fmt::Debug for Device<'_> {
163 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
164 let alternate = f.alternate();
165
166 let mut s = f.debug_struct("Device");
167 s.field("name", &self.name)
168 .field("compatible", &self.compatible)
169 .field("phandle", &self.phandle);
170 if alternate {
171 s.field_with("<properties>", |f| {
172 let mut f = f.debug_list();
173 for prop in self.properties() {
174 f.entry(&prop);
175 }
176 f.finish()
177 });
178
179 s.field_with("<children>", |f| {
180 let mut f = f.debug_list();
181 for prop in self.children() {
182 f.entry(&prop);
183 }
184 f.finish()
185 });
186
187 s.finish()
188 } else {
189 s.finish_non_exhaustive()
190 }
191 }
192}
193
194impl<'a> Device<'a> {
195 /// Returns `true` if this device is usable, i.e. its reported status property is "okay".
196 pub fn is_available(&self) -> bool {
197 self.properties()
198 .any(|prop| prop.inner.name == "status" && prop.inner.raw == b"okay")
199 }
200
201 /// Matches the device `compatible` string against the given list of strings.
202 pub fn is_compatible<'b>(&self, compats: impl IntoIterator<Item = &'b str>) -> bool {
203 compats.into_iter().any(|c| self.compatible.contains(c))
204 }
205
206 pub fn parent(&self) -> Option<&Device<'a>> {
207 // Safety: tree construction guarantees that the pointer is valid
208 self.parent.map(|parent| unsafe { parent.as_ref() })
209 }
210
211 /// Returns an iterator over all immediate children of this device.
212 pub fn children(&self) -> Children {
213 Children {
214 current: self.first_child,
215 }
216 }
217
218 /// Returns an iterator over all descendants of this device in depth-first order.
219 pub fn descendants(&self) -> Descendants {
220 Descendants {
221 stack: smallvec![],
222 current: self.children(),
223 }
224 }
225
226 /// Returns an iterator over all properties of this device.
227 pub fn properties(&self) -> Properties {
228 Properties {
229 current: self.properties,
230 }
231 }
232
233 /// Returns the property with the given name.
234 pub fn property(&self, name: &str) -> Option<&Property> {
235 self.properties().find(|prop| prop.inner.name == name)
236 }
237
238 /// Returns the device with the given path starting from this device.
239 pub fn find_by_path(&self, path: &str) -> Option<&Device> {
240 let mut node = self;
241 for component in path.trim_start_matches('/').split('/') {
242 node = node.children().find(|child| child.name.name == component)?;
243 }
244 Some(node)
245 }
246
247 pub fn cell_sizes(&self) -> CellSizes {
248 let address_cells = self
249 .property("#address-cells")
250 .and_then(|prop| prop.as_usize().ok());
251 let size_cells = self
252 .property("#size-cells")
253 .and_then(|prop| prop.as_usize().ok());
254
255 if let (Some(address_cells), Some(size_cells)) = (address_cells, size_cells) {
256 CellSizes {
257 address_cells,
258 size_cells,
259 }
260 } else if let Some(parent) = self.parent {
261 // Safety: tree construction ensures the parent ptr is always valid
262 unsafe { parent.as_ref() }.cell_sizes()
263 } else {
264 CellSizes::default()
265 }
266 }
267
268 pub fn regs(&self) -> Option<fdt::Regs> {
269 self.properties()
270 .find(|p| p.name() == "reg")
271 .map(|prop| prop.inner.as_regs(self.cell_sizes()))
272 }
273
274 pub fn interrupt_cells(&self) -> Option<usize> {
275 self.property("#interrupt-cells")?.as_usize().ok()
276 }
277
278 pub fn interrupt_parent(&self, devtree: &'a DeviceTree) -> Option<&Device<'a>> {
279 self.properties()
280 .find(|p| p.name() == "interrupt-parent")
281 .and_then(|prop| devtree.find_by_phandle(prop.as_u32().ok()?))
282 }
283
284 pub fn interrupts(&'a self, devtree: &'a DeviceTree) -> Option<Interrupts<'a>> {
285 let prop = self.property("interrupts")?;
286 let raw = prop.inner.raw.array_chunks::<4>();
287 let parent = self.interrupt_parent(devtree)?;
288 Some(Interrupts {
289 parent,
290 parent_cells: parent.interrupt_cells()?,
291 raw: raw.map(|chunk| u32::from_be_bytes(*chunk)),
292 })
293 }
294
295 pub fn interrupts_extended(
296 &'a self,
297 devtree: &'a DeviceTree,
298 ) -> Option<InterruptsExtended<'a>> {
299 let prop = self.property("interrupts-extended")?;
300 let raw = prop.inner.raw.array_chunks::<4>();
301 Some(InterruptsExtended {
302 devtree,
303 raw: raw.map(|chunk| u32::from_be_bytes(*chunk)),
304 })
305 }
306}
307
308impl fmt::Debug for Property<'_> {
309 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
310 f.debug_struct("Property")
311 .field("name", &self.inner.name)
312 .field("raw", &self.inner.raw)
313 .finish()
314 }
315}
316
317impl<'a> Property<'a> {
318 pub fn name(&self) -> &'a str {
319 self.inner.name
320 }
321
322 pub fn raw(&self) -> &'a [u8] {
323 self.inner.raw
324 }
325
326 /// Returns the property as a `u32`.
327 ///
328 /// # Errors
329 ///
330 /// Returns an error if the property is not a u32.
331 pub fn as_u32(&self) -> Result<u32, Error> {
332 self.inner.as_u32()
333 }
334
335 /// Returns the property as a `u64`.
336 ///
337 /// # Errors
338 ///
339 /// Returns an error if the property is not a u64.
340 pub fn as_u64(&self) -> Result<u64, Error> {
341 self.inner.as_u64()
342 }
343
344 /// Returns the property as a `usize`.
345 ///
346 /// # Errors
347 ///
348 /// Returns an error if the property is not a usize.
349 pub fn as_usize(&self) -> Result<usize, Error> {
350 self.inner.as_usize()
351 }
352
353 /// Returns the property as a C string.
354 ///
355 /// # Errors
356 ///
357 /// Returns an error if the property is not a valid C string.
358 pub fn as_cstr(&self) -> Result<&'a CStr, Error> {
359 self.inner.as_cstr()
360 }
361
362 /// Returns the property as a string.
363 ///
364 /// # Errors
365 ///
366 /// Returns an error if the property is not a valid UTF-8 string.
367 pub fn as_str(&self) -> Result<&'a str, Error> {
368 self.inner.as_str()
369 }
370
371 /// Returns a fallible iterator over the strings in the property.
372 ///
373 /// # Errors
374 ///
375 /// Returns an error if the property is not a valid UTF-8 string.
376 pub fn as_strlist(&self) -> Result<StringList<'a>, Error> {
377 self.inner.as_strlist()
378 }
379}
380
381pub struct Children<'a> {
382 current: Link<Device<'a>>,
383}
384
385impl<'a> Iterator for Children<'a> {
386 type Item = &'a Device<'a>;
387
388 fn next(&mut self) -> Option<Self::Item> {
389 // Safety: tree construction guarantees that the pointer is valid
390 let dev = unsafe { self.current?.as_ref() };
391 self.current = dev.next_sibling;
392 Some(dev)
393 }
394}
395
396pub struct Descendants<'a> {
397 stack: SmallVec<[Children<'a>; 6]>,
398 current: Children<'a>,
399}
400
401impl<'a> Iterator for Descendants<'a> {
402 type Item = (usize, &'a Device<'a>);
403
404 fn next(&mut self) -> Option<Self::Item> {
405 if let Some(next) = self.current.next() {
406 let depth = self.stack.len();
407 if next.first_child.is_some() {
408 let parent = mem::replace(&mut self.current, next.children());
409 self.stack.push(parent);
410 }
411 Some((depth, next))
412 } else {
413 self.current = self.stack.pop()?;
414 self.next()
415 }
416 }
417}
418
419pub struct Properties<'a> {
420 current: Link<Property<'a>>,
421}
422
423impl<'a> Iterator for Properties<'a> {
424 type Item = &'a Property<'a>;
425
426 fn next(&mut self) -> Option<Self::Item> {
427 // Safety: list construction guarantees that the pointer is valid
428 let dev = unsafe { self.current?.as_ref() };
429 self.current = dev.next;
430 Some(dev)
431 }
432}
433
434#[derive(Debug)]
435pub enum IrqSource {
436 C1(u32),
437 C3(u32, u32, u32),
438}
439
440#[expect(clippy::type_complexity, reason = "this is not thaaat complex")]
441pub struct Interrupts<'a> {
442 parent: &'a Device<'a>,
443 parent_cells: usize,
444 raw: iter::Map<slice::ArrayChunks<'a, u8, 4>, fn(&[u8; 4]) -> u32>,
445}
446impl<'a> Iterator for Interrupts<'a> {
447 type Item = (&'a Device<'a>, IrqSource);
448
449 fn next(&mut self) -> Option<Self::Item> {
450 Some((
451 self.parent,
452 interrupt_address(&mut self.raw, self.parent_cells)?,
453 ))
454 }
455}
456
457#[expect(clippy::type_complexity, reason = "this is not thaaat complex")]
458pub struct InterruptsExtended<'a> {
459 devtree: &'a DeviceTree,
460 raw: iter::Map<slice::ArrayChunks<'a, u8, 4>, fn(&[u8; 4]) -> u32>,
461}
462impl<'a> Iterator for InterruptsExtended<'a> {
463 type Item = (&'a Device<'a>, IrqSource);
464
465 fn next(&mut self) -> Option<Self::Item> {
466 let parent_phandle = self.raw.next()?;
467 let parent = self.devtree.find_by_phandle(parent_phandle)?;
468 let parent_interrupt_cells = parent.interrupt_cells()?;
469 Some((
470 parent,
471 interrupt_address(&mut self.raw, parent_interrupt_cells)?,
472 ))
473 }
474}
475
476fn interrupt_address(
477 iter: &mut impl Iterator<Item = u32>,
478 interrupt_cells: usize,
479) -> Option<IrqSource> {
480 match interrupt_cells {
481 1 => Some(IrqSource::C1(iter.next()?)),
482 3 if let Ok([a, b, c]) = iter.next_chunk() => Some(IrqSource::C3(a, b, c)),
483 _ => None,
484 }
485}
486
487fn unflatten_root<'a>(fdt: &Fdt, alloc: &'a Bump) -> crate::Result<NonNull<Device<'a>>> {
488 let mut compatible: Option<&str> = None;
489
490 let mut props_head: Link<Property> = None;
491 let mut props_tail: Link<Property> = None;
492
493 let mut props = fdt.properties();
494 while let Some(prop) = props.next()? {
495 if prop.name == "compatible" {
496 compatible = Some(alloc.alloc_str(prop.as_str()?));
497 } else {
498 unflatten_property(prop, &mut props_head, &mut props_tail, alloc);
499 }
500 }
501
502 let ptr = NonNull::from(alloc.alloc(Device {
503 name: NodeName {
504 name: "",
505 unit_address: None,
506 },
507 compatible: compatible.unwrap_or_default(),
508 phandle: None,
509 properties: props_head,
510 parent: None,
511 first_child: None,
512 next_sibling: None,
513 }));
514
515 Ok(ptr)
516}
517
518fn unflatten_node<'a>(
519 node: fdt::Node,
520 phandle2ptr: &mut HashMap<u32, NonNull<Device<'a>>>,
521 mut parent: NonNull<Device<'a>>,
522 prev_sibling: Link<Device<'a>>,
523 alloc: &'a Bump,
524) -> crate::Result<NonNull<Device<'a>>> {
525 let mut compatible: Option<&'a str> = None;
526 let mut phandle: Option<u32> = None;
527
528 let mut props_head: Link<Property> = None;
529 let mut props_tail: Link<Property> = None;
530
531 let mut props = node.properties();
532 while let Some(prop) = props.next()? {
533 if prop.name == "compatible" {
534 compatible = Some(alloc.alloc_str(prop.as_str()?));
535 } else if prop.name == "phandle" {
536 phandle = prop.as_u32().ok();
537 } else {
538 unflatten_property(prop, &mut props_head, &mut props_tail, alloc);
539 }
540 }
541
542 let name = node.name()?;
543 let node = NonNull::from(alloc.alloc(Device {
544 name: NodeName {
545 name: alloc.alloc_str(name.name),
546 unit_address: name.unit_address.map(|addr| &*alloc.alloc_str(addr)),
547 },
548 compatible: compatible.unwrap_or_default(),
549 phandle,
550 properties: props_head,
551 parent: Some(parent),
552 first_child: None,
553 next_sibling: None,
554 }));
555
556 if let Some(phandle) = phandle {
557 phandle2ptr.insert(phandle, node);
558 }
559
560 // update the parents `first_child` pointer if necessary
561 // Safety: callers responsibility to ensure that the parent pointer is valid
562 unsafe {
563 parent.as_mut().first_child.get_or_insert(node);
564 }
565
566 // update the previous sibling's `next_sibling` pointer if necessary
567 if let Some(mut sibling) = prev_sibling {
568 // Safety: callers responsibility to ensure that the parent pointer is valid
569 unsafe {
570 sibling.as_mut().next_sibling = Some(node);
571 }
572 }
573
574 Ok(node)
575}
576
577fn unflatten_property<'a>(
578 prop: fdt::Property,
579 head: &mut Link<Property<'a>>,
580 tail: &mut Link<Property<'a>>,
581 alloc: &'a Bump,
582) {
583 let prop = NonNull::from(alloc.alloc(Property {
584 inner: fdt::Property {
585 name: alloc.alloc_str(prop.name),
586 raw: alloc.alloc_slice_copy(prop.raw),
587 },
588 next: None,
589 }));
590
591 // if there already is a tail node append the new node to it
592 if let &mut Some(mut tail) = tail {
593 // Safety: tail is either `None` or a valid pointer we allocated in a previous call below
594 let tail = unsafe { tail.as_mut() };
595 tail.next = Some(prop);
596 } else {
597 // otherwise the list is empty, so update the head pointer
598 debug_assert!(head.is_none());
599 *head = Some(prop);
600 }
601
602 // update the tail pointer so we will become the new tail in the next iteration
603 *tail = Some(prop);
604}