doc: document the device-tree
crate
This commit is contained in:
parent
b090fb9fb6
commit
b28b12b069
@ -1,3 +1,4 @@
|
||||
//! Device-tree interrupt controller handling
|
||||
use alloc::{collections::btree_map::BTreeMap, sync::Arc};
|
||||
use device_api::interrupt::FullIrq;
|
||||
use fdt_rs::spec::Phandle;
|
||||
@ -10,12 +11,18 @@ use super::Node;
|
||||
pub(crate) static INTERRUPT_CONTROLLERS: IrqSafeRwLock<BTreeMap<Phandle, Arc<Node>>> =
|
||||
IrqSafeRwLock::new(BTreeMap::new());
|
||||
|
||||
/// Looks up an `interrupt-controller` node by its phandle. Additionally, the function lazily
|
||||
/// probes the node by running [Node::probe].
|
||||
///
|
||||
/// The function returns [None] if the node is found, but no driver/device is available.
|
||||
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)
|
||||
}
|
||||
|
||||
/// Reads interrupt information, as interpreted by `interrupt_controller`, from `property` at a
|
||||
/// given `offset`.
|
||||
pub fn map_interrupt_at(
|
||||
interrupt_controller: &Arc<Node>,
|
||||
property: &TProp,
|
||||
@ -25,6 +32,8 @@ pub fn map_interrupt_at(
|
||||
interrupt_controller.map_interrupt(property, offset)
|
||||
}
|
||||
|
||||
/// Same as [map_interrupt_at], but uses a phandle to address the `interrupt-controller` node
|
||||
/// and a scaled `index`.
|
||||
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()?;
|
||||
|
@ -1,3 +1,12 @@
|
||||
//! Device tree driver macros
|
||||
|
||||
/// Performs registration of a device tree driver.
|
||||
///
|
||||
/// The `compatible` list should contain strings, ordered from the most specific to the most
|
||||
/// generic implementation supported.
|
||||
///
|
||||
/// The registration happens in runtime, after the kernel sets up memory management, through the
|
||||
/// `.init_array`-based mechanism.
|
||||
pub macro device_tree_driver(
|
||||
compatible: [$($compatible:literal),+ $(,)?],
|
||||
driver: $driver:tt
|
||||
|
@ -15,6 +15,11 @@ pub use registry::register_driver;
|
||||
pub use traits::{DeviceTreeInterruptController, Driver, ProbeContext};
|
||||
pub use tree::{find_node, unflatten_device_tree, walk_device_tree, Node};
|
||||
|
||||
/// Performs a walk of the device tree and initializes nodes which haven't already been
|
||||
/// initialized/probed.
|
||||
///
|
||||
/// Reports successful initializations via `success_handler` and
|
||||
/// failures via `error_handler`.
|
||||
pub fn lazy_init<S: Fn(&Arc<Node>), F: Fn(&Arc<Node>, Error)>(
|
||||
success_handler: S,
|
||||
error_handler: F,
|
||||
@ -29,6 +34,11 @@ pub fn lazy_init<S: Fn(&Arc<Node>), F: Fn(&Arc<Node>, Error)>(
|
||||
});
|
||||
}
|
||||
|
||||
/// Performs a walk of the device and initializes IRQs for nodes. Additionally performs lazy
|
||||
/// initialization (if need to enable IRQs, but a device is not initialized).
|
||||
///
|
||||
/// Reports successful initializations via `success_handler` and
|
||||
/// failures via `error_handler`.
|
||||
pub fn init_irqs<S: Fn(&Arc<Node>), F: Fn(&Arc<Node>, Error)>(
|
||||
success_handler: S,
|
||||
error_handler: F,
|
||||
|
@ -1,3 +1,4 @@
|
||||
//! Device tree driver registration
|
||||
use alloc::{sync::Arc, vec::Vec};
|
||||
use libk_util::{sync::spin_rwlock::IrqSafeRwLock, OneTimeInit};
|
||||
|
||||
@ -5,9 +6,9 @@ use crate::DeviceTree;
|
||||
|
||||
use super::{Driver, Node};
|
||||
|
||||
pub struct DriverRegistration {
|
||||
pub compatible: &'static [&'static str],
|
||||
pub imp: &'static dyn Driver,
|
||||
pub(crate) struct DriverRegistration {
|
||||
pub(crate) compatible: &'static [&'static str],
|
||||
pub(crate) imp: &'static dyn Driver,
|
||||
}
|
||||
|
||||
pub(crate) static DRIVERS: IrqSafeRwLock<Vec<DriverRegistration>> = IrqSafeRwLock::new(Vec::new());
|
||||
@ -20,6 +21,7 @@ impl DriverRegistration {
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn register_driver(compatible: &'static [&'static str], driver: &'static dyn Driver) {
|
||||
DRIVERS.write().push(DriverRegistration {
|
||||
compatible,
|
||||
|
@ -1,3 +1,4 @@
|
||||
//! Device tree driver interfaces
|
||||
use core::ops::Range;
|
||||
|
||||
use alloc::sync::{Arc, Weak};
|
||||
@ -7,19 +8,26 @@ use crate::TProp;
|
||||
|
||||
use super::Node;
|
||||
|
||||
/// Device tree driver interface
|
||||
pub trait Driver: Sync {
|
||||
/// Constructs a [Device] for a given matching node
|
||||
fn probe(&self, node: &Arc<Node>, context: &ProbeContext) -> Option<Arc<dyn Device>>;
|
||||
}
|
||||
|
||||
/// Device-tree based interrupt mapper/controller interface
|
||||
pub trait DeviceTreeInterruptController {
|
||||
/// Reads interrupt information from `property` at given `offset` and maps it to
|
||||
/// the interrupt used by the controller
|
||||
fn map_interrupt(&self, property: &TProp, offset: usize) -> Option<FullIrq>;
|
||||
}
|
||||
|
||||
/// Context passed to the driver's `probe` function
|
||||
pub struct ProbeContext {
|
||||
pub(crate) bus: Option<Weak<dyn Bus>>,
|
||||
}
|
||||
|
||||
impl ProbeContext {
|
||||
/// See [Node::map_range]
|
||||
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
|
||||
|
@ -1,3 +1,4 @@
|
||||
//! Device hierachy management code
|
||||
use core::ops::Range;
|
||||
|
||||
use alloc::{
|
||||
@ -19,6 +20,12 @@ use super::{
|
||||
DeviceTreeInterruptController, Driver, ProbeContext,
|
||||
};
|
||||
|
||||
/// Represents a single node in the device tree, which may or may not:
|
||||
///
|
||||
/// * Have a device probed (see [Node::probe])
|
||||
/// * Have a device initialized (see [Node::lazy_init])
|
||||
/// * Be a bus
|
||||
/// * Be an interrupt-controller
|
||||
pub struct Node {
|
||||
// Info from device tree
|
||||
dt_node: TNode<'static>,
|
||||
@ -86,19 +93,40 @@ impl Node {
|
||||
(device, bus)
|
||||
}
|
||||
|
||||
/// Performs a "probe" of the node by looking up a matching driver. If the node
|
||||
/// has a parent, its parent is probed first.
|
||||
///
|
||||
/// If the node has already been probed, the device reference is just returned instead.
|
||||
pub fn probe(self: Arc<Self>) -> Option<Arc<dyn Device>> {
|
||||
let (device, _) = self.probe_upwards();
|
||||
device
|
||||
}
|
||||
|
||||
/// When called from an interrupt-controller driver, informs the node of its capability as
|
||||
/// an interrupt controller, allowing any other nodes which refer to it to map their
|
||||
/// interrupts.
|
||||
///
|
||||
/// This function has to be called in the driver's `probe` impl to function properly.
|
||||
pub fn make_interrupt_controller(&self, intc: Arc<dyn DeviceTreeInterruptController>) {
|
||||
self.interrupt_controller.init(intc);
|
||||
}
|
||||
|
||||
/// Returns the device driver associated with this node, if any was probed.
|
||||
pub fn driver(&self) -> Option<&'static dyn Driver> {
|
||||
Some(self.device.try_get()?.driver)
|
||||
}
|
||||
|
||||
/// Performs a lazy initialization of the node:
|
||||
///
|
||||
/// 1. If the node hasn't yet been probed for a device, a driver is looked up
|
||||
/// (see [Node::probe]).
|
||||
/// 2. If the node hasn't yet been initialized, initialization is performed.
|
||||
///
|
||||
/// Returns:
|
||||
///
|
||||
/// * `Some(Ok(()))` - device was probed and initialized (now or before).
|
||||
/// * `Some(Err(...))` - device was probed, but initialization failed.
|
||||
/// * `None` - no driver matched the device.
|
||||
pub fn lazy_init(self: Arc<Self>) -> Option<Result<(), Error>> {
|
||||
let device = self.clone().probe()?;
|
||||
let result = self.init_token.or_try_init_with(|| {
|
||||
@ -111,6 +139,12 @@ impl Node {
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs an IRQ initialization for the node's device:
|
||||
///
|
||||
/// 1. Does the same thing that [Node::lazy_init] does.
|
||||
/// 2. If [Node::lazy_init] succeeds and a device exists, calls [Device::init_irq].
|
||||
///
|
||||
/// Return value is the same as in [Node::lazy_init].
|
||||
pub fn init_irqs(self: Arc<Self>) -> Option<Result<(), Error>> {
|
||||
match self.clone().lazy_init() {
|
||||
Some(Ok(())) => {
|
||||
@ -122,6 +156,17 @@ impl Node {
|
||||
}
|
||||
}
|
||||
|
||||
/// "Forces" an initialization of the device. This is a stricter version of [Node::lazy_init]
|
||||
/// which enforces that:
|
||||
///
|
||||
/// 1. A device/driver actually exists for this node.
|
||||
/// 2. The node's device hasn't been initialized before.
|
||||
///
|
||||
/// Returns:
|
||||
///
|
||||
/// * `Ok(())` - if probe/init succeeded.
|
||||
/// * `Err(Error::DoesNotExist)` - couldn't find a device/driver for this node.
|
||||
/// * `Err(other)` - initialization failed.
|
||||
pub fn force_init(self: Arc<Self>) -> Result<(), Error> {
|
||||
let device = self.clone().probe().ok_or(Error::DoesNotExist)?;
|
||||
|
||||
@ -132,43 +177,59 @@ impl Node {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns an iterator over the node's children
|
||||
pub fn children(&self) -> impl Iterator<Item = &Arc<Node>> {
|
||||
self.children.get().iter()
|
||||
}
|
||||
|
||||
// Properties
|
||||
|
||||
/// Returns the node's name string
|
||||
pub fn name(&self) -> Option<&'static str> {
|
||||
self.name
|
||||
}
|
||||
|
||||
/// Returns the addresses by which some part of the device is represented on its parent bus.
|
||||
/// The bus address is taken from `reg[index]` property and is expected to be a pair
|
||||
/// of `(#address-cells, #size-cells)`-sized elements.
|
||||
pub fn bus_range(&self, index: usize) -> Option<Range<u64>> {
|
||||
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)
|
||||
}
|
||||
|
||||
/// Returns the physical-address range used by some part of the device.
|
||||
///
|
||||
/// This function is a result of [Node::bus_range], passed through its' parent bus mapper
|
||||
/// (or returned as-is if the device has no parent bus).
|
||||
pub fn map_range(&self, context: &ProbeContext, index: usize) -> Option<Range<u64>> {
|
||||
let bus_range = self.bus_range(index)?;
|
||||
context.map_range(bus_range)
|
||||
}
|
||||
|
||||
/// Same as [Node::map_range], but only returns the base of the range, cast to
|
||||
/// [PhysicalAddress].
|
||||
pub fn map_base(&self, context: &ProbeContext, index: usize) -> Option<PhysicalAddress> {
|
||||
self.map_range(context, index)
|
||||
.map(|range| PhysicalAddress::from_u64(range.start))
|
||||
}
|
||||
|
||||
/// Reads interrupt information from `interrupts[index]` property, mapped by the node's
|
||||
/// `interrupt-parent`.
|
||||
pub fn interrupt(&self, index: usize) -> Option<FullIrq> {
|
||||
let interrupts = self.property("interrupts")?;
|
||||
let phandle = self.interrupt_parent?;
|
||||
map_interrupt(phandle, &interrupts, index)
|
||||
}
|
||||
|
||||
/// Same as [Node::interrupt], but allows specifying other property to read the information
|
||||
/// from.
|
||||
pub fn interrupt_from(&self, property: &TProp, index: usize) -> Option<FullIrq> {
|
||||
let phandle = self.interrupt_parent?;
|
||||
map_interrupt(phandle, property, index)
|
||||
}
|
||||
|
||||
/// Returns a node reference to the device's `interrupt-parent`.
|
||||
pub fn interrupt_controller(&self) -> Option<Arc<Node>> {
|
||||
INTERRUPT_CONTROLLERS
|
||||
.read()
|
||||
@ -176,28 +237,33 @@ impl Node {
|
||||
.cloned()
|
||||
}
|
||||
|
||||
/// Returns the `#address-cells` value of the node's parent bus
|
||||
pub fn bus_address_cells(&self) -> usize {
|
||||
self.bus_address_cells
|
||||
}
|
||||
|
||||
/// Returns the `#interrupt-cells` of this node, if any
|
||||
pub fn self_interrupt_cells(&self) -> Option<usize> {
|
||||
self.property("#interrupt-cells")
|
||||
.and_then(|v| v.read_cell(0, 1))
|
||||
.map(|v| v as usize)
|
||||
}
|
||||
|
||||
/// Returns the `#address-cells` of this node, if any
|
||||
pub fn self_address_cells(&self) -> Option<usize> {
|
||||
self.property("#address-cells")
|
||||
.and_then(|v| v.read_cell(0, 1))
|
||||
.map(|v| v as usize)
|
||||
}
|
||||
|
||||
/// Returns the `#size-cells` of this node, if any
|
||||
pub fn self_size_cells(&self) -> Option<usize> {
|
||||
self.property("#size-cells")
|
||||
.and_then(|v| v.read_cell(0, 1))
|
||||
.map(|v| v as usize)
|
||||
}
|
||||
|
||||
/// Looks up a property `name` in this node
|
||||
pub fn property(&self, name: &str) -> Option<TProp<'static>> {
|
||||
self.dt_node.property(name)
|
||||
}
|
||||
@ -266,6 +332,8 @@ fn unflatten_node(
|
||||
node
|
||||
}
|
||||
|
||||
/// Sets up node hierarchy from a [DeviceTree] reference. This is a requirement to be able to
|
||||
/// probe/initialize/manage devices using a device-tree approach.
|
||||
pub fn unflatten_device_tree(dt: &'static DeviceTree) {
|
||||
const DEFAULT_ADDRESS_CELLS: u32 = 2;
|
||||
const DEFAULT_SIZE_CELLS: u32 = 1;
|
||||
@ -298,6 +366,8 @@ fn walk_node<T, V: FnMut(&Arc<Node>) -> Option<T>>(node: &Arc<Node>, visitor: &m
|
||||
None
|
||||
}
|
||||
|
||||
/// Performs a walk of the device (in no particular order), passing each node to the visitor and
|
||||
/// aborting if `Some(...)` is returned by it.
|
||||
pub fn walk_device_tree<T, V: FnMut(&Arc<Node>) -> Option<T>>(mut visitor: V) -> Option<T> {
|
||||
if let Some(root) = ROOT.try_get() {
|
||||
walk_node(root, &mut visitor)
|
||||
@ -306,6 +376,7 @@ pub fn walk_device_tree<T, V: FnMut(&Arc<Node>) -> Option<T>>(mut visitor: V) ->
|
||||
}
|
||||
}
|
||||
|
||||
/// Finds a node in the device tree by its absolute path or alias.
|
||||
pub fn find_node(name: &str) -> Option<Arc<Node>> {
|
||||
let dt = *DEVICE_TREE.try_get()?;
|
||||
let mut path = dt.resolve_alias(name)?.trim_matches('/');
|
||||
|
@ -1,6 +1,18 @@
|
||||
//! High level device tree handling library.
|
||||
//!
|
||||
//! Main functionality:
|
||||
//!
|
||||
//! * Creation of device tree-based drivers for devices
|
||||
//! * Collection, initialization and hierarchy for device tree nodes.
|
||||
//!
|
||||
//! For example drivers, see the following code in the kernel:
|
||||
//!
|
||||
//! * `pl011` - basic peripheral usage with `reg` and `interrupts`
|
||||
//! * `gic` in aarch64 - interrupt controller/mapper implementation
|
||||
#![cfg_attr(any(not(test), rust_analyzer), no_std)]
|
||||
#![feature(trait_alias, let_chains, decl_macro)]
|
||||
#![allow(clippy::type_complexity)]
|
||||
#![warn(missing_docs)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
//! Device tree node utilities
|
||||
use fdt_rs::{prelude::PropReader, spec::Phandle};
|
||||
|
||||
use crate::{
|
||||
@ -5,28 +6,39 @@ use crate::{
|
||||
tree::{DeviceTree, TNode, TProp},
|
||||
};
|
||||
|
||||
/// Accessor trait for a device tree node
|
||||
pub trait DeviceTreeNodeExt<'a> {
|
||||
/// Looks up a property with given `name` in the node
|
||||
fn property(&self, name: &str) -> Option<TProp<'a>>;
|
||||
/// Looks up a child with given `name` in the node
|
||||
fn child(&self, name: &str) -> Option<TNode<'a>>;
|
||||
/// Looks up a property with given `name` in the nearest parent.
|
||||
fn parent_property(&self, name: &str) -> Option<TProp<'a>>;
|
||||
/// Returns a property `name`, interpreted as a single string value
|
||||
fn prop_string(&self, name: &str) -> Option<&'a str>;
|
||||
/// Returns a property `name`, interpreted as a cell of given `size`
|
||||
fn prop_cell(&self, name: &str, size: usize) -> Option<u64>;
|
||||
|
||||
/// Returns a property `name`, interpreted as a single cell, cast to [usize]
|
||||
fn prop_cell_usize(&self, name: &str) -> Option<usize> {
|
||||
self.prop_cell(name, 1).map(|v| v as _)
|
||||
}
|
||||
/// Returns a property `name`, interpreted as a phandle
|
||||
fn prop_phandle(&self, name: &str) -> Option<Phandle> {
|
||||
self.prop_cell(name, 1).map(|v| v as _)
|
||||
}
|
||||
/// Returns `true` if the node contains a property `name`
|
||||
fn has_property(&self, name: &str) -> bool {
|
||||
self.property(name).is_some()
|
||||
}
|
||||
|
||||
/// Returns the `#address-cells` value of the nearest parent
|
||||
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
|
||||
}
|
||||
/// Returns the `#size-cells` value of the nearest parent
|
||||
fn parent_size_cells(&self) -> usize {
|
||||
self.parent_property("#size-cells")
|
||||
.and_then(|property| property.read_cell(0, 1))
|
||||
|
@ -1,9 +1,11 @@
|
||||
//! Device tree nodes' property accessors
|
||||
use fdt_rs::{
|
||||
base::iters::StringPropIter,
|
||||
index::DevTreeIndexProp,
|
||||
prelude::{FallibleIterator, PropReader},
|
||||
};
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait CellTuple: Sized {
|
||||
type Sizes: Copy;
|
||||
|
||||
@ -15,20 +17,33 @@ pub trait CellTuple: Sized {
|
||||
fn entry_size(sizes: Self::Sizes) -> usize;
|
||||
}
|
||||
|
||||
/// Accessor trait for a device tree property
|
||||
pub trait DeviceTreePropertyRead {
|
||||
/// Reads a cell value of `size` at a given `offset`.
|
||||
///
|
||||
/// **NOTE** supported cell sizes are 1 and 2. If the size is 3 and larger,
|
||||
/// perform multiple split reads instead.
|
||||
fn read_cell(&self, offset: usize, size: usize) -> Option<u64>;
|
||||
/// Reads the property as a single string
|
||||
fn as_str(&self) -> Option<&str>;
|
||||
/// Reads the property as an iterator over a `<stringlist>`
|
||||
fn as_str_list(&self) -> impl Iterator<Item = &str>;
|
||||
/// Returns the length of the property in bytes
|
||||
fn len(&self) -> usize;
|
||||
|
||||
/// Reads a cell tuple from the property. The property's value is assumed to be
|
||||
/// an uniform array of such tuples, specified by the `sizes` parameter.
|
||||
fn read_cells<T: CellTuple>(&self, index: usize, sizes: T::Sizes) -> Option<T> {
|
||||
self.read_cells_at(index * T::entry_size(sizes), sizes)
|
||||
}
|
||||
|
||||
/// Same as [DeviceTreePropertyRead::read_cells], but uses an unscaled `offset` instead.
|
||||
fn read_cells_at<T: CellTuple>(&self, offset: usize, sizes: T::Sizes) -> Option<T> {
|
||||
T::read(self, offset, sizes)
|
||||
}
|
||||
|
||||
/// Reads the property as an iterator over uniformly-sized tuples, specified by the
|
||||
/// `sizes` parameter.
|
||||
fn iter_cells<T: CellTuple>(&self, sizes: T::Sizes) -> CellTupleIter<Self, T> {
|
||||
CellTupleIter {
|
||||
property: self,
|
||||
@ -37,17 +52,20 @@ pub trait DeviceTreePropertyRead {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if `self.len() == 0`
|
||||
fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator over the tuple cells of some device tree node's property
|
||||
pub struct CellTupleIter<'a, P: DeviceTreePropertyRead + ?Sized, T: CellTuple> {
|
||||
property: &'a P,
|
||||
offset: usize,
|
||||
sizes: T::Sizes,
|
||||
}
|
||||
|
||||
/// An iterator over the string list propoerty
|
||||
pub struct StringListIter<'a> {
|
||||
inner: StringPropIter<'a>,
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
//! Raw device tree handling structures
|
||||
use core::ptr;
|
||||
|
||||
use fdt_rs::{
|
||||
@ -14,9 +15,12 @@ use crate::{
|
||||
|
||||
const FDT_INDEX_BUFFER_SIZE: usize = 65536;
|
||||
|
||||
/// Helper alias for a device tree node.
|
||||
pub type TNode<'a> = DevTreeIndexNode<'a, 'a, 'a>;
|
||||
/// Helper alias for a device tree node's property.
|
||||
pub type TProp<'a> = DevTreeIndexProp<'a, 'a, 'a>;
|
||||
|
||||
/// Main data structure for interacting with a raw device tree
|
||||
pub struct DeviceTree<'a> {
|
||||
tree: DevTree<'a>,
|
||||
index: DevTreeIndex<'a, 'a>,
|
||||
@ -26,10 +30,14 @@ pub struct DeviceTree<'a> {
|
||||
struct FdtIndexBuffer([u8; FDT_INDEX_BUFFER_SIZE]);
|
||||
|
||||
impl<'a> DeviceTree<'a> {
|
||||
/// Minimum possible FDT header size
|
||||
pub const MIN_HEADER_SIZE: usize = DevTree::MIN_HEADER_SIZE;
|
||||
/// Default `#address-cells` value assumed if one is absent
|
||||
pub const DEFAULT_ADDRESS_CELLS: usize = 2;
|
||||
/// Default `#size-cells` value assumed if one is absent
|
||||
pub const DEFAULT_SIZE_CELLS: usize = 1;
|
||||
|
||||
/// Constructs a device tree from a raw tree and an index
|
||||
pub fn from_parts(tree: DevTree<'a>, index: DevTreeIndex<'a, 'a>) -> Self {
|
||||
Self { tree, index }
|
||||
}
|
||||
@ -57,14 +65,17 @@ impl<'a> DeviceTree<'a> {
|
||||
Self::from_raw_with_index(address, &mut BUFFER.0[..])
|
||||
}
|
||||
|
||||
/// Returns the total size in bytes of the underlying FDT blob
|
||||
pub fn size(&self) -> usize {
|
||||
self.tree.totalsize()
|
||||
}
|
||||
|
||||
/// Returns the root node of this device tree
|
||||
pub fn root(&self) -> TNode {
|
||||
self.index.root()
|
||||
}
|
||||
|
||||
/// Looks up a node by its `/absolute/path`.
|
||||
pub fn find_absolute(&'a self, path: &str) -> Option<TNode<'a>> {
|
||||
let mut path = path.trim_start_matches('/');
|
||||
let mut current = self.root();
|
||||
@ -86,6 +97,8 @@ impl<'a> DeviceTree<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolves an alias to its real path by looking it up in the `/aliases` node of the
|
||||
/// device tree.
|
||||
pub fn resolve_alias(&'a self, name: &'a str) -> Option<&'a str> {
|
||||
if name.starts_with('/') {
|
||||
Some(name)
|
||||
@ -114,6 +127,12 @@ impl<'a> DeviceTree<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the start and end address of the initrd specified by this device tree.
|
||||
///
|
||||
/// The initrd info is read from:
|
||||
///
|
||||
/// * `/chosen/linux,initrd-start`
|
||||
/// * `/chosen/linux,initrd-end`
|
||||
pub fn chosen_initrd(&self) -> Option<(PhysicalAddress, PhysicalAddress)> {
|
||||
let root = self.root();
|
||||
let chosen = root.child("chosen")?;
|
||||
@ -135,6 +154,7 @@ impl<'a> DeviceTree<'a> {
|
||||
))
|
||||
}
|
||||
|
||||
/// Returns an iterator over the memory regions specified by this device tree
|
||||
pub fn memory_regions(&self) -> DeviceTreeMemoryRegionIter {
|
||||
DeviceTreeMemoryRegionIter::new(self)
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
//! Utility functions for device tree handling
|
||||
use fdt_rs::index::iters::DevTreeIndexNodeSiblingIter;
|
||||
use libk_mm::{address::PhysicalAddress, phys::PhysicalMemoryRegion};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user