dt: refactor device-tree lib

This commit is contained in:
Mark Poliakov 2024-12-16 12:56:05 +02:00
parent a9340ea089
commit dfae656833
17 changed files with 624 additions and 535 deletions

View File

@ -0,0 +1,32 @@
use alloc::{collections::btree_map::BTreeMap, sync::Arc};
use device_api::interrupt::FullIrq;
use fdt_rs::spec::Phandle;
use libk_util::sync::spin_rwlock::IrqSafeRwLock;
use crate::TProp;
use super::Node;
pub(crate) static INTERRUPT_CONTROLLERS: IrqSafeRwLock<BTreeMap<Phandle, Arc<Node>>> =
IrqSafeRwLock::new(BTreeMap::new());
pub fn interrupt_controller(phandle: Phandle) -> Option<Arc<Node>> {
let interrupt_controller = INTERRUPT_CONTROLLERS.read().get(&phandle).cloned()?;
interrupt_controller.clone().probe()?;
Some(interrupt_controller)
}
pub fn map_interrupt_at(
interrupt_controller: &Arc<Node>,
property: &TProp,
offset: usize,
) -> Option<FullIrq> {
let interrupt_controller = interrupt_controller.interrupt_controller.try_get()?;
interrupt_controller.map_interrupt(property, offset)
}
pub fn map_interrupt(phandle: Phandle, property: &TProp, index: usize) -> Option<FullIrq> {
let interrupt_controller = interrupt_controller(phandle)?;
let interrupt_cells = interrupt_controller.self_interrupt_cells()?;
map_interrupt_at(&interrupt_controller, property, index * interrupt_cells)
}

View File

@ -0,0 +1,20 @@
pub macro device_tree_driver(
compatible: [$($compatible:literal),+ $(,)?],
driver: $driver:tt
) {
struct __DtDriver;
impl $crate::driver::Driver for __DtDriver $driver
static __DT_DRIVER: __DtDriver = __DtDriver;
#[link_section = ".init_array"]
#[used]
static __REGISTER_FN: extern "C" fn() = __register_fn;
extern "C" fn __register_fn() {
$crate::driver::register_driver(
&[$($compatible),+],
&__DT_DRIVER
)
}
}

View File

@ -0,0 +1,45 @@
//! Device tree-based driver definitions
use alloc::sync::Arc;
use yggdrasil_abi::error::Error;
mod interrupt;
mod macros;
mod registry;
mod traits;
mod tree;
pub use interrupt::{interrupt_controller, map_interrupt, map_interrupt_at};
pub use macros::device_tree_driver;
pub use registry::register_driver;
pub use traits::{DeviceTreeInterruptController, Driver, ProbeContext};
pub use tree::{find_node, unflatten_device_tree, walk_device_tree, Node};
pub fn lazy_init<S: Fn(&Arc<Node>), F: Fn(&Arc<Node>, Error)>(
success_handler: S,
error_handler: F,
) {
walk_device_tree::<(), _>(|node| {
match node.clone().lazy_init() {
Some(Ok(())) => success_handler(node),
Some(Err(error)) => error_handler(node, error),
None => (),
}
None
});
}
pub fn init_irqs<S: Fn(&Arc<Node>), F: Fn(&Arc<Node>, Error)>(
success_handler: S,
error_handler: F,
) {
walk_device_tree::<(), _>(|node| {
match node.clone().init_irqs() {
Some(Ok(())) => success_handler(node),
Some(Err(error)) => error_handler(node, error),
None => (),
}
None
});
}

View File

@ -0,0 +1,28 @@
use alloc::{sync::Arc, vec::Vec};
use libk_util::{sync::spin_rwlock::IrqSafeRwLock, OneTimeInit};
use crate::DeviceTree;
use super::{Driver, Node};
pub struct DriverRegistration {
pub compatible: &'static [&'static str],
pub imp: &'static dyn Driver,
}
pub(crate) static DRIVERS: IrqSafeRwLock<Vec<DriverRegistration>> = IrqSafeRwLock::new(Vec::new());
pub(crate) static ROOT: OneTimeInit<Arc<Node>> = OneTimeInit::new();
pub(crate) static DEVICE_TREE: OneTimeInit<&'static DeviceTree> = OneTimeInit::new();
impl DriverRegistration {
pub(crate) fn matches(&self, compatible: &str) -> bool {
self.compatible.contains(&compatible)
}
}
pub fn register_driver(compatible: &'static [&'static str], driver: &'static dyn Driver) {
DRIVERS.write().push(DriverRegistration {
compatible,
imp: driver,
});
}

View File

@ -0,0 +1,32 @@
use core::ops::Range;
use alloc::sync::{Arc, Weak};
use device_api::{bus::Bus, device::Device, interrupt::FullIrq};
use crate::TProp;
use super::Node;
pub trait Driver: Sync {
fn probe(&self, node: &Arc<Node>, context: &ProbeContext) -> Option<Arc<dyn Device>>;
}
pub trait DeviceTreeInterruptController {
fn map_interrupt(&self, property: &TProp, offset: usize) -> Option<FullIrq>;
}
pub struct ProbeContext {
pub(crate) bus: Option<Weak<dyn Bus>>,
}
impl ProbeContext {
pub fn map_range(&self, range: Range<u64>) -> Option<Range<u64>> {
if let Some(bus) = self.bus.as_ref().and_then(Weak::upgrade) {
// Use parent bus to map the address
bus.map_range(range)
} else {
// Identity
Some(range)
}
}
}

View File

@ -1,52 +1,23 @@
//! Device tree-based driver definitions
use core::ops::Range;
use alloc::{
collections::btree_map::BTreeMap,
sync::{Arc, Weak},
vec::Vec,
};
use device_api::{bus::Bus, device::Device, interrupt::FullIrq};
use fdt_rs::{prelude::PropReader, spec::Phandle};
use fdt_rs::spec::Phandle;
use libk_mm::address::PhysicalAddress;
use libk_util::{sync::spin_rwlock::IrqSafeRwLock, OneTimeInit};
use libk_util::OneTimeInit;
use yggdrasil_abi::error::Error;
use crate::dt::{DevTreeIndexNodePropGet, DevTreeIndexPropExt, DeviceTree, TNode, TProp};
use crate::{DeviceTree, DeviceTreeNodeExt, DeviceTreePropertyRead, TNode, TProp};
pub macro device_tree_driver(
compatible: [$($compatible:literal),+ $(,)?],
driver: $driver:tt
) {
struct __DtDriver;
impl $crate::driver::Driver for __DtDriver $driver
static __DT_DRIVER: __DtDriver = __DtDriver;
#[link_section = ".init_array"]
#[used]
static __REGISTER_FN: extern "C" fn() = __register_fn;
extern "C" fn __register_fn() {
$crate::driver::register_driver(
&[$($compatible),+],
&__DT_DRIVER
)
}
}
pub trait Driver: Sync {
fn probe(&self, node: &Arc<Node>, context: &ProbeContext) -> Option<Arc<dyn Device>>;
fn enumerate_children(&self, node: &Arc<Node>) {
let _ = node;
}
}
pub struct DriverRegistration {
pub compatible: &'static [&'static str],
pub imp: &'static dyn Driver,
}
use super::{
interrupt::INTERRUPT_CONTROLLERS,
map_interrupt,
registry::{DEVICE_TREE, DRIVERS, ROOT},
DeviceTreeInterruptController, Driver, ProbeContext,
};
pub struct Node {
// Info from device tree
@ -64,7 +35,7 @@ pub struct Node {
// Driver/device info
device: OneTimeInit<NodeDevice>,
init_token: OneTimeInit<()>,
interrupt_controller: OneTimeInit<Arc<dyn DeviceTreeInterruptController>>,
pub(crate) interrupt_controller: OneTimeInit<Arc<dyn DeviceTreeInterruptController>>,
}
struct NodeDevice {
@ -79,14 +50,6 @@ struct EnumerationContext {
interrupt_parent: Option<Phandle>,
}
pub struct ProbeContext {
bus: Option<Weak<dyn Bus>>,
}
pub trait DeviceTreeInterruptController {
fn map_interrupt(&self, property: &TProp, offset: usize) -> Option<FullIrq>;
}
impl Node {
fn probe_upwards(self: Arc<Self>) -> (Option<Arc<dyn Device>>, Option<Weak<dyn Bus>>) {
let mut parent_bus = None;
@ -180,7 +143,7 @@ impl Node {
}
pub fn bus_range(&self, index: usize) -> Option<Range<u64>> {
let reg = crate::find_prop(&self.dt_node, "reg")?;
let reg = self.dt_node.property("reg")?;
let (base, size) = reg.read_cells(index, (self.bus_address_cells, self.bus_size_cells))?;
Some(base..base + size)
}
@ -218,50 +181,31 @@ impl Node {
}
pub fn self_interrupt_cells(&self) -> Option<usize> {
self.prop::<u32>("#interrupt-cells").map(|v| v as usize)
self.property("#interrupt-cells")
.and_then(|v| v.read_cell(0, 1))
.map(|v| v as usize)
}
pub fn self_address_cells(&self) -> Option<usize> {
self.prop::<u32>("#address-cells").map(|v| v as usize)
self.property("#address-cells")
.and_then(|v| v.read_cell(0, 1))
.map(|v| v as usize)
}
pub fn self_size_cells(&self) -> Option<usize> {
self.prop::<u32>("#size-cells").map(|v| v as usize)
self.property("#size-cells")
.and_then(|v| v.read_cell(0, 1))
.map(|v| v as usize)
}
pub fn property(&self, name: &str) -> Option<TProp<'static>> {
crate::find_prop(&self.dt_node, name)
}
pub fn prop<T>(&self, name: &str) -> Option<T>
where
TNode<'static>: DevTreeIndexNodePropGet<T>,
{
self.dt_node.prop(name)
}
}
impl ProbeContext {
pub fn map_range(&self, range: Range<u64>) -> Option<Range<u64>> {
if let Some(bus) = self.bus.as_ref().and_then(Weak::upgrade) {
// Use parent bus to map the address
bus.map_range(range)
} else {
// Identity
Some(range)
}
self.dt_node.property(name)
}
}
unsafe impl Sync for Node {}
unsafe impl Send for Node {}
static DRIVERS: IrqSafeRwLock<Vec<DriverRegistration>> = IrqSafeRwLock::new(Vec::new());
static ROOT: OneTimeInit<Arc<Node>> = OneTimeInit::new();
static DEVICE_TREE: OneTimeInit<&'static DeviceTree> = OneTimeInit::new();
static INTERRUPT_CONTROLLERS: IrqSafeRwLock<BTreeMap<Phandle, Arc<Node>>> =
IrqSafeRwLock::new(BTreeMap::new());
fn unflatten_node(
dt: TNode<'static>,
parent: Option<Weak<Node>>,
@ -269,26 +213,19 @@ fn unflatten_node(
) -> Arc<Node> {
let name = dt.name().ok();
// TODO <stringlist>
let compatible: Option<&str> = dt.prop("compatible");
let compatible = dt.prop_string("compatible");
// Extract #...-cells for children
let address_cells: Option<u32> = dt.prop("#address-cells");
let size_cells: Option<u32> = dt.prop("#size-cells");
let address_cells = address_cells
.map(|s| s as usize)
let address_cells = dt
.prop_cell_usize("#address-cells")
.unwrap_or(ctx.address_cells);
let size_cells = size_cells.map(|s| s as usize).unwrap_or(ctx.size_cells);
let size_cells = dt.prop_cell_usize("#size-cells").unwrap_or(ctx.size_cells);
// Extract interrupt-parent for children
let interrupt_parent: Option<Phandle> = dt.prop("interrupt-parent");
let interrupt_parent = interrupt_parent.or(ctx.interrupt_parent);
let interrupt_parent = dt.prop_phandle("interrupt-parent").or(ctx.interrupt_parent);
let is_interrupt_controller = dt.has_property("interrupt-controller");
let is_interrupt_controller = dt.props().any(|p| {
p.name()
.map(|name| name == "interrupt-controller")
.unwrap_or(false)
});
let phandle = dt.prop("phandle");
let phandle = dt.prop_phandle("phandle");
// Setup this node
let node = Arc::new(Node {
@ -347,27 +284,6 @@ pub fn unflatten_device_tree(dt: &'static DeviceTree) {
ROOT.init(root);
}
pub fn interrupt_controller(phandle: Phandle) -> Option<Arc<Node>> {
let interrupt_controller = INTERRUPT_CONTROLLERS.read().get(&phandle).cloned()?;
interrupt_controller.clone().probe()?;
Some(interrupt_controller)
}
pub fn map_interrupt_at(
interrupt_controller: &Arc<Node>,
property: &TProp,
offset: usize,
) -> Option<FullIrq> {
let interrupt_controller = interrupt_controller.interrupt_controller.try_get()?;
interrupt_controller.map_interrupt(property, offset)
}
pub fn map_interrupt(phandle: Phandle, property: &TProp, index: usize) -> Option<FullIrq> {
let interrupt_controller = interrupt_controller(phandle)?;
let interrupt_cells = interrupt_controller.self_interrupt_cells()?;
map_interrupt_at(&interrupt_controller, property, index * interrupt_cells)
}
fn walk_node<T, V: FnMut(&Arc<Node>) -> Option<T>>(node: &Arc<Node>, visitor: &mut V) -> Option<T> {
if let Some(value) = visitor(node) {
return Some(value);
@ -412,45 +328,3 @@ pub fn find_node(name: &str) -> Option<Arc<Node>> {
current = child.clone();
}
}
pub fn lazy_init<S: Fn(&Arc<Node>), F: Fn(&Arc<Node>, Error)>(
success_handler: S,
error_handler: F,
) {
walk_device_tree::<(), _>(|node| {
match node.clone().lazy_init() {
Some(Ok(())) => success_handler(node),
Some(Err(error)) => error_handler(node, error),
None => (),
}
None
});
}
pub fn init_irqs<S: Fn(&Arc<Node>), F: Fn(&Arc<Node>, Error)>(
success_handler: S,
error_handler: F,
) {
walk_device_tree::<(), _>(|node| {
match node.clone().init_irqs() {
Some(Ok(())) => success_handler(node),
Some(Err(error)) => error_handler(node, error),
None => (),
}
None
});
}
impl DriverRegistration {
fn matches(&self, compatible: &str) -> bool {
self.compatible.contains(&compatible)
}
}
pub fn register_driver(compatible: &'static [&'static str], driver: &'static dyn Driver) {
DRIVERS.write().push(DriverRegistration {
compatible,
imp: driver,
});
}

View File

@ -27,175 +27,12 @@ pub type TNode<'a> = DevTreeIndexNode<'a, 'a, 'a>;
/// Device tree property
pub type TProp<'a> = DevTreeIndexProp<'a, 'a, 'a>;
pub struct CellTupleIterator<'a, P: DevTreeIndexPropExt + ?Sized, T: CellTuple> {
property: &'a P,
offset: usize,
sizes: T::Sizes,
}
pub trait CellTuple: Sized {
type Sizes: Copy;
fn read<P: DevTreeIndexPropExt + ?Sized>(
property: &P,
offset: usize,
sizes: Self::Sizes,
) -> Option<Self>;
fn entry_size(sizes: Self::Sizes) -> usize;
}
impl CellTuple for (u64, u64) {
type Sizes = (usize, usize);
fn read<P: DevTreeIndexPropExt + ?Sized>(
property: &P,
offset: usize,
sizes: Self::Sizes,
) -> Option<Self> {
let v0 = property.read_cell(offset, sizes.0)?;
let v1 = property.read_cell(offset + sizes.0, sizes.1)?;
Some((v0, v1))
}
fn entry_size(sizes: Self::Sizes) -> usize {
sizes.0 + sizes.1
}
}
impl CellTuple for (u64, u64, u64) {
type Sizes = (usize, usize, usize);
fn read<P: DevTreeIndexPropExt + ?Sized>(
property: &P,
offset: usize,
sizes: Self::Sizes,
) -> Option<Self> {
let v0 = property.read_cell(offset, sizes.0)?;
let v1 = property.read_cell(offset + sizes.0, sizes.1)?;
let v2 = property.read_cell(offset + sizes.0 + sizes.1, sizes.2)?;
Some((v0, v1, v2))
}
fn entry_size(sizes: Self::Sizes) -> usize {
sizes.0 + sizes.1 + sizes.2
}
}
impl CellTuple for (u64, u64, u64, u64) {
type Sizes = (usize, usize, usize, usize);
fn read<P: DevTreeIndexPropExt + ?Sized>(
property: &P,
offset: usize,
sizes: Self::Sizes,
) -> Option<Self> {
let v0 = property.read_cell(offset, sizes.0)?;
let v1 = property.read_cell(offset + sizes.0, sizes.1)?;
let v2 = property.read_cell(offset + sizes.0 + sizes.1, sizes.2)?;
let v3 = property.read_cell(offset + sizes.0 + sizes.1 + sizes.2, sizes.3)?;
Some((v0, v1, v2, v3))
}
fn entry_size(sizes: Self::Sizes) -> usize {
sizes.0 + sizes.1 + sizes.2 + sizes.3
}
}
/// Helper trait to provide extra functionality for [DevTreeIndexProp]
#[allow(clippy::len_without_is_empty)]
pub trait DevTreeIndexPropExt {
/// Reads a cell value from single-type cell array at given cell index
fn cell1_array_item(&self, index: usize, cells: usize) -> Option<u64>;
/// Reads a cell pair from cell pair array at given pair index
fn cell2_array_item(&self, index: usize, cells0: usize, cells1: usize) -> Option<(u64, u64)>;
/// Reads a cell value from the property at given offset
fn read_cell(&self, u32_offset: usize, cell_size: usize) -> Option<u64>;
fn read_cells<T: CellTuple>(&self, index: usize, sizes: T::Sizes) -> Option<T> {
let offset = index * T::entry_size(sizes);
T::read(self, offset, sizes)
}
fn read_cells_at<T: CellTuple>(&self, offset: usize, sizes: T::Sizes) -> Option<T> {
T::read(self, offset, sizes)
}
fn iter_cells<T: CellTuple>(&self, sizes: T::Sizes) -> CellTupleIterator<Self, T> {
CellTupleIterator {
property: self,
offset: 0,
sizes,
}
}
/// Returns the length in bytes
fn len(&self) -> usize;
}
impl<P: DevTreeIndexPropExt, T: CellTuple> Iterator for CellTupleIterator<'_, P, T> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
let size = T::entry_size(self.sizes);
let entry = T::read(self.property, self.offset, self.sizes)?;
self.offset += size;
Some(entry)
}
}
/// Helper trait to provide extra functionality for [DevTreeIndexNode]
pub trait DevTreeIndexNodeExt {
fn address_cells(&self) -> usize {
match self.find_parent_property("#address-cells") {
Some(value) => value.u32(0).unwrap() as usize,
None => loop {
// TODO
core::hint::spin_loop();
},
}
}
fn size_cells(&self) -> usize {
match self.find_parent_property("#size-cells") {
Some(value) => value.u32(0).unwrap() as usize,
None => loop {
// TODO
core::hint::spin_loop();
},
}
}
fn find_parent_property(&self, name: &str) -> Option<DevTreeIndexProp>;
fn interrupt_parent(&self) -> Option<TNode>;
}
impl DevTreeIndexNodeExt for DevTreeIndexNode<'_, '_, '_> {
fn find_parent_property(&self, name: &str) -> Option<DevTreeIndexProp> {
let mut current = self.parent();
while let Some(node) = current {
if let Some(prop) = find_prop(&node, name) {
return Some(prop);
}
current = node.parent();
}
None
}
fn interrupt_parent(&self) -> Option<TNode> {
find_interrupt_controller(self)
}
}
/// Extension trait for [DevTreeIndexNode] to obtain typed property values
pub trait DevTreeIndexNodePropGet<T> {
/// Returns a property value of given type, if it exists
fn prop(&self, name: &str) -> Option<T>;
}
/// Iterator for physical memory regions present in the device tree
#[derive(Clone)]
pub struct FdtMemoryRegionIter<'a> {
inner: DevTreeIndexNodeSiblingIter<'a, 'a, 'a>,
}
/// Device tree wrapper struct
pub struct DeviceTree<'a> {
tree: DevTree<'a>,
@ -203,199 +40,10 @@ pub struct DeviceTree<'a> {
}
impl<'a> DeviceTree<'a> {
pub const MIN_HEADER_SIZE: usize = DevTree::MIN_HEADER_SIZE;
/// Constructs a device tree wrapper from the DTB virtual address.
///
/// # Safety
///
/// The caller must ensure the validity of the address. This function is not safe
/// for use with multiple device trees.
#[allow(static_mut_refs)]
pub unsafe fn from_addr(virt: usize) -> Self {
FDT_INDEX_BUFFER.0.fill(0);
Self::from_addr_with_index(virt, &mut FDT_INDEX_BUFFER.0)
}
/// Constructs a device tree wrapper from the DTB virtual address and provided buffer
///
/// # Safety
///
/// The caller must ensure the validity of the address.
pub unsafe fn from_addr_with_index(virt: usize, index: &'static mut [u8]) -> Self {
let tree = DevTree::from_raw_pointer(core::ptr::with_exposed_provenance(virt)).unwrap();
let index = DevTreeIndex::new(tree, index).unwrap();
Self { tree, index }
}
/// Returns the total size of the device tree in memory
pub fn size(&self) -> usize {
self.tree.totalsize()
}
/// Returns the root node of the device tree
pub fn root(&self) -> DevTreeIndexNode {
self.index.root()
}
pub fn find_absolute(&'a self, path: &str) -> Option<TNode<'a>> {
let mut path = path.trim_start_matches('/');
let mut current = self.root();
'l0: loop {
if path.is_empty() {
return Some(current);
}
let (head, tail) = match path.split_once('/') {
Some(elem) => elem,
None => (path, ""),
};
path = tail;
for child in current.children() {
let Ok(name) = child.name() else {
continue;
};
if name == head {
current = child;
continue 'l0;
}
}
// Not found
return None;
}
}
pub fn find(&'a self, name: &str) -> Option<TNode<'a>> {
let path = self.resolve_alias(name)?;
self.find_absolute(path)
}
pub fn resolve_alias(&'a self, name: &'a str) -> Option<&'a str> {
if name.starts_with('/') {
Some(name)
} else {
// TODO cache aliases
// TODO cache phandles
let aliases = self.find_absolute("/aliases")?;
aliases.prop(name)
}
}
// Commonly used functions for convenience
/// Returns the /chosen.stdout-path value.
/// The value is in the following form:
///
/// ```ignore
/// stdout-path = "<device-name>[:<settings>]";
/// ```
pub fn chosen_stdout(&self) -> Option<(&str, Option<&str>)> {
let chosen = self.find_absolute("/chosen")?;
let value: &str = chosen.prop("stdout-path")?;
match value.split_once(':') {
Some((left, right)) => Some((left, Some(right))),
None => Some((value, None)),
}
}
pub fn chosen_initrd(&self) -> Option<(PhysicalAddress, PhysicalAddress)> {
let chosen = self.find_absolute("/chosen")?;
let address_cells = chosen.address_cells();
let initrd_start = find_prop(&chosen, "linux,initrd-start")?;
let initrd_end = find_prop(&chosen, "linux,initrd-end")?;
let initrd_start = initrd_start.cell1_array_item(0, address_cells)?;
let initrd_end = initrd_end.cell1_array_item(0, address_cells)?;
Some((
PhysicalAddress::from_u64(initrd_start),
PhysicalAddress::from_u64(initrd_end),
))
}
/// Returns the length of the header provided as a slice of bytes.
///
/// # Safety
///
/// Preconditions:
///
/// * `header` is at least u32-aligned;
/// * `header` points to a slice of at least [DevTree::MIN_HEADER_SIZE] bytes.
pub unsafe fn read_totalsize(header: &[u8]) -> Result<usize, Error> {
DevTree::read_totalsize(header).map_err(|_| Error::InvalidArgument)
}
}
impl DevTreeIndexPropExt for DevTreeIndexProp<'_, '_, '_> {
fn read_cell(&self, u32_offset: usize, cell_size: usize) -> Option<u64> {
match cell_size {
1 => self.u32(u32_offset).map(|x| x as u64).ok(),
2 => {
let high = self.u32(u32_offset).ok()? as u64;
let low = self.u32(u32_offset + 1).ok()? as u64;
Some((high << 32) | low)
}
_ => unimplemented!(),
}
}
fn cell1_array_item(&self, index: usize, cells: usize) -> Option<u64> {
self.read_cell(index * cells, cells)
}
fn cell2_array_item(&self, index: usize, cells0: usize, cells1: usize) -> Option<(u64, u64)> {
let u32_index = index * (cells0 + cells1);
let cell0 = self.read_cell(u32_index, cells0)?;
let cell1 = self.read_cell(u32_index + cells0, cells1)?;
Some((cell0, cell1))
}
fn len(&self) -> usize {
self.length()
}
}
impl<'a> FdtMemoryRegionIter<'a> {
/// Constructs a memory region iterator for given device tree
pub fn new(dt: &'a DeviceTree) -> Self {
let inner = dt.index.root().children();
Self { inner }
}
}
impl Iterator for FdtMemoryRegionIter<'_> {
type Item = PhysicalMemoryRegion;
fn next(&mut self) -> Option<Self::Item> {
loop {
let Some(node) = self.inner.next() else {
break None;
};
let name = node.name().unwrap_or("");
if (name.starts_with("memory@") || name == "memory")
&& let Some(reg) = find_prop(&node, "reg")
{
let address_cells = node.address_cells();
let size_cells = node.size_cells();
if let Some((base, size)) = reg.cell2_array_item(0, address_cells, size_cells) {
let base = PhysicalAddress::from_u64(base);
let size = size.try_into().unwrap();
break Some(PhysicalMemoryRegion { base, size });
}
}
}
}
}
impl DevTreeIndexNodePropGet<u32> for DevTreeIndexNode<'_, '_, '_> {

View File

@ -1,12 +1,16 @@
#![cfg_attr(any(not(test), rust_analyzer), no_std)]
#![feature(trait_alias, let_chains, decl_macro)]
#![allow(clippy::missing_transmute_annotations, clippy::type_complexity)]
extern crate alloc;
#[macro_use]
pub mod driver;
pub mod dt;
pub mod node;
pub mod property;
pub mod tree;
pub mod util;
pub use dt::find_prop;
pub use node::DeviceTreeNodeExt;
pub use property::DeviceTreePropertyRead;
pub use tree::{DeviceTree, TNode, TProp};
pub use util::DeviceTreeMemoryRegionIter;

View File

@ -0,0 +1,66 @@
use fdt_rs::{prelude::PropReader, spec::Phandle};
use crate::{
property::DeviceTreePropertyRead,
tree::{DeviceTree, TNode, TProp},
};
pub trait DeviceTreeNodeExt<'a> {
fn property(&self, name: &str) -> Option<TProp<'a>>;
fn child(&self, name: &str) -> Option<TNode<'a>>;
fn parent_property(&self, name: &str) -> Option<TProp<'a>>;
fn prop_string(&self, name: &str) -> Option<&'a str>;
fn prop_cell(&self, name: &str, size: usize) -> Option<u64>;
fn prop_cell_usize(&self, name: &str) -> Option<usize> {
self.prop_cell(name, 1).map(|v| v as _)
}
fn prop_phandle(&self, name: &str) -> Option<Phandle> {
self.prop_cell(name, 1).map(|v| v as _)
}
fn has_property(&self, name: &str) -> bool {
self.property(name).is_some()
}
fn parent_address_cells(&self) -> usize {
self.parent_property("#address-cells")
.and_then(|property| property.read_cell(0, 1))
.unwrap_or(DeviceTree::DEFAULT_ADDRESS_CELLS as u64) as usize
}
fn parent_size_cells(&self) -> usize {
self.parent_property("#size-cells")
.and_then(|property| property.read_cell(0, 1))
.unwrap_or(DeviceTree::DEFAULT_SIZE_CELLS as u64) as usize
}
}
impl<'a> DeviceTreeNodeExt<'a> for TNode<'a> {
fn property(&self, name: &str) -> Option<TProp<'a>> {
self.props()
.find(|prop| prop.name().map(|n| n == name).unwrap_or(false))
}
fn child(&self, name: &str) -> Option<TNode<'a>> {
self.children()
.find(|child| child.name().map(|n| n == name).unwrap_or(false))
}
fn parent_property(&self, name: &str) -> Option<TProp<'a>> {
let mut current = self.parent()?;
loop {
if let Some(property) = current.property(name) {
return Some(property);
}
current = current.parent()?;
}
}
fn prop_cell(&self, name: &str, size: usize) -> Option<u64> {
self.property(name).and_then(|prop| prop.read_cell(0, size))
}
fn prop_string(&self, name: &str) -> Option<&'a str> {
self.property(name).and_then(|prop| prop.str().ok())
}
}

View File

@ -0,0 +1,158 @@
use fdt_rs::{
base::iters::StringPropIter,
index::DevTreeIndexProp,
prelude::{FallibleIterator, PropReader},
};
pub trait CellTuple: Sized {
type Sizes: Copy;
fn read<P: DeviceTreePropertyRead + ?Sized>(
property: &P,
offset: usize,
sizes: Self::Sizes,
) -> Option<Self>;
fn entry_size(sizes: Self::Sizes) -> usize;
}
pub trait DeviceTreePropertyRead {
fn read_cell(&self, offset: usize, size: usize) -> Option<u64>;
fn as_str(&self) -> Option<&str>;
fn as_str_list(&self) -> impl Iterator<Item = &str>;
fn len(&self) -> usize;
fn read_cells<T: CellTuple>(&self, index: usize, sizes: T::Sizes) -> Option<T> {
self.read_cells_at(index * T::entry_size(sizes), sizes)
}
fn read_cells_at<T: CellTuple>(&self, offset: usize, sizes: T::Sizes) -> Option<T> {
T::read(self, offset, sizes)
}
fn iter_cells<T: CellTuple>(&self, sizes: T::Sizes) -> CellTupleIter<Self, T> {
CellTupleIter {
property: self,
offset: 0,
sizes,
}
}
fn is_empty(&self) -> bool {
self.len() == 0
}
}
pub struct CellTupleIter<'a, P: DeviceTreePropertyRead + ?Sized, T: CellTuple> {
property: &'a P,
offset: usize,
sizes: T::Sizes,
}
pub struct StringListIter<'a> {
inner: StringPropIter<'a>,
}
impl DeviceTreePropertyRead for DevTreeIndexProp<'_, '_, '_> {
fn read_cell(&self, offset: usize, size: usize) -> Option<u64> {
match size {
1 => self.u32(offset).ok().map(Into::into),
2 => {
let v0 = self.u32(offset).ok()? as u64;
let v1 = self.u32(offset + 1).ok()? as u64;
Some((v0 << 32) | v1)
}
// If cell size is larger than 2, split the read
_ => None,
}
}
fn as_str(&self) -> Option<&str> {
self.str().ok()
}
fn as_str_list(&self) -> impl Iterator<Item = &str> {
StringListIter {
inner: self.iter_str(),
}
}
fn len(&self) -> usize {
self.length()
}
}
impl<P: DeviceTreePropertyRead + ?Sized, T: CellTuple> Iterator for CellTupleIter<'_, P, T> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
let size = T::entry_size(self.sizes);
let entry = T::read(self.property, self.offset, self.sizes)?;
self.offset += size;
Some(entry)
}
}
impl<'a> Iterator for StringListIter<'a> {
type Item = &'a str;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().ok().flatten()
}
}
macro impl_cell_tuple_types($($t:literal ($ty:ty) )+) {
($($ty),+)
}
macro impl_cell_tuple_sum {
($n:expr, $i:tt) => { $n.$i },
($n:expr, $i:tt $($t:tt)+) => { $n.$i + impl_cell_tuple_sum!($n, $($t)+) }
}
macro impl_cell_tuple_read {
( [ $($a:tt),* ] { $off:expr, $prop:ident, $sizes:ident }: $n:tt $($t:tt)*) => {
impl_cell_tuple_read!(
[
$($a,)*
($prop.read_cell($off, $sizes.$n)?)
]
{ $off + $sizes.$n, $prop, $sizes }
:
$($t)*
)
},
( [ $($a:tt),* ] { $off:expr, $prop:ident, $sizes:ident }: ) => {
Some(($($a),*))
},
}
macro impl_cell_tuples(
$(
( $($index:tt)+ )
),+
) {
$(
impl CellTuple for impl_cell_tuple_types!($($index (u64))+) {
type Sizes = impl_cell_tuple_types!($($index (usize))+);
fn read<P: DeviceTreePropertyRead + ?Sized>(
property: &P,
offset: usize,
sizes: Self::Sizes,
) -> Option<Self> {
impl_cell_tuple_read!([] { offset, property, sizes }: $($index)+)
}
fn entry_size(sizes: Self::Sizes) -> usize {
impl_cell_tuple_sum!(sizes, $($index)+)
}
}
)+
}
impl_cell_tuples!(
(0 1),
(0 1 2),
(0 1 2 3),
(0 1 2 3 4),
(0 1 2 3 4 5)
);

View File

@ -0,0 +1,143 @@
use core::ptr;
use fdt_rs::{
base::DevTree,
index::{DevTreeIndex, DevTreeIndexNode, DevTreeIndexProp},
prelude::PropReader,
};
use libk_mm::address::PhysicalAddress;
use yggdrasil_abi::error::Error;
use crate::{
node::DeviceTreeNodeExt, property::DeviceTreePropertyRead, util::DeviceTreeMemoryRegionIter,
};
const FDT_INDEX_BUFFER_SIZE: usize = 65536;
pub type TNode<'a> = DevTreeIndexNode<'a, 'a, 'a>;
pub type TProp<'a> = DevTreeIndexProp<'a, 'a, 'a>;
pub struct DeviceTree<'a> {
tree: DevTree<'a>,
index: DevTreeIndex<'a, 'a>,
}
#[repr(C, align(0x10))]
struct FdtIndexBuffer([u8; FDT_INDEX_BUFFER_SIZE]);
impl<'a> DeviceTree<'a> {
pub const MIN_HEADER_SIZE: usize = DevTree::MIN_HEADER_SIZE;
pub const DEFAULT_ADDRESS_CELLS: usize = 2;
pub const DEFAULT_SIZE_CELLS: usize = 1;
pub fn from_parts(tree: DevTree<'a>, index: DevTreeIndex<'a, 'a>) -> Self {
Self { tree, index }
}
pub unsafe fn from_raw_with_index(address: usize, index: &'a mut [u8]) -> Result<Self, Error> {
let tree = DevTree::from_raw_pointer(ptr::with_exposed_provenance(address))
.map_err(|_| Error::InvalidArgument)?;
let index = DevTreeIndex::new(tree, index).map_err(|_| Error::InvalidArgument)?;
Ok(Self { tree, index })
}
pub unsafe fn from_raw(address: usize) -> Result<Self, Error> {
static mut BUFFER: FdtIndexBuffer = FdtIndexBuffer([0; FDT_INDEX_BUFFER_SIZE]);
#[allow(static_mut_refs)]
Self::from_raw_with_index(address, &mut BUFFER.0[..])
}
pub fn size(&self) -> usize {
self.tree.totalsize()
}
pub fn root(&self) -> TNode {
self.index.root()
}
pub fn find_absolute(&'a self, path: &str) -> Option<TNode<'a>> {
let mut path = path.trim_start_matches('/');
let mut current = self.root();
loop {
if path.is_empty() {
return Some(current);
}
let (head, tail) = match path.split_once('/') {
Some((head, tail)) => (head, tail.trim_start_matches('/')),
None => (path, ""),
};
path = tail;
current = current
.children()
.find(|child| child.name().map(|n| n == head).unwrap_or(false))?;
}
}
pub fn resolve_alias(&'a self, name: &'a str) -> Option<&'a str> {
if name.starts_with('/') {
Some(name)
} else {
let aliases = self.find_absolute("/aliases")?;
let alias = aliases.property(name)?;
alias.str().ok()
}
}
// Convenience methods
/// Returns the /chosen.stdout-path value.
/// The value is in the following form:
///
/// ```ignore
/// stdout-path = "<device-name>[:<settings>]";
/// ```
pub fn chosen_stdout(&self) -> Option<(&str, Option<&str>)> {
let chosen = self.find_absolute("/chosen")?;
let value = chosen.property("stdout-path")?.str().ok()?;
match value.split_once(':') {
Some((left, right)) => Some((left, Some(right))),
None => Some((value, None)),
}
}
pub fn chosen_initrd(&self) -> Option<(PhysicalAddress, PhysicalAddress)> {
let root = self.root();
let chosen = root.child("chosen")?;
let address_cells = root
.property("#address-cells")
.and_then(|prop| prop.read_cell(0, 1))
.unwrap_or(Self::DEFAULT_ADDRESS_CELLS as u64) as usize;
let initrd_start = chosen.property("linux,initrd-start")?; // find_prop(&chosen, "linux,initrd-start")?;
let initrd_end = chosen.property("linux,initrd-end")?;
let initrd_start = initrd_start.read_cell(0, address_cells)?;
let initrd_end = initrd_end.read_cell(0, address_cells)?;
Some((
PhysicalAddress::from_u64(initrd_start),
PhysicalAddress::from_u64(initrd_end),
))
}
pub fn memory_regions(&self) -> DeviceTreeMemoryRegionIter {
DeviceTreeMemoryRegionIter::new(self)
}
/// Returns the length of the header provided as a slice of bytes.
///
/// # Safety
///
/// Preconditions:
///
/// * `header` is at least u32-aligned;
/// * `header` points to a slice of at least [DevTree::MIN_HEADER_SIZE] bytes.
pub unsafe fn read_totalsize(header: &[u8]) -> Result<usize, Error> {
DevTree::read_totalsize(header).map_err(|_| Error::InvalidArgument)
}
}

View File

@ -0,0 +1,45 @@
use fdt_rs::index::iters::DevTreeIndexNodeSiblingIter;
use libk_mm::{address::PhysicalAddress, phys::PhysicalMemoryRegion};
use crate::{node::DeviceTreeNodeExt, property::DeviceTreePropertyRead, tree::DeviceTree};
/// Iterator for physical memory regions present in the device tree
#[derive(Clone)]
pub struct DeviceTreeMemoryRegionIter<'a> {
inner: DevTreeIndexNodeSiblingIter<'a, 'a, 'a>,
}
impl<'a> DeviceTreeMemoryRegionIter<'a> {
pub(crate) fn new(dt: &'a DeviceTree) -> Self {
let inner = dt.root().children();
Self { inner }
}
}
impl Iterator for DeviceTreeMemoryRegionIter<'_> {
type Item = PhysicalMemoryRegion;
fn next(&mut self) -> Option<Self::Item> {
loop {
let Some(node) = self.inner.next() else {
break None;
};
let name = node.name().unwrap_or("");
if !name.starts_with("memory@") && name != "memory" {
continue;
}
if let Some(reg) = node.property("reg") {
let address_cells = node.parent_address_cells();
let size_cells = node.parent_size_cells();
if let Some((base, size)) = reg.read_cells(0, (address_cells, size_cells)) {
let base = PhysicalAddress::from_u64(base);
let size = size as usize;
break Some(PhysicalMemoryRegion { base, size });
}
}
}
}
}

View File

@ -15,7 +15,7 @@ use device_api::{
};
use device_tree::{
driver::{device_tree_driver, DeviceTreeInterruptController, Node, ProbeContext},
dt::{DevTreeIndexPropExt, TProp},
DeviceTreePropertyRead, TProp,
};
use kernel_arch_aarch64::{GicInterface, CPU_COUNT};
use libk::{arch::Cpu, device::register_external_interrupt_controller, task::cpu_index};

View File

@ -9,10 +9,7 @@ use device_api::{
interrupt::{Irq, LocalInterruptController},
ResetDevice,
};
use device_tree::{
driver::unflatten_device_tree,
dt::{DevTreeIndexNodePropGet, DeviceTree, FdtMemoryRegionIter},
};
use device_tree::{driver::unflatten_device_tree, DeviceTree, DeviceTreeNodeExt};
use kernel_arch::Architecture;
use kernel_arch_aarch64::{
mem::{
@ -154,8 +151,7 @@ impl AArch64 {
);
let dtb_slice = EarlyMapping::<u8>::map_slice(dtb, dtb_size)?;
let dt = DeviceTree::from_addr(dtb_slice.as_ptr() as usize);
let dt = DeviceTree::from_raw(dtb_slice.as_ptr() as usize)?;
// Setup initrd from the dt
let initrd = dt.chosen_initrd();
@ -175,14 +171,12 @@ impl AArch64 {
}
// Initialize the physical memory
let regions = FdtMemoryRegionIter::new(&dt);
phys::init_from_iter(regions, Self::map_physical_memory)?;
phys::init_from_iter(dt.memory_regions(), Self::map_physical_memory)?;
// EarlyMapping for DTB no longer needed, it lives in physical memory and can be obtained
// through PhysicalRef
let dtb_slice: PhysicalRef<'static, [u8]> = PhysicalRef::map_slice(dtb, dtb_size);
let dt = DeviceTree::from_addr(dtb_slice.as_ptr() as usize);
let dt = DeviceTree::from_raw(dtb_slice.as_ptr().addr())?;
self.dt.init(dt);
@ -222,7 +216,10 @@ impl AArch64 {
}
fn machine_name(dt: &'static DeviceTree) -> (Option<&'static str>, Option<&'static str>) {
(dt.root().prop("compatible"), dt.root().prop("model"))
(
dt.root().prop_string("compatible"),
dt.root().prop_string("model"),
)
}
fn apply_machine_workarounds(compatible: &str) {

View File

@ -2,7 +2,7 @@
use core::sync::atomic::Ordering;
use abi::error::Error;
use device_tree::dt::{DevTreeIndexNodePropGet, DeviceTree};
use device_tree::{DeviceTree, DeviceTreeNodeExt};
use kernel_arch_aarch64::CPU_COUNT;
use crate::mem::KERNEL_VIRT_OFFSET;
@ -24,20 +24,17 @@ struct CpuInfo<'a> {
}
fn enumerate_cpus<'a>(dt: &'a DeviceTree) -> impl Iterator<Item = CpuInfo<'a>> {
// let cpus = dt.find_absolute("/cpus").unwrap();
let cpus = dt.find_absolute("/cpus").unwrap();
cpus.children().filter_map(|cpu_node| {
let compatible = cpu_node.prop("compatible")?;
let id = cpu_node.prop("reg")?;
let enable_method_str: &str = cpu_node.prop("enable-method")?;
let enable_method = match enable_method_str {
let compatible = cpu_node.prop_string("compatible")?;
let id = cpu_node.prop_cell("reg", 0)? as u32;
let enable_method = cpu_node.prop_string("enable-method")?;
let enable_method = match enable_method {
"psci" => CpuEnableMethod::Psci,
_ => {
log::warn!(
"Cannot enable cpu #{}: enable-method={:?} unsupported",
id,
enable_method_str
);
log::warn!("Unknown enable method for cpu #{id}: {enable_method:?}");
return None;
}
};

View File

@ -4,7 +4,7 @@ use alloc::{collections::btree_map::BTreeMap, sync::Arc, vec::Vec};
use device_api::{device::Device, interrupt::Irq};
use device_tree::{
driver::{device_tree_driver, interrupt_controller, map_interrupt_at, Node, ProbeContext},
dt::DevTreeIndexPropExt,
DeviceTreePropertyRead,
};
use libk_mm::address::PhysicalAddress;
use ygg_driver_pci::{

View File

@ -10,7 +10,7 @@ use alloc::{sync::Arc, vec::Vec};
use device_api::{bus::Bus, device::Device};
use device_tree::{
driver::{device_tree_driver, Node, ProbeContext},
dt::DevTreeIndexPropExt,
DeviceTreePropertyRead,
};
struct SimpleBus {