blob: 8a4e2518ce776bb82c26fa3dd0d984c91596e8e2 [file] [log] [blame]
David Brazdil1baa9a92022-06-28 14:47:50 +01001// Copyright 2022, The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Wrapper around libfdt library. Provides parsing/generating functionality
16//! to a bare-metal environment.
17
18#![no_std]
19
Andrew Walbran55ad01b2022-12-05 17:00:40 +000020mod iterators;
21
Jaewan Kimfe06c852023-10-05 23:40:06 +090022pub use iterators::{
Jaewan Kimc9e14112023-12-04 17:05:27 +090023 AddressRange, CellIterator, CompatibleIterator, DescendantsIterator, MemRegIterator,
24 PropertyIterator, RangesIterator, Reg, RegIterator, SubnodeIterator,
Jaewan Kimfe06c852023-10-05 23:40:06 +090025};
Andrew Walbran55ad01b2022-12-05 17:00:40 +000026
Jiyong Parke9d87e82023-03-21 19:28:40 +090027use core::cmp::max;
David Brazdil1baa9a92022-06-28 14:47:50 +010028use core::ffi::{c_int, c_void, CStr};
29use core::fmt;
30use core::mem;
Alice Wang2422bdc2023-06-12 08:37:55 +000031use core::ops::Range;
Jaewan Kim5b057772023-10-19 01:02:17 +090032use core::ptr;
David Brazdil1baa9a92022-06-28 14:47:50 +010033use core::result;
Pierre-Clément Tosi1bf532b2023-11-13 11:06:20 +000034use cstr::cstr;
Pierre-Clément Tosic27c4272023-05-19 15:46:26 +000035use zerocopy::AsBytes as _;
David Brazdil1baa9a92022-06-28 14:47:50 +010036
37/// Error type corresponding to libfdt error codes.
38#[derive(Clone, Copy, Debug, Eq, PartialEq)]
39pub enum FdtError {
40 /// FDT_ERR_NOTFOUND
41 NotFound,
42 /// FDT_ERR_EXISTS
43 Exists,
44 /// FDT_ERR_NOSPACE
45 NoSpace,
46 /// FDT_ERR_BADOFFSET
47 BadOffset,
48 /// FDT_ERR_BADPATH
49 BadPath,
50 /// FDT_ERR_BADPHANDLE
51 BadPhandle,
52 /// FDT_ERR_BADSTATE
53 BadState,
54 /// FDT_ERR_TRUNCATED
55 Truncated,
56 /// FDT_ERR_BADMAGIC
57 BadMagic,
58 /// FDT_ERR_BADVERSION
59 BadVersion,
60 /// FDT_ERR_BADSTRUCTURE
61 BadStructure,
62 /// FDT_ERR_BADLAYOUT
63 BadLayout,
64 /// FDT_ERR_INTERNAL
65 Internal,
66 /// FDT_ERR_BADNCELLS
67 BadNCells,
68 /// FDT_ERR_BADVALUE
69 BadValue,
70 /// FDT_ERR_BADOVERLAY
71 BadOverlay,
72 /// FDT_ERR_NOPHANDLES
73 NoPhandles,
74 /// FDT_ERR_BADFLAGS
75 BadFlags,
76 /// FDT_ERR_ALIGNMENT
77 Alignment,
78 /// Unexpected error code
79 Unknown(i32),
80}
81
82impl fmt::Display for FdtError {
83 /// Prints error messages from libfdt.h documentation.
84 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
85 match self {
86 Self::NotFound => write!(f, "The requested node or property does not exist"),
87 Self::Exists => write!(f, "Attempted to create an existing node or property"),
88 Self::NoSpace => write!(f, "Insufficient buffer space to contain the expanded tree"),
89 Self::BadOffset => write!(f, "Structure block offset is out-of-bounds or invalid"),
90 Self::BadPath => write!(f, "Badly formatted path"),
91 Self::BadPhandle => write!(f, "Invalid phandle length or value"),
92 Self::BadState => write!(f, "Received incomplete device tree"),
93 Self::Truncated => write!(f, "Device tree or sub-block is improperly terminated"),
94 Self::BadMagic => write!(f, "Device tree header missing its magic number"),
95 Self::BadVersion => write!(f, "Device tree has a version which can't be handled"),
96 Self::BadStructure => write!(f, "Device tree has a corrupt structure block"),
97 Self::BadLayout => write!(f, "Device tree sub-blocks in unsupported order"),
98 Self::Internal => write!(f, "libfdt has failed an internal assertion"),
99 Self::BadNCells => write!(f, "Bad format or value of #address-cells or #size-cells"),
100 Self::BadValue => write!(f, "Unexpected property value"),
101 Self::BadOverlay => write!(f, "Overlay cannot be applied"),
102 Self::NoPhandles => write!(f, "Device tree doesn't have any phandle available anymore"),
103 Self::BadFlags => write!(f, "Invalid flag or invalid combination of flags"),
104 Self::Alignment => write!(f, "Device tree base address is not 8-byte aligned"),
105 Self::Unknown(e) => write!(f, "Unknown libfdt error '{e}'"),
106 }
107 }
108}
109
110/// Result type with FdtError enum.
111pub type Result<T> = result::Result<T, FdtError>;
112
113fn fdt_err(val: c_int) -> Result<c_int> {
114 if val >= 0 {
115 Ok(val)
116 } else {
117 Err(match -val as _ {
118 libfdt_bindgen::FDT_ERR_NOTFOUND => FdtError::NotFound,
119 libfdt_bindgen::FDT_ERR_EXISTS => FdtError::Exists,
120 libfdt_bindgen::FDT_ERR_NOSPACE => FdtError::NoSpace,
121 libfdt_bindgen::FDT_ERR_BADOFFSET => FdtError::BadOffset,
122 libfdt_bindgen::FDT_ERR_BADPATH => FdtError::BadPath,
123 libfdt_bindgen::FDT_ERR_BADPHANDLE => FdtError::BadPhandle,
124 libfdt_bindgen::FDT_ERR_BADSTATE => FdtError::BadState,
125 libfdt_bindgen::FDT_ERR_TRUNCATED => FdtError::Truncated,
126 libfdt_bindgen::FDT_ERR_BADMAGIC => FdtError::BadMagic,
127 libfdt_bindgen::FDT_ERR_BADVERSION => FdtError::BadVersion,
128 libfdt_bindgen::FDT_ERR_BADSTRUCTURE => FdtError::BadStructure,
129 libfdt_bindgen::FDT_ERR_BADLAYOUT => FdtError::BadLayout,
130 libfdt_bindgen::FDT_ERR_INTERNAL => FdtError::Internal,
131 libfdt_bindgen::FDT_ERR_BADNCELLS => FdtError::BadNCells,
132 libfdt_bindgen::FDT_ERR_BADVALUE => FdtError::BadValue,
133 libfdt_bindgen::FDT_ERR_BADOVERLAY => FdtError::BadOverlay,
134 libfdt_bindgen::FDT_ERR_NOPHANDLES => FdtError::NoPhandles,
135 libfdt_bindgen::FDT_ERR_BADFLAGS => FdtError::BadFlags,
136 libfdt_bindgen::FDT_ERR_ALIGNMENT => FdtError::Alignment,
137 _ => FdtError::Unknown(val),
138 })
139 }
140}
141
142fn fdt_err_expect_zero(val: c_int) -> Result<()> {
143 match fdt_err(val)? {
144 0 => Ok(()),
145 _ => Err(FdtError::Unknown(val)),
146 }
147}
148
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000149fn fdt_err_or_option(val: c_int) -> Result<Option<c_int>> {
150 match fdt_err(val) {
151 Ok(val) => Ok(Some(val)),
152 Err(FdtError::NotFound) => Ok(None),
153 Err(e) => Err(e),
154 }
155}
156
David Brazdil1baa9a92022-06-28 14:47:50 +0100157/// Value of a #address-cells property.
Andrew Walbranb39e6922022-12-05 17:01:20 +0000158#[derive(Copy, Clone, Debug, Eq, PartialEq)]
David Brazdil1baa9a92022-06-28 14:47:50 +0100159enum AddrCells {
160 Single = 1,
161 Double = 2,
Andrew Walbranb39e6922022-12-05 17:01:20 +0000162 Triple = 3,
David Brazdil1baa9a92022-06-28 14:47:50 +0100163}
164
165impl TryFrom<c_int> for AddrCells {
166 type Error = FdtError;
167
168 fn try_from(res: c_int) -> Result<Self> {
169 match fdt_err(res)? {
170 x if x == Self::Single as c_int => Ok(Self::Single),
171 x if x == Self::Double as c_int => Ok(Self::Double),
Andrew Walbranb39e6922022-12-05 17:01:20 +0000172 x if x == Self::Triple as c_int => Ok(Self::Triple),
David Brazdil1baa9a92022-06-28 14:47:50 +0100173 _ => Err(FdtError::BadNCells),
174 }
175 }
176}
177
178/// Value of a #size-cells property.
Andrew Walbranb39e6922022-12-05 17:01:20 +0000179#[derive(Copy, Clone, Debug, Eq, PartialEq)]
David Brazdil1baa9a92022-06-28 14:47:50 +0100180enum SizeCells {
181 None = 0,
182 Single = 1,
183 Double = 2,
184}
185
186impl TryFrom<c_int> for SizeCells {
187 type Error = FdtError;
188
189 fn try_from(res: c_int) -> Result<Self> {
190 match fdt_err(res)? {
191 x if x == Self::None as c_int => Ok(Self::None),
192 x if x == Self::Single as c_int => Ok(Self::Single),
193 x if x == Self::Double as c_int => Ok(Self::Double),
194 _ => Err(FdtError::BadNCells),
195 }
196 }
197}
198
Jaewan Kim72d10902023-10-12 21:59:26 +0900199/// DT property wrapper to abstract endianess changes
200#[repr(transparent)]
201#[derive(Debug)]
202struct FdtPropertyStruct(libfdt_bindgen::fdt_property);
203
204impl FdtPropertyStruct {
205 fn from_offset(fdt: &Fdt, offset: c_int) -> Result<&Self> {
206 let mut len = 0;
207 let prop =
208 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
209 unsafe { libfdt_bindgen::fdt_get_property_by_offset(fdt.as_ptr(), offset, &mut len) };
210 if prop.is_null() {
211 fdt_err(len)?;
212 return Err(FdtError::Internal); // shouldn't happen.
213 }
214 // SAFETY: prop is only returned when it points to valid libfdt_bindgen.
215 Ok(unsafe { &*prop.cast::<FdtPropertyStruct>() })
216 }
217
218 fn name_offset(&self) -> c_int {
219 u32::from_be(self.0.nameoff).try_into().unwrap()
220 }
221
222 fn data_len(&self) -> usize {
223 u32::from_be(self.0.len).try_into().unwrap()
224 }
225
226 fn data_ptr(&self) -> *const c_void {
227 self.0.data.as_ptr().cast::<_>()
228 }
229}
230
231/// DT property.
232#[derive(Clone, Copy, Debug)]
233pub struct FdtProperty<'a> {
234 fdt: &'a Fdt,
235 offset: c_int,
236 property: &'a FdtPropertyStruct,
237}
238
239impl<'a> FdtProperty<'a> {
240 fn new(fdt: &'a Fdt, offset: c_int) -> Result<Self> {
241 let property = FdtPropertyStruct::from_offset(fdt, offset)?;
242 Ok(Self { fdt, offset, property })
243 }
244
245 /// Returns the property name
246 pub fn name(&self) -> Result<&'a CStr> {
247 self.fdt.string(self.property.name_offset())
248 }
249
250 /// Returns the property value
251 pub fn value(&self) -> Result<&'a [u8]> {
252 self.fdt.get_from_ptr(self.property.data_ptr(), self.property.data_len())
253 }
254
255 fn next_property(&self) -> Result<Option<Self>> {
256 let ret =
257 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
258 unsafe { libfdt_bindgen::fdt_next_property_offset(self.fdt.as_ptr(), self.offset) };
259
260 fdt_err_or_option(ret)?.map(|offset| Self::new(self.fdt, offset)).transpose()
261 }
262}
263
David Brazdil1baa9a92022-06-28 14:47:50 +0100264/// DT node.
Alice Wang9d4df702023-05-25 14:14:12 +0000265#[derive(Clone, Copy, Debug)]
David Brazdil1baa9a92022-06-28 14:47:50 +0100266pub struct FdtNode<'a> {
267 fdt: &'a Fdt,
268 offset: c_int,
269}
270
271impl<'a> FdtNode<'a> {
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900272 /// Creates immutable node from a mutable node at the same offset.
Jiyong Park9c63cd12023-03-21 17:53:07 +0900273 pub fn from_mut(other: &'a FdtNodeMut) -> Self {
274 FdtNode { fdt: other.fdt, offset: other.offset }
275 }
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900276 /// Returns parent node.
David Brazdil1baa9a92022-06-28 14:47:50 +0100277 pub fn parent(&self) -> Result<Self> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000278 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
David Brazdil1baa9a92022-06-28 14:47:50 +0100279 let ret = unsafe { libfdt_bindgen::fdt_parent_offset(self.fdt.as_ptr(), self.offset) };
280
281 Ok(Self { fdt: self.fdt, offset: fdt_err(ret)? })
282 }
283
Jaewan Kim5b057772023-10-19 01:02:17 +0900284 /// Returns supernode with depth. Note that root is at depth 0.
285 pub fn supernode_at_depth(&self, depth: usize) -> Result<Self> {
286 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
287 let ret = unsafe {
288 libfdt_bindgen::fdt_supernode_atdepth_offset(
289 self.fdt.as_ptr(),
290 self.offset,
291 depth.try_into().unwrap(),
292 ptr::null_mut(),
293 )
294 };
295
296 Ok(Self { fdt: self.fdt, offset: fdt_err(ret)? })
297 }
298
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900299 /// Returns the standard (deprecated) device_type <string> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000300 pub fn device_type(&self) -> Result<Option<&CStr>> {
Jaewan Kimb635bb02023-11-01 13:00:34 +0900301 self.getprop_str(cstr!("device_type"))
David Brazdil1baa9a92022-06-28 14:47:50 +0100302 }
303
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900304 /// Returns the standard reg <prop-encoded-array> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000305 pub fn reg(&self) -> Result<Option<RegIterator<'a>>> {
Jaewan Kimb635bb02023-11-01 13:00:34 +0900306 let reg = cstr!("reg");
David Brazdil1baa9a92022-06-28 14:47:50 +0100307
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000308 if let Some(cells) = self.getprop_cells(reg)? {
309 let parent = self.parent()?;
David Brazdil1baa9a92022-06-28 14:47:50 +0100310
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000311 let addr_cells = parent.address_cells()?;
312 let size_cells = parent.size_cells()?;
313
314 Ok(Some(RegIterator::new(cells, addr_cells, size_cells)))
315 } else {
316 Ok(None)
317 }
David Brazdil1baa9a92022-06-28 14:47:50 +0100318 }
319
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900320 /// Returns the standard ranges property.
Andrew Walbranb39e6922022-12-05 17:01:20 +0000321 pub fn ranges<A, P, S>(&self) -> Result<Option<RangesIterator<'a, A, P, S>>> {
Jaewan Kimb635bb02023-11-01 13:00:34 +0900322 let ranges = cstr!("ranges");
Andrew Walbranb39e6922022-12-05 17:01:20 +0000323 if let Some(cells) = self.getprop_cells(ranges)? {
324 let parent = self.parent()?;
325 let addr_cells = self.address_cells()?;
326 let parent_addr_cells = parent.address_cells()?;
327 let size_cells = self.size_cells()?;
328 Ok(Some(RangesIterator::<A, P, S>::new(
329 cells,
330 addr_cells,
331 parent_addr_cells,
332 size_cells,
333 )))
334 } else {
335 Ok(None)
336 }
337 }
338
Jaewan Kimaa638702023-09-19 13:34:01 +0900339 /// Returns the node name.
340 pub fn name(&self) -> Result<&'a CStr> {
341 let mut len: c_int = 0;
342 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor). On success, the
343 // function returns valid null terminating string and otherwise returned values are dropped.
344 let name = unsafe { libfdt_bindgen::fdt_get_name(self.fdt.as_ptr(), self.offset, &mut len) }
345 as *const c_void;
346 let len = usize::try_from(fdt_err(len)?).unwrap();
347 let name = self.fdt.get_from_ptr(name, len + 1)?;
348 CStr::from_bytes_with_nul(name).map_err(|_| FdtError::Internal)
349 }
350
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900351 /// Returns the value of a given <string> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000352 pub fn getprop_str(&self, name: &CStr) -> Result<Option<&CStr>> {
353 let value = if let Some(bytes) = self.getprop(name)? {
354 Some(CStr::from_bytes_with_nul(bytes).map_err(|_| FdtError::BadValue)?)
355 } else {
356 None
357 };
358 Ok(value)
David Brazdil1baa9a92022-06-28 14:47:50 +0100359 }
360
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900361 /// Returns the value of a given property as an array of cells.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000362 pub fn getprop_cells(&self, name: &CStr) -> Result<Option<CellIterator<'a>>> {
363 if let Some(cells) = self.getprop(name)? {
364 Ok(Some(CellIterator::new(cells)))
365 } else {
366 Ok(None)
367 }
David Brazdil1baa9a92022-06-28 14:47:50 +0100368 }
369
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900370 /// Returns the value of a given <u32> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000371 pub fn getprop_u32(&self, name: &CStr) -> Result<Option<u32>> {
372 let value = if let Some(bytes) = self.getprop(name)? {
373 Some(u32::from_be_bytes(bytes.try_into().map_err(|_| FdtError::BadValue)?))
374 } else {
375 None
376 };
377 Ok(value)
David Brazdil1baa9a92022-06-28 14:47:50 +0100378 }
379
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900380 /// Returns the value of a given <u64> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000381 pub fn getprop_u64(&self, name: &CStr) -> Result<Option<u64>> {
382 let value = if let Some(bytes) = self.getprop(name)? {
383 Some(u64::from_be_bytes(bytes.try_into().map_err(|_| FdtError::BadValue)?))
384 } else {
385 None
386 };
387 Ok(value)
David Brazdil1baa9a92022-06-28 14:47:50 +0100388 }
389
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900390 /// Returns the value of a given property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000391 pub fn getprop(&self, name: &CStr) -> Result<Option<&'a [u8]>> {
Jiyong Park9c63cd12023-03-21 17:53:07 +0900392 if let Some((prop, len)) = Self::getprop_internal(self.fdt, self.offset, name)? {
Jaewan Kimaa638702023-09-19 13:34:01 +0900393 Ok(Some(self.fdt.get_from_ptr(prop, len)?))
Jiyong Park9c63cd12023-03-21 17:53:07 +0900394 } else {
395 Ok(None) // property was not found
396 }
397 }
398
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900399 /// Returns the pointer and size of the property named `name`, in a node at offset `offset`, in
Jiyong Park9c63cd12023-03-21 17:53:07 +0900400 /// a device tree `fdt`. The pointer is guaranteed to be non-null, in which case error returns.
401 fn getprop_internal(
402 fdt: &'a Fdt,
403 offset: c_int,
404 name: &CStr,
405 ) -> Result<Option<(*const c_void, usize)>> {
David Brazdil1baa9a92022-06-28 14:47:50 +0100406 let mut len: i32 = 0;
Andrew Walbran84b9a232023-07-05 14:01:40 +0000407 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor) and the
David Brazdil1baa9a92022-06-28 14:47:50 +0100408 // function respects the passed number of characters.
409 let prop = unsafe {
410 libfdt_bindgen::fdt_getprop_namelen(
Jiyong Park9c63cd12023-03-21 17:53:07 +0900411 fdt.as_ptr(),
412 offset,
David Brazdil1baa9a92022-06-28 14:47:50 +0100413 name.as_ptr(),
414 // *_namelen functions don't include the trailing nul terminator in 'len'.
415 name.to_bytes().len().try_into().map_err(|_| FdtError::BadPath)?,
416 &mut len as *mut i32,
417 )
418 } as *const u8;
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000419
420 let Some(len) = fdt_err_or_option(len)? else {
421 return Ok(None); // Property was not found.
422 };
Jaewan Kimaa638702023-09-19 13:34:01 +0900423 let len = usize::try_from(len).unwrap();
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000424
David Brazdil1baa9a92022-06-28 14:47:50 +0100425 if prop.is_null() {
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000426 // We expected an error code in len but still received a valid value?!
427 return Err(FdtError::Internal);
David Brazdil1baa9a92022-06-28 14:47:50 +0100428 }
Jiyong Park9c63cd12023-03-21 17:53:07 +0900429 Ok(Some((prop.cast::<c_void>(), len)))
David Brazdil1baa9a92022-06-28 14:47:50 +0100430 }
431
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900432 /// Returns reference to the containing device tree.
David Brazdil1baa9a92022-06-28 14:47:50 +0100433 pub fn fdt(&self) -> &Fdt {
434 self.fdt
435 }
436
Alice Wang474c0ee2023-09-14 12:52:33 +0000437 /// Returns the compatible node of the given name that is next after this node.
438 pub fn next_compatible(self, compatible: &CStr) -> Result<Option<Self>> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000439 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000440 let ret = unsafe {
441 libfdt_bindgen::fdt_node_offset_by_compatible(
442 self.fdt.as_ptr(),
443 self.offset,
444 compatible.as_ptr(),
445 )
446 };
447
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000448 Ok(fdt_err_or_option(ret)?.map(|offset| Self { fdt: self.fdt, offset }))
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000449 }
450
Alice Wang474c0ee2023-09-14 12:52:33 +0000451 /// Returns the first range of `reg` in this node.
452 pub fn first_reg(&self) -> Result<Reg<u64>> {
453 self.reg()?.ok_or(FdtError::NotFound)?.next().ok_or(FdtError::NotFound)
454 }
455
David Brazdil1baa9a92022-06-28 14:47:50 +0100456 fn address_cells(&self) -> Result<AddrCells> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000457 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
David Brazdil1baa9a92022-06-28 14:47:50 +0100458 unsafe { libfdt_bindgen::fdt_address_cells(self.fdt.as_ptr(), self.offset) }
459 .try_into()
460 .map_err(|_| FdtError::Internal)
461 }
462
463 fn size_cells(&self) -> Result<SizeCells> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000464 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
David Brazdil1baa9a92022-06-28 14:47:50 +0100465 unsafe { libfdt_bindgen::fdt_size_cells(self.fdt.as_ptr(), self.offset) }
466 .try_into()
467 .map_err(|_| FdtError::Internal)
468 }
Jaewan Kimbc828d72023-09-19 15:52:08 +0900469
470 /// Returns an iterator of subnodes
Jaewan Kim4a34b0d2024-01-19 13:17:47 +0900471 pub fn subnodes(&self) -> Result<SubnodeIterator<'a>> {
Jaewan Kimbc828d72023-09-19 15:52:08 +0900472 SubnodeIterator::new(self)
473 }
474
475 fn first_subnode(&self) -> Result<Option<Self>> {
476 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
477 let ret = unsafe { libfdt_bindgen::fdt_first_subnode(self.fdt.as_ptr(), self.offset) };
478
479 Ok(fdt_err_or_option(ret)?.map(|offset| FdtNode { fdt: self.fdt, offset }))
480 }
481
482 fn next_subnode(&self) -> Result<Option<Self>> {
483 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
484 let ret = unsafe { libfdt_bindgen::fdt_next_subnode(self.fdt.as_ptr(), self.offset) };
485
486 Ok(fdt_err_or_option(ret)?.map(|offset| FdtNode { fdt: self.fdt, offset }))
487 }
Jaewan Kim72d10902023-10-12 21:59:26 +0900488
Jaewan Kimc9e14112023-12-04 17:05:27 +0900489 /// Returns an iterator of descendants
Jaewan Kim1eab7232024-01-04 09:46:16 +0900490 pub fn descendants(&self) -> DescendantsIterator<'a> {
Jaewan Kimc9e14112023-12-04 17:05:27 +0900491 DescendantsIterator::new(self)
492 }
493
494 fn next_node(&self, depth: usize) -> Result<Option<(Self, usize)>> {
495 let mut next_depth: c_int = depth.try_into().unwrap();
496 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
497 let ret = unsafe {
498 libfdt_bindgen::fdt_next_node(self.fdt.as_ptr(), self.offset, &mut next_depth)
499 };
500 let Ok(next_depth) = usize::try_from(next_depth) else {
501 return Ok(None);
502 };
503 Ok(fdt_err_or_option(ret)?.map(|offset| (FdtNode { fdt: self.fdt, offset }, next_depth)))
504 }
505
Jaewan Kim72d10902023-10-12 21:59:26 +0900506 /// Returns an iterator of properties
507 pub fn properties(&'a self) -> Result<PropertyIterator<'a>> {
508 PropertyIterator::new(self)
509 }
510
511 fn first_property(&self) -> Result<Option<FdtProperty<'a>>> {
512 let ret =
513 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
514 unsafe { libfdt_bindgen::fdt_first_property_offset(self.fdt.as_ptr(), self.offset) };
515
516 fdt_err_or_option(ret)?.map(|offset| FdtProperty::new(self.fdt, offset)).transpose()
517 }
Jaewan Kimf34f4b82023-11-03 19:38:38 +0900518
519 /// Returns the phandle
520 pub fn get_phandle(&self) -> Result<Option<Phandle>> {
521 // This rewrites the fdt_get_phandle() because it doesn't return error code.
522 if let Some(prop) = self.getprop_u32(cstr!("phandle"))? {
523 Ok(Some(prop.try_into()?))
524 } else if let Some(prop) = self.getprop_u32(cstr!("linux,phandle"))? {
525 Ok(Some(prop.try_into()?))
526 } else {
527 Ok(None)
528 }
529 }
Jaewan Kim52026012023-12-13 13:49:28 +0900530
531 /// Returns the subnode of the given name. The name doesn't need to be nul-terminated.
532 pub fn subnode(&self, name: &CStr) -> Result<Option<Self>> {
533 let offset = self.subnode_offset(name.to_bytes())?;
534 Ok(offset.map(|offset| Self { fdt: self.fdt, offset }))
535 }
536
537 /// Returns the subnode of the given name bytes
538 pub fn subnode_with_name_bytes(&self, name: &[u8]) -> Result<Option<Self>> {
539 let offset = self.subnode_offset(name)?;
540 Ok(offset.map(|offset| Self { fdt: self.fdt, offset }))
541 }
542
543 fn subnode_offset(&self, name: &[u8]) -> Result<Option<c_int>> {
544 let namelen = name.len().try_into().unwrap();
545 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
546 let ret = unsafe {
547 libfdt_bindgen::fdt_subnode_offset_namelen(
548 self.fdt.as_ptr(),
549 self.offset,
550 name.as_ptr().cast::<_>(),
551 namelen,
552 )
553 };
554 fdt_err_or_option(ret)
555 }
David Brazdil1baa9a92022-06-28 14:47:50 +0100556}
557
Pierre-Clément Tosi504b4302023-10-30 12:22:50 +0000558impl<'a> PartialEq for FdtNode<'a> {
559 fn eq(&self, other: &Self) -> bool {
560 self.fdt.as_ptr() == other.fdt.as_ptr() && self.offset == other.offset
561 }
562}
563
Jaewan Kim17ba7a32023-10-19 13:25:15 +0900564/// Phandle of a FDT node
565#[repr(transparent)]
Jaewan Kim55f438c2023-11-15 01:24:36 +0900566#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
Jaewan Kim17ba7a32023-10-19 13:25:15 +0900567pub struct Phandle(u32);
568
569impl Phandle {
Pierre-Clément Tosieba27792023-10-30 12:04:12 +0000570 /// Minimum valid value for device tree phandles.
571 pub const MIN: Self = Self(1);
572 /// Maximum valid value for device tree phandles.
573 pub const MAX: Self = Self(libfdt_bindgen::FDT_MAX_PHANDLE);
574
Jaewan Kim17ba7a32023-10-19 13:25:15 +0900575 /// Creates a new Phandle
Pierre-Clément Tosieba27792023-10-30 12:04:12 +0000576 pub const fn new(value: u32) -> Option<Self> {
577 if Self::MIN.0 <= value && value <= Self::MAX.0 {
578 Some(Self(value))
579 } else {
580 None
Jaewan Kim17ba7a32023-10-19 13:25:15 +0900581 }
Jaewan Kim17ba7a32023-10-19 13:25:15 +0900582 }
583}
584
585impl From<Phandle> for u32 {
586 fn from(phandle: Phandle) -> u32 {
587 phandle.0
588 }
589}
590
Pierre-Clément Tosieba27792023-10-30 12:04:12 +0000591impl TryFrom<u32> for Phandle {
592 type Error = FdtError;
593
594 fn try_from(value: u32) -> Result<Self> {
595 Self::new(value).ok_or(FdtError::BadPhandle)
596 }
597}
598
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000599/// Mutable FDT node.
Pierre-Clément Tosi504b4302023-10-30 12:22:50 +0000600#[derive(Debug)]
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000601pub struct FdtNodeMut<'a> {
602 fdt: &'a mut Fdt,
603 offset: c_int,
604}
605
606impl<'a> FdtNodeMut<'a> {
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900607 /// Appends a property name-value (possibly empty) pair to the given node.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000608 pub fn appendprop<T: AsRef<[u8]>>(&mut self, name: &CStr, value: &T) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000609 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000610 let ret = unsafe {
611 libfdt_bindgen::fdt_appendprop(
612 self.fdt.as_mut_ptr(),
613 self.offset,
614 name.as_ptr(),
615 value.as_ref().as_ptr().cast::<c_void>(),
616 value.as_ref().len().try_into().map_err(|_| FdtError::BadValue)?,
617 )
618 };
619
620 fdt_err_expect_zero(ret)
621 }
622
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900623 /// Appends a (address, size) pair property to the given node.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000624 pub fn appendprop_addrrange(&mut self, name: &CStr, addr: u64, size: u64) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000625 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000626 let ret = unsafe {
627 libfdt_bindgen::fdt_appendprop_addrrange(
628 self.fdt.as_mut_ptr(),
629 self.parent()?.offset,
630 self.offset,
631 name.as_ptr(),
632 addr,
633 size,
634 )
635 };
636
637 fdt_err_expect_zero(ret)
638 }
639
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900640 /// Sets a property name-value pair to the given node.
641 ///
642 /// This may create a new prop or replace existing value.
Jaewan Kimba8929b2023-01-13 11:13:29 +0900643 pub fn setprop(&mut self, name: &CStr, value: &[u8]) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000644 // SAFETY: New value size is constrained to the DT totalsize
Jaewan Kimba8929b2023-01-13 11:13:29 +0900645 // (validated by underlying libfdt).
646 let ret = unsafe {
647 libfdt_bindgen::fdt_setprop(
648 self.fdt.as_mut_ptr(),
649 self.offset,
650 name.as_ptr(),
651 value.as_ptr().cast::<c_void>(),
652 value.len().try_into().map_err(|_| FdtError::BadValue)?,
653 )
654 };
655
656 fdt_err_expect_zero(ret)
657 }
658
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900659 /// Sets the value of the given property with the given value, and ensure that the given
660 /// value has the same length as the current value length.
661 ///
662 /// This can only be used to replace existing value.
Jiyong Park9c63cd12023-03-21 17:53:07 +0900663 pub fn setprop_inplace(&mut self, name: &CStr, value: &[u8]) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000664 // SAFETY: fdt size is not altered
Jiyong Park9c63cd12023-03-21 17:53:07 +0900665 let ret = unsafe {
666 libfdt_bindgen::fdt_setprop_inplace(
667 self.fdt.as_mut_ptr(),
668 self.offset,
669 name.as_ptr(),
670 value.as_ptr().cast::<c_void>(),
671 value.len().try_into().map_err(|_| FdtError::BadValue)?,
672 )
673 };
674
675 fdt_err_expect_zero(ret)
676 }
677
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900678 /// Sets the value of the given (address, size) pair property with the given value, and
679 /// ensure that the given value has the same length as the current value length.
680 ///
681 /// This can only be used to replace existing value.
Pierre-Clément Tosic27c4272023-05-19 15:46:26 +0000682 pub fn setprop_addrrange_inplace(&mut self, name: &CStr, addr: u64, size: u64) -> Result<()> {
683 let pair = [addr.to_be(), size.to_be()];
684 self.setprop_inplace(name, pair.as_bytes())
685 }
686
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900687 /// Sets a flag-like empty property.
688 ///
689 /// This may create a new prop or replace existing value.
Pierre-Clément Tosi4ba79662023-02-13 11:22:41 +0000690 pub fn setprop_empty(&mut self, name: &CStr) -> Result<()> {
691 self.setprop(name, &[])
692 }
693
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900694 /// Deletes the given property.
Pierre-Clément Tosi4ba79662023-02-13 11:22:41 +0000695 pub fn delprop(&mut self, name: &CStr) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000696 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor) when the
Pierre-Clément Tosi4ba79662023-02-13 11:22:41 +0000697 // library locates the node's property. Removing the property may shift the offsets of
698 // other nodes and properties but the borrow checker should prevent this function from
699 // being called when FdtNode instances are in use.
700 let ret = unsafe {
701 libfdt_bindgen::fdt_delprop(self.fdt.as_mut_ptr(), self.offset, name.as_ptr())
702 };
703
704 fdt_err_expect_zero(ret)
705 }
706
Jaewan Kim4ae0e712023-10-19 14:16:17 +0900707 /// Deletes the given property effectively from DT, by setting it with FDT_NOP.
Pierre-Clément Tosibe3a97b2023-05-19 14:56:23 +0000708 pub fn nop_property(&mut self, name: &CStr) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000709 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor) when the
Pierre-Clément Tosibe3a97b2023-05-19 14:56:23 +0000710 // library locates the node's property.
711 let ret = unsafe {
712 libfdt_bindgen::fdt_nop_property(self.fdt.as_mut_ptr(), self.offset, name.as_ptr())
713 };
714
715 fdt_err_expect_zero(ret)
716 }
717
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900718 /// Trims the size of the given property to new_size.
Jiyong Park9c63cd12023-03-21 17:53:07 +0900719 pub fn trimprop(&mut self, name: &CStr, new_size: usize) -> Result<()> {
720 let (prop, len) =
721 FdtNode::getprop_internal(self.fdt, self.offset, name)?.ok_or(FdtError::NotFound)?;
722 if len == new_size {
723 return Ok(());
724 }
725 if new_size > len {
726 return Err(FdtError::NoSpace);
727 }
728
Andrew Walbran84b9a232023-07-05 14:01:40 +0000729 // SAFETY: new_size is smaller than the old size
Jiyong Park9c63cd12023-03-21 17:53:07 +0900730 let ret = unsafe {
731 libfdt_bindgen::fdt_setprop(
732 self.fdt.as_mut_ptr(),
733 self.offset,
734 name.as_ptr(),
735 prop.cast::<c_void>(),
736 new_size.try_into().map_err(|_| FdtError::BadValue)?,
737 )
738 };
739
740 fdt_err_expect_zero(ret)
741 }
742
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900743 /// Returns reference to the containing device tree.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000744 pub fn fdt(&mut self) -> &mut Fdt {
745 self.fdt
746 }
747
Jaewan Kimf72f4f22023-11-03 19:21:34 +0900748 /// Returns immutable FdtNode of this node.
749 pub fn as_node(&self) -> FdtNode {
750 FdtNode { fdt: self.fdt, offset: self.offset }
751 }
752
Jaewan Kime6363422024-01-19 14:00:00 +0900753 /// Adds new subnodes to the given node.
754 pub fn add_subnodes(&mut self, names: &[&CStr]) -> Result<()> {
755 for name in names {
756 self.add_subnode_offset(name.to_bytes())?;
757 }
758 Ok(())
759 }
760
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900761 /// Adds a new subnode to the given node and return it as a FdtNodeMut on success.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000762 pub fn add_subnode(&'a mut self, name: &CStr) -> Result<Self> {
Jaewan Kim5ab13582023-10-20 20:56:27 +0900763 let offset = self.add_subnode_offset(name.to_bytes())?;
764 Ok(Self { fdt: self.fdt, offset })
765 }
766
767 /// Adds a new subnode to the given node with name and namelen, and returns it as a FdtNodeMut
768 /// on success.
769 pub fn add_subnode_with_namelen(&'a mut self, name: &CStr, namelen: usize) -> Result<Self> {
770 let offset = { self.add_subnode_offset(&name.to_bytes()[..namelen])? };
771 Ok(Self { fdt: self.fdt, offset })
772 }
773
774 fn add_subnode_offset(&mut self, name: &[u8]) -> Result<c_int> {
775 let namelen = name.len().try_into().unwrap();
Andrew Walbran84b9a232023-07-05 14:01:40 +0000776 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000777 let ret = unsafe {
Jaewan Kim5ab13582023-10-20 20:56:27 +0900778 libfdt_bindgen::fdt_add_subnode_namelen(
779 self.fdt.as_mut_ptr(),
780 self.offset,
781 name.as_ptr().cast::<_>(),
782 namelen,
783 )
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000784 };
Jaewan Kim5ab13582023-10-20 20:56:27 +0900785 fdt_err(ret)
786 }
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000787
Jaewan Kim5f1a6032023-12-18 15:17:58 +0900788 /// Returns the first subnode of this
789 pub fn first_subnode(&'a mut self) -> Result<Option<Self>> {
790 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
791 let ret = unsafe { libfdt_bindgen::fdt_first_subnode(self.fdt.as_ptr(), self.offset) };
792
793 Ok(fdt_err_or_option(ret)?.map(|offset| Self { fdt: self.fdt, offset }))
794 }
795
796 /// Returns the next subnode that shares the same parent with this
797 pub fn next_subnode(self) -> Result<Option<Self>> {
798 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
799 let ret = unsafe { libfdt_bindgen::fdt_next_subnode(self.fdt.as_ptr(), self.offset) };
800
801 Ok(fdt_err_or_option(ret)?.map(|offset| Self { fdt: self.fdt, offset }))
802 }
803
804 /// Deletes the current node and returns the next subnode
805 pub fn delete_and_next_subnode(mut self) -> Result<Option<Self>> {
806 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
807 let ret = unsafe { libfdt_bindgen::fdt_next_subnode(self.fdt.as_ptr(), self.offset) };
808
809 let next_offset = fdt_err_or_option(ret)?;
810
811 if Some(self.offset) == next_offset {
812 return Err(FdtError::Internal);
813 }
814
815 // SAFETY: nop_self() only touches bytes of the self and its properties and subnodes, and
816 // doesn't alter any other blob in the tree. self.fdt and next_offset would remain valid.
817 unsafe { self.nop_self()? };
818
819 Ok(next_offset.map(|offset| Self { fdt: self.fdt, offset }))
820 }
821
Jaewan Kim28a13ea2024-01-04 09:22:40 +0900822 fn next_node_offset(&self, depth: usize) -> Result<Option<(c_int, usize)>> {
823 let mut next_depth: c_int = depth.try_into().or(Err(FdtError::BadValue))?;
824 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
825 let ret = unsafe {
826 libfdt_bindgen::fdt_next_node(self.fdt.as_ptr(), self.offset, &mut next_depth)
827 };
828 let Ok(next_depth) = usize::try_from(next_depth) else {
829 return Ok(None);
830 };
831 Ok(fdt_err_or_option(ret)?.map(|offset| (offset, next_depth)))
832 }
833
834 /// Returns the next node
835 pub fn next_node(self, depth: usize) -> Result<Option<(Self, usize)>> {
836 Ok(self
837 .next_node_offset(depth)?
838 .map(|(offset, next_depth)| (FdtNodeMut { fdt: self.fdt, offset }, next_depth)))
839 }
840
841 /// Deletes this and returns the next node
842 pub fn delete_and_next_node(mut self, depth: usize) -> Result<Option<(Self, usize)>> {
843 // Skip all would-be-removed descendants.
844 let mut iter = self.next_node_offset(depth)?;
845 while let Some((descendant_offset, descendant_depth)) = iter {
846 if descendant_depth <= depth {
847 break;
848 }
849 let descendant = FdtNodeMut { fdt: self.fdt, offset: descendant_offset };
850 iter = descendant.next_node_offset(descendant_depth)?;
851 }
852 // SAFETY: This consumes self, so invalid node wouldn't be used any further
853 unsafe { self.nop_self()? };
854 Ok(iter.map(|(offset, next_depth)| (FdtNodeMut { fdt: self.fdt, offset }, next_depth)))
855 }
856
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000857 fn parent(&'a self) -> Result<FdtNode<'a>> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000858 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000859 let ret = unsafe { libfdt_bindgen::fdt_parent_offset(self.fdt.as_ptr(), self.offset) };
860
861 Ok(FdtNode { fdt: &*self.fdt, offset: fdt_err(ret)? })
862 }
Jiyong Park9c63cd12023-03-21 17:53:07 +0900863
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900864 /// Returns the compatible node of the given name that is next after this node.
Jiyong Park9c63cd12023-03-21 17:53:07 +0900865 pub fn next_compatible(self, compatible: &CStr) -> Result<Option<Self>> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000866 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
Jiyong Park9c63cd12023-03-21 17:53:07 +0900867 let ret = unsafe {
868 libfdt_bindgen::fdt_node_offset_by_compatible(
869 self.fdt.as_ptr(),
870 self.offset,
871 compatible.as_ptr(),
872 )
873 };
874
875 Ok(fdt_err_or_option(ret)?.map(|offset| Self { fdt: self.fdt, offset }))
876 }
877
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900878 /// Deletes the node effectively by overwriting this node and its subtree with nop tags.
879 /// Returns the next compatible node of the given name.
Jiyong Park9c63cd12023-03-21 17:53:07 +0900880 // Side note: without this, filterint out excessive compatible nodes from the DT is impossible.
881 // The reason is that libfdt ensures that the node from where the search for the next
882 // compatible node is started is always a valid one -- except for the special case of offset =
883 // -1 which is to find the first compatible node. So, we can't delete a node and then find the
884 // next compatible node from it.
885 //
886 // We can't do in the opposite direction either. If we call next_compatible to find the next
887 // node, and delete the current node, the Rust borrow checker kicks in. The next node has a
888 // mutable reference to DT, so we can't use current node (which also has a mutable reference to
889 // DT).
Jaewan Kim4ae0e712023-10-19 14:16:17 +0900890 pub fn delete_and_next_compatible(mut self, compatible: &CStr) -> Result<Option<Self>> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000891 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
Jiyong Park9c63cd12023-03-21 17:53:07 +0900892 let ret = unsafe {
893 libfdt_bindgen::fdt_node_offset_by_compatible(
894 self.fdt.as_ptr(),
895 self.offset,
896 compatible.as_ptr(),
897 )
898 };
899 let next_offset = fdt_err_or_option(ret)?;
900
Jaewan Kim4ae0e712023-10-19 14:16:17 +0900901 if Some(self.offset) == next_offset {
902 return Err(FdtError::Internal);
903 }
904
905 // SAFETY: nop_self() only touches bytes of the self and its properties and subnodes, and
906 // doesn't alter any other blob in the tree. self.fdt and next_offset would remain valid.
907 unsafe { self.nop_self()? };
Jiyong Park9c63cd12023-03-21 17:53:07 +0900908
909 Ok(next_offset.map(|offset| Self { fdt: self.fdt, offset }))
910 }
Jaewan Kim4ae0e712023-10-19 14:16:17 +0900911
912 /// Deletes this node effectively from DT, by setting it with FDT_NOP
913 pub fn nop(mut self) -> Result<()> {
914 // SAFETY: This consumes self, so invalid node wouldn't be used any further
915 unsafe { self.nop_self() }
916 }
917
918 /// Deletes this node effectively from DT, by setting it with FDT_NOP.
919 /// This only changes bytes of the node and its properties and subnodes, and doesn't alter or
920 /// move any other part of the tree.
921 /// SAFETY: This node is no longer valid.
922 unsafe fn nop_self(&mut self) -> Result<()> {
923 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
924 let ret = unsafe { libfdt_bindgen::fdt_nop_node(self.fdt.as_mut_ptr(), self.offset) };
925
926 fdt_err_expect_zero(ret)
927 }
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000928}
929
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000930/// Wrapper around low-level libfdt functions.
Alice Wang9d4df702023-05-25 14:14:12 +0000931#[derive(Debug)]
David Brazdil1baa9a92022-06-28 14:47:50 +0100932#[repr(transparent)]
933pub struct Fdt {
Pierre-Clément Tosief2030e2022-11-28 11:21:20 +0000934 buffer: [u8],
David Brazdil1baa9a92022-06-28 14:47:50 +0100935}
936
937impl Fdt {
938 /// Wraps a slice containing a Flattened Device Tree.
939 ///
940 /// Fails if the FDT does not pass validation.
941 pub fn from_slice(fdt: &[u8]) -> Result<&Self> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000942 // SAFETY: The FDT will be validated before it is returned.
David Brazdil1baa9a92022-06-28 14:47:50 +0100943 let fdt = unsafe { Self::unchecked_from_slice(fdt) };
944 fdt.check_full()?;
945 Ok(fdt)
946 }
947
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000948 /// Wraps a mutable slice containing a Flattened Device Tree.
949 ///
950 /// Fails if the FDT does not pass validation.
951 pub fn from_mut_slice(fdt: &mut [u8]) -> Result<&mut Self> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000952 // SAFETY: The FDT will be validated before it is returned.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000953 let fdt = unsafe { Self::unchecked_from_mut_slice(fdt) };
954 fdt.check_full()?;
955 Ok(fdt)
956 }
957
Jaewan Kim4cf20aa2023-04-03 10:25:38 +0900958 /// Creates an empty Flattened Device Tree with a mutable slice.
959 pub fn create_empty_tree(fdt: &mut [u8]) -> Result<&mut Self> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000960 // SAFETY: fdt_create_empty_tree() only write within the specified length,
Jaewan Kim4cf20aa2023-04-03 10:25:38 +0900961 // and returns error if buffer was insufficient.
962 // There will be no memory write outside of the given fdt.
963 let ret = unsafe {
964 libfdt_bindgen::fdt_create_empty_tree(
965 fdt.as_mut_ptr().cast::<c_void>(),
966 fdt.len() as i32,
967 )
968 };
969 fdt_err_expect_zero(ret)?;
970
Andrew Walbran84b9a232023-07-05 14:01:40 +0000971 // SAFETY: The FDT will be validated before it is returned.
Jaewan Kim4cf20aa2023-04-03 10:25:38 +0900972 let fdt = unsafe { Self::unchecked_from_mut_slice(fdt) };
973 fdt.check_full()?;
974
975 Ok(fdt)
976 }
977
David Brazdil1baa9a92022-06-28 14:47:50 +0100978 /// Wraps a slice containing a Flattened Device Tree.
979 ///
980 /// # Safety
981 ///
982 /// The returned FDT might be invalid, only use on slices containing a valid DT.
983 pub unsafe fn unchecked_from_slice(fdt: &[u8]) -> &Self {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000984 // SAFETY: Fdt is a wrapper around a [u8], so the transmute is valid. The caller is
985 // responsible for ensuring that it is actually a valid FDT.
986 unsafe { mem::transmute::<&[u8], &Self>(fdt) }
David Brazdil1baa9a92022-06-28 14:47:50 +0100987 }
988
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000989 /// Wraps a mutable slice containing a Flattened Device Tree.
990 ///
991 /// # Safety
992 ///
993 /// The returned FDT might be invalid, only use on slices containing a valid DT.
994 pub unsafe fn unchecked_from_mut_slice(fdt: &mut [u8]) -> &mut Self {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000995 // SAFETY: Fdt is a wrapper around a [u8], so the transmute is valid. The caller is
996 // responsible for ensuring that it is actually a valid FDT.
997 unsafe { mem::transmute::<&mut [u8], &mut Self>(fdt) }
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000998 }
999
Jaewan Kimb3dcfc22023-09-20 10:20:52 +09001000 /// Updates this FDT from a slice containing another FDT.
Jiyong Parke9d87e82023-03-21 19:28:40 +09001001 pub fn copy_from_slice(&mut self, new_fdt: &[u8]) -> Result<()> {
1002 if self.buffer.len() < new_fdt.len() {
1003 Err(FdtError::NoSpace)
1004 } else {
1005 let totalsize = self.totalsize();
1006 self.buffer[..new_fdt.len()].clone_from_slice(new_fdt);
1007 // Zeroize the remaining part. We zeroize up to the size of the original DT because
1008 // zeroizing the entire buffer (max 2MB) is not necessary and may increase the VM boot
1009 // time.
1010 self.buffer[new_fdt.len()..max(new_fdt.len(), totalsize)].fill(0_u8);
1011 Ok(())
1012 }
1013 }
1014
Jaewan Kimb3dcfc22023-09-20 10:20:52 +09001015 /// Unpacks the DT to cover the whole slice it is contained in.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +00001016 pub fn unpack(&mut self) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +00001017 // SAFETY: "Opens" the DT in-place (supported use-case) by updating its header and
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +00001018 // internal structures to make use of the whole self.fdt slice but performs no accesses
1019 // outside of it and leaves the DT in a state that will be detected by other functions.
1020 let ret = unsafe {
1021 libfdt_bindgen::fdt_open_into(
1022 self.as_ptr(),
1023 self.as_mut_ptr(),
1024 self.capacity().try_into().map_err(|_| FdtError::Internal)?,
1025 )
1026 };
1027 fdt_err_expect_zero(ret)
1028 }
1029
Jaewan Kimb3dcfc22023-09-20 10:20:52 +09001030 /// Packs the DT to take a minimum amount of memory.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +00001031 ///
1032 /// Doesn't shrink the underlying memory slice.
1033 pub fn pack(&mut self) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +00001034 // SAFETY: "Closes" the DT in-place by updating its header and relocating its structs.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +00001035 let ret = unsafe { libfdt_bindgen::fdt_pack(self.as_mut_ptr()) };
1036 fdt_err_expect_zero(ret)
1037 }
1038
Pierre-Clément Tosi90e19352022-11-21 17:11:48 +00001039 /// Applies a DT overlay on the base DT.
1040 ///
1041 /// # Safety
1042 ///
1043 /// On failure, the library corrupts the DT and overlay so both must be discarded.
1044 pub unsafe fn apply_overlay<'a>(&'a mut self, overlay: &'a mut Fdt) -> Result<&'a mut Self> {
Andrew Walbran84b9a232023-07-05 14:01:40 +00001045 let ret =
1046 // SAFETY: Both pointers are valid because they come from references, and fdt_overlay_apply
1047 // doesn't keep them after it returns. It may corrupt their contents if there is an error,
1048 // but that's our caller's responsibility.
1049 unsafe { libfdt_bindgen::fdt_overlay_apply(self.as_mut_ptr(), overlay.as_mut_ptr()) };
1050 fdt_err_expect_zero(ret)?;
Pierre-Clément Tosi90e19352022-11-21 17:11:48 +00001051 Ok(self)
1052 }
1053
Alice Wang2422bdc2023-06-12 08:37:55 +00001054 /// Returns an iterator of memory banks specified the "/memory" node.
1055 /// Throws an error when the "/memory" is not found in the device tree.
David Brazdil1baa9a92022-06-28 14:47:50 +01001056 ///
1057 /// NOTE: This does not support individual "/memory@XXXX" banks.
Alice Wang2422bdc2023-06-12 08:37:55 +00001058 pub fn memory(&self) -> Result<MemRegIterator> {
Jaewan Kimb635bb02023-11-01 13:00:34 +09001059 let memory_node_name = cstr!("/memory");
1060 let memory_device_type = cstr!("memory");
David Brazdil1baa9a92022-06-28 14:47:50 +01001061
Alice Wang2422bdc2023-06-12 08:37:55 +00001062 let node = self.node(memory_node_name)?.ok_or(FdtError::NotFound)?;
1063 if node.device_type()? != Some(memory_device_type) {
1064 return Err(FdtError::BadValue);
Pierre-Clément Tosib244d932022-11-24 16:45:53 +00001065 }
Alice Wang2422bdc2023-06-12 08:37:55 +00001066 node.reg()?.ok_or(FdtError::BadValue).map(MemRegIterator::new)
1067 }
1068
1069 /// Returns the first memory range in the `/memory` node.
1070 pub fn first_memory_range(&self) -> Result<Range<usize>> {
1071 self.memory()?.next().ok_or(FdtError::NotFound)
David Brazdil1baa9a92022-06-28 14:47:50 +01001072 }
1073
Jaewan Kimb3dcfc22023-09-20 10:20:52 +09001074 /// Returns the standard /chosen node.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +00001075 pub fn chosen(&self) -> Result<Option<FdtNode>> {
Jaewan Kimb635bb02023-11-01 13:00:34 +09001076 self.node(cstr!("/chosen"))
David Brazdil1baa9a92022-06-28 14:47:50 +01001077 }
1078
Jaewan Kimb3dcfc22023-09-20 10:20:52 +09001079 /// Returns the standard /chosen node as mutable.
Pierre-Clément Tosi4ba79662023-02-13 11:22:41 +00001080 pub fn chosen_mut(&mut self) -> Result<Option<FdtNodeMut>> {
Jaewan Kimb635bb02023-11-01 13:00:34 +09001081 self.node_mut(cstr!("/chosen"))
Pierre-Clément Tosi4ba79662023-02-13 11:22:41 +00001082 }
1083
Jaewan Kimb3dcfc22023-09-20 10:20:52 +09001084 /// Returns the root node of the tree.
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +00001085 pub fn root(&self) -> Result<FdtNode> {
Jaewan Kimb635bb02023-11-01 13:00:34 +09001086 self.node(cstr!("/"))?.ok_or(FdtError::Internal)
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +00001087 }
1088
Jaewan Kimf163d762023-11-01 13:12:50 +09001089 /// Returns the standard /__symbols__ node.
1090 pub fn symbols(&self) -> Result<Option<FdtNode>> {
1091 self.node(cstr!("/__symbols__"))
1092 }
1093
1094 /// Returns the standard /__symbols__ node as mutable
1095 pub fn symbols_mut(&mut self) -> Result<Option<FdtNodeMut>> {
1096 self.node_mut(cstr!("/__symbols__"))
1097 }
1098
Jaewan Kimb3dcfc22023-09-20 10:20:52 +09001099 /// Returns a tree node by its full path.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +00001100 pub fn node(&self, path: &CStr) -> Result<Option<FdtNode>> {
Jaewan Kimbab42592023-10-13 15:47:19 +09001101 Ok(self.path_offset(path.to_bytes())?.map(|offset| FdtNode { fdt: self, offset }))
David Brazdil1baa9a92022-06-28 14:47:50 +01001102 }
1103
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +00001104 /// Iterate over nodes with a given compatible string.
1105 pub fn compatible_nodes<'a>(&'a self, compatible: &'a CStr) -> Result<CompatibleIterator<'a>> {
1106 CompatibleIterator::new(self, compatible)
1107 }
1108
Jaewan Kim17ba7a32023-10-19 13:25:15 +09001109 /// Returns max phandle in the tree.
1110 pub fn max_phandle(&self) -> Result<Phandle> {
1111 let mut phandle: u32 = 0;
1112 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
1113 let ret = unsafe { libfdt_bindgen::fdt_find_max_phandle(self.as_ptr(), &mut phandle) };
1114
1115 fdt_err_expect_zero(ret)?;
Pierre-Clément Tosieba27792023-10-30 12:04:12 +00001116 phandle.try_into()
Jaewan Kim17ba7a32023-10-19 13:25:15 +09001117 }
1118
1119 /// Returns a node with the phandle
1120 pub fn node_with_phandle(&self, phandle: Phandle) -> Result<Option<FdtNode>> {
Jaewan Kimc63246d2023-11-09 15:41:01 +09001121 let offset = self.node_offset_with_phandle(phandle)?;
1122 Ok(offset.map(|offset| FdtNode { fdt: self, offset }))
1123 }
1124
1125 /// Returns a mutable node with the phandle
1126 pub fn node_mut_with_phandle(&mut self, phandle: Phandle) -> Result<Option<FdtNodeMut>> {
1127 let offset = self.node_offset_with_phandle(phandle)?;
1128 Ok(offset.map(|offset| FdtNodeMut { fdt: self, offset }))
1129 }
1130
1131 fn node_offset_with_phandle(&self, phandle: Phandle) -> Result<Option<c_int>> {
1132 // SAFETY: Accesses are constrained to the DT totalsize.
Jaewan Kim17ba7a32023-10-19 13:25:15 +09001133 let ret = unsafe { libfdt_bindgen::fdt_node_offset_by_phandle(self.as_ptr(), phandle.0) };
Jaewan Kimc63246d2023-11-09 15:41:01 +09001134 fdt_err_or_option(ret)
Jaewan Kim17ba7a32023-10-19 13:25:15 +09001135 }
1136
Jaewan Kimb3dcfc22023-09-20 10:20:52 +09001137 /// Returns the mutable root node of the tree.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +00001138 pub fn root_mut(&mut self) -> Result<FdtNodeMut> {
Jaewan Kimb635bb02023-11-01 13:00:34 +09001139 self.node_mut(cstr!("/"))?.ok_or(FdtError::Internal)
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +00001140 }
1141
Jaewan Kimb3dcfc22023-09-20 10:20:52 +09001142 /// Returns a mutable tree node by its full path.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +00001143 pub fn node_mut(&mut self, path: &CStr) -> Result<Option<FdtNodeMut>> {
Jaewan Kimbab42592023-10-13 15:47:19 +09001144 Ok(self.path_offset(path.to_bytes())?.map(|offset| FdtNodeMut { fdt: self, offset }))
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +00001145 }
1146
Jaewan Kimb3dcfc22023-09-20 10:20:52 +09001147 /// Returns the device tree as a slice (may be smaller than the containing buffer).
Pierre-Clément Tosidb74cb12022-12-08 13:56:25 +00001148 pub fn as_slice(&self) -> &[u8] {
1149 &self.buffer[..self.totalsize()]
1150 }
1151
Jaewan Kimbab42592023-10-13 15:47:19 +09001152 fn path_offset(&self, path: &[u8]) -> Result<Option<c_int>> {
1153 let len = path.len().try_into().map_err(|_| FdtError::BadPath)?;
Andrew Walbran84b9a232023-07-05 14:01:40 +00001154 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor) and the
David Brazdil1baa9a92022-06-28 14:47:50 +01001155 // function respects the passed number of characters.
1156 let ret = unsafe {
1157 // *_namelen functions don't include the trailing nul terminator in 'len'.
Jaewan Kimbab42592023-10-13 15:47:19 +09001158 libfdt_bindgen::fdt_path_offset_namelen(self.as_ptr(), path.as_ptr().cast::<_>(), len)
David Brazdil1baa9a92022-06-28 14:47:50 +01001159 };
1160
Pierre-Clément Tosib244d932022-11-24 16:45:53 +00001161 fdt_err_or_option(ret)
David Brazdil1baa9a92022-06-28 14:47:50 +01001162 }
1163
1164 fn check_full(&self) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +00001165 // SAFETY: Only performs read accesses within the limits of the slice. If successful, this
David Brazdil1baa9a92022-06-28 14:47:50 +01001166 // call guarantees to other unsafe calls that the header contains a valid totalsize (w.r.t.
1167 // 'len' i.e. the self.fdt slice) that those C functions can use to perform bounds
1168 // checking. The library doesn't maintain an internal state (such as pointers) between
1169 // calls as it expects the client code to keep track of the objects (DT, nodes, ...).
Pierre-Clément Tosi02017da2023-09-26 17:57:04 +01001170 let ret = unsafe { libfdt_bindgen::fdt_check_full(self.as_ptr(), self.capacity()) };
David Brazdil1baa9a92022-06-28 14:47:50 +01001171 fdt_err_expect_zero(ret)
1172 }
1173
Jaewan Kimaa638702023-09-19 13:34:01 +09001174 fn get_from_ptr(&self, ptr: *const c_void, len: usize) -> Result<&[u8]> {
1175 let ptr = ptr as usize;
1176 let offset = ptr.checked_sub(self.as_ptr() as usize).ok_or(FdtError::Internal)?;
1177 self.buffer.get(offset..(offset + len)).ok_or(FdtError::Internal)
1178 }
1179
Jaewan Kim72d10902023-10-12 21:59:26 +09001180 fn string(&self, offset: c_int) -> Result<&CStr> {
1181 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
1182 let res = unsafe { libfdt_bindgen::fdt_string(self.as_ptr(), offset) };
1183 if res.is_null() {
1184 return Err(FdtError::Internal);
1185 }
1186
1187 // SAFETY: Non-null return from fdt_string() is valid null-terminating string within FDT.
1188 Ok(unsafe { CStr::from_ptr(res) })
1189 }
1190
Jaewan Kimb3dcfc22023-09-20 10:20:52 +09001191 /// Returns a shared pointer to the device tree.
Pierre-Clément Tosi8036b4f2023-02-17 10:31:31 +00001192 pub fn as_ptr(&self) -> *const c_void {
Pierre-Clément Tosi0dcc75e2023-05-02 13:43:55 +00001193 self.buffer.as_ptr().cast::<_>()
David Brazdil1baa9a92022-06-28 14:47:50 +01001194 }
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +00001195
1196 fn as_mut_ptr(&mut self) -> *mut c_void {
Pierre-Clément Tosi0dcc75e2023-05-02 13:43:55 +00001197 self.buffer.as_mut_ptr().cast::<_>()
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +00001198 }
1199
1200 fn capacity(&self) -> usize {
Pierre-Clément Tosief2030e2022-11-28 11:21:20 +00001201 self.buffer.len()
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +00001202 }
Pierre-Clément Tosidb74cb12022-12-08 13:56:25 +00001203
1204 fn header(&self) -> &libfdt_bindgen::fdt_header {
Pierre-Clément Tosi0dcc75e2023-05-02 13:43:55 +00001205 let p = self.as_ptr().cast::<_>();
Andrew Walbran84b9a232023-07-05 14:01:40 +00001206 // SAFETY: A valid FDT (verified by constructor) must contain a valid fdt_header.
Pierre-Clément Tosi0dcc75e2023-05-02 13:43:55 +00001207 unsafe { &*p }
Pierre-Clément Tosidb74cb12022-12-08 13:56:25 +00001208 }
1209
1210 fn totalsize(&self) -> usize {
1211 u32::from_be(self.header().totalsize) as usize
1212 }
David Brazdil1baa9a92022-06-28 14:47:50 +01001213}