#![cfg_attr(not(test), no_std)]
#![warn(missing_docs, rustdoc::missing_crate_level_docs)]
#![deny(unsafe_code)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#[cfg(feature = "write")]
extern crate alloc;
pub mod error;
pub mod fdt;
pub mod memreserve;
#[cfg(feature = "write")]
pub mod model;
pub mod standard;
use core::ffi::CStr;
use core::fmt::{self, Display, Formatter};
use core::ops::{BitOr, Shl};
use zerocopy::{FromBytes, big_endian};
use crate::error::{PropertyError, StandardError};
pub trait Node<'a>: Sized {
type Property: Property<'a>;
#[must_use]
fn name(&self) -> &'a str;
#[must_use]
fn name_without_address(&self) -> &'a str {
let name = self.name();
if let Some((name, _)) = name.split_once('@') {
name
} else {
name
}
}
fn property(&self, name: &str) -> Option<Self::Property> {
self.properties().find(|property| property.name() == name)
}
fn properties(&self) -> impl Iterator<Item = Self::Property> + use<'a, Self>;
fn child(&self, name: &str) -> Option<Self> {
let include_address = name.contains('@');
self.children().find(|child| {
if include_address {
child.name() == name
} else {
child.name_without_address() == name
}
})
}
fn children(&self) -> impl Iterator<Item = Self> + use<'a, Self>;
}
pub trait Property<'a>: Sized {
#[must_use]
fn name(&self) -> &'a str;
#[must_use]
fn value(&self) -> &'a [u8];
fn as_u32(&self) -> Result<u32, PropertyError> {
self.value()
.try_into()
.map(u32::from_be_bytes)
.map_err(|_| PropertyError::InvalidLength)
}
fn as_u64(&self) -> Result<u64, PropertyError> {
self.value()
.try_into()
.map(u64::from_be_bytes)
.map_err(|_| PropertyError::InvalidLength)
}
fn as_cells(&self) -> Result<Cells<'a>, PropertyError> {
Ok(Cells(
<[big_endian::U32]>::ref_from_bytes(self.value())
.map_err(|_| PropertyError::InvalidLength)?,
))
}
fn as_str(&self) -> Result<&'a str, PropertyError> {
let cstr =
CStr::from_bytes_with_nul(self.value()).map_err(|_| PropertyError::InvalidString)?;
cstr.to_str().map_err(|_| PropertyError::InvalidString)
}
fn as_str_list(&self) -> impl Iterator<Item = &'a str> + use<'a, Self> {
FdtStringListIterator {
value: self.value(),
}
}
fn as_prop_encoded_array<const N: usize>(
&self,
fields_cells: [usize; N],
) -> Result<impl Iterator<Item = [Cells<'a>; N]> + use<'a, N, Self>, PropertyError> {
let chunk_cells = fields_cells.iter().sum();
let chunk_bytes = chunk_cells * size_of::<u32>();
if !self.value().len().is_multiple_of(chunk_bytes) {
return Err(PropertyError::PropEncodedArraySizeMismatch {
size: self.value().len(),
chunk: chunk_cells,
});
}
Ok(self.value().chunks_exact(chunk_bytes).map(move |chunk| {
let mut cells = <[big_endian::U32]>::ref_from_bytes(chunk)
.expect("chunk should be a multiple of 4 bytes because of chunks_exact");
fields_cells.map(|field_cells| {
let field;
(field, cells) = cells.split_at(field_cells);
Cells(field)
})
}))
}
}
struct FdtStringListIterator<'a> {
value: &'a [u8],
}
impl<'a> Iterator for FdtStringListIterator<'a> {
type Item = &'a str;
fn next(&mut self) -> Option<Self::Item> {
if self.value.is_empty() {
return None;
}
let cstr = CStr::from_bytes_until_nul(self.value).ok()?;
let s = cstr.to_str().ok()?;
self.value = &self.value[s.len() + 1..];
Some(s)
}
}
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Cells<'a>(pub(crate) &'a [big_endian::U32]);
impl Cells<'_> {
pub fn to_int<T: Default + From<u32> + Shl<usize, Output = T> + BitOr<Output = T>>(
self,
) -> Result<T, StandardError> {
if size_of::<T>() < self.0.len() * size_of::<u32>() {
Err(StandardError::TooManyCells {
cells: self.0.len(),
})
} else if let [size] = self.0 {
Ok(size.get().into())
} else {
let mut value = Default::default();
for cell in self.0 {
value = value << 32 | cell.get().into();
}
Ok(value)
}
}
}
impl Display for Cells<'_> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.write_str("0x")?;
for part in self.0 {
write!(f, "{part:08x}")?;
}
Ok(())
}
}