use alloc::{
collections::BTreeMap,
string::{String, ToString},
vec::Vec,
};
use crate::{
FdtData, FdtEncoder, FdtError, Node, NodeId, NodeType, NodeTypeMut, NodeView, Phandle,
};
pub use fdt_raw::MemoryReservation;
#[derive(Clone)]
pub struct Fdt {
pub boot_cpuid_phys: u32,
pub memory_reservations: Vec<MemoryReservation>,
nodes: BTreeMap<NodeId, Node>,
parent_map: BTreeMap<NodeId, NodeId>,
root: NodeId,
next_id: NodeId,
phandle_cache: BTreeMap<Phandle, NodeId>,
}
impl Default for Fdt {
fn default() -> Self {
Self::new()
}
}
impl Fdt {
pub fn new() -> Self {
let mut nodes = BTreeMap::new();
let root_id: NodeId = 0;
nodes.insert(root_id, Node::new(""));
Self {
boot_cpuid_phys: 0,
memory_reservations: Vec::new(),
nodes,
parent_map: BTreeMap::new(),
root: root_id,
next_id: 1,
phandle_cache: BTreeMap::new(),
}
}
fn alloc_node(&mut self, node: Node) -> NodeId {
let id = self.next_id;
self.next_id += 1;
self.nodes.insert(id, node);
id
}
pub fn root_id(&self) -> NodeId {
self.root
}
pub fn parent_of(&self, id: NodeId) -> Option<NodeId> {
self.parent_map.get(&id).copied()
}
pub fn node(&self, id: NodeId) -> Option<&Node> {
self.nodes.get(&id)
}
pub fn node_mut(&mut self, id: NodeId) -> Option<&mut Node> {
self.nodes.get_mut(&id)
}
pub fn node_count(&self) -> usize {
self.nodes.len()
}
pub fn add_node(&mut self, parent: NodeId, node: Node) -> NodeId {
let name = node.name.clone();
let id = self.alloc_node(node);
self.parent_map.insert(id, parent);
if let Some(parent_node) = self.nodes.get_mut(&parent) {
parent_node.add_child(&name, id);
}
if let Some(phandle) = self.nodes.get(&id).and_then(|n| n.phandle()) {
self.phandle_cache.insert(phandle, id);
}
id
}
pub fn remove_node(&mut self, parent: NodeId, name: &str) -> Option<NodeId> {
let removed_id = {
let parent_node = self.nodes.get_mut(&parent)?;
parent_node.remove_child(name)?
};
self.rebuild_name_cache(parent);
self.remove_subtree(removed_id);
Some(removed_id)
}
fn remove_subtree(&mut self, id: NodeId) {
if let Some(node) = self.nodes.remove(&id) {
self.parent_map.remove(&id);
if let Some(phandle) = node.phandle() {
self.phandle_cache.remove(&phandle);
}
for child_id in node.children() {
self.remove_subtree(*child_id);
}
}
}
fn rebuild_name_cache(&mut self, id: NodeId) {
let names: Vec<(String, usize)> = {
let node = match self.nodes.get(&id) {
Some(n) => n,
None => return,
};
node.children()
.iter()
.enumerate()
.filter_map(|(idx, &child_id)| {
self.nodes.get(&child_id).map(|c| (c.name.clone(), idx))
})
.collect()
};
if let Some(node) = self.nodes.get_mut(&id) {
node.rebuild_name_cache_with_names(&names);
}
}
pub fn resolve_alias(&self, alias: &str) -> Option<&str> {
let root = self.nodes.get(&self.root)?;
let alias_node_id = root.get_child("aliases")?;
let alias_node = self.nodes.get(&alias_node_id)?;
let prop = alias_node.get_property(alias)?;
prop.as_str()
}
fn normalize_path(&self, path: &str) -> Option<String> {
if path.starts_with('/') {
Some(path.to_string())
} else {
self.resolve_alias(path).map(|s| s.to_string())
}
}
pub fn get_by_path_id(&self, path: &str) -> Option<NodeId> {
let normalized_path = self.normalize_path(path)?;
let normalized = normalized_path.trim_start_matches('/');
if normalized.is_empty() {
return Some(self.root);
}
let mut current = self.root;
for part in normalized.split('/') {
let node = self.nodes.get(¤t)?;
current = node.get_child(part)?;
}
Some(current)
}
pub fn get_by_phandle_id(&self, phandle: Phandle) -> Option<NodeId> {
self.phandle_cache.get(&phandle).copied()
}
pub fn path_of(&self, id: NodeId) -> String {
let mut parts: Vec<&str> = Vec::new();
let mut cur = id;
while let Some(node) = self.nodes.get(&cur) {
if cur == self.root {
break;
}
parts.push(&node.name);
match self.parent_map.get(&cur) {
Some(&p) => cur = p,
None => break,
}
}
parts.reverse();
if parts.is_empty() {
return String::from("/");
}
format!("/{}", parts.join("/"))
}
pub fn remove_by_path(&mut self, path: &str) -> Option<NodeId> {
let normalized = path.trim_start_matches('/');
if normalized.is_empty() {
return None; }
let parts: Vec<&str> = normalized.split('/').collect();
let child_name = *parts.last()?;
let parent_path = &parts[..parts.len() - 1];
let mut parent_id = self.root;
for &part in parent_path {
let node = self.nodes.get(&parent_id)?;
parent_id = node.get_child(part)?;
}
self.remove_node(parent_id, child_name)
}
pub fn iter_node_ids(&self) -> NodeDfsIter<'_> {
NodeDfsIter {
fdt: self,
stack: vec![self.root],
}
}
pub fn from_bytes(data: &[u8]) -> Result<Self, FdtError> {
let raw_fdt = fdt_raw::Fdt::from_bytes(data)?;
Self::from_raw(&raw_fdt)
}
pub unsafe fn from_ptr(ptr: *mut u8) -> Result<Self, FdtError> {
let raw_fdt = unsafe { fdt_raw::Fdt::from_ptr(ptr)? };
Self::from_raw(&raw_fdt)
}
fn from_raw(raw_fdt: &fdt_raw::Fdt) -> Result<Self, FdtError> {
let header = raw_fdt.header();
let mut fdt = Fdt {
boot_cpuid_phys: header.boot_cpuid_phys,
memory_reservations: raw_fdt.memory_reservations().collect(),
nodes: BTreeMap::new(),
parent_map: BTreeMap::new(),
root: 0,
next_id: 0,
phandle_cache: BTreeMap::new(),
};
let mut id_stack: Vec<(NodeId, usize)> = Vec::new();
for raw_node in raw_fdt.all_nodes() {
let level = raw_node.level();
let node = Node::from(&raw_node);
let node_name = node.name.clone();
let node_id = fdt.alloc_node(node);
if let Some(phandle) = fdt.nodes.get(&node_id).and_then(|n| n.phandle()) {
fdt.phandle_cache.insert(phandle, node_id);
}
while let Some(&(_, stack_level)) = id_stack.last() {
if stack_level >= level {
id_stack.pop();
} else {
break;
}
}
if let Some(&(parent_id, _)) = id_stack.last() {
fdt.parent_map.insert(node_id, parent_id);
if let Some(parent) = fdt.nodes.get_mut(&parent_id) {
parent.add_child(&node_name, node_id);
}
} else {
fdt.root = node_id;
}
id_stack.push((node_id, level));
}
Ok(fdt)
}
pub fn get_by_path(&self, path: &str) -> Option<NodeType<'_>> {
let id = self.get_by_path_id(path)?;
Some(NodeView::new(self, id).classify())
}
pub fn get_by_path_mut(&mut self, path: &str) -> Option<NodeTypeMut<'_>> {
let id = self.get_by_path_id(path)?;
Some(NodeView::new(self, id).classify_mut())
}
pub fn get_by_phandle(&self, phandle: crate::Phandle) -> Option<NodeType<'_>> {
let id = self.get_by_phandle_id(phandle)?;
Some(NodeView::new(self, id).classify())
}
pub fn get_by_phandle_mut(&mut self, phandle: crate::Phandle) -> Option<NodeTypeMut<'_>> {
let id = self.get_by_phandle_id(phandle)?;
Some(NodeView::new(self, id).classify_mut())
}
fn iter_raw_nodes(&self) -> impl Iterator<Item = NodeView<'_>> {
self.iter_node_ids().map(move |id| NodeView::new(self, id))
}
pub fn all_nodes(&self) -> impl Iterator<Item = NodeType<'_>> {
self.iter_raw_nodes().map(|v| v.classify())
}
pub fn root_mut(&mut self) -> NodeTypeMut<'_> {
self.view_typed_mut(self.root).unwrap()
}
pub fn find_compatible(&self, compatible: &[&str]) -> Vec<NodeType<'_>> {
let mut results = Vec::new();
for node_ref in self.all_nodes() {
let compatibles = node_ref.as_node().compatibles();
let mut found = false;
for comp in compatibles {
if compatible.contains(&comp) {
results.push(node_ref);
found = true;
break;
}
}
if found {
continue;
}
}
results
}
pub fn encode(&self) -> FdtData {
FdtEncoder::new(self).encode()
}
}
pub struct NodeDfsIter<'a> {
fdt: &'a Fdt,
stack: Vec<NodeId>,
}
impl<'a> Iterator for NodeDfsIter<'a> {
type Item = NodeId;
fn next(&mut self) -> Option<Self::Item> {
let id = self.stack.pop()?;
if let Some(node) = self.fdt.nodes.get(&id) {
for &child_id in node.children().iter().rev() {
self.stack.push(child_id);
}
}
Some(id)
}
}