dtb_walker/
lib.rs

1#![no_std]
2#![deny(warnings)] // cancel this line during developing
3
4mod header;
5mod indent;
6mod path;
7mod property;
8mod structure_block;
9mod walker;
10
11pub use path::Path;
12pub use property::{PHandle, Property, Reg, Str, StrList};
13pub mod utils {
14    pub use crate::indent::indent;
15}
16pub use header::HeaderError;
17
18use core::{fmt, mem, slice};
19use header::FdtHeader;
20use property::RegCfg;
21use structure_block::StructureBlock;
22use walker::Walker;
23
24/// 设备树二进制对象。
25pub struct Dtb<'a>(&'a [u8]);
26
27impl Dtb<'static> {
28    /// 构造设备树二进制对象。
29    ///
30    /// # Safety
31    ///
32    /// 如果指针指向一个有效的 DTB 首部,其中描述的整个二进制对象会被切片。
33    #[inline]
34    pub unsafe fn from_raw_parts(ptr: *const u8) -> Result<Self, HeaderError> {
35        (*ptr.cast::<FdtHeader>()).verify(|_| true)?;
36        Ok(Self::from_raw_parts_unchecked(ptr))
37    }
38
39    /// 构造设备树二进制对象。
40    ///
41    /// # Safety
42    ///
43    /// 如果指针指向一个有效的 DTB 首部,其中描述的整个二进制对象会被切片。
44    #[inline]
45    pub unsafe fn from_raw_parts_filtered(
46        ptr: *const u8,
47        f: impl Fn(&HeaderError) -> bool,
48    ) -> Result<Self, HeaderError> {
49        (*ptr.cast::<FdtHeader>()).verify(f)?;
50        Ok(Self::from_raw_parts_unchecked(ptr))
51    }
52
53    /// 不检查首部正确性,直接构造设备树二进制对象。
54    ///
55    /// # Safety
56    ///
57    /// 假设指针指向一个正确的设备树二进制对象,其首部描述的整个二进制对象会被切片。
58    #[inline]
59    pub unsafe fn from_raw_parts_unchecked(ptr: *const u8) -> Self {
60        Self(slice::from_raw_parts(
61            ptr,
62            (*ptr.cast::<FdtHeader>()).totalsize.into_u32() as _,
63        ))
64    }
65}
66
67pub enum ConvertError {
68    Truncated,
69    Header(HeaderError),
70}
71
72impl<'a> Dtb<'a> {
73    /// 从内存切片安全地创建设备树二进制对象。
74    pub fn from_slice(slice: &'a [u8]) -> Result<Self, ConvertError> {
75        if slice.len() < mem::size_of::<FdtHeader>() {
76            return Err(ConvertError::Truncated);
77        }
78        let header = unsafe { &*slice.as_ptr().cast::<FdtHeader>() };
79        match header.verify(|_| true) {
80            Ok(()) => {
81                let len = header.totalsize.into_u32() as usize;
82                if len <= slice.len() {
83                    Ok(Self(&slice[..len]))
84                } else {
85                    Err(ConvertError::Truncated)
86                }
87            }
88            Err(e) => Err(ConvertError::Header(e)),
89        }
90    }
91}
92
93impl Dtb<'_> {
94    /// 返回整个二进制对象的尺寸。
95    #[inline]
96    pub const fn total_size(&self) -> usize {
97        self.0.len()
98    }
99
100    /// 遍历。
101    pub fn walk(&self, mut f: impl FnMut(&Path<'_>, DtbObj) -> WalkOperation) {
102        let header = self.header();
103        let off_struct = header.off_dt_struct.into_u32() as usize;
104        let len_struct = header.size_dt_struct.into_u32() as usize;
105        let off_strings = header.off_dt_strings.into_u32() as usize;
106        let len_strings = header.size_dt_strings.into_u32() as usize;
107        Walker {
108            tail: unsafe {
109                slice::from_raw_parts(
110                    self.0[off_struct..]
111                        .as_ptr()
112                        .cast::<StructureBlock>()
113                        .offset(2),
114                    len_struct / StructureBlock::LEN - 3,
115                )
116            },
117            strings: &self.0[off_strings..][..len_strings],
118        }
119        .walk_inner(&mut f, &Path::ROOT, RegCfg::DEFAULT, false);
120    }
121
122    #[inline]
123    fn header(&self) -> &FdtHeader {
124        unsafe { &*self.0.as_ptr().cast() }
125    }
126}
127
128/// 设备树二进制小对象。
129pub enum DtbObj<'a> {
130    /// 子节点
131    SubNode { name: &'a [u8] },
132    /// 一般属性
133    Property(Property<'a>),
134}
135
136/// 遍历操作。
137pub enum WalkOperation {
138    /// 进入子节点
139    StepInto,
140    /// 跳过子节点
141    StepOver,
142    /// 跳过当前子树
143    StepOut,
144    /// 结束遍历
145    Terminate,
146}
147
148#[repr(transparent)]
149#[derive(Clone, Copy, PartialEq, Eq)]
150struct U32BigEndian(u32);
151
152impl U32BigEndian {
153    #[inline]
154    pub const fn from_u32(val: u32) -> Self {
155        Self(u32::to_be(val))
156    }
157
158    #[inline]
159    pub const fn into_u32(self) -> u32 {
160        u32::from_be(self.0)
161    }
162}
163
164impl fmt::Debug for U32BigEndian {
165    #[inline]
166    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
167        u32::from_be(self.0).fmt(f)
168    }
169}
170
171#[inline]
172fn is_aligned(val: usize, bits: usize) -> bool {
173    val & (bits - 1) == 0
174}