blob: 91214b32bf38003d9d65f20ca3af99af872e59b5 [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
Andrew Walbranb39e6922022-12-05 17:01:20 +000022pub use iterators::{AddressRange, CellIterator, MemRegIterator, RangesIterator, Reg, RegIterator};
Andrew Walbran55ad01b2022-12-05 17:00:40 +000023
Jiyong Parke9d87e82023-03-21 19:28:40 +090024use core::cmp::max;
David Brazdil1baa9a92022-06-28 14:47:50 +010025use core::ffi::{c_int, c_void, CStr};
26use core::fmt;
27use core::mem;
David Brazdil1baa9a92022-06-28 14:47:50 +010028use core::result;
Pierre-Clément Tosic27c4272023-05-19 15:46:26 +000029use zerocopy::AsBytes as _;
David Brazdil1baa9a92022-06-28 14:47:50 +010030
31/// Error type corresponding to libfdt error codes.
32#[derive(Clone, Copy, Debug, Eq, PartialEq)]
33pub enum FdtError {
34 /// FDT_ERR_NOTFOUND
35 NotFound,
36 /// FDT_ERR_EXISTS
37 Exists,
38 /// FDT_ERR_NOSPACE
39 NoSpace,
40 /// FDT_ERR_BADOFFSET
41 BadOffset,
42 /// FDT_ERR_BADPATH
43 BadPath,
44 /// FDT_ERR_BADPHANDLE
45 BadPhandle,
46 /// FDT_ERR_BADSTATE
47 BadState,
48 /// FDT_ERR_TRUNCATED
49 Truncated,
50 /// FDT_ERR_BADMAGIC
51 BadMagic,
52 /// FDT_ERR_BADVERSION
53 BadVersion,
54 /// FDT_ERR_BADSTRUCTURE
55 BadStructure,
56 /// FDT_ERR_BADLAYOUT
57 BadLayout,
58 /// FDT_ERR_INTERNAL
59 Internal,
60 /// FDT_ERR_BADNCELLS
61 BadNCells,
62 /// FDT_ERR_BADVALUE
63 BadValue,
64 /// FDT_ERR_BADOVERLAY
65 BadOverlay,
66 /// FDT_ERR_NOPHANDLES
67 NoPhandles,
68 /// FDT_ERR_BADFLAGS
69 BadFlags,
70 /// FDT_ERR_ALIGNMENT
71 Alignment,
72 /// Unexpected error code
73 Unknown(i32),
74}
75
76impl fmt::Display for FdtError {
77 /// Prints error messages from libfdt.h documentation.
78 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
79 match self {
80 Self::NotFound => write!(f, "The requested node or property does not exist"),
81 Self::Exists => write!(f, "Attempted to create an existing node or property"),
82 Self::NoSpace => write!(f, "Insufficient buffer space to contain the expanded tree"),
83 Self::BadOffset => write!(f, "Structure block offset is out-of-bounds or invalid"),
84 Self::BadPath => write!(f, "Badly formatted path"),
85 Self::BadPhandle => write!(f, "Invalid phandle length or value"),
86 Self::BadState => write!(f, "Received incomplete device tree"),
87 Self::Truncated => write!(f, "Device tree or sub-block is improperly terminated"),
88 Self::BadMagic => write!(f, "Device tree header missing its magic number"),
89 Self::BadVersion => write!(f, "Device tree has a version which can't be handled"),
90 Self::BadStructure => write!(f, "Device tree has a corrupt structure block"),
91 Self::BadLayout => write!(f, "Device tree sub-blocks in unsupported order"),
92 Self::Internal => write!(f, "libfdt has failed an internal assertion"),
93 Self::BadNCells => write!(f, "Bad format or value of #address-cells or #size-cells"),
94 Self::BadValue => write!(f, "Unexpected property value"),
95 Self::BadOverlay => write!(f, "Overlay cannot be applied"),
96 Self::NoPhandles => write!(f, "Device tree doesn't have any phandle available anymore"),
97 Self::BadFlags => write!(f, "Invalid flag or invalid combination of flags"),
98 Self::Alignment => write!(f, "Device tree base address is not 8-byte aligned"),
99 Self::Unknown(e) => write!(f, "Unknown libfdt error '{e}'"),
100 }
101 }
102}
103
104/// Result type with FdtError enum.
105pub type Result<T> = result::Result<T, FdtError>;
106
107fn fdt_err(val: c_int) -> Result<c_int> {
108 if val >= 0 {
109 Ok(val)
110 } else {
111 Err(match -val as _ {
112 libfdt_bindgen::FDT_ERR_NOTFOUND => FdtError::NotFound,
113 libfdt_bindgen::FDT_ERR_EXISTS => FdtError::Exists,
114 libfdt_bindgen::FDT_ERR_NOSPACE => FdtError::NoSpace,
115 libfdt_bindgen::FDT_ERR_BADOFFSET => FdtError::BadOffset,
116 libfdt_bindgen::FDT_ERR_BADPATH => FdtError::BadPath,
117 libfdt_bindgen::FDT_ERR_BADPHANDLE => FdtError::BadPhandle,
118 libfdt_bindgen::FDT_ERR_BADSTATE => FdtError::BadState,
119 libfdt_bindgen::FDT_ERR_TRUNCATED => FdtError::Truncated,
120 libfdt_bindgen::FDT_ERR_BADMAGIC => FdtError::BadMagic,
121 libfdt_bindgen::FDT_ERR_BADVERSION => FdtError::BadVersion,
122 libfdt_bindgen::FDT_ERR_BADSTRUCTURE => FdtError::BadStructure,
123 libfdt_bindgen::FDT_ERR_BADLAYOUT => FdtError::BadLayout,
124 libfdt_bindgen::FDT_ERR_INTERNAL => FdtError::Internal,
125 libfdt_bindgen::FDT_ERR_BADNCELLS => FdtError::BadNCells,
126 libfdt_bindgen::FDT_ERR_BADVALUE => FdtError::BadValue,
127 libfdt_bindgen::FDT_ERR_BADOVERLAY => FdtError::BadOverlay,
128 libfdt_bindgen::FDT_ERR_NOPHANDLES => FdtError::NoPhandles,
129 libfdt_bindgen::FDT_ERR_BADFLAGS => FdtError::BadFlags,
130 libfdt_bindgen::FDT_ERR_ALIGNMENT => FdtError::Alignment,
131 _ => FdtError::Unknown(val),
132 })
133 }
134}
135
136fn fdt_err_expect_zero(val: c_int) -> Result<()> {
137 match fdt_err(val)? {
138 0 => Ok(()),
139 _ => Err(FdtError::Unknown(val)),
140 }
141}
142
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000143fn fdt_err_or_option(val: c_int) -> Result<Option<c_int>> {
144 match fdt_err(val) {
145 Ok(val) => Ok(Some(val)),
146 Err(FdtError::NotFound) => Ok(None),
147 Err(e) => Err(e),
148 }
149}
150
David Brazdil1baa9a92022-06-28 14:47:50 +0100151/// Value of a #address-cells property.
Andrew Walbranb39e6922022-12-05 17:01:20 +0000152#[derive(Copy, Clone, Debug, Eq, PartialEq)]
David Brazdil1baa9a92022-06-28 14:47:50 +0100153enum AddrCells {
154 Single = 1,
155 Double = 2,
Andrew Walbranb39e6922022-12-05 17:01:20 +0000156 Triple = 3,
David Brazdil1baa9a92022-06-28 14:47:50 +0100157}
158
159impl TryFrom<c_int> for AddrCells {
160 type Error = FdtError;
161
162 fn try_from(res: c_int) -> Result<Self> {
163 match fdt_err(res)? {
164 x if x == Self::Single as c_int => Ok(Self::Single),
165 x if x == Self::Double as c_int => Ok(Self::Double),
Andrew Walbranb39e6922022-12-05 17:01:20 +0000166 x if x == Self::Triple as c_int => Ok(Self::Triple),
David Brazdil1baa9a92022-06-28 14:47:50 +0100167 _ => Err(FdtError::BadNCells),
168 }
169 }
170}
171
172/// Value of a #size-cells property.
Andrew Walbranb39e6922022-12-05 17:01:20 +0000173#[derive(Copy, Clone, Debug, Eq, PartialEq)]
David Brazdil1baa9a92022-06-28 14:47:50 +0100174enum SizeCells {
175 None = 0,
176 Single = 1,
177 Double = 2,
178}
179
180impl TryFrom<c_int> for SizeCells {
181 type Error = FdtError;
182
183 fn try_from(res: c_int) -> Result<Self> {
184 match fdt_err(res)? {
185 x if x == Self::None as c_int => Ok(Self::None),
186 x if x == Self::Single as c_int => Ok(Self::Single),
187 x if x == Self::Double as c_int => Ok(Self::Double),
188 _ => Err(FdtError::BadNCells),
189 }
190 }
191}
192
David Brazdil1baa9a92022-06-28 14:47:50 +0100193/// DT node.
Alice Wang9d4df702023-05-25 14:14:12 +0000194#[derive(Clone, Copy, Debug)]
David Brazdil1baa9a92022-06-28 14:47:50 +0100195pub struct FdtNode<'a> {
196 fdt: &'a Fdt,
197 offset: c_int,
198}
199
200impl<'a> FdtNode<'a> {
Jiyong Park9c63cd12023-03-21 17:53:07 +0900201 /// Create immutable node from a mutable node at the same offset
202 pub fn from_mut(other: &'a FdtNodeMut) -> Self {
203 FdtNode { fdt: other.fdt, offset: other.offset }
204 }
David Brazdil1baa9a92022-06-28 14:47:50 +0100205 /// Find parent node.
206 pub fn parent(&self) -> Result<Self> {
207 // SAFETY - Accesses (read-only) are constrained to the DT totalsize.
208 let ret = unsafe { libfdt_bindgen::fdt_parent_offset(self.fdt.as_ptr(), self.offset) };
209
210 Ok(Self { fdt: self.fdt, offset: fdt_err(ret)? })
211 }
212
213 /// Retrieve the standard (deprecated) device_type <string> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000214 pub fn device_type(&self) -> Result<Option<&CStr>> {
David Brazdil1baa9a92022-06-28 14:47:50 +0100215 self.getprop_str(CStr::from_bytes_with_nul(b"device_type\0").unwrap())
216 }
217
218 /// Retrieve the standard reg <prop-encoded-array> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000219 pub fn reg(&self) -> Result<Option<RegIterator<'a>>> {
220 let reg = CStr::from_bytes_with_nul(b"reg\0").unwrap();
David Brazdil1baa9a92022-06-28 14:47:50 +0100221
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000222 if let Some(cells) = self.getprop_cells(reg)? {
223 let parent = self.parent()?;
David Brazdil1baa9a92022-06-28 14:47:50 +0100224
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000225 let addr_cells = parent.address_cells()?;
226 let size_cells = parent.size_cells()?;
227
228 Ok(Some(RegIterator::new(cells, addr_cells, size_cells)))
229 } else {
230 Ok(None)
231 }
David Brazdil1baa9a92022-06-28 14:47:50 +0100232 }
233
Andrew Walbranb39e6922022-12-05 17:01:20 +0000234 /// Retrieves the standard ranges property.
235 pub fn ranges<A, P, S>(&self) -> Result<Option<RangesIterator<'a, A, P, S>>> {
236 let ranges = CStr::from_bytes_with_nul(b"ranges\0").unwrap();
237 if let Some(cells) = self.getprop_cells(ranges)? {
238 let parent = self.parent()?;
239 let addr_cells = self.address_cells()?;
240 let parent_addr_cells = parent.address_cells()?;
241 let size_cells = self.size_cells()?;
242 Ok(Some(RangesIterator::<A, P, S>::new(
243 cells,
244 addr_cells,
245 parent_addr_cells,
246 size_cells,
247 )))
248 } else {
249 Ok(None)
250 }
251 }
252
David Brazdil1baa9a92022-06-28 14:47:50 +0100253 /// Retrieve the value of a given <string> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000254 pub fn getprop_str(&self, name: &CStr) -> Result<Option<&CStr>> {
255 let value = if let Some(bytes) = self.getprop(name)? {
256 Some(CStr::from_bytes_with_nul(bytes).map_err(|_| FdtError::BadValue)?)
257 } else {
258 None
259 };
260 Ok(value)
David Brazdil1baa9a92022-06-28 14:47:50 +0100261 }
262
263 /// Retrieve the value of a given property as an array of cells.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000264 pub fn getprop_cells(&self, name: &CStr) -> Result<Option<CellIterator<'a>>> {
265 if let Some(cells) = self.getprop(name)? {
266 Ok(Some(CellIterator::new(cells)))
267 } else {
268 Ok(None)
269 }
David Brazdil1baa9a92022-06-28 14:47:50 +0100270 }
271
272 /// Retrieve the value of a given <u32> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000273 pub fn getprop_u32(&self, name: &CStr) -> Result<Option<u32>> {
274 let value = if let Some(bytes) = self.getprop(name)? {
275 Some(u32::from_be_bytes(bytes.try_into().map_err(|_| FdtError::BadValue)?))
276 } else {
277 None
278 };
279 Ok(value)
David Brazdil1baa9a92022-06-28 14:47:50 +0100280 }
281
282 /// Retrieve the value of a given <u64> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000283 pub fn getprop_u64(&self, name: &CStr) -> Result<Option<u64>> {
284 let value = if let Some(bytes) = self.getprop(name)? {
285 Some(u64::from_be_bytes(bytes.try_into().map_err(|_| FdtError::BadValue)?))
286 } else {
287 None
288 };
289 Ok(value)
David Brazdil1baa9a92022-06-28 14:47:50 +0100290 }
291
292 /// Retrieve the value of a given property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000293 pub fn getprop(&self, name: &CStr) -> Result<Option<&'a [u8]>> {
Jiyong Park9c63cd12023-03-21 17:53:07 +0900294 if let Some((prop, len)) = Self::getprop_internal(self.fdt, self.offset, name)? {
295 let offset = (prop as usize)
296 .checked_sub(self.fdt.as_ptr() as usize)
297 .ok_or(FdtError::Internal)?;
298
299 Ok(Some(self.fdt.buffer.get(offset..(offset + len)).ok_or(FdtError::Internal)?))
300 } else {
301 Ok(None) // property was not found
302 }
303 }
304
305 /// Return the pointer and size of the property named `name`, in a node at offset `offset`, in
306 /// a device tree `fdt`. The pointer is guaranteed to be non-null, in which case error returns.
307 fn getprop_internal(
308 fdt: &'a Fdt,
309 offset: c_int,
310 name: &CStr,
311 ) -> Result<Option<(*const c_void, usize)>> {
David Brazdil1baa9a92022-06-28 14:47:50 +0100312 let mut len: i32 = 0;
313 // SAFETY - Accesses are constrained to the DT totalsize (validated by ctor) and the
314 // function respects the passed number of characters.
315 let prop = unsafe {
316 libfdt_bindgen::fdt_getprop_namelen(
Jiyong Park9c63cd12023-03-21 17:53:07 +0900317 fdt.as_ptr(),
318 offset,
David Brazdil1baa9a92022-06-28 14:47:50 +0100319 name.as_ptr(),
320 // *_namelen functions don't include the trailing nul terminator in 'len'.
321 name.to_bytes().len().try_into().map_err(|_| FdtError::BadPath)?,
322 &mut len as *mut i32,
323 )
324 } as *const u8;
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000325
326 let Some(len) = fdt_err_or_option(len)? else {
327 return Ok(None); // Property was not found.
328 };
329 let len = usize::try_from(len).map_err(|_| FdtError::Internal)?;
330
David Brazdil1baa9a92022-06-28 14:47:50 +0100331 if prop.is_null() {
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000332 // We expected an error code in len but still received a valid value?!
333 return Err(FdtError::Internal);
David Brazdil1baa9a92022-06-28 14:47:50 +0100334 }
Jiyong Park9c63cd12023-03-21 17:53:07 +0900335 Ok(Some((prop.cast::<c_void>(), len)))
David Brazdil1baa9a92022-06-28 14:47:50 +0100336 }
337
338 /// Get reference to the containing device tree.
339 pub fn fdt(&self) -> &Fdt {
340 self.fdt
341 }
342
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000343 fn next_compatible(self, compatible: &CStr) -> Result<Option<Self>> {
344 // SAFETY - Accesses (read-only) are constrained to the DT totalsize.
345 let ret = unsafe {
346 libfdt_bindgen::fdt_node_offset_by_compatible(
347 self.fdt.as_ptr(),
348 self.offset,
349 compatible.as_ptr(),
350 )
351 };
352
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000353 Ok(fdt_err_or_option(ret)?.map(|offset| Self { fdt: self.fdt, offset }))
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000354 }
355
David Brazdil1baa9a92022-06-28 14:47:50 +0100356 fn address_cells(&self) -> Result<AddrCells> {
357 // SAFETY - Accesses are constrained to the DT totalsize (validated by ctor).
358 unsafe { libfdt_bindgen::fdt_address_cells(self.fdt.as_ptr(), self.offset) }
359 .try_into()
360 .map_err(|_| FdtError::Internal)
361 }
362
363 fn size_cells(&self) -> Result<SizeCells> {
364 // SAFETY - Accesses are constrained to the DT totalsize (validated by ctor).
365 unsafe { libfdt_bindgen::fdt_size_cells(self.fdt.as_ptr(), self.offset) }
366 .try_into()
367 .map_err(|_| FdtError::Internal)
368 }
369}
370
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000371/// Mutable FDT node.
372pub struct FdtNodeMut<'a> {
373 fdt: &'a mut Fdt,
374 offset: c_int,
375}
376
377impl<'a> FdtNodeMut<'a> {
378 /// Append a property name-value (possibly empty) pair to the given node.
379 pub fn appendprop<T: AsRef<[u8]>>(&mut self, name: &CStr, value: &T) -> Result<()> {
380 // SAFETY - Accesses are constrained to the DT totalsize (validated by ctor).
381 let ret = unsafe {
382 libfdt_bindgen::fdt_appendprop(
383 self.fdt.as_mut_ptr(),
384 self.offset,
385 name.as_ptr(),
386 value.as_ref().as_ptr().cast::<c_void>(),
387 value.as_ref().len().try_into().map_err(|_| FdtError::BadValue)?,
388 )
389 };
390
391 fdt_err_expect_zero(ret)
392 }
393
394 /// Append a (address, size) pair property to the given node.
395 pub fn appendprop_addrrange(&mut self, name: &CStr, addr: u64, size: u64) -> Result<()> {
396 // SAFETY - Accesses are constrained to the DT totalsize (validated by ctor).
397 let ret = unsafe {
398 libfdt_bindgen::fdt_appendprop_addrrange(
399 self.fdt.as_mut_ptr(),
400 self.parent()?.offset,
401 self.offset,
402 name.as_ptr(),
403 addr,
404 size,
405 )
406 };
407
408 fdt_err_expect_zero(ret)
409 }
410
Jaewan Kimba8929b2023-01-13 11:13:29 +0900411 /// Create or change a property name-value pair to the given node.
412 pub fn setprop(&mut self, name: &CStr, value: &[u8]) -> Result<()> {
413 // SAFETY - New value size is constrained to the DT totalsize
414 // (validated by underlying libfdt).
415 let ret = unsafe {
416 libfdt_bindgen::fdt_setprop(
417 self.fdt.as_mut_ptr(),
418 self.offset,
419 name.as_ptr(),
420 value.as_ptr().cast::<c_void>(),
421 value.len().try_into().map_err(|_| FdtError::BadValue)?,
422 )
423 };
424
425 fdt_err_expect_zero(ret)
426 }
427
Jiyong Park9c63cd12023-03-21 17:53:07 +0900428 /// Replace the value of the given property with the given value, and ensure that the given
429 /// value has the same length as the current value length
430 pub fn setprop_inplace(&mut self, name: &CStr, value: &[u8]) -> Result<()> {
431 // SAFETY - fdt size is not altered
432 let ret = unsafe {
433 libfdt_bindgen::fdt_setprop_inplace(
434 self.fdt.as_mut_ptr(),
435 self.offset,
436 name.as_ptr(),
437 value.as_ptr().cast::<c_void>(),
438 value.len().try_into().map_err(|_| FdtError::BadValue)?,
439 )
440 };
441
442 fdt_err_expect_zero(ret)
443 }
444
Pierre-Clément Tosic27c4272023-05-19 15:46:26 +0000445 /// Replace the value of the given (address, size) pair property with the given value, and
446 /// ensure that the given value has the same length as the current value length
447 pub fn setprop_addrrange_inplace(&mut self, name: &CStr, addr: u64, size: u64) -> Result<()> {
448 let pair = [addr.to_be(), size.to_be()];
449 self.setprop_inplace(name, pair.as_bytes())
450 }
451
Pierre-Clément Tosi4ba79662023-02-13 11:22:41 +0000452 /// Create or change a flag-like empty property.
453 pub fn setprop_empty(&mut self, name: &CStr) -> Result<()> {
454 self.setprop(name, &[])
455 }
456
457 /// Delete the given property.
458 pub fn delprop(&mut self, name: &CStr) -> Result<()> {
459 // SAFETY - Accesses are constrained to the DT totalsize (validated by ctor) when the
460 // library locates the node's property. Removing the property may shift the offsets of
461 // other nodes and properties but the borrow checker should prevent this function from
462 // being called when FdtNode instances are in use.
463 let ret = unsafe {
464 libfdt_bindgen::fdt_delprop(self.fdt.as_mut_ptr(), self.offset, name.as_ptr())
465 };
466
467 fdt_err_expect_zero(ret)
468 }
469
Pierre-Clément Tosibe3a97b2023-05-19 14:56:23 +0000470 /// Overwrite the given property with FDT_NOP, effectively removing it from the DT.
471 pub fn nop_property(&mut self, name: &CStr) -> Result<()> {
472 // SAFETY - Accesses are constrained to the DT totalsize (validated by ctor) when the
473 // library locates the node's property.
474 let ret = unsafe {
475 libfdt_bindgen::fdt_nop_property(self.fdt.as_mut_ptr(), self.offset, name.as_ptr())
476 };
477
478 fdt_err_expect_zero(ret)
479 }
480
Jiyong Park9c63cd12023-03-21 17:53:07 +0900481 /// Reduce the size of the given property to new_size
482 pub fn trimprop(&mut self, name: &CStr, new_size: usize) -> Result<()> {
483 let (prop, len) =
484 FdtNode::getprop_internal(self.fdt, self.offset, name)?.ok_or(FdtError::NotFound)?;
485 if len == new_size {
486 return Ok(());
487 }
488 if new_size > len {
489 return Err(FdtError::NoSpace);
490 }
491
492 // SAFETY - new_size is smaller than the old size
493 let ret = unsafe {
494 libfdt_bindgen::fdt_setprop(
495 self.fdt.as_mut_ptr(),
496 self.offset,
497 name.as_ptr(),
498 prop.cast::<c_void>(),
499 new_size.try_into().map_err(|_| FdtError::BadValue)?,
500 )
501 };
502
503 fdt_err_expect_zero(ret)
504 }
505
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000506 /// Get reference to the containing device tree.
507 pub fn fdt(&mut self) -> &mut Fdt {
508 self.fdt
509 }
510
511 /// Add a new subnode to the given node and return it as a FdtNodeMut on success.
512 pub fn add_subnode(&'a mut self, name: &CStr) -> Result<Self> {
513 // SAFETY - Accesses are constrained to the DT totalsize (validated by ctor).
514 let ret = unsafe {
515 libfdt_bindgen::fdt_add_subnode(self.fdt.as_mut_ptr(), self.offset, name.as_ptr())
516 };
517
518 Ok(Self { fdt: self.fdt, offset: fdt_err(ret)? })
519 }
520
521 fn parent(&'a self) -> Result<FdtNode<'a>> {
522 // SAFETY - Accesses (read-only) are constrained to the DT totalsize.
523 let ret = unsafe { libfdt_bindgen::fdt_parent_offset(self.fdt.as_ptr(), self.offset) };
524
525 Ok(FdtNode { fdt: &*self.fdt, offset: fdt_err(ret)? })
526 }
Jiyong Park9c63cd12023-03-21 17:53:07 +0900527
528 /// Return the compatible node of the given name that is next to this node
529 pub fn next_compatible(self, compatible: &CStr) -> Result<Option<Self>> {
530 // SAFETY - Accesses (read-only) are constrained to the DT totalsize.
531 let ret = unsafe {
532 libfdt_bindgen::fdt_node_offset_by_compatible(
533 self.fdt.as_ptr(),
534 self.offset,
535 compatible.as_ptr(),
536 )
537 };
538
539 Ok(fdt_err_or_option(ret)?.map(|offset| Self { fdt: self.fdt, offset }))
540 }
541
542 /// Replace this node and its subtree with nop tags, effectively removing it from the tree, and
543 /// then return the next compatible node of the given name.
544 // Side note: without this, filterint out excessive compatible nodes from the DT is impossible.
545 // The reason is that libfdt ensures that the node from where the search for the next
546 // compatible node is started is always a valid one -- except for the special case of offset =
547 // -1 which is to find the first compatible node. So, we can't delete a node and then find the
548 // next compatible node from it.
549 //
550 // We can't do in the opposite direction either. If we call next_compatible to find the next
551 // node, and delete the current node, the Rust borrow checker kicks in. The next node has a
552 // mutable reference to DT, so we can't use current node (which also has a mutable reference to
553 // DT).
554 pub fn delete_and_next_compatible(self, compatible: &CStr) -> Result<Option<Self>> {
555 // SAFETY - Accesses (read-only) are constrained to the DT totalsize.
556 let ret = unsafe {
557 libfdt_bindgen::fdt_node_offset_by_compatible(
558 self.fdt.as_ptr(),
559 self.offset,
560 compatible.as_ptr(),
561 )
562 };
563 let next_offset = fdt_err_or_option(ret)?;
564
565 // SAFETY - fdt_nop_node alter only the bytes in the blob which contain the node and its
566 // properties and subnodes, and will not alter or move any other part of the tree.
567 let ret = unsafe { libfdt_bindgen::fdt_nop_node(self.fdt.as_mut_ptr(), self.offset) };
568 fdt_err_expect_zero(ret)?;
569
570 Ok(next_offset.map(|offset| Self { fdt: self.fdt, offset }))
571 }
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000572}
573
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000574/// Iterator over nodes sharing a same compatible string.
575pub struct CompatibleIterator<'a> {
576 node: FdtNode<'a>,
577 compatible: &'a CStr,
578}
579
580impl<'a> CompatibleIterator<'a> {
581 fn new(fdt: &'a Fdt, compatible: &'a CStr) -> Result<Self> {
582 let node = fdt.root()?;
583 Ok(Self { node, compatible })
584 }
585}
586
587impl<'a> Iterator for CompatibleIterator<'a> {
588 type Item = FdtNode<'a>;
589
590 fn next(&mut self) -> Option<Self::Item> {
591 let next = self.node.next_compatible(self.compatible).ok()?;
592
593 if let Some(node) = next {
594 self.node = node;
595 }
596
597 next
598 }
599}
600
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000601/// Wrapper around low-level libfdt functions.
Alice Wang9d4df702023-05-25 14:14:12 +0000602#[derive(Debug)]
David Brazdil1baa9a92022-06-28 14:47:50 +0100603#[repr(transparent)]
604pub struct Fdt {
Pierre-Clément Tosief2030e2022-11-28 11:21:20 +0000605 buffer: [u8],
David Brazdil1baa9a92022-06-28 14:47:50 +0100606}
607
608impl Fdt {
609 /// Wraps a slice containing a Flattened Device Tree.
610 ///
611 /// Fails if the FDT does not pass validation.
612 pub fn from_slice(fdt: &[u8]) -> Result<&Self> {
613 // SAFETY - The FDT will be validated before it is returned.
614 let fdt = unsafe { Self::unchecked_from_slice(fdt) };
615 fdt.check_full()?;
616 Ok(fdt)
617 }
618
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000619 /// Wraps a mutable slice containing a Flattened Device Tree.
620 ///
621 /// Fails if the FDT does not pass validation.
622 pub fn from_mut_slice(fdt: &mut [u8]) -> Result<&mut Self> {
623 // SAFETY - The FDT will be validated before it is returned.
624 let fdt = unsafe { Self::unchecked_from_mut_slice(fdt) };
625 fdt.check_full()?;
626 Ok(fdt)
627 }
628
Jaewan Kim4cf20aa2023-04-03 10:25:38 +0900629 /// Creates an empty Flattened Device Tree with a mutable slice.
630 pub fn create_empty_tree(fdt: &mut [u8]) -> Result<&mut Self> {
631 // SAFETY - fdt_create_empty_tree() only write within the specified length,
632 // and returns error if buffer was insufficient.
633 // There will be no memory write outside of the given fdt.
634 let ret = unsafe {
635 libfdt_bindgen::fdt_create_empty_tree(
636 fdt.as_mut_ptr().cast::<c_void>(),
637 fdt.len() as i32,
638 )
639 };
640 fdt_err_expect_zero(ret)?;
641
642 // SAFETY - The FDT will be validated before it is returned.
643 let fdt = unsafe { Self::unchecked_from_mut_slice(fdt) };
644 fdt.check_full()?;
645
646 Ok(fdt)
647 }
648
David Brazdil1baa9a92022-06-28 14:47:50 +0100649 /// Wraps a slice containing a Flattened Device Tree.
650 ///
651 /// # Safety
652 ///
653 /// The returned FDT might be invalid, only use on slices containing a valid DT.
654 pub unsafe fn unchecked_from_slice(fdt: &[u8]) -> &Self {
655 mem::transmute::<&[u8], &Self>(fdt)
656 }
657
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000658 /// Wraps a mutable slice containing a Flattened Device Tree.
659 ///
660 /// # Safety
661 ///
662 /// The returned FDT might be invalid, only use on slices containing a valid DT.
663 pub unsafe fn unchecked_from_mut_slice(fdt: &mut [u8]) -> &mut Self {
664 mem::transmute::<&mut [u8], &mut Self>(fdt)
665 }
666
Jiyong Parke9d87e82023-03-21 19:28:40 +0900667 /// Update this FDT from a slice containing another FDT
668 pub fn copy_from_slice(&mut self, new_fdt: &[u8]) -> Result<()> {
669 if self.buffer.len() < new_fdt.len() {
670 Err(FdtError::NoSpace)
671 } else {
672 let totalsize = self.totalsize();
673 self.buffer[..new_fdt.len()].clone_from_slice(new_fdt);
674 // Zeroize the remaining part. We zeroize up to the size of the original DT because
675 // zeroizing the entire buffer (max 2MB) is not necessary and may increase the VM boot
676 // time.
677 self.buffer[new_fdt.len()..max(new_fdt.len(), totalsize)].fill(0_u8);
678 Ok(())
679 }
680 }
681
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000682 /// Make the whole slice containing the DT available to libfdt.
683 pub fn unpack(&mut self) -> Result<()> {
684 // SAFETY - "Opens" the DT in-place (supported use-case) by updating its header and
685 // internal structures to make use of the whole self.fdt slice but performs no accesses
686 // outside of it and leaves the DT in a state that will be detected by other functions.
687 let ret = unsafe {
688 libfdt_bindgen::fdt_open_into(
689 self.as_ptr(),
690 self.as_mut_ptr(),
691 self.capacity().try_into().map_err(|_| FdtError::Internal)?,
692 )
693 };
694 fdt_err_expect_zero(ret)
695 }
696
697 /// Pack the DT to take a minimum amount of memory.
698 ///
699 /// Doesn't shrink the underlying memory slice.
700 pub fn pack(&mut self) -> Result<()> {
701 // SAFETY - "Closes" the DT in-place by updating its header and relocating its structs.
702 let ret = unsafe { libfdt_bindgen::fdt_pack(self.as_mut_ptr()) };
703 fdt_err_expect_zero(ret)
704 }
705
Pierre-Clément Tosi90e19352022-11-21 17:11:48 +0000706 /// Applies a DT overlay on the base DT.
707 ///
708 /// # Safety
709 ///
710 /// On failure, the library corrupts the DT and overlay so both must be discarded.
711 pub unsafe fn apply_overlay<'a>(&'a mut self, overlay: &'a mut Fdt) -> Result<&'a mut Self> {
712 fdt_err_expect_zero(libfdt_bindgen::fdt_overlay_apply(
713 self.as_mut_ptr(),
714 overlay.as_mut_ptr(),
715 ))?;
716 Ok(self)
717 }
718
David Brazdil1baa9a92022-06-28 14:47:50 +0100719 /// Return an iterator of memory banks specified the "/memory" node.
720 ///
721 /// NOTE: This does not support individual "/memory@XXXX" banks.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000722 pub fn memory(&self) -> Result<Option<MemRegIterator>> {
David Brazdil1baa9a92022-06-28 14:47:50 +0100723 let memory = CStr::from_bytes_with_nul(b"/memory\0").unwrap();
724 let device_type = CStr::from_bytes_with_nul(b"memory\0").unwrap();
725
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000726 if let Some(node) = self.node(memory)? {
727 if node.device_type()? != Some(device_type) {
728 return Err(FdtError::BadValue);
729 }
730 let reg = node.reg()?.ok_or(FdtError::BadValue)?;
David Brazdil1baa9a92022-06-28 14:47:50 +0100731
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000732 Ok(Some(MemRegIterator::new(reg)))
733 } else {
734 Ok(None)
735 }
David Brazdil1baa9a92022-06-28 14:47:50 +0100736 }
737
738 /// Retrieve the standard /chosen node.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000739 pub fn chosen(&self) -> Result<Option<FdtNode>> {
David Brazdil1baa9a92022-06-28 14:47:50 +0100740 self.node(CStr::from_bytes_with_nul(b"/chosen\0").unwrap())
741 }
742
Pierre-Clément Tosi4ba79662023-02-13 11:22:41 +0000743 /// Retrieve the standard /chosen node as mutable.
744 pub fn chosen_mut(&mut self) -> Result<Option<FdtNodeMut>> {
745 self.node_mut(CStr::from_bytes_with_nul(b"/chosen\0").unwrap())
746 }
747
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000748 /// Get the root node of the tree.
749 pub fn root(&self) -> Result<FdtNode> {
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000750 self.node(CStr::from_bytes_with_nul(b"/\0").unwrap())?.ok_or(FdtError::Internal)
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000751 }
752
David Brazdil1baa9a92022-06-28 14:47:50 +0100753 /// Find a tree node by its full path.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000754 pub fn node(&self, path: &CStr) -> Result<Option<FdtNode>> {
755 Ok(self.path_offset(path)?.map(|offset| FdtNode { fdt: self, offset }))
David Brazdil1baa9a92022-06-28 14:47:50 +0100756 }
757
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000758 /// Iterate over nodes with a given compatible string.
759 pub fn compatible_nodes<'a>(&'a self, compatible: &'a CStr) -> Result<CompatibleIterator<'a>> {
760 CompatibleIterator::new(self, compatible)
761 }
762
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000763 /// Get the mutable root node of the tree.
764 pub fn root_mut(&mut self) -> Result<FdtNodeMut> {
765 self.node_mut(CStr::from_bytes_with_nul(b"/\0").unwrap())?.ok_or(FdtError::Internal)
766 }
767
768 /// Find a mutable tree node by its full path.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000769 pub fn node_mut(&mut self, path: &CStr) -> Result<Option<FdtNodeMut>> {
770 Ok(self.path_offset(path)?.map(|offset| FdtNodeMut { fdt: self, offset }))
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000771 }
772
Pierre-Clément Tosidb74cb12022-12-08 13:56:25 +0000773 /// Return the device tree as a slice (may be smaller than the containing buffer).
774 pub fn as_slice(&self) -> &[u8] {
775 &self.buffer[..self.totalsize()]
776 }
777
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000778 fn path_offset(&self, path: &CStr) -> Result<Option<c_int>> {
David Brazdil1baa9a92022-06-28 14:47:50 +0100779 let len = path.to_bytes().len().try_into().map_err(|_| FdtError::BadPath)?;
780 // SAFETY - Accesses are constrained to the DT totalsize (validated by ctor) and the
781 // function respects the passed number of characters.
782 let ret = unsafe {
783 // *_namelen functions don't include the trailing nul terminator in 'len'.
784 libfdt_bindgen::fdt_path_offset_namelen(self.as_ptr(), path.as_ptr(), len)
785 };
786
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000787 fdt_err_or_option(ret)
David Brazdil1baa9a92022-06-28 14:47:50 +0100788 }
789
790 fn check_full(&self) -> Result<()> {
Pierre-Clément Tosief2030e2022-11-28 11:21:20 +0000791 let len = self.buffer.len();
David Brazdil1baa9a92022-06-28 14:47:50 +0100792 // SAFETY - Only performs read accesses within the limits of the slice. If successful, this
793 // call guarantees to other unsafe calls that the header contains a valid totalsize (w.r.t.
794 // 'len' i.e. the self.fdt slice) that those C functions can use to perform bounds
795 // checking. The library doesn't maintain an internal state (such as pointers) between
796 // calls as it expects the client code to keep track of the objects (DT, nodes, ...).
797 let ret = unsafe { libfdt_bindgen::fdt_check_full(self.as_ptr(), len) };
798 fdt_err_expect_zero(ret)
799 }
800
Pierre-Clément Tosi8036b4f2023-02-17 10:31:31 +0000801 /// Return a shared pointer to the device tree.
802 pub fn as_ptr(&self) -> *const c_void {
Pierre-Clément Tosi0dcc75e2023-05-02 13:43:55 +0000803 self.buffer.as_ptr().cast::<_>()
David Brazdil1baa9a92022-06-28 14:47:50 +0100804 }
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000805
806 fn as_mut_ptr(&mut self) -> *mut c_void {
Pierre-Clément Tosi0dcc75e2023-05-02 13:43:55 +0000807 self.buffer.as_mut_ptr().cast::<_>()
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000808 }
809
810 fn capacity(&self) -> usize {
Pierre-Clément Tosief2030e2022-11-28 11:21:20 +0000811 self.buffer.len()
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000812 }
Pierre-Clément Tosidb74cb12022-12-08 13:56:25 +0000813
814 fn header(&self) -> &libfdt_bindgen::fdt_header {
Pierre-Clément Tosi0dcc75e2023-05-02 13:43:55 +0000815 let p = self.as_ptr().cast::<_>();
Pierre-Clément Tosidb74cb12022-12-08 13:56:25 +0000816 // SAFETY - A valid FDT (verified by constructor) must contain a valid fdt_header.
Pierre-Clément Tosi0dcc75e2023-05-02 13:43:55 +0000817 unsafe { &*p }
Pierre-Clément Tosidb74cb12022-12-08 13:56:25 +0000818 }
819
820 fn totalsize(&self) -> usize {
821 u32::from_be(self.header().totalsize) as usize
822 }
David Brazdil1baa9a92022-06-28 14:47:50 +0100823}