Skip to main content

fdt_edit/node/view/
mod.rs

1//! Node view types for safe, typed access to device tree nodes.
2//!
3//! `NodeView` and `NodeViewMut` provide safe handles to nodes stored in the
4//! `Fdt` arena. `NodeType` and `NodeTypeMut` enums allow dispatching to
5//! type-specialized views such as `MemoryNodeView` and `IntcNodeView`.
6
7// Specialized node view modules
8mod clock;
9mod generic;
10mod intc;
11mod memory;
12mod pci;
13
14use core::fmt::Display;
15
16use alloc::{borrow::ToOwned, string::String, vec::Vec};
17use enum_dispatch::enum_dispatch;
18use fdt_raw::Phandle;
19
20use crate::{Fdt, Node, NodeId, Property, RangesEntry};
21
22// Re-export specialized view types
23pub use clock::{ClockNodeView, ClockNodeViewMut, ClockRef, ClockType, FixedClock};
24pub use generic::{NodeGeneric, NodeGenericMut};
25pub use intc::{IntcNodeView, IntcNodeViewMut, InterruptRef};
26pub use memory::{MemoryNodeView, MemoryNodeViewMut};
27pub use pci::{PciInterruptInfo, PciInterruptMap, PciNodeView, PciNodeViewMut, PciRange, PciSpace};
28
29#[enum_dispatch]
30pub(crate) trait ViewOp<'a> {
31    fn as_view(&self) -> NodeView<'a>;
32    fn display(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
33        self.as_view().fmt(f)
34    }
35}
36
37pub(crate) trait ViewMutOp<'a> {
38    fn new(node: NodeGenericMut<'a>) -> Self;
39}
40
41// ---------------------------------------------------------------------------
42// NodeView — immutable view
43// ---------------------------------------------------------------------------
44
45/// An immutable view of a node in the device tree.
46///
47/// Borrows the `Fdt` arena and a `NodeId`, providing safe read access to the
48/// node and its relationships (children, parent, path).
49#[derive(Clone, Copy)]
50pub(crate) struct NodeView<'a> {
51    fdt: *mut Fdt,
52    id: NodeId,
53    _marker: core::marker::PhantomData<&'a ()>, // for lifetime tracking
54}
55
56unsafe impl<'a> Send for NodeView<'a> {}
57
58impl<'a> NodeView<'a> {
59    /// Creates a new `NodeView`.
60    pub(crate) fn new(fdt: &'a Fdt, id: NodeId) -> Self {
61        Self {
62            fdt: fdt as *const Fdt as *mut Fdt,
63            id,
64            _marker: core::marker::PhantomData,
65        }
66    }
67
68    pub fn name(&self) -> &'a str {
69        self.as_node().name()
70    }
71
72    /// Returns the underlying `NodeId`.
73    pub fn id(&self) -> NodeId {
74        self.id
75    }
76
77    /// Returns a reference to the underlying `Node`.
78    pub fn as_node(&self) -> &'a Node {
79        self.fdt()
80            .node(self.id)
81            .expect("NodeView references a valid node")
82    }
83
84    pub fn as_node_mut(&mut self) -> &'a mut Node {
85        self.fdt_mut()
86            .node_mut(self.id)
87            .expect("NodeViewMut references a valid node")
88    }
89
90    /// Returns the `Fdt` arena this view belongs to.
91    pub fn fdt(&self) -> &'a Fdt {
92        unsafe { &*self.fdt }
93    }
94
95    pub fn fdt_mut(&mut self) -> &'a mut Fdt {
96        unsafe { &mut *self.fdt }
97    }
98
99    pub fn path(&self) -> String {
100        self.fdt().path_of(self.id)
101    }
102
103    pub fn parent(&self) -> Option<NodeType<'a>> {
104        self.fdt()
105            .parent_of(self.id)
106            .map(|pid| NodeView::new(self.fdt(), pid).classify())
107    }
108
109    #[allow(dead_code)]
110    pub fn parent_mut(&mut self) -> Option<NodeTypeMut<'a>> {
111        let parent = self.fdt().parent_of(self.id)?;
112        let mut parent_view = NodeView::new(self.fdt(), parent);
113        let cl = parent_view.classify_mut();
114        Some(cl)
115    }
116
117    pub fn address_cells(&self) -> Option<u32> {
118        self.as_node().address_cells()
119    }
120
121    pub fn size_cells(&self) -> Option<u32> {
122        self.as_node().size_cells()
123    }
124
125    /// Returns the effective `interrupt-parent`, inheriting from ancestors.
126    pub fn interrupt_parent(&self) -> Option<Phandle> {
127        let mut current = Some(self.id);
128
129        while let Some(node_id) = current {
130            let node = self.fdt().node(node_id)?;
131            if let Some(phandle) = node.interrupt_parent() {
132                return Some(phandle);
133            }
134            current = self.fdt().parent_of(node_id);
135        }
136
137        None
138    }
139
140    /// Parses the `clocks` property into clock references.
141    ///
142    /// Each entry starts with a provider phandle followed by a provider-defined
143    /// number of specifier cells from that node's `#clock-cells` property.
144    pub fn clocks(&self) -> Vec<ClockRef> {
145        let Some(prop) = self.as_node().get_property("clocks") else {
146            return Vec::new();
147        };
148
149        let clock_names: Vec<String> = self
150            .as_node()
151            .get_property("clock-names")
152            .map(|prop| prop.as_str_iter().map(|s| s.to_owned()).collect())
153            .unwrap_or_default();
154
155        let mut reader = prop.as_reader();
156        let mut refs = Vec::new();
157        let mut index = 0;
158
159        while let Some(phandle_raw) = reader.read_u32() {
160            let phandle = Phandle::from(phandle_raw);
161            let clock_cells = self
162                .fdt()
163                .get_by_phandle(phandle)
164                .and_then(|provider| provider.as_node().get_property("#clock-cells"))
165                .and_then(|prop| prop.get_u32())
166                .unwrap_or(1);
167
168            let mut specifier = Vec::with_capacity(clock_cells as usize);
169            let mut complete = true;
170            for _ in 0..clock_cells {
171                if let Some(value) = reader.read_u32() {
172                    specifier.push(value);
173                } else {
174                    complete = false;
175                    break;
176                }
177            }
178
179            if !complete {
180                break;
181            }
182
183            refs.push(ClockRef::with_name(
184                clock_names.get(index).cloned(),
185                phandle,
186                clock_cells,
187                specifier,
188            ));
189            index += 1;
190        }
191
192        refs
193    }
194
195    /// Parses the `interrupts` property into interrupt references.
196    ///
197    /// The specifier width is derived from the effective `interrupt-parent`
198    /// provider's `#interrupt-cells` value.
199    pub fn interrupts(&self) -> Vec<InterruptRef> {
200        let Some(prop) = self.as_node().get_property("interrupts") else {
201            return Vec::new();
202        };
203
204        let Some(interrupt_parent) = self.interrupt_parent() else {
205            return Vec::new();
206        };
207
208        let cells = self
209            .fdt()
210            .get_by_phandle(interrupt_parent)
211            .and_then(|provider| provider.as_node().get_property("#interrupt-cells"))
212            .and_then(|prop| prop.get_u32())
213            .unwrap_or(1);
214
215        if cells == 0 {
216            return Vec::new();
217        }
218
219        let interrupt_names: Vec<String> = self
220            .as_node()
221            .get_property("interrupt-names")
222            .map(|prop| prop.as_str_iter().map(|s| s.to_owned()).collect())
223            .unwrap_or_default();
224
225        let mut reader = prop.as_reader();
226        let mut refs = Vec::new();
227        let mut index = 0;
228
229        while let Some(first) = reader.read_u32() {
230            let mut specifier = Vec::with_capacity(cells as usize);
231            specifier.push(first);
232
233            let mut complete = true;
234            for _ in 1..cells {
235                if let Some(value) = reader.read_u32() {
236                    specifier.push(value);
237                } else {
238                    complete = false;
239                    break;
240                }
241            }
242
243            if !complete {
244                break;
245            }
246
247            refs.push(InterruptRef::with_name(
248                interrupt_names.get(index).cloned(),
249                interrupt_parent,
250                cells,
251                specifier,
252            ));
253            index += 1;
254        }
255
256        refs
257    }
258
259    /// Parses the `reg` property and returns corrected register entries.
260    ///
261    /// Uses parent node's `ranges` property to translate bus addresses to CPU addresses.
262    pub fn regs(&self) -> Vec<RegFixed> {
263        let node = self.as_node();
264        let reg = match node.get_property("reg") {
265            Some(p) => p,
266            None => return Vec::new(),
267        };
268
269        // Get address-cells and size-cells from parent (or default 2/1)
270        let (addr_cells, size_cells) = self.parent_cells();
271
272        // Get parent's ranges for address translation
273        let ranges = self.parent_ranges();
274
275        let mut reader = reg.as_reader();
276        let mut results = Vec::new();
277
278        while let Some(child_bus_address) = reader.read_cells(addr_cells) {
279            let size = if size_cells > 0 {
280                reader.read_cells(size_cells)
281            } else {
282                None
283            };
284
285            // Convert bus address to CPU address using ranges
286            let mut address = child_bus_address;
287            if let Some(ref ranges) = ranges {
288                for r in ranges {
289                    if child_bus_address >= r.child_bus_address
290                        && child_bus_address < r.child_bus_address + r.length
291                    {
292                        address = child_bus_address - r.child_bus_address + r.parent_bus_address;
293                        break;
294                    }
295                }
296            }
297
298            results.push(RegFixed {
299                address,
300                child_bus_address,
301                size,
302            });
303        }
304
305        results
306    }
307
308    /// Returns (address_cells, size_cells) from the parent node (defaults: 2, 1).
309    fn parent_cells(&self) -> (usize, usize) {
310        if let Some(parent) = self.parent() {
311            let ac = parent.as_view().address_cells().unwrap_or(2) as usize;
312            let sc = parent.as_view().size_cells().unwrap_or(1) as usize;
313            (ac, sc)
314        } else {
315            (2, 1)
316        }
317    }
318
319    /// Returns the parent node's ranges entries for address translation.
320    fn parent_ranges(&self) -> Option<Vec<RangesEntry>> {
321        self.parent().and_then(|p| {
322            let view = p.as_view();
323            // Get grandparent's address-cells for parsing parent_bus_address
324            let parent_addr_cells = p
325                .parent()
326                .and_then(|gp| gp.as_view().address_cells())
327                .unwrap_or(2);
328            view.as_node().ranges(parent_addr_cells)
329        })
330    }
331
332    /// Sets the `reg` property from CPU addresses.
333    ///
334    /// Converts CPU addresses to bus addresses using parent's `ranges` property
335    /// and stores them in big-endian format.
336    pub fn set_regs(&mut self, regs: &[fdt_raw::RegInfo]) {
337        // Get address-cells and size-cells from parent (or default 2/1)
338        let (addr_cells, size_cells) = self.parent_cells();
339
340        // Get parent's ranges for address translation
341        let ranges = self.parent_ranges();
342
343        let mut data = Vec::new();
344
345        for reg in regs {
346            // Convert CPU address to bus address
347            let mut bus_address = reg.address;
348            if let Some(ref ranges) = ranges {
349                for r in ranges {
350                    // Check if CPU address is within the range mapping
351                    if reg.address >= r.parent_bus_address
352                        && reg.address < r.parent_bus_address + r.length
353                    {
354                        // Reverse conversion: cpu_address -> bus_address
355                        bus_address = reg.address - r.parent_bus_address + r.child_bus_address;
356                        break;
357                    }
358                }
359            }
360
361            // Write bus address (big-endian)
362            match addr_cells {
363                1 => data.extend_from_slice(&(bus_address as u32).to_be_bytes()),
364                2 => {
365                    data.extend_from_slice(&((bus_address >> 32) as u32).to_be_bytes());
366                    data.extend_from_slice(&((bus_address & 0xFFFF_FFFF) as u32).to_be_bytes());
367                }
368                n => {
369                    // Handle arbitrary address cells
370                    for i in 0..n {
371                        let shift = (n - 1 - i) * 32;
372                        data.extend_from_slice(&(((bus_address >> shift) as u32).to_be_bytes()));
373                    }
374                }
375            }
376
377            // Write size (big-endian)
378            let size = reg.size.unwrap_or(0);
379            match size_cells {
380                1 => data.extend_from_slice(&(size as u32).to_be_bytes()),
381                2 => {
382                    data.extend_from_slice(&((size >> 32) as u32).to_be_bytes());
383                    data.extend_from_slice(&((size & 0xFFFF_FFFF) as u32).to_be_bytes());
384                }
385                n => {
386                    for i in 0..n {
387                        let shift = (n - 1 - i) * 32;
388                        data.extend_from_slice(&(((size >> shift) as u32).to_be_bytes()));
389                    }
390                }
391            }
392        }
393
394        let prop = Property::new("reg", data);
395        self.as_node_mut().set_property(prop);
396    }
397
398    pub(crate) fn classify(&self) -> NodeType<'a> {
399        if let Some(node) = ClockNodeView::try_from_view(*self) {
400            return NodeType::Clock(node);
401        }
402
403        if let Some(node) = PciNodeView::try_from_view(*self) {
404            return NodeType::Pci(node);
405        }
406
407        if let Some(node) = MemoryNodeView::try_from_view(*self) {
408            return NodeType::Memory(node);
409        }
410
411        if let Some(node) = IntcNodeView::try_from_view(*self) {
412            return NodeType::InterruptController(node);
413        }
414
415        NodeType::Generic(NodeGeneric { inner: *self })
416    }
417
418    pub(crate) fn classify_mut(&mut self) -> NodeTypeMut<'a> {
419        if let Some(node) = ClockNodeViewMut::try_from_view(*self) {
420            return NodeTypeMut::Clock(node);
421        }
422
423        if let Some(node) = PciNodeViewMut::try_from_view(*self) {
424            return NodeTypeMut::Pci(node);
425        }
426
427        if let Some(node) = MemoryNodeViewMut::try_from_view(*self) {
428            return NodeTypeMut::Memory(node);
429        }
430
431        if let Some(node) = IntcNodeViewMut::try_from_view(*self) {
432            return NodeTypeMut::InterruptController(node);
433        }
434
435        NodeTypeMut::Generic(NodeGenericMut { inner: *self })
436    }
437}
438
439impl core::fmt::Display for NodeView<'_> {
440    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
441        write!(f, "{}", self.path())?;
442        for prop in self.as_node().properties() {
443            write!(f, "\n  {} = ", prop.name())?;
444            if prop.name() == "compatible" {
445                write!(f, "[")?;
446                let strs: Vec<&str> = prop.as_str_iter().collect();
447                for (i, s) in strs.iter().enumerate() {
448                    write!(f, "\"{}\"", s)?;
449                    if i < strs.len() - 1 {
450                        write!(f, ", ")?;
451                    }
452                }
453                write!(f, "]")?;
454                continue;
455            }
456            if let Some(s) = prop.as_str() {
457                write!(f, "\"{}\";", s)?;
458            } else {
459                for cell in prop.get_u32_iter() {
460                    write!(f, "{:#x} ", cell)?;
461                }
462                write!(f, ";")?;
463            }
464        }
465        Ok(())
466    }
467}
468
469// ---------------------------------------------------------------------------
470// NodeType — classified immutable view enum
471// ---------------------------------------------------------------------------
472
473#[enum_dispatch(ViewOp)]
474#[derive(Clone, Copy)]
475/// Typed node view enum, allowing pattern matching by node kind.
476pub enum NodeType<'a> {
477    /// A clock provider node (has `#clock-cells` property).
478    Clock(ClockNodeView<'a>),
479    /// A memory node (`device_type = "memory"` or name starts with "memory").
480    Memory(MemoryNodeView<'a>),
481    /// An interrupt controller node (has the `interrupt-controller` property).
482    InterruptController(IntcNodeView<'a>),
483    /// A PCI bridge node (`device_type = "pci"`).
484    Pci(PciNodeView<'a>),
485    /// A generic node (no special classification).
486    Generic(NodeGeneric<'a>),
487}
488
489impl<'a> NodeType<'a> {
490    /// Returns the underlying `Node` reference.
491    pub fn as_node(&self) -> &'a Node {
492        self.as_view().as_node()
493    }
494
495    /// Returns the node's full path string.
496    pub fn path(&self) -> String {
497        self.as_view().path()
498    }
499
500    pub fn parent(&self) -> Option<NodeType<'a>> {
501        self.as_view().parent()
502    }
503
504    /// Returns the node's ID.
505    pub fn id(&self) -> NodeId {
506        self.as_view().id()
507    }
508
509    /// Returns the node's name.
510    pub fn name(&self) -> &'a str {
511        self.as_view().name()
512    }
513
514    /// Parses the `reg` property and returns corrected register entries.
515    pub fn regs(&self) -> Vec<RegFixed> {
516        self.as_view().regs()
517    }
518
519    /// Returns the effective `interrupt-parent`, inheriting from ancestors.
520    pub fn interrupt_parent(&self) -> Option<Phandle> {
521        self.as_view().interrupt_parent()
522    }
523
524    /// Parses the `interrupts` property into interrupt references.
525    pub fn interrupts(&self) -> Vec<InterruptRef> {
526        self.as_view().interrupts()
527    }
528
529    /// Parses the `clocks` property into clock references.
530    pub fn clocks(&self) -> Vec<ClockRef> {
531        self.as_view().clocks()
532    }
533}
534
535impl core::fmt::Display for NodeType<'_> {
536    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
537        self.display(f)
538    }
539}
540
541// ---------------------------------------------------------------------------
542// NodeTypeMut — classified mutable view enum
543// ---------------------------------------------------------------------------
544
545/// Typed mutable node view enum.
546#[enum_dispatch(ViewOp)]
547pub enum NodeTypeMut<'a> {
548    Clock(ClockNodeViewMut<'a>),
549    Memory(MemoryNodeViewMut<'a>),
550    InterruptController(IntcNodeViewMut<'a>),
551    Pci(PciNodeViewMut<'a>),
552    Generic(NodeGenericMut<'a>),
553}
554
555impl<'a> NodeTypeMut<'a> {
556    /// Returns the inner node ID regardless of variant.
557    pub fn id(&self) -> NodeId {
558        self.as_view().id()
559    }
560
561    /// Sets the `reg` property from CPU addresses.
562    ///
563    /// Converts CPU addresses to bus addresses using parent's `ranges` property
564    /// and stores them in big-endian format.
565    pub fn set_regs(&mut self, regs: &[fdt_raw::RegInfo]) {
566        self.as_view().set_regs(regs);
567    }
568}
569
570// ---------------------------------------------------------------------------
571// Fdt convenience methods returning views
572// ---------------------------------------------------------------------------
573
574impl Fdt {
575    /// Returns a `NodeView` for the given node ID, if it exists.
576    fn view(&self, id: NodeId) -> Option<NodeView<'_>> {
577        if self.node(id).is_some() {
578            Some(NodeView::new(self, id))
579        } else {
580            None
581        }
582    }
583
584    /// Returns a classified `NodeType` for the given node ID.
585    pub fn view_typed(&self, id: NodeId) -> Option<NodeType<'_>> {
586        self.view(id).map(|v| v.classify())
587    }
588
589    /// Returns a classified `NodeTypeMut` for the given node ID.
590    pub fn view_typed_mut(&mut self, id: NodeId) -> Option<NodeTypeMut<'_>> {
591        self.view(id).map(|mut v| v.classify_mut())
592    }
593}
594
595impl<'a> NodeGenericMut<'a> {
596    pub fn add_child_memory(&mut self, name: &str) -> MemoryNodeViewMut<'a> {
597        self.add_child(name)
598    }
599
600    pub fn add_child_interrupt_controller(&mut self, name: &str) -> IntcNodeViewMut<'a> {
601        self.add_child(name)
602    }
603}
604
605#[derive(Clone, Copy, Debug)]
606pub struct RegFixed {
607    pub address: u64,
608    pub child_bus_address: u64,
609    pub size: Option<u64>,
610}