fdt_rs/base/
tree.rs

1#[cfg(doc)]
2use crate::base::parse::ParsedTok;
3#[cfg(doc)]
4use crate::base::*;
5
6use core::mem::size_of;
7use core::ptr;
8use core::slice;
9
10use crate::error::{DevTreeError, Result};
11
12use crate::priv_util::SliceRead;
13use crate::spec::{fdt_header, FDT_MAGIC};
14
15use fallible_iterator::FallibleIterator;
16
17use super::iters::{
18    DevTreeCompatibleNodeIter, DevTreeIter, DevTreeNodeIter, DevTreeParseIter, DevTreePropIter,
19    DevTreeReserveEntryIter,
20};
21use super::DevTreeNode;
22
23const fn is_aligned<T>(offset: usize) -> bool {
24    offset % size_of::<T>() == 0
25}
26
27const fn verify_offset_aligned<T>(offset: usize) -> Result<usize> {
28    let i: [Result<usize>; 2] = [Err(DevTreeError::ParseError), Ok(offset)];
29    i[is_aligned::<T>(offset) as usize]
30}
31
32macro_rules! get_be32_field {
33    ( $f:ident, $s:ident , $buf:expr ) => {
34        $buf.read_be_u32(offset_of!($s, $f))
35    };
36}
37
38/// A parseable Flattened Device Tree.
39///
40/// This parser was written according to the v0.3 specification provided at
41/// https://www.devicetree.org/
42#[derive(Copy, Clone, Debug)]
43pub struct DevTree<'dt> {
44    buf: &'dt [u8],
45}
46
47impl<'dt> PartialEq for DevTree<'dt> {
48    fn eq(&self, other: &Self) -> bool {
49        ptr::eq(self.buf, other.buf)
50    }
51}
52
53impl<'dt> DevTree<'dt> {
54    pub const MIN_HEADER_SIZE: usize = size_of::<fdt_header>();
55    /// Verify the magic header of a Device Tree buffer
56    ///
57    /// # Safety
58    ///
59    /// Callers of this method the must guarantee the following:
60    /// - The passed buffer is 32-bit aligned.
61    ///
62    /// The passed byte buffer will be interpreted as a Flattened Device Tree. For this reason this API
63    /// is marked unsafe.
64    #[inline]
65    pub unsafe fn verify_magic(buf: &[u8]) -> Result<()> {
66        if get_be32_field!(magic, fdt_header, buf)? != FDT_MAGIC {
67            Err(DevTreeError::InvalidMagicNumber)
68        } else {
69            Ok(())
70        }
71    }
72
73    /// Using the provided byte slice this method will:
74    ///
75    /// 1. Verify that the slice begins with the magic Device Tree header
76    /// 2. Return the reported `totalsize` field of the Device Tree header
77    ///
78    /// When parsing a FDT, it's possible that the actual size of the device tree may be unknown.
79    /// For that reason, this method can be called before constructing the [`DevTree`]. For this
80    /// read to take place, the provided buffer must be at least [`Self::MIN_HEADER_SIZE`] long.
81    ///
82    /// Once known, the user should resize the raw byte slice to this function's return value and
83    /// pass that slice to [`DevTree::new()`].
84    ///
85    /// # Example
86    ///
87    /// TODO
88    ///
89    /// # Safety
90    ///
91    /// Callers of this method the must guarantee the following:
92    /// - The passed buffer is 32-bit aligned.
93    /// - The passed buffer is of at least [`DevTree::MIN_HEADER_SIZE`] bytes in length
94    ///
95    /// The passed byte buffer will be interpreted as a Flattened Device Tree. For this reason this API
96    /// is marked unsafe.
97    #[inline]
98    pub unsafe fn read_totalsize(buf: &[u8]) -> Result<usize> {
99        // Verify provided buffer alignment
100        verify_offset_aligned::<u32>(buf.as_ptr() as usize)
101            .map_err(|_| DevTreeError::InvalidParameter("Unaligned buffer provided"))?;
102
103        // Verify provided buffer magic
104        Self::verify_magic(buf)?;
105        Ok(get_be32_field!(totalsize, fdt_header, buf)? as usize)
106    }
107
108    /// Construct the parseable DevTree object from the provided byte slice without any check. This
109    /// is for iternal use only
110    ///
111    /// # Safety
112    ///
113    /// Callers of this method the must guarantee the following:
114    ///
115    /// - The passed buffer is 32-bit aligned.
116    /// - The passed buffer is exactly the length returned by [`Self::read_totalsize()`]
117    #[inline]
118    unsafe fn from_safe_slice(buf: &'dt [u8]) -> Result<Self> {
119        let ret = Self { buf };
120        // Verify required alignment before returning.
121        verify_offset_aligned::<u32>(ret.off_mem_rsvmap())?;
122        verify_offset_aligned::<u32>(ret.off_dt_struct())?;
123        Ok(ret)
124    }
125
126    /// Construct the parseable DevTree object from the provided byte slice.
127    ///
128    /// # Safety
129    ///
130    /// Callers of this method the must guarantee the following:
131    ///
132    /// - The passed buffer is 32-bit aligned.
133    /// - The passed buffer is exactly the length returned by [`Self::read_totalsize()`]
134    #[inline]
135    pub unsafe fn new(buf: &'dt [u8]) -> Result<Self> {
136        if Self::read_totalsize(buf)? < buf.len() {
137            Err(DevTreeError::ParseError)
138        } else {
139            Self::from_safe_slice(buf)
140        }
141    }
142
143    /// Construct the parseable DevTree object from a raw byte pointer
144    ///
145    /// # Safety
146    ///
147    /// Callers of this method the must guarantee the following:
148    ///
149    /// - The passed address is 32-bit aligned.
150    #[inline]
151    pub unsafe fn from_raw_pointer(addr: *const u8) -> Result<Self> {
152        let buf: &[u8] = slice::from_raw_parts(addr, Self::MIN_HEADER_SIZE);
153        let buf_size = Self::read_totalsize(buf)?;
154        let buf: &[u8] = slice::from_raw_parts(addr, buf_size);
155
156        Self::from_safe_slice(buf)
157    }
158
159    /// Returns the totalsize field of the Device Tree. This is the number of bytes of the device
160    /// tree structure.
161    #[inline]
162    #[must_use]
163    pub fn totalsize(&self) -> usize {
164        unsafe { get_be32_field!(totalsize, fdt_header, self.buf).unwrap() as usize }
165    }
166
167    /// Returns the rsvmap offset field of the Device Tree
168    #[inline]
169    #[must_use]
170    pub fn off_mem_rsvmap(&self) -> usize {
171        unsafe { get_be32_field!(off_mem_rsvmap, fdt_header, self.buf).unwrap() as usize }
172    }
173
174    /// Returns the dt_struct offset field of the Device Tree
175    #[inline]
176    #[must_use]
177    pub fn off_dt_struct(&self) -> usize {
178        unsafe { get_be32_field!(off_dt_struct, fdt_header, self.buf).unwrap() as usize }
179    }
180
181    /// Returns the dt_strings offset field of the Device Tree
182    #[inline]
183    #[must_use]
184    pub fn off_dt_strings(&self) -> usize {
185        unsafe { get_be32_field!(off_dt_strings, fdt_header, self.buf).unwrap() as usize }
186    }
187
188    /// Returns the magic field of the Device Tree
189    #[inline]
190    #[must_use]
191    pub fn magic(&self) -> u32 {
192        unsafe { get_be32_field!(magic, fdt_header, self.buf).unwrap() }
193    }
194
195    /// Returns the version field of the Device Tree
196    #[inline]
197    #[must_use]
198    pub fn version(&self) -> u32 {
199        unsafe { get_be32_field!(version, fdt_header, self.buf).unwrap() }
200    }
201
202    /// Returns the boot_cpuid_phys field of the Device Tree
203    #[inline]
204    #[must_use]
205    pub fn boot_cpuid_phys(&self) -> u32 {
206        unsafe { get_be32_field!(boot_cpuid_phys, fdt_header, self.buf).unwrap() }
207    }
208
209    /// Returns the last_comp_version field of the Device Tree
210    #[inline]
211    #[must_use]
212    pub fn last_comp_version(&self) -> u32 {
213        unsafe { get_be32_field!(last_comp_version, fdt_header, self.buf).unwrap() }
214    }
215
216    /// Returns the size_dt_strings field of the Device Tree
217    #[inline]
218    #[must_use]
219    pub fn size_dt_strings(&self) -> u32 {
220        unsafe { get_be32_field!(size_dt_strings, fdt_header, self.buf).unwrap() }
221    }
222
223    /// Returns the size_dt_struct field of the Device Tree
224    #[inline]
225    #[must_use]
226    pub fn size_dt_struct(&self) -> u32 {
227        unsafe { get_be32_field!(size_dt_struct, fdt_header, self.buf).unwrap() }
228    }
229
230    /// Returns a typed `*const T` to the given offset in the Device Tree buffer.
231    ///
232    /// # Safety
233    ///
234    /// Due to the unsafe nature of re-interpretation casts this method is unsafe.  This method
235    /// will verify that enough space to fit type T remains within the buffer.
236    ///
237    /// The caller must verify that the pointer is not misaligned before it is dereferenced.
238    pub(crate) unsafe fn ptr_at<T>(&self, offset: usize) -> Result<*const T> {
239        if offset + size_of::<T>() > self.buf.len() {
240            Err(DevTreeError::InvalidOffset)
241        } else {
242            Ok(self.buf.as_ptr().add(offset) as *const T)
243        }
244    }
245
246    /// Returns an iterator over the Dev Tree "5.3 Memory Reservation Blocks"
247    #[must_use]
248    pub fn reserved_entries(&self) -> DevTreeReserveEntryIter {
249        DevTreeReserveEntryIter::new(self)
250    }
251
252    /// Returns an iterator over [`DevTreeNode`] objects
253    pub fn nodes(&self) -> DevTreeNodeIter<'_, 'dt> {
254        DevTreeNodeIter(DevTreeIter::new(self))
255    }
256
257    #[must_use]
258    pub fn props(&self) -> DevTreePropIter<'_, 'dt> {
259        DevTreePropIter(DevTreeIter::new(self))
260    }
261
262    /// Returns an iterator over objects within the [`DevTreeItem`] enum
263    pub fn items(&self) -> DevTreeIter<'_, 'dt> {
264        DevTreeIter::new(self)
265    }
266
267    /// Returns an iterator over low level parsing tokens, [`ParsedTok`].
268    #[must_use]
269    pub fn parse_iter(&self) -> DevTreeParseIter<'_, 'dt> {
270        DevTreeParseIter::new(self)
271    }
272
273    /// Returns the first [`DevTreeNode`] object with the provided compatible device tree property
274    /// or `None` if none exists.
275    pub fn compatible_nodes<'s, 'a: 's>(
276        &'a self,
277        string: &'s str,
278    ) -> DevTreeCompatibleNodeIter<'s, 'a, 'dt> {
279        DevTreeCompatibleNodeIter {
280            iter: self.items(),
281            string,
282        }
283    }
284
285    pub fn buf(&self) -> &'dt [u8] {
286        self.buf
287    }
288
289    /// Returns the root [`DevTreeNode`] object of the device tree (if it exists).
290    pub fn root(&self) -> Result<Option<DevTreeNode<'_, 'dt>>> {
291        self.nodes().next()
292    }
293}