blob: 19ce0f7205468d1676bacfe3f3096aa1f9c7f45c [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;
Alice Wang2422bdc2023-06-12 08:37:55 +000028use core::ops::Range;
David Brazdil1baa9a92022-06-28 14:47:50 +010029use core::result;
Pierre-Clément Tosic27c4272023-05-19 15:46:26 +000030use zerocopy::AsBytes as _;
David Brazdil1baa9a92022-06-28 14:47:50 +010031
32/// Error type corresponding to libfdt error codes.
33#[derive(Clone, Copy, Debug, Eq, PartialEq)]
34pub enum FdtError {
35 /// FDT_ERR_NOTFOUND
36 NotFound,
37 /// FDT_ERR_EXISTS
38 Exists,
39 /// FDT_ERR_NOSPACE
40 NoSpace,
41 /// FDT_ERR_BADOFFSET
42 BadOffset,
43 /// FDT_ERR_BADPATH
44 BadPath,
45 /// FDT_ERR_BADPHANDLE
46 BadPhandle,
47 /// FDT_ERR_BADSTATE
48 BadState,
49 /// FDT_ERR_TRUNCATED
50 Truncated,
51 /// FDT_ERR_BADMAGIC
52 BadMagic,
53 /// FDT_ERR_BADVERSION
54 BadVersion,
55 /// FDT_ERR_BADSTRUCTURE
56 BadStructure,
57 /// FDT_ERR_BADLAYOUT
58 BadLayout,
59 /// FDT_ERR_INTERNAL
60 Internal,
61 /// FDT_ERR_BADNCELLS
62 BadNCells,
63 /// FDT_ERR_BADVALUE
64 BadValue,
65 /// FDT_ERR_BADOVERLAY
66 BadOverlay,
67 /// FDT_ERR_NOPHANDLES
68 NoPhandles,
69 /// FDT_ERR_BADFLAGS
70 BadFlags,
71 /// FDT_ERR_ALIGNMENT
72 Alignment,
73 /// Unexpected error code
74 Unknown(i32),
75}
76
77impl fmt::Display for FdtError {
78 /// Prints error messages from libfdt.h documentation.
79 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
80 match self {
81 Self::NotFound => write!(f, "The requested node or property does not exist"),
82 Self::Exists => write!(f, "Attempted to create an existing node or property"),
83 Self::NoSpace => write!(f, "Insufficient buffer space to contain the expanded tree"),
84 Self::BadOffset => write!(f, "Structure block offset is out-of-bounds or invalid"),
85 Self::BadPath => write!(f, "Badly formatted path"),
86 Self::BadPhandle => write!(f, "Invalid phandle length or value"),
87 Self::BadState => write!(f, "Received incomplete device tree"),
88 Self::Truncated => write!(f, "Device tree or sub-block is improperly terminated"),
89 Self::BadMagic => write!(f, "Device tree header missing its magic number"),
90 Self::BadVersion => write!(f, "Device tree has a version which can't be handled"),
91 Self::BadStructure => write!(f, "Device tree has a corrupt structure block"),
92 Self::BadLayout => write!(f, "Device tree sub-blocks in unsupported order"),
93 Self::Internal => write!(f, "libfdt has failed an internal assertion"),
94 Self::BadNCells => write!(f, "Bad format or value of #address-cells or #size-cells"),
95 Self::BadValue => write!(f, "Unexpected property value"),
96 Self::BadOverlay => write!(f, "Overlay cannot be applied"),
97 Self::NoPhandles => write!(f, "Device tree doesn't have any phandle available anymore"),
98 Self::BadFlags => write!(f, "Invalid flag or invalid combination of flags"),
99 Self::Alignment => write!(f, "Device tree base address is not 8-byte aligned"),
100 Self::Unknown(e) => write!(f, "Unknown libfdt error '{e}'"),
101 }
102 }
103}
104
105/// Result type with FdtError enum.
106pub type Result<T> = result::Result<T, FdtError>;
107
108fn fdt_err(val: c_int) -> Result<c_int> {
109 if val >= 0 {
110 Ok(val)
111 } else {
112 Err(match -val as _ {
113 libfdt_bindgen::FDT_ERR_NOTFOUND => FdtError::NotFound,
114 libfdt_bindgen::FDT_ERR_EXISTS => FdtError::Exists,
115 libfdt_bindgen::FDT_ERR_NOSPACE => FdtError::NoSpace,
116 libfdt_bindgen::FDT_ERR_BADOFFSET => FdtError::BadOffset,
117 libfdt_bindgen::FDT_ERR_BADPATH => FdtError::BadPath,
118 libfdt_bindgen::FDT_ERR_BADPHANDLE => FdtError::BadPhandle,
119 libfdt_bindgen::FDT_ERR_BADSTATE => FdtError::BadState,
120 libfdt_bindgen::FDT_ERR_TRUNCATED => FdtError::Truncated,
121 libfdt_bindgen::FDT_ERR_BADMAGIC => FdtError::BadMagic,
122 libfdt_bindgen::FDT_ERR_BADVERSION => FdtError::BadVersion,
123 libfdt_bindgen::FDT_ERR_BADSTRUCTURE => FdtError::BadStructure,
124 libfdt_bindgen::FDT_ERR_BADLAYOUT => FdtError::BadLayout,
125 libfdt_bindgen::FDT_ERR_INTERNAL => FdtError::Internal,
126 libfdt_bindgen::FDT_ERR_BADNCELLS => FdtError::BadNCells,
127 libfdt_bindgen::FDT_ERR_BADVALUE => FdtError::BadValue,
128 libfdt_bindgen::FDT_ERR_BADOVERLAY => FdtError::BadOverlay,
129 libfdt_bindgen::FDT_ERR_NOPHANDLES => FdtError::NoPhandles,
130 libfdt_bindgen::FDT_ERR_BADFLAGS => FdtError::BadFlags,
131 libfdt_bindgen::FDT_ERR_ALIGNMENT => FdtError::Alignment,
132 _ => FdtError::Unknown(val),
133 })
134 }
135}
136
137fn fdt_err_expect_zero(val: c_int) -> Result<()> {
138 match fdt_err(val)? {
139 0 => Ok(()),
140 _ => Err(FdtError::Unknown(val)),
141 }
142}
143
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000144fn fdt_err_or_option(val: c_int) -> Result<Option<c_int>> {
145 match fdt_err(val) {
146 Ok(val) => Ok(Some(val)),
147 Err(FdtError::NotFound) => Ok(None),
148 Err(e) => Err(e),
149 }
150}
151
David Brazdil1baa9a92022-06-28 14:47:50 +0100152/// Value of a #address-cells property.
Andrew Walbranb39e6922022-12-05 17:01:20 +0000153#[derive(Copy, Clone, Debug, Eq, PartialEq)]
David Brazdil1baa9a92022-06-28 14:47:50 +0100154enum AddrCells {
155 Single = 1,
156 Double = 2,
Andrew Walbranb39e6922022-12-05 17:01:20 +0000157 Triple = 3,
David Brazdil1baa9a92022-06-28 14:47:50 +0100158}
159
160impl TryFrom<c_int> for AddrCells {
161 type Error = FdtError;
162
163 fn try_from(res: c_int) -> Result<Self> {
164 match fdt_err(res)? {
165 x if x == Self::Single as c_int => Ok(Self::Single),
166 x if x == Self::Double as c_int => Ok(Self::Double),
Andrew Walbranb39e6922022-12-05 17:01:20 +0000167 x if x == Self::Triple as c_int => Ok(Self::Triple),
David Brazdil1baa9a92022-06-28 14:47:50 +0100168 _ => Err(FdtError::BadNCells),
169 }
170 }
171}
172
173/// Value of a #size-cells property.
Andrew Walbranb39e6922022-12-05 17:01:20 +0000174#[derive(Copy, Clone, Debug, Eq, PartialEq)]
David Brazdil1baa9a92022-06-28 14:47:50 +0100175enum SizeCells {
176 None = 0,
177 Single = 1,
178 Double = 2,
179}
180
181impl TryFrom<c_int> for SizeCells {
182 type Error = FdtError;
183
184 fn try_from(res: c_int) -> Result<Self> {
185 match fdt_err(res)? {
186 x if x == Self::None as c_int => Ok(Self::None),
187 x if x == Self::Single as c_int => Ok(Self::Single),
188 x if x == Self::Double as c_int => Ok(Self::Double),
189 _ => Err(FdtError::BadNCells),
190 }
191 }
192}
193
David Brazdil1baa9a92022-06-28 14:47:50 +0100194/// DT node.
Alice Wang9d4df702023-05-25 14:14:12 +0000195#[derive(Clone, Copy, Debug)]
David Brazdil1baa9a92022-06-28 14:47:50 +0100196pub struct FdtNode<'a> {
197 fdt: &'a Fdt,
198 offset: c_int,
199}
200
201impl<'a> FdtNode<'a> {
Jiyong Park9c63cd12023-03-21 17:53:07 +0900202 /// Create immutable node from a mutable node at the same offset
203 pub fn from_mut(other: &'a FdtNodeMut) -> Self {
204 FdtNode { fdt: other.fdt, offset: other.offset }
205 }
David Brazdil1baa9a92022-06-28 14:47:50 +0100206 /// Find parent node.
207 pub fn parent(&self) -> Result<Self> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000208 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
David Brazdil1baa9a92022-06-28 14:47:50 +0100209 let ret = unsafe { libfdt_bindgen::fdt_parent_offset(self.fdt.as_ptr(), self.offset) };
210
211 Ok(Self { fdt: self.fdt, offset: fdt_err(ret)? })
212 }
213
214 /// Retrieve the standard (deprecated) device_type <string> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000215 pub fn device_type(&self) -> Result<Option<&CStr>> {
David Brazdil1baa9a92022-06-28 14:47:50 +0100216 self.getprop_str(CStr::from_bytes_with_nul(b"device_type\0").unwrap())
217 }
218
219 /// Retrieve the standard reg <prop-encoded-array> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000220 pub fn reg(&self) -> Result<Option<RegIterator<'a>>> {
221 let reg = CStr::from_bytes_with_nul(b"reg\0").unwrap();
David Brazdil1baa9a92022-06-28 14:47:50 +0100222
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000223 if let Some(cells) = self.getprop_cells(reg)? {
224 let parent = self.parent()?;
David Brazdil1baa9a92022-06-28 14:47:50 +0100225
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000226 let addr_cells = parent.address_cells()?;
227 let size_cells = parent.size_cells()?;
228
229 Ok(Some(RegIterator::new(cells, addr_cells, size_cells)))
230 } else {
231 Ok(None)
232 }
David Brazdil1baa9a92022-06-28 14:47:50 +0100233 }
234
Andrew Walbranb39e6922022-12-05 17:01:20 +0000235 /// Retrieves the standard ranges property.
236 pub fn ranges<A, P, S>(&self) -> Result<Option<RangesIterator<'a, A, P, S>>> {
237 let ranges = CStr::from_bytes_with_nul(b"ranges\0").unwrap();
238 if let Some(cells) = self.getprop_cells(ranges)? {
239 let parent = self.parent()?;
240 let addr_cells = self.address_cells()?;
241 let parent_addr_cells = parent.address_cells()?;
242 let size_cells = self.size_cells()?;
243 Ok(Some(RangesIterator::<A, P, S>::new(
244 cells,
245 addr_cells,
246 parent_addr_cells,
247 size_cells,
248 )))
249 } else {
250 Ok(None)
251 }
252 }
253
Jaewan Kimaa638702023-09-19 13:34:01 +0900254 /// Returns the node name.
255 pub fn name(&self) -> Result<&'a CStr> {
256 let mut len: c_int = 0;
257 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor). On success, the
258 // function returns valid null terminating string and otherwise returned values are dropped.
259 let name = unsafe { libfdt_bindgen::fdt_get_name(self.fdt.as_ptr(), self.offset, &mut len) }
260 as *const c_void;
261 let len = usize::try_from(fdt_err(len)?).unwrap();
262 let name = self.fdt.get_from_ptr(name, len + 1)?;
263 CStr::from_bytes_with_nul(name).map_err(|_| FdtError::Internal)
264 }
265
David Brazdil1baa9a92022-06-28 14:47:50 +0100266 /// Retrieve the value of a given <string> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000267 pub fn getprop_str(&self, name: &CStr) -> Result<Option<&CStr>> {
268 let value = if let Some(bytes) = self.getprop(name)? {
269 Some(CStr::from_bytes_with_nul(bytes).map_err(|_| FdtError::BadValue)?)
270 } else {
271 None
272 };
273 Ok(value)
David Brazdil1baa9a92022-06-28 14:47:50 +0100274 }
275
276 /// Retrieve the value of a given property as an array of cells.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000277 pub fn getprop_cells(&self, name: &CStr) -> Result<Option<CellIterator<'a>>> {
278 if let Some(cells) = self.getprop(name)? {
279 Ok(Some(CellIterator::new(cells)))
280 } else {
281 Ok(None)
282 }
David Brazdil1baa9a92022-06-28 14:47:50 +0100283 }
284
285 /// Retrieve the value of a given <u32> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000286 pub fn getprop_u32(&self, name: &CStr) -> Result<Option<u32>> {
287 let value = if let Some(bytes) = self.getprop(name)? {
288 Some(u32::from_be_bytes(bytes.try_into().map_err(|_| FdtError::BadValue)?))
289 } else {
290 None
291 };
292 Ok(value)
David Brazdil1baa9a92022-06-28 14:47:50 +0100293 }
294
295 /// Retrieve the value of a given <u64> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000296 pub fn getprop_u64(&self, name: &CStr) -> Result<Option<u64>> {
297 let value = if let Some(bytes) = self.getprop(name)? {
298 Some(u64::from_be_bytes(bytes.try_into().map_err(|_| FdtError::BadValue)?))
299 } else {
300 None
301 };
302 Ok(value)
David Brazdil1baa9a92022-06-28 14:47:50 +0100303 }
304
305 /// Retrieve the value of a given property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000306 pub fn getprop(&self, name: &CStr) -> Result<Option<&'a [u8]>> {
Jiyong Park9c63cd12023-03-21 17:53:07 +0900307 if let Some((prop, len)) = Self::getprop_internal(self.fdt, self.offset, name)? {
Jaewan Kimaa638702023-09-19 13:34:01 +0900308 Ok(Some(self.fdt.get_from_ptr(prop, len)?))
Jiyong Park9c63cd12023-03-21 17:53:07 +0900309 } else {
310 Ok(None) // property was not found
311 }
312 }
313
314 /// Return the pointer and size of the property named `name`, in a node at offset `offset`, in
315 /// a device tree `fdt`. The pointer is guaranteed to be non-null, in which case error returns.
316 fn getprop_internal(
317 fdt: &'a Fdt,
318 offset: c_int,
319 name: &CStr,
320 ) -> Result<Option<(*const c_void, usize)>> {
David Brazdil1baa9a92022-06-28 14:47:50 +0100321 let mut len: i32 = 0;
Andrew Walbran84b9a232023-07-05 14:01:40 +0000322 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor) and the
David Brazdil1baa9a92022-06-28 14:47:50 +0100323 // function respects the passed number of characters.
324 let prop = unsafe {
325 libfdt_bindgen::fdt_getprop_namelen(
Jiyong Park9c63cd12023-03-21 17:53:07 +0900326 fdt.as_ptr(),
327 offset,
David Brazdil1baa9a92022-06-28 14:47:50 +0100328 name.as_ptr(),
329 // *_namelen functions don't include the trailing nul terminator in 'len'.
330 name.to_bytes().len().try_into().map_err(|_| FdtError::BadPath)?,
331 &mut len as *mut i32,
332 )
333 } as *const u8;
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000334
335 let Some(len) = fdt_err_or_option(len)? else {
336 return Ok(None); // Property was not found.
337 };
Jaewan Kimaa638702023-09-19 13:34:01 +0900338 let len = usize::try_from(len).unwrap();
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000339
David Brazdil1baa9a92022-06-28 14:47:50 +0100340 if prop.is_null() {
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000341 // We expected an error code in len but still received a valid value?!
342 return Err(FdtError::Internal);
David Brazdil1baa9a92022-06-28 14:47:50 +0100343 }
Jiyong Park9c63cd12023-03-21 17:53:07 +0900344 Ok(Some((prop.cast::<c_void>(), len)))
David Brazdil1baa9a92022-06-28 14:47:50 +0100345 }
346
347 /// Get reference to the containing device tree.
348 pub fn fdt(&self) -> &Fdt {
349 self.fdt
350 }
351
Alice Wang474c0ee2023-09-14 12:52:33 +0000352 /// Returns the compatible node of the given name that is next after this node.
353 pub fn next_compatible(self, compatible: &CStr) -> Result<Option<Self>> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000354 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000355 let ret = unsafe {
356 libfdt_bindgen::fdt_node_offset_by_compatible(
357 self.fdt.as_ptr(),
358 self.offset,
359 compatible.as_ptr(),
360 )
361 };
362
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000363 Ok(fdt_err_or_option(ret)?.map(|offset| Self { fdt: self.fdt, offset }))
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000364 }
365
Alice Wang474c0ee2023-09-14 12:52:33 +0000366 /// Returns the first range of `reg` in this node.
367 pub fn first_reg(&self) -> Result<Reg<u64>> {
368 self.reg()?.ok_or(FdtError::NotFound)?.next().ok_or(FdtError::NotFound)
369 }
370
David Brazdil1baa9a92022-06-28 14:47:50 +0100371 fn address_cells(&self) -> Result<AddrCells> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000372 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
David Brazdil1baa9a92022-06-28 14:47:50 +0100373 unsafe { libfdt_bindgen::fdt_address_cells(self.fdt.as_ptr(), self.offset) }
374 .try_into()
375 .map_err(|_| FdtError::Internal)
376 }
377
378 fn size_cells(&self) -> Result<SizeCells> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000379 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
David Brazdil1baa9a92022-06-28 14:47:50 +0100380 unsafe { libfdt_bindgen::fdt_size_cells(self.fdt.as_ptr(), self.offset) }
381 .try_into()
382 .map_err(|_| FdtError::Internal)
383 }
384}
385
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000386/// Mutable FDT node.
387pub struct FdtNodeMut<'a> {
388 fdt: &'a mut Fdt,
389 offset: c_int,
390}
391
392impl<'a> FdtNodeMut<'a> {
393 /// Append a property name-value (possibly empty) pair to the given node.
394 pub fn appendprop<T: AsRef<[u8]>>(&mut self, name: &CStr, value: &T) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000395 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000396 let ret = unsafe {
397 libfdt_bindgen::fdt_appendprop(
398 self.fdt.as_mut_ptr(),
399 self.offset,
400 name.as_ptr(),
401 value.as_ref().as_ptr().cast::<c_void>(),
402 value.as_ref().len().try_into().map_err(|_| FdtError::BadValue)?,
403 )
404 };
405
406 fdt_err_expect_zero(ret)
407 }
408
409 /// Append a (address, size) pair property to the given node.
410 pub fn appendprop_addrrange(&mut self, name: &CStr, addr: u64, size: u64) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000411 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000412 let ret = unsafe {
413 libfdt_bindgen::fdt_appendprop_addrrange(
414 self.fdt.as_mut_ptr(),
415 self.parent()?.offset,
416 self.offset,
417 name.as_ptr(),
418 addr,
419 size,
420 )
421 };
422
423 fdt_err_expect_zero(ret)
424 }
425
Jaewan Kimba8929b2023-01-13 11:13:29 +0900426 /// Create or change a property name-value pair to the given node.
427 pub fn setprop(&mut self, name: &CStr, value: &[u8]) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000428 // SAFETY: New value size is constrained to the DT totalsize
Jaewan Kimba8929b2023-01-13 11:13:29 +0900429 // (validated by underlying libfdt).
430 let ret = unsafe {
431 libfdt_bindgen::fdt_setprop(
432 self.fdt.as_mut_ptr(),
433 self.offset,
434 name.as_ptr(),
435 value.as_ptr().cast::<c_void>(),
436 value.len().try_into().map_err(|_| FdtError::BadValue)?,
437 )
438 };
439
440 fdt_err_expect_zero(ret)
441 }
442
Jiyong Park9c63cd12023-03-21 17:53:07 +0900443 /// Replace the value of the given property with the given value, and ensure that the given
444 /// value has the same length as the current value length
445 pub fn setprop_inplace(&mut self, name: &CStr, value: &[u8]) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000446 // SAFETY: fdt size is not altered
Jiyong Park9c63cd12023-03-21 17:53:07 +0900447 let ret = unsafe {
448 libfdt_bindgen::fdt_setprop_inplace(
449 self.fdt.as_mut_ptr(),
450 self.offset,
451 name.as_ptr(),
452 value.as_ptr().cast::<c_void>(),
453 value.len().try_into().map_err(|_| FdtError::BadValue)?,
454 )
455 };
456
457 fdt_err_expect_zero(ret)
458 }
459
Pierre-Clément Tosic27c4272023-05-19 15:46:26 +0000460 /// Replace the value of the given (address, size) pair property with the given value, and
461 /// ensure that the given value has the same length as the current value length
462 pub fn setprop_addrrange_inplace(&mut self, name: &CStr, addr: u64, size: u64) -> Result<()> {
463 let pair = [addr.to_be(), size.to_be()];
464 self.setprop_inplace(name, pair.as_bytes())
465 }
466
Pierre-Clément Tosi4ba79662023-02-13 11:22:41 +0000467 /// Create or change a flag-like empty property.
468 pub fn setprop_empty(&mut self, name: &CStr) -> Result<()> {
469 self.setprop(name, &[])
470 }
471
472 /// Delete the given property.
473 pub fn delprop(&mut self, name: &CStr) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000474 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor) when the
Pierre-Clément Tosi4ba79662023-02-13 11:22:41 +0000475 // library locates the node's property. Removing the property may shift the offsets of
476 // other nodes and properties but the borrow checker should prevent this function from
477 // being called when FdtNode instances are in use.
478 let ret = unsafe {
479 libfdt_bindgen::fdt_delprop(self.fdt.as_mut_ptr(), self.offset, name.as_ptr())
480 };
481
482 fdt_err_expect_zero(ret)
483 }
484
Pierre-Clément Tosibe3a97b2023-05-19 14:56:23 +0000485 /// Overwrite the given property with FDT_NOP, effectively removing it from the DT.
486 pub fn nop_property(&mut self, name: &CStr) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000487 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor) when the
Pierre-Clément Tosibe3a97b2023-05-19 14:56:23 +0000488 // library locates the node's property.
489 let ret = unsafe {
490 libfdt_bindgen::fdt_nop_property(self.fdt.as_mut_ptr(), self.offset, name.as_ptr())
491 };
492
493 fdt_err_expect_zero(ret)
494 }
495
Jiyong Park9c63cd12023-03-21 17:53:07 +0900496 /// Reduce the size of the given property to new_size
497 pub fn trimprop(&mut self, name: &CStr, new_size: usize) -> Result<()> {
498 let (prop, len) =
499 FdtNode::getprop_internal(self.fdt, self.offset, name)?.ok_or(FdtError::NotFound)?;
500 if len == new_size {
501 return Ok(());
502 }
503 if new_size > len {
504 return Err(FdtError::NoSpace);
505 }
506
Andrew Walbran84b9a232023-07-05 14:01:40 +0000507 // SAFETY: new_size is smaller than the old size
Jiyong Park9c63cd12023-03-21 17:53:07 +0900508 let ret = unsafe {
509 libfdt_bindgen::fdt_setprop(
510 self.fdt.as_mut_ptr(),
511 self.offset,
512 name.as_ptr(),
513 prop.cast::<c_void>(),
514 new_size.try_into().map_err(|_| FdtError::BadValue)?,
515 )
516 };
517
518 fdt_err_expect_zero(ret)
519 }
520
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000521 /// Get reference to the containing device tree.
522 pub fn fdt(&mut self) -> &mut Fdt {
523 self.fdt
524 }
525
526 /// Add a new subnode to the given node and return it as a FdtNodeMut on success.
527 pub fn add_subnode(&'a mut self, name: &CStr) -> Result<Self> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000528 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000529 let ret = unsafe {
530 libfdt_bindgen::fdt_add_subnode(self.fdt.as_mut_ptr(), self.offset, name.as_ptr())
531 };
532
533 Ok(Self { fdt: self.fdt, offset: fdt_err(ret)? })
534 }
535
536 fn parent(&'a self) -> Result<FdtNode<'a>> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000537 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000538 let ret = unsafe { libfdt_bindgen::fdt_parent_offset(self.fdt.as_ptr(), self.offset) };
539
540 Ok(FdtNode { fdt: &*self.fdt, offset: fdt_err(ret)? })
541 }
Jiyong Park9c63cd12023-03-21 17:53:07 +0900542
Alice Wang474c0ee2023-09-14 12:52:33 +0000543 /// Returns the compatible node of the given name that is next after this node
Jiyong Park9c63cd12023-03-21 17:53:07 +0900544 pub fn next_compatible(self, compatible: &CStr) -> Result<Option<Self>> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000545 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
Jiyong Park9c63cd12023-03-21 17:53:07 +0900546 let ret = unsafe {
547 libfdt_bindgen::fdt_node_offset_by_compatible(
548 self.fdt.as_ptr(),
549 self.offset,
550 compatible.as_ptr(),
551 )
552 };
553
554 Ok(fdt_err_or_option(ret)?.map(|offset| Self { fdt: self.fdt, offset }))
555 }
556
557 /// Replace this node and its subtree with nop tags, effectively removing it from the tree, and
558 /// then return the next compatible node of the given name.
559 // Side note: without this, filterint out excessive compatible nodes from the DT is impossible.
560 // The reason is that libfdt ensures that the node from where the search for the next
561 // compatible node is started is always a valid one -- except for the special case of offset =
562 // -1 which is to find the first compatible node. So, we can't delete a node and then find the
563 // next compatible node from it.
564 //
565 // We can't do in the opposite direction either. If we call next_compatible to find the next
566 // node, and delete the current node, the Rust borrow checker kicks in. The next node has a
567 // mutable reference to DT, so we can't use current node (which also has a mutable reference to
568 // DT).
569 pub fn delete_and_next_compatible(self, compatible: &CStr) -> Result<Option<Self>> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000570 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
Jiyong Park9c63cd12023-03-21 17:53:07 +0900571 let ret = unsafe {
572 libfdt_bindgen::fdt_node_offset_by_compatible(
573 self.fdt.as_ptr(),
574 self.offset,
575 compatible.as_ptr(),
576 )
577 };
578 let next_offset = fdt_err_or_option(ret)?;
579
Andrew Walbran84b9a232023-07-05 14:01:40 +0000580 // SAFETY: fdt_nop_node alter only the bytes in the blob which contain the node and its
Jiyong Park9c63cd12023-03-21 17:53:07 +0900581 // properties and subnodes, and will not alter or move any other part of the tree.
582 let ret = unsafe { libfdt_bindgen::fdt_nop_node(self.fdt.as_mut_ptr(), self.offset) };
583 fdt_err_expect_zero(ret)?;
584
585 Ok(next_offset.map(|offset| Self { fdt: self.fdt, offset }))
586 }
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000587}
588
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000589/// Iterator over nodes sharing a same compatible string.
590pub struct CompatibleIterator<'a> {
591 node: FdtNode<'a>,
592 compatible: &'a CStr,
593}
594
595impl<'a> CompatibleIterator<'a> {
596 fn new(fdt: &'a Fdt, compatible: &'a CStr) -> Result<Self> {
597 let node = fdt.root()?;
598 Ok(Self { node, compatible })
599 }
600}
601
602impl<'a> Iterator for CompatibleIterator<'a> {
603 type Item = FdtNode<'a>;
604
605 fn next(&mut self) -> Option<Self::Item> {
606 let next = self.node.next_compatible(self.compatible).ok()?;
607
608 if let Some(node) = next {
609 self.node = node;
610 }
611
612 next
613 }
614}
615
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000616/// Wrapper around low-level libfdt functions.
Alice Wang9d4df702023-05-25 14:14:12 +0000617#[derive(Debug)]
David Brazdil1baa9a92022-06-28 14:47:50 +0100618#[repr(transparent)]
619pub struct Fdt {
Pierre-Clément Tosief2030e2022-11-28 11:21:20 +0000620 buffer: [u8],
David Brazdil1baa9a92022-06-28 14:47:50 +0100621}
622
623impl Fdt {
624 /// Wraps a slice containing a Flattened Device Tree.
625 ///
626 /// Fails if the FDT does not pass validation.
627 pub fn from_slice(fdt: &[u8]) -> Result<&Self> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000628 // SAFETY: The FDT will be validated before it is returned.
David Brazdil1baa9a92022-06-28 14:47:50 +0100629 let fdt = unsafe { Self::unchecked_from_slice(fdt) };
630 fdt.check_full()?;
631 Ok(fdt)
632 }
633
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000634 /// Wraps a mutable slice containing a Flattened Device Tree.
635 ///
636 /// Fails if the FDT does not pass validation.
637 pub fn from_mut_slice(fdt: &mut [u8]) -> Result<&mut Self> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000638 // SAFETY: The FDT will be validated before it is returned.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000639 let fdt = unsafe { Self::unchecked_from_mut_slice(fdt) };
640 fdt.check_full()?;
641 Ok(fdt)
642 }
643
Jaewan Kim4cf20aa2023-04-03 10:25:38 +0900644 /// Creates an empty Flattened Device Tree with a mutable slice.
645 pub fn create_empty_tree(fdt: &mut [u8]) -> Result<&mut Self> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000646 // SAFETY: fdt_create_empty_tree() only write within the specified length,
Jaewan Kim4cf20aa2023-04-03 10:25:38 +0900647 // and returns error if buffer was insufficient.
648 // There will be no memory write outside of the given fdt.
649 let ret = unsafe {
650 libfdt_bindgen::fdt_create_empty_tree(
651 fdt.as_mut_ptr().cast::<c_void>(),
652 fdt.len() as i32,
653 )
654 };
655 fdt_err_expect_zero(ret)?;
656
Andrew Walbran84b9a232023-07-05 14:01:40 +0000657 // SAFETY: The FDT will be validated before it is returned.
Jaewan Kim4cf20aa2023-04-03 10:25:38 +0900658 let fdt = unsafe { Self::unchecked_from_mut_slice(fdt) };
659 fdt.check_full()?;
660
661 Ok(fdt)
662 }
663
David Brazdil1baa9a92022-06-28 14:47:50 +0100664 /// Wraps a slice containing a Flattened Device Tree.
665 ///
666 /// # Safety
667 ///
668 /// The returned FDT might be invalid, only use on slices containing a valid DT.
669 pub unsafe fn unchecked_from_slice(fdt: &[u8]) -> &Self {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000670 // SAFETY: Fdt is a wrapper around a [u8], so the transmute is valid. The caller is
671 // responsible for ensuring that it is actually a valid FDT.
672 unsafe { mem::transmute::<&[u8], &Self>(fdt) }
David Brazdil1baa9a92022-06-28 14:47:50 +0100673 }
674
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000675 /// Wraps a mutable slice containing a Flattened Device Tree.
676 ///
677 /// # Safety
678 ///
679 /// The returned FDT might be invalid, only use on slices containing a valid DT.
680 pub unsafe fn unchecked_from_mut_slice(fdt: &mut [u8]) -> &mut Self {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000681 // SAFETY: Fdt is a wrapper around a [u8], so the transmute is valid. The caller is
682 // responsible for ensuring that it is actually a valid FDT.
683 unsafe { mem::transmute::<&mut [u8], &mut Self>(fdt) }
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000684 }
685
Jiyong Parke9d87e82023-03-21 19:28:40 +0900686 /// Update this FDT from a slice containing another FDT
687 pub fn copy_from_slice(&mut self, new_fdt: &[u8]) -> Result<()> {
688 if self.buffer.len() < new_fdt.len() {
689 Err(FdtError::NoSpace)
690 } else {
691 let totalsize = self.totalsize();
692 self.buffer[..new_fdt.len()].clone_from_slice(new_fdt);
693 // Zeroize the remaining part. We zeroize up to the size of the original DT because
694 // zeroizing the entire buffer (max 2MB) is not necessary and may increase the VM boot
695 // time.
696 self.buffer[new_fdt.len()..max(new_fdt.len(), totalsize)].fill(0_u8);
697 Ok(())
698 }
699 }
700
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000701 /// Make the whole slice containing the DT available to libfdt.
702 pub fn unpack(&mut self) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000703 // SAFETY: "Opens" the DT in-place (supported use-case) by updating its header and
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000704 // internal structures to make use of the whole self.fdt slice but performs no accesses
705 // outside of it and leaves the DT in a state that will be detected by other functions.
706 let ret = unsafe {
707 libfdt_bindgen::fdt_open_into(
708 self.as_ptr(),
709 self.as_mut_ptr(),
710 self.capacity().try_into().map_err(|_| FdtError::Internal)?,
711 )
712 };
713 fdt_err_expect_zero(ret)
714 }
715
716 /// Pack the DT to take a minimum amount of memory.
717 ///
718 /// Doesn't shrink the underlying memory slice.
719 pub fn pack(&mut self) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000720 // SAFETY: "Closes" the DT in-place by updating its header and relocating its structs.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000721 let ret = unsafe { libfdt_bindgen::fdt_pack(self.as_mut_ptr()) };
722 fdt_err_expect_zero(ret)
723 }
724
Pierre-Clément Tosi90e19352022-11-21 17:11:48 +0000725 /// Applies a DT overlay on the base DT.
726 ///
727 /// # Safety
728 ///
729 /// On failure, the library corrupts the DT and overlay so both must be discarded.
730 pub unsafe fn apply_overlay<'a>(&'a mut self, overlay: &'a mut Fdt) -> Result<&'a mut Self> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000731 let ret =
732 // SAFETY: Both pointers are valid because they come from references, and fdt_overlay_apply
733 // doesn't keep them after it returns. It may corrupt their contents if there is an error,
734 // but that's our caller's responsibility.
735 unsafe { libfdt_bindgen::fdt_overlay_apply(self.as_mut_ptr(), overlay.as_mut_ptr()) };
736 fdt_err_expect_zero(ret)?;
Pierre-Clément Tosi90e19352022-11-21 17:11:48 +0000737 Ok(self)
738 }
739
Alice Wang2422bdc2023-06-12 08:37:55 +0000740 /// Returns an iterator of memory banks specified the "/memory" node.
741 /// Throws an error when the "/memory" is not found in the device tree.
David Brazdil1baa9a92022-06-28 14:47:50 +0100742 ///
743 /// NOTE: This does not support individual "/memory@XXXX" banks.
Alice Wang2422bdc2023-06-12 08:37:55 +0000744 pub fn memory(&self) -> Result<MemRegIterator> {
745 let memory_node_name = CStr::from_bytes_with_nul(b"/memory\0").unwrap();
746 let memory_device_type = CStr::from_bytes_with_nul(b"memory\0").unwrap();
David Brazdil1baa9a92022-06-28 14:47:50 +0100747
Alice Wang2422bdc2023-06-12 08:37:55 +0000748 let node = self.node(memory_node_name)?.ok_or(FdtError::NotFound)?;
749 if node.device_type()? != Some(memory_device_type) {
750 return Err(FdtError::BadValue);
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000751 }
Alice Wang2422bdc2023-06-12 08:37:55 +0000752 node.reg()?.ok_or(FdtError::BadValue).map(MemRegIterator::new)
753 }
754
755 /// Returns the first memory range in the `/memory` node.
756 pub fn first_memory_range(&self) -> Result<Range<usize>> {
757 self.memory()?.next().ok_or(FdtError::NotFound)
David Brazdil1baa9a92022-06-28 14:47:50 +0100758 }
759
760 /// Retrieve the standard /chosen node.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000761 pub fn chosen(&self) -> Result<Option<FdtNode>> {
David Brazdil1baa9a92022-06-28 14:47:50 +0100762 self.node(CStr::from_bytes_with_nul(b"/chosen\0").unwrap())
763 }
764
Pierre-Clément Tosi4ba79662023-02-13 11:22:41 +0000765 /// Retrieve the standard /chosen node as mutable.
766 pub fn chosen_mut(&mut self) -> Result<Option<FdtNodeMut>> {
767 self.node_mut(CStr::from_bytes_with_nul(b"/chosen\0").unwrap())
768 }
769
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000770 /// Get the root node of the tree.
771 pub fn root(&self) -> Result<FdtNode> {
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000772 self.node(CStr::from_bytes_with_nul(b"/\0").unwrap())?.ok_or(FdtError::Internal)
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000773 }
774
David Brazdil1baa9a92022-06-28 14:47:50 +0100775 /// Find a tree node by its full path.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000776 pub fn node(&self, path: &CStr) -> Result<Option<FdtNode>> {
777 Ok(self.path_offset(path)?.map(|offset| FdtNode { fdt: self, offset }))
David Brazdil1baa9a92022-06-28 14:47:50 +0100778 }
779
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000780 /// Iterate over nodes with a given compatible string.
781 pub fn compatible_nodes<'a>(&'a self, compatible: &'a CStr) -> Result<CompatibleIterator<'a>> {
782 CompatibleIterator::new(self, compatible)
783 }
784
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000785 /// Get the mutable root node of the tree.
786 pub fn root_mut(&mut self) -> Result<FdtNodeMut> {
787 self.node_mut(CStr::from_bytes_with_nul(b"/\0").unwrap())?.ok_or(FdtError::Internal)
788 }
789
790 /// Find a mutable tree node by its full path.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000791 pub fn node_mut(&mut self, path: &CStr) -> Result<Option<FdtNodeMut>> {
792 Ok(self.path_offset(path)?.map(|offset| FdtNodeMut { fdt: self, offset }))
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000793 }
794
Pierre-Clément Tosidb74cb12022-12-08 13:56:25 +0000795 /// Return the device tree as a slice (may be smaller than the containing buffer).
796 pub fn as_slice(&self) -> &[u8] {
797 &self.buffer[..self.totalsize()]
798 }
799
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000800 fn path_offset(&self, path: &CStr) -> Result<Option<c_int>> {
David Brazdil1baa9a92022-06-28 14:47:50 +0100801 let len = path.to_bytes().len().try_into().map_err(|_| FdtError::BadPath)?;
Andrew Walbran84b9a232023-07-05 14:01:40 +0000802 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor) and the
David Brazdil1baa9a92022-06-28 14:47:50 +0100803 // function respects the passed number of characters.
804 let ret = unsafe {
805 // *_namelen functions don't include the trailing nul terminator in 'len'.
806 libfdt_bindgen::fdt_path_offset_namelen(self.as_ptr(), path.as_ptr(), len)
807 };
808
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000809 fdt_err_or_option(ret)
David Brazdil1baa9a92022-06-28 14:47:50 +0100810 }
811
812 fn check_full(&self) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000813 // SAFETY: Only performs read accesses within the limits of the slice. If successful, this
David Brazdil1baa9a92022-06-28 14:47:50 +0100814 // call guarantees to other unsafe calls that the header contains a valid totalsize (w.r.t.
815 // 'len' i.e. the self.fdt slice) that those C functions can use to perform bounds
816 // checking. The library doesn't maintain an internal state (such as pointers) between
817 // calls as it expects the client code to keep track of the objects (DT, nodes, ...).
Pierre-Clément Tosi02017da2023-09-26 17:57:04 +0100818 let ret = unsafe { libfdt_bindgen::fdt_check_full(self.as_ptr(), self.capacity()) };
David Brazdil1baa9a92022-06-28 14:47:50 +0100819 fdt_err_expect_zero(ret)
820 }
821
Jaewan Kimaa638702023-09-19 13:34:01 +0900822 fn get_from_ptr(&self, ptr: *const c_void, len: usize) -> Result<&[u8]> {
823 let ptr = ptr as usize;
824 let offset = ptr.checked_sub(self.as_ptr() as usize).ok_or(FdtError::Internal)?;
825 self.buffer.get(offset..(offset + len)).ok_or(FdtError::Internal)
826 }
827
Pierre-Clément Tosi8036b4f2023-02-17 10:31:31 +0000828 /// Return a shared pointer to the device tree.
829 pub fn as_ptr(&self) -> *const c_void {
Pierre-Clément Tosi0dcc75e2023-05-02 13:43:55 +0000830 self.buffer.as_ptr().cast::<_>()
David Brazdil1baa9a92022-06-28 14:47:50 +0100831 }
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000832
833 fn as_mut_ptr(&mut self) -> *mut c_void {
Pierre-Clément Tosi0dcc75e2023-05-02 13:43:55 +0000834 self.buffer.as_mut_ptr().cast::<_>()
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000835 }
836
837 fn capacity(&self) -> usize {
Pierre-Clément Tosief2030e2022-11-28 11:21:20 +0000838 self.buffer.len()
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000839 }
Pierre-Clément Tosidb74cb12022-12-08 13:56:25 +0000840
841 fn header(&self) -> &libfdt_bindgen::fdt_header {
Pierre-Clément Tosi0dcc75e2023-05-02 13:43:55 +0000842 let p = self.as_ptr().cast::<_>();
Andrew Walbran84b9a232023-07-05 14:01:40 +0000843 // SAFETY: A valid FDT (verified by constructor) must contain a valid fdt_header.
Pierre-Clément Tosi0dcc75e2023-05-02 13:43:55 +0000844 unsafe { &*p }
Pierre-Clément Tosidb74cb12022-12-08 13:56:25 +0000845 }
846
847 fn totalsize(&self) -> usize {
848 u32::from_be(self.header().totalsize) as usize
849 }
David Brazdil1baa9a92022-06-28 14:47:50 +0100850}