Skip to content

Commit 3b9c72f

Browse files
authored
Merge pull request #136 from YtvwlD/bootloader-header
add missing functionality in multiboot2-header (finding the header, getting tags)
2 parents e89b493 + e1b7ffb commit 3b9c72f

File tree

1 file changed

+145
-26
lines changed

1 file changed

+145
-26
lines changed

multiboot2-header/src/header.rs

+145-26
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
use crate::{
22
AddressHeaderTag, ConsoleHeaderTag, EfiBootServiceHeaderTag, EndHeaderTag,
33
EntryAddressHeaderTag, EntryEfi32HeaderTag, EntryEfi64HeaderTag, FramebufferHeaderTag,
4-
HeaderTag, HeaderTagISA, HeaderTagType, InformationRequestHeaderTag, RelocatableHeaderTag,
4+
HeaderTag, HeaderTagISA, HeaderTagType, InformationRequestHeaderTag, ModuleAlignHeaderTag,
5+
RelocatableHeaderTag,
56
};
7+
use core::convert::TryInto;
68
use core::fmt::{Debug, Formatter};
79
use core::mem::size_of;
810

@@ -21,12 +23,10 @@ pub struct Multiboot2Header<'a> {
2123
}
2224

2325
impl<'a> Multiboot2Header<'a> {
24-
/// Public constructor for this type with various validations. It panics if the address is invalid.
25-
/// It panics rather than returning a result, because if this fails, it is
26-
/// a fatal, unrecoverable error anyways and a bug in your code.
26+
/// Public constructor for this type with various validations.
2727
///
28-
/// # Panics
29-
/// Panics if one of the following conditions is true:
28+
/// If the header is invalid, it returns a [`LoadError`].
29+
/// This may be because:
3030
/// - `addr` is a null-pointer
3131
/// - `addr` isn't 8-byte aligned
3232
/// - the magic value of the header is not present
@@ -35,28 +35,69 @@ impl<'a> Multiboot2Header<'a> {
3535
/// # Safety
3636
/// This function may produce undefined behaviour, if the provided `addr` is not a valid
3737
/// Multiboot2 header pointer.
38-
pub unsafe fn from_addr(addr: usize) -> Self {
39-
assert_ne!(0, addr, "`addr` is null pointer");
40-
assert_eq!(
41-
addr % 8,
42-
0,
43-
"`addr` must be 8-byte aligned, see Multiboot2 spec"
44-
);
38+
// This function can be `const` on newer Rust versions.
39+
#[allow(clippy::missing_const_for_fn)]
40+
pub unsafe fn from_addr(addr: usize) -> Result<Self, LoadError> {
41+
if addr == 0 || addr % 8 != 0 {
42+
return Err(LoadError::InvalidAddress);
43+
}
4544
let ptr = addr as *const Multiboot2BasicHeader;
4645
let reference = &*ptr;
47-
assert_eq!(
48-
reference.header_magic(),
49-
MULTIBOOT2_HEADER_MAGIC,
50-
"The Multiboot2 header must contain the MULTIBOOT2_HEADER_MAGIC={:x}",
51-
MULTIBOOT2_HEADER_MAGIC
52-
);
53-
assert!(
54-
reference.verify_checksum(),
55-
"checksum invalid! Is {:x}, expected {:x}",
56-
reference.checksum(),
57-
Self::calc_checksum(reference.header_magic, reference.arch, reference.length)
58-
);
59-
Self { inner: reference }
46+
if reference.header_magic() != MULTIBOOT2_HEADER_MAGIC {
47+
return Err(LoadError::MagicNotFound);
48+
}
49+
if !reference.verify_checksum() {
50+
return Err(LoadError::ChecksumMismatch);
51+
}
52+
Ok(Self { inner: reference })
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 properply 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+
// the magic is 32 bit aligned and inside the first 8192 bytes
64+
assert!(buffer.len() >= 8192);
65+
let mut windows = buffer[0..8192].windows(4);
66+
let magic_index = match windows.position(|vals| {
67+
u32::from_le_bytes(vals.try_into().unwrap()) // yes, there's 4 bytes here
68+
== MULTIBOOT2_HEADER_MAGIC
69+
}) {
70+
Some(idx) => {
71+
if idx % 8 == 0 {
72+
idx
73+
} else {
74+
return Err(LoadError::InvalidAddress);
75+
}
76+
}
77+
None => return Ok(None),
78+
};
79+
// skip over rest of magic
80+
windows.next();
81+
windows.next();
82+
windows.next();
83+
// arch
84+
windows.next();
85+
windows.next();
86+
windows.next();
87+
windows.next();
88+
let header_length: usize = u32::from_le_bytes(
89+
windows
90+
.next()
91+
.ok_or(LoadError::TooSmall)?
92+
.try_into()
93+
.unwrap(), // 4 bytes are a u32
94+
)
95+
.try_into()
96+
.unwrap();
97+
Ok(Some((
98+
&buffer[magic_index..magic_index + header_length],
99+
magic_index as u32,
100+
)))
60101
}
61102

62103
/// Wrapper around [`Multiboot2BasicHeader::verify_checksum`].
@@ -87,6 +128,66 @@ impl<'a> Multiboot2Header<'a> {
87128
pub const fn calc_checksum(magic: u32, arch: HeaderTagISA, length: u32) -> u32 {
88129
Multiboot2BasicHeader::calc_checksum(magic, arch, length)
89130
}
131+
132+
/// Search for the address header tag.
133+
pub fn address_tag(&self) -> Option<&AddressHeaderTag> {
134+
self.get_tag(HeaderTagType::Address)
135+
.map(|tag| unsafe { &*(tag as *const HeaderTag as *const AddressHeaderTag) })
136+
}
137+
138+
/// Search for the entry address header tag.
139+
pub fn entry_address_tag(&self) -> Option<&EntryAddressHeaderTag> {
140+
self.get_tag(HeaderTagType::EntryAddress)
141+
.map(|tag| unsafe { &*(tag as *const HeaderTag as *const EntryAddressHeaderTag) })
142+
}
143+
144+
/// Search for the EFI32 entry address header tag.
145+
pub fn entry_address_efi32_tag(&self) -> Option<&EntryEfi32HeaderTag> {
146+
self.get_tag(HeaderTagType::EntryAddressEFI32)
147+
.map(|tag| unsafe { &*(tag as *const HeaderTag as *const EntryEfi32HeaderTag) })
148+
}
149+
150+
/// Search for the EFI64 entry address header tag.
151+
pub fn entry_address_efi64_tag(&self) -> Option<&EntryEfi64HeaderTag> {
152+
self.get_tag(HeaderTagType::EntryAddressEFI64)
153+
.map(|tag| unsafe { &*(tag as *const HeaderTag as *const EntryEfi64HeaderTag) })
154+
}
155+
156+
/// Search for the console flags header tag.
157+
pub fn console_flags_tag(&self) -> Option<&ConsoleHeaderTag> {
158+
self.get_tag(HeaderTagType::ConsoleFlags)
159+
.map(|tag| unsafe { &*(tag as *const HeaderTag as *const ConsoleHeaderTag) })
160+
}
161+
162+
/// Search for the framebuffer header tag.
163+
pub fn framebuffer_tag(&self) -> Option<&FramebufferHeaderTag> {
164+
self.get_tag(HeaderTagType::Framebuffer)
165+
.map(|tag| unsafe { &*(tag as *const HeaderTag as *const FramebufferHeaderTag) })
166+
}
167+
168+
/// Search for the module align header tag.
169+
pub fn module_align_tag(&self) -> Option<&ModuleAlignHeaderTag> {
170+
self.get_tag(HeaderTagType::ModuleAlign)
171+
.map(|tag| unsafe { &*(tag as *const HeaderTag as *const ModuleAlignHeaderTag) })
172+
}
173+
174+
/// Search for the EFI Boot Services header tag.
175+
pub fn efi_boot_services_tag(&self) -> Option<&EfiBootServiceHeaderTag> {
176+
self.get_tag(HeaderTagType::EfiBS)
177+
.map(|tag| unsafe { &*(tag as *const HeaderTag as *const EfiBootServiceHeaderTag) })
178+
}
179+
180+
/// Search for the EFI32 entry address header tag.
181+
pub fn relocatable_tag(&self) -> Option<&RelocatableHeaderTag> {
182+
self.get_tag(HeaderTagType::Relocatable)
183+
.map(|tag| unsafe { &*(tag as *const HeaderTag as *const RelocatableHeaderTag) })
184+
}
185+
186+
fn get_tag(&self, typ: HeaderTagType) -> Option<&HeaderTag> {
187+
self.iter()
188+
.map(|tag| unsafe { tag.as_ref() }.unwrap())
189+
.find(|tag| tag.typ() == typ)
190+
}
90191
}
91192

92193
impl<'a> Debug for Multiboot2Header<'a> {
@@ -97,6 +198,20 @@ impl<'a> Debug for Multiboot2Header<'a> {
97198
}
98199
}
99200

201+
/// Errors that can occur when parsing a header from a slice.
202+
/// See [`Multiboot2Header::find_header`].
203+
#[derive(Debug, Clone, PartialEq, Eq)]
204+
pub enum LoadError {
205+
/// The checksum does not match the data.
206+
ChecksumMismatch,
207+
/// The header is not properly 64-bit aligned (or a null pointer).
208+
InvalidAddress,
209+
/// The header does not contain the correct magic number.
210+
MagicNotFound,
211+
/// The header is truncated.
212+
TooSmall,
213+
}
214+
100215
/// **Use this only if you know what you do. You probably want to use
101216
/// [`Multiboot2Header`] instead.**
102217
///
@@ -313,6 +428,10 @@ impl Debug for Multiboot2HeaderTagIter {
313428
let entry = t as *const EntryEfi64HeaderTag;
314429
let entry = &*(entry);
315430
debug.entry(entry);
431+
} else if typ == HeaderTagType::ModuleAlign {
432+
let entry = t as *const ModuleAlignHeaderTag;
433+
let entry = &*(entry);
434+
debug.entry(entry);
316435
} else if typ == HeaderTagType::Relocatable {
317436
let entry = t as *const RelocatableHeaderTag;
318437
let entry = &*(entry);

0 commit comments

Comments
 (0)