blob: ff1db63c0fda07d185de5bfe5bd32a9b788c2b5d [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]
Pierre-Clément Tosib244d932022-11-24 16:45:53 +000019#![feature(let_else)] // Stabilized in 1.65.0
David Brazdil1baa9a92022-06-28 14:47:50 +010020
21use core::ffi::{c_int, c_void, CStr};
22use core::fmt;
23use core::mem;
24use core::ops::Range;
25use core::result;
26use core::slice;
27
28/// Error type corresponding to libfdt error codes.
29#[derive(Clone, Copy, Debug, Eq, PartialEq)]
30pub enum FdtError {
31 /// FDT_ERR_NOTFOUND
32 NotFound,
33 /// FDT_ERR_EXISTS
34 Exists,
35 /// FDT_ERR_NOSPACE
36 NoSpace,
37 /// FDT_ERR_BADOFFSET
38 BadOffset,
39 /// FDT_ERR_BADPATH
40 BadPath,
41 /// FDT_ERR_BADPHANDLE
42 BadPhandle,
43 /// FDT_ERR_BADSTATE
44 BadState,
45 /// FDT_ERR_TRUNCATED
46 Truncated,
47 /// FDT_ERR_BADMAGIC
48 BadMagic,
49 /// FDT_ERR_BADVERSION
50 BadVersion,
51 /// FDT_ERR_BADSTRUCTURE
52 BadStructure,
53 /// FDT_ERR_BADLAYOUT
54 BadLayout,
55 /// FDT_ERR_INTERNAL
56 Internal,
57 /// FDT_ERR_BADNCELLS
58 BadNCells,
59 /// FDT_ERR_BADVALUE
60 BadValue,
61 /// FDT_ERR_BADOVERLAY
62 BadOverlay,
63 /// FDT_ERR_NOPHANDLES
64 NoPhandles,
65 /// FDT_ERR_BADFLAGS
66 BadFlags,
67 /// FDT_ERR_ALIGNMENT
68 Alignment,
69 /// Unexpected error code
70 Unknown(i32),
71}
72
73impl fmt::Display for FdtError {
74 /// Prints error messages from libfdt.h documentation.
75 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
76 match self {
77 Self::NotFound => write!(f, "The requested node or property does not exist"),
78 Self::Exists => write!(f, "Attempted to create an existing node or property"),
79 Self::NoSpace => write!(f, "Insufficient buffer space to contain the expanded tree"),
80 Self::BadOffset => write!(f, "Structure block offset is out-of-bounds or invalid"),
81 Self::BadPath => write!(f, "Badly formatted path"),
82 Self::BadPhandle => write!(f, "Invalid phandle length or value"),
83 Self::BadState => write!(f, "Received incomplete device tree"),
84 Self::Truncated => write!(f, "Device tree or sub-block is improperly terminated"),
85 Self::BadMagic => write!(f, "Device tree header missing its magic number"),
86 Self::BadVersion => write!(f, "Device tree has a version which can't be handled"),
87 Self::BadStructure => write!(f, "Device tree has a corrupt structure block"),
88 Self::BadLayout => write!(f, "Device tree sub-blocks in unsupported order"),
89 Self::Internal => write!(f, "libfdt has failed an internal assertion"),
90 Self::BadNCells => write!(f, "Bad format or value of #address-cells or #size-cells"),
91 Self::BadValue => write!(f, "Unexpected property value"),
92 Self::BadOverlay => write!(f, "Overlay cannot be applied"),
93 Self::NoPhandles => write!(f, "Device tree doesn't have any phandle available anymore"),
94 Self::BadFlags => write!(f, "Invalid flag or invalid combination of flags"),
95 Self::Alignment => write!(f, "Device tree base address is not 8-byte aligned"),
96 Self::Unknown(e) => write!(f, "Unknown libfdt error '{e}'"),
97 }
98 }
99}
100
101/// Result type with FdtError enum.
102pub type Result<T> = result::Result<T, FdtError>;
103
104fn fdt_err(val: c_int) -> Result<c_int> {
105 if val >= 0 {
106 Ok(val)
107 } else {
108 Err(match -val as _ {
109 libfdt_bindgen::FDT_ERR_NOTFOUND => FdtError::NotFound,
110 libfdt_bindgen::FDT_ERR_EXISTS => FdtError::Exists,
111 libfdt_bindgen::FDT_ERR_NOSPACE => FdtError::NoSpace,
112 libfdt_bindgen::FDT_ERR_BADOFFSET => FdtError::BadOffset,
113 libfdt_bindgen::FDT_ERR_BADPATH => FdtError::BadPath,
114 libfdt_bindgen::FDT_ERR_BADPHANDLE => FdtError::BadPhandle,
115 libfdt_bindgen::FDT_ERR_BADSTATE => FdtError::BadState,
116 libfdt_bindgen::FDT_ERR_TRUNCATED => FdtError::Truncated,
117 libfdt_bindgen::FDT_ERR_BADMAGIC => FdtError::BadMagic,
118 libfdt_bindgen::FDT_ERR_BADVERSION => FdtError::BadVersion,
119 libfdt_bindgen::FDT_ERR_BADSTRUCTURE => FdtError::BadStructure,
120 libfdt_bindgen::FDT_ERR_BADLAYOUT => FdtError::BadLayout,
121 libfdt_bindgen::FDT_ERR_INTERNAL => FdtError::Internal,
122 libfdt_bindgen::FDT_ERR_BADNCELLS => FdtError::BadNCells,
123 libfdt_bindgen::FDT_ERR_BADVALUE => FdtError::BadValue,
124 libfdt_bindgen::FDT_ERR_BADOVERLAY => FdtError::BadOverlay,
125 libfdt_bindgen::FDT_ERR_NOPHANDLES => FdtError::NoPhandles,
126 libfdt_bindgen::FDT_ERR_BADFLAGS => FdtError::BadFlags,
127 libfdt_bindgen::FDT_ERR_ALIGNMENT => FdtError::Alignment,
128 _ => FdtError::Unknown(val),
129 })
130 }
131}
132
133fn fdt_err_expect_zero(val: c_int) -> Result<()> {
134 match fdt_err(val)? {
135 0 => Ok(()),
136 _ => Err(FdtError::Unknown(val)),
137 }
138}
139
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000140fn fdt_err_or_option(val: c_int) -> Result<Option<c_int>> {
141 match fdt_err(val) {
142 Ok(val) => Ok(Some(val)),
143 Err(FdtError::NotFound) => Ok(None),
144 Err(e) => Err(e),
145 }
146}
147
David Brazdil1baa9a92022-06-28 14:47:50 +0100148/// Value of a #address-cells property.
149#[derive(Copy, Clone, Debug)]
150enum AddrCells {
151 Single = 1,
152 Double = 2,
153}
154
155impl TryFrom<c_int> for AddrCells {
156 type Error = FdtError;
157
158 fn try_from(res: c_int) -> Result<Self> {
159 match fdt_err(res)? {
160 x if x == Self::Single as c_int => Ok(Self::Single),
161 x if x == Self::Double as c_int => Ok(Self::Double),
162 _ => Err(FdtError::BadNCells),
163 }
164 }
165}
166
167/// Value of a #size-cells property.
168#[derive(Copy, Clone, Debug)]
169enum SizeCells {
170 None = 0,
171 Single = 1,
172 Double = 2,
173}
174
175impl TryFrom<c_int> for SizeCells {
176 type Error = FdtError;
177
178 fn try_from(res: c_int) -> Result<Self> {
179 match fdt_err(res)? {
180 x if x == Self::None as c_int => Ok(Self::None),
181 x if x == Self::Single as c_int => Ok(Self::Single),
182 x if x == Self::Double as c_int => Ok(Self::Double),
183 _ => Err(FdtError::BadNCells),
184 }
185 }
186}
187
188/// Iterator over cells of a DT property.
189#[derive(Debug)]
190pub struct CellIterator<'a> {
191 chunks: slice::ChunksExact<'a, u8>,
192}
193
194impl<'a> CellIterator<'a> {
195 fn new(bytes: &'a [u8]) -> Self {
196 const CHUNK_SIZE: usize = mem::size_of::<<CellIterator as Iterator>::Item>();
197
198 Self { chunks: bytes.chunks_exact(CHUNK_SIZE) }
199 }
200}
201
202impl<'a> Iterator for CellIterator<'a> {
203 type Item = u32;
204
205 fn next(&mut self) -> Option<Self::Item> {
206 Some(Self::Item::from_be_bytes(self.chunks.next()?.try_into().ok()?))
207 }
208}
209
210/// Iterator over a 'reg' property of a DT node.
211#[derive(Debug)]
212pub struct RegIterator<'a> {
213 cells: CellIterator<'a>,
214 addr_cells: AddrCells,
215 size_cells: SizeCells,
216}
217
218/// Represents a contiguous region within the address space defined by the parent bus.
219/// Commonly means the offsets and lengths of MMIO blocks, but may have a different meaning on some
220/// bus types. Addresses in the address space defined by the root node are CPU real addresses.
221#[derive(Copy, Clone, Debug)]
222pub struct Reg<T> {
223 /// Base address of the region.
224 pub addr: T,
225 /// Size of the region (optional).
226 pub size: Option<T>,
227}
228
229impl<'a> RegIterator<'a> {
230 fn new(cells: CellIterator<'a>, addr_cells: AddrCells, size_cells: SizeCells) -> Self {
231 Self { cells, addr_cells, size_cells }
232 }
233}
234
235impl<'a> Iterator for RegIterator<'a> {
236 type Item = Reg<u64>;
237
238 fn next(&mut self) -> Option<Self::Item> {
239 let make_double = |a, b| (u64::from(a) << 32) | u64::from(b);
240
241 let addr = match self.addr_cells {
242 AddrCells::Single => self.cells.next()?.into(),
243 AddrCells::Double => make_double(self.cells.next()?, self.cells.next()?),
244 };
245 // If the parent node specifies a value of 0 for #size-cells, 'size' shall be omitted.
246 let size = match self.size_cells {
247 SizeCells::None => None,
248 SizeCells::Single => Some(self.cells.next()?.into()),
249 SizeCells::Double => Some(make_double(self.cells.next()?, self.cells.next()?)),
250 };
251
252 Some(Self::Item { addr, size })
253 }
254}
255
256/// Iterator over the address ranges defined by the /memory/ node.
257#[derive(Debug)]
258pub struct MemRegIterator<'a> {
259 reg: RegIterator<'a>,
260}
261
262impl<'a> MemRegIterator<'a> {
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000263 fn new(reg: RegIterator<'a>) -> Self {
264 Self { reg }
David Brazdil1baa9a92022-06-28 14:47:50 +0100265 }
266}
267
268impl<'a> Iterator for MemRegIterator<'a> {
269 type Item = Range<usize>;
270
271 fn next(&mut self) -> Option<Self::Item> {
272 let next = self.reg.next()?;
273 let addr = usize::try_from(next.addr).ok()?;
274 let size = usize::try_from(next.size?).ok()?;
275
276 Some(addr..addr.checked_add(size)?)
277 }
278}
279
280/// DT node.
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000281#[derive(Clone, Copy)]
David Brazdil1baa9a92022-06-28 14:47:50 +0100282pub struct FdtNode<'a> {
283 fdt: &'a Fdt,
284 offset: c_int,
285}
286
287impl<'a> FdtNode<'a> {
288 /// Find parent node.
289 pub fn parent(&self) -> Result<Self> {
290 // SAFETY - Accesses (read-only) are constrained to the DT totalsize.
291 let ret = unsafe { libfdt_bindgen::fdt_parent_offset(self.fdt.as_ptr(), self.offset) };
292
293 Ok(Self { fdt: self.fdt, offset: fdt_err(ret)? })
294 }
295
296 /// Retrieve the standard (deprecated) device_type <string> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000297 pub fn device_type(&self) -> Result<Option<&CStr>> {
David Brazdil1baa9a92022-06-28 14:47:50 +0100298 self.getprop_str(CStr::from_bytes_with_nul(b"device_type\0").unwrap())
299 }
300
301 /// Retrieve the standard reg <prop-encoded-array> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000302 pub fn reg(&self) -> Result<Option<RegIterator<'a>>> {
303 let reg = CStr::from_bytes_with_nul(b"reg\0").unwrap();
David Brazdil1baa9a92022-06-28 14:47:50 +0100304
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000305 if let Some(cells) = self.getprop_cells(reg)? {
306 let parent = self.parent()?;
David Brazdil1baa9a92022-06-28 14:47:50 +0100307
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000308 let addr_cells = parent.address_cells()?;
309 let size_cells = parent.size_cells()?;
310
311 Ok(Some(RegIterator::new(cells, addr_cells, size_cells)))
312 } else {
313 Ok(None)
314 }
David Brazdil1baa9a92022-06-28 14:47:50 +0100315 }
316
317 /// Retrieve the value of a given <string> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000318 pub fn getprop_str(&self, name: &CStr) -> Result<Option<&CStr>> {
319 let value = if let Some(bytes) = self.getprop(name)? {
320 Some(CStr::from_bytes_with_nul(bytes).map_err(|_| FdtError::BadValue)?)
321 } else {
322 None
323 };
324 Ok(value)
David Brazdil1baa9a92022-06-28 14:47:50 +0100325 }
326
327 /// Retrieve the value of a given property as an array of cells.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000328 pub fn getprop_cells(&self, name: &CStr) -> Result<Option<CellIterator<'a>>> {
329 if let Some(cells) = self.getprop(name)? {
330 Ok(Some(CellIterator::new(cells)))
331 } else {
332 Ok(None)
333 }
David Brazdil1baa9a92022-06-28 14:47:50 +0100334 }
335
336 /// Retrieve the value of a given <u32> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000337 pub fn getprop_u32(&self, name: &CStr) -> Result<Option<u32>> {
338 let value = if let Some(bytes) = self.getprop(name)? {
339 Some(u32::from_be_bytes(bytes.try_into().map_err(|_| FdtError::BadValue)?))
340 } else {
341 None
342 };
343 Ok(value)
David Brazdil1baa9a92022-06-28 14:47:50 +0100344 }
345
346 /// Retrieve the value of a given <u64> property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000347 pub fn getprop_u64(&self, name: &CStr) -> Result<Option<u64>> {
348 let value = if let Some(bytes) = self.getprop(name)? {
349 Some(u64::from_be_bytes(bytes.try_into().map_err(|_| FdtError::BadValue)?))
350 } else {
351 None
352 };
353 Ok(value)
David Brazdil1baa9a92022-06-28 14:47:50 +0100354 }
355
356 /// Retrieve the value of a given property.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000357 pub fn getprop(&self, name: &CStr) -> Result<Option<&'a [u8]>> {
David Brazdil1baa9a92022-06-28 14:47:50 +0100358 let mut len: i32 = 0;
359 // SAFETY - Accesses are constrained to the DT totalsize (validated by ctor) and the
360 // function respects the passed number of characters.
361 let prop = unsafe {
362 libfdt_bindgen::fdt_getprop_namelen(
363 self.fdt.as_ptr(),
364 self.offset,
365 name.as_ptr(),
366 // *_namelen functions don't include the trailing nul terminator in 'len'.
367 name.to_bytes().len().try_into().map_err(|_| FdtError::BadPath)?,
368 &mut len as *mut i32,
369 )
370 } as *const u8;
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000371
372 let Some(len) = fdt_err_or_option(len)? else {
373 return Ok(None); // Property was not found.
374 };
375 let len = usize::try_from(len).map_err(|_| FdtError::Internal)?;
376
David Brazdil1baa9a92022-06-28 14:47:50 +0100377 if prop.is_null() {
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000378 // We expected an error code in len but still received a valid value?!
379 return Err(FdtError::Internal);
David Brazdil1baa9a92022-06-28 14:47:50 +0100380 }
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000381
382 let offset =
David Brazdil1baa9a92022-06-28 14:47:50 +0100383 (prop as usize).checked_sub(self.fdt.as_ptr() as usize).ok_or(FdtError::Internal)?;
384
Pierre-Clément Tosief2030e2022-11-28 11:21:20 +0000385 Ok(Some(self.fdt.buffer.get(offset..(offset + len)).ok_or(FdtError::Internal)?))
David Brazdil1baa9a92022-06-28 14:47:50 +0100386 }
387
388 /// Get reference to the containing device tree.
389 pub fn fdt(&self) -> &Fdt {
390 self.fdt
391 }
392
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000393 fn next_compatible(self, compatible: &CStr) -> Result<Option<Self>> {
394 // SAFETY - Accesses (read-only) are constrained to the DT totalsize.
395 let ret = unsafe {
396 libfdt_bindgen::fdt_node_offset_by_compatible(
397 self.fdt.as_ptr(),
398 self.offset,
399 compatible.as_ptr(),
400 )
401 };
402
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000403 Ok(fdt_err_or_option(ret)?.map(|offset| Self { fdt: self.fdt, offset }))
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000404 }
405
David Brazdil1baa9a92022-06-28 14:47:50 +0100406 fn address_cells(&self) -> Result<AddrCells> {
407 // SAFETY - Accesses are constrained to the DT totalsize (validated by ctor).
408 unsafe { libfdt_bindgen::fdt_address_cells(self.fdt.as_ptr(), self.offset) }
409 .try_into()
410 .map_err(|_| FdtError::Internal)
411 }
412
413 fn size_cells(&self) -> Result<SizeCells> {
414 // SAFETY - Accesses are constrained to the DT totalsize (validated by ctor).
415 unsafe { libfdt_bindgen::fdt_size_cells(self.fdt.as_ptr(), self.offset) }
416 .try_into()
417 .map_err(|_| FdtError::Internal)
418 }
419}
420
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000421/// Mutable FDT node.
422pub struct FdtNodeMut<'a> {
423 fdt: &'a mut Fdt,
424 offset: c_int,
425}
426
427impl<'a> FdtNodeMut<'a> {
428 /// Append a property name-value (possibly empty) pair to the given node.
429 pub fn appendprop<T: AsRef<[u8]>>(&mut self, name: &CStr, value: &T) -> Result<()> {
430 // SAFETY - Accesses are constrained to the DT totalsize (validated by ctor).
431 let ret = unsafe {
432 libfdt_bindgen::fdt_appendprop(
433 self.fdt.as_mut_ptr(),
434 self.offset,
435 name.as_ptr(),
436 value.as_ref().as_ptr().cast::<c_void>(),
437 value.as_ref().len().try_into().map_err(|_| FdtError::BadValue)?,
438 )
439 };
440
441 fdt_err_expect_zero(ret)
442 }
443
444 /// Append a (address, size) pair property to the given node.
445 pub fn appendprop_addrrange(&mut self, name: &CStr, addr: u64, size: u64) -> Result<()> {
446 // SAFETY - Accesses are constrained to the DT totalsize (validated by ctor).
447 let ret = unsafe {
448 libfdt_bindgen::fdt_appendprop_addrrange(
449 self.fdt.as_mut_ptr(),
450 self.parent()?.offset,
451 self.offset,
452 name.as_ptr(),
453 addr,
454 size,
455 )
456 };
457
458 fdt_err_expect_zero(ret)
459 }
460
461 /// Get reference to the containing device tree.
462 pub fn fdt(&mut self) -> &mut Fdt {
463 self.fdt
464 }
465
466 /// Add a new subnode to the given node and return it as a FdtNodeMut on success.
467 pub fn add_subnode(&'a mut self, name: &CStr) -> Result<Self> {
468 // SAFETY - Accesses are constrained to the DT totalsize (validated by ctor).
469 let ret = unsafe {
470 libfdt_bindgen::fdt_add_subnode(self.fdt.as_mut_ptr(), self.offset, name.as_ptr())
471 };
472
473 Ok(Self { fdt: self.fdt, offset: fdt_err(ret)? })
474 }
475
476 fn parent(&'a self) -> Result<FdtNode<'a>> {
477 // SAFETY - Accesses (read-only) are constrained to the DT totalsize.
478 let ret = unsafe { libfdt_bindgen::fdt_parent_offset(self.fdt.as_ptr(), self.offset) };
479
480 Ok(FdtNode { fdt: &*self.fdt, offset: fdt_err(ret)? })
481 }
482}
483
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000484/// Iterator over nodes sharing a same compatible string.
485pub struct CompatibleIterator<'a> {
486 node: FdtNode<'a>,
487 compatible: &'a CStr,
488}
489
490impl<'a> CompatibleIterator<'a> {
491 fn new(fdt: &'a Fdt, compatible: &'a CStr) -> Result<Self> {
492 let node = fdt.root()?;
493 Ok(Self { node, compatible })
494 }
495}
496
497impl<'a> Iterator for CompatibleIterator<'a> {
498 type Item = FdtNode<'a>;
499
500 fn next(&mut self) -> Option<Self::Item> {
501 let next = self.node.next_compatible(self.compatible).ok()?;
502
503 if let Some(node) = next {
504 self.node = node;
505 }
506
507 next
508 }
509}
510
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000511/// Wrapper around low-level libfdt functions.
David Brazdil1baa9a92022-06-28 14:47:50 +0100512#[repr(transparent)]
513pub struct Fdt {
Pierre-Clément Tosief2030e2022-11-28 11:21:20 +0000514 buffer: [u8],
David Brazdil1baa9a92022-06-28 14:47:50 +0100515}
516
517impl Fdt {
518 /// Wraps a slice containing a Flattened Device Tree.
519 ///
520 /// Fails if the FDT does not pass validation.
521 pub fn from_slice(fdt: &[u8]) -> Result<&Self> {
522 // SAFETY - The FDT will be validated before it is returned.
523 let fdt = unsafe { Self::unchecked_from_slice(fdt) };
524 fdt.check_full()?;
525 Ok(fdt)
526 }
527
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000528 /// Wraps a mutable slice containing a Flattened Device Tree.
529 ///
530 /// Fails if the FDT does not pass validation.
531 pub fn from_mut_slice(fdt: &mut [u8]) -> Result<&mut Self> {
532 // SAFETY - The FDT will be validated before it is returned.
533 let fdt = unsafe { Self::unchecked_from_mut_slice(fdt) };
534 fdt.check_full()?;
535 Ok(fdt)
536 }
537
David Brazdil1baa9a92022-06-28 14:47:50 +0100538 /// Wraps a slice containing a Flattened Device Tree.
539 ///
540 /// # Safety
541 ///
542 /// The returned FDT might be invalid, only use on slices containing a valid DT.
543 pub unsafe fn unchecked_from_slice(fdt: &[u8]) -> &Self {
544 mem::transmute::<&[u8], &Self>(fdt)
545 }
546
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000547 /// Wraps a mutable slice containing a Flattened Device Tree.
548 ///
549 /// # Safety
550 ///
551 /// The returned FDT might be invalid, only use on slices containing a valid DT.
552 pub unsafe fn unchecked_from_mut_slice(fdt: &mut [u8]) -> &mut Self {
553 mem::transmute::<&mut [u8], &mut Self>(fdt)
554 }
555
556 /// Make the whole slice containing the DT available to libfdt.
557 pub fn unpack(&mut self) -> Result<()> {
558 // SAFETY - "Opens" the DT in-place (supported use-case) by updating its header and
559 // internal structures to make use of the whole self.fdt slice but performs no accesses
560 // outside of it and leaves the DT in a state that will be detected by other functions.
561 let ret = unsafe {
562 libfdt_bindgen::fdt_open_into(
563 self.as_ptr(),
564 self.as_mut_ptr(),
565 self.capacity().try_into().map_err(|_| FdtError::Internal)?,
566 )
567 };
568 fdt_err_expect_zero(ret)
569 }
570
571 /// Pack the DT to take a minimum amount of memory.
572 ///
573 /// Doesn't shrink the underlying memory slice.
574 pub fn pack(&mut self) -> Result<()> {
575 // SAFETY - "Closes" the DT in-place by updating its header and relocating its structs.
576 let ret = unsafe { libfdt_bindgen::fdt_pack(self.as_mut_ptr()) };
577 fdt_err_expect_zero(ret)
578 }
579
David Brazdil1baa9a92022-06-28 14:47:50 +0100580 /// Return an iterator of memory banks specified the "/memory" node.
581 ///
582 /// NOTE: This does not support individual "/memory@XXXX" banks.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000583 pub fn memory(&self) -> Result<Option<MemRegIterator>> {
David Brazdil1baa9a92022-06-28 14:47:50 +0100584 let memory = CStr::from_bytes_with_nul(b"/memory\0").unwrap();
585 let device_type = CStr::from_bytes_with_nul(b"memory\0").unwrap();
586
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000587 if let Some(node) = self.node(memory)? {
588 if node.device_type()? != Some(device_type) {
589 return Err(FdtError::BadValue);
590 }
591 let reg = node.reg()?.ok_or(FdtError::BadValue)?;
David Brazdil1baa9a92022-06-28 14:47:50 +0100592
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000593 Ok(Some(MemRegIterator::new(reg)))
594 } else {
595 Ok(None)
596 }
David Brazdil1baa9a92022-06-28 14:47:50 +0100597 }
598
599 /// Retrieve the standard /chosen node.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000600 pub fn chosen(&self) -> Result<Option<FdtNode>> {
David Brazdil1baa9a92022-06-28 14:47:50 +0100601 self.node(CStr::from_bytes_with_nul(b"/chosen\0").unwrap())
602 }
603
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000604 /// Get the root node of the tree.
605 pub fn root(&self) -> Result<FdtNode> {
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000606 self.node(CStr::from_bytes_with_nul(b"/\0").unwrap())?.ok_or(FdtError::Internal)
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000607 }
608
David Brazdil1baa9a92022-06-28 14:47:50 +0100609 /// Find a tree node by its full path.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000610 pub fn node(&self, path: &CStr) -> Result<Option<FdtNode>> {
611 Ok(self.path_offset(path)?.map(|offset| FdtNode { fdt: self, offset }))
David Brazdil1baa9a92022-06-28 14:47:50 +0100612 }
613
Pierre-Clément Tosi41c158e2022-11-21 19:16:25 +0000614 /// Iterate over nodes with a given compatible string.
615 pub fn compatible_nodes<'a>(&'a self, compatible: &'a CStr) -> Result<CompatibleIterator<'a>> {
616 CompatibleIterator::new(self, compatible)
617 }
618
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000619 /// Get the mutable root node of the tree.
620 pub fn root_mut(&mut self) -> Result<FdtNodeMut> {
621 self.node_mut(CStr::from_bytes_with_nul(b"/\0").unwrap())?.ok_or(FdtError::Internal)
622 }
623
624 /// Find a mutable tree node by its full path.
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000625 pub fn node_mut(&mut self, path: &CStr) -> Result<Option<FdtNodeMut>> {
626 Ok(self.path_offset(path)?.map(|offset| FdtNodeMut { fdt: self, offset }))
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000627 }
628
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000629 fn path_offset(&self, path: &CStr) -> Result<Option<c_int>> {
David Brazdil1baa9a92022-06-28 14:47:50 +0100630 let len = path.to_bytes().len().try_into().map_err(|_| FdtError::BadPath)?;
631 // SAFETY - Accesses are constrained to the DT totalsize (validated by ctor) and the
632 // function respects the passed number of characters.
633 let ret = unsafe {
634 // *_namelen functions don't include the trailing nul terminator in 'len'.
635 libfdt_bindgen::fdt_path_offset_namelen(self.as_ptr(), path.as_ptr(), len)
636 };
637
Pierre-Clément Tosib244d932022-11-24 16:45:53 +0000638 fdt_err_or_option(ret)
David Brazdil1baa9a92022-06-28 14:47:50 +0100639 }
640
641 fn check_full(&self) -> Result<()> {
Pierre-Clément Tosief2030e2022-11-28 11:21:20 +0000642 let len = self.buffer.len();
David Brazdil1baa9a92022-06-28 14:47:50 +0100643 // SAFETY - Only performs read accesses within the limits of the slice. If successful, this
644 // call guarantees to other unsafe calls that the header contains a valid totalsize (w.r.t.
645 // 'len' i.e. the self.fdt slice) that those C functions can use to perform bounds
646 // checking. The library doesn't maintain an internal state (such as pointers) between
647 // calls as it expects the client code to keep track of the objects (DT, nodes, ...).
648 let ret = unsafe { libfdt_bindgen::fdt_check_full(self.as_ptr(), len) };
649 fdt_err_expect_zero(ret)
650 }
651
652 fn as_ptr(&self) -> *const c_void {
653 self as *const _ as *const c_void
654 }
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000655
656 fn as_mut_ptr(&mut self) -> *mut c_void {
657 self as *mut _ as *mut c_void
658 }
659
660 fn capacity(&self) -> usize {
Pierre-Clément Tosief2030e2022-11-28 11:21:20 +0000661 self.buffer.len()
Pierre-Clément Tosi1b0d8902022-11-21 18:16:59 +0000662 }
David Brazdil1baa9a92022-06-28 14:47:50 +0100663}