blob: 96ac3f4afb3b83065befe3a99817273ce2f92773 [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 Kim72d10902023-10-12 21:59:26 +090023 AddressRange, CellIterator, CompatibleIterator, MemRegIterator, PropertyIterator,
24 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 Tosic27c4272023-05-19 15:46:26 +000034use zerocopy::AsBytes as _;
David Brazdil1baa9a92022-06-28 14:47:50 +010035
36/// Error type corresponding to libfdt error codes.
37#[derive(Clone, Copy, Debug, Eq, PartialEq)]
38pub enum FdtError {
39 /// FDT_ERR_NOTFOUND
40 NotFound,
41 /// FDT_ERR_EXISTS
42 Exists,
43 /// FDT_ERR_NOSPACE
44 NoSpace,
45 /// FDT_ERR_BADOFFSET
46 BadOffset,
47 /// FDT_ERR_BADPATH
48 BadPath,
49 /// FDT_ERR_BADPHANDLE
50 BadPhandle,
51 /// FDT_ERR_BADSTATE
52 BadState,
53 /// FDT_ERR_TRUNCATED
54 Truncated,
55 /// FDT_ERR_BADMAGIC
56 BadMagic,
57 /// FDT_ERR_BADVERSION
58 BadVersion,
59 /// FDT_ERR_BADSTRUCTURE
60 BadStructure,
61 /// FDT_ERR_BADLAYOUT
62 BadLayout,
63 /// FDT_ERR_INTERNAL
64 Internal,
65 /// FDT_ERR_BADNCELLS
66 BadNCells,
67 /// FDT_ERR_BADVALUE
68 BadValue,
69 /// FDT_ERR_BADOVERLAY
70 BadOverlay,
71 /// FDT_ERR_NOPHANDLES
72 NoPhandles,
73 /// FDT_ERR_BADFLAGS
74 BadFlags,
75 /// FDT_ERR_ALIGNMENT
76 Alignment,
77 /// Unexpected error code
78 Unknown(i32),
79}
80
81impl fmt::Display for FdtError {
82 /// Prints error messages from libfdt.h documentation.
83 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
84 match self {
85 Self::NotFound => write!(f, "The requested node or property does not exist"),
86 Self::Exists => write!(f, "Attempted to create an existing node or property"),
87 Self::NoSpace => write!(f, "Insufficient buffer space to contain the expanded tree"),
88 Self::BadOffset => write!(f, "Structure block offset is out-of-bounds or invalid"),
89 Self::BadPath => write!(f, "Badly formatted path"),
90 Self::BadPhandle => write!(f, "Invalid phandle length or value"),
91 Self::BadState => write!(f, "Received incomplete device tree"),
92 Self::Truncated => write!(f, "Device tree or sub-block is improperly terminated"),
93 Self::BadMagic => write!(f, "Device tree header missing its magic number"),
94 Self::BadVersion => write!(f, "Device tree has a version which can't be handled"),
95 Self::BadStructure => write!(f, "Device tree has a corrupt structure block"),
96 Self::BadLayout => write!(f, "Device tree sub-blocks in unsupported order"),
97 Self::Internal => write!(f, "libfdt has failed an internal assertion"),
98 Self::BadNCells => write!(f, "Bad format or value of #address-cells or #size-cells"),
99 Self::BadValue => write!(f, "Unexpected property value"),
100 Self::BadOverlay => write!(f, "Overlay cannot be applied"),
101 Self::NoPhandles => write!(f, "Device tree doesn't have any phandle available anymore"),
102 Self::BadFlags => write!(f, "Invalid flag or invalid combination of flags"),
103 Self::Alignment => write!(f, "Device tree base address is not 8-byte aligned"),
104 Self::Unknown(e) => write!(f, "Unknown libfdt error '{e}'"),
105 }
106 }
107}
108
109/// Result type with FdtError enum.
110pub type Result<T> = result::Result<T, FdtError>;
111
112fn fdt_err(val: c_int) -> Result<c_int> {
113 if val >= 0 {
114 Ok(val)
115 } else {
116 Err(match -val as _ {
117 libfdt_bindgen::FDT_ERR_NOTFOUND => FdtError::NotFound,
118 libfdt_bindgen::FDT_ERR_EXISTS => FdtError::Exists,
119 libfdt_bindgen::FDT_ERR_NOSPACE => FdtError::NoSpace,
120 libfdt_bindgen::FDT_ERR_BADOFFSET => FdtError::BadOffset,
121 libfdt_bindgen::FDT_ERR_BADPATH => FdtError::BadPath,
122 libfdt_bindgen::FDT_ERR_BADPHANDLE => FdtError::BadPhandle,
123 libfdt_bindgen::FDT_ERR_BADSTATE => FdtError::BadState,
124 libfdt_bindgen::FDT_ERR_TRUNCATED => FdtError::Truncated,
125 libfdt_bindgen::FDT_ERR_BADMAGIC => FdtError::BadMagic,
126 libfdt_bindgen::FDT_ERR_BADVERSION => FdtError::BadVersion,
127 libfdt_bindgen::FDT_ERR_BADSTRUCTURE => FdtError::BadStructure,
128 libfdt_bindgen::FDT_ERR_BADLAYOUT => FdtError::BadLayout,
129 libfdt_bindgen::FDT_ERR_INTERNAL => FdtError::Internal,
130 libfdt_bindgen::FDT_ERR_BADNCELLS => FdtError::BadNCells,
131 libfdt_bindgen::FDT_ERR_BADVALUE => FdtError::BadValue,
132 libfdt_bindgen::FDT_ERR_BADOVERLAY => FdtError::BadOverlay,
133 libfdt_bindgen::FDT_ERR_NOPHANDLES => FdtError::NoPhandles,
134 libfdt_bindgen::FDT_ERR_BADFLAGS => FdtError::BadFlags,
135 libfdt_bindgen::FDT_ERR_ALIGNMENT => FdtError::Alignment,
136 _ => FdtError::Unknown(val),
137 })
138 }
139}
140
141fn fdt_err_expect_zero(val: c_int) -> Result<()> {
142 match fdt_err(val)? {
143 0 => Ok(()),
144 _ => Err(FdtError::Unknown(val)),
145 }
146}
147
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000148fn fdt_err_or_option(val: c_int) -> Result<Option<c_int>> {
149 match fdt_err(val) {
150 Ok(val) => Ok(Some(val)),
151 Err(FdtError::NotFound) => Ok(None),
152 Err(e) => Err(e),
153 }
154}
155
David Brazdil1baa9a92022-06-28 14:47:50 +0100156/// Value of a #address-cells property.
Andrew Walbranb39e6922022-12-05 17:01:20 +0000157#[derive(Copy, Clone, Debug, Eq, PartialEq)]
David Brazdil1baa9a92022-06-28 14:47:50 +0100158enum AddrCells {
159 Single = 1,
160 Double = 2,
Andrew Walbranb39e6922022-12-05 17:01:20 +0000161 Triple = 3,
David Brazdil1baa9a92022-06-28 14:47:50 +0100162}
163
164impl TryFrom<c_int> for AddrCells {
165 type Error = FdtError;
166
167 fn try_from(res: c_int) -> Result<Self> {
168 match fdt_err(res)? {
169 x if x == Self::Single as c_int => Ok(Self::Single),
170 x if x == Self::Double as c_int => Ok(Self::Double),
Andrew Walbranb39e6922022-12-05 17:01:20 +0000171 x if x == Self::Triple as c_int => Ok(Self::Triple),
David Brazdil1baa9a92022-06-28 14:47:50 +0100172 _ => Err(FdtError::BadNCells),
173 }
174 }
175}
176
177/// Value of a #size-cells property.
Andrew Walbranb39e6922022-12-05 17:01:20 +0000178#[derive(Copy, Clone, Debug, Eq, PartialEq)]
David Brazdil1baa9a92022-06-28 14:47:50 +0100179enum SizeCells {
180 None = 0,
181 Single = 1,
182 Double = 2,
183}
184
185impl TryFrom<c_int> for SizeCells {
186 type Error = FdtError;
187
188 fn try_from(res: c_int) -> Result<Self> {
189 match fdt_err(res)? {
190 x if x == Self::None as c_int => Ok(Self::None),
191 x if x == Self::Single as c_int => Ok(Self::Single),
192 x if x == Self::Double as c_int => Ok(Self::Double),
193 _ => Err(FdtError::BadNCells),
194 }
195 }
196}
197
Jaewan Kim72d10902023-10-12 21:59:26 +0900198/// DT property wrapper to abstract endianess changes
199#[repr(transparent)]
200#[derive(Debug)]
201struct FdtPropertyStruct(libfdt_bindgen::fdt_property);
202
203impl FdtPropertyStruct {
204 fn from_offset(fdt: &Fdt, offset: c_int) -> Result<&Self> {
205 let mut len = 0;
206 let prop =
207 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
208 unsafe { libfdt_bindgen::fdt_get_property_by_offset(fdt.as_ptr(), offset, &mut len) };
209 if prop.is_null() {
210 fdt_err(len)?;
211 return Err(FdtError::Internal); // shouldn't happen.
212 }
213 // SAFETY: prop is only returned when it points to valid libfdt_bindgen.
214 Ok(unsafe { &*prop.cast::<FdtPropertyStruct>() })
215 }
216
217 fn name_offset(&self) -> c_int {
218 u32::from_be(self.0.nameoff).try_into().unwrap()
219 }
220
221 fn data_len(&self) -> usize {
222 u32::from_be(self.0.len).try_into().unwrap()
223 }
224
225 fn data_ptr(&self) -> *const c_void {
226 self.0.data.as_ptr().cast::<_>()
227 }
228}
229
230/// DT property.
231#[derive(Clone, Copy, Debug)]
232pub struct FdtProperty<'a> {
233 fdt: &'a Fdt,
234 offset: c_int,
235 property: &'a FdtPropertyStruct,
236}
237
238impl<'a> FdtProperty<'a> {
239 fn new(fdt: &'a Fdt, offset: c_int) -> Result<Self> {
240 let property = FdtPropertyStruct::from_offset(fdt, offset)?;
241 Ok(Self { fdt, offset, property })
242 }
243
244 /// Returns the property name
245 pub fn name(&self) -> Result<&'a CStr> {
246 self.fdt.string(self.property.name_offset())
247 }
248
249 /// Returns the property value
250 pub fn value(&self) -> Result<&'a [u8]> {
251 self.fdt.get_from_ptr(self.property.data_ptr(), self.property.data_len())
252 }
253
254 fn next_property(&self) -> Result<Option<Self>> {
255 let ret =
256 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
257 unsafe { libfdt_bindgen::fdt_next_property_offset(self.fdt.as_ptr(), self.offset) };
258
259 fdt_err_or_option(ret)?.map(|offset| Self::new(self.fdt, offset)).transpose()
260 }
261}
262
David Brazdil1baa9a92022-06-28 14:47:50 +0100263/// DT node.
Alice Wang9d4df702023-05-25 14:14:12 +0000264#[derive(Clone, Copy, Debug)]
David Brazdil1baa9a92022-06-28 14:47:50 +0100265pub struct FdtNode<'a> {
266 fdt: &'a Fdt,
267 offset: c_int,
268}
269
270impl<'a> FdtNode<'a> {
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900271 /// Creates immutable node from a mutable node at the same offset.
Jiyong Park9c63cd12023-03-21 17:53:07 +0900272 pub fn from_mut(other: &'a FdtNodeMut) -> Self {
273 FdtNode { fdt: other.fdt, offset: other.offset }
274 }
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900275 /// Returns parent node.
David Brazdil1baa9a92022-06-28 14:47:50 +0100276 pub fn parent(&self) -> Result<Self> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000277 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
David Brazdil1baa9a92022-06-28 14:47:50 +0100278 let ret = unsafe { libfdt_bindgen::fdt_parent_offset(self.fdt.as_ptr(), self.offset) };
279
280 Ok(Self { fdt: self.fdt, offset: fdt_err(ret)? })
281 }
282
Jaewan Kim5b057772023-10-19 01:02:17 +0900283 /// Returns supernode with depth. Note that root is at depth 0.
284 pub fn supernode_at_depth(&self, depth: usize) -> Result<Self> {
285 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
286 let ret = unsafe {
287 libfdt_bindgen::fdt_supernode_atdepth_offset(
288 self.fdt.as_ptr(),
289 self.offset,
290 depth.try_into().unwrap(),
291 ptr::null_mut(),
292 )
293 };
294
295 Ok(Self { fdt: self.fdt, offset: fdt_err(ret)? })
296 }
297
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900298 /// Returns the standard (deprecated) device_type <string> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000299 pub fn device_type(&self) -> Result<Option<&CStr>> {
David Brazdil1baa9a92022-06-28 14:47:50 +0100300 self.getprop_str(CStr::from_bytes_with_nul(b"device_type\0").unwrap())
301 }
302
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900303 /// Returns the standard reg <prop-encoded-array> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000304 pub fn reg(&self) -> Result<Option<RegIterator<'a>>> {
305 let reg = CStr::from_bytes_with_nul(b"reg\0").unwrap();
David Brazdil1baa9a92022-06-28 14:47:50 +0100306
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000307 if let Some(cells) = self.getprop_cells(reg)? {
308 let parent = self.parent()?;
David Brazdil1baa9a92022-06-28 14:47:50 +0100309
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000310 let addr_cells = parent.address_cells()?;
311 let size_cells = parent.size_cells()?;
312
313 Ok(Some(RegIterator::new(cells, addr_cells, size_cells)))
314 } else {
315 Ok(None)
316 }
David Brazdil1baa9a92022-06-28 14:47:50 +0100317 }
318
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900319 /// Returns the standard ranges property.
Andrew Walbranb39e6922022-12-05 17:01:20 +0000320 pub fn ranges<A, P, S>(&self) -> Result<Option<RangesIterator<'a, A, P, S>>> {
321 let ranges = CStr::from_bytes_with_nul(b"ranges\0").unwrap();
322 if let Some(cells) = self.getprop_cells(ranges)? {
323 let parent = self.parent()?;
324 let addr_cells = self.address_cells()?;
325 let parent_addr_cells = parent.address_cells()?;
326 let size_cells = self.size_cells()?;
327 Ok(Some(RangesIterator::<A, P, S>::new(
328 cells,
329 addr_cells,
330 parent_addr_cells,
331 size_cells,
332 )))
333 } else {
334 Ok(None)
335 }
336 }
337
Jaewan Kimaa638702023-09-19 13:34:01 +0900338 /// Returns the node name.
339 pub fn name(&self) -> Result<&'a CStr> {
340 let mut len: c_int = 0;
341 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor). On success, the
342 // function returns valid null terminating string and otherwise returned values are dropped.
343 let name = unsafe { libfdt_bindgen::fdt_get_name(self.fdt.as_ptr(), self.offset, &mut len) }
344 as *const c_void;
345 let len = usize::try_from(fdt_err(len)?).unwrap();
346 let name = self.fdt.get_from_ptr(name, len + 1)?;
347 CStr::from_bytes_with_nul(name).map_err(|_| FdtError::Internal)
348 }
349
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900350 /// Returns the value of a given <string> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000351 pub fn getprop_str(&self, name: &CStr) -> Result<Option<&CStr>> {
352 let value = if let Some(bytes) = self.getprop(name)? {
353 Some(CStr::from_bytes_with_nul(bytes).map_err(|_| FdtError::BadValue)?)
354 } else {
355 None
356 };
357 Ok(value)
David Brazdil1baa9a92022-06-28 14:47:50 +0100358 }
359
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900360 /// Returns the value of a given property as an array of cells.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000361 pub fn getprop_cells(&self, name: &CStr) -> Result<Option<CellIterator<'a>>> {
362 if let Some(cells) = self.getprop(name)? {
363 Ok(Some(CellIterator::new(cells)))
364 } else {
365 Ok(None)
366 }
David Brazdil1baa9a92022-06-28 14:47:50 +0100367 }
368
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900369 /// Returns the value of a given <u32> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000370 pub fn getprop_u32(&self, name: &CStr) -> Result<Option<u32>> {
371 let value = if let Some(bytes) = self.getprop(name)? {
372 Some(u32::from_be_bytes(bytes.try_into().map_err(|_| FdtError::BadValue)?))
373 } else {
374 None
375 };
376 Ok(value)
David Brazdil1baa9a92022-06-28 14:47:50 +0100377 }
378
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900379 /// Returns the value of a given <u64> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000380 pub fn getprop_u64(&self, name: &CStr) -> Result<Option<u64>> {
381 let value = if let Some(bytes) = self.getprop(name)? {
382 Some(u64::from_be_bytes(bytes.try_into().map_err(|_| FdtError::BadValue)?))
383 } else {
384 None
385 };
386 Ok(value)
David Brazdil1baa9a92022-06-28 14:47:50 +0100387 }
388
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900389 /// Returns the value of a given property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000390 pub fn getprop(&self, name: &CStr) -> Result<Option<&'a [u8]>> {
Jiyong Park9c63cd12023-03-21 17:53:07 +0900391 if let Some((prop, len)) = Self::getprop_internal(self.fdt, self.offset, name)? {
Jaewan Kimaa638702023-09-19 13:34:01 +0900392 Ok(Some(self.fdt.get_from_ptr(prop, len)?))
Jiyong Park9c63cd12023-03-21 17:53:07 +0900393 } else {
394 Ok(None) // property was not found
395 }
396 }
397
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900398 /// Returns the pointer and size of the property named `name`, in a node at offset `offset`, in
Jiyong Park9c63cd12023-03-21 17:53:07 +0900399 /// a device tree `fdt`. The pointer is guaranteed to be non-null, in which case error returns.
400 fn getprop_internal(
401 fdt: &'a Fdt,
402 offset: c_int,
403 name: &CStr,
404 ) -> Result<Option<(*const c_void, usize)>> {
David Brazdil1baa9a92022-06-28 14:47:50 +0100405 let mut len: i32 = 0;
Andrew Walbran84b9a232023-07-05 14:01:40 +0000406 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor) and the
David Brazdil1baa9a92022-06-28 14:47:50 +0100407 // function respects the passed number of characters.
408 let prop = unsafe {
409 libfdt_bindgen::fdt_getprop_namelen(
Jiyong Park9c63cd12023-03-21 17:53:07 +0900410 fdt.as_ptr(),
411 offset,
David Brazdil1baa9a92022-06-28 14:47:50 +0100412 name.as_ptr(),
413 // *_namelen functions don't include the trailing nul terminator in 'len'.
414 name.to_bytes().len().try_into().map_err(|_| FdtError::BadPath)?,
415 &mut len as *mut i32,
416 )
417 } as *const u8;
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000418
419 let Some(len) = fdt_err_or_option(len)? else {
420 return Ok(None); // Property was not found.
421 };
Jaewan Kimaa638702023-09-19 13:34:01 +0900422 let len = usize::try_from(len).unwrap();
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000423
David Brazdil1baa9a92022-06-28 14:47:50 +0100424 if prop.is_null() {
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000425 // We expected an error code in len but still received a valid value?!
426 return Err(FdtError::Internal);
David Brazdil1baa9a92022-06-28 14:47:50 +0100427 }
Jiyong Park9c63cd12023-03-21 17:53:07 +0900428 Ok(Some((prop.cast::<c_void>(), len)))
David Brazdil1baa9a92022-06-28 14:47:50 +0100429 }
430
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900431 /// Returns reference to the containing device tree.
David Brazdil1baa9a92022-06-28 14:47:50 +0100432 pub fn fdt(&self) -> &Fdt {
433 self.fdt
434 }
435
Alice Wang474c0ee2023-09-14 12:52:33 +0000436 /// Returns the compatible node of the given name that is next after this node.
437 pub fn next_compatible(self, compatible: &CStr) -> Result<Option<Self>> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000438 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000439 let ret = unsafe {
440 libfdt_bindgen::fdt_node_offset_by_compatible(
441 self.fdt.as_ptr(),
442 self.offset,
443 compatible.as_ptr(),
444 )
445 };
446
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000447 Ok(fdt_err_or_option(ret)?.map(|offset| Self { fdt: self.fdt, offset }))
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000448 }
449
Alice Wang474c0ee2023-09-14 12:52:33 +0000450 /// Returns the first range of `reg` in this node.
451 pub fn first_reg(&self) -> Result<Reg<u64>> {
452 self.reg()?.ok_or(FdtError::NotFound)?.next().ok_or(FdtError::NotFound)
453 }
454
David Brazdil1baa9a92022-06-28 14:47:50 +0100455 fn address_cells(&self) -> Result<AddrCells> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000456 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
David Brazdil1baa9a92022-06-28 14:47:50 +0100457 unsafe { libfdt_bindgen::fdt_address_cells(self.fdt.as_ptr(), self.offset) }
458 .try_into()
459 .map_err(|_| FdtError::Internal)
460 }
461
462 fn size_cells(&self) -> Result<SizeCells> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000463 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
David Brazdil1baa9a92022-06-28 14:47:50 +0100464 unsafe { libfdt_bindgen::fdt_size_cells(self.fdt.as_ptr(), self.offset) }
465 .try_into()
466 .map_err(|_| FdtError::Internal)
467 }
Jaewan Kimbc828d72023-09-19 15:52:08 +0900468
469 /// Returns an iterator of subnodes
470 pub fn subnodes(&'a self) -> Result<SubnodeIterator<'a>> {
471 SubnodeIterator::new(self)
472 }
473
474 fn first_subnode(&self) -> Result<Option<Self>> {
475 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
476 let ret = unsafe { libfdt_bindgen::fdt_first_subnode(self.fdt.as_ptr(), self.offset) };
477
478 Ok(fdt_err_or_option(ret)?.map(|offset| FdtNode { fdt: self.fdt, offset }))
479 }
480
481 fn next_subnode(&self) -> Result<Option<Self>> {
482 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
483 let ret = unsafe { libfdt_bindgen::fdt_next_subnode(self.fdt.as_ptr(), self.offset) };
484
485 Ok(fdt_err_or_option(ret)?.map(|offset| FdtNode { fdt: self.fdt, offset }))
486 }
Jaewan Kim72d10902023-10-12 21:59:26 +0900487
488 /// Returns an iterator of properties
489 pub fn properties(&'a self) -> Result<PropertyIterator<'a>> {
490 PropertyIterator::new(self)
491 }
492
493 fn first_property(&self) -> Result<Option<FdtProperty<'a>>> {
494 let ret =
495 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
496 unsafe { libfdt_bindgen::fdt_first_property_offset(self.fdt.as_ptr(), self.offset) };
497
498 fdt_err_or_option(ret)?.map(|offset| FdtProperty::new(self.fdt, offset)).transpose()
499 }
David Brazdil1baa9a92022-06-28 14:47:50 +0100500}
501
Pierre-Clément Tosi504b4302023-10-30 12:22:50 +0000502impl<'a> PartialEq for FdtNode<'a> {
503 fn eq(&self, other: &Self) -> bool {
504 self.fdt.as_ptr() == other.fdt.as_ptr() && self.offset == other.offset
505 }
506}
507
Jaewan Kim17ba7a32023-10-19 13:25:15 +0900508/// Phandle of a FDT node
509#[repr(transparent)]
510#[derive(Debug, Copy, Clone, PartialEq)]
511pub struct Phandle(u32);
512
513impl Phandle {
Pierre-Clément Tosieba27792023-10-30 12:04:12 +0000514 /// Minimum valid value for device tree phandles.
515 pub const MIN: Self = Self(1);
516 /// Maximum valid value for device tree phandles.
517 pub const MAX: Self = Self(libfdt_bindgen::FDT_MAX_PHANDLE);
518
Jaewan Kim17ba7a32023-10-19 13:25:15 +0900519 /// Creates a new Phandle
Pierre-Clément Tosieba27792023-10-30 12:04:12 +0000520 pub const fn new(value: u32) -> Option<Self> {
521 if Self::MIN.0 <= value && value <= Self::MAX.0 {
522 Some(Self(value))
523 } else {
524 None
Jaewan Kim17ba7a32023-10-19 13:25:15 +0900525 }
Jaewan Kim17ba7a32023-10-19 13:25:15 +0900526 }
527}
528
529impl From<Phandle> for u32 {
530 fn from(phandle: Phandle) -> u32 {
531 phandle.0
532 }
533}
534
Pierre-Clément Tosieba27792023-10-30 12:04:12 +0000535impl TryFrom<u32> for Phandle {
536 type Error = FdtError;
537
538 fn try_from(value: u32) -> Result<Self> {
539 Self::new(value).ok_or(FdtError::BadPhandle)
540 }
541}
542
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000543/// Mutable FDT node.
Pierre-Clément Tosi504b4302023-10-30 12:22:50 +0000544#[derive(Debug)]
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000545pub struct FdtNodeMut<'a> {
546 fdt: &'a mut Fdt,
547 offset: c_int,
548}
549
550impl<'a> FdtNodeMut<'a> {
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900551 /// Appends a property name-value (possibly empty) pair to the given node.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000552 pub fn appendprop<T: AsRef<[u8]>>(&mut self, name: &CStr, value: &T) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000553 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000554 let ret = unsafe {
555 libfdt_bindgen::fdt_appendprop(
556 self.fdt.as_mut_ptr(),
557 self.offset,
558 name.as_ptr(),
559 value.as_ref().as_ptr().cast::<c_void>(),
560 value.as_ref().len().try_into().map_err(|_| FdtError::BadValue)?,
561 )
562 };
563
564 fdt_err_expect_zero(ret)
565 }
566
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900567 /// Appends a (address, size) pair property to the given node.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000568 pub fn appendprop_addrrange(&mut self, name: &CStr, addr: u64, size: u64) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000569 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000570 let ret = unsafe {
571 libfdt_bindgen::fdt_appendprop_addrrange(
572 self.fdt.as_mut_ptr(),
573 self.parent()?.offset,
574 self.offset,
575 name.as_ptr(),
576 addr,
577 size,
578 )
579 };
580
581 fdt_err_expect_zero(ret)
582 }
583
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900584 /// Sets a property name-value pair to the given node.
585 ///
586 /// This may create a new prop or replace existing value.
Jaewan Kimba8929b2023-01-13 11:13:29 +0900587 pub fn setprop(&mut self, name: &CStr, value: &[u8]) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000588 // SAFETY: New value size is constrained to the DT totalsize
Jaewan Kimba8929b2023-01-13 11:13:29 +0900589 // (validated by underlying libfdt).
590 let ret = unsafe {
591 libfdt_bindgen::fdt_setprop(
592 self.fdt.as_mut_ptr(),
593 self.offset,
594 name.as_ptr(),
595 value.as_ptr().cast::<c_void>(),
596 value.len().try_into().map_err(|_| FdtError::BadValue)?,
597 )
598 };
599
600 fdt_err_expect_zero(ret)
601 }
602
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900603 /// Sets the value of the given property with the given value, and ensure that the given
604 /// value has the same length as the current value length.
605 ///
606 /// This can only be used to replace existing value.
Jiyong Park9c63cd12023-03-21 17:53:07 +0900607 pub fn setprop_inplace(&mut self, name: &CStr, value: &[u8]) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000608 // SAFETY: fdt size is not altered
Jiyong Park9c63cd12023-03-21 17:53:07 +0900609 let ret = unsafe {
610 libfdt_bindgen::fdt_setprop_inplace(
611 self.fdt.as_mut_ptr(),
612 self.offset,
613 name.as_ptr(),
614 value.as_ptr().cast::<c_void>(),
615 value.len().try_into().map_err(|_| FdtError::BadValue)?,
616 )
617 };
618
619 fdt_err_expect_zero(ret)
620 }
621
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900622 /// Sets the value of the given (address, size) pair property with the given value, and
623 /// ensure that the given value has the same length as the current value length.
624 ///
625 /// This can only be used to replace existing value.
Pierre-Clément Tosic27c4272023-05-19 15:46:26 +0000626 pub fn setprop_addrrange_inplace(&mut self, name: &CStr, addr: u64, size: u64) -> Result<()> {
627 let pair = [addr.to_be(), size.to_be()];
628 self.setprop_inplace(name, pair.as_bytes())
629 }
630
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900631 /// Sets a flag-like empty property.
632 ///
633 /// This may create a new prop or replace existing value.
Pierre-Clément Tosi4ba79662023-02-13 11:22:41 +0000634 pub fn setprop_empty(&mut self, name: &CStr) -> Result<()> {
635 self.setprop(name, &[])
636 }
637
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900638 /// Deletes the given property.
Pierre-Clément Tosi4ba79662023-02-13 11:22:41 +0000639 pub fn delprop(&mut self, name: &CStr) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000640 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor) when the
Pierre-Clément Tosi4ba79662023-02-13 11:22:41 +0000641 // library locates the node's property. Removing the property may shift the offsets of
642 // other nodes and properties but the borrow checker should prevent this function from
643 // being called when FdtNode instances are in use.
644 let ret = unsafe {
645 libfdt_bindgen::fdt_delprop(self.fdt.as_mut_ptr(), self.offset, name.as_ptr())
646 };
647
648 fdt_err_expect_zero(ret)
649 }
650
Jaewan Kim4ae0e712023-10-19 14:16:17 +0900651 /// Deletes the given property effectively from DT, by setting it with FDT_NOP.
Pierre-Clément Tosibe3a97b2023-05-19 14:56:23 +0000652 pub fn nop_property(&mut self, name: &CStr) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000653 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor) when the
Pierre-Clément Tosibe3a97b2023-05-19 14:56:23 +0000654 // library locates the node's property.
655 let ret = unsafe {
656 libfdt_bindgen::fdt_nop_property(self.fdt.as_mut_ptr(), self.offset, name.as_ptr())
657 };
658
659 fdt_err_expect_zero(ret)
660 }
661
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900662 /// Trims the size of the given property to new_size.
Jiyong Park9c63cd12023-03-21 17:53:07 +0900663 pub fn trimprop(&mut self, name: &CStr, new_size: usize) -> Result<()> {
664 let (prop, len) =
665 FdtNode::getprop_internal(self.fdt, self.offset, name)?.ok_or(FdtError::NotFound)?;
666 if len == new_size {
667 return Ok(());
668 }
669 if new_size > len {
670 return Err(FdtError::NoSpace);
671 }
672
Andrew Walbran84b9a232023-07-05 14:01:40 +0000673 // SAFETY: new_size is smaller than the old size
Jiyong Park9c63cd12023-03-21 17:53:07 +0900674 let ret = unsafe {
675 libfdt_bindgen::fdt_setprop(
676 self.fdt.as_mut_ptr(),
677 self.offset,
678 name.as_ptr(),
679 prop.cast::<c_void>(),
680 new_size.try_into().map_err(|_| FdtError::BadValue)?,
681 )
682 };
683
684 fdt_err_expect_zero(ret)
685 }
686
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900687 /// Returns reference to the containing device tree.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000688 pub fn fdt(&mut self) -> &mut Fdt {
689 self.fdt
690 }
691
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900692 /// 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 +0000693 pub fn add_subnode(&'a mut self, name: &CStr) -> Result<Self> {
Jaewan Kim5ab13582023-10-20 20:56:27 +0900694 let offset = self.add_subnode_offset(name.to_bytes())?;
695 Ok(Self { fdt: self.fdt, offset })
696 }
697
698 /// Adds a new subnode to the given node with name and namelen, and returns it as a FdtNodeMut
699 /// on success.
700 pub fn add_subnode_with_namelen(&'a mut self, name: &CStr, namelen: usize) -> Result<Self> {
701 let offset = { self.add_subnode_offset(&name.to_bytes()[..namelen])? };
702 Ok(Self { fdt: self.fdt, offset })
703 }
704
705 fn add_subnode_offset(&mut self, name: &[u8]) -> Result<c_int> {
706 let namelen = name.len().try_into().unwrap();
Andrew Walbran84b9a232023-07-05 14:01:40 +0000707 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000708 let ret = unsafe {
Jaewan Kim5ab13582023-10-20 20:56:27 +0900709 libfdt_bindgen::fdt_add_subnode_namelen(
710 self.fdt.as_mut_ptr(),
711 self.offset,
712 name.as_ptr().cast::<_>(),
713 namelen,
714 )
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000715 };
Jaewan Kim5ab13582023-10-20 20:56:27 +0900716 fdt_err(ret)
717 }
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000718
Jaewan Kim5ab13582023-10-20 20:56:27 +0900719 /// Returns the subnode of the given name with len.
720 pub fn subnode_with_namelen(&'a mut self, name: &CStr, namelen: usize) -> Result<Option<Self>> {
721 let offset = self.subnode_offset(&name.to_bytes()[..namelen])?;
722 Ok(offset.map(|offset| Self { fdt: self.fdt, offset }))
723 }
724
725 fn subnode_offset(&self, name: &[u8]) -> Result<Option<c_int>> {
726 let namelen = name.len().try_into().unwrap();
727 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
728 let ret = unsafe {
729 libfdt_bindgen::fdt_subnode_offset_namelen(
730 self.fdt.as_ptr(),
731 self.offset,
732 name.as_ptr().cast::<_>(),
733 namelen,
734 )
735 };
736 fdt_err_or_option(ret)
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000737 }
738
739 fn parent(&'a self) -> Result<FdtNode<'a>> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000740 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000741 let ret = unsafe { libfdt_bindgen::fdt_parent_offset(self.fdt.as_ptr(), self.offset) };
742
743 Ok(FdtNode { fdt: &*self.fdt, offset: fdt_err(ret)? })
744 }
Jiyong Park9c63cd12023-03-21 17:53:07 +0900745
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900746 /// Returns the compatible node of the given name that is next after this node.
Jiyong Park9c63cd12023-03-21 17:53:07 +0900747 pub fn next_compatible(self, compatible: &CStr) -> Result<Option<Self>> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000748 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
Jiyong Park9c63cd12023-03-21 17:53:07 +0900749 let ret = unsafe {
750 libfdt_bindgen::fdt_node_offset_by_compatible(
751 self.fdt.as_ptr(),
752 self.offset,
753 compatible.as_ptr(),
754 )
755 };
756
757 Ok(fdt_err_or_option(ret)?.map(|offset| Self { fdt: self.fdt, offset }))
758 }
759
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900760 /// Deletes the node effectively by overwriting this node and its subtree with nop tags.
761 /// Returns the next compatible node of the given name.
Jiyong Park9c63cd12023-03-21 17:53:07 +0900762 // Side note: without this, filterint out excessive compatible nodes from the DT is impossible.
763 // The reason is that libfdt ensures that the node from where the search for the next
764 // compatible node is started is always a valid one -- except for the special case of offset =
765 // -1 which is to find the first compatible node. So, we can't delete a node and then find the
766 // next compatible node from it.
767 //
768 // We can't do in the opposite direction either. If we call next_compatible to find the next
769 // node, and delete the current node, the Rust borrow checker kicks in. The next node has a
770 // mutable reference to DT, so we can't use current node (which also has a mutable reference to
771 // DT).
Jaewan Kim4ae0e712023-10-19 14:16:17 +0900772 pub fn delete_and_next_compatible(mut self, compatible: &CStr) -> Result<Option<Self>> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000773 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
Jiyong Park9c63cd12023-03-21 17:53:07 +0900774 let ret = unsafe {
775 libfdt_bindgen::fdt_node_offset_by_compatible(
776 self.fdt.as_ptr(),
777 self.offset,
778 compatible.as_ptr(),
779 )
780 };
781 let next_offset = fdt_err_or_option(ret)?;
782
Jaewan Kim4ae0e712023-10-19 14:16:17 +0900783 if Some(self.offset) == next_offset {
784 return Err(FdtError::Internal);
785 }
786
787 // SAFETY: nop_self() only touches bytes of the self and its properties and subnodes, and
788 // doesn't alter any other blob in the tree. self.fdt and next_offset would remain valid.
789 unsafe { self.nop_self()? };
Jiyong Park9c63cd12023-03-21 17:53:07 +0900790
791 Ok(next_offset.map(|offset| Self { fdt: self.fdt, offset }))
792 }
Jaewan Kim4ae0e712023-10-19 14:16:17 +0900793
794 /// Deletes this node effectively from DT, by setting it with FDT_NOP
795 pub fn nop(mut self) -> Result<()> {
796 // SAFETY: This consumes self, so invalid node wouldn't be used any further
797 unsafe { self.nop_self() }
798 }
799
800 /// Deletes this node effectively from DT, by setting it with FDT_NOP.
801 /// This only changes bytes of the node and its properties and subnodes, and doesn't alter or
802 /// move any other part of the tree.
803 /// SAFETY: This node is no longer valid.
804 unsafe fn nop_self(&mut self) -> Result<()> {
805 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
806 let ret = unsafe { libfdt_bindgen::fdt_nop_node(self.fdt.as_mut_ptr(), self.offset) };
807
808 fdt_err_expect_zero(ret)
809 }
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000810}
811
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000812/// Wrapper around low-level libfdt functions.
Alice Wang9d4df702023-05-25 14:14:12 +0000813#[derive(Debug)]
David Brazdil1baa9a92022-06-28 14:47:50 +0100814#[repr(transparent)]
815pub struct Fdt {
Pierre-Clément Tosief2030e2022-11-28 11:21:20 +0000816 buffer: [u8],
David Brazdil1baa9a92022-06-28 14:47:50 +0100817}
818
819impl Fdt {
820 /// Wraps a slice containing a Flattened Device Tree.
821 ///
822 /// Fails if the FDT does not pass validation.
823 pub fn from_slice(fdt: &[u8]) -> Result<&Self> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000824 // SAFETY: The FDT will be validated before it is returned.
David Brazdil1baa9a92022-06-28 14:47:50 +0100825 let fdt = unsafe { Self::unchecked_from_slice(fdt) };
826 fdt.check_full()?;
827 Ok(fdt)
828 }
829
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000830 /// Wraps a mutable slice containing a Flattened Device Tree.
831 ///
832 /// Fails if the FDT does not pass validation.
833 pub fn from_mut_slice(fdt: &mut [u8]) -> Result<&mut Self> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000834 // SAFETY: The FDT will be validated before it is returned.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000835 let fdt = unsafe { Self::unchecked_from_mut_slice(fdt) };
836 fdt.check_full()?;
837 Ok(fdt)
838 }
839
Jaewan Kim4cf20aa2023-04-03 10:25:38 +0900840 /// Creates an empty Flattened Device Tree with a mutable slice.
841 pub fn create_empty_tree(fdt: &mut [u8]) -> Result<&mut Self> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000842 // SAFETY: fdt_create_empty_tree() only write within the specified length,
Jaewan Kim4cf20aa2023-04-03 10:25:38 +0900843 // and returns error if buffer was insufficient.
844 // There will be no memory write outside of the given fdt.
845 let ret = unsafe {
846 libfdt_bindgen::fdt_create_empty_tree(
847 fdt.as_mut_ptr().cast::<c_void>(),
848 fdt.len() as i32,
849 )
850 };
851 fdt_err_expect_zero(ret)?;
852
Andrew Walbran84b9a232023-07-05 14:01:40 +0000853 // SAFETY: The FDT will be validated before it is returned.
Jaewan Kim4cf20aa2023-04-03 10:25:38 +0900854 let fdt = unsafe { Self::unchecked_from_mut_slice(fdt) };
855 fdt.check_full()?;
856
857 Ok(fdt)
858 }
859
David Brazdil1baa9a92022-06-28 14:47:50 +0100860 /// Wraps a slice containing a Flattened Device Tree.
861 ///
862 /// # Safety
863 ///
864 /// The returned FDT might be invalid, only use on slices containing a valid DT.
865 pub unsafe fn unchecked_from_slice(fdt: &[u8]) -> &Self {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000866 // SAFETY: Fdt is a wrapper around a [u8], so the transmute is valid. The caller is
867 // responsible for ensuring that it is actually a valid FDT.
868 unsafe { mem::transmute::<&[u8], &Self>(fdt) }
David Brazdil1baa9a92022-06-28 14:47:50 +0100869 }
870
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000871 /// Wraps a mutable slice containing a Flattened Device Tree.
872 ///
873 /// # Safety
874 ///
875 /// The returned FDT might be invalid, only use on slices containing a valid DT.
876 pub unsafe fn unchecked_from_mut_slice(fdt: &mut [u8]) -> &mut Self {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000877 // SAFETY: Fdt is a wrapper around a [u8], so the transmute is valid. The caller is
878 // responsible for ensuring that it is actually a valid FDT.
879 unsafe { mem::transmute::<&mut [u8], &mut Self>(fdt) }
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000880 }
881
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900882 /// Updates this FDT from a slice containing another FDT.
Jiyong Parke9d87e82023-03-21 19:28:40 +0900883 pub fn copy_from_slice(&mut self, new_fdt: &[u8]) -> Result<()> {
884 if self.buffer.len() < new_fdt.len() {
885 Err(FdtError::NoSpace)
886 } else {
887 let totalsize = self.totalsize();
888 self.buffer[..new_fdt.len()].clone_from_slice(new_fdt);
889 // Zeroize the remaining part. We zeroize up to the size of the original DT because
890 // zeroizing the entire buffer (max 2MB) is not necessary and may increase the VM boot
891 // time.
892 self.buffer[new_fdt.len()..max(new_fdt.len(), totalsize)].fill(0_u8);
893 Ok(())
894 }
895 }
896
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900897 /// Unpacks the DT to cover the whole slice it is contained in.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000898 pub fn unpack(&mut self) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000899 // SAFETY: "Opens" the DT in-place (supported use-case) by updating its header and
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000900 // internal structures to make use of the whole self.fdt slice but performs no accesses
901 // outside of it and leaves the DT in a state that will be detected by other functions.
902 let ret = unsafe {
903 libfdt_bindgen::fdt_open_into(
904 self.as_ptr(),
905 self.as_mut_ptr(),
906 self.capacity().try_into().map_err(|_| FdtError::Internal)?,
907 )
908 };
909 fdt_err_expect_zero(ret)
910 }
911
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900912 /// Packs the DT to take a minimum amount of memory.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000913 ///
914 /// Doesn't shrink the underlying memory slice.
915 pub fn pack(&mut self) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000916 // SAFETY: "Closes" the DT in-place by updating its header and relocating its structs.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000917 let ret = unsafe { libfdt_bindgen::fdt_pack(self.as_mut_ptr()) };
918 fdt_err_expect_zero(ret)
919 }
920
Pierre-Clément Tosi90e19352022-11-21 17:11:48 +0000921 /// Applies a DT overlay on the base DT.
922 ///
923 /// # Safety
924 ///
925 /// On failure, the library corrupts the DT and overlay so both must be discarded.
926 pub unsafe fn apply_overlay<'a>(&'a mut self, overlay: &'a mut Fdt) -> Result<&'a mut Self> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000927 let ret =
928 // SAFETY: Both pointers are valid because they come from references, and fdt_overlay_apply
929 // doesn't keep them after it returns. It may corrupt their contents if there is an error,
930 // but that's our caller's responsibility.
931 unsafe { libfdt_bindgen::fdt_overlay_apply(self.as_mut_ptr(), overlay.as_mut_ptr()) };
932 fdt_err_expect_zero(ret)?;
Pierre-Clément Tosi90e19352022-11-21 17:11:48 +0000933 Ok(self)
934 }
935
Alice Wang2422bdc2023-06-12 08:37:55 +0000936 /// Returns an iterator of memory banks specified the "/memory" node.
937 /// Throws an error when the "/memory" is not found in the device tree.
David Brazdil1baa9a92022-06-28 14:47:50 +0100938 ///
939 /// NOTE: This does not support individual "/memory@XXXX" banks.
Alice Wang2422bdc2023-06-12 08:37:55 +0000940 pub fn memory(&self) -> Result<MemRegIterator> {
941 let memory_node_name = CStr::from_bytes_with_nul(b"/memory\0").unwrap();
942 let memory_device_type = CStr::from_bytes_with_nul(b"memory\0").unwrap();
David Brazdil1baa9a92022-06-28 14:47:50 +0100943
Alice Wang2422bdc2023-06-12 08:37:55 +0000944 let node = self.node(memory_node_name)?.ok_or(FdtError::NotFound)?;
945 if node.device_type()? != Some(memory_device_type) {
946 return Err(FdtError::BadValue);
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000947 }
Alice Wang2422bdc2023-06-12 08:37:55 +0000948 node.reg()?.ok_or(FdtError::BadValue).map(MemRegIterator::new)
949 }
950
951 /// Returns the first memory range in the `/memory` node.
952 pub fn first_memory_range(&self) -> Result<Range<usize>> {
953 self.memory()?.next().ok_or(FdtError::NotFound)
David Brazdil1baa9a92022-06-28 14:47:50 +0100954 }
955
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900956 /// Returns the standard /chosen node.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000957 pub fn chosen(&self) -> Result<Option<FdtNode>> {
David Brazdil1baa9a92022-06-28 14:47:50 +0100958 self.node(CStr::from_bytes_with_nul(b"/chosen\0").unwrap())
959 }
960
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900961 /// Returns the standard /chosen node as mutable.
Pierre-Clément Tosi4ba79662023-02-13 11:22:41 +0000962 pub fn chosen_mut(&mut self) -> Result<Option<FdtNodeMut>> {
963 self.node_mut(CStr::from_bytes_with_nul(b"/chosen\0").unwrap())
964 }
965
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900966 /// Returns the root node of the tree.
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000967 pub fn root(&self) -> Result<FdtNode> {
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000968 self.node(CStr::from_bytes_with_nul(b"/\0").unwrap())?.ok_or(FdtError::Internal)
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000969 }
970
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900971 /// Returns a tree node by its full path.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000972 pub fn node(&self, path: &CStr) -> Result<Option<FdtNode>> {
Jaewan Kimbab42592023-10-13 15:47:19 +0900973 Ok(self.path_offset(path.to_bytes())?.map(|offset| FdtNode { fdt: self, offset }))
David Brazdil1baa9a92022-06-28 14:47:50 +0100974 }
975
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000976 /// Iterate over nodes with a given compatible string.
977 pub fn compatible_nodes<'a>(&'a self, compatible: &'a CStr) -> Result<CompatibleIterator<'a>> {
978 CompatibleIterator::new(self, compatible)
979 }
980
Jaewan Kim17ba7a32023-10-19 13:25:15 +0900981 /// Returns max phandle in the tree.
982 pub fn max_phandle(&self) -> Result<Phandle> {
983 let mut phandle: u32 = 0;
984 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
985 let ret = unsafe { libfdt_bindgen::fdt_find_max_phandle(self.as_ptr(), &mut phandle) };
986
987 fdt_err_expect_zero(ret)?;
Pierre-Clément Tosieba27792023-10-30 12:04:12 +0000988 phandle.try_into()
Jaewan Kim17ba7a32023-10-19 13:25:15 +0900989 }
990
991 /// Returns a node with the phandle
992 pub fn node_with_phandle(&self, phandle: Phandle) -> Result<Option<FdtNode>> {
993 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
994 let ret = unsafe { libfdt_bindgen::fdt_node_offset_by_phandle(self.as_ptr(), phandle.0) };
995 Ok(fdt_err_or_option(ret)?.map(|offset| FdtNode { fdt: self, offset }))
996 }
997
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900998 /// Returns the mutable root node of the tree.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000999 pub fn root_mut(&mut self) -> Result<FdtNodeMut> {
1000 self.node_mut(CStr::from_bytes_with_nul(b"/\0").unwrap())?.ok_or(FdtError::Internal)
1001 }
1002
Jaewan Kimb3dcfc22023-09-20 10:20:52 +09001003 /// Returns a mutable tree node by its full path.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +00001004 pub fn node_mut(&mut self, path: &CStr) -> Result<Option<FdtNodeMut>> {
Jaewan Kimbab42592023-10-13 15:47:19 +09001005 Ok(self.path_offset(path.to_bytes())?.map(|offset| FdtNodeMut { fdt: self, offset }))
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +00001006 }
1007
Jaewan Kimb3dcfc22023-09-20 10:20:52 +09001008 /// Returns the device tree as a slice (may be smaller than the containing buffer).
Pierre-Clément Tosidb74cb12022-12-08 13:56:25 +00001009 pub fn as_slice(&self) -> &[u8] {
1010 &self.buffer[..self.totalsize()]
1011 }
1012
Jaewan Kimbab42592023-10-13 15:47:19 +09001013 fn path_offset(&self, path: &[u8]) -> Result<Option<c_int>> {
1014 let len = path.len().try_into().map_err(|_| FdtError::BadPath)?;
Andrew Walbran84b9a232023-07-05 14:01:40 +00001015 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor) and the
David Brazdil1baa9a92022-06-28 14:47:50 +01001016 // function respects the passed number of characters.
1017 let ret = unsafe {
1018 // *_namelen functions don't include the trailing nul terminator in 'len'.
Jaewan Kimbab42592023-10-13 15:47:19 +09001019 libfdt_bindgen::fdt_path_offset_namelen(self.as_ptr(), path.as_ptr().cast::<_>(), len)
David Brazdil1baa9a92022-06-28 14:47:50 +01001020 };
1021
Pierre-Clément Tosib244d932022-11-24 16:45:53 +00001022 fdt_err_or_option(ret)
David Brazdil1baa9a92022-06-28 14:47:50 +01001023 }
1024
1025 fn check_full(&self) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +00001026 // SAFETY: Only performs read accesses within the limits of the slice. If successful, this
David Brazdil1baa9a92022-06-28 14:47:50 +01001027 // call guarantees to other unsafe calls that the header contains a valid totalsize (w.r.t.
1028 // 'len' i.e. the self.fdt slice) that those C functions can use to perform bounds
1029 // checking. The library doesn't maintain an internal state (such as pointers) between
1030 // calls as it expects the client code to keep track of the objects (DT, nodes, ...).
Pierre-Clément Tosi02017da2023-09-26 17:57:04 +01001031 let ret = unsafe { libfdt_bindgen::fdt_check_full(self.as_ptr(), self.capacity()) };
David Brazdil1baa9a92022-06-28 14:47:50 +01001032 fdt_err_expect_zero(ret)
1033 }
1034
Jaewan Kimaa638702023-09-19 13:34:01 +09001035 fn get_from_ptr(&self, ptr: *const c_void, len: usize) -> Result<&[u8]> {
1036 let ptr = ptr as usize;
1037 let offset = ptr.checked_sub(self.as_ptr() as usize).ok_or(FdtError::Internal)?;
1038 self.buffer.get(offset..(offset + len)).ok_or(FdtError::Internal)
1039 }
1040
Jaewan Kim72d10902023-10-12 21:59:26 +09001041 fn string(&self, offset: c_int) -> Result<&CStr> {
1042 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
1043 let res = unsafe { libfdt_bindgen::fdt_string(self.as_ptr(), offset) };
1044 if res.is_null() {
1045 return Err(FdtError::Internal);
1046 }
1047
1048 // SAFETY: Non-null return from fdt_string() is valid null-terminating string within FDT.
1049 Ok(unsafe { CStr::from_ptr(res) })
1050 }
1051
Jaewan Kimb3dcfc22023-09-20 10:20:52 +09001052 /// Returns a shared pointer to the device tree.
Pierre-Clément Tosi8036b4f2023-02-17 10:31:31 +00001053 pub fn as_ptr(&self) -> *const c_void {
Pierre-Clément Tosi0dcc75e2023-05-02 13:43:55 +00001054 self.buffer.as_ptr().cast::<_>()
David Brazdil1baa9a92022-06-28 14:47:50 +01001055 }
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +00001056
1057 fn as_mut_ptr(&mut self) -> *mut c_void {
Pierre-Clément Tosi0dcc75e2023-05-02 13:43:55 +00001058 self.buffer.as_mut_ptr().cast::<_>()
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +00001059 }
1060
1061 fn capacity(&self) -> usize {
Pierre-Clément Tosief2030e2022-11-28 11:21:20 +00001062 self.buffer.len()
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +00001063 }
Pierre-Clément Tosidb74cb12022-12-08 13:56:25 +00001064
1065 fn header(&self) -> &libfdt_bindgen::fdt_header {
Pierre-Clément Tosi0dcc75e2023-05-02 13:43:55 +00001066 let p = self.as_ptr().cast::<_>();
Andrew Walbran84b9a232023-07-05 14:01:40 +00001067 // SAFETY: A valid FDT (verified by constructor) must contain a valid fdt_header.
Pierre-Clément Tosi0dcc75e2023-05-02 13:43:55 +00001068 unsafe { &*p }
Pierre-Clément Tosidb74cb12022-12-08 13:56:25 +00001069 }
1070
1071 fn totalsize(&self) -> usize {
1072 u32::from_be(self.header().totalsize) as usize
1073 }
David Brazdil1baa9a92022-06-28 14:47:50 +01001074}