multiboot2_header/
header.rs

1use crate::{
2    AddressHeaderTag, ConsoleHeaderTag, EfiBootServiceHeaderTag, EntryAddressHeaderTag,
3    EntryEfi32HeaderTag, EntryEfi64HeaderTag, FramebufferHeaderTag, HeaderTagHeader, HeaderTagISA,
4    HeaderTagType, InformationRequestHeaderTag, ModuleAlignHeaderTag, RelocatableHeaderTag,
5    TagIter,
6};
7use core::fmt::{Debug, Formatter};
8use core::mem::size_of;
9use core::ptr::NonNull;
10use multiboot2_common::{ALIGNMENT, DynSizedStructure, Header, MemoryError, Tag};
11use thiserror::Error;
12
13/// Magic value for a [`Multiboot2Header`], as defined by the spec.
14pub const MAGIC: u32 = 0xe85250d6;
15
16/// Wrapper type around a pointer to the Multiboot2 header.
17///
18/// The Multiboot2 header is the [`Multiboot2BasicHeader`] followed
19/// by all tags (see [`crate::tags::HeaderTagType`]).
20/// Use this if you get a pointer to the header and just want
21/// to parse it. If you want to construct the type by yourself,
22/// please look at `HeaderBuilder` (requires the `builder` feature).
23#[repr(transparent)]
24pub struct Multiboot2Header<'a>(&'a DynSizedStructure<Multiboot2BasicHeader>);
25
26impl<'a> Multiboot2Header<'a> {
27    /// Public constructor for this type with various validations.
28    ///
29    /// If the header is invalid, it returns a [`LoadError`].
30    /// This may be because:
31    /// - `addr` is a null-pointer
32    /// - `addr` isn't 8-byte aligned
33    /// - the magic value of the header is not present
34    /// - the checksum field is invalid
35    ///
36    /// # Safety
37    /// This function may produce undefined behaviour, if the provided `addr` is not a valid
38    /// Multiboot2 header pointer.
39    pub unsafe fn load(ptr: *const Multiboot2BasicHeader) -> Result<Self, LoadError> {
40        let ptr = NonNull::new(ptr.cast_mut()).ok_or(LoadError::Memory(MemoryError::Null))?;
41        let inner = unsafe { DynSizedStructure::ref_from_ptr(ptr).map_err(LoadError::Memory)? };
42        let this = Self(inner);
43
44        let header = this.0.header();
45        if header.header_magic != MAGIC {
46            return Err(LoadError::MagicNotFound);
47        }
48        if !header.verify_checksum() {
49            return Err(LoadError::ChecksumMismatch);
50        }
51        Ok(this)
52    }
53
54    /// Find the header in a given slice.
55    ///
56    /// If it succeeds, it returns a tuple consisting of the subslice containing
57    /// just the header and the index of the header in the given slice.
58    /// If it fails (either because the header is not properly 64-bit aligned
59    /// or because it is truncated), it returns a [`LoadError`].
60    /// If there is no header, it returns `None`.
61    pub fn find_header(buffer: &[u8]) -> Result<Option<(&[u8], u32)>, LoadError> {
62        if buffer.as_ptr().align_offset(ALIGNMENT) != 0 {
63            return Err(LoadError::Memory(MemoryError::WrongAlignment));
64        }
65
66        let mut windows = buffer[0..8192].windows(4);
67        let magic_index = match windows.position(|vals| {
68            u32::from_le_bytes(vals.try_into().unwrap()) // yes, there's 4 bytes here
69            == MAGIC
70        }) {
71            Some(idx) => {
72                if idx % 8 == 0 {
73                    idx
74                } else {
75                    return Err(LoadError::Memory(MemoryError::WrongAlignment));
76                }
77            }
78            None => return Ok(None),
79        };
80        // skip over rest of magic
81        windows.next();
82        windows.next();
83        windows.next();
84        // arch
85        windows.next();
86        windows.next();
87        windows.next();
88        windows.next();
89        let header_length: usize = u32::from_le_bytes(
90            windows
91                .next()
92                .ok_or(LoadError::Memory(MemoryError::MissingPadding))?
93                .try_into()
94                .unwrap(), // 4 bytes are a u32
95        )
96        .try_into()
97        .unwrap();
98        Ok(Some((
99            &buffer[magic_index..magic_index + header_length],
100            magic_index as u32,
101        )))
102    }
103
104    /// Returns a [`TagIter`].
105    #[must_use]
106    pub fn iter(&self) -> TagIter {
107        TagIter::new(self.0.payload())
108    }
109
110    /// Wrapper around [`Multiboot2BasicHeader::verify_checksum`].
111    #[must_use]
112    pub const fn verify_checksum(&self) -> bool {
113        self.0.header().verify_checksum()
114    }
115    /// Wrapper around [`Multiboot2BasicHeader::header_magic`].
116    #[must_use]
117    pub const fn header_magic(&self) -> u32 {
118        self.0.header().header_magic()
119    }
120    /// Wrapper around [`Multiboot2BasicHeader::arch`].
121    #[must_use]
122    pub const fn arch(&self) -> HeaderTagISA {
123        self.0.header().arch()
124    }
125    /// Wrapper around [`Multiboot2BasicHeader::length`].
126    #[must_use]
127    pub const fn length(&self) -> u32 {
128        self.0.header().length()
129    }
130    /// Wrapper around [`Multiboot2BasicHeader::checksum`].
131    #[must_use]
132    pub const fn checksum(&self) -> u32 {
133        self.0.header().checksum()
134    }
135    /// Wrapper around [`Multiboot2BasicHeader::calc_checksum`].
136    #[must_use]
137    pub const fn calc_checksum(magic: u32, arch: HeaderTagISA, length: u32) -> u32 {
138        Multiboot2BasicHeader::calc_checksum(magic, arch, length)
139    }
140
141    /// Search for the [`InformationRequestHeaderTag`] header tag.
142    #[must_use]
143    pub fn information_request_tag(&self) -> Option<&InformationRequestHeaderTag> {
144        self.get_tag()
145    }
146
147    /// Search for the [`AddressHeaderTag`] header tag.
148    #[must_use]
149    pub fn address_tag(&self) -> Option<&AddressHeaderTag> {
150        self.get_tag()
151    }
152
153    /// Search for the [`EntryAddressHeaderTag`] header tag.
154    #[must_use]
155    pub fn entry_address_tag(&self) -> Option<&EntryAddressHeaderTag> {
156        self.get_tag()
157    }
158
159    /// Search for the [`EntryEfi32HeaderTag`] header tag.
160    #[must_use]
161    pub fn entry_address_efi32_tag(&self) -> Option<&EntryEfi32HeaderTag> {
162        self.get_tag()
163    }
164
165    /// Search for the [`EntryEfi64HeaderTag`] header tag.
166    #[must_use]
167    pub fn entry_address_efi64_tag(&self) -> Option<&EntryEfi64HeaderTag> {
168        self.get_tag()
169    }
170
171    /// Search for the [`ConsoleHeaderTag`] header tag.
172    #[must_use]
173    pub fn console_flags_tag(&self) -> Option<&ConsoleHeaderTag> {
174        self.get_tag()
175    }
176
177    /// Search for the [`FramebufferHeaderTag`] header tag.
178    #[must_use]
179    pub fn framebuffer_tag(&self) -> Option<&FramebufferHeaderTag> {
180        self.get_tag()
181    }
182
183    /// Search for the [`ModuleAlignHeaderTag`] header tag.
184    #[must_use]
185    pub fn module_align_tag(&self) -> Option<&ModuleAlignHeaderTag> {
186        self.get_tag()
187    }
188
189    /// Search for the [`EfiBootServiceHeaderTag`] header tag.
190    #[must_use]
191    pub fn efi_boot_services_tag(&self) -> Option<&EfiBootServiceHeaderTag> {
192        self.get_tag()
193    }
194
195    /// Search for the [`RelocatableHeaderTag`] header tag.
196    #[must_use]
197    pub fn relocatable_tag(&self) -> Option<&RelocatableHeaderTag> {
198        self.get_tag()
199    }
200
201    /// Searches for the specified tag by iterating the structure and returns
202    /// the first occurrence, if present.
203    #[must_use]
204    fn get_tag<T: Tag<IDType = HeaderTagType, Header = HeaderTagHeader> + ?Sized + 'a>(
205        &'a self,
206    ) -> Option<&'a T> {
207        self.iter()
208            .find(|tag| tag.header().typ() == T::ID)
209            .map(|tag| tag.cast::<T>())
210    }
211}
212
213impl Debug for Multiboot2Header<'_> {
214    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
215        f.debug_struct("Multiboot2Header")
216            .field("magic", &self.header_magic())
217            .field("arch", &self.arch())
218            .field("length", &self.length())
219            .field("checksum", &self.checksum())
220            // TODO better debug impl
221            .field("tags", &"<tags iter>")
222            .finish()
223    }
224}
225
226/// Errors that occur when a chunk of memory can't be parsed as
227/// [`Multiboot2Header`].
228#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Error)]
229pub enum LoadError {
230    /// The provided checksum does not match the expected value.
231    #[error("checksum does not match expected value")]
232    ChecksumMismatch,
233    /// The header does not contain the correct magic number.
234    #[error("header does not contain expected magic value")]
235    MagicNotFound,
236    /// The provided memory can't be parsed as [`Multiboot2Header`].
237    /// See [`MemoryError`].
238    #[error("memory can't be parsed as multiboot2 header")]
239    Memory(#[source] MemoryError),
240}
241
242/// The "basic" Multiboot2 header. This means only the properties, that are known during
243/// compile time. All other information are derived during runtime from the size property.
244#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
245#[repr(C, align(8))]
246pub struct Multiboot2BasicHeader {
247    /// Must be the value of [`MAGIC`].
248    header_magic: u32,
249    arch: HeaderTagISA,
250    length: u32,
251    checksum: u32,
252    // Followed by dynamic amount of dynamically sized header tags.
253    // At minimum, the end tag.
254}
255
256impl Multiboot2BasicHeader {
257    #[cfg(feature = "builder")]
258    /// Constructor for the basic header.
259    pub(crate) const fn new(arch: HeaderTagISA, length: u32) -> Self {
260        let magic = MAGIC;
261        let checksum = Self::calc_checksum(magic, arch, length);
262        Self {
263            header_magic: magic,
264            arch,
265            length,
266            checksum,
267        }
268    }
269
270    /// Verifies that a Multiboot2 header is valid.
271    #[must_use]
272    pub const fn verify_checksum(&self) -> bool {
273        let check = Self::calc_checksum(self.header_magic, self.arch, self.length);
274        check == self.checksum
275    }
276
277    /// Calculates the checksum as described in the spec.
278    #[must_use]
279    pub const fn calc_checksum(magic: u32, arch: HeaderTagISA, length: u32) -> u32 {
280        (0x100000000 - magic as u64 - arch as u64 - length as u64) as u32
281    }
282
283    /// Returns the header magic.
284    #[must_use]
285    pub const fn header_magic(&self) -> u32 {
286        self.header_magic
287    }
288
289    /// Returns the [`HeaderTagISA`].
290    #[must_use]
291    pub const fn arch(&self) -> HeaderTagISA {
292        self.arch
293    }
294
295    /// Returns the length.
296    #[must_use]
297    pub const fn length(&self) -> u32 {
298        self.length
299    }
300
301    /// Returns the checksum.
302    #[must_use]
303    pub const fn checksum(&self) -> u32 {
304        self.checksum
305    }
306}
307
308impl Header for Multiboot2BasicHeader {
309    fn payload_len(&self) -> usize {
310        self.length as usize - size_of::<Self>()
311    }
312
313    fn set_size(&mut self, total_size: usize) {
314        self.length = total_size as u32;
315        self.checksum = Self::calc_checksum(self.header_magic, self.arch, total_size as u32);
316    }
317}
318
319impl Debug for Multiboot2BasicHeader {
320    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
321        f.debug_struct("Multiboot2Header")
322            .field("header_magic", &{ self.header_magic })
323            .field("arch", &{ self.arch })
324            .field("length", &{ self.length })
325            .field("checksum", &{ self.checksum })
326            //.field("tags", &self.iter())
327            .finish()
328    }
329}
330
331#[cfg(test)]
332mod tests {
333    use crate::Multiboot2BasicHeader;
334
335    #[test]
336    fn test_assert_size() {
337        assert_eq!(core::mem::size_of::<Multiboot2BasicHeader>(), 4 + 4 + 4 + 4);
338    }
339}