#![no_std]
extern crate alloc;
extern crate hashbrown;
mod error;
pub mod util;
use alloc::{borrow::ToOwned, string::String, vec::Vec};
use core::str;
use serde::{Deserialize, Serialize};
pub use error::*;
use util::{align, SliceRead, VecWrite};
#[cfg(not(feature = "string-dedup"))]
mod string_table;
#[cfg(feature = "string-dedup")]
mod advanced_string_table;
#[cfg(not(feature = "string-dedup"))]
use string_table::StringTable;
#[cfg(feature = "string-dedup")]
use advanced_string_table::StringTable;
const MAGIC_NUMBER: u32 = 0xd00dfeed;
const SUPPORTED_VERSION: u32 = 17;
const COMPAT_VERSION: u32 = 16;
const OF_DT_BEGIN_NODE: u32 = 0x00000001;
const OF_DT_END_NODE: u32 = 0x00000002;
const OF_DT_PROP: u32 = 0x00000003;
const OF_DT_END: u32 = 0x00000009;
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct DeviceTree {
pub version: u32,
pub boot_cpuid_phys: u32,
pub reserved: Vec<(u64, u64)>,
pub root: Node,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct Node {
pub name: String,
pub props: Vec<(String, Vec<u8>)>,
pub children: Vec<Node>,
}
impl DeviceTree {
pub fn load(buffer: &[u8]) -> Result<DeviceTree> {
if buffer.read_be_u32(0)? != MAGIC_NUMBER {
return Err(Error::InvalidMagicNumber);
}
if buffer.read_be_u32(4)? as usize != buffer.len() {
return Err(Error::SizeMismatch);
}
let version = buffer.read_be_u32(20)?;
if version != SUPPORTED_VERSION {
return Err(Error::VersionNotSupported);
}
let off_dt_struct = buffer.read_be_u32(8)? as usize;
let off_dt_strings = buffer.read_be_u32(12)? as usize;
let off_mem_rsvmap = buffer.read_be_u32(16)? as usize;
let boot_cpuid_phys = buffer.read_be_u32(28)?;
let mut reserved = Vec::new();
let mut pos = off_mem_rsvmap;
loop {
let offset = buffer.read_be_u64(pos)?;
pos += 8;
let size = buffer.read_be_u64(pos)?;
pos += 8;
reserved.push((offset, size));
if size == 0 {
break;
}
}
let (_, root) = Node::load(buffer, off_dt_struct, off_dt_strings)?;
Ok(DeviceTree {
version,
boot_cpuid_phys,
reserved,
root,
})
}
pub fn find<'a>(&'a self, path: &str) -> Option<&'a Node> {
if !path.starts_with('/') {
return None;
}
self.root.find(&path[1..])
}
pub fn store(&self) -> Result<Vec<u8>> {
let mut dtb = Vec::new();
let mut strings = StringTable::new();
let len = dtb.len();
dtb.write_be_u32(len, MAGIC_NUMBER)?;
let size_off = dtb.len();
dtb.write_be_u32(size_off, 0)?; let off_dt_struct = dtb.len();
dtb.write_be_u32(off_dt_struct, 0)?; let off_dt_strings = dtb.len();
dtb.write_be_u32(off_dt_strings, 0)?; let off_mem_rsvmap = dtb.len();
dtb.write_be_u32(off_mem_rsvmap, 0)?;
let len = dtb.len();
dtb.write_be_u32(len, SUPPORTED_VERSION)?;
let len = dtb.len();
dtb.write_be_u32(len, COMPAT_VERSION)?;
let len = dtb.len();
dtb.write_be_u32(len, self.boot_cpuid_phys)?;
let off_size_strings = dtb.len();
dtb.write_be_u32(off_size_strings, 0)?; let off_size_struct = dtb.len();
dtb.write_be_u32(off_size_struct, 0)?;
dtb.pad(8)?;
let len = dtb.len();
dtb.write_be_u32(off_mem_rsvmap, len as u32)?;
for reservation in self.reserved.iter() {
let len = dtb.len();
dtb.write_be_u64(len, reservation.0)?;
let len = dtb.len();
dtb.write_be_u64(len, reservation.1)?;
}
dtb.pad(4)?;
let structure_start = dtb.len();
dtb.write_be_u32(off_dt_struct, structure_start as u32)?;
self.root.store(&mut dtb, &mut strings)?;
dtb.pad(4)?;
let len = dtb.len();
dtb.write_be_u32(len, OF_DT_END)?;
let len = dtb.len();
dtb.write_be_u32(off_size_struct, (len - structure_start) as u32)?;
dtb.write_be_u32(off_size_strings, strings.buffer.len() as u32)?;
dtb.pad(4)?;
let len = dtb.len();
dtb.write_be_u32(off_dt_strings, len as u32)?;
dtb.extend_from_slice(&strings.buffer);
let len = dtb.len();
dtb.write_be_u32(size_off, len as u32)?;
Ok(dtb)
}
}
impl Node {
fn load(
buffer: &[u8],
start: usize,
off_dt_strings: usize,
) -> Result<(usize, Node)> {
if buffer.read_be_u32(start)? != OF_DT_BEGIN_NODE {
return Err(Error::ParseError(start));
}
let raw_name = buffer.read_bstring0(start + 4)?;
let mut pos = align(start + 4 + raw_name.len() + 1, 4);
let mut props = Vec::new();
while buffer.read_be_u32(pos)? == OF_DT_PROP {
let val_size = buffer.read_be_u32(pos + 4)? as usize;
let name_offset = buffer.read_be_u32(pos + 8)? as usize;
let val_start = pos + 12;
let val_end = val_start + val_size;
let val = buffer.subslice(val_start, val_end)?;
let prop_name = buffer.read_bstring0(off_dt_strings + name_offset)?;
props.push((str::from_utf8(prop_name)?.to_owned(), val.to_owned()));
pos = align(val_end, 4);
}
let mut children = Vec::new();
while buffer.read_be_u32(pos)? == OF_DT_BEGIN_NODE {
let (new_pos, child_node) = Node::load(buffer, pos, off_dt_strings)?;
pos = new_pos;
children.push(child_node);
}
if buffer.read_be_u32(pos)? != OF_DT_END_NODE {
return Err(Error::ParseError(pos));
}
pos += 4;
Ok((
pos,
Node {
name: str::from_utf8(raw_name)?.to_owned(),
props,
children,
},
))
}
pub fn find<'a>(&'a self, path: &str) -> Option<&'a Node> {
if path.is_empty() {
return Some(self);
}
match path.find('/') {
Some(idx) => {
let (l, r) = path.split_at(idx);
let subpath = &r[1..];
for child in self.children.iter() {
if child.name == l {
return child.find(subpath);
}
}
None
}
None => self.children.iter().find(|n| n.name == path),
}
}
pub fn has_prop(&self, name: &str) -> bool {
self.prop_raw(name).is_some()
}
pub fn prop_str<'a>(&'a self, name: &str) -> Result<&'a str> {
let raw = self.prop_raw(name).ok_or(PropError::NotFound)?;
let l = raw.len();
if l < 1 || raw[l - 1] != 0 {
return Err(PropError::Missing0.into());
}
Ok(str::from_utf8(&raw[..(l - 1)])?)
}
pub fn prop_raw<'a>(&'a self, name: &str) -> Option<&'a Vec<u8>> {
for (key, val) in self.props.iter() {
if key == name {
return Some(val);
}
}
None
}
pub fn prop_u64(&self, name: &str) -> Result<u64> {
let raw = self.prop_raw(name).ok_or(PropError::NotFound)?;
Ok(raw.as_slice().read_be_u64(0)?)
}
pub fn prop_u32(&self, name: &str) -> Result<u32> {
let raw = self.prop_raw(name).ok_or(PropError::NotFound)?;
Ok(raw.as_slice().read_be_u32(0)?)
}
pub fn store(
&self,
structure: &mut Vec<u8>,
strings: &mut StringTable,
) -> Result<()> {
structure.pad(4)?;
let len = structure.len();
structure.write_be_u32(len, OF_DT_BEGIN_NODE)?;
structure.write_bstring0(&self.name)?;
for prop in self.props.iter() {
structure.pad(4)?;
let len = structure.len();
structure.write_be_u32(len, OF_DT_PROP)?;
structure.pad(4)?;
let len = structure.len();
structure.write_be_u32(len, prop.1.len() as u32)?;
structure.pad(4)?;
let len = structure.len();
structure.write_be_u32(len, strings.add_string(&prop.0))?;
structure.extend_from_slice(&prop.1);
}
for child in self.children.iter() {
child.store(structure, strings)?;
}
structure.pad(4)?;
let len = structure.len();
structure.write_be_u32(len, OF_DT_END_NODE)?;
Ok(())
}
}