blob: afc36d0b52cc17a54f140bd28c4c601d2a43e348 [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]
Andrew Walbran84b9a232023-07-05 14:01:40 +000019#![deny(unsafe_op_in_unsafe_fn)]
20#![deny(clippy::undocumented_unsafe_blocks)]
David Brazdil1baa9a92022-06-28 14:47:50 +010021
Andrew Walbran55ad01b2022-12-05 17:00:40 +000022mod iterators;
23
Andrew Walbranb39e6922022-12-05 17:01:20 +000024pub use iterators::{AddressRange, CellIterator, MemRegIterator, RangesIterator, Reg, RegIterator};
Andrew Walbran55ad01b2022-12-05 17:00:40 +000025
Jiyong Parke9d87e82023-03-21 19:28:40 +090026use core::cmp::max;
David Brazdil1baa9a92022-06-28 14:47:50 +010027use core::ffi::{c_int, c_void, CStr};
28use core::fmt;
29use core::mem;
Alice Wang2422bdc2023-06-12 08:37:55 +000030use core::ops::Range;
David Brazdil1baa9a92022-06-28 14:47:50 +010031use core::result;
Pierre-Clément Tosic27c4272023-05-19 15:46:26 +000032use zerocopy::AsBytes as _;
David Brazdil1baa9a92022-06-28 14:47:50 +010033
34/// Error type corresponding to libfdt error codes.
35#[derive(Clone, Copy, Debug, Eq, PartialEq)]
36pub enum FdtError {
37 /// FDT_ERR_NOTFOUND
38 NotFound,
39 /// FDT_ERR_EXISTS
40 Exists,
41 /// FDT_ERR_NOSPACE
42 NoSpace,
43 /// FDT_ERR_BADOFFSET
44 BadOffset,
45 /// FDT_ERR_BADPATH
46 BadPath,
47 /// FDT_ERR_BADPHANDLE
48 BadPhandle,
49 /// FDT_ERR_BADSTATE
50 BadState,
51 /// FDT_ERR_TRUNCATED
52 Truncated,
53 /// FDT_ERR_BADMAGIC
54 BadMagic,
55 /// FDT_ERR_BADVERSION
56 BadVersion,
57 /// FDT_ERR_BADSTRUCTURE
58 BadStructure,
59 /// FDT_ERR_BADLAYOUT
60 BadLayout,
61 /// FDT_ERR_INTERNAL
62 Internal,
63 /// FDT_ERR_BADNCELLS
64 BadNCells,
65 /// FDT_ERR_BADVALUE
66 BadValue,
67 /// FDT_ERR_BADOVERLAY
68 BadOverlay,
69 /// FDT_ERR_NOPHANDLES
70 NoPhandles,
71 /// FDT_ERR_BADFLAGS
72 BadFlags,
73 /// FDT_ERR_ALIGNMENT
74 Alignment,
75 /// Unexpected error code
76 Unknown(i32),
77}
78
79impl fmt::Display for FdtError {
80 /// Prints error messages from libfdt.h documentation.
81 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
82 match self {
83 Self::NotFound => write!(f, "The requested node or property does not exist"),
84 Self::Exists => write!(f, "Attempted to create an existing node or property"),
85 Self::NoSpace => write!(f, "Insufficient buffer space to contain the expanded tree"),
86 Self::BadOffset => write!(f, "Structure block offset is out-of-bounds or invalid"),
87 Self::BadPath => write!(f, "Badly formatted path"),
88 Self::BadPhandle => write!(f, "Invalid phandle length or value"),
89 Self::BadState => write!(f, "Received incomplete device tree"),
90 Self::Truncated => write!(f, "Device tree or sub-block is improperly terminated"),
91 Self::BadMagic => write!(f, "Device tree header missing its magic number"),
92 Self::BadVersion => write!(f, "Device tree has a version which can't be handled"),
93 Self::BadStructure => write!(f, "Device tree has a corrupt structure block"),
94 Self::BadLayout => write!(f, "Device tree sub-blocks in unsupported order"),
95 Self::Internal => write!(f, "libfdt has failed an internal assertion"),
96 Self::BadNCells => write!(f, "Bad format or value of #address-cells or #size-cells"),
97 Self::BadValue => write!(f, "Unexpected property value"),
98 Self::BadOverlay => write!(f, "Overlay cannot be applied"),
99 Self::NoPhandles => write!(f, "Device tree doesn't have any phandle available anymore"),
100 Self::BadFlags => write!(f, "Invalid flag or invalid combination of flags"),
101 Self::Alignment => write!(f, "Device tree base address is not 8-byte aligned"),
102 Self::Unknown(e) => write!(f, "Unknown libfdt error '{e}'"),
103 }
104 }
105}
106
107/// Result type with FdtError enum.
108pub type Result<T> = result::Result<T, FdtError>;
109
110fn fdt_err(val: c_int) -> Result<c_int> {
111 if val >= 0 {
112 Ok(val)
113 } else {
114 Err(match -val as _ {
115 libfdt_bindgen::FDT_ERR_NOTFOUND => FdtError::NotFound,
116 libfdt_bindgen::FDT_ERR_EXISTS => FdtError::Exists,
117 libfdt_bindgen::FDT_ERR_NOSPACE => FdtError::NoSpace,
118 libfdt_bindgen::FDT_ERR_BADOFFSET => FdtError::BadOffset,
119 libfdt_bindgen::FDT_ERR_BADPATH => FdtError::BadPath,
120 libfdt_bindgen::FDT_ERR_BADPHANDLE => FdtError::BadPhandle,
121 libfdt_bindgen::FDT_ERR_BADSTATE => FdtError::BadState,
122 libfdt_bindgen::FDT_ERR_TRUNCATED => FdtError::Truncated,
123 libfdt_bindgen::FDT_ERR_BADMAGIC => FdtError::BadMagic,
124 libfdt_bindgen::FDT_ERR_BADVERSION => FdtError::BadVersion,
125 libfdt_bindgen::FDT_ERR_BADSTRUCTURE => FdtError::BadStructure,
126 libfdt_bindgen::FDT_ERR_BADLAYOUT => FdtError::BadLayout,
127 libfdt_bindgen::FDT_ERR_INTERNAL => FdtError::Internal,
128 libfdt_bindgen::FDT_ERR_BADNCELLS => FdtError::BadNCells,
129 libfdt_bindgen::FDT_ERR_BADVALUE => FdtError::BadValue,
130 libfdt_bindgen::FDT_ERR_BADOVERLAY => FdtError::BadOverlay,
131 libfdt_bindgen::FDT_ERR_NOPHANDLES => FdtError::NoPhandles,
132 libfdt_bindgen::FDT_ERR_BADFLAGS => FdtError::BadFlags,
133 libfdt_bindgen::FDT_ERR_ALIGNMENT => FdtError::Alignment,
134 _ => FdtError::Unknown(val),
135 })
136 }
137}
138
139fn fdt_err_expect_zero(val: c_int) -> Result<()> {
140 match fdt_err(val)? {
141 0 => Ok(()),
142 _ => Err(FdtError::Unknown(val)),
143 }
144}
145
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000146fn fdt_err_or_option(val: c_int) -> Result<Option<c_int>> {
147 match fdt_err(val) {
148 Ok(val) => Ok(Some(val)),
149 Err(FdtError::NotFound) => Ok(None),
150 Err(e) => Err(e),
151 }
152}
153
David Brazdil1baa9a92022-06-28 14:47:50 +0100154/// Value of a #address-cells property.
Andrew Walbranb39e6922022-12-05 17:01:20 +0000155#[derive(Copy, Clone, Debug, Eq, PartialEq)]
David Brazdil1baa9a92022-06-28 14:47:50 +0100156enum AddrCells {
157 Single = 1,
158 Double = 2,
Andrew Walbranb39e6922022-12-05 17:01:20 +0000159 Triple = 3,
David Brazdil1baa9a92022-06-28 14:47:50 +0100160}
161
162impl TryFrom<c_int> for AddrCells {
163 type Error = FdtError;
164
165 fn try_from(res: c_int) -> Result<Self> {
166 match fdt_err(res)? {
167 x if x == Self::Single as c_int => Ok(Self::Single),
168 x if x == Self::Double as c_int => Ok(Self::Double),
Andrew Walbranb39e6922022-12-05 17:01:20 +0000169 x if x == Self::Triple as c_int => Ok(Self::Triple),
David Brazdil1baa9a92022-06-28 14:47:50 +0100170 _ => Err(FdtError::BadNCells),
171 }
172 }
173}
174
175/// Value of a #size-cells property.
Andrew Walbranb39e6922022-12-05 17:01:20 +0000176#[derive(Copy, Clone, Debug, Eq, PartialEq)]
David Brazdil1baa9a92022-06-28 14:47:50 +0100177enum SizeCells {
178 None = 0,
179 Single = 1,
180 Double = 2,
181}
182
183impl TryFrom<c_int> for SizeCells {
184 type Error = FdtError;
185
186 fn try_from(res: c_int) -> Result<Self> {
187 match fdt_err(res)? {
188 x if x == Self::None as c_int => Ok(Self::None),
189 x if x == Self::Single as c_int => Ok(Self::Single),
190 x if x == Self::Double as c_int => Ok(Self::Double),
191 _ => Err(FdtError::BadNCells),
192 }
193 }
194}
195
David Brazdil1baa9a92022-06-28 14:47:50 +0100196/// DT node.
Alice Wang9d4df702023-05-25 14:14:12 +0000197#[derive(Clone, Copy, Debug)]
David Brazdil1baa9a92022-06-28 14:47:50 +0100198pub struct FdtNode<'a> {
199 fdt: &'a Fdt,
200 offset: c_int,
201}
202
203impl<'a> FdtNode<'a> {
Jiyong Park9c63cd12023-03-21 17:53:07 +0900204 /// Create immutable node from a mutable node at the same offset
205 pub fn from_mut(other: &'a FdtNodeMut) -> Self {
206 FdtNode { fdt: other.fdt, offset: other.offset }
207 }
David Brazdil1baa9a92022-06-28 14:47:50 +0100208 /// Find parent node.
209 pub fn parent(&self) -> Result<Self> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000210 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
David Brazdil1baa9a92022-06-28 14:47:50 +0100211 let ret = unsafe { libfdt_bindgen::fdt_parent_offset(self.fdt.as_ptr(), self.offset) };
212
213 Ok(Self { fdt: self.fdt, offset: fdt_err(ret)? })
214 }
215
216 /// Retrieve the standard (deprecated) device_type <string> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000217 pub fn device_type(&self) -> Result<Option<&CStr>> {
David Brazdil1baa9a92022-06-28 14:47:50 +0100218 self.getprop_str(CStr::from_bytes_with_nul(b"device_type\0").unwrap())
219 }
220
221 /// Retrieve the standard reg <prop-encoded-array> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000222 pub fn reg(&self) -> Result<Option<RegIterator<'a>>> {
223 let reg = CStr::from_bytes_with_nul(b"reg\0").unwrap();
David Brazdil1baa9a92022-06-28 14:47:50 +0100224
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000225 if let Some(cells) = self.getprop_cells(reg)? {
226 let parent = self.parent()?;
David Brazdil1baa9a92022-06-28 14:47:50 +0100227
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000228 let addr_cells = parent.address_cells()?;
229 let size_cells = parent.size_cells()?;
230
231 Ok(Some(RegIterator::new(cells, addr_cells, size_cells)))
232 } else {
233 Ok(None)
234 }
David Brazdil1baa9a92022-06-28 14:47:50 +0100235 }
236
Andrew Walbranb39e6922022-12-05 17:01:20 +0000237 /// Retrieves the standard ranges property.
238 pub fn ranges<A, P, S>(&self) -> Result<Option<RangesIterator<'a, A, P, S>>> {
239 let ranges = CStr::from_bytes_with_nul(b"ranges\0").unwrap();
240 if let Some(cells) = self.getprop_cells(ranges)? {
241 let parent = self.parent()?;
242 let addr_cells = self.address_cells()?;
243 let parent_addr_cells = parent.address_cells()?;
244 let size_cells = self.size_cells()?;
245 Ok(Some(RangesIterator::<A, P, S>::new(
246 cells,
247 addr_cells,
248 parent_addr_cells,
249 size_cells,
250 )))
251 } else {
252 Ok(None)
253 }
254 }
255
David Brazdil1baa9a92022-06-28 14:47:50 +0100256 /// Retrieve the value of a given <string> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000257 pub fn getprop_str(&self, name: &CStr) -> Result<Option<&CStr>> {
258 let value = if let Some(bytes) = self.getprop(name)? {
259 Some(CStr::from_bytes_with_nul(bytes).map_err(|_| FdtError::BadValue)?)
260 } else {
261 None
262 };
263 Ok(value)
David Brazdil1baa9a92022-06-28 14:47:50 +0100264 }
265
266 /// Retrieve the value of a given property as an array of cells.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000267 pub fn getprop_cells(&self, name: &CStr) -> Result<Option<CellIterator<'a>>> {
268 if let Some(cells) = self.getprop(name)? {
269 Ok(Some(CellIterator::new(cells)))
270 } else {
271 Ok(None)
272 }
David Brazdil1baa9a92022-06-28 14:47:50 +0100273 }
274
275 /// Retrieve the value of a given <u32> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000276 pub fn getprop_u32(&self, name: &CStr) -> Result<Option<u32>> {
277 let value = if let Some(bytes) = self.getprop(name)? {
278 Some(u32::from_be_bytes(bytes.try_into().map_err(|_| FdtError::BadValue)?))
279 } else {
280 None
281 };
282 Ok(value)
David Brazdil1baa9a92022-06-28 14:47:50 +0100283 }
284
285 /// Retrieve the value of a given <u64> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000286 pub fn getprop_u64(&self, name: &CStr) -> Result<Option<u64>> {
287 let value = if let Some(bytes) = self.getprop(name)? {
288 Some(u64::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 property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000296 pub fn getprop(&self, name: &CStr) -> Result<Option<&'a [u8]>> {
Jiyong Park9c63cd12023-03-21 17:53:07 +0900297 if let Some((prop, len)) = Self::getprop_internal(self.fdt, self.offset, name)? {
298 let offset = (prop as usize)
299 .checked_sub(self.fdt.as_ptr() as usize)
300 .ok_or(FdtError::Internal)?;
301
302 Ok(Some(self.fdt.buffer.get(offset..(offset + len)).ok_or(FdtError::Internal)?))
303 } else {
304 Ok(None) // property was not found
305 }
306 }
307
308 /// Return the pointer and size of the property named `name`, in a node at offset `offset`, in
309 /// a device tree `fdt`. The pointer is guaranteed to be non-null, in which case error returns.
310 fn getprop_internal(
311 fdt: &'a Fdt,
312 offset: c_int,
313 name: &CStr,
314 ) -> Result<Option<(*const c_void, usize)>> {
David Brazdil1baa9a92022-06-28 14:47:50 +0100315 let mut len: i32 = 0;
Andrew Walbran84b9a232023-07-05 14:01:40 +0000316 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor) and the
David Brazdil1baa9a92022-06-28 14:47:50 +0100317 // function respects the passed number of characters.
318 let prop = unsafe {
319 libfdt_bindgen::fdt_getprop_namelen(
Jiyong Park9c63cd12023-03-21 17:53:07 +0900320 fdt.as_ptr(),
321 offset,
David Brazdil1baa9a92022-06-28 14:47:50 +0100322 name.as_ptr(),
323 // *_namelen functions don't include the trailing nul terminator in 'len'.
324 name.to_bytes().len().try_into().map_err(|_| FdtError::BadPath)?,
325 &mut len as *mut i32,
326 )
327 } as *const u8;
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000328
329 let Some(len) = fdt_err_or_option(len)? else {
330 return Ok(None); // Property was not found.
331 };
332 let len = usize::try_from(len).map_err(|_| FdtError::Internal)?;
333
David Brazdil1baa9a92022-06-28 14:47:50 +0100334 if prop.is_null() {
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000335 // We expected an error code in len but still received a valid value?!
336 return Err(FdtError::Internal);
David Brazdil1baa9a92022-06-28 14:47:50 +0100337 }
Jiyong Park9c63cd12023-03-21 17:53:07 +0900338 Ok(Some((prop.cast::<c_void>(), len)))
David Brazdil1baa9a92022-06-28 14:47:50 +0100339 }
340
341 /// Get reference to the containing device tree.
342 pub fn fdt(&self) -> &Fdt {
343 self.fdt
344 }
345
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000346 fn next_compatible(self, compatible: &CStr) -> Result<Option<Self>> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000347 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000348 let ret = unsafe {
349 libfdt_bindgen::fdt_node_offset_by_compatible(
350 self.fdt.as_ptr(),
351 self.offset,
352 compatible.as_ptr(),
353 )
354 };
355
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000356 Ok(fdt_err_or_option(ret)?.map(|offset| Self { fdt: self.fdt, offset }))
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000357 }
358
David Brazdil1baa9a92022-06-28 14:47:50 +0100359 fn address_cells(&self) -> Result<AddrCells> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000360 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
David Brazdil1baa9a92022-06-28 14:47:50 +0100361 unsafe { libfdt_bindgen::fdt_address_cells(self.fdt.as_ptr(), self.offset) }
362 .try_into()
363 .map_err(|_| FdtError::Internal)
364 }
365
366 fn size_cells(&self) -> Result<SizeCells> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000367 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
David Brazdil1baa9a92022-06-28 14:47:50 +0100368 unsafe { libfdt_bindgen::fdt_size_cells(self.fdt.as_ptr(), self.offset) }
369 .try_into()
370 .map_err(|_| FdtError::Internal)
371 }
372}
373
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000374/// Mutable FDT node.
375pub struct FdtNodeMut<'a> {
376 fdt: &'a mut Fdt,
377 offset: c_int,
378}
379
380impl<'a> FdtNodeMut<'a> {
381 /// Append a property name-value (possibly empty) pair to the given node.
382 pub fn appendprop<T: AsRef<[u8]>>(&mut self, name: &CStr, value: &T) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000383 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000384 let ret = unsafe {
385 libfdt_bindgen::fdt_appendprop(
386 self.fdt.as_mut_ptr(),
387 self.offset,
388 name.as_ptr(),
389 value.as_ref().as_ptr().cast::<c_void>(),
390 value.as_ref().len().try_into().map_err(|_| FdtError::BadValue)?,
391 )
392 };
393
394 fdt_err_expect_zero(ret)
395 }
396
397 /// Append a (address, size) pair property to the given node.
398 pub fn appendprop_addrrange(&mut self, name: &CStr, addr: u64, size: u64) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000399 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000400 let ret = unsafe {
401 libfdt_bindgen::fdt_appendprop_addrrange(
402 self.fdt.as_mut_ptr(),
403 self.parent()?.offset,
404 self.offset,
405 name.as_ptr(),
406 addr,
407 size,
408 )
409 };
410
411 fdt_err_expect_zero(ret)
412 }
413
Jaewan Kimba8929b2023-01-13 11:13:29 +0900414 /// Create or change a property name-value pair to the given node.
415 pub fn setprop(&mut self, name: &CStr, value: &[u8]) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000416 // SAFETY: New value size is constrained to the DT totalsize
Jaewan Kimba8929b2023-01-13 11:13:29 +0900417 // (validated by underlying libfdt).
418 let ret = unsafe {
419 libfdt_bindgen::fdt_setprop(
420 self.fdt.as_mut_ptr(),
421 self.offset,
422 name.as_ptr(),
423 value.as_ptr().cast::<c_void>(),
424 value.len().try_into().map_err(|_| FdtError::BadValue)?,
425 )
426 };
427
428 fdt_err_expect_zero(ret)
429 }
430
Jiyong Park9c63cd12023-03-21 17:53:07 +0900431 /// Replace the value of the given property with the given value, and ensure that the given
432 /// value has the same length as the current value length
433 pub fn setprop_inplace(&mut self, name: &CStr, value: &[u8]) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000434 // SAFETY: fdt size is not altered
Jiyong Park9c63cd12023-03-21 17:53:07 +0900435 let ret = unsafe {
436 libfdt_bindgen::fdt_setprop_inplace(
437 self.fdt.as_mut_ptr(),
438 self.offset,
439 name.as_ptr(),
440 value.as_ptr().cast::<c_void>(),
441 value.len().try_into().map_err(|_| FdtError::BadValue)?,
442 )
443 };
444
445 fdt_err_expect_zero(ret)
446 }
447
Pierre-Clément Tosic27c4272023-05-19 15:46:26 +0000448 /// Replace the value of the given (address, size) pair property with the given value, and
449 /// ensure that the given value has the same length as the current value length
450 pub fn setprop_addrrange_inplace(&mut self, name: &CStr, addr: u64, size: u64) -> Result<()> {
451 let pair = [addr.to_be(), size.to_be()];
452 self.setprop_inplace(name, pair.as_bytes())
453 }
454
Pierre-Clément Tosi4ba79662023-02-13 11:22:41 +0000455 /// Create or change a flag-like empty property.
456 pub fn setprop_empty(&mut self, name: &CStr) -> Result<()> {
457 self.setprop(name, &[])
458 }
459
460 /// Delete the given property.
461 pub fn delprop(&mut self, name: &CStr) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000462 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor) when the
Pierre-Clément Tosi4ba79662023-02-13 11:22:41 +0000463 // library locates the node's property. Removing the property may shift the offsets of
464 // other nodes and properties but the borrow checker should prevent this function from
465 // being called when FdtNode instances are in use.
466 let ret = unsafe {
467 libfdt_bindgen::fdt_delprop(self.fdt.as_mut_ptr(), self.offset, name.as_ptr())
468 };
469
470 fdt_err_expect_zero(ret)
471 }
472
Pierre-Clément Tosibe3a97b2023-05-19 14:56:23 +0000473 /// Overwrite the given property with FDT_NOP, effectively removing it from the DT.
474 pub fn nop_property(&mut self, name: &CStr) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000475 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor) when the
Pierre-Clément Tosibe3a97b2023-05-19 14:56:23 +0000476 // library locates the node's property.
477 let ret = unsafe {
478 libfdt_bindgen::fdt_nop_property(self.fdt.as_mut_ptr(), self.offset, name.as_ptr())
479 };
480
481 fdt_err_expect_zero(ret)
482 }
483
Jiyong Park9c63cd12023-03-21 17:53:07 +0900484 /// Reduce the size of the given property to new_size
485 pub fn trimprop(&mut self, name: &CStr, new_size: usize) -> Result<()> {
486 let (prop, len) =
487 FdtNode::getprop_internal(self.fdt, self.offset, name)?.ok_or(FdtError::NotFound)?;
488 if len == new_size {
489 return Ok(());
490 }
491 if new_size > len {
492 return Err(FdtError::NoSpace);
493 }
494
Andrew Walbran84b9a232023-07-05 14:01:40 +0000495 // SAFETY: new_size is smaller than the old size
Jiyong Park9c63cd12023-03-21 17:53:07 +0900496 let ret = unsafe {
497 libfdt_bindgen::fdt_setprop(
498 self.fdt.as_mut_ptr(),
499 self.offset,
500 name.as_ptr(),
501 prop.cast::<c_void>(),
502 new_size.try_into().map_err(|_| FdtError::BadValue)?,
503 )
504 };
505
506 fdt_err_expect_zero(ret)
507 }
508
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000509 /// Get reference to the containing device tree.
510 pub fn fdt(&mut self) -> &mut Fdt {
511 self.fdt
512 }
513
514 /// Add a new subnode to the given node and return it as a FdtNodeMut on success.
515 pub fn add_subnode(&'a mut self, name: &CStr) -> Result<Self> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000516 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000517 let ret = unsafe {
518 libfdt_bindgen::fdt_add_subnode(self.fdt.as_mut_ptr(), self.offset, name.as_ptr())
519 };
520
521 Ok(Self { fdt: self.fdt, offset: fdt_err(ret)? })
522 }
523
524 fn parent(&'a self) -> Result<FdtNode<'a>> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000525 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000526 let ret = unsafe { libfdt_bindgen::fdt_parent_offset(self.fdt.as_ptr(), self.offset) };
527
528 Ok(FdtNode { fdt: &*self.fdt, offset: fdt_err(ret)? })
529 }
Jiyong Park9c63cd12023-03-21 17:53:07 +0900530
531 /// Return the compatible node of the given name that is next to this node
532 pub fn next_compatible(self, compatible: &CStr) -> Result<Option<Self>> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000533 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
Jiyong Park9c63cd12023-03-21 17:53:07 +0900534 let ret = unsafe {
535 libfdt_bindgen::fdt_node_offset_by_compatible(
536 self.fdt.as_ptr(),
537 self.offset,
538 compatible.as_ptr(),
539 )
540 };
541
542 Ok(fdt_err_or_option(ret)?.map(|offset| Self { fdt: self.fdt, offset }))
543 }
544
545 /// Replace this node and its subtree with nop tags, effectively removing it from the tree, and
546 /// then return the next compatible node of the given name.
547 // Side note: without this, filterint out excessive compatible nodes from the DT is impossible.
548 // The reason is that libfdt ensures that the node from where the search for the next
549 // compatible node is started is always a valid one -- except for the special case of offset =
550 // -1 which is to find the first compatible node. So, we can't delete a node and then find the
551 // next compatible node from it.
552 //
553 // We can't do in the opposite direction either. If we call next_compatible to find the next
554 // node, and delete the current node, the Rust borrow checker kicks in. The next node has a
555 // mutable reference to DT, so we can't use current node (which also has a mutable reference to
556 // DT).
557 pub fn delete_and_next_compatible(self, compatible: &CStr) -> Result<Option<Self>> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000558 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
Jiyong Park9c63cd12023-03-21 17:53:07 +0900559 let ret = unsafe {
560 libfdt_bindgen::fdt_node_offset_by_compatible(
561 self.fdt.as_ptr(),
562 self.offset,
563 compatible.as_ptr(),
564 )
565 };
566 let next_offset = fdt_err_or_option(ret)?;
567
Andrew Walbran84b9a232023-07-05 14:01:40 +0000568 // SAFETY: fdt_nop_node alter only the bytes in the blob which contain the node and its
Jiyong Park9c63cd12023-03-21 17:53:07 +0900569 // properties and subnodes, and will not alter or move any other part of the tree.
570 let ret = unsafe { libfdt_bindgen::fdt_nop_node(self.fdt.as_mut_ptr(), self.offset) };
571 fdt_err_expect_zero(ret)?;
572
573 Ok(next_offset.map(|offset| Self { fdt: self.fdt, offset }))
574 }
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000575}
576
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000577/// Iterator over nodes sharing a same compatible string.
578pub struct CompatibleIterator<'a> {
579 node: FdtNode<'a>,
580 compatible: &'a CStr,
581}
582
583impl<'a> CompatibleIterator<'a> {
584 fn new(fdt: &'a Fdt, compatible: &'a CStr) -> Result<Self> {
585 let node = fdt.root()?;
586 Ok(Self { node, compatible })
587 }
588}
589
590impl<'a> Iterator for CompatibleIterator<'a> {
591 type Item = FdtNode<'a>;
592
593 fn next(&mut self) -> Option<Self::Item> {
594 let next = self.node.next_compatible(self.compatible).ok()?;
595
596 if let Some(node) = next {
597 self.node = node;
598 }
599
600 next
601 }
602}
603
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000604/// Wrapper around low-level libfdt functions.
Alice Wang9d4df702023-05-25 14:14:12 +0000605#[derive(Debug)]
David Brazdil1baa9a92022-06-28 14:47:50 +0100606#[repr(transparent)]
607pub struct Fdt {
Pierre-Clément Tosief2030e2022-11-28 11:21:20 +0000608 buffer: [u8],
David Brazdil1baa9a92022-06-28 14:47:50 +0100609}
610
611impl Fdt {
612 /// Wraps a slice containing a Flattened Device Tree.
613 ///
614 /// Fails if the FDT does not pass validation.
615 pub fn from_slice(fdt: &[u8]) -> Result<&Self> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000616 // SAFETY: The FDT will be validated before it is returned.
David Brazdil1baa9a92022-06-28 14:47:50 +0100617 let fdt = unsafe { Self::unchecked_from_slice(fdt) };
618 fdt.check_full()?;
619 Ok(fdt)
620 }
621
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000622 /// Wraps a mutable slice containing a Flattened Device Tree.
623 ///
624 /// Fails if the FDT does not pass validation.
625 pub fn from_mut_slice(fdt: &mut [u8]) -> Result<&mut Self> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000626 // SAFETY: The FDT will be validated before it is returned.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000627 let fdt = unsafe { Self::unchecked_from_mut_slice(fdt) };
628 fdt.check_full()?;
629 Ok(fdt)
630 }
631
Jaewan Kim4cf20aa2023-04-03 10:25:38 +0900632 /// Creates an empty Flattened Device Tree with a mutable slice.
633 pub fn create_empty_tree(fdt: &mut [u8]) -> Result<&mut Self> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000634 // SAFETY: fdt_create_empty_tree() only write within the specified length,
Jaewan Kim4cf20aa2023-04-03 10:25:38 +0900635 // and returns error if buffer was insufficient.
636 // There will be no memory write outside of the given fdt.
637 let ret = unsafe {
638 libfdt_bindgen::fdt_create_empty_tree(
639 fdt.as_mut_ptr().cast::<c_void>(),
640 fdt.len() as i32,
641 )
642 };
643 fdt_err_expect_zero(ret)?;
644
Andrew Walbran84b9a232023-07-05 14:01:40 +0000645 // SAFETY: The FDT will be validated before it is returned.
Jaewan Kim4cf20aa2023-04-03 10:25:38 +0900646 let fdt = unsafe { Self::unchecked_from_mut_slice(fdt) };
647 fdt.check_full()?;
648
649 Ok(fdt)
650 }
651
David Brazdil1baa9a92022-06-28 14:47:50 +0100652 /// Wraps a slice containing a Flattened Device Tree.
653 ///
654 /// # Safety
655 ///
656 /// The returned FDT might be invalid, only use on slices containing a valid DT.
657 pub unsafe fn unchecked_from_slice(fdt: &[u8]) -> &Self {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000658 // SAFETY: Fdt is a wrapper around a [u8], so the transmute is valid. The caller is
659 // responsible for ensuring that it is actually a valid FDT.
660 unsafe { mem::transmute::<&[u8], &Self>(fdt) }
David Brazdil1baa9a92022-06-28 14:47:50 +0100661 }
662
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000663 /// Wraps a mutable slice containing a Flattened Device Tree.
664 ///
665 /// # Safety
666 ///
667 /// The returned FDT might be invalid, only use on slices containing a valid DT.
668 pub unsafe fn unchecked_from_mut_slice(fdt: &mut [u8]) -> &mut Self {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000669 // SAFETY: Fdt is a wrapper around a [u8], so the transmute is valid. The caller is
670 // responsible for ensuring that it is actually a valid FDT.
671 unsafe { mem::transmute::<&mut [u8], &mut Self>(fdt) }
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000672 }
673
Jiyong Parke9d87e82023-03-21 19:28:40 +0900674 /// Update this FDT from a slice containing another FDT
675 pub fn copy_from_slice(&mut self, new_fdt: &[u8]) -> Result<()> {
676 if self.buffer.len() < new_fdt.len() {
677 Err(FdtError::NoSpace)
678 } else {
679 let totalsize = self.totalsize();
680 self.buffer[..new_fdt.len()].clone_from_slice(new_fdt);
681 // Zeroize the remaining part. We zeroize up to the size of the original DT because
682 // zeroizing the entire buffer (max 2MB) is not necessary and may increase the VM boot
683 // time.
684 self.buffer[new_fdt.len()..max(new_fdt.len(), totalsize)].fill(0_u8);
685 Ok(())
686 }
687 }
688
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000689 /// Make the whole slice containing the DT available to libfdt.
690 pub fn unpack(&mut self) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000691 // SAFETY: "Opens" the DT in-place (supported use-case) by updating its header and
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000692 // internal structures to make use of the whole self.fdt slice but performs no accesses
693 // outside of it and leaves the DT in a state that will be detected by other functions.
694 let ret = unsafe {
695 libfdt_bindgen::fdt_open_into(
696 self.as_ptr(),
697 self.as_mut_ptr(),
698 self.capacity().try_into().map_err(|_| FdtError::Internal)?,
699 )
700 };
701 fdt_err_expect_zero(ret)
702 }
703
704 /// Pack the DT to take a minimum amount of memory.
705 ///
706 /// Doesn't shrink the underlying memory slice.
707 pub fn pack(&mut self) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000708 // SAFETY: "Closes" the DT in-place by updating its header and relocating its structs.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000709 let ret = unsafe { libfdt_bindgen::fdt_pack(self.as_mut_ptr()) };
710 fdt_err_expect_zero(ret)
711 }
712
Pierre-Clément Tosi90e19352022-11-21 17:11:48 +0000713 /// Applies a DT overlay on the base DT.
714 ///
715 /// # Safety
716 ///
717 /// On failure, the library corrupts the DT and overlay so both must be discarded.
718 pub unsafe fn apply_overlay<'a>(&'a mut self, overlay: &'a mut Fdt) -> Result<&'a mut Self> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000719 let ret =
720 // SAFETY: Both pointers are valid because they come from references, and fdt_overlay_apply
721 // doesn't keep them after it returns. It may corrupt their contents if there is an error,
722 // but that's our caller's responsibility.
723 unsafe { libfdt_bindgen::fdt_overlay_apply(self.as_mut_ptr(), overlay.as_mut_ptr()) };
724 fdt_err_expect_zero(ret)?;
Pierre-Clément Tosi90e19352022-11-21 17:11:48 +0000725 Ok(self)
726 }
727
Alice Wang2422bdc2023-06-12 08:37:55 +0000728 /// Returns an iterator of memory banks specified the "/memory" node.
729 /// Throws an error when the "/memory" is not found in the device tree.
David Brazdil1baa9a92022-06-28 14:47:50 +0100730 ///
731 /// NOTE: This does not support individual "/memory@XXXX" banks.
Alice Wang2422bdc2023-06-12 08:37:55 +0000732 pub fn memory(&self) -> Result<MemRegIterator> {
733 let memory_node_name = CStr::from_bytes_with_nul(b"/memory\0").unwrap();
734 let memory_device_type = CStr::from_bytes_with_nul(b"memory\0").unwrap();
David Brazdil1baa9a92022-06-28 14:47:50 +0100735
Alice Wang2422bdc2023-06-12 08:37:55 +0000736 let node = self.node(memory_node_name)?.ok_or(FdtError::NotFound)?;
737 if node.device_type()? != Some(memory_device_type) {
738 return Err(FdtError::BadValue);
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000739 }
Alice Wang2422bdc2023-06-12 08:37:55 +0000740 node.reg()?.ok_or(FdtError::BadValue).map(MemRegIterator::new)
741 }
742
743 /// Returns the first memory range in the `/memory` node.
744 pub fn first_memory_range(&self) -> Result<Range<usize>> {
745 self.memory()?.next().ok_or(FdtError::NotFound)
David Brazdil1baa9a92022-06-28 14:47:50 +0100746 }
747
748 /// Retrieve the standard /chosen node.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000749 pub fn chosen(&self) -> Result<Option<FdtNode>> {
David Brazdil1baa9a92022-06-28 14:47:50 +0100750 self.node(CStr::from_bytes_with_nul(b"/chosen\0").unwrap())
751 }
752
Pierre-Clément Tosi4ba79662023-02-13 11:22:41 +0000753 /// Retrieve the standard /chosen node as mutable.
754 pub fn chosen_mut(&mut self) -> Result<Option<FdtNodeMut>> {
755 self.node_mut(CStr::from_bytes_with_nul(b"/chosen\0").unwrap())
756 }
757
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000758 /// Get the root node of the tree.
759 pub fn root(&self) -> Result<FdtNode> {
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000760 self.node(CStr::from_bytes_with_nul(b"/\0").unwrap())?.ok_or(FdtError::Internal)
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000761 }
762
David Brazdil1baa9a92022-06-28 14:47:50 +0100763 /// Find a tree node by its full path.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000764 pub fn node(&self, path: &CStr) -> Result<Option<FdtNode>> {
765 Ok(self.path_offset(path)?.map(|offset| FdtNode { fdt: self, offset }))
David Brazdil1baa9a92022-06-28 14:47:50 +0100766 }
767
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000768 /// Iterate over nodes with a given compatible string.
769 pub fn compatible_nodes<'a>(&'a self, compatible: &'a CStr) -> Result<CompatibleIterator<'a>> {
770 CompatibleIterator::new(self, compatible)
771 }
772
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000773 /// Get the mutable root node of the tree.
774 pub fn root_mut(&mut self) -> Result<FdtNodeMut> {
775 self.node_mut(CStr::from_bytes_with_nul(b"/\0").unwrap())?.ok_or(FdtError::Internal)
776 }
777
778 /// Find a mutable tree node by its full path.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000779 pub fn node_mut(&mut self, path: &CStr) -> Result<Option<FdtNodeMut>> {
780 Ok(self.path_offset(path)?.map(|offset| FdtNodeMut { fdt: self, offset }))
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000781 }
782
Pierre-Clément Tosidb74cb12022-12-08 13:56:25 +0000783 /// Return the device tree as a slice (may be smaller than the containing buffer).
784 pub fn as_slice(&self) -> &[u8] {
785 &self.buffer[..self.totalsize()]
786 }
787
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000788 fn path_offset(&self, path: &CStr) -> Result<Option<c_int>> {
David Brazdil1baa9a92022-06-28 14:47:50 +0100789 let len = path.to_bytes().len().try_into().map_err(|_| FdtError::BadPath)?;
Andrew Walbran84b9a232023-07-05 14:01:40 +0000790 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor) and the
David Brazdil1baa9a92022-06-28 14:47:50 +0100791 // function respects the passed number of characters.
792 let ret = unsafe {
793 // *_namelen functions don't include the trailing nul terminator in 'len'.
794 libfdt_bindgen::fdt_path_offset_namelen(self.as_ptr(), path.as_ptr(), len)
795 };
796
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000797 fdt_err_or_option(ret)
David Brazdil1baa9a92022-06-28 14:47:50 +0100798 }
799
800 fn check_full(&self) -> Result<()> {
Pierre-Clément Tosief2030e2022-11-28 11:21:20 +0000801 let len = self.buffer.len();
Andrew Walbran84b9a232023-07-05 14:01:40 +0000802 // SAFETY: Only performs read accesses within the limits of the slice. If successful, this
David Brazdil1baa9a92022-06-28 14:47:50 +0100803 // call guarantees to other unsafe calls that the header contains a valid totalsize (w.r.t.
804 // 'len' i.e. the self.fdt slice) that those C functions can use to perform bounds
805 // checking. The library doesn't maintain an internal state (such as pointers) between
806 // calls as it expects the client code to keep track of the objects (DT, nodes, ...).
807 let ret = unsafe { libfdt_bindgen::fdt_check_full(self.as_ptr(), len) };
808 fdt_err_expect_zero(ret)
809 }
810
Pierre-Clément Tosi8036b4f2023-02-17 10:31:31 +0000811 /// Return a shared pointer to the device tree.
812 pub fn as_ptr(&self) -> *const c_void {
Pierre-Clément Tosi0dcc75e2023-05-02 13:43:55 +0000813 self.buffer.as_ptr().cast::<_>()
David Brazdil1baa9a92022-06-28 14:47:50 +0100814 }
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000815
816 fn as_mut_ptr(&mut self) -> *mut c_void {
Pierre-Clément Tosi0dcc75e2023-05-02 13:43:55 +0000817 self.buffer.as_mut_ptr().cast::<_>()
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000818 }
819
820 fn capacity(&self) -> usize {
Pierre-Clément Tosief2030e2022-11-28 11:21:20 +0000821 self.buffer.len()
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000822 }
Pierre-Clément Tosidb74cb12022-12-08 13:56:25 +0000823
824 fn header(&self) -> &libfdt_bindgen::fdt_header {
Pierre-Clément Tosi0dcc75e2023-05-02 13:43:55 +0000825 let p = self.as_ptr().cast::<_>();
Andrew Walbran84b9a232023-07-05 14:01:40 +0000826 // SAFETY: A valid FDT (verified by constructor) must contain a valid fdt_header.
Pierre-Clément Tosi0dcc75e2023-05-02 13:43:55 +0000827 unsafe { &*p }
Pierre-Clément Tosidb74cb12022-12-08 13:56:25 +0000828 }
829
830 fn totalsize(&self) -> usize {
831 u32::from_be(self.header().totalsize) as usize
832 }
David Brazdil1baa9a92022-06-28 14:47:50 +0100833}