1use crate::{
6 parsing::{BigEndianU32, BigEndianU64, CStr, FdtData},
7 standard_nodes::{Compatible, MemoryRegion},
8 Fdt,
9};
10
11const FDT_BEGIN_NODE: u32 = 1;
12const FDT_END_NODE: u32 = 2;
13const FDT_PROP: u32 = 3;
14pub(crate) const FDT_NOP: u32 = 4;
15const FDT_END: u32 = 5;
16
17#[derive(Debug, Clone, Copy)]
18#[repr(C)]
19struct FdtProperty {
20 len: BigEndianU32,
21 name_offset: BigEndianU32,
22}
23
24impl FdtProperty {
25 fn from_bytes(bytes: &mut FdtData<'_>) -> Option<Self> {
26 let len = bytes.u32()?;
27 let name_offset = bytes.u32()?;
28
29 Some(Self { len, name_offset })
30 }
31}
32
33#[derive(Debug, Clone, Copy)]
35pub struct FdtNode<'b, 'a: 'b> {
36 pub name: &'a str,
37 pub(crate) header: &'b Fdt<'a>,
38 props: &'a [u8],
39 parent_props: Option<&'a [u8]>,
40}
41
42impl<'b, 'a: 'b> FdtNode<'b, 'a> {
43 fn new(
44 name: &'a str,
45 header: &'b Fdt<'a>,
46 props: &'a [u8],
47 parent_props: Option<&'a [u8]>,
48 ) -> Self {
49 Self { name, header, props, parent_props }
50 }
51
52 pub fn properties(self) -> impl Iterator<Item = NodeProperty<'a>> + 'b {
54 let mut stream = FdtData::new(self.props);
55 let mut done = false;
56
57 core::iter::from_fn(move || {
58 if stream.is_empty() || done {
59 return None;
60 }
61
62 while stream.peek_u32()?.get() == FDT_NOP {
63 stream.skip(4);
64 }
65
66 if stream.peek_u32().unwrap().get() == FDT_PROP {
67 Some(NodeProperty::parse(&mut stream, self.header))
68 } else {
69 done = true;
70 None
71 }
72 })
73 }
74
75 pub fn property(self, name: &str) -> Option<NodeProperty<'a>> {
77 self.properties().find(|p| p.name == name)
78 }
79
80 pub fn children(self) -> impl Iterator<Item = FdtNode<'b, 'a>> {
82 let mut stream = FdtData::new(self.props);
83
84 while stream.peek_u32().unwrap().get() == FDT_NOP {
85 stream.skip(4);
86 }
87
88 while stream.peek_u32().unwrap().get() == FDT_PROP {
89 NodeProperty::parse(&mut stream, self.header);
90 }
91
92 let mut done = false;
93
94 core::iter::from_fn(move || {
95 if stream.is_empty() || done {
96 return None;
97 }
98
99 while stream.peek_u32()?.get() == FDT_NOP {
100 stream.skip(4);
101 }
102
103 if stream.peek_u32()?.get() == FDT_BEGIN_NODE {
104 let origin = stream.remaining();
105 let ret = {
106 stream.skip(4);
107 let unit_name = CStr::new(stream.remaining()).expect("unit name").as_str()?;
108 let full_name_len = unit_name.len() + 1;
109 stream.skip(full_name_len);
110
111 if full_name_len % 4 != 0 {
112 stream.skip(4 - (full_name_len % 4));
113 }
114
115 Some(Self::new(unit_name, self.header, stream.remaining(), Some(self.props)))
116 };
117
118 stream = FdtData::new(origin);
119
120 skip_current_node(&mut stream, self.header);
121
122 ret
123 } else {
124 done = true;
125 None
126 }
127 })
128 }
129
130 pub fn reg(self) -> Option<impl Iterator<Item = crate::MemoryRegion> + 'a> {
144 let sizes = self.parent_cell_sizes();
145 if sizes.address_cells > 2 || sizes.size_cells > 2 {
146 return None;
147 }
148
149 let mut reg = None;
150 for prop in self.properties() {
151 if prop.name == "reg" {
152 let mut stream = FdtData::new(prop.value);
153 reg = Some(core::iter::from_fn(move || {
154 let starting_address = match sizes.address_cells {
155 1 => stream.u32()?.get() as usize,
156 2 => stream.u64()?.get() as usize,
157 _ => return None,
158 } as *const u8;
159
160 let size = match sizes.size_cells {
161 0 => None,
162 1 => Some(stream.u32()?.get() as usize),
163 2 => Some(stream.u64()?.get() as usize),
164 _ => return None,
165 };
166
167 Some(MemoryRegion { starting_address, size })
168 }));
169 break;
170 }
171 }
172
173 reg
174 }
175
176 pub fn raw_reg(self) -> Option<impl Iterator<Item = RawReg<'a>> + 'a> {
179 let sizes = self.parent_cell_sizes();
180
181 if let Some(prop) = self.property("reg") {
182 let mut stream = FdtData::new(prop.value);
183 return Some(core::iter::from_fn(move || {
184 Some(RawReg {
185 address: stream.take(sizes.address_cells * 4)?,
186 size: stream.take(sizes.size_cells * 4)?,
187 })
188 }));
189 }
190
191 None
192 }
193
194 pub fn compatible(self) -> Option<Compatible<'a>> {
196 let mut s = None;
197 for prop in self.properties() {
198 if prop.name == "compatible" {
199 s = Some(Compatible { data: prop.value });
200 }
201 }
202
203 s
204 }
205
206 pub fn cell_sizes(self) -> CellSizes {
208 let mut cell_sizes = CellSizes::default();
209
210 for property in self.properties() {
211 match property.name {
212 "#address-cells" => {
213 cell_sizes.address_cells = BigEndianU32::from_bytes(property.value)
214 .expect("not enough bytes for #address-cells value")
215 .get() as usize;
216 }
217 "#size-cells" => {
218 cell_sizes.size_cells = BigEndianU32::from_bytes(property.value)
219 .expect("not enough bytes for #size-cells value")
220 .get() as usize;
221 }
222 _ => {}
223 }
224 }
225
226 cell_sizes
227 }
228
229 pub fn interrupt_parent(self) -> Option<FdtNode<'b, 'a>> {
231 self.properties()
232 .find(|p| p.name == "interrupt-parent")
233 .and_then(|p| self.header.find_phandle(BigEndianU32::from_bytes(p.value)?.get()))
234 }
235
236 pub fn interrupt_cells(self) -> Option<usize> {
238 let mut interrupt_cells = None;
239
240 if let Some(prop) = self.property("#interrupt-cells") {
241 interrupt_cells = BigEndianU32::from_bytes(prop.value).map(|n| n.get() as usize)
242 }
243
244 interrupt_cells
245 }
246
247 pub fn interrupts(self) -> Option<impl Iterator<Item = usize> + 'a> {
249 let sizes = self.parent_interrupt_cells()?;
250
251 let mut interrupt = None;
252 for prop in self.properties() {
253 if prop.name == "interrupts" {
254 let mut stream = FdtData::new(prop.value);
255 interrupt = Some(core::iter::from_fn(move || {
256 let interrupt = match sizes {
257 1 => stream.u32()?.get() as usize,
258 2 => stream.u64()?.get() as usize,
259 _ => return None,
260 };
261
262 Some(interrupt)
263 }));
264 break;
265 }
266 }
267
268 interrupt
269 }
270
271 pub(crate) fn parent_cell_sizes(self) -> CellSizes {
272 let mut cell_sizes = CellSizes::default();
273
274 if let Some(parent) = self.parent_props {
275 let parent =
276 FdtNode { name: "", props: parent, header: self.header, parent_props: None };
277 cell_sizes = parent.cell_sizes();
278 }
279
280 cell_sizes
281 }
282
283 pub(crate) fn parent_interrupt_cells(self) -> Option<usize> {
284 let mut interrupt_cells = None;
285 let parent = self
286 .property("interrupt-parent")
287 .and_then(|p| self.header.find_phandle(BigEndianU32::from_bytes(p.value)?.get()))
288 .or_else(|| {
289 Some(FdtNode {
290 name: "",
291 props: self.parent_props?,
292 header: self.header,
293 parent_props: None,
294 })
295 });
296
297 if let Some(size) = parent.and_then(|parent| parent.interrupt_cells()) {
298 interrupt_cells = Some(size);
299 }
300
301 interrupt_cells
302 }
303}
304
305#[derive(Debug, Clone, Copy)]
307pub struct CellSizes {
308 pub address_cells: usize,
310 pub size_cells: usize,
312}
313
314impl Default for CellSizes {
315 fn default() -> Self {
316 CellSizes { address_cells: 2, size_cells: 1 }
317 }
318}
319
320#[derive(Debug, Clone, Copy, PartialEq)]
322pub struct RawReg<'a> {
323 pub address: &'a [u8],
326 pub size: &'a [u8],
329}
330
331pub(crate) fn find_node<'b, 'a: 'b>(
332 stream: &mut FdtData<'a>,
333 name: &str,
334 header: &'b Fdt<'a>,
335 parent_props: Option<&'a [u8]>,
336) -> Option<FdtNode<'b, 'a>> {
337 let mut parts = name.splitn(2, '/');
338 let looking_for = parts.next()?;
339
340 stream.skip_nops();
341
342 let curr_data = stream.remaining();
343
344 match stream.u32()?.get() {
345 FDT_BEGIN_NODE => {}
346 _ => return None,
347 }
348
349 let unit_name = CStr::new(stream.remaining()).expect("unit name C str").as_str()?;
350
351 let full_name_len = unit_name.len() + 1;
352 skip_4_aligned(stream, full_name_len);
353
354 let looking_contains_addr = looking_for.contains('@');
355 let addr_name_same = unit_name == looking_for;
356 let base_name_same = unit_name.split('@').next()? == looking_for;
357
358 if (looking_contains_addr && !addr_name_same) || (!looking_contains_addr && !base_name_same) {
359 *stream = FdtData::new(curr_data);
360 skip_current_node(stream, header);
361
362 return None;
363 }
364
365 let next_part = match parts.next() {
366 None | Some("") => {
367 return Some(FdtNode::new(unit_name, header, stream.remaining(), parent_props))
368 }
369 Some(part) => part,
370 };
371
372 stream.skip_nops();
373
374 let parent_props = Some(stream.remaining());
375
376 while stream.peek_u32()?.get() == FDT_PROP {
377 let _ = NodeProperty::parse(stream, header);
378 }
379
380 while stream.peek_u32()?.get() == FDT_BEGIN_NODE {
381 if let Some(p) = find_node(stream, next_part, header, parent_props) {
382 return Some(p);
383 }
384 }
385
386 stream.skip_nops();
387
388 if stream.u32()?.get() != FDT_END_NODE {
389 return None;
390 }
391
392 None
393}
394
395pub(crate) fn all_nodes<'b, 'a: 'b>(header: &'b Fdt<'a>) -> impl Iterator<Item = FdtNode<'b, 'a>> {
397 let mut stream = FdtData::new(header.structs_block());
398 let mut done = false;
399 let mut parents: [&[u8]; 64] = [&[]; 64];
400 let mut parent_index = 0;
401
402 core::iter::from_fn(move || {
403 if stream.is_empty() || done {
404 return None;
405 }
406
407 while stream.peek_u32()?.get() == FDT_END_NODE {
408 parent_index -= 1;
409 stream.skip(4);
410 }
411
412 if stream.peek_u32()?.get() == FDT_END {
413 done = true;
414 return None;
415 }
416
417 while stream.peek_u32()?.get() == FDT_NOP {
418 stream.skip(4);
419 }
420
421 match stream.u32()?.get() {
422 FDT_BEGIN_NODE => {}
423 _ => return None,
424 }
425
426 let unit_name = CStr::new(stream.remaining()).expect("unit name C str").as_str().unwrap();
427 let full_name_len = unit_name.len() + 1;
428 skip_4_aligned(&mut stream, full_name_len);
429
430 let curr_node = stream.remaining();
431
432 parent_index += 1;
433 parents[parent_index] = curr_node;
434
435 while stream.peek_u32()?.get() == FDT_NOP {
436 stream.skip(4);
437 }
438
439 while stream.peek_u32()?.get() == FDT_PROP {
440 NodeProperty::parse(&mut stream, header);
441 }
442
443 Some(FdtNode {
444 name: if unit_name.is_empty() { "/" } else { unit_name },
445 header,
446 parent_props: match parent_index {
447 1 => None,
448 _ => Some(parents[parent_index - 1]),
449 },
450 props: curr_node,
451 })
452 })
453}
454
455pub(crate) fn skip_current_node<'a>(stream: &mut FdtData<'a>, header: &Fdt<'a>) {
456 assert_eq!(stream.u32().unwrap().get(), FDT_BEGIN_NODE, "bad node");
457
458 let unit_name = CStr::new(stream.remaining()).expect("unit_name C str").as_str().unwrap();
459 let full_name_len = unit_name.len() + 1;
460 skip_4_aligned(stream, full_name_len);
461
462 while stream.peek_u32().unwrap().get() == FDT_PROP {
463 NodeProperty::parse(stream, header);
464 }
465
466 while stream.peek_u32().unwrap().get() == FDT_BEGIN_NODE {
467 skip_current_node(stream, header);
468 }
469
470 stream.skip_nops();
471
472 assert_eq!(stream.u32().unwrap().get(), FDT_END_NODE, "bad node");
473}
474
475#[derive(Debug, Clone, Copy)]
477pub struct NodeProperty<'a> {
478 pub name: &'a str,
480 pub value: &'a [u8],
482}
483
484impl<'a> NodeProperty<'a> {
485 pub fn as_usize(self) -> Option<usize> {
487 match self.value.len() {
488 4 => BigEndianU32::from_bytes(self.value).map(|i| i.get() as usize),
489 8 => BigEndianU64::from_bytes(self.value).map(|i| i.get() as usize),
490 _ => None,
491 }
492 }
493
494 pub fn as_str(self) -> Option<&'a str> {
496 core::str::from_utf8(self.value).map(|s| s.trim_end_matches('\0')).ok()
497 }
498
499 fn parse(stream: &mut FdtData<'a>, header: &Fdt<'a>) -> Self {
500 match stream.u32().unwrap().get() {
501 FDT_PROP => {}
502 other => panic!("bad prop, tag: {}", other),
503 }
504
505 let prop = FdtProperty::from_bytes(stream).expect("FDT property");
506 let data_len = prop.len.get() as usize;
507
508 let data = &stream.remaining()[..data_len];
509
510 skip_4_aligned(stream, data_len);
511
512 NodeProperty { name: header.str_at_offset(prop.name_offset.get() as usize), value: data }
513 }
514}
515
516#[derive(Debug)]
518#[repr(C)]
519pub struct MemoryReservation {
520 pub(crate) address: BigEndianU64,
521 pub(crate) size: BigEndianU64,
522}
523
524impl MemoryReservation {
525 pub fn address(&self) -> *const u8 {
527 self.address.get() as usize as *const u8
528 }
529
530 pub fn size(&self) -> usize {
532 self.size.get() as usize
533 }
534
535 pub(crate) fn from_bytes(bytes: &mut FdtData<'_>) -> Option<Self> {
536 let address = bytes.u64()?;
537 let size = bytes.u64()?;
538
539 Some(Self { address, size })
540 }
541}
542
543fn skip_4_aligned(stream: &mut FdtData<'_>, len: usize) {
544 stream.skip((len + 3) & !0x3);
545}