fdt/
standard_nodes.rs

1// This Source Code Form is subject to the terms of the Mozilla Public License,
2// v. 2.0. If a copy of the MPL was not distributed with this file, You can
3// obtain one at https://mozilla.org/MPL/2.0/.
4
5use crate::{
6    node::{CellSizes, FdtNode, NodeProperty},
7    parsing::{BigEndianU32, BigEndianU64, CStr, FdtData},
8    Fdt,
9};
10
11/// Represents the `/chosen` node with specific helper methods
12#[derive(Debug, Clone, Copy)]
13pub struct Chosen<'b, 'a: 'b> {
14    pub(crate) node: FdtNode<'b, 'a>,
15}
16
17impl<'b, 'a: 'b> Chosen<'b, 'a> {
18    /// Contains the bootargs, if they exist
19    pub fn bootargs(self) -> Option<&'a str> {
20        self.node
21            .properties()
22            .find(|n| n.name == "bootargs")
23            .and_then(|n| core::str::from_utf8(&n.value[..n.value.len() - 1]).ok())
24    }
25
26    /// Searches for the node representing `stdout`, if the property exists,
27    /// attempting to resolve aliases if the node name doesn't exist as-is
28    pub fn stdout(self) -> Option<FdtNode<'b, 'a>> {
29        self.node
30            .properties()
31            .find(|n| n.name == "stdout-path")
32            .and_then(|n| core::str::from_utf8(&n.value[..n.value.len() - 1]).ok())
33            .and_then(|name| self.node.header.find_node(name))
34    }
35
36    /// Searches for the node representing `stdout`, if the property exists,
37    /// attempting to resolve aliases if the node name doesn't exist as-is. If
38    /// no `stdin` property exists, but `stdout` is present, it will return the
39    /// node specified by the `stdout` property.
40    pub fn stdin(self) -> Option<FdtNode<'b, 'a>> {
41        self.node
42            .properties()
43            .find(|n| n.name == "stdin-path")
44            .and_then(|n| core::str::from_utf8(&n.value[..n.value.len() - 1]).ok())
45            .and_then(|name| self.node.header.find_node(name))
46            .or_else(|| self.stdout())
47    }
48}
49
50/// Represents the root (`/`) node with specific helper methods
51#[derive(Debug, Clone, Copy)]
52pub struct Root<'b, 'a: 'b> {
53    pub(crate) node: FdtNode<'b, 'a>,
54}
55
56impl<'b, 'a: 'b> Root<'b, 'a> {
57    /// Root node cell sizes
58    pub fn cell_sizes(self) -> CellSizes {
59        self.node.cell_sizes()
60    }
61
62    /// `model` property
63    pub fn model(self) -> &'a str {
64        self.node
65            .properties()
66            .find(|p| p.name == "model")
67            .and_then(|p| core::str::from_utf8(p.value).map(|s| s.trim_end_matches('\0')).ok())
68            .unwrap()
69    }
70
71    /// `compatible` property
72    pub fn compatible(self) -> Compatible<'a> {
73        self.node.compatible().unwrap()
74    }
75
76    /// Returns an iterator over all of the available properties
77    pub fn properties(self) -> impl Iterator<Item = NodeProperty<'a>> + 'b {
78        self.node.properties()
79    }
80
81    /// Attempts to find the a property by its name
82    pub fn property(self, name: &str) -> Option<NodeProperty<'a>> {
83        self.node.properties().find(|p| p.name == name)
84    }
85}
86
87/// Represents the `/aliases` node with specific helper methods
88#[derive(Debug, Clone, Copy)]
89pub struct Aliases<'b, 'a: 'b> {
90    pub(crate) header: &'b Fdt<'a>,
91    pub(crate) node: FdtNode<'b, 'a>,
92}
93
94impl<'b, 'a: 'b> Aliases<'b, 'a> {
95    /// Attempt to resolve an alias to a node name
96    pub fn resolve(self, alias: &str) -> Option<&'a str> {
97        self.node
98            .properties()
99            .find(|p| p.name == alias)
100            .and_then(|p| core::str::from_utf8(p.value).map(|s| s.trim_end_matches('\0')).ok())
101    }
102
103    /// Attempt to find the node specified by the given alias
104    pub fn resolve_node(self, alias: &str) -> Option<FdtNode<'b, 'a>> {
105        self.resolve(alias).and_then(|name| self.header.find_node(name))
106    }
107
108    /// Returns an iterator over all of the available aliases
109    pub fn all(self) -> impl Iterator<Item = (&'a str, &'a str)> + 'b {
110        self.node.properties().filter_map(|p| {
111            Some((p.name, core::str::from_utf8(p.value).map(|s| s.trim_end_matches('\0')).ok()?))
112        })
113    }
114}
115
116/// Represents a `/cpus/cpu*` node with specific helper methods
117#[derive(Debug, Clone, Copy)]
118pub struct Cpu<'b, 'a: 'b> {
119    pub(crate) parent: FdtNode<'b, 'a>,
120    pub(crate) node: FdtNode<'b, 'a>,
121}
122
123impl<'b, 'a: 'b> Cpu<'b, 'a> {
124    /// Return the IDs for the given CPU
125    pub fn ids(self) -> CpuIds<'a> {
126        let address_cells = self.node.parent_cell_sizes().address_cells;
127
128        CpuIds {
129            reg: self
130                .node
131                .properties()
132                .find(|p| p.name == "reg")
133                .expect("reg is a required property of cpu nodes"),
134            address_cells,
135        }
136    }
137
138    /// `clock-frequency` property
139    pub fn clock_frequency(self) -> usize {
140        self.node
141            .properties()
142            .find(|p| p.name == "clock-frequency")
143            .or_else(|| self.parent.property("clock-frequency"))
144            .map(|p| match p.value.len() {
145                4 => BigEndianU32::from_bytes(p.value).unwrap().get() as usize,
146                8 => BigEndianU64::from_bytes(p.value).unwrap().get() as usize,
147                _ => unreachable!(),
148            })
149            .expect("clock-frequency is a required property of cpu nodes")
150    }
151
152    /// `timebase-frequency` property
153    pub fn timebase_frequency(self) -> usize {
154        self.node
155            .properties()
156            .find(|p| p.name == "timebase-frequency")
157            .or_else(|| self.parent.property("timebase-frequency"))
158            .map(|p| match p.value.len() {
159                4 => BigEndianU32::from_bytes(p.value).unwrap().get() as usize,
160                8 => BigEndianU64::from_bytes(p.value).unwrap().get() as usize,
161                _ => unreachable!(),
162            })
163            .expect("timebase-frequency is a required property of cpu nodes")
164    }
165
166    /// Returns an iterator over all of the properties for the CPU node
167    pub fn properties(self) -> impl Iterator<Item = NodeProperty<'a>> + 'b {
168        self.node.properties()
169    }
170
171    /// Attempts to find the a property by its name
172    pub fn property(self, name: &str) -> Option<NodeProperty<'a>> {
173        self.node.properties().find(|p| p.name == name)
174    }
175}
176
177/// Represents the value of the `reg` property of a `/cpus/cpu*` node which may
178/// contain more than one CPU or thread ID
179#[derive(Debug, Clone, Copy)]
180pub struct CpuIds<'a> {
181    pub(crate) reg: NodeProperty<'a>,
182    pub(crate) address_cells: usize,
183}
184
185impl<'a> CpuIds<'a> {
186    /// The first listed CPU ID, which will always exist
187    pub fn first(self) -> usize {
188        match self.address_cells {
189            1 => BigEndianU32::from_bytes(self.reg.value).unwrap().get() as usize,
190            2 => BigEndianU64::from_bytes(self.reg.value).unwrap().get() as usize,
191            n => panic!("address-cells of size {} is currently not supported", n),
192        }
193    }
194
195    /// Returns an iterator over all of the listed CPU IDs
196    pub fn all(self) -> impl Iterator<Item = usize> + 'a {
197        let mut vals = FdtData::new(self.reg.value);
198        core::iter::from_fn(move || match vals.remaining() {
199            [] => None,
200            _ => Some(match self.address_cells {
201                1 => vals.u32()?.get() as usize,
202                2 => vals.u64()?.get() as usize,
203                n => panic!("address-cells of size {} is currently not supported", n),
204            }),
205        })
206    }
207}
208
209/// Represents the `compatible` property of a node
210#[derive(Clone, Copy)]
211pub struct Compatible<'a> {
212    pub(crate) data: &'a [u8],
213}
214
215impl<'a> Compatible<'a> {
216    /// First compatible string
217    pub fn first(self) -> &'a str {
218        CStr::new(self.data).expect("expected C str").as_str().unwrap()
219    }
220
221    /// Returns an iterator over all available compatible strings
222    pub fn all(self) -> impl Iterator<Item = &'a str> {
223        let mut data = self.data;
224        core::iter::from_fn(move || {
225            if data.is_empty() {
226                return None;
227            }
228
229            match data.iter().position(|b| *b == b'\0') {
230                Some(idx) => {
231                    let ret = Some(core::str::from_utf8(&data[..idx]).ok()?);
232                    data = &data[idx + 1..];
233
234                    ret
235                }
236                None => {
237                    let ret = Some(core::str::from_utf8(data).ok()?);
238                    data = &[];
239
240                    ret
241                }
242            }
243        })
244    }
245}
246
247/// Represents the `/memory` node with specific helper methods
248#[derive(Debug, Clone, Copy)]
249pub struct Memory<'b, 'a: 'b> {
250    pub(crate) node: FdtNode<'b, 'a>,
251}
252
253impl Memory<'_, '_> {
254    /// Returns an iterator over all of the available memory regions
255    pub fn regions(&self) -> impl Iterator<Item = MemoryRegion> + '_ {
256        self.node.reg().unwrap()
257    }
258
259    /// Returns the initial mapped area, if it exists
260    pub fn initial_mapped_area(&self) -> Option<MappedArea> {
261        let mut mapped_area = None;
262
263        if let Some(init_mapped_area) = self.node.property("initial_mapped_area") {
264            let mut stream = FdtData::new(init_mapped_area.value);
265            let effective_address = stream.u64().expect("effective address");
266            let physical_address = stream.u64().expect("physical address");
267            let size = stream.u32().expect("size");
268
269            mapped_area = Some(MappedArea {
270                effective_address: effective_address.get() as usize,
271                physical_address: physical_address.get() as usize,
272                size: size.get() as usize,
273            });
274        }
275
276        mapped_area
277    }
278}
279
280/// An area described by the `initial-mapped-area` property of the `/memory`
281/// node
282#[derive(Debug, Clone, Copy, PartialEq)]
283#[repr(C)]
284pub struct MappedArea {
285    /// Effective address of the mapped area
286    pub effective_address: usize,
287    /// Physical address of the mapped area
288    pub physical_address: usize,
289    /// Size of the mapped area
290    pub size: usize,
291}
292
293/// A memory region
294#[derive(Debug, Clone, Copy, PartialEq)]
295pub struct MemoryRegion {
296    /// Starting address represented as a pointer
297    pub starting_address: *const u8,
298    /// Size of the memory region
299    pub size: Option<usize>,
300}