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}