1use core::{iter, ptr::NonNull};
2
3use crate::{
4 chosen::Chosen, error::*, memory::Memory, meta::MetaData, node::Node, read::FdtReader,
5 FdtHeader, MemoryRegion, Phandle, Token,
6};
7
8#[derive(Clone)]
10pub struct Fdt<'a> {
11 pub(crate) header: FdtHeader,
12 pub(crate) data: &'a [u8],
13}
14
15impl<'a> Fdt<'a> {
16 pub fn from_bytes(data: &'a [u8]) -> FdtResult<'a, Self> {
18 let header = FdtHeader::from_bytes(data)?;
19
20 header.valid_magic()?;
21
22 Ok(Self { header, data })
23 }
24
25 pub fn from_ptr(ptr: NonNull<u8>) -> FdtResult<'a, Self> {
27 let tmp_header =
28 unsafe { core::slice::from_raw_parts(ptr.as_ptr(), core::mem::size_of::<FdtHeader>()) };
29 let real_size = FdtHeader::from_bytes(tmp_header)?.totalsize.get() as usize;
30
31 Self::from_bytes(unsafe { core::slice::from_raw_parts(ptr.as_ptr(), real_size) })
32 }
33
34 fn reader(&'a self, offset: usize) -> FdtReader<'a> {
35 FdtReader::new(&self.data[offset..])
36 }
37
38 pub fn total_size(&self) -> usize {
39 self.header.totalsize.get() as _
40 }
41
42 pub fn version(&self) -> usize {
43 self.header.version.get() as _
44 }
45
46 pub fn boot_cpuid_phys(&self) -> u32 {
49 self.header.boot_cpuid_phys.get()
50 }
51
52 pub fn memory_reservation_block(&self) -> impl Iterator<Item = MemoryRegion> + '_ {
56 let mut reader = self.reader(self.header.off_mem_rsvmap.get() as _);
57 iter::from_fn(move || match reader.reserved_memory() {
58 Some(region) => {
59 if region.address == 0 && region.size == 0 {
60 None
61 } else {
62 Some(region.into())
63 }
64 }
65 None => None,
66 })
67 }
68
69 pub fn reserved_memory(&self) -> impl Iterator<Item = Node<'a>> + 'a {
73 self.find_nodes("/reserved-memory")
74 }
75
76 pub(crate) fn get_str(&self, offset: usize) -> FdtResult<'a, &'a str> {
77 let string_bytes = &self.data[self.header.strings_range()];
78 let reader = FdtReader::new(&string_bytes[offset..]);
79 reader.peek_str()
80 }
81
82 pub fn all_nodes(&self) -> impl Iterator<Item = Node<'a>> {
83 self.new_fdt_itr()
84 }
85
86 fn new_fdt_itr(&self) -> FdtIter<'a> {
87 let struct_bytes = &self.data[self.header.struct_range()];
88
89 let reader = FdtReader::new(struct_bytes);
90 FdtIter {
91 fdt: self.clone(),
92 current_level: 0,
93 reader,
94 stack: Default::default(),
95 node_reader: None,
96 node_name: "",
97 }
98 }
99
100 pub fn chosen(&'a self) -> Option<Chosen<'a>> {
101 self.find_nodes("/chosen").next().map(Chosen::new)
102 }
103
104 pub fn get_node_by_phandle(&self, phandle: Phandle) -> Option<Node<'a>> {
105 self.all_nodes()
106 .find(|x| match x.phandle() {
107 Some(p) => p.eq(&phandle),
108 None => false,
109 })
110 .clone()
111 }
112
113 pub fn get_node_by_name(&'a self, name: &str) -> Option<Node<'a>> {
114 self.all_nodes().find(|x| x.name().eq(name)).clone()
115 }
116
117 pub fn find_compatible(&'a self, with: &'a [&'a str]) -> impl Iterator<Item = Node<'a>> + 'a {
118 let mut all = self.all_nodes();
119
120 iter::from_fn(move || loop {
121 let node = all.next()?;
122 let caps = node.compatibles();
123 for cap in caps {
124 for want in with {
125 if cap.eq(*want) {
126 return Some(node);
127 }
128 }
129 }
130 })
131 }
132
133 pub fn find_nodes(&self, path: &'a str) -> impl Iterator<Item = Node<'a>> + 'a {
135 let path = if path.starts_with("/") {
136 path
137 } else {
138 self.find_aliase(path).expect("aliase not found")
139 };
140
141 IterFindNode::new(self.new_fdt_itr(), path)
142 }
143
144 pub fn find_aliase(&self, name: &str) -> Option<&'a str> {
145 let aliases = self.find_nodes("/aliases").next()?;
146 for prop in aliases.propertys() {
147 if prop.name.eq(name) {
148 return Some(prop.str());
149 }
150 }
151 None
152 }
153
154 pub fn memory(&'a self) -> impl Iterator<Item = Memory<'a>> + 'a {
155 self.find_nodes("/memory").map(Memory::new)
156 }
157}
158
159pub struct FdtIter<'a> {
160 fdt: Fdt<'a>,
161 current_level: usize,
162 reader: FdtReader<'a>,
163 stack: [MetaData<'a>; 12],
164 node_reader: Option<FdtReader<'a>>,
165 node_name: &'a str,
166}
167
168impl<'a> FdtIter<'a> {
169 fn get_meta_parent(&self) -> MetaData<'a> {
170 let mut meta = MetaData::default();
171 let level = match self.level_parent_index() {
172 Some(l) => l,
173 None => return MetaData::default(),
174 } + 1;
175 macro_rules! get_field {
176 ($cell:ident) => {{
177 let mut size = None;
178 for i in (0..level).rev() {
179 if let Some(cell_size) = &self.stack[i].$cell {
180 size = Some(cell_size.clone());
181 break;
182 }
183 }
184 meta.$cell = size;
185 }};
186 }
187
188 get_field!(address_cells);
189 get_field!(size_cells);
190 get_field!(clock_cells);
191 get_field!(interrupt_cells);
192 get_field!(gpio_cells);
193 get_field!(dma_cells);
194 get_field!(cooling_cells);
195 get_field!(range);
196 get_field!(interrupt_parent);
197
198 meta
199 }
200 fn level_current_index(&self) -> usize {
201 self.current_level - 1
202 }
203 fn level_parent_index(&self) -> Option<usize> {
204 if self.level_current_index() > 0 {
205 Some(self.level_current_index() - 1)
206 } else {
207 None
208 }
209 }
210
211 fn handle_node_begin(&mut self) {
212 self.current_level += 1;
213 let i = self.level_current_index();
214 self.stack[i] = MetaData::default();
215 self.node_name = self.reader.take_unit_name().unwrap();
216 self.node_reader = Some(self.reader.clone());
217 }
218
219 fn finish_node(&mut self) -> Option<Node<'a>> {
220 let reader = self.node_reader.take()?;
221 let level = self.current_level;
222 let meta = self.stack[self.level_current_index()].clone();
223 let meta_parent = self.get_meta_parent();
224
225 let mut node = Node::new(&self.fdt, level, self.node_name, reader, meta_parent, meta);
226 let ranges = node.node_ranges();
227 self.stack[self.level_current_index()].range = ranges.clone();
228 let ph = node.node_interrupt_parent();
229 self.stack[self.level_current_index()].interrupt_parent = ph;
230
231 node.meta = self.stack[self.level_current_index()].clone();
232
233 Some(node)
234 }
235}
236
237impl<'a> Iterator for FdtIter<'a> {
238 type Item = Node<'a>;
239
240 fn next(&mut self) -> Option<Self::Item> {
241 loop {
242 let token = self.reader.take_token()?;
243
244 match token {
245 Token::BeginNode => {
246 let node = self.finish_node();
247 self.handle_node_begin();
248 if node.is_some() {
249 return node;
250 }
251 }
252 Token::EndNode => {
253 let node = self.finish_node();
254 self.current_level -= 1;
255 if node.is_some() {
256 return node;
257 }
258 }
259 Token::Prop => {
260 let prop = self.reader.take_prop(&self.fdt)?;
261 let index = self.level_current_index();
262 macro_rules! update_cell {
263 ($cell:ident) => {
264 self.stack[index].$cell = Some(prop.u32() as _)
265 };
266 }
267 match prop.name {
268 "#address-cells" => update_cell!(address_cells),
269 "#size-cells" => update_cell!(size_cells),
270 "#clock-cells" => update_cell!(clock_cells),
271 "#interrupt-cells" => update_cell!(interrupt_cells),
272 "#gpio-cells" => update_cell!(gpio_cells),
273 "#dma-cells" => update_cell!(dma_cells),
274 "#cooling-cells" => update_cell!(cooling_cells),
275 _ => {}
276 }
277 }
278 Token::End => {
279 return self.finish_node();
280 }
281 _ => {}
282 }
283 }
284 }
285}
286
287struct IterFindNode<'a> {
288 itr: FdtIter<'a>,
289 want: &'a str,
290 want_itr: usize,
291 is_path_last: bool,
292}
293
294impl<'a> IterFindNode<'a> {
295 fn new(itr: FdtIter<'a>, want: &'a str) -> Self {
296 IterFindNode {
297 itr,
298 want,
299 want_itr: 0,
300 is_path_last: false,
301 }
302 }
303}
304
305impl<'a> Iterator for IterFindNode<'a> {
306 type Item = Node<'a>;
307
308 fn next(&mut self) -> Option<Self::Item> {
309 let mut out = None;
310 loop {
311 let mut parts = self.want.split("/").filter(|o| !o.is_empty());
312 let mut want_part = "/";
313 for _ in 0..self.want_itr {
314 if let Some(part) = parts.next() {
315 want_part = part;
316 } else {
317 self.is_path_last = true;
318 if let Some(out) = out {
319 return Some(out);
320 }
321 }
322 }
323 let node = self.itr.next()?;
324
325 let eq = if want_part.contains("@") {
326 node.name.eq(want_part)
327 } else {
328 let name = node.name.split("@").next().unwrap();
329 name.eq(want_part)
330 };
331 if eq {
332 self.want_itr += 1;
333 out = Some(node);
334 }
335 }
336 }
337}