// Copyright 2022, The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //! Wrapper around libfdt library. Provides parsing/generating functionality //! to a bare-metal environment. #![no_std] mod iterators; mod libfdt; mod result; mod safe_types; pub use iterators::{ AddressRange, CellIterator, CompatibleIterator, DescendantsIterator, MemRegIterator, PropertyIterator, RangesIterator, Reg, RegIterator, SubnodeIterator, }; pub use result::{FdtError, Result}; pub use safe_types::{FdtHeader, NodeOffset, Phandle, PropOffset, StringOffset}; use core::ffi::{c_void, CStr}; use core::ops::Range; use cstr::cstr; use libfdt::get_slice_at_ptr; use zerocopy::AsBytes as _; use crate::libfdt::{Libfdt, LibfdtMut}; /// Value of a #address-cells property. #[derive(Copy, Clone, Debug, Eq, PartialEq)] enum AddrCells { Single = 1, Double = 2, Triple = 3, } impl TryFrom for AddrCells { type Error = FdtError; fn try_from(value: usize) -> Result { match value { x if x == Self::Single as _ => Ok(Self::Single), x if x == Self::Double as _ => Ok(Self::Double), x if x == Self::Triple as _ => Ok(Self::Triple), _ => Err(FdtError::BadNCells), } } } /// Value of a #size-cells property. #[derive(Copy, Clone, Debug, Eq, PartialEq)] enum SizeCells { None = 0, Single = 1, Double = 2, } impl TryFrom for SizeCells { type Error = FdtError; fn try_from(value: usize) -> Result { match value { x if x == Self::None as _ => Ok(Self::None), x if x == Self::Single as _ => Ok(Self::Single), x if x == Self::Double as _ => Ok(Self::Double), _ => Err(FdtError::BadNCells), } } } /// DT property wrapper to abstract endianess changes #[repr(transparent)] #[derive(Debug)] struct FdtPropertyStruct(libfdt_bindgen::fdt_property); impl AsRef for libfdt_bindgen::fdt_property { fn as_ref(&self) -> &FdtPropertyStruct { let ptr = self as *const _ as *const _; // SAFETY: Types have the same layout (transparent) so the valid reference remains valid. unsafe { &*ptr } } } impl FdtPropertyStruct { fn from_offset(fdt: &Fdt, offset: PropOffset) -> Result<&Self> { Ok(fdt.get_property_by_offset(offset)?.as_ref()) } fn name_offset(&self) -> StringOffset { StringOffset(u32::from_be(self.0.nameoff).try_into().unwrap()) } fn data_len(&self) -> usize { u32::from_be(self.0.len).try_into().unwrap() } fn data_ptr(&self) -> *const c_void { self.0.data.as_ptr().cast() } } /// DT property. #[derive(Clone, Copy, Debug)] pub struct FdtProperty<'a> { fdt: &'a Fdt, offset: PropOffset, property: &'a FdtPropertyStruct, } impl<'a> FdtProperty<'a> { fn new(fdt: &'a Fdt, offset: PropOffset) -> Result { let property = FdtPropertyStruct::from_offset(fdt, offset)?; Ok(Self { fdt, offset, property }) } /// Returns the property name pub fn name(&self) -> Result<&'a CStr> { self.fdt.string(self.property.name_offset()) } /// Returns the property value pub fn value(&self) -> Result<&'a [u8]> { self.fdt.get_from_ptr(self.property.data_ptr(), self.property.data_len()) } fn next_property(&self) -> Result> { if let Some(offset) = self.fdt.next_property_offset(self.offset)? { Ok(Some(Self::new(self.fdt, offset)?)) } else { Ok(None) } } } /// DT node. #[derive(Clone, Copy, Debug)] pub struct FdtNode<'a> { fdt: &'a Fdt, offset: NodeOffset, } impl<'a> FdtNode<'a> { /// Returns parent node. pub fn parent(&self) -> Result { let offset = self.fdt.parent_offset(self.offset)?; Ok(Self { fdt: self.fdt, offset }) } /// Returns supernode with depth. Note that root is at depth 0. pub fn supernode_at_depth(&self, depth: usize) -> Result { let offset = self.fdt.supernode_atdepth_offset(self.offset, depth)?; Ok(Self { fdt: self.fdt, offset }) } /// Returns the standard (deprecated) device_type property. pub fn device_type(&self) -> Result> { self.getprop_str(cstr!("device_type")) } /// Returns the standard reg property. pub fn reg(&self) -> Result>> { if let Some(cells) = self.getprop_cells(cstr!("reg"))? { let parent = self.parent()?; let addr_cells = parent.address_cells()?; let size_cells = parent.size_cells()?; Ok(Some(RegIterator::new(cells, addr_cells, size_cells))) } else { Ok(None) } } /// Returns the standard ranges property. pub fn ranges(&self) -> Result>> { if let Some(cells) = self.getprop_cells(cstr!("ranges"))? { let parent = self.parent()?; let addr_cells = self.address_cells()?; let parent_addr_cells = parent.address_cells()?; let size_cells = self.size_cells()?; Ok(Some(RangesIterator::::new( cells, addr_cells, parent_addr_cells, size_cells, ))) } else { Ok(None) } } /// Returns the node name. pub fn name(&self) -> Result<&'a CStr> { let name = self.fdt.get_name(self.offset)?; CStr::from_bytes_with_nul(name).map_err(|_| FdtError::Internal) } /// Returns the value of a given property. pub fn getprop_str(&self, name: &CStr) -> Result> { if let Some(bytes) = self.getprop(name)? { Ok(Some(CStr::from_bytes_with_nul(bytes).map_err(|_| FdtError::BadValue)?)) } else { Ok(None) } } /// Returns the value of a given property as an array of cells. pub fn getprop_cells(&self, name: &CStr) -> Result>> { if let Some(cells) = self.getprop(name)? { Ok(Some(CellIterator::new(cells))) } else { Ok(None) } } /// Returns the value of a given property. pub fn getprop_u32(&self, name: &CStr) -> Result> { if let Some(bytes) = self.getprop(name)? { Ok(Some(u32::from_be_bytes(bytes.try_into().map_err(|_| FdtError::BadValue)?))) } else { Ok(None) } } /// Returns the value of a given property. pub fn getprop_u64(&self, name: &CStr) -> Result> { if let Some(bytes) = self.getprop(name)? { Ok(Some(u64::from_be_bytes(bytes.try_into().map_err(|_| FdtError::BadValue)?))) } else { Ok(None) } } /// Returns the value of a given property. pub fn getprop(&self, name: &CStr) -> Result> { self.fdt.getprop_namelen(self.offset, name.to_bytes()) } /// Returns reference to the containing device tree. pub fn fdt(&self) -> &Fdt { self.fdt } /// Returns the compatible node of the given name that is next after this node. pub fn next_compatible(self, compatible: &CStr) -> Result> { let offset = self.fdt.node_offset_by_compatible(self.offset, compatible)?; Ok(offset.map(|offset| Self { fdt: self.fdt, offset })) } /// Returns the first range of `reg` in this node. pub fn first_reg(&self) -> Result> { self.reg()?.ok_or(FdtError::NotFound)?.next().ok_or(FdtError::NotFound) } fn address_cells(&self) -> Result { self.fdt.address_cells(self.offset)?.try_into() } fn size_cells(&self) -> Result { self.fdt.size_cells(self.offset)?.try_into() } /// Returns an iterator of subnodes pub fn subnodes(&self) -> Result> { SubnodeIterator::new(self) } fn first_subnode(&self) -> Result> { let offset = self.fdt.first_subnode(self.offset)?; Ok(offset.map(|offset| Self { fdt: self.fdt, offset })) } fn next_subnode(&self) -> Result> { let offset = self.fdt.next_subnode(self.offset)?; Ok(offset.map(|offset| Self { fdt: self.fdt, offset })) } /// Returns an iterator of descendants pub fn descendants(&self) -> DescendantsIterator<'a> { DescendantsIterator::new(self) } fn next_node(&self, depth: usize) -> Result> { if let Some((offset, depth)) = self.fdt.next_node(self.offset, depth)? { Ok(Some((Self { fdt: self.fdt, offset }, depth))) } else { Ok(None) } } /// Returns an iterator of properties pub fn properties(&'a self) -> Result> { PropertyIterator::new(self) } fn first_property(&self) -> Result>> { if let Some(offset) = self.fdt.first_property_offset(self.offset)? { Ok(Some(FdtProperty::new(self.fdt, offset)?)) } else { Ok(None) } } /// Returns the phandle pub fn get_phandle(&self) -> Result> { // This rewrites the fdt_get_phandle() because it doesn't return error code. if let Some(prop) = self.getprop_u32(cstr!("phandle"))? { Ok(Some(prop.try_into()?)) } else if let Some(prop) = self.getprop_u32(cstr!("linux,phandle"))? { Ok(Some(prop.try_into()?)) } else { Ok(None) } } /// Returns the subnode of the given name. The name doesn't need to be nul-terminated. pub fn subnode(&self, name: &CStr) -> Result> { let name = name.to_bytes(); let offset = self.fdt.subnode_offset_namelen(self.offset, name)?; Ok(offset.map(|offset| Self { fdt: self.fdt, offset })) } /// Returns the subnode of the given name bytes pub fn subnode_with_name_bytes(&self, name: &[u8]) -> Result> { let offset = self.fdt.subnode_offset_namelen(self.offset, name)?; Ok(offset.map(|offset| Self { fdt: self.fdt, offset })) } } impl<'a> PartialEq for FdtNode<'a> { fn eq(&self, other: &Self) -> bool { self.fdt.as_ptr() == other.fdt.as_ptr() && self.offset == other.offset } } /// Mutable FDT node. #[derive(Debug)] pub struct FdtNodeMut<'a> { fdt: &'a mut Fdt, offset: NodeOffset, } impl<'a> FdtNodeMut<'a> { /// Appends a property name-value (possibly empty) pair to the given node. pub fn appendprop>(&mut self, name: &CStr, value: &T) -> Result<()> { self.fdt.appendprop(self.offset, name, value.as_ref()) } /// Appends a (address, size) pair property to the given node. pub fn appendprop_addrrange(&mut self, name: &CStr, addr: u64, size: u64) -> Result<()> { let parent = self.parent()?.offset; self.fdt.appendprop_addrrange(parent, self.offset, name, addr, size) } /// Sets a property name-value pair to the given node. /// /// This may create a new prop or replace existing value. pub fn setprop(&mut self, name: &CStr, value: &[u8]) -> Result<()> { self.fdt.setprop(self.offset, name, value) } /// Sets the value of the given property with the given value, and ensure that the given /// value has the same length as the current value length. /// /// This can only be used to replace existing value. pub fn setprop_inplace(&mut self, name: &CStr, value: &[u8]) -> Result<()> { self.fdt.setprop_inplace(self.offset, name, value) } /// Sets the value of the given (address, size) pair property with the given value, and /// ensure that the given value has the same length as the current value length. /// /// This can only be used to replace existing value. pub fn setprop_addrrange_inplace(&mut self, name: &CStr, addr: u64, size: u64) -> Result<()> { let pair = [addr.to_be(), size.to_be()]; self.fdt.setprop_inplace(self.offset, name, pair.as_bytes()) } /// Sets a flag-like empty property. /// /// This may create a new prop or replace existing value. pub fn setprop_empty(&mut self, name: &CStr) -> Result<()> { self.fdt.setprop(self.offset, name, &[]) } /// Deletes the given property. pub fn delprop(&mut self, name: &CStr) -> Result<()> { self.fdt.delprop(self.offset, name) } /// Deletes the given property effectively from DT, by setting it with FDT_NOP. pub fn nop_property(&mut self, name: &CStr) -> Result<()> { self.fdt.nop_property(self.offset, name) } /// Trims the size of the given property to new_size. pub fn trimprop(&mut self, name: &CStr, new_size: usize) -> Result<()> { let prop = self.as_node().getprop(name)?.ok_or(FdtError::NotFound)?; match prop.len() { x if x == new_size => Ok(()), x if x < new_size => Err(FdtError::NoSpace), _ => self.fdt.setprop_placeholder(self.offset, name, new_size).map(|_| ()), } } /// Returns reference to the containing device tree. pub fn fdt(&mut self) -> &mut Fdt { self.fdt } /// Returns immutable FdtNode of this node. pub fn as_node(&self) -> FdtNode { FdtNode { fdt: self.fdt, offset: self.offset } } /// Adds new subnodes to the given node. pub fn add_subnodes(self, names: &[&CStr]) -> Result<()> { for name in names { self.fdt.add_subnode_namelen(self.offset, name.to_bytes())?; } Ok(()) } /// Adds a new subnode to the given node and return it as a FdtNodeMut on success. pub fn add_subnode(self, name: &CStr) -> Result { let name = name.to_bytes(); let offset = self.fdt.add_subnode_namelen(self.offset, name)?; Ok(Self { fdt: self.fdt, offset }) } /// Adds a new subnode to the given node with name and namelen, and returns it as a FdtNodeMut /// on success. pub fn add_subnode_with_namelen(self, name: &CStr, namelen: usize) -> Result { let name = &name.to_bytes()[..namelen]; let offset = self.fdt.add_subnode_namelen(self.offset, name)?; Ok(Self { fdt: self.fdt, offset }) } /// Returns the first subnode of this pub fn first_subnode(self) -> Result> { let offset = self.fdt.first_subnode(self.offset)?; Ok(offset.map(|offset| Self { fdt: self.fdt, offset })) } /// Returns the next subnode that shares the same parent with this pub fn next_subnode(self) -> Result> { let offset = self.fdt.next_subnode(self.offset)?; Ok(offset.map(|offset| Self { fdt: self.fdt, offset })) } /// Deletes the current node and returns the next subnode pub fn delete_and_next_subnode(self) -> Result> { let next_offset = self.fdt.next_subnode(self.offset)?; self.delete_and_next(next_offset) } /// Returns the next node. Use this API to travel descendant of a node. /// /// Returned depth is relative to the initial node that had called with any of next node APIs. /// Returns None if end of FDT reached or depth becomes negative. /// /// See also: [`next_node_skip_subnodes`], and [`delete_and_next_node`] pub fn next_node(self, depth: usize) -> Result> { let next = self.fdt.next_node(self.offset, depth)?; Ok(next.map(|(offset, depth)| (Self { fdt: self.fdt, offset }, depth))) } /// Returns the next node skipping subnodes. Use this API to travel descendants of a node while /// ignoring certain node. /// /// Returned depth is relative to the initial node that had called with any of next node APIs. /// Returns None if end of FDT reached or depth becomes negative. /// /// See also: [`next_node`], and [`delete_and_next_node`] pub fn next_node_skip_subnodes(self, depth: usize) -> Result> { let next = self.fdt.next_node_skip_subnodes(self.offset, depth)?; Ok(next.map(|(offset, depth)| (Self { fdt: self.fdt, offset }, depth))) } /// Deletes this and returns the next node. Use this API to travel descendants of a node while /// removing certain node. /// /// Returned depth is relative to the initial node that had called with any of next node APIs. /// Returns None if end of FDT reached or depth becomes negative. /// /// See also: [`next_node`], and [`next_node_skip_subnodes`] pub fn delete_and_next_node(self, depth: usize) -> Result> { let next_node = self.fdt.next_node_skip_subnodes(self.offset, depth)?; if let Some((offset, depth)) = next_node { let next_node = self.delete_and_next(Some(offset))?.unwrap(); Ok(Some((next_node, depth))) } else { self.delete_and_next(None)?; Ok(None) } } fn parent(&'a self) -> Result> { self.as_node().parent() } /// Returns the compatible node of the given name that is next after this node. pub fn next_compatible(self, compatible: &CStr) -> Result> { let offset = self.fdt.node_offset_by_compatible(self.offset, compatible)?; Ok(offset.map(|offset| Self { fdt: self.fdt, offset })) } /// Deletes the node effectively by overwriting this node and its subtree with nop tags. /// Returns the next compatible node of the given name. // Side note: without this, filterint out excessive compatible nodes from the DT is impossible. // The reason is that libfdt ensures that the node from where the search for the next // compatible node is started is always a valid one -- except for the special case of offset = // -1 which is to find the first compatible node. So, we can't delete a node and then find the // next compatible node from it. // // We can't do in the opposite direction either. If we call next_compatible to find the next // node, and delete the current node, the Rust borrow checker kicks in. The next node has a // mutable reference to DT, so we can't use current node (which also has a mutable reference to // DT). pub fn delete_and_next_compatible(self, compatible: &CStr) -> Result> { let next_offset = self.fdt.node_offset_by_compatible(self.offset, compatible)?; self.delete_and_next(next_offset) } fn delete_and_next(self, next_offset: Option) -> Result> { if Some(self.offset) == next_offset { return Err(FdtError::Internal); } self.fdt.nop_node(self.offset)?; Ok(next_offset.map(|offset| Self { fdt: self.fdt, offset })) } /// Deletes this node effectively from DT, by setting it with FDT_NOP pub fn nop(self) -> Result<()> { self.fdt.nop_node(self.offset) } } /// Wrapper around low-level libfdt functions. #[derive(Debug)] #[repr(transparent)] pub struct Fdt { buffer: [u8], } // SAFETY: Fdt calls check_full() before safely returning a &Self, making it impossible for trait // methods to be called on invalid device trees. unsafe impl Libfdt for Fdt { fn as_fdt_slice(&self) -> &[u8] { &self.buffer[..self.totalsize()] } } // SAFETY: Fdt calls check_full() before safely returning a &Self, making it impossible for trait // methods to be called on invalid device trees. unsafe impl LibfdtMut for Fdt { fn as_fdt_slice_mut(&mut self) -> &mut [u8] { &mut self.buffer } } impl Fdt { /// Wraps a slice containing a Flattened Device Tree. /// /// Fails if the FDT does not pass validation. pub fn from_slice(fdt: &[u8]) -> Result<&Self> { libfdt::check_full(fdt)?; // SAFETY: The FDT was validated. let fdt = unsafe { Self::unchecked_from_slice(fdt) }; Ok(fdt) } /// Wraps a mutable slice containing a Flattened Device Tree. /// /// Fails if the FDT does not pass validation. pub fn from_mut_slice(fdt: &mut [u8]) -> Result<&mut Self> { libfdt::check_full(fdt)?; // SAFETY: The FDT was validated. let fdt = unsafe { Self::unchecked_from_mut_slice(fdt) }; Ok(fdt) } /// Creates an empty Flattened Device Tree with a mutable slice. pub fn create_empty_tree(fdt: &mut [u8]) -> Result<&mut Self> { libfdt::create_empty_tree(fdt)?; Self::from_mut_slice(fdt) } /// Wraps a slice containing a Flattened Device Tree. /// /// # Safety /// /// It is undefined to call this function on a slice that does not contain a valid device tree. pub const unsafe fn unchecked_from_slice(fdt: &[u8]) -> &Self { let self_ptr = fdt as *const _ as *const _; // SAFETY: The pointer is non-null, dereferenceable, and points to allocated memory. unsafe { &*self_ptr } } /// Wraps a mutable slice containing a Flattened Device Tree. /// /// # Safety /// /// It is undefined to call this function on a slice that does not contain a valid device tree. pub unsafe fn unchecked_from_mut_slice(fdt: &mut [u8]) -> &mut Self { let self_mut_ptr = fdt as *mut _ as *mut _; // SAFETY: The pointer is non-null, dereferenceable, and points to allocated memory. unsafe { &mut *self_mut_ptr } } /// Updates this FDT from another FDT. pub fn clone_from(&mut self, other: &Self) -> Result<()> { let new_len = other.buffer.len(); if self.buffer.len() < new_len { return Err(FdtError::NoSpace); } let zeroed_len = self.totalsize().checked_sub(new_len); let (cloned, zeroed) = self.buffer.split_at_mut(new_len); cloned.clone_from_slice(&other.buffer); if let Some(len) = zeroed_len { zeroed[..len].fill(0); } Ok(()) } /// Unpacks the DT to cover the whole slice it is contained in. pub fn unpack(&mut self) -> Result<()> { self.open_into_self() } /// Packs the DT to take a minimum amount of memory. /// /// Doesn't shrink the underlying memory slice. pub fn pack(&mut self) -> Result<()> { LibfdtMut::pack(self) } /// Applies a DT overlay on the base DT. /// /// # Safety /// /// As libfdt corrupts the input DT on failure, `self` should be discarded on error: /// /// let fdt = fdt.apply_overlay(overlay)?; /// /// Furthermore, `overlay` is _always_ corrupted by libfdt and will never refer to a valid /// `Fdt` after this function returns and must therefore be discarded by the caller. pub unsafe fn apply_overlay<'a>(&'a mut self, overlay: &mut Fdt) -> Result<&'a mut Self> { // SAFETY: Our caller will properly discard overlay and/or self as needed. unsafe { self.overlay_apply(overlay) }?; Ok(self) } /// Returns an iterator of memory banks specified the "/memory" node. /// Throws an error when the "/memory" is not found in the device tree. /// /// NOTE: This does not support individual "/memory@XXXX" banks. pub fn memory(&self) -> Result { let node = self.root().subnode(cstr!("memory"))?.ok_or(FdtError::NotFound)?; if node.device_type()? != Some(cstr!("memory")) { return Err(FdtError::BadValue); } node.reg()?.ok_or(FdtError::BadValue).map(MemRegIterator::new) } /// Returns the first memory range in the `/memory` node. pub fn first_memory_range(&self) -> Result> { self.memory()?.next().ok_or(FdtError::NotFound) } /// Returns the standard /chosen node. pub fn chosen(&self) -> Result> { self.root().subnode(cstr!("chosen")) } /// Returns the standard /chosen node as mutable. pub fn chosen_mut(&mut self) -> Result> { self.node_mut(cstr!("/chosen")) } /// Returns the root node of the tree. pub fn root(&self) -> FdtNode { FdtNode { fdt: self, offset: NodeOffset::ROOT } } /// Returns the standard /__symbols__ node. pub fn symbols(&self) -> Result> { self.root().subnode(cstr!("__symbols__")) } /// Returns the standard /__symbols__ node as mutable pub fn symbols_mut(&mut self) -> Result> { self.node_mut(cstr!("/__symbols__")) } /// Returns a tree node by its full path. pub fn node(&self, path: &CStr) -> Result> { let offset = self.path_offset_namelen(path.to_bytes())?; Ok(offset.map(|offset| FdtNode { fdt: self, offset })) } /// Iterate over nodes with a given compatible string. pub fn compatible_nodes<'a>(&'a self, compatible: &'a CStr) -> Result> { CompatibleIterator::new(self, compatible) } /// Returns max phandle in the tree. pub fn max_phandle(&self) -> Result { self.find_max_phandle() } /// Returns a node with the phandle pub fn node_with_phandle(&self, phandle: Phandle) -> Result> { let offset = self.node_offset_by_phandle(phandle)?; Ok(offset.map(|offset| FdtNode { fdt: self, offset })) } /// Returns a mutable node with the phandle pub fn node_mut_with_phandle(&mut self, phandle: Phandle) -> Result> { let offset = self.node_offset_by_phandle(phandle)?; Ok(offset.map(|offset| FdtNodeMut { fdt: self, offset })) } /// Returns the mutable root node of the tree. pub fn root_mut(&mut self) -> FdtNodeMut { FdtNodeMut { fdt: self, offset: NodeOffset::ROOT } } /// Returns a mutable tree node by its full path. pub fn node_mut(&mut self, path: &CStr) -> Result> { let offset = self.path_offset_namelen(path.to_bytes())?; Ok(offset.map(|offset| FdtNodeMut { fdt: self, offset })) } fn next_node_skip_subnodes( &self, node: NodeOffset, depth: usize, ) -> Result> { let mut iter = self.next_node(node, depth)?; while let Some((offset, next_depth)) = iter { if next_depth <= depth { return Ok(Some((offset, next_depth))); } iter = self.next_node(offset, next_depth)?; } Ok(None) } /// Returns the device tree as a slice (may be smaller than the containing buffer). pub fn as_slice(&self) -> &[u8] { self.as_fdt_slice() } fn get_from_ptr(&self, ptr: *const c_void, len: usize) -> Result<&[u8]> { get_slice_at_ptr(self.as_fdt_slice(), ptr.cast(), len).ok_or(FdtError::Internal) } /// Returns a shared pointer to the device tree. pub fn as_ptr(&self) -> *const c_void { self.buffer.as_ptr().cast() } fn header(&self) -> &FdtHeader { let p = self.as_ptr().cast::(); // SAFETY: A valid FDT (verified by constructor) must contain a valid fdt_header. let header = unsafe { &*p }; header.as_ref() } fn totalsize(&self) -> usize { self.header().totalsize.get().try_into().unwrap() } }