fdt_parser/cache/
fdt.rs

1use alloc::{
2    collections::{btree_map::BTreeMap, btree_set::BTreeSet},
3    string::{String, ToString},
4    sync::Arc,
5    vec::Vec,
6};
7
8use super::{Align4Vec, Node};
9use crate::{base, cache::NodeMeta, data::Raw, FdtError, Header, Phandle};
10
11#[derive(Clone)]
12pub struct Fdt {
13    pub(super) inner: Arc<Inner>,
14}
15
16impl Fdt {
17    /// Create a new `Fdt` from byte slice.
18    pub fn from_bytes(data: &[u8]) -> Result<Fdt, FdtError> {
19        let inner = Inner::new(data)?;
20        Ok(Self {
21            inner: Arc::new(inner),
22        })
23    }
24
25    pub fn as_slice(&self) -> &[u8] {
26        &self.inner.raw
27    }
28
29    /// Create a new `Fdt` from a raw pointer and size in bytes.
30    ///
31    /// # Safety
32    ///
33    /// The caller must ensure that the pointer is valid and points to a
34    /// memory region of at least `size` bytes that contains a valid device tree
35    /// blob.
36    pub unsafe fn from_ptr(ptr: *mut u8) -> Result<Fdt, FdtError> {
37        let b = base::Fdt::from_ptr(ptr)?;
38        Self::from_bytes(b.raw())
39    }
40
41    pub(super) fn fdt_base<'a>(&'a self) -> base::Fdt<'a> {
42        base::Fdt::from_bytes(&self.inner.raw).unwrap()
43    }
44
45    pub fn version(&self) -> u32 {
46        self.fdt_base().version()
47    }
48
49    pub fn header(&self) -> Header {
50        self.fdt_base().header().clone()
51    }
52
53    pub fn all_nodes(&self) -> Vec<Node> {
54        self.inner
55            .all_nodes
56            .iter()
57            .map(|meta| Node::new(self, meta))
58            .collect()
59    }
60
61    /// if path start with '/' then search by path, else search by aliases
62    pub fn find_nodes(&self, path: impl AsRef<str>) -> Vec<Node> {
63        let path = path.as_ref();
64        let path = if path.starts_with("/") {
65            path.to_string()
66        } else {
67            self.find_aliase(path).unwrap()
68        };
69        let mut out = Vec::new();
70        for node in self.all_nodes() {
71            if node.full_path().starts_with(path.as_str()) {
72                let right = node.full_path().trim_start_matches(&path);
73                if right.split("/").count() < 2 {
74                    out.push(node);
75                }
76            }
77        }
78
79        out
80    }
81
82    pub fn find_aliase(&self, name: impl AsRef<str>) -> Option<String> {
83        let fdt = self.fdt_base();
84        let s = fdt.find_aliase(name.as_ref()).ok()?;
85        Some(s.into())
86    }
87
88    pub fn get_node_by_phandle(&self, phandle: Phandle) -> Option<Node> {
89        let meta = self.inner.get_node_by_phandle(phandle)?;
90        Some(Node::new(self, &meta))
91    }
92
93    pub fn find_compatible(&self, with: &[&str]) -> Vec<Node> {
94        let mut ids = BTreeSet::new();
95        for &c in with {
96            if let Some(s) = self.inner.compatible_cache.get(c) {
97                for n in s {
98                    ids.insert(n);
99                }
100            }
101        }
102        let mut out = Vec::new();
103        for id in ids {
104            if let Some(meta) = self.inner.get_node_by_index(*id) {
105                out.push(Node::new(self, &meta));
106            }
107        }
108
109        out
110    }
111
112    pub fn memory_reservation_blocks(&self) -> Vec<crate::MemoryRegion> {
113        let fdt = self.fdt_base();
114        fdt.memory_reservation_blocks().collect()
115    }
116
117    pub fn raw<'a>(&'a self) -> Raw<'a> {
118        Raw::new(&self.inner.raw)
119    }
120
121    /// Get a node by its path in the device tree
122    pub fn get_node_by_path(&self, path: &str) -> Option<Node> {
123        let meta = self.inner.get_node_by_path(path)?;
124        Some(Node::new(self, &meta))
125    }
126
127    pub fn memory(&self) -> Result<Vec<super::node::Memory>, FdtError> {
128        let nodes = self.find_nodes("/memory@");
129        let mut out = Vec::new();
130        for node in nodes {
131            let super::Node::Memory(m) = node else {
132                return Err(FdtError::NodeNotFound("memory"));
133            };
134            out.push(m);
135        }
136        Ok(out)
137    }
138}
139
140pub(super) struct Inner {
141    raw: Align4Vec,
142    phandle_cache: BTreeMap<Phandle, usize>,
143    /// compatible -> set(name)
144    compatible_cache: BTreeMap<String, BTreeSet<usize>>,
145    /// same order as all_nodes()
146    all_nodes: Vec<NodeMeta>,
147    path_cache: BTreeMap<String, usize>,
148}
149
150unsafe impl Send for Inner {}
151unsafe impl Sync for Inner {}
152
153impl Inner {
154    fn new(data: &[u8]) -> Result<Self, FdtError> {
155        let b = base::Fdt::from_bytes(data)?;
156        let mut inner = Inner {
157            raw: Align4Vec::new(data),
158            phandle_cache: BTreeMap::new(),
159            compatible_cache: BTreeMap::new(),
160            all_nodes: Vec::new(),
161            path_cache: BTreeMap::new(),
162        };
163        let mut node_vec = Vec::new();
164        let mut path_stack = Vec::new();
165        let mut node_stack: Vec<NodeMeta> = Vec::new();
166        for (i, node) in b.all_nodes().enumerate() {
167            let node = node?;
168            let node_name = node.name();
169            let level = node.level();
170
171            while let Some(last) = node_stack.last() {
172                if level <= last.level {
173                    node_stack.pop();
174                } else {
175                    break;
176                }
177            }
178
179            if level < path_stack.len() {
180                path_stack.truncate(level);
181            }
182            path_stack.push(node_name.trim_start_matches("/"));
183            let full_path = if path_stack.len() > 1 {
184                alloc::format!("/{}", path_stack[1..].join("/"))
185            } else {
186                "/".to_string()
187            };
188            for prop in node.properties() {
189                let _ = prop?;
190            }
191            let parent = node_stack.last();
192            let dnode = NodeMeta::new(&node, full_path.clone(), parent);
193            node_stack.push(dnode.clone());
194            inner.all_nodes.push(dnode.clone());
195            inner.path_cache.insert(full_path, i);
196
197            match node.phandle() {
198                Ok(phandle) => {
199                    inner.phandle_cache.entry(phandle).or_insert(i);
200                }
201                Err(FdtError::NotFound) => {}
202                Err(e) => return Err(e),
203            }
204            match node.compatibles_flatten() {
205                Ok(iter) => {
206                    for compatible in iter {
207                        let map = inner.compatible_cache.entry(compatible.into()).or_default();
208                        map.insert(i);
209                    }
210                }
211                Err(FdtError::NotFound) => {}
212                Err(e) => return Err(e),
213            }
214            node_vec.push(node);
215        }
216
217        Ok(inner)
218    }
219
220    pub(crate) fn get_node_by_path(&self, path: &str) -> Option<NodeMeta> {
221        let idx = self.path_cache.get(path)?;
222        Some(self.all_nodes[*idx].clone())
223    }
224
225    fn get_node_by_index(&self, index: usize) -> Option<NodeMeta> {
226        self.all_nodes.get(index).cloned()
227    }
228
229    fn get_node_by_phandle(&self, phandle: Phandle) -> Option<NodeMeta> {
230        let idx = self.phandle_cache.get(&phandle)?;
231        Some(self.all_nodes[*idx].clone())
232    }
233}