use core::fmt;
use core::ops::Deref;
use core::{ffi::CStr, fmt::Debug};
use crate::Fdt;
use crate::fmt_utils;
use crate::{
FdtError, Phandle, Token,
data::{Bytes, Reader, U32_SIZE},
};
mod chosen;
mod memory;
mod prop;
pub use chosen::Chosen;
pub use memory::{Memory, MemoryRegion};
pub use prop::{PropIter, Property, RangeInfo, RegInfo, RegIter, VecRange};
#[derive(Clone)]
pub(crate) struct NodeContext {
pub address_cells: u8,
pub size_cells: u8,
pub interrupt_parent: Option<Phandle>,
}
impl Default for NodeContext {
fn default() -> Self {
NodeContext {
address_cells: 2,
size_cells: 1,
interrupt_parent: None,
}
}
}
#[derive(Clone)]
pub struct NodeBase<'a> {
name: &'a str,
data: Bytes<'a>,
strings: Bytes<'a>,
level: usize,
_fdt: Fdt<'a>,
pub address_cells: u8,
pub size_cells: u8,
context: NodeContext,
path_components: heapless::Vec<&'a str, 16>,
}
impl<'a> NodeBase<'a> {
pub fn name(&self) -> &'a str {
self.name
}
pub fn level(&self) -> usize {
self.level
}
pub fn properties(&self) -> PropIter<'a> {
PropIter::new(self.data.reader(), self.strings.clone())
}
pub fn find_property(&self, name: &str) -> Option<Property<'a>> {
self.properties().find(|p| p.name() == name)
}
pub fn find_property_str(&self, name: &str) -> Option<&'a str> {
let prop = self.find_property(name)?;
prop.as_str()
}
pub fn reg(&self) -> Option<RegIter<'a>> {
let prop = self.find_property("reg")?;
Some(RegIter::new(
prop.data().reader(),
self.context.address_cells,
self.context.size_cells,
))
}
pub fn reg_array<const N: usize>(&self) -> heapless::Vec<RegInfo, N> {
let mut result = heapless::Vec::new();
if let Some(reg) = self.reg() {
for info in reg {
if result.push(info).is_err() {
break; }
}
}
result
}
fn is_chosen(&self) -> bool {
self.name == "chosen"
}
fn is_memory(&self) -> bool {
self.name.starts_with("memory")
}
pub fn ranges(&self) -> Option<VecRange<'a>> {
let prop = self.find_property("ranges")?;
Some(VecRange::new(
self.address_cells as usize,
self.context.address_cells as usize,
self.context.size_cells as usize,
prop.data(),
))
}
pub fn compatibles(&self) -> impl Iterator<Item = &'a str> {
self.find_property("compatible")
.into_iter()
.flat_map(|p| p.as_str_iter())
}
pub fn interrupt_parent(&self) -> Option<Phandle> {
self.find_property("interrupt-parent")
.and_then(|prop| prop.as_interrupt_parent())
.or(self.context.interrupt_parent)
}
pub fn path(&self) -> heapless::String<256> {
let mut result = heapless::String::new();
if self.path_components.is_empty() {
let _ = result.push('/');
return result;
}
for component in &self.path_components {
let _ = result.push('/');
let _ = result.push_str(component);
}
result
}
}
impl fmt::Display for NodeBase<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt_utils::write_indent(f, self.level, " ")?;
let name = if self.name.is_empty() { "/" } else { self.name };
writeln!(f, "{} {{", name)?;
for prop in self.properties() {
fmt_utils::write_indent(f, self.level + 1, " ")?;
writeln!(f, "{};", prop)?;
}
fmt_utils::write_indent(f, self.level, " ")?;
write!(f, "}}")
}
}
#[derive(Clone)]
pub enum Node<'a> {
General(NodeBase<'a>),
Chosen(Chosen<'a>),
Memory(Memory<'a>),
}
impl<'a> From<NodeBase<'a>> for Node<'a> {
fn from(node: NodeBase<'a>) -> Self {
if node.is_chosen() {
Node::Chosen(Chosen::new(node))
} else if node.is_memory() {
Node::Memory(Memory::new(node))
} else {
Node::General(node)
}
}
}
impl<'a> Deref for Node<'a> {
type Target = NodeBase<'a>;
fn deref(&self) -> &Self::Target {
match self {
Node::General(n) => n,
Node::Chosen(c) => c.deref(),
Node::Memory(m) => m.deref(),
}
}
}
impl fmt::Display for Node<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Debug::fmt(self, f)
}
}
impl fmt::Debug for Node<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Node::General(n) => f.debug_tuple("General").field(&n.name()).finish(),
Node::Chosen(c) => c.fmt(f),
Node::Memory(m) => m.fmt(f),
}
}
}
#[derive(Debug, Clone, Default)]
pub(crate) struct ParsedProps {
pub address_cells: Option<u8>,
pub size_cells: Option<u8>,
pub interrupt_parent: Option<Phandle>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum OneNodeState {
Processing,
ChildBegin,
End,
}
pub(crate) struct OneNodeIter<'a> {
reader: Reader<'a>,
strings: Bytes<'a>,
state: OneNodeState,
level: usize,
context: NodeContext,
parsed_props: ParsedProps,
fdt: Fdt<'a>,
}
impl<'a> OneNodeIter<'a> {
pub fn new(
reader: Reader<'a>,
strings: Bytes<'a>,
level: usize,
context: NodeContext,
fdt: Fdt<'a>,
) -> Self {
Self {
reader,
strings,
state: OneNodeState::Processing,
level,
context,
parsed_props: ParsedProps::default(),
fdt,
}
}
pub fn reader(&self) -> &Reader<'a> {
&self.reader
}
pub fn parsed_props(&self) -> &ParsedProps {
&self.parsed_props
}
pub fn read_node_name(
&mut self,
parent_path: &heapless::Vec<&'a str, 16>,
) -> Result<NodeBase<'a>, FdtError> {
let name = self.read_cstr()?;
self.align4();
let data = self.reader.remain();
let mut path_components = parent_path.clone();
if !name.is_empty() {
let _ = path_components.push(name);
}
Ok(NodeBase {
name,
data,
strings: self.strings.clone(),
level: self.level,
address_cells: 2,
size_cells: 1,
context: self.context.clone(),
_fdt: self.fdt.clone(),
path_components,
})
}
fn read_cstr(&mut self) -> Result<&'a str, FdtError> {
let bytes = self.reader.remain();
let cstr = CStr::from_bytes_until_nul(bytes.as_slice())?;
let s = cstr.to_str()?;
let _ = self.reader.read_bytes(s.len() + 1);
Ok(s)
}
fn align4(&mut self) {
let pos = self.reader.position();
let aligned = (pos + U32_SIZE - 1) & !(U32_SIZE - 1);
let skip = aligned - pos;
if skip > 0 {
let _ = self.reader.read_bytes(skip);
}
}
fn read_prop_name(&self, nameoff: u32) -> Result<&'a str, FdtError> {
let bytes = self.strings.slice(nameoff as usize..self.strings.len());
let cstr = CStr::from_bytes_until_nul(bytes.as_slice())?;
Ok(cstr.to_str()?)
}
fn read_u32_be(data: &[u8], offset: usize) -> u64 {
u32::from_be_bytes(data[offset..offset + U32_SIZE].try_into().unwrap()) as u64
}
pub fn process(&mut self) -> Result<OneNodeState, FdtError> {
loop {
let token = self.reader.read_token()?;
match token {
Token::BeginNode => {
self.reader.backtrack(U32_SIZE);
self.state = OneNodeState::ChildBegin;
return Ok(OneNodeState::ChildBegin);
}
Token::EndNode => {
self.state = OneNodeState::End;
return Ok(OneNodeState::End);
}
Token::Prop => {
let len = self.reader.read_u32().ok_or(FdtError::BufferTooSmall {
pos: self.reader.position(),
})? as usize;
let nameoff = self.reader.read_u32().ok_or(FdtError::BufferTooSmall {
pos: self.reader.position(),
})?;
let prop_data = if len > 0 {
self.reader
.read_bytes(len)
.ok_or(FdtError::BufferTooSmall {
pos: self.reader.position(),
})?
} else {
Bytes::new(&[])
};
if let Ok(prop_name) = self.read_prop_name(nameoff) {
match prop_name {
"#address-cells" if len == 4 => {
self.parsed_props.address_cells =
Some(Self::read_u32_be(&prop_data, 0) as u8);
}
"#size-cells" if len == 4 => {
self.parsed_props.size_cells =
Some(Self::read_u32_be(&prop_data, 0) as u8);
}
"interrupt-parent" if len == 4 => {
self.parsed_props.interrupt_parent =
Some(Phandle::from(Self::read_u32_be(&prop_data, 0) as u32));
}
_ => {}
}
}
self.align4();
}
Token::Nop => {
}
Token::End => {
self.state = OneNodeState::End;
return Ok(OneNodeState::End);
}
Token::Data(_) => {
return Err(FdtError::BufferTooSmall {
pos: self.reader.position(),
});
}
}
}
}
}