multiboot2_header/
header.rs1use 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
13pub const MAGIC: u32 = 0xe85250d6;
15
16#[repr(transparent)]
24pub struct Multiboot2Header<'a>(&'a DynSizedStructure<Multiboot2BasicHeader>);
25
26impl<'a> Multiboot2Header<'a> {
27 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 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()) == 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 windows.next();
82 windows.next();
83 windows.next();
84 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(), )
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 #[must_use]
106 pub fn iter(&self) -> TagIter {
107 TagIter::new(self.0.payload())
108 }
109
110 #[must_use]
112 pub const fn verify_checksum(&self) -> bool {
113 self.0.header().verify_checksum()
114 }
115 #[must_use]
117 pub const fn header_magic(&self) -> u32 {
118 self.0.header().header_magic()
119 }
120 #[must_use]
122 pub const fn arch(&self) -> HeaderTagISA {
123 self.0.header().arch()
124 }
125 #[must_use]
127 pub const fn length(&self) -> u32 {
128 self.0.header().length()
129 }
130 #[must_use]
132 pub const fn checksum(&self) -> u32 {
133 self.0.header().checksum()
134 }
135 #[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 #[must_use]
143 pub fn information_request_tag(&self) -> Option<&InformationRequestHeaderTag> {
144 self.get_tag()
145 }
146
147 #[must_use]
149 pub fn address_tag(&self) -> Option<&AddressHeaderTag> {
150 self.get_tag()
151 }
152
153 #[must_use]
155 pub fn entry_address_tag(&self) -> Option<&EntryAddressHeaderTag> {
156 self.get_tag()
157 }
158
159 #[must_use]
161 pub fn entry_address_efi32_tag(&self) -> Option<&EntryEfi32HeaderTag> {
162 self.get_tag()
163 }
164
165 #[must_use]
167 pub fn entry_address_efi64_tag(&self) -> Option<&EntryEfi64HeaderTag> {
168 self.get_tag()
169 }
170
171 #[must_use]
173 pub fn console_flags_tag(&self) -> Option<&ConsoleHeaderTag> {
174 self.get_tag()
175 }
176
177 #[must_use]
179 pub fn framebuffer_tag(&self) -> Option<&FramebufferHeaderTag> {
180 self.get_tag()
181 }
182
183 #[must_use]
185 pub fn module_align_tag(&self) -> Option<&ModuleAlignHeaderTag> {
186 self.get_tag()
187 }
188
189 #[must_use]
191 pub fn efi_boot_services_tag(&self) -> Option<&EfiBootServiceHeaderTag> {
192 self.get_tag()
193 }
194
195 #[must_use]
197 pub fn relocatable_tag(&self) -> Option<&RelocatableHeaderTag> {
198 self.get_tag()
199 }
200
201 #[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 .field("tags", &"<tags iter>")
222 .finish()
223 }
224}
225
226#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Error)]
229pub enum LoadError {
230 #[error("checksum does not match expected value")]
232 ChecksumMismatch,
233 #[error("header does not contain expected magic value")]
235 MagicNotFound,
236 #[error("memory can't be parsed as multiboot2 header")]
239 Memory(#[source] MemoryError),
240}
241
242#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
245#[repr(C, align(8))]
246pub struct Multiboot2BasicHeader {
247 header_magic: u32,
249 arch: HeaderTagISA,
250 length: u32,
251 checksum: u32,
252 }
255
256impl Multiboot2BasicHeader {
257 #[cfg(feature = "builder")]
258 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 #[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 #[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 #[must_use]
285 pub const fn header_magic(&self) -> u32 {
286 self.header_magic
287 }
288
289 #[must_use]
291 pub const fn arch(&self) -> HeaderTagISA {
292 self.arch
293 }
294
295 #[must_use]
297 pub const fn length(&self) -> u32 {
298 self.length
299 }
300
301 #[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 .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}