use crate::{
AddressHeaderTag, ConsoleHeaderTag, EfiBootServiceHeaderTag, EntryAddressHeaderTag,
EntryEfi32HeaderTag, EntryEfi64HeaderTag, FramebufferHeaderTag, HeaderTagHeader, HeaderTagISA,
HeaderTagType, InformationRequestHeaderTag, ModuleAlignHeaderTag, RelocatableHeaderTag,
TagIter,
};
use core::fmt::{Debug, Formatter};
use core::mem::size_of;
use core::ptr::NonNull;
use multiboot2_common::{ALIGNMENT, DynSizedStructure, Header, MemoryError, Tag};
use thiserror::Error;
pub const MAGIC: u32 = 0xe85250d6;
#[repr(transparent)]
pub struct Multiboot2Header<'a>(&'a DynSizedStructure<Multiboot2BasicHeader>);
impl<'a> Multiboot2Header<'a> {
pub unsafe fn load(ptr: *const Multiboot2BasicHeader) -> Result<Self, LoadError> {
let ptr = NonNull::new(ptr.cast_mut()).ok_or(LoadError::Memory(MemoryError::Null))?;
let inner = unsafe { DynSizedStructure::ref_from_ptr(ptr).map_err(LoadError::Memory)? };
let this = Self(inner);
let header = this.0.header();
if header.header_magic != MAGIC {
return Err(LoadError::MagicNotFound);
}
if !header.verify_checksum() {
return Err(LoadError::ChecksumMismatch);
}
Ok(this)
}
pub fn find_header(buffer: &[u8]) -> Result<Option<(&[u8], u32)>, LoadError> {
if buffer.as_ptr().align_offset(ALIGNMENT) != 0 {
return Err(LoadError::Memory(MemoryError::WrongAlignment));
}
let mut windows = buffer[0..8192].windows(4);
let magic_index = match windows.position(|vals| {
u32::from_le_bytes(vals.try_into().unwrap()) == MAGIC
}) {
Some(idx) => {
if idx % 8 == 0 {
idx
} else {
return Err(LoadError::Memory(MemoryError::WrongAlignment));
}
}
None => return Ok(None),
};
windows.next();
windows.next();
windows.next();
windows.next();
windows.next();
windows.next();
windows.next();
let header_length: usize = u32::from_le_bytes(
windows
.next()
.ok_or(LoadError::Memory(MemoryError::MissingPadding))?
.try_into()
.unwrap(), )
.try_into()
.unwrap();
Ok(Some((
&buffer[magic_index..magic_index + header_length],
magic_index as u32,
)))
}
#[must_use]
pub fn iter(&self) -> TagIter {
TagIter::new(self.0.payload())
}
#[must_use]
pub const fn verify_checksum(&self) -> bool {
self.0.header().verify_checksum()
}
#[must_use]
pub const fn header_magic(&self) -> u32 {
self.0.header().header_magic()
}
#[must_use]
pub const fn arch(&self) -> HeaderTagISA {
self.0.header().arch()
}
#[must_use]
pub const fn length(&self) -> u32 {
self.0.header().length()
}
#[must_use]
pub const fn checksum(&self) -> u32 {
self.0.header().checksum()
}
#[must_use]
pub const fn calc_checksum(magic: u32, arch: HeaderTagISA, length: u32) -> u32 {
Multiboot2BasicHeader::calc_checksum(magic, arch, length)
}
#[must_use]
pub fn information_request_tag(&self) -> Option<&InformationRequestHeaderTag> {
self.get_tag()
}
#[must_use]
pub fn address_tag(&self) -> Option<&AddressHeaderTag> {
self.get_tag()
}
#[must_use]
pub fn entry_address_tag(&self) -> Option<&EntryAddressHeaderTag> {
self.get_tag()
}
#[must_use]
pub fn entry_address_efi32_tag(&self) -> Option<&EntryEfi32HeaderTag> {
self.get_tag()
}
#[must_use]
pub fn entry_address_efi64_tag(&self) -> Option<&EntryEfi64HeaderTag> {
self.get_tag()
}
#[must_use]
pub fn console_flags_tag(&self) -> Option<&ConsoleHeaderTag> {
self.get_tag()
}
#[must_use]
pub fn framebuffer_tag(&self) -> Option<&FramebufferHeaderTag> {
self.get_tag()
}
#[must_use]
pub fn module_align_tag(&self) -> Option<&ModuleAlignHeaderTag> {
self.get_tag()
}
#[must_use]
pub fn efi_boot_services_tag(&self) -> Option<&EfiBootServiceHeaderTag> {
self.get_tag()
}
#[must_use]
pub fn relocatable_tag(&self) -> Option<&RelocatableHeaderTag> {
self.get_tag()
}
#[must_use]
fn get_tag<T: Tag<IDType = HeaderTagType, Header = HeaderTagHeader> + ?Sized + 'a>(
&'a self,
) -> Option<&'a T> {
self.iter()
.find(|tag| tag.header().typ() == T::ID)
.map(|tag| tag.cast::<T>())
}
}
impl Debug for Multiboot2Header<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Multiboot2Header")
.field("magic", &self.header_magic())
.field("arch", &self.arch())
.field("length", &self.length())
.field("checksum", &self.checksum())
.field("tags", &"<tags iter>")
.finish()
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Error)]
pub enum LoadError {
#[error("checksum does not match expected value")]
ChecksumMismatch,
#[error("header does not contain expected magic value")]
MagicNotFound,
#[error("memory can't be parsed as multiboot2 header")]
Memory(#[source] MemoryError),
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(C, align(8))]
pub struct Multiboot2BasicHeader {
header_magic: u32,
arch: HeaderTagISA,
length: u32,
checksum: u32,
}
impl Multiboot2BasicHeader {
#[cfg(feature = "builder")]
pub(crate) const fn new(arch: HeaderTagISA, length: u32) -> Self {
let magic = MAGIC;
let checksum = Self::calc_checksum(magic, arch, length);
Self {
header_magic: magic,
arch,
length,
checksum,
}
}
#[must_use]
pub const fn verify_checksum(&self) -> bool {
let check = Self::calc_checksum(self.header_magic, self.arch, self.length);
check == self.checksum
}
#[must_use]
pub const fn calc_checksum(magic: u32, arch: HeaderTagISA, length: u32) -> u32 {
(0x100000000 - magic as u64 - arch as u64 - length as u64) as u32
}
#[must_use]
pub const fn header_magic(&self) -> u32 {
self.header_magic
}
#[must_use]
pub const fn arch(&self) -> HeaderTagISA {
self.arch
}
#[must_use]
pub const fn length(&self) -> u32 {
self.length
}
#[must_use]
pub const fn checksum(&self) -> u32 {
self.checksum
}
}
impl Header for Multiboot2BasicHeader {
fn payload_len(&self) -> usize {
self.length as usize - size_of::<Self>()
}
fn set_size(&mut self, total_size: usize) {
self.length = total_size as u32;
self.checksum = Self::calc_checksum(self.header_magic, self.arch, total_size as u32);
}
}
impl Debug for Multiboot2BasicHeader {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Multiboot2Header")
.field("header_magic", &{ self.header_magic })
.field("arch", &{ self.arch })
.field("length", &{ self.length })
.field("checksum", &{ self.checksum })
.finish()
}
}
#[cfg(test)]
mod tests {
use crate::Multiboot2BasicHeader;
#[test]
fn test_assert_size() {
assert_eq!(core::mem::size_of::<Multiboot2BasicHeader>(), 4 + 4 + 4 + 4);
}
}