Skip to main content

fdt_edit/
fdt.rs

1//! Editable Flattened Device Tree (FDT) structure.
2//!
3//! This module provides the main `Fdt` type for creating, modifying, and
4//! encoding device tree blobs. It supports loading from existing DTB files,
5//! building new trees programmatically, and applying device tree overlays.
6//!
7//! All nodes are stored in a flat `BTreeMap<NodeId, Node>` arena. Child
8//! relationships are represented as `Vec<NodeId>` inside each `Node`.
9
10use alloc::{
11    collections::BTreeMap,
12    string::{String, ToString},
13    vec::Vec,
14};
15
16use crate::{
17    FdtData, FdtEncoder, FdtError, Node, NodeId, NodeType, NodeTypeMut, NodeView, Phandle,
18};
19
20pub use fdt_raw::MemoryReservation;
21
22/// An editable Flattened Device Tree (FDT).
23///
24/// All nodes are stored in a flat `BTreeMap<NodeId, Node>`. The tree structure
25/// is maintained through `Vec<NodeId>` children lists in each `Node` and an
26/// optional `parent: Option<NodeId>` back-pointer.
27#[derive(Clone)]
28pub struct Fdt {
29    /// Boot CPU ID
30    pub boot_cpuid_phys: u32,
31    /// Memory reservation block entries
32    pub memory_reservations: Vec<MemoryReservation>,
33    /// Flat storage for all nodes
34    nodes: BTreeMap<NodeId, Node>,
35    /// Parent mapping: child_id -> parent_id
36    parent_map: BTreeMap<NodeId, NodeId>,
37    /// Root node ID
38    root: NodeId,
39    /// Next unique node ID to allocate
40    next_id: NodeId,
41    /// Cache mapping phandles to node IDs for fast lookup
42    phandle_cache: BTreeMap<Phandle, NodeId>,
43}
44
45impl Default for Fdt {
46    fn default() -> Self {
47        Self::new()
48    }
49}
50
51impl Fdt {
52    /// Creates a new empty FDT with an empty root node.
53    pub fn new() -> Self {
54        let mut nodes = BTreeMap::new();
55        let root_id: NodeId = 0;
56        nodes.insert(root_id, Node::new(""));
57        Self {
58            boot_cpuid_phys: 0,
59            memory_reservations: Vec::new(),
60            nodes,
61            parent_map: BTreeMap::new(),
62            root: root_id,
63            next_id: 1,
64            phandle_cache: BTreeMap::new(),
65        }
66    }
67
68    /// Allocates a new node in the arena, returning its unique ID.
69    fn alloc_node(&mut self, node: Node) -> NodeId {
70        let id = self.next_id;
71        self.next_id += 1;
72        self.nodes.insert(id, node);
73        id
74    }
75
76    /// Returns the root node ID.
77    pub fn root_id(&self) -> NodeId {
78        self.root
79    }
80
81    /// Returns the parent node ID for the given node, if any.
82    pub fn parent_of(&self, id: NodeId) -> Option<NodeId> {
83        self.parent_map.get(&id).copied()
84    }
85
86    /// Returns a reference to the node with the given ID.
87    pub fn node(&self, id: NodeId) -> Option<&Node> {
88        self.nodes.get(&id)
89    }
90
91    /// Returns a mutable reference to the node with the given ID.
92    pub fn node_mut(&mut self, id: NodeId) -> Option<&mut Node> {
93        self.nodes.get_mut(&id)
94    }
95
96    /// Returns the total number of nodes in the tree.
97    pub fn node_count(&self) -> usize {
98        self.nodes.len()
99    }
100
101    /// Adds a new node as a child of `parent`, returning the new node's ID.
102    ///
103    /// Sets the new node's `parent` field and updates the parent's children list
104    /// and name cache.
105    pub fn add_node(&mut self, parent: NodeId, node: Node) -> NodeId {
106        let name = node.name.clone();
107        let id = self.alloc_node(node);
108        self.parent_map.insert(id, parent);
109
110        if let Some(parent_node) = self.nodes.get_mut(&parent) {
111            parent_node.add_child(&name, id);
112        }
113
114        // Update phandle cache if the new node has a phandle
115        if let Some(phandle) = self.nodes.get(&id).and_then(|n| n.phandle()) {
116            self.phandle_cache.insert(phandle, id);
117        }
118
119        id
120    }
121
122    /// Removes a child node (by name) from the given parent, and recursively
123    /// removes the entire subtree from the arena.
124    ///
125    /// Returns the removed node's ID if found.
126    pub fn remove_node(&mut self, parent: NodeId, name: &str) -> Option<NodeId> {
127        let removed_id = {
128            let parent_node = self.nodes.get_mut(&parent)?;
129            parent_node.remove_child(name)?
130        };
131
132        // Rebuild parent's name cache (needs arena access for child names)
133        self.rebuild_name_cache(parent);
134
135        // Recursively remove the subtree
136        self.remove_subtree(removed_id);
137
138        Some(removed_id)
139    }
140
141    /// Recursively removes a node and all its descendants from the arena.
142    fn remove_subtree(&mut self, id: NodeId) {
143        if let Some(node) = self.nodes.remove(&id) {
144            // Remove from parent map
145            self.parent_map.remove(&id);
146            // Remove from phandle cache
147            if let Some(phandle) = node.phandle() {
148                self.phandle_cache.remove(&phandle);
149            }
150            // Recursively remove children
151            for child_id in node.children() {
152                self.remove_subtree(*child_id);
153            }
154        }
155    }
156
157    /// Rebuilds the name cache for a node based on its current children.
158    fn rebuild_name_cache(&mut self, id: NodeId) {
159        let names: Vec<(String, usize)> = {
160            let node = match self.nodes.get(&id) {
161                Some(n) => n,
162                None => return,
163            };
164            node.children()
165                .iter()
166                .enumerate()
167                .filter_map(|(idx, &child_id)| {
168                    self.nodes.get(&child_id).map(|c| (c.name.clone(), idx))
169                })
170                .collect()
171        };
172        if let Some(node) = self.nodes.get_mut(&id) {
173            node.rebuild_name_cache_with_names(&names);
174        }
175    }
176
177    pub fn resolve_alias(&self, alias: &str) -> Option<&str> {
178        let root = self.nodes.get(&self.root)?;
179        let alias_node_id = root.get_child("aliases")?;
180        let alias_node = self.nodes.get(&alias_node_id)?;
181        let prop = alias_node.get_property(alias)?;
182        prop.as_str()
183    }
184
185    /// 规范化路径:如果是别名则解析为完整路径,否则确保以 / 开头
186    fn normalize_path(&self, path: &str) -> Option<String> {
187        if path.starts_with('/') {
188            Some(path.to_string())
189        } else {
190            // 尝试解析别名
191            self.resolve_alias(path).map(|s| s.to_string())
192        }
193    }
194
195    /// Looks up a node by its full path (e.g. "/soc/uart@10000"),
196    /// returning its `NodeId`.
197    ///
198    /// The root node is matched by "/" or "".
199    pub fn get_by_path_id(&self, path: &str) -> Option<NodeId> {
200        let normalized_path = self.normalize_path(path)?;
201        let normalized = normalized_path.trim_start_matches('/');
202        if normalized.is_empty() {
203            return Some(self.root);
204        }
205
206        let mut current = self.root;
207        for part in normalized.split('/') {
208            let node = self.nodes.get(&current)?;
209            current = node.get_child(part)?;
210        }
211        Some(current)
212    }
213
214    /// Looks up a node by its phandle value, returning its `NodeId`.
215    pub fn get_by_phandle_id(&self, phandle: Phandle) -> Option<NodeId> {
216        self.phandle_cache.get(&phandle).copied()
217    }
218
219    /// Computes the full path string for a node by walking up parent links.
220    pub fn path_of(&self, id: NodeId) -> String {
221        let mut parts: Vec<&str> = Vec::new();
222        let mut cur = id;
223        while let Some(node) = self.nodes.get(&cur) {
224            if cur == self.root {
225                break;
226            }
227            parts.push(&node.name);
228            match self.parent_map.get(&cur) {
229                Some(&p) => cur = p,
230                None => break,
231            }
232        }
233        parts.reverse();
234        if parts.is_empty() {
235            return String::from("/");
236        }
237        format!("/{}", parts.join("/"))
238    }
239
240    /// Removes a node and its subtree by path.
241    ///
242    /// Returns the removed node's ID if found.
243    pub fn remove_by_path(&mut self, path: &str) -> Option<NodeId> {
244        let normalized = path.trim_start_matches('/');
245        if normalized.is_empty() {
246            return None; // Cannot remove root
247        }
248
249        let parts: Vec<&str> = normalized.split('/').collect();
250        let child_name = *parts.last()?;
251
252        // Find the parent node
253        let parent_path = &parts[..parts.len() - 1];
254        let mut parent_id = self.root;
255        for &part in parent_path {
256            let node = self.nodes.get(&parent_id)?;
257            parent_id = node.get_child(part)?;
258        }
259
260        self.remove_node(parent_id, child_name)
261    }
262
263    /// Returns a depth-first iterator over all node IDs in the tree.
264    pub fn iter_node_ids(&self) -> NodeDfsIter<'_> {
265        NodeDfsIter {
266            fdt: self,
267            stack: vec![self.root],
268        }
269    }
270
271    /// Parses an FDT from raw byte data.
272    pub fn from_bytes(data: &[u8]) -> Result<Self, FdtError> {
273        let raw_fdt = fdt_raw::Fdt::from_bytes(data)?;
274        Self::from_raw(&raw_fdt)
275    }
276
277    /// Parses an FDT from a raw pointer.
278    ///
279    /// # Safety
280    ///
281    /// The caller must ensure that the pointer is valid and points to a
282    /// valid FDT data structure.
283    pub unsafe fn from_ptr(ptr: *mut u8) -> Result<Self, FdtError> {
284        let raw_fdt = unsafe { fdt_raw::Fdt::from_ptr(ptr)? };
285        Self::from_raw(&raw_fdt)
286    }
287
288    /// Converts from a raw FDT parser instance.
289    fn from_raw(raw_fdt: &fdt_raw::Fdt) -> Result<Self, FdtError> {
290        let header = raw_fdt.header();
291
292        let mut fdt = Fdt {
293            boot_cpuid_phys: header.boot_cpuid_phys,
294            memory_reservations: raw_fdt.memory_reservations().collect(),
295            nodes: BTreeMap::new(),
296            parent_map: BTreeMap::new(),
297            root: 0,
298            next_id: 0,
299            phandle_cache: BTreeMap::new(),
300        };
301
302        // Build node tree using a stack to track parent node IDs.
303        // raw_fdt.all_nodes() yields nodes in DFS pre-order with level info.
304        // We use a stack of (NodeId, level) to find parents.
305        let mut id_stack: Vec<(NodeId, usize)> = Vec::new();
306
307        for raw_node in raw_fdt.all_nodes() {
308            let level = raw_node.level();
309            let node = Node::from(&raw_node);
310            let node_name = node.name.clone();
311
312            // Allocate the node in the arena
313            let node_id = fdt.alloc_node(node);
314
315            // Update phandle cache
316            if let Some(phandle) = fdt.nodes.get(&node_id).and_then(|n| n.phandle()) {
317                fdt.phandle_cache.insert(phandle, node_id);
318            }
319
320            // Pop the stack until we find the parent at level - 1
321            while let Some(&(_, stack_level)) = id_stack.last() {
322                if stack_level >= level {
323                    id_stack.pop();
324                } else {
325                    break;
326                }
327            }
328
329            if let Some(&(parent_id, _)) = id_stack.last() {
330                // Set parent link
331                fdt.parent_map.insert(node_id, parent_id);
332                // Add as child to parent
333                if let Some(parent) = fdt.nodes.get_mut(&parent_id) {
334                    parent.add_child(&node_name, node_id);
335                }
336            } else {
337                // This is the root node
338                fdt.root = node_id;
339            }
340
341            id_stack.push((node_id, level));
342        }
343
344        Ok(fdt)
345    }
346
347    /// Looks up a node by path and returns an immutable classified view.
348    pub fn get_by_path(&self, path: &str) -> Option<NodeType<'_>> {
349        let id = self.get_by_path_id(path)?;
350        Some(NodeView::new(self, id).classify())
351    }
352
353    /// Looks up a node by path and returns a mutable classified view.
354    pub fn get_by_path_mut(&mut self, path: &str) -> Option<NodeTypeMut<'_>> {
355        let id = self.get_by_path_id(path)?;
356        Some(NodeView::new(self, id).classify_mut())
357    }
358
359    /// Looks up a node by phandle and returns an immutable classified view.
360    pub fn get_by_phandle(&self, phandle: crate::Phandle) -> Option<NodeType<'_>> {
361        let id = self.get_by_phandle_id(phandle)?;
362        Some(NodeView::new(self, id).classify())
363    }
364
365    /// Looks up a node by phandle and returns a mutable classified view.
366    pub fn get_by_phandle_mut(&mut self, phandle: crate::Phandle) -> Option<NodeTypeMut<'_>> {
367        let id = self.get_by_phandle_id(phandle)?;
368        Some(NodeView::new(self, id).classify_mut())
369    }
370
371    /// Returns a depth-first iterator over `NodeView`s.
372    fn iter_raw_nodes(&self) -> impl Iterator<Item = NodeView<'_>> {
373        self.iter_node_ids().map(move |id| NodeView::new(self, id))
374    }
375
376    /// Returns a depth-first iterator over classified `NodeType`s.
377    pub fn all_nodes(&self) -> impl Iterator<Item = NodeType<'_>> {
378        self.iter_raw_nodes().map(|v| v.classify())
379    }
380
381    pub fn root_mut(&mut self) -> NodeTypeMut<'_> {
382        self.view_typed_mut(self.root).unwrap()
383    }
384
385    /// Finds nodes with matching compatible strings.
386    pub fn find_compatible(&self, compatible: &[&str]) -> Vec<NodeType<'_>> {
387        let mut results = Vec::new();
388        for node_ref in self.all_nodes() {
389            let compatibles = node_ref.as_node().compatibles();
390            let mut found = false;
391
392            for comp in compatibles {
393                if compatible.contains(&comp) {
394                    results.push(node_ref);
395                    found = true;
396                    break;
397                }
398            }
399
400            if found {
401                continue;
402            }
403        }
404        results
405    }
406
407    /// Encodes the FDT to DTB binary format.
408    pub fn encode(&self) -> FdtData {
409        FdtEncoder::new(self).encode()
410    }
411}
412
413/// Depth-first iterator over all node IDs in the tree.
414pub struct NodeDfsIter<'a> {
415    fdt: &'a Fdt,
416    stack: Vec<NodeId>,
417}
418
419impl<'a> Iterator for NodeDfsIter<'a> {
420    type Item = NodeId;
421
422    fn next(&mut self) -> Option<Self::Item> {
423        let id = self.stack.pop()?;
424        if let Some(node) = self.fdt.nodes.get(&id) {
425            // Push children in reverse order so that the first child is visited first
426            for &child_id in node.children().iter().rev() {
427                self.stack.push(child_id);
428            }
429        }
430        Some(id)
431    }
432}