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};
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 `reg` property and returns corrected register entries.
196    ///
197    /// Uses parent node's `ranges` property to translate bus addresses to CPU addresses.
198    pub fn regs(&self) -> Vec<RegFixed> {
199        let node = self.as_node();
200        let reg = match node.get_property("reg") {
201            Some(p) => p,
202            None => return Vec::new(),
203        };
204
205        // Get address-cells and size-cells from parent (or default 2/1)
206        let (addr_cells, size_cells) = self.parent_cells();
207
208        // Get parent's ranges for address translation
209        let ranges = self.parent_ranges();
210
211        let mut reader = reg.as_reader();
212        let mut results = Vec::new();
213
214        while let Some(child_bus_address) = reader.read_cells(addr_cells) {
215            let size = if size_cells > 0 {
216                reader.read_cells(size_cells)
217            } else {
218                None
219            };
220
221            // Convert bus address to CPU address using ranges
222            let mut address = child_bus_address;
223            if let Some(ref ranges) = ranges {
224                for r in ranges {
225                    if child_bus_address >= r.child_bus_address
226                        && child_bus_address < r.child_bus_address + r.length
227                    {
228                        address = child_bus_address - r.child_bus_address + r.parent_bus_address;
229                        break;
230                    }
231                }
232            }
233
234            results.push(RegFixed {
235                address,
236                child_bus_address,
237                size,
238            });
239        }
240
241        results
242    }
243
244    /// Returns (address_cells, size_cells) from the parent node (defaults: 2, 1).
245    fn parent_cells(&self) -> (usize, usize) {
246        if let Some(parent) = self.parent() {
247            let ac = parent.as_view().address_cells().unwrap_or(2) as usize;
248            let sc = parent.as_view().size_cells().unwrap_or(1) as usize;
249            (ac, sc)
250        } else {
251            (2, 1)
252        }
253    }
254
255    /// Returns the parent node's ranges entries for address translation.
256    fn parent_ranges(&self) -> Option<Vec<RangesEntry>> {
257        self.parent().and_then(|p| {
258            let view = p.as_view();
259            // Get grandparent's address-cells for parsing parent_bus_address
260            let parent_addr_cells = p
261                .parent()
262                .and_then(|gp| gp.as_view().address_cells())
263                .unwrap_or(2);
264            view.as_node().ranges(parent_addr_cells)
265        })
266    }
267
268    /// Sets the `reg` property from CPU addresses.
269    ///
270    /// Converts CPU addresses to bus addresses using parent's `ranges` property
271    /// and stores them in big-endian format.
272    pub fn set_regs(&mut self, regs: &[fdt_raw::RegInfo]) {
273        // Get address-cells and size-cells from parent (or default 2/1)
274        let (addr_cells, size_cells) = self.parent_cells();
275
276        // Get parent's ranges for address translation
277        let ranges = self.parent_ranges();
278
279        let mut data = Vec::new();
280
281        for reg in regs {
282            // Convert CPU address to bus address
283            let mut bus_address = reg.address;
284            if let Some(ref ranges) = ranges {
285                for r in ranges {
286                    // Check if CPU address is within the range mapping
287                    if reg.address >= r.parent_bus_address
288                        && reg.address < r.parent_bus_address + r.length
289                    {
290                        // Reverse conversion: cpu_address -> bus_address
291                        bus_address = reg.address - r.parent_bus_address + r.child_bus_address;
292                        break;
293                    }
294                }
295            }
296
297            // Write bus address (big-endian)
298            match addr_cells {
299                1 => data.extend_from_slice(&(bus_address as u32).to_be_bytes()),
300                2 => {
301                    data.extend_from_slice(&((bus_address >> 32) as u32).to_be_bytes());
302                    data.extend_from_slice(&((bus_address & 0xFFFF_FFFF) as u32).to_be_bytes());
303                }
304                n => {
305                    // Handle arbitrary address cells
306                    for i in 0..n {
307                        let shift = (n - 1 - i) * 32;
308                        data.extend_from_slice(&(((bus_address >> shift) as u32).to_be_bytes()));
309                    }
310                }
311            }
312
313            // Write size (big-endian)
314            let size = reg.size.unwrap_or(0);
315            match size_cells {
316                1 => data.extend_from_slice(&(size as u32).to_be_bytes()),
317                2 => {
318                    data.extend_from_slice(&((size >> 32) as u32).to_be_bytes());
319                    data.extend_from_slice(&((size & 0xFFFF_FFFF) as u32).to_be_bytes());
320                }
321                n => {
322                    for i in 0..n {
323                        let shift = (n - 1 - i) * 32;
324                        data.extend_from_slice(&(((size >> shift) as u32).to_be_bytes()));
325                    }
326                }
327            }
328        }
329
330        let prop = Property::new("reg", data);
331        self.as_node_mut().set_property(prop);
332    }
333
334    pub(crate) fn classify(&self) -> NodeType<'a> {
335        if let Some(node) = ClockNodeView::try_from_view(*self) {
336            return NodeType::Clock(node);
337        }
338
339        if let Some(node) = PciNodeView::try_from_view(*self) {
340            return NodeType::Pci(node);
341        }
342
343        if let Some(node) = MemoryNodeView::try_from_view(*self) {
344            return NodeType::Memory(node);
345        }
346
347        if let Some(node) = IntcNodeView::try_from_view(*self) {
348            return NodeType::InterruptController(node);
349        }
350
351        NodeType::Generic(NodeGeneric { inner: *self })
352    }
353
354    pub(crate) fn classify_mut(&mut self) -> NodeTypeMut<'a> {
355        if let Some(node) = ClockNodeViewMut::try_from_view(*self) {
356            return NodeTypeMut::Clock(node);
357        }
358
359        if let Some(node) = PciNodeViewMut::try_from_view(*self) {
360            return NodeTypeMut::Pci(node);
361        }
362
363        if let Some(node) = MemoryNodeViewMut::try_from_view(*self) {
364            return NodeTypeMut::Memory(node);
365        }
366
367        if let Some(node) = IntcNodeViewMut::try_from_view(*self) {
368            return NodeTypeMut::InterruptController(node);
369        }
370
371        NodeTypeMut::Generic(NodeGenericMut { inner: *self })
372    }
373}
374
375impl core::fmt::Display for NodeView<'_> {
376    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
377        write!(f, "{}", self.path())?;
378        for prop in self.as_node().properties() {
379            write!(f, "\n  {} = ", prop.name())?;
380            if prop.name() == "compatible" {
381                write!(f, "[")?;
382                let strs: Vec<&str> = prop.as_str_iter().collect();
383                for (i, s) in strs.iter().enumerate() {
384                    write!(f, "\"{}\"", s)?;
385                    if i < strs.len() - 1 {
386                        write!(f, ", ")?;
387                    }
388                }
389                write!(f, "]")?;
390                continue;
391            }
392            if let Some(s) = prop.as_str() {
393                write!(f, "\"{}\";", s)?;
394            } else {
395                for cell in prop.get_u32_iter() {
396                    write!(f, "{:#x} ", cell)?;
397                }
398                write!(f, ";")?;
399            }
400        }
401        Ok(())
402    }
403}
404
405// ---------------------------------------------------------------------------
406// NodeType — classified immutable view enum
407// ---------------------------------------------------------------------------
408
409#[enum_dispatch(ViewOp)]
410/// Typed node view enum, allowing pattern matching by node kind.
411pub enum NodeType<'a> {
412    /// A clock provider node (has `#clock-cells` property).
413    Clock(ClockNodeView<'a>),
414    /// A memory node (`device_type = "memory"` or name starts with "memory").
415    Memory(MemoryNodeView<'a>),
416    /// An interrupt controller node (has the `interrupt-controller` property).
417    InterruptController(IntcNodeView<'a>),
418    /// A PCI bridge node (`device_type = "pci"`).
419    Pci(PciNodeView<'a>),
420    /// A generic node (no special classification).
421    Generic(NodeGeneric<'a>),
422}
423
424impl<'a> NodeType<'a> {
425    /// Returns the underlying `Node` reference.
426    pub fn as_node(&self) -> &'a Node {
427        self.as_view().as_node()
428    }
429
430    /// Returns the node's full path string.
431    pub fn path(&self) -> String {
432        self.as_view().path()
433    }
434
435    pub fn parent(&self) -> Option<NodeType<'a>> {
436        self.as_view().parent()
437    }
438
439    /// Returns the node's ID.
440    pub fn id(&self) -> NodeId {
441        self.as_view().id()
442    }
443
444    /// Returns the node's name.
445    pub fn name(&self) -> &'a str {
446        self.as_view().name()
447    }
448
449    /// Parses the `reg` property and returns corrected register entries.
450    pub fn regs(&self) -> Vec<RegFixed> {
451        self.as_view().regs()
452    }
453
454    /// Returns the effective `interrupt-parent`, inheriting from ancestors.
455    pub fn interrupt_parent(&self) -> Option<Phandle> {
456        self.as_view().interrupt_parent()
457    }
458
459    /// Parses the `clocks` property into clock references.
460    pub fn clocks(&self) -> Vec<ClockRef> {
461        self.as_view().clocks()
462    }
463}
464
465impl core::fmt::Display for NodeType<'_> {
466    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
467        self.display(f)
468    }
469}
470
471// ---------------------------------------------------------------------------
472// NodeTypeMut — classified mutable view enum
473// ---------------------------------------------------------------------------
474
475/// Typed mutable node view enum.
476#[enum_dispatch(ViewOp)]
477pub enum NodeTypeMut<'a> {
478    Clock(ClockNodeViewMut<'a>),
479    Memory(MemoryNodeViewMut<'a>),
480    InterruptController(IntcNodeViewMut<'a>),
481    Pci(PciNodeViewMut<'a>),
482    Generic(NodeGenericMut<'a>),
483}
484
485impl<'a> NodeTypeMut<'a> {
486    /// Returns the inner node ID regardless of variant.
487    pub fn id(&self) -> NodeId {
488        self.as_view().id()
489    }
490
491    /// Sets the `reg` property from CPU addresses.
492    ///
493    /// Converts CPU addresses to bus addresses using parent's `ranges` property
494    /// and stores them in big-endian format.
495    pub fn set_regs(&mut self, regs: &[fdt_raw::RegInfo]) {
496        self.as_view().set_regs(regs);
497    }
498}
499
500// ---------------------------------------------------------------------------
501// Fdt convenience methods returning views
502// ---------------------------------------------------------------------------
503
504impl Fdt {
505    /// Returns a `NodeView` for the given node ID, if it exists.
506    fn view(&self, id: NodeId) -> Option<NodeView<'_>> {
507        if self.node(id).is_some() {
508            Some(NodeView::new(self, id))
509        } else {
510            None
511        }
512    }
513
514    /// Returns a classified `NodeType` for the given node ID.
515    pub fn view_typed(&self, id: NodeId) -> Option<NodeType<'_>> {
516        self.view(id).map(|v| v.classify())
517    }
518
519    /// Returns a classified `NodeTypeMut` for the given node ID.
520    pub fn view_typed_mut(&mut self, id: NodeId) -> Option<NodeTypeMut<'_>> {
521        self.view(id).map(|mut v| v.classify_mut())
522    }
523}
524
525impl<'a> NodeGenericMut<'a> {
526    pub fn add_child_memory(&mut self, name: &str) -> MemoryNodeViewMut<'a> {
527        self.add_child(name)
528    }
529
530    pub fn add_child_interrupt_controller(&mut self, name: &str) -> IntcNodeViewMut<'a> {
531        self.add_child(name)
532    }
533}
534
535#[derive(Clone, Copy, Debug)]
536pub struct RegFixed {
537    pub address: u64,
538    pub child_bus_address: u64,
539    pub size: Option<u64>,
540}