multiboot2_header/
header.rs1use 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
14pub const MAGIC: u32 = 0xe85250d6;
16
17#[repr(transparent)]
25pub struct Multiboot2Header<'a>(&'a DynSizedStructure<Multiboot2BasicHeader>);
26
27impl<'a> Multiboot2Header<'a> {
28 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 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()) == 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 windows.next();
83 windows.next();
84 windows.next();
85 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(), )
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 #[must_use]
107 pub fn iter(&self) -> TagIter {
108 TagIter::new(self.0.payload())
109 }
110
111 #[must_use]
113 pub const fn verify_checksum(&self) -> bool {
114 self.0.header().verify_checksum()
115 }
116 #[must_use]
118 pub const fn header_magic(&self) -> u32 {
119 self.0.header().header_magic()
120 }
121 #[must_use]
123 pub const fn arch(&self) -> HeaderTagISA {
124 self.0.header().arch()
125 }
126 #[must_use]
128 pub const fn length(&self) -> u32 {
129 self.0.header().length()
130 }
131 #[must_use]
133 pub const fn checksum(&self) -> u32 {
134 self.0.header().checksum()
135 }
136 #[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 #[must_use]
144 pub fn information_request_tag(&self) -> Option<&InformationRequestHeaderTag> {
145 self.get_tag()
146 }
147
148 #[must_use]
150 pub fn address_tag(&self) -> Option<&AddressHeaderTag> {
151 self.get_tag()
152 }
153
154 #[must_use]
156 pub fn entry_address_tag(&self) -> Option<&EntryAddressHeaderTag> {
157 self.get_tag()
158 }
159
160 #[must_use]
162 pub fn entry_address_efi32_tag(&self) -> Option<&EntryEfi32HeaderTag> {
163 self.get_tag()
164 }
165
166 #[must_use]
168 pub fn entry_address_efi64_tag(&self) -> Option<&EntryEfi64HeaderTag> {
169 self.get_tag()
170 }
171
172 #[must_use]
174 pub fn console_flags_tag(&self) -> Option<&ConsoleHeaderTag> {
175 self.get_tag()
176 }
177
178 #[must_use]
180 pub fn framebuffer_tag(&self) -> Option<&FramebufferHeaderTag> {
181 self.get_tag()
182 }
183
184 #[must_use]
186 pub fn module_align_tag(&self) -> Option<&ModuleAlignHeaderTag> {
187 self.get_tag()
188 }
189
190 #[must_use]
192 pub fn efi_boot_services_tag(&self) -> Option<&EfiBootServiceHeaderTag> {
193 self.get_tag()
194 }
195
196 #[must_use]
198 pub fn relocatable_tag(&self) -> Option<&RelocatableHeaderTag> {
199 self.get_tag()
200 }
201
202 #[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 .field("tags", &"<tags iter>")
223 .finish()
224 }
225}
226
227#[derive(Copy, Clone, Debug, derive_more::Display, PartialEq, Eq, PartialOrd, Ord, Hash)]
230pub enum LoadError {
231 ChecksumMismatch,
233 MagicNotFound,
235 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#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
253#[repr(C, align(8))]
254pub struct Multiboot2BasicHeader {
255 header_magic: u32,
257 arch: HeaderTagISA,
258 length: u32,
259 checksum: u32,
260 }
263
264impl Multiboot2BasicHeader {
265 #[cfg(feature = "builder")]
266 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 #[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 #[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 #[must_use]
293 pub const fn header_magic(&self) -> u32 {
294 self.header_magic
295 }
296
297 #[must_use]
299 pub const fn arch(&self) -> HeaderTagISA {
300 self.arch
301 }
302
303 #[must_use]
305 pub const fn length(&self) -> u32 {
306 self.length
307 }
308
309 #[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 .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}