dt: refactor device-tree lib
This commit is contained in:
parent
a9340ea089
commit
dfae656833
32
kernel/lib/device-tree/src/driver/interrupt.rs
Normal file
32
kernel/lib/device-tree/src/driver/interrupt.rs
Normal 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)
|
||||
}
|
20
kernel/lib/device-tree/src/driver/macros.rs
Normal file
20
kernel/lib/device-tree/src/driver/macros.rs
Normal 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
|
||||
)
|
||||
}
|
||||
}
|
45
kernel/lib/device-tree/src/driver/mod.rs
Normal file
45
kernel/lib/device-tree/src/driver/mod.rs
Normal 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
|
||||
});
|
||||
}
|
28
kernel/lib/device-tree/src/driver/registry.rs
Normal file
28
kernel/lib/device-tree/src/driver/registry.rs
Normal 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,
|
||||
});
|
||||
}
|
32
kernel/lib/device-tree/src/driver/traits.rs
Normal file
32
kernel/lib/device-tree/src/driver/traits.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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,
|
||||
});
|
||||
}
|
@ -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<'_, '_, '_> {
|
||||
|
@ -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;
|
||||
|
66
kernel/lib/device-tree/src/node.rs
Normal file
66
kernel/lib/device-tree/src/node.rs
Normal 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())
|
||||
}
|
||||
}
|
158
kernel/lib/device-tree/src/property.rs
Normal file
158
kernel/lib/device-tree/src/property.rs
Normal 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)
|
||||
);
|
143
kernel/lib/device-tree/src/tree.rs
Normal file
143
kernel/lib/device-tree/src/tree.rs
Normal 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)
|
||||
}
|
||||
}
|
45
kernel/lib/device-tree/src/util.rs
Normal file
45
kernel/lib/device-tree/src/util.rs
Normal 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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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};
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
@ -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::{
|
||||
|
@ -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 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user