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