Skip to main content

fdt_raw/
header.rs

1//! FDT header parsing.
2//!
3//! This module handles parsing of the Flattened Device Tree header structure,
4//! which appears at the beginning of every device tree blob and contains
5//! metadata about the layout and version of the FDT.
6
7use core::ptr::NonNull;
8
9use crate::FdtError;
10use crate::data::U32_SIZE;
11
12/// A 4-byte aligned buffer for header data.
13///
14/// The Device Tree Blob specification requires 4-byte alignment, and this
15/// wrapper ensures that we have properly aligned memory when reading from
16/// potentially unaligned pointers.
17#[repr(align(4))]
18struct AlignedHeader([u8; size_of::<Header>()]);
19
20/// The FDT header structure.
21///
22/// Every device tree blob begins with this header, which contains metadata
23/// about the layout and version of the FDT. All fields are stored in big-endian
24/// byte order on-disk and are converted to host byte order when parsed.
25#[derive(Debug, Clone)]
26pub struct Header {
27    /// FDT header magic number (must be 0xd00dfeed)
28    pub magic: u32,
29    /// Total size in bytes of the FDT structure
30    pub totalsize: u32,
31    /// Offset in bytes from the start of the header to the structure block
32    pub off_dt_struct: u32,
33    /// Offset in bytes from the start of the header to the strings block
34    pub off_dt_strings: u32,
35    /// Offset in bytes from the start of the header to the memory reservation block
36    pub off_mem_rsvmap: u32,
37    /// FDT version number
38    pub version: u32,
39    /// Last compatible FDT version
40    pub last_comp_version: u32,
41    /// Physical ID of the boot CPU
42    pub boot_cpuid_phys: u32,
43    /// Length in bytes of the strings block
44    pub size_dt_strings: u32,
45    /// Length in bytes of the structure block
46    pub size_dt_struct: u32,
47}
48
49impl Header {
50    /// Read a header from a byte slice.
51    ///
52    /// Parses an FDT header from the beginning of a byte slice, validating
53    /// the magic number and converting all fields from big-endian to host order.
54    ///
55    /// # Errors
56    ///
57    /// Returns `FdtError::BufferTooSmall` if the slice is too small to contain
58    /// a complete header, or `FdtError::InvalidMagic` if the magic number doesn't
59    /// match the expected value.
60    pub fn from_bytes(data: &[u8]) -> Result<Self, FdtError> {
61        if data.len() < core::mem::size_of::<Header>() {
62            return Err(FdtError::BufferTooSmall {
63                pos: core::mem::size_of::<Header>(),
64            });
65        }
66        let ptr = NonNull::new(data.as_ptr() as *mut u8).ok_or(FdtError::InvalidPtr)?;
67        unsafe { Self::from_ptr(ptr.as_ptr()) }
68    }
69
70    /// Read a header from a raw pointer.
71    ///
72    /// Parses an FDT header from the memory location pointed to by `ptr`,
73    /// validating the magic number and converting all fields from big-endian
74    /// to host order. Handles unaligned pointers by copying to an aligned buffer.
75    ///
76    /// # Safety
77    ///
78    /// The caller must ensure that the pointer is valid and points to a
79    /// memory region of at least `size_of::<Header>()` bytes that contains a
80    /// valid device tree blob header.
81    ///
82    /// # Errors
83    ///
84    /// Returns `FdtError::InvalidPtr` if the pointer is null, or
85    /// `FdtError::InvalidMagic` if the magic number doesn't match.
86    pub unsafe fn from_ptr(ptr: *mut u8) -> Result<Self, FdtError> {
87        if !(ptr as usize).is_multiple_of(Self::alignment()) {
88            // Pointer is not aligned, so we need to copy the data to an aligned
89            // buffer first.
90            let mut aligned = AlignedHeader([0u8; core::mem::size_of::<Header>()]);
91            unsafe {
92                core::ptr::copy_nonoverlapping(
93                    ptr,
94                    aligned.0.as_mut_ptr(),
95                    core::mem::size_of::<Header>(),
96                );
97            }
98            Self::from_aligned_ptr(aligned.0.as_mut_ptr())
99        } else {
100            // Pointer is aligned, we can read directly from it.
101            Self::from_aligned_ptr(ptr)
102        }
103    }
104
105    /// Read a header from an aligned pointer.
106    ///
107    /// Internal helper that assumes the pointer is already 4-byte aligned.
108    /// Reads the raw header bytes and converts each field from big-endian.
109    ///
110    /// # Safety
111    ///
112    /// Caller must ensure the pointer is valid, aligned, and points to
113    /// sufficient memory containing a valid FDT header.
114    fn from_aligned_ptr(ptr: *mut u8) -> Result<Self, FdtError> {
115        let ptr = NonNull::new(ptr).ok_or(FdtError::InvalidPtr)?;
116
117        // SAFETY: caller provided a valid pointer to the beginning of a device
118        // tree blob. We read the raw header as it exists in memory (which is
119        // big-endian on-disk). Then convert each u32 field from big-endian to
120        // host order using `u32::from_be`.
121        let raw = unsafe { &*(ptr.cast::<Header>().as_ptr()) };
122
123        let magic = u32::from_be(raw.magic);
124        if magic != crate::FDT_MAGIC {
125            return Err(FdtError::InvalidMagic(magic));
126        }
127
128        Ok(Header {
129            magic,
130            totalsize: u32::from_be(raw.totalsize),
131            off_dt_struct: u32::from_be(raw.off_dt_struct),
132            off_dt_strings: u32::from_be(raw.off_dt_strings),
133            off_mem_rsvmap: u32::from_be(raw.off_mem_rsvmap),
134            version: u32::from_be(raw.version),
135            last_comp_version: u32::from_be(raw.last_comp_version),
136            boot_cpuid_phys: u32::from_be(raw.boot_cpuid_phys),
137            size_dt_strings: u32::from_be(raw.size_dt_strings),
138            size_dt_struct: u32::from_be(raw.size_dt_struct),
139        })
140    }
141
142    /// Returns the required alignment for FDT structures.
143    #[inline]
144    pub const fn alignment() -> usize {
145        U32_SIZE
146    }
147}