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