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