use super::error::DtbError;
use super::header::DtbHeader;
use super::memory::MemoryReservation;
use super::tokens::DtbToken;
use super::tree::{DeviceTreeNode, parse_node_name, parse_property_data};
use alloc::vec::Vec;
#[derive(Debug)]
pub struct DeviceTreeParser<'a> {
data: &'a [u8],
}
impl<'a> DeviceTreeParser<'a> {
#[must_use]
pub fn new(data: &'a [u8]) -> Self {
Self { data }
}
#[must_use]
pub fn data(&self) -> &[u8] {
self.data
}
pub fn parse_header(&self) -> Result<DtbHeader, DtbError> {
let (_remaining, header) = DtbHeader::parse(self.data)?;
Ok(header)
}
pub fn parse_memory_reservations(&self) -> Result<Vec<MemoryReservation>, DtbError> {
let header = self.parse_header()?;
let reservation_data = &self.data[header.off_mem_rsvmap as usize..];
let (_remaining, reservations) = MemoryReservation::parse_all(reservation_data)?;
Ok(reservations)
}
pub fn parse_tree(&self) -> Result<DeviceTreeNode<'a>, DtbError> {
let header = self.parse_header()?;
let struct_block_start = header.off_dt_struct as usize;
let struct_block_end = struct_block_start + header.size_dt_struct as usize;
let strings_block_start = header.off_dt_strings as usize;
if struct_block_start >= self.data.len()
|| struct_block_end > self.data.len()
|| strings_block_start >= self.data.len()
{
return Err(DtbError::MalformedHeader);
}
let struct_block = &self.data[struct_block_start..struct_block_end];
let strings_block = &self.data[strings_block_start..];
Self::parse_structure_block(struct_block, strings_block)
}
pub fn uart_addresses(&self) -> Result<Vec<u64>, DtbError> {
let root = self.parse_tree()?;
let mut addresses = Vec::new();
let uart_compatibles = [
"ns16550a",
"ns16550",
"arm,pl011",
"arm,sbsa-uart",
"snps,dw-apb-uart",
];
for compatible in &uart_compatibles {
let uart_nodes = root.find_compatible_nodes(compatible);
for node in uart_nodes {
if let Some(reg) = node.prop_u32_array("reg")
&& reg.len() >= 2
{
addresses.push(u64::from(reg[0]));
}
}
}
Ok(addresses)
}
pub fn timebase_frequency(&self) -> Result<Option<u32>, DtbError> {
let root = self.parse_tree()?;
if let Some(cpus_node) = root.find_node("/cpus") {
if let Some(freq) = cpus_node.prop_u32("timebase-frequency") {
return Ok(Some(freq));
}
for cpu in cpus_node {
if let Some(freq) = cpu.prop_u32("timebase-frequency") {
return Ok(Some(freq));
}
}
}
Ok(None)
}
pub fn discover_mmio_regions(&self) -> Result<Vec<(u64, u64)>, DtbError> {
let root = self.parse_tree()?;
let mut regions = Vec::new();
for node in root.iter_nodes() {
if let Some(reg) = node.prop_u32_array("reg") {
let mut i = 0;
while i + 1 < reg.len() {
let address = u64::from(reg[i]);
let size = u64::from(reg[i + 1]);
regions.push((address, size));
i += 2;
}
}
}
Ok(regions)
}
pub fn discover_mmio_regions_translated(
&self,
translate_addresses: bool,
) -> Result<Vec<(u64, u64)>, DtbError> {
let root = self.parse_tree()?;
let mut regions = Vec::new();
for node in root.iter_nodes() {
if let Some(reg) = node.prop_u32_array("reg") {
let address_cells = node.address_cells().unwrap_or(2);
let size_cells = node.size_cells().unwrap_or(1);
let entry_size = (address_cells + size_cells) as usize;
let mut i = 0;
while i + entry_size <= reg.len() {
let mut address = 0u64;
for j in 0..address_cells as usize {
address = (address << 32) | u64::from(reg[i + j]);
}
let mut size = 0u64;
for j in 0..size_cells as usize {
size = (size << 32) | u64::from(reg[i + address_cells as usize + j]);
}
let final_address = if translate_addresses {
match node.translate_address(address, None, address_cells) {
Ok(translated) => translated,
Err(_) => {
address
}
}
} else {
address
};
regions.push((final_address, size));
i += entry_size;
}
}
}
Ok(regions)
}
pub fn find_node(&self, path: &str) -> Result<Option<DeviceTreeNode<'a>>, DtbError> {
let root = self.parse_tree()?;
Ok(root.find_node(path).cloned())
}
pub fn find_compatible_nodes(
&self,
compatible: &str,
) -> Result<Vec<DeviceTreeNode<'a>>, DtbError> {
let root = self.parse_tree()?;
let nodes = root.find_compatible_nodes(compatible);
Ok(nodes.into_iter().cloned().collect())
}
fn parse_structure_block(
struct_block: &'a [u8],
strings_block: &'a [u8],
) -> Result<DeviceTreeNode<'a>, DtbError> {
parse_device_tree_iterative(struct_block, strings_block)
}
}
fn parse_device_tree_iterative<'a>(
mut input: &'a [u8],
strings_block: &'a [u8],
) -> Result<DeviceTreeNode<'a>, DtbError> {
use alloc::vec::Vec;
let mut node_stack: Vec<DeviceTreeNode<'a>> = Vec::new();
loop {
let (remaining, token) = DtbToken::parse(input)?;
input = remaining;
match token {
DtbToken::BeginNode => {
let (remaining, name) = parse_node_name(input)?;
input = remaining;
let node = DeviceTreeNode::new(name);
node_stack.push(node);
}
DtbToken::Property => {
let (remaining, property) = parse_property_data(input, strings_block)?;
input = remaining;
if let Some(current_node) = node_stack.last_mut() {
current_node.add_property(property);
} else {
return Err(DtbError::InvalidToken);
}
}
DtbToken::EndNode => {
if let Some(completed_node) = node_stack.pop() {
if node_stack.is_empty() {
return Ok(completed_node);
}
if let Some(parent_node) = node_stack.last_mut() {
parent_node.add_child(completed_node);
}
} else {
return Err(DtbError::InvalidToken);
}
}
DtbToken::End => {
if let Some(root_node) = node_stack.pop()
&& node_stack.is_empty()
{
return Ok(root_node);
}
return Err(DtbError::InvalidToken);
}
}
}
}