blob: 9eeee60b753edeb67a3b573759a74d348d83a816 [file] [log] [blame]
David Brazdil1baa9a92022-06-28 14:47:50 +01001// Copyright 2022, The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Wrapper around libfdt library. Provides parsing/generating functionality
16//! to a bare-metal environment.
17
18#![no_std]
19
Andrew Walbran55ad01b2022-12-05 17:00:40 +000020mod iterators;
21
Jaewan Kimfe06c852023-10-05 23:40:06 +090022pub use iterators::{
Jaewan Kim72d10902023-10-12 21:59:26 +090023 AddressRange, CellIterator, CompatibleIterator, MemRegIterator, PropertyIterator,
24 RangesIterator, Reg, RegIterator, SubnodeIterator,
Jaewan Kimfe06c852023-10-05 23:40:06 +090025};
Andrew Walbran55ad01b2022-12-05 17:00:40 +000026
Jiyong Parke9d87e82023-03-21 19:28:40 +090027use core::cmp::max;
David Brazdil1baa9a92022-06-28 14:47:50 +010028use core::ffi::{c_int, c_void, CStr};
29use core::fmt;
30use core::mem;
Alice Wang2422bdc2023-06-12 08:37:55 +000031use core::ops::Range;
David Brazdil1baa9a92022-06-28 14:47:50 +010032use core::result;
Pierre-Clément Tosic27c4272023-05-19 15:46:26 +000033use zerocopy::AsBytes as _;
David Brazdil1baa9a92022-06-28 14:47:50 +010034
35/// Error type corresponding to libfdt error codes.
36#[derive(Clone, Copy, Debug, Eq, PartialEq)]
37pub enum FdtError {
38 /// FDT_ERR_NOTFOUND
39 NotFound,
40 /// FDT_ERR_EXISTS
41 Exists,
42 /// FDT_ERR_NOSPACE
43 NoSpace,
44 /// FDT_ERR_BADOFFSET
45 BadOffset,
46 /// FDT_ERR_BADPATH
47 BadPath,
48 /// FDT_ERR_BADPHANDLE
49 BadPhandle,
50 /// FDT_ERR_BADSTATE
51 BadState,
52 /// FDT_ERR_TRUNCATED
53 Truncated,
54 /// FDT_ERR_BADMAGIC
55 BadMagic,
56 /// FDT_ERR_BADVERSION
57 BadVersion,
58 /// FDT_ERR_BADSTRUCTURE
59 BadStructure,
60 /// FDT_ERR_BADLAYOUT
61 BadLayout,
62 /// FDT_ERR_INTERNAL
63 Internal,
64 /// FDT_ERR_BADNCELLS
65 BadNCells,
66 /// FDT_ERR_BADVALUE
67 BadValue,
68 /// FDT_ERR_BADOVERLAY
69 BadOverlay,
70 /// FDT_ERR_NOPHANDLES
71 NoPhandles,
72 /// FDT_ERR_BADFLAGS
73 BadFlags,
74 /// FDT_ERR_ALIGNMENT
75 Alignment,
76 /// Unexpected error code
77 Unknown(i32),
78}
79
80impl fmt::Display for FdtError {
81 /// Prints error messages from libfdt.h documentation.
82 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
83 match self {
84 Self::NotFound => write!(f, "The requested node or property does not exist"),
85 Self::Exists => write!(f, "Attempted to create an existing node or property"),
86 Self::NoSpace => write!(f, "Insufficient buffer space to contain the expanded tree"),
87 Self::BadOffset => write!(f, "Structure block offset is out-of-bounds or invalid"),
88 Self::BadPath => write!(f, "Badly formatted path"),
89 Self::BadPhandle => write!(f, "Invalid phandle length or value"),
90 Self::BadState => write!(f, "Received incomplete device tree"),
91 Self::Truncated => write!(f, "Device tree or sub-block is improperly terminated"),
92 Self::BadMagic => write!(f, "Device tree header missing its magic number"),
93 Self::BadVersion => write!(f, "Device tree has a version which can't be handled"),
94 Self::BadStructure => write!(f, "Device tree has a corrupt structure block"),
95 Self::BadLayout => write!(f, "Device tree sub-blocks in unsupported order"),
96 Self::Internal => write!(f, "libfdt has failed an internal assertion"),
97 Self::BadNCells => write!(f, "Bad format or value of #address-cells or #size-cells"),
98 Self::BadValue => write!(f, "Unexpected property value"),
99 Self::BadOverlay => write!(f, "Overlay cannot be applied"),
100 Self::NoPhandles => write!(f, "Device tree doesn't have any phandle available anymore"),
101 Self::BadFlags => write!(f, "Invalid flag or invalid combination of flags"),
102 Self::Alignment => write!(f, "Device tree base address is not 8-byte aligned"),
103 Self::Unknown(e) => write!(f, "Unknown libfdt error '{e}'"),
104 }
105 }
106}
107
108/// Result type with FdtError enum.
109pub type Result<T> = result::Result<T, FdtError>;
110
111fn fdt_err(val: c_int) -> Result<c_int> {
112 if val >= 0 {
113 Ok(val)
114 } else {
115 Err(match -val as _ {
116 libfdt_bindgen::FDT_ERR_NOTFOUND => FdtError::NotFound,
117 libfdt_bindgen::FDT_ERR_EXISTS => FdtError::Exists,
118 libfdt_bindgen::FDT_ERR_NOSPACE => FdtError::NoSpace,
119 libfdt_bindgen::FDT_ERR_BADOFFSET => FdtError::BadOffset,
120 libfdt_bindgen::FDT_ERR_BADPATH => FdtError::BadPath,
121 libfdt_bindgen::FDT_ERR_BADPHANDLE => FdtError::BadPhandle,
122 libfdt_bindgen::FDT_ERR_BADSTATE => FdtError::BadState,
123 libfdt_bindgen::FDT_ERR_TRUNCATED => FdtError::Truncated,
124 libfdt_bindgen::FDT_ERR_BADMAGIC => FdtError::BadMagic,
125 libfdt_bindgen::FDT_ERR_BADVERSION => FdtError::BadVersion,
126 libfdt_bindgen::FDT_ERR_BADSTRUCTURE => FdtError::BadStructure,
127 libfdt_bindgen::FDT_ERR_BADLAYOUT => FdtError::BadLayout,
128 libfdt_bindgen::FDT_ERR_INTERNAL => FdtError::Internal,
129 libfdt_bindgen::FDT_ERR_BADNCELLS => FdtError::BadNCells,
130 libfdt_bindgen::FDT_ERR_BADVALUE => FdtError::BadValue,
131 libfdt_bindgen::FDT_ERR_BADOVERLAY => FdtError::BadOverlay,
132 libfdt_bindgen::FDT_ERR_NOPHANDLES => FdtError::NoPhandles,
133 libfdt_bindgen::FDT_ERR_BADFLAGS => FdtError::BadFlags,
134 libfdt_bindgen::FDT_ERR_ALIGNMENT => FdtError::Alignment,
135 _ => FdtError::Unknown(val),
136 })
137 }
138}
139
140fn fdt_err_expect_zero(val: c_int) -> Result<()> {
141 match fdt_err(val)? {
142 0 => Ok(()),
143 _ => Err(FdtError::Unknown(val)),
144 }
145}
146
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000147fn fdt_err_or_option(val: c_int) -> Result<Option<c_int>> {
148 match fdt_err(val) {
149 Ok(val) => Ok(Some(val)),
150 Err(FdtError::NotFound) => Ok(None),
151 Err(e) => Err(e),
152 }
153}
154
David Brazdil1baa9a92022-06-28 14:47:50 +0100155/// Value of a #address-cells property.
Andrew Walbranb39e6922022-12-05 17:01:20 +0000156#[derive(Copy, Clone, Debug, Eq, PartialEq)]
David Brazdil1baa9a92022-06-28 14:47:50 +0100157enum AddrCells {
158 Single = 1,
159 Double = 2,
Andrew Walbranb39e6922022-12-05 17:01:20 +0000160 Triple = 3,
David Brazdil1baa9a92022-06-28 14:47:50 +0100161}
162
163impl TryFrom<c_int> for AddrCells {
164 type Error = FdtError;
165
166 fn try_from(res: c_int) -> Result<Self> {
167 match fdt_err(res)? {
168 x if x == Self::Single as c_int => Ok(Self::Single),
169 x if x == Self::Double as c_int => Ok(Self::Double),
Andrew Walbranb39e6922022-12-05 17:01:20 +0000170 x if x == Self::Triple as c_int => Ok(Self::Triple),
David Brazdil1baa9a92022-06-28 14:47:50 +0100171 _ => Err(FdtError::BadNCells),
172 }
173 }
174}
175
176/// Value of a #size-cells property.
Andrew Walbranb39e6922022-12-05 17:01:20 +0000177#[derive(Copy, Clone, Debug, Eq, PartialEq)]
David Brazdil1baa9a92022-06-28 14:47:50 +0100178enum SizeCells {
179 None = 0,
180 Single = 1,
181 Double = 2,
182}
183
184impl TryFrom<c_int> for SizeCells {
185 type Error = FdtError;
186
187 fn try_from(res: c_int) -> Result<Self> {
188 match fdt_err(res)? {
189 x if x == Self::None as c_int => Ok(Self::None),
190 x if x == Self::Single as c_int => Ok(Self::Single),
191 x if x == Self::Double as c_int => Ok(Self::Double),
192 _ => Err(FdtError::BadNCells),
193 }
194 }
195}
196
Jaewan Kim72d10902023-10-12 21:59:26 +0900197/// DT property wrapper to abstract endianess changes
198#[repr(transparent)]
199#[derive(Debug)]
200struct FdtPropertyStruct(libfdt_bindgen::fdt_property);
201
202impl FdtPropertyStruct {
203 fn from_offset(fdt: &Fdt, offset: c_int) -> Result<&Self> {
204 let mut len = 0;
205 let prop =
206 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
207 unsafe { libfdt_bindgen::fdt_get_property_by_offset(fdt.as_ptr(), offset, &mut len) };
208 if prop.is_null() {
209 fdt_err(len)?;
210 return Err(FdtError::Internal); // shouldn't happen.
211 }
212 // SAFETY: prop is only returned when it points to valid libfdt_bindgen.
213 Ok(unsafe { &*prop.cast::<FdtPropertyStruct>() })
214 }
215
216 fn name_offset(&self) -> c_int {
217 u32::from_be(self.0.nameoff).try_into().unwrap()
218 }
219
220 fn data_len(&self) -> usize {
221 u32::from_be(self.0.len).try_into().unwrap()
222 }
223
224 fn data_ptr(&self) -> *const c_void {
225 self.0.data.as_ptr().cast::<_>()
226 }
227}
228
229/// DT property.
230#[derive(Clone, Copy, Debug)]
231pub struct FdtProperty<'a> {
232 fdt: &'a Fdt,
233 offset: c_int,
234 property: &'a FdtPropertyStruct,
235}
236
237impl<'a> FdtProperty<'a> {
238 fn new(fdt: &'a Fdt, offset: c_int) -> Result<Self> {
239 let property = FdtPropertyStruct::from_offset(fdt, offset)?;
240 Ok(Self { fdt, offset, property })
241 }
242
243 /// Returns the property name
244 pub fn name(&self) -> Result<&'a CStr> {
245 self.fdt.string(self.property.name_offset())
246 }
247
248 /// Returns the property value
249 pub fn value(&self) -> Result<&'a [u8]> {
250 self.fdt.get_from_ptr(self.property.data_ptr(), self.property.data_len())
251 }
252
253 fn next_property(&self) -> Result<Option<Self>> {
254 let ret =
255 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
256 unsafe { libfdt_bindgen::fdt_next_property_offset(self.fdt.as_ptr(), self.offset) };
257
258 fdt_err_or_option(ret)?.map(|offset| Self::new(self.fdt, offset)).transpose()
259 }
260}
261
David Brazdil1baa9a92022-06-28 14:47:50 +0100262/// DT node.
Alice Wang9d4df702023-05-25 14:14:12 +0000263#[derive(Clone, Copy, Debug)]
David Brazdil1baa9a92022-06-28 14:47:50 +0100264pub struct FdtNode<'a> {
265 fdt: &'a Fdt,
266 offset: c_int,
267}
268
269impl<'a> FdtNode<'a> {
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900270 /// Creates immutable node from a mutable node at the same offset.
Jiyong Park9c63cd12023-03-21 17:53:07 +0900271 pub fn from_mut(other: &'a FdtNodeMut) -> Self {
272 FdtNode { fdt: other.fdt, offset: other.offset }
273 }
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900274 /// Returns parent node.
David Brazdil1baa9a92022-06-28 14:47:50 +0100275 pub fn parent(&self) -> Result<Self> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000276 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
David Brazdil1baa9a92022-06-28 14:47:50 +0100277 let ret = unsafe { libfdt_bindgen::fdt_parent_offset(self.fdt.as_ptr(), self.offset) };
278
279 Ok(Self { fdt: self.fdt, offset: fdt_err(ret)? })
280 }
281
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900282 /// Returns the standard (deprecated) device_type <string> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000283 pub fn device_type(&self) -> Result<Option<&CStr>> {
David Brazdil1baa9a92022-06-28 14:47:50 +0100284 self.getprop_str(CStr::from_bytes_with_nul(b"device_type\0").unwrap())
285 }
286
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900287 /// Returns the standard reg <prop-encoded-array> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000288 pub fn reg(&self) -> Result<Option<RegIterator<'a>>> {
289 let reg = CStr::from_bytes_with_nul(b"reg\0").unwrap();
David Brazdil1baa9a92022-06-28 14:47:50 +0100290
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000291 if let Some(cells) = self.getprop_cells(reg)? {
292 let parent = self.parent()?;
David Brazdil1baa9a92022-06-28 14:47:50 +0100293
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000294 let addr_cells = parent.address_cells()?;
295 let size_cells = parent.size_cells()?;
296
297 Ok(Some(RegIterator::new(cells, addr_cells, size_cells)))
298 } else {
299 Ok(None)
300 }
David Brazdil1baa9a92022-06-28 14:47:50 +0100301 }
302
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900303 /// Returns the standard ranges property.
Andrew Walbranb39e6922022-12-05 17:01:20 +0000304 pub fn ranges<A, P, S>(&self) -> Result<Option<RangesIterator<'a, A, P, S>>> {
305 let ranges = CStr::from_bytes_with_nul(b"ranges\0").unwrap();
306 if let Some(cells) = self.getprop_cells(ranges)? {
307 let parent = self.parent()?;
308 let addr_cells = self.address_cells()?;
309 let parent_addr_cells = parent.address_cells()?;
310 let size_cells = self.size_cells()?;
311 Ok(Some(RangesIterator::<A, P, S>::new(
312 cells,
313 addr_cells,
314 parent_addr_cells,
315 size_cells,
316 )))
317 } else {
318 Ok(None)
319 }
320 }
321
Jaewan Kimaa638702023-09-19 13:34:01 +0900322 /// Returns the node name.
323 pub fn name(&self) -> Result<&'a CStr> {
324 let mut len: c_int = 0;
325 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor). On success, the
326 // function returns valid null terminating string and otherwise returned values are dropped.
327 let name = unsafe { libfdt_bindgen::fdt_get_name(self.fdt.as_ptr(), self.offset, &mut len) }
328 as *const c_void;
329 let len = usize::try_from(fdt_err(len)?).unwrap();
330 let name = self.fdt.get_from_ptr(name, len + 1)?;
331 CStr::from_bytes_with_nul(name).map_err(|_| FdtError::Internal)
332 }
333
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900334 /// Returns the value of a given <string> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000335 pub fn getprop_str(&self, name: &CStr) -> Result<Option<&CStr>> {
336 let value = if let Some(bytes) = self.getprop(name)? {
337 Some(CStr::from_bytes_with_nul(bytes).map_err(|_| FdtError::BadValue)?)
338 } else {
339 None
340 };
341 Ok(value)
David Brazdil1baa9a92022-06-28 14:47:50 +0100342 }
343
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900344 /// Returns the value of a given property as an array of cells.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000345 pub fn getprop_cells(&self, name: &CStr) -> Result<Option<CellIterator<'a>>> {
346 if let Some(cells) = self.getprop(name)? {
347 Ok(Some(CellIterator::new(cells)))
348 } else {
349 Ok(None)
350 }
David Brazdil1baa9a92022-06-28 14:47:50 +0100351 }
352
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900353 /// Returns the value of a given <u32> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000354 pub fn getprop_u32(&self, name: &CStr) -> Result<Option<u32>> {
355 let value = if let Some(bytes) = self.getprop(name)? {
356 Some(u32::from_be_bytes(bytes.try_into().map_err(|_| FdtError::BadValue)?))
357 } else {
358 None
359 };
360 Ok(value)
David Brazdil1baa9a92022-06-28 14:47:50 +0100361 }
362
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900363 /// Returns the value of a given <u64> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000364 pub fn getprop_u64(&self, name: &CStr) -> Result<Option<u64>> {
365 let value = if let Some(bytes) = self.getprop(name)? {
366 Some(u64::from_be_bytes(bytes.try_into().map_err(|_| FdtError::BadValue)?))
367 } else {
368 None
369 };
370 Ok(value)
David Brazdil1baa9a92022-06-28 14:47:50 +0100371 }
372
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900373 /// Returns the value of a given property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000374 pub fn getprop(&self, name: &CStr) -> Result<Option<&'a [u8]>> {
Jiyong Park9c63cd12023-03-21 17:53:07 +0900375 if let Some((prop, len)) = Self::getprop_internal(self.fdt, self.offset, name)? {
Jaewan Kimaa638702023-09-19 13:34:01 +0900376 Ok(Some(self.fdt.get_from_ptr(prop, len)?))
Jiyong Park9c63cd12023-03-21 17:53:07 +0900377 } else {
378 Ok(None) // property was not found
379 }
380 }
381
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900382 /// Returns the pointer and size of the property named `name`, in a node at offset `offset`, in
Jiyong Park9c63cd12023-03-21 17:53:07 +0900383 /// a device tree `fdt`. The pointer is guaranteed to be non-null, in which case error returns.
384 fn getprop_internal(
385 fdt: &'a Fdt,
386 offset: c_int,
387 name: &CStr,
388 ) -> Result<Option<(*const c_void, usize)>> {
David Brazdil1baa9a92022-06-28 14:47:50 +0100389 let mut len: i32 = 0;
Andrew Walbran84b9a232023-07-05 14:01:40 +0000390 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor) and the
David Brazdil1baa9a92022-06-28 14:47:50 +0100391 // function respects the passed number of characters.
392 let prop = unsafe {
393 libfdt_bindgen::fdt_getprop_namelen(
Jiyong Park9c63cd12023-03-21 17:53:07 +0900394 fdt.as_ptr(),
395 offset,
David Brazdil1baa9a92022-06-28 14:47:50 +0100396 name.as_ptr(),
397 // *_namelen functions don't include the trailing nul terminator in 'len'.
398 name.to_bytes().len().try_into().map_err(|_| FdtError::BadPath)?,
399 &mut len as *mut i32,
400 )
401 } as *const u8;
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000402
403 let Some(len) = fdt_err_or_option(len)? else {
404 return Ok(None); // Property was not found.
405 };
Jaewan Kimaa638702023-09-19 13:34:01 +0900406 let len = usize::try_from(len).unwrap();
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000407
David Brazdil1baa9a92022-06-28 14:47:50 +0100408 if prop.is_null() {
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000409 // We expected an error code in len but still received a valid value?!
410 return Err(FdtError::Internal);
David Brazdil1baa9a92022-06-28 14:47:50 +0100411 }
Jiyong Park9c63cd12023-03-21 17:53:07 +0900412 Ok(Some((prop.cast::<c_void>(), len)))
David Brazdil1baa9a92022-06-28 14:47:50 +0100413 }
414
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900415 /// Returns reference to the containing device tree.
David Brazdil1baa9a92022-06-28 14:47:50 +0100416 pub fn fdt(&self) -> &Fdt {
417 self.fdt
418 }
419
Alice Wang474c0ee2023-09-14 12:52:33 +0000420 /// Returns the compatible node of the given name that is next after this node.
421 pub fn next_compatible(self, compatible: &CStr) -> Result<Option<Self>> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000422 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000423 let ret = unsafe {
424 libfdt_bindgen::fdt_node_offset_by_compatible(
425 self.fdt.as_ptr(),
426 self.offset,
427 compatible.as_ptr(),
428 )
429 };
430
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000431 Ok(fdt_err_or_option(ret)?.map(|offset| Self { fdt: self.fdt, offset }))
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000432 }
433
Alice Wang474c0ee2023-09-14 12:52:33 +0000434 /// Returns the first range of `reg` in this node.
435 pub fn first_reg(&self) -> Result<Reg<u64>> {
436 self.reg()?.ok_or(FdtError::NotFound)?.next().ok_or(FdtError::NotFound)
437 }
438
David Brazdil1baa9a92022-06-28 14:47:50 +0100439 fn address_cells(&self) -> Result<AddrCells> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000440 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
David Brazdil1baa9a92022-06-28 14:47:50 +0100441 unsafe { libfdt_bindgen::fdt_address_cells(self.fdt.as_ptr(), self.offset) }
442 .try_into()
443 .map_err(|_| FdtError::Internal)
444 }
445
446 fn size_cells(&self) -> Result<SizeCells> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000447 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
David Brazdil1baa9a92022-06-28 14:47:50 +0100448 unsafe { libfdt_bindgen::fdt_size_cells(self.fdt.as_ptr(), self.offset) }
449 .try_into()
450 .map_err(|_| FdtError::Internal)
451 }
Jaewan Kimbc828d72023-09-19 15:52:08 +0900452
453 /// Returns an iterator of subnodes
454 pub fn subnodes(&'a self) -> Result<SubnodeIterator<'a>> {
455 SubnodeIterator::new(self)
456 }
457
458 fn first_subnode(&self) -> Result<Option<Self>> {
459 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
460 let ret = unsafe { libfdt_bindgen::fdt_first_subnode(self.fdt.as_ptr(), self.offset) };
461
462 Ok(fdt_err_or_option(ret)?.map(|offset| FdtNode { fdt: self.fdt, offset }))
463 }
464
465 fn next_subnode(&self) -> Result<Option<Self>> {
466 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
467 let ret = unsafe { libfdt_bindgen::fdt_next_subnode(self.fdt.as_ptr(), self.offset) };
468
469 Ok(fdt_err_or_option(ret)?.map(|offset| FdtNode { fdt: self.fdt, offset }))
470 }
Jaewan Kim72d10902023-10-12 21:59:26 +0900471
472 /// Returns an iterator of properties
473 pub fn properties(&'a self) -> Result<PropertyIterator<'a>> {
474 PropertyIterator::new(self)
475 }
476
477 fn first_property(&self) -> Result<Option<FdtProperty<'a>>> {
478 let ret =
479 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
480 unsafe { libfdt_bindgen::fdt_first_property_offset(self.fdt.as_ptr(), self.offset) };
481
482 fdt_err_or_option(ret)?.map(|offset| FdtProperty::new(self.fdt, offset)).transpose()
483 }
David Brazdil1baa9a92022-06-28 14:47:50 +0100484}
485
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000486/// Mutable FDT node.
487pub struct FdtNodeMut<'a> {
488 fdt: &'a mut Fdt,
489 offset: c_int,
490}
491
492impl<'a> FdtNodeMut<'a> {
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900493 /// Appends a property name-value (possibly empty) pair to the given node.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000494 pub fn appendprop<T: AsRef<[u8]>>(&mut self, name: &CStr, value: &T) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000495 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000496 let ret = unsafe {
497 libfdt_bindgen::fdt_appendprop(
498 self.fdt.as_mut_ptr(),
499 self.offset,
500 name.as_ptr(),
501 value.as_ref().as_ptr().cast::<c_void>(),
502 value.as_ref().len().try_into().map_err(|_| FdtError::BadValue)?,
503 )
504 };
505
506 fdt_err_expect_zero(ret)
507 }
508
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900509 /// Appends a (address, size) pair property to the given node.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000510 pub fn appendprop_addrrange(&mut self, name: &CStr, addr: u64, size: u64) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000511 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000512 let ret = unsafe {
513 libfdt_bindgen::fdt_appendprop_addrrange(
514 self.fdt.as_mut_ptr(),
515 self.parent()?.offset,
516 self.offset,
517 name.as_ptr(),
518 addr,
519 size,
520 )
521 };
522
523 fdt_err_expect_zero(ret)
524 }
525
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900526 /// Sets a property name-value pair to the given node.
527 ///
528 /// This may create a new prop or replace existing value.
Jaewan Kimba8929b2023-01-13 11:13:29 +0900529 pub fn setprop(&mut self, name: &CStr, value: &[u8]) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000530 // SAFETY: New value size is constrained to the DT totalsize
Jaewan Kimba8929b2023-01-13 11:13:29 +0900531 // (validated by underlying libfdt).
532 let ret = unsafe {
533 libfdt_bindgen::fdt_setprop(
534 self.fdt.as_mut_ptr(),
535 self.offset,
536 name.as_ptr(),
537 value.as_ptr().cast::<c_void>(),
538 value.len().try_into().map_err(|_| FdtError::BadValue)?,
539 )
540 };
541
542 fdt_err_expect_zero(ret)
543 }
544
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900545 /// Sets the value of the given property with the given value, and ensure that the given
546 /// value has the same length as the current value length.
547 ///
548 /// This can only be used to replace existing value.
Jiyong Park9c63cd12023-03-21 17:53:07 +0900549 pub fn setprop_inplace(&mut self, name: &CStr, value: &[u8]) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000550 // SAFETY: fdt size is not altered
Jiyong Park9c63cd12023-03-21 17:53:07 +0900551 let ret = unsafe {
552 libfdt_bindgen::fdt_setprop_inplace(
553 self.fdt.as_mut_ptr(),
554 self.offset,
555 name.as_ptr(),
556 value.as_ptr().cast::<c_void>(),
557 value.len().try_into().map_err(|_| FdtError::BadValue)?,
558 )
559 };
560
561 fdt_err_expect_zero(ret)
562 }
563
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900564 /// Sets the value of the given (address, size) pair property with the given value, and
565 /// ensure that the given value has the same length as the current value length.
566 ///
567 /// This can only be used to replace existing value.
Pierre-Clément Tosic27c4272023-05-19 15:46:26 +0000568 pub fn setprop_addrrange_inplace(&mut self, name: &CStr, addr: u64, size: u64) -> Result<()> {
569 let pair = [addr.to_be(), size.to_be()];
570 self.setprop_inplace(name, pair.as_bytes())
571 }
572
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900573 /// Sets a flag-like empty property.
574 ///
575 /// This may create a new prop or replace existing value.
Pierre-Clément Tosi4ba79662023-02-13 11:22:41 +0000576 pub fn setprop_empty(&mut self, name: &CStr) -> Result<()> {
577 self.setprop(name, &[])
578 }
579
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900580 /// Deletes the given property.
Pierre-Clément Tosi4ba79662023-02-13 11:22:41 +0000581 pub fn delprop(&mut self, name: &CStr) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000582 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor) when the
Pierre-Clément Tosi4ba79662023-02-13 11:22:41 +0000583 // library locates the node's property. Removing the property may shift the offsets of
584 // other nodes and properties but the borrow checker should prevent this function from
585 // being called when FdtNode instances are in use.
586 let ret = unsafe {
587 libfdt_bindgen::fdt_delprop(self.fdt.as_mut_ptr(), self.offset, name.as_ptr())
588 };
589
590 fdt_err_expect_zero(ret)
591 }
592
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900593 /// Sets the given property with FDT_NOP, effectively removing it from the DT.
Pierre-Clément Tosibe3a97b2023-05-19 14:56:23 +0000594 pub fn nop_property(&mut self, name: &CStr) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000595 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor) when the
Pierre-Clément Tosibe3a97b2023-05-19 14:56:23 +0000596 // library locates the node's property.
597 let ret = unsafe {
598 libfdt_bindgen::fdt_nop_property(self.fdt.as_mut_ptr(), self.offset, name.as_ptr())
599 };
600
601 fdt_err_expect_zero(ret)
602 }
603
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900604 /// Trims the size of the given property to new_size.
Jiyong Park9c63cd12023-03-21 17:53:07 +0900605 pub fn trimprop(&mut self, name: &CStr, new_size: usize) -> Result<()> {
606 let (prop, len) =
607 FdtNode::getprop_internal(self.fdt, self.offset, name)?.ok_or(FdtError::NotFound)?;
608 if len == new_size {
609 return Ok(());
610 }
611 if new_size > len {
612 return Err(FdtError::NoSpace);
613 }
614
Andrew Walbran84b9a232023-07-05 14:01:40 +0000615 // SAFETY: new_size is smaller than the old size
Jiyong Park9c63cd12023-03-21 17:53:07 +0900616 let ret = unsafe {
617 libfdt_bindgen::fdt_setprop(
618 self.fdt.as_mut_ptr(),
619 self.offset,
620 name.as_ptr(),
621 prop.cast::<c_void>(),
622 new_size.try_into().map_err(|_| FdtError::BadValue)?,
623 )
624 };
625
626 fdt_err_expect_zero(ret)
627 }
628
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900629 /// Returns reference to the containing device tree.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000630 pub fn fdt(&mut self) -> &mut Fdt {
631 self.fdt
632 }
633
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900634 /// Adds a new subnode to the given node and return it as a FdtNodeMut on success.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000635 pub fn add_subnode(&'a mut self, name: &CStr) -> Result<Self> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000636 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor).
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000637 let ret = unsafe {
638 libfdt_bindgen::fdt_add_subnode(self.fdt.as_mut_ptr(), self.offset, name.as_ptr())
639 };
640
641 Ok(Self { fdt: self.fdt, offset: fdt_err(ret)? })
642 }
643
644 fn parent(&'a self) -> Result<FdtNode<'a>> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000645 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000646 let ret = unsafe { libfdt_bindgen::fdt_parent_offset(self.fdt.as_ptr(), self.offset) };
647
648 Ok(FdtNode { fdt: &*self.fdt, offset: fdt_err(ret)? })
649 }
Jiyong Park9c63cd12023-03-21 17:53:07 +0900650
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900651 /// Returns the compatible node of the given name that is next after this node.
Jiyong Park9c63cd12023-03-21 17:53:07 +0900652 pub fn next_compatible(self, compatible: &CStr) -> Result<Option<Self>> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000653 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
Jiyong Park9c63cd12023-03-21 17:53:07 +0900654 let ret = unsafe {
655 libfdt_bindgen::fdt_node_offset_by_compatible(
656 self.fdt.as_ptr(),
657 self.offset,
658 compatible.as_ptr(),
659 )
660 };
661
662 Ok(fdt_err_or_option(ret)?.map(|offset| Self { fdt: self.fdt, offset }))
663 }
664
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900665 /// Deletes the node effectively by overwriting this node and its subtree with nop tags.
666 /// Returns the next compatible node of the given name.
Jiyong Park9c63cd12023-03-21 17:53:07 +0900667 // Side note: without this, filterint out excessive compatible nodes from the DT is impossible.
668 // The reason is that libfdt ensures that the node from where the search for the next
669 // compatible node is started is always a valid one -- except for the special case of offset =
670 // -1 which is to find the first compatible node. So, we can't delete a node and then find the
671 // next compatible node from it.
672 //
673 // We can't do in the opposite direction either. If we call next_compatible to find the next
674 // node, and delete the current node, the Rust borrow checker kicks in. The next node has a
675 // mutable reference to DT, so we can't use current node (which also has a mutable reference to
676 // DT).
677 pub fn delete_and_next_compatible(self, compatible: &CStr) -> Result<Option<Self>> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000678 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
Jiyong Park9c63cd12023-03-21 17:53:07 +0900679 let ret = unsafe {
680 libfdt_bindgen::fdt_node_offset_by_compatible(
681 self.fdt.as_ptr(),
682 self.offset,
683 compatible.as_ptr(),
684 )
685 };
686 let next_offset = fdt_err_or_option(ret)?;
687
Andrew Walbran84b9a232023-07-05 14:01:40 +0000688 // SAFETY: fdt_nop_node alter only the bytes in the blob which contain the node and its
Jiyong Park9c63cd12023-03-21 17:53:07 +0900689 // properties and subnodes, and will not alter or move any other part of the tree.
690 let ret = unsafe { libfdt_bindgen::fdt_nop_node(self.fdt.as_mut_ptr(), self.offset) };
691 fdt_err_expect_zero(ret)?;
692
693 Ok(next_offset.map(|offset| Self { fdt: self.fdt, offset }))
694 }
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000695}
696
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000697/// Wrapper around low-level libfdt functions.
Alice Wang9d4df702023-05-25 14:14:12 +0000698#[derive(Debug)]
David Brazdil1baa9a92022-06-28 14:47:50 +0100699#[repr(transparent)]
700pub struct Fdt {
Pierre-Clément Tosief2030e2022-11-28 11:21:20 +0000701 buffer: [u8],
David Brazdil1baa9a92022-06-28 14:47:50 +0100702}
703
704impl Fdt {
705 /// Wraps a slice containing a Flattened Device Tree.
706 ///
707 /// Fails if the FDT does not pass validation.
708 pub fn from_slice(fdt: &[u8]) -> Result<&Self> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000709 // SAFETY: The FDT will be validated before it is returned.
David Brazdil1baa9a92022-06-28 14:47:50 +0100710 let fdt = unsafe { Self::unchecked_from_slice(fdt) };
711 fdt.check_full()?;
712 Ok(fdt)
713 }
714
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000715 /// Wraps a mutable slice containing a Flattened Device Tree.
716 ///
717 /// Fails if the FDT does not pass validation.
718 pub fn from_mut_slice(fdt: &mut [u8]) -> Result<&mut Self> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000719 // SAFETY: The FDT will be validated before it is returned.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000720 let fdt = unsafe { Self::unchecked_from_mut_slice(fdt) };
721 fdt.check_full()?;
722 Ok(fdt)
723 }
724
Jaewan Kim4cf20aa2023-04-03 10:25:38 +0900725 /// Creates an empty Flattened Device Tree with a mutable slice.
726 pub fn create_empty_tree(fdt: &mut [u8]) -> Result<&mut Self> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000727 // SAFETY: fdt_create_empty_tree() only write within the specified length,
Jaewan Kim4cf20aa2023-04-03 10:25:38 +0900728 // and returns error if buffer was insufficient.
729 // There will be no memory write outside of the given fdt.
730 let ret = unsafe {
731 libfdt_bindgen::fdt_create_empty_tree(
732 fdt.as_mut_ptr().cast::<c_void>(),
733 fdt.len() as i32,
734 )
735 };
736 fdt_err_expect_zero(ret)?;
737
Andrew Walbran84b9a232023-07-05 14:01:40 +0000738 // SAFETY: The FDT will be validated before it is returned.
Jaewan Kim4cf20aa2023-04-03 10:25:38 +0900739 let fdt = unsafe { Self::unchecked_from_mut_slice(fdt) };
740 fdt.check_full()?;
741
742 Ok(fdt)
743 }
744
David Brazdil1baa9a92022-06-28 14:47:50 +0100745 /// Wraps a slice containing a Flattened Device Tree.
746 ///
747 /// # Safety
748 ///
749 /// The returned FDT might be invalid, only use on slices containing a valid DT.
750 pub unsafe fn unchecked_from_slice(fdt: &[u8]) -> &Self {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000751 // SAFETY: Fdt is a wrapper around a [u8], so the transmute is valid. The caller is
752 // responsible for ensuring that it is actually a valid FDT.
753 unsafe { mem::transmute::<&[u8], &Self>(fdt) }
David Brazdil1baa9a92022-06-28 14:47:50 +0100754 }
755
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000756 /// Wraps a mutable slice containing a Flattened Device Tree.
757 ///
758 /// # Safety
759 ///
760 /// The returned FDT might be invalid, only use on slices containing a valid DT.
761 pub unsafe fn unchecked_from_mut_slice(fdt: &mut [u8]) -> &mut Self {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000762 // SAFETY: Fdt is a wrapper around a [u8], so the transmute is valid. The caller is
763 // responsible for ensuring that it is actually a valid FDT.
764 unsafe { mem::transmute::<&mut [u8], &mut Self>(fdt) }
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000765 }
766
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900767 /// Updates this FDT from a slice containing another FDT.
Jiyong Parke9d87e82023-03-21 19:28:40 +0900768 pub fn copy_from_slice(&mut self, new_fdt: &[u8]) -> Result<()> {
769 if self.buffer.len() < new_fdt.len() {
770 Err(FdtError::NoSpace)
771 } else {
772 let totalsize = self.totalsize();
773 self.buffer[..new_fdt.len()].clone_from_slice(new_fdt);
774 // Zeroize the remaining part. We zeroize up to the size of the original DT because
775 // zeroizing the entire buffer (max 2MB) is not necessary and may increase the VM boot
776 // time.
777 self.buffer[new_fdt.len()..max(new_fdt.len(), totalsize)].fill(0_u8);
778 Ok(())
779 }
780 }
781
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900782 /// Unpacks the DT to cover the whole slice it is contained in.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000783 pub fn unpack(&mut self) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000784 // SAFETY: "Opens" the DT in-place (supported use-case) by updating its header and
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000785 // internal structures to make use of the whole self.fdt slice but performs no accesses
786 // outside of it and leaves the DT in a state that will be detected by other functions.
787 let ret = unsafe {
788 libfdt_bindgen::fdt_open_into(
789 self.as_ptr(),
790 self.as_mut_ptr(),
791 self.capacity().try_into().map_err(|_| FdtError::Internal)?,
792 )
793 };
794 fdt_err_expect_zero(ret)
795 }
796
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900797 /// Packs the DT to take a minimum amount of memory.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000798 ///
799 /// Doesn't shrink the underlying memory slice.
800 pub fn pack(&mut self) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000801 // SAFETY: "Closes" the DT in-place by updating its header and relocating its structs.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000802 let ret = unsafe { libfdt_bindgen::fdt_pack(self.as_mut_ptr()) };
803 fdt_err_expect_zero(ret)
804 }
805
Pierre-Clément Tosi90e19352022-11-21 17:11:48 +0000806 /// Applies a DT overlay on the base DT.
807 ///
808 /// # Safety
809 ///
810 /// On failure, the library corrupts the DT and overlay so both must be discarded.
811 pub unsafe fn apply_overlay<'a>(&'a mut self, overlay: &'a mut Fdt) -> Result<&'a mut Self> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000812 let ret =
813 // SAFETY: Both pointers are valid because they come from references, and fdt_overlay_apply
814 // doesn't keep them after it returns. It may corrupt their contents if there is an error,
815 // but that's our caller's responsibility.
816 unsafe { libfdt_bindgen::fdt_overlay_apply(self.as_mut_ptr(), overlay.as_mut_ptr()) };
817 fdt_err_expect_zero(ret)?;
Pierre-Clément Tosi90e19352022-11-21 17:11:48 +0000818 Ok(self)
819 }
820
Alice Wang2422bdc2023-06-12 08:37:55 +0000821 /// Returns an iterator of memory banks specified the "/memory" node.
822 /// Throws an error when the "/memory" is not found in the device tree.
David Brazdil1baa9a92022-06-28 14:47:50 +0100823 ///
824 /// NOTE: This does not support individual "/memory@XXXX" banks.
Alice Wang2422bdc2023-06-12 08:37:55 +0000825 pub fn memory(&self) -> Result<MemRegIterator> {
826 let memory_node_name = CStr::from_bytes_with_nul(b"/memory\0").unwrap();
827 let memory_device_type = CStr::from_bytes_with_nul(b"memory\0").unwrap();
David Brazdil1baa9a92022-06-28 14:47:50 +0100828
Alice Wang2422bdc2023-06-12 08:37:55 +0000829 let node = self.node(memory_node_name)?.ok_or(FdtError::NotFound)?;
830 if node.device_type()? != Some(memory_device_type) {
831 return Err(FdtError::BadValue);
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000832 }
Alice Wang2422bdc2023-06-12 08:37:55 +0000833 node.reg()?.ok_or(FdtError::BadValue).map(MemRegIterator::new)
834 }
835
836 /// Returns the first memory range in the `/memory` node.
837 pub fn first_memory_range(&self) -> Result<Range<usize>> {
838 self.memory()?.next().ok_or(FdtError::NotFound)
David Brazdil1baa9a92022-06-28 14:47:50 +0100839 }
840
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900841 /// Returns the standard /chosen node.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000842 pub fn chosen(&self) -> Result<Option<FdtNode>> {
David Brazdil1baa9a92022-06-28 14:47:50 +0100843 self.node(CStr::from_bytes_with_nul(b"/chosen\0").unwrap())
844 }
845
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900846 /// Returns the standard /chosen node as mutable.
Pierre-Clément Tosi4ba79662023-02-13 11:22:41 +0000847 pub fn chosen_mut(&mut self) -> Result<Option<FdtNodeMut>> {
848 self.node_mut(CStr::from_bytes_with_nul(b"/chosen\0").unwrap())
849 }
850
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900851 /// Returns the root node of the tree.
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000852 pub fn root(&self) -> Result<FdtNode> {
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000853 self.node(CStr::from_bytes_with_nul(b"/\0").unwrap())?.ok_or(FdtError::Internal)
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000854 }
855
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900856 /// Returns a tree node by its full path.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000857 pub fn node(&self, path: &CStr) -> Result<Option<FdtNode>> {
858 Ok(self.path_offset(path)?.map(|offset| FdtNode { fdt: self, offset }))
David Brazdil1baa9a92022-06-28 14:47:50 +0100859 }
860
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000861 /// Iterate over nodes with a given compatible string.
862 pub fn compatible_nodes<'a>(&'a self, compatible: &'a CStr) -> Result<CompatibleIterator<'a>> {
863 CompatibleIterator::new(self, compatible)
864 }
865
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900866 /// Returns the mutable root node of the tree.
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000867 pub fn root_mut(&mut self) -> Result<FdtNodeMut> {
868 self.node_mut(CStr::from_bytes_with_nul(b"/\0").unwrap())?.ok_or(FdtError::Internal)
869 }
870
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900871 /// Returns a mutable tree node by its full path.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000872 pub fn node_mut(&mut self, path: &CStr) -> Result<Option<FdtNodeMut>> {
873 Ok(self.path_offset(path)?.map(|offset| FdtNodeMut { fdt: self, offset }))
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000874 }
875
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900876 /// Returns the device tree as a slice (may be smaller than the containing buffer).
Pierre-Clément Tosidb74cb12022-12-08 13:56:25 +0000877 pub fn as_slice(&self) -> &[u8] {
878 &self.buffer[..self.totalsize()]
879 }
880
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000881 fn path_offset(&self, path: &CStr) -> Result<Option<c_int>> {
David Brazdil1baa9a92022-06-28 14:47:50 +0100882 let len = path.to_bytes().len().try_into().map_err(|_| FdtError::BadPath)?;
Andrew Walbran84b9a232023-07-05 14:01:40 +0000883 // SAFETY: Accesses are constrained to the DT totalsize (validated by ctor) and the
David Brazdil1baa9a92022-06-28 14:47:50 +0100884 // function respects the passed number of characters.
885 let ret = unsafe {
886 // *_namelen functions don't include the trailing nul terminator in 'len'.
887 libfdt_bindgen::fdt_path_offset_namelen(self.as_ptr(), path.as_ptr(), len)
888 };
889
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000890 fdt_err_or_option(ret)
David Brazdil1baa9a92022-06-28 14:47:50 +0100891 }
892
893 fn check_full(&self) -> Result<()> {
Andrew Walbran84b9a232023-07-05 14:01:40 +0000894 // SAFETY: Only performs read accesses within the limits of the slice. If successful, this
David Brazdil1baa9a92022-06-28 14:47:50 +0100895 // call guarantees to other unsafe calls that the header contains a valid totalsize (w.r.t.
896 // 'len' i.e. the self.fdt slice) that those C functions can use to perform bounds
897 // checking. The library doesn't maintain an internal state (such as pointers) between
898 // calls as it expects the client code to keep track of the objects (DT, nodes, ...).
Pierre-Clément Tosi02017da2023-09-26 17:57:04 +0100899 let ret = unsafe { libfdt_bindgen::fdt_check_full(self.as_ptr(), self.capacity()) };
David Brazdil1baa9a92022-06-28 14:47:50 +0100900 fdt_err_expect_zero(ret)
901 }
902
Jaewan Kimaa638702023-09-19 13:34:01 +0900903 fn get_from_ptr(&self, ptr: *const c_void, len: usize) -> Result<&[u8]> {
904 let ptr = ptr as usize;
905 let offset = ptr.checked_sub(self.as_ptr() as usize).ok_or(FdtError::Internal)?;
906 self.buffer.get(offset..(offset + len)).ok_or(FdtError::Internal)
907 }
908
Jaewan Kim72d10902023-10-12 21:59:26 +0900909 fn string(&self, offset: c_int) -> Result<&CStr> {
910 // SAFETY: Accesses (read-only) are constrained to the DT totalsize.
911 let res = unsafe { libfdt_bindgen::fdt_string(self.as_ptr(), offset) };
912 if res.is_null() {
913 return Err(FdtError::Internal);
914 }
915
916 // SAFETY: Non-null return from fdt_string() is valid null-terminating string within FDT.
917 Ok(unsafe { CStr::from_ptr(res) })
918 }
919
Jaewan Kimb3dcfc22023-09-20 10:20:52 +0900920 /// Returns a shared pointer to the device tree.
Pierre-Clément Tosi8036b4f2023-02-17 10:31:31 +0000921 pub fn as_ptr(&self) -> *const c_void {
Pierre-Clément Tosi0dcc75e2023-05-02 13:43:55 +0000922 self.buffer.as_ptr().cast::<_>()
David Brazdil1baa9a92022-06-28 14:47:50 +0100923 }
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000924
925 fn as_mut_ptr(&mut self) -> *mut c_void {
Pierre-Clément Tosi0dcc75e2023-05-02 13:43:55 +0000926 self.buffer.as_mut_ptr().cast::<_>()
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000927 }
928
929 fn capacity(&self) -> usize {
Pierre-Clément Tosief2030e2022-11-28 11:21:20 +0000930 self.buffer.len()
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000931 }
Pierre-Clément Tosidb74cb12022-12-08 13:56:25 +0000932
933 fn header(&self) -> &libfdt_bindgen::fdt_header {
Pierre-Clément Tosi0dcc75e2023-05-02 13:43:55 +0000934 let p = self.as_ptr().cast::<_>();
Andrew Walbran84b9a232023-07-05 14:01:40 +0000935 // SAFETY: A valid FDT (verified by constructor) must contain a valid fdt_header.
Pierre-Clément Tosi0dcc75e2023-05-02 13:43:55 +0000936 unsafe { &*p }
Pierre-Clément Tosidb74cb12022-12-08 13:56:25 +0000937 }
938
939 fn totalsize(&self) -> usize {
940 u32::from_be(self.header().totalsize) as usize
941 }
David Brazdil1baa9a92022-06-28 14:47:50 +0100942}