fdt_parser/
header.rs

1use core::ptr::NonNull;
2
3use crate::FdtError;
4
5#[repr(align(4))]
6struct AlignedHeader([u8; size_of::<Header>()]);
7
8#[derive(Debug, Clone)]
9pub struct Header {
10    /// FDT header magic
11    pub magic: u32,
12    /// Total size in bytes of the FDT structure
13    pub totalsize: u32,
14    /// Offset in bytes from the start of the header to the structure block
15    pub off_dt_struct: u32,
16    /// Offset in bytes from the start of the header to the strings block
17    pub off_dt_strings: u32,
18    /// Offset in bytes from the start of the header to the memory reservation
19    /// block
20    pub off_mem_rsvmap: u32,
21    /// FDT version
22    pub version: u32,
23    /// Last compatible FDT version
24    pub last_comp_version: u32,
25    /// System boot CPU ID
26    pub boot_cpuid_phys: u32,
27    /// Length in bytes of the strings block
28    pub size_dt_strings: u32,
29    /// Length in bytes of the struct block
30    pub size_dt_struct: u32,
31}
32
33impl Header {
34    /// Read a header from a byte slice and return an owned `Header` whose
35    /// fields are converted from big-endian (on-disk) to host order.
36    pub fn from_bytes(data: &[u8]) -> Result<Self, FdtError> {
37        if data.len() < core::mem::size_of::<Header>() {
38            return Err(FdtError::BufferTooSmall {
39                pos: core::mem::size_of::<Header>(),
40            });
41        }
42        let ptr = NonNull::new(data.as_ptr() as *mut u8).ok_or(FdtError::InvalidPtr)?;
43        unsafe { Self::from_ptr(ptr.as_ptr()) }
44    }
45
46    /// Read a header from a raw pointer and return an owned `Header` whose
47    /// fields are converted from big-endian (on-disk) to host order.
48    ///
49    /// # Safety
50    ///
51    /// The caller must ensure that the pointer is valid and points to a
52    /// memory region of at least `size_of::<Header>()` bytes that contains a
53    /// valid device tree blob.
54    pub unsafe fn from_ptr(ptr: *mut u8) -> Result<Self, FdtError> {
55        if !(ptr as usize).is_multiple_of(core::mem::align_of::<Header>()) {
56            // Pointer is not aligned, so we need to copy the data to an aligned
57            // buffer first.
58            let mut aligned = AlignedHeader([0u8; core::mem::size_of::<Header>()]);
59            unsafe {
60                core::ptr::copy_nonoverlapping(
61                    ptr,
62                    aligned.0.as_mut_ptr(),
63                    core::mem::size_of::<Header>(),
64                );
65            }
66            Self::from_aligned_ptr(aligned.0.as_mut_ptr())
67        } else {
68            // Pointer is aligned, we can read directly from it.
69            Self::from_aligned_ptr(ptr)
70        }
71    }
72
73    fn from_aligned_ptr(ptr: *mut u8) -> Result<Self, FdtError> {
74        let ptr = NonNull::new(ptr).ok_or(FdtError::InvalidPtr)?;
75
76        // SAFETY: caller provided a valid pointer to the beginning of a device
77        // tree blob. We read the raw header as it exists in memory (which is
78        // big-endian on-disk). Then convert each u32 field from big-endian to
79        // host order using `u32::from_be`.
80        let raw = unsafe { &*(ptr.cast::<Header>().as_ptr()) };
81
82        let magic = u32::from_be(raw.magic);
83        if magic != crate::FDT_MAGIC {
84            return Err(FdtError::InvalidMagic(magic));
85        }
86
87        Ok(Header {
88            magic,
89            totalsize: u32::from_be(raw.totalsize),
90            off_dt_struct: u32::from_be(raw.off_dt_struct),
91            off_dt_strings: u32::from_be(raw.off_dt_strings),
92            off_mem_rsvmap: u32::from_be(raw.off_mem_rsvmap),
93            version: u32::from_be(raw.version),
94            last_comp_version: u32::from_be(raw.last_comp_version),
95            boot_cpuid_phys: u32::from_be(raw.boot_cpuid_phys),
96            size_dt_strings: u32::from_be(raw.size_dt_strings),
97            size_dt_struct: u32::from_be(raw.size_dt_struct),
98        })
99    }
100}