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 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 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 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 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_cache: BTreeMap<String, BTreeSet<usize>>,
145 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}