mod ranges;
mod reg;
use core::ffi::CStr;
use core::fmt;
use log::error;
pub use ranges::*;
pub use reg::{RegInfo, RegIter};
use crate::{
FdtError, Phandle, Status, Token,
data::{Bytes, Reader, StrIter, U32_SIZE, U32Iter},
};
#[derive(Clone)]
pub struct Property<'a> {
name: &'a str,
data: Bytes<'a>,
}
impl<'a> Property<'a> {
pub fn new(name: &'a str, data: Bytes<'a>) -> Self {
Self { name, data }
}
pub fn name(&self) -> &'a str {
self.name
}
pub fn data(&self) -> Bytes<'a> {
self.data.clone()
}
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
pub fn len(&self) -> usize {
self.data.len()
}
pub fn as_u32_iter(&self) -> U32Iter<'a> {
self.data.as_u32_iter()
}
pub fn as_str_iter(&self) -> StrIter<'a> {
self.data.as_str_iter()
}
pub fn as_slice(&self) -> &[u8] {
self.data.as_slice()
}
pub fn as_u64(&self) -> Option<u64> {
let mut iter = self.as_u32_iter();
let high = iter.next()? as u64;
let low = iter.next()? as u64;
if iter.next().is_some() {
return None;
}
Some((high << 32) | low)
}
pub fn as_u32(&self) -> Option<u32> {
let mut iter = self.as_u32_iter();
let value = iter.next()?;
if iter.next().is_some() {
return None;
}
Some(value)
}
pub fn as_str(&self) -> Option<&'a str> {
let bytes = self.data.as_slice();
let cstr = CStr::from_bytes_until_nul(bytes).ok()?;
cstr.to_str().ok()
}
pub fn as_address_cells(&self) -> Option<u8> {
if self.name == "#address-cells" {
self.as_u32().map(|v| v as u8)
} else {
None
}
}
pub fn as_size_cells(&self) -> Option<u8> {
if self.name == "#size-cells" {
self.as_u32().map(|v| v as u8)
} else {
None
}
}
pub fn as_interrupt_cells(&self) -> Option<u8> {
if self.name == "#interrupt-cells" {
self.as_u32().map(|v| v as u8)
} else {
None
}
}
pub fn as_status(&self) -> Option<Status> {
let v = self.as_str()?;
if self.name == "status" {
match v {
"okay" | "ok" => Some(Status::Okay),
"disabled" => Some(Status::Disabled),
_ => None,
}
} else {
None
}
}
pub fn as_phandle(&self) -> Option<Phandle> {
if self.name == "phandle" {
self.as_u32().map(Phandle::from)
} else {
None
}
}
pub fn as_device_type(&self) -> Option<&'a str> {
if self.name == "device_type" {
self.as_str()
} else {
None
}
}
pub fn as_interrupt_parent(&self) -> Option<Phandle> {
if self.name == "interrupt-parent" {
self.as_u32().map(Phandle::from)
} else {
None
}
}
pub fn as_clock_names(&self) -> Option<StrIter<'a>> {
if self.name == "clock-names" {
Some(self.as_str_iter())
} else {
None
}
}
pub fn as_compatible(&self) -> Option<StrIter<'a>> {
if self.name == "compatible" {
Some(self.as_str_iter())
} else {
None
}
}
pub fn is_dma_coherent(&self) -> bool {
self.name == "dma-coherent" && self.data.is_empty()
}
}
impl fmt::Display for Property<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_empty() {
return write!(f, "{}", self.name());
}
if let Some(result) = self.try_format_typed(f) {
return result;
}
match self.name() {
"reg" => {
write!(f, "reg = ")?;
format_bytes(f, &self.data())
}
_ => self.format_generic(f),
}
}
}
impl Property<'_> {
fn try_format_typed(&self, f: &mut fmt::Formatter<'_>) -> Option<fmt::Result> {
if let Some(v) = self.as_address_cells() {
return Some(write!(f, "#address-cells = <{:#x}>", v));
}
if let Some(v) = self.as_size_cells() {
return Some(write!(f, "#size-cells = <{:#x}>", v));
}
if let Some(v) = self.as_interrupt_cells() {
return Some(write!(f, "#interrupt-cells = <{:#x}>", v));
}
if let Some(s) = self.as_status() {
return Some(write!(f, "status = \"{:?}\"", s));
}
if let Some(p) = self.as_phandle() {
return Some(write!(f, "phandle = {}", p));
}
if let Some(p) = self.as_interrupt_parent() {
return Some(write!(f, "interrupt-parent = {}", p));
}
if let Some(s) = self.as_device_type() {
return Some(write!(f, "device_type = \"{}\"", s));
}
if let Some(iter) = self.as_compatible() {
return Some(format_string_list(f, "compatible", iter));
}
if let Some(iter) = self.as_clock_names() {
return Some(format_string_list(f, "clock-names", iter));
}
if self.is_dma_coherent() {
return Some(write!(f, "dma-coherent"));
}
None
}
fn format_generic(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.has_multiple_strings() {
return format_string_list(f, self.name(), self.as_str_iter());
}
if let Some(s) = self.as_str() {
return write!(f, "{} = \"{}\"", self.name(), s);
}
if self.len() == 4 {
let v = u32::from_be_bytes(self.data().as_slice().try_into().unwrap());
return write!(f, "{} = <{:#x}>", self.name(), v);
}
write!(f, "{} = ", self.name())?;
format_bytes(f, &self.data())
}
fn has_multiple_strings(&self) -> bool {
self.data().iter().filter(|&&b| b == 0).count() > 1
}
}
fn format_string_list<'a>(
f: &mut fmt::Formatter<'_>,
name: &str,
iter: impl Iterator<Item = &'a str>,
) -> fmt::Result {
write!(f, "{} = ", name)?;
let mut first = true;
for s in iter {
if !first {
write!(f, ", ")?;
}
write!(f, "\"{}\"", s)?;
first = false;
}
Ok(())
}
fn format_bytes(f: &mut fmt::Formatter<'_>, data: &[u8]) -> fmt::Result {
if data.len().is_multiple_of(4) {
write!(f, "<")?;
let mut first = true;
for chunk in data.chunks(4) {
if !first {
write!(f, " ")?;
}
let v = u32::from_be_bytes(chunk.try_into().unwrap());
write!(f, "{:#x}", v)?;
first = false;
}
write!(f, ">")
} else {
write!(f, "[")?;
for (i, b) in data.iter().enumerate() {
if i > 0 {
write!(f, " ")?;
}
write!(f, "{:02x}", b)?;
}
write!(f, "]")
}
}
pub struct PropIter<'a> {
reader: Reader<'a>,
strings: Bytes<'a>,
finished: bool,
}
impl<'a> PropIter<'a> {
pub(crate) fn new(reader: Reader<'a>, strings: Bytes<'a>) -> Self {
Self {
reader,
strings,
finished: false,
}
}
fn handle_error(&mut self, err: FdtError) {
error!("Property parse error: {}", err);
self.finished = true;
}
fn read_prop_name(&self, nameoff: u32) -> Result<&'a str, FdtError> {
if nameoff as usize >= self.strings.len() {
return Err(FdtError::BufferTooSmall {
pos: nameoff as usize,
});
}
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 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);
}
}
}
impl<'a> Iterator for PropIter<'a> {
type Item = Property<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.finished {
return None;
}
loop {
let token = match self.reader.read_token() {
Ok(t) => t,
Err(e) => {
self.handle_error(e);
return None;
}
};
match token {
Token::Prop => {
let len = match self.reader.read_u32() {
Some(b) => b,
None => {
self.handle_error(FdtError::BufferTooSmall {
pos: self.reader.position(),
});
return None;
}
};
let nameoff = match self.reader.read_u32() {
Some(b) => b,
None => {
self.handle_error(FdtError::BufferTooSmall {
pos: self.reader.position(),
});
return None;
}
};
let prop_data = if len > 0 {
match self.reader.read_bytes(len as _) {
Some(b) => b,
None => {
self.handle_error(FdtError::BufferTooSmall {
pos: self.reader.position(),
});
return None;
}
}
} else {
Bytes::new(&[])
};
let name = match self.read_prop_name(nameoff) {
Ok(n) => n,
Err(e) => {
self.handle_error(e);
return None;
}
};
self.align4();
return Some(Property::new(name, prop_data));
}
Token::BeginNode | Token::EndNode | Token::End => {
self.reader.backtrack(U32_SIZE);
self.finished = true;
return None;
}
Token::Nop => {
continue;
}
Token::Data(_) => {
self.handle_error(FdtError::BufferTooSmall {
pos: self.reader.position(),
});
return None;
}
}
}
}
}