Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion fuzz/fuzz_targets/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
use dtoolkit::{
Node, Property,
fdt::{Fdt, FdtNode},
standard::NodeStandard,
standard::{NodeStandard, TreeStandard},
};
use libfuzzer_sys::fuzz_target;

Expand Down
26 changes: 12 additions & 14 deletions src/fdt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ use zerocopy_derive::*;

pub use self::node::FdtNode;
pub use self::property::FdtProperty;
use crate::Node;
use crate::error::{FdtErrorKind, FdtParseError};
use crate::memreserve::MemoryReservation;
use crate::{Node, Tree};

/// Version of the FDT specification supported by this library.
const FDT_VERSION: u32 = 17;
Expand Down Expand Up @@ -185,6 +185,15 @@ impl TryFrom<u32> for FdtToken {
}
}

impl<'a> Tree<'a> for Fdt<'a> {
type Node = FdtNode<'a>;

fn root(self) -> FdtNode<'a> {
let offset = self.header().off_dt_struct() as usize;
FdtNode::new(self, offset)
}
}

impl<'a> Fdt<'a> {
/// Creates a new `Fdt` from the given byte slice.
///
Expand Down Expand Up @@ -664,8 +673,7 @@ impl<'a> Fdt<'a> {
/// ```
#[must_use]
pub fn root(self) -> FdtNode<'a> {
let offset = self.header().off_dt_struct() as usize;
FdtNode::new(self, offset)
Tree::root(self)
}

/// Finds a node by its path.
Expand Down Expand Up @@ -719,17 +727,7 @@ impl<'a> Fdt<'a> {
/// ```
#[must_use]
pub fn find_node(self, path: &str) -> Option<FdtNode<'a>> {
if !path.starts_with('/') {
return None;
}
let mut current_node = self.root();
if path == "/" {
return Some(current_node);
}
for component in path.split('/').filter(|s| !s.is_empty()) {
current_node = current_node.child(component)?;
}
Some(current_node)
Tree::find_node(self, path)
}

/// Returns the node at the given byte offset relative to the start of the
Expand Down
35 changes: 35 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,41 @@ pub use crate::values::{
FdtStringListIterator, FromPropertyValue, PropEncodedArrayIterator, ToPropertyValue,
};

/// A device tree.
pub trait Tree<'a>: Copy {
/// The type used for nodes of the device tree.
type Node: Node<'a>;

/// Returns the root node of the device tree.
#[must_use]
fn root(self) -> Self::Node;

/// Finds a node by its path.
///
/// If a name in the given path contains a _unit-address_ (the part after
/// the `@` sign) then both the _node-name_ and _unit-address_ must
/// match. If it doesn't have a _unit-address_, then nodes with any
/// _unit-address_ or none will be allowed.
///
/// For example, searching for `/cpus/cpu` would match either `/cpus/cpu`,
/// `/cpus/cpu@0` or `/cpus/cpu@1`, while `/cpus/cpu@1` would match only the
/// latter.
#[must_use]
fn find_node(self, path: &str) -> Option<Self::Node> {
if !path.starts_with('/') {
return None;
}
let mut current_node = self.root();
if path == "/" {
return Some(current_node);
}
for component in path.split('/').filter(|s| !s.is_empty()) {
current_node = current_node.child(component)?;
}
Some(current_node)
}
}

/// A device tree node.
pub trait Node<'a>: Copy {
/// The type used for properties of the node.
Expand Down
24 changes: 11 additions & 13 deletions src/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use core::fmt::Display;
pub use node::{DeviceTreeNode, DeviceTreeNodeBuilder, DeviceTreeNodeRef};
pub use property::DeviceTreeProperty;

use crate::Node;
use crate::Tree;
use crate::fdt::Fdt;
use crate::memreserve::MemoryReservation;

Expand All @@ -51,6 +51,14 @@ pub struct DeviceTree {
pub memory_reservations: Vec<MemoryReservation>,
}

impl<'a> Tree<'a> for &'a DeviceTree {
type Node = DeviceTreeNodeRef<'a>;

fn root(self) -> DeviceTreeNodeRef<'a> {
self.root.as_read_only()
}
}

impl DeviceTree {
/// Creates a new `DeviceTree` with the given root node.
///
Expand Down Expand Up @@ -95,7 +103,7 @@ impl DeviceTree {
/// Returns a read-only view of the root node of the device tree.
#[must_use]
pub fn root(&self) -> DeviceTreeNodeRef<'_> {
self.root.as_read_only()
Tree::root(self)
}

/// Finds a node by its path and returns a read-only view of it.
Expand All @@ -119,17 +127,7 @@ impl DeviceTree {
/// ```
#[must_use]
pub fn find_node(&self, path: &str) -> Option<DeviceTreeNodeRef<'_>> {
if !path.starts_with('/') {
return None;
}
let mut current_node = self.root();
if path == "/" {
return Some(current_node);
}
for component in path.split('/').filter(|s| !s.is_empty()) {
current_node = current_node.child(component)?;
}
Some(current_node)
Tree::find_node(self, path)
}

/// Finds a node by its path and returns a mutable reference to it.
Expand Down
52 changes: 51 additions & 1 deletion src/standard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,61 @@ pub use self::ranges::Range;
pub use self::reg::Reg;
pub use self::status::Status;
use crate::error::{PropertyError, StandardError};
use crate::{Node, Property};
use crate::{Node, Property, Tree};

pub(crate) const DEFAULT_ADDRESS_CELLS: u32 = 2;
pub(crate) const DEFAULT_SIZE_CELLS: u32 = 1;

/// Methods to access standard nodes on a device tree.
pub trait TreeStandard<'a>: Tree<'a> {
/// Returns the `/cpus` node.
///
/// This should always be included in a valid device tree.
///
/// # Errors
///
/// Returns [`StandardError::CpusMissing`] if the CPUs node is missing.
fn cpus(self) -> Result<Cpus<Self::Node>, StandardError> {
let node = self.find_node("/cpus").ok_or(StandardError::CpusMissing)?;
Ok(Cpus::new(node))
}

/// Returns the `/memory` node.
///
/// This should always be included in a valid device tree.
///
/// # Errors
///
/// Returns [`StandardError::MemoryMissing`] if the memory node is missing.
fn memory(self) -> Result<Memory<Self::Node>, StandardError> {
let node = self
.find_node("/memory")
.ok_or(StandardError::MemoryMissing)?;
Ok(Memory::new(node))
}

/// Returns the `/reserved-memory/*` nodes, if any.
#[must_use]
fn reserved_memory(
self,
) -> Option<impl Iterator<Item = ReservedMemory<Self::Node>> + use<'a, Self>> {
Some(
self.find_node("/reserved-memory")?
.children()
.map(ReservedMemory::new),
)
}

/// Returns the `/chosen` node, if it exists.
#[must_use]
fn chosen(self) -> Option<Chosen<Self::Node>> {
let node = self.find_node("/chosen")?;
Some(Chosen::new(node))
}
}

impl<'a, T: Tree<'a>> TreeStandard<'a> for T {}

/// Methods to access standard properties on FDT nodes.
pub trait NodeStandard<'a>: Node<'a> {
/// Returns the value of the standard `compatible` property.
Expand Down
14 changes: 4 additions & 10 deletions src/standard/chosen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,8 @@ use core::fmt::{self, Display, Formatter};
use core::ops::Deref;

use crate::error::PropertyError;
use crate::fdt::{Fdt, FdtNode};
use crate::{Node, Property};

impl<'a> Fdt<'a> {
/// Returns the `/chosen` node, if it exists.
#[must_use]
pub fn chosen(self) -> Option<Chosen<FdtNode<'a>>> {
let node = self.find_node("/chosen")?;
Some(Chosen { node })
}
}

/// Typed wrapper for a `/chosen` node.
#[derive(Debug, Clone, Copy)]
pub struct Chosen<N> {
Expand All @@ -43,6 +33,10 @@ impl<N: Display> Display for Chosen<N> {
}

impl<'a, N: Node<'a>> Chosen<N> {
pub(super) const fn new(node: N) -> Self {
Self { node }
}

/// Returns the value of the standard `bootargs` property.
///
/// # Errors
Expand Down
21 changes: 4 additions & 17 deletions src/standard/cpus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,10 @@ use core::fmt::{self, Display, Formatter};
use core::ops::Deref;

use crate::error::{PropertyError, StandardError};
use crate::fdt::{Fdt, FdtNode};
use crate::standard::NodeStandard;
use crate::values::FdtStringListIterator;
use crate::{Cells, Node, Property};

impl<'a> Fdt<'a> {
/// Returns the `/cpus` node.
///
/// This should always be included in a valid device tree.
///
/// # Errors
///
/// Returns a parse error if there was a problem reading the FDT structure
/// to find the node, or [`StandardError::CpusMissing`] if the CPUs node is
/// missing.
pub fn cpus(self) -> Result<Cpus<FdtNode<'a>>, StandardError> {
let node = self.find_node("/cpus").ok_or(StandardError::CpusMissing)?;
Ok(Cpus { node })
}
}

/// Typed wrapper for a `/cpus` node.
#[derive(Debug, Clone, Copy)]
pub struct Cpus<N> {
Expand All @@ -52,6 +35,10 @@ impl<N: Display> Display for Cpus<N> {
}

impl<'a, N: Node<'a>> Cpus<N> {
pub(super) const fn new(node: N) -> Self {
Self { node }
}

/// Returns an iterator over the `/cpus/cpu@*` nodes.
pub fn cpus(self) -> impl Iterator<Item = Cpu<N>> + use<'a, N> {
self.node.children().filter_map(|child| {
Expand Down
35 changes: 8 additions & 27 deletions src/standard/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,36 +10,9 @@ use core::fmt::{self, Display, Formatter};
use core::ops::Deref;

use crate::error::{PropertyError, StandardError};
use crate::fdt::{Fdt, FdtNode};
use crate::standard::Reg;
use crate::{Cells, Node, Property};

impl<'a> Fdt<'a> {
/// Returns the `/memory` node.
///
/// This should always be included in a valid device tree.
///
/// # Errors
///
/// Returns [`StandardError::MemoryMissing`] if the memory node is missing.
pub fn memory(self) -> Result<Memory<FdtNode<'a>>, StandardError> {
let node = self
.find_node("/memory")
.ok_or(StandardError::MemoryMissing)?;
Ok(Memory { node })
}

/// Returns the `/reserved-memory/*` nodes, if any.
#[must_use]
pub fn reserved_memory(self) -> Option<impl Iterator<Item = ReservedMemory<FdtNode<'a>>>> {
Some(
self.find_node("/reserved-memory")?
.children()
.map(|node| ReservedMemory { node }),
)
}
}

/// Typed wrapper for a `/memory` node.
#[derive(Debug, Clone, Copy)]
pub struct Memory<N> {
Expand All @@ -61,6 +34,10 @@ impl<N: Display> Display for Memory<N> {
}

impl<'a, N: Node<'a>> Memory<N> {
pub(super) const fn new(node: N) -> Self {
Self { node }
}

/// Returns the value of the standard `initial-mapped-area` property of the
/// memory node.
///
Expand Down Expand Up @@ -140,6 +117,10 @@ impl<N: Display> Display for ReservedMemory<N> {
}

impl<'a, N: Node<'a>> ReservedMemory<N> {
pub(super) const fn new(node: N) -> Self {
Self { node }
}

/// Returns the value of the standard `size` property of the reserved memory
/// node, if it is present.
///
Expand Down
2 changes: 1 addition & 1 deletion tests/fdt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use dtoolkit::fdt::{Fdt, FdtNode};
use dtoolkit::fdt_mut::FdtMut;
#[cfg(feature = "write")]
use dtoolkit::model::{DeviceTree, DeviceTreeNode, DeviceTreeProperty};
use dtoolkit::standard::{InitialMappedArea, NodeStandard, Status};
use dtoolkit::standard::{InitialMappedArea, NodeStandard, Status, TreeStandard};
use dtoolkit::{Node, Property};

#[test]
Expand Down
Loading
Loading