use crate::{
AddressHeaderTag, ConsoleHeaderTag, EfiBootServiceHeaderTag, EndHeaderTag,
EntryAddressHeaderTag, EntryEfi32HeaderTag, EntryEfi64HeaderTag, FramebufferHeaderTag,
HeaderTag, HeaderTagISA, HeaderTagType, InformationRequestHeaderTag, RelocatableHeaderTag,
};
use core::fmt::{Debug, Formatter};
use core::mem::size_of;
pub const MULTIBOOT2_HEADER_MAGIC: u32 = 0xe85250d6;
#[repr(transparent)]
pub struct Multiboot2Header<'a> {
inner: &'a Multiboot2BasicHeader,
}
impl<'a> Multiboot2Header<'a> {
pub unsafe fn from_addr(addr: usize) -> Self {
assert_ne!(0, addr, "`addr` is null pointer");
assert_eq!(
addr % 8,
0,
"`addr` must be 8-byte aligned, see Multiboot2 spec"
);
let ptr = addr as *const Multiboot2BasicHeader;
let reference = &*ptr;
assert_eq!(
reference.header_magic(),
MULTIBOOT2_HEADER_MAGIC,
"The Multiboot2 header must contain the MULTIBOOT2_HEADER_MAGIC={:x}",
MULTIBOOT2_HEADER_MAGIC
);
assert!(
reference.verify_checksum(),
"checksum invalid! Is {:x}, expected {:x}",
reference.checksum(),
Self::calc_checksum(reference.header_magic, reference.arch, reference.length)
);
Self { inner: reference }
}
pub const fn verify_checksum(&self) -> bool {
self.inner.verify_checksum()
}
pub const fn header_magic(&self) -> u32 {
self.inner.header_magic()
}
pub const fn arch(&self) -> HeaderTagISA {
self.inner.arch()
}
pub const fn length(&self) -> u32 {
self.inner.length()
}
pub const fn checksum(&self) -> u32 {
self.inner.checksum()
}
pub fn iter(&self) -> Multiboot2HeaderTagIter {
self.inner.tag_iter()
}
pub const fn calc_checksum(magic: u32, arch: HeaderTagISA, length: u32) -> u32 {
Multiboot2BasicHeader::calc_checksum(magic, arch, length)
}
}
impl<'a> Debug for Multiboot2Header<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
let reference = unsafe { &*(self.inner as *const Multiboot2BasicHeader) };
Debug::fmt(reference, f)
}
}
#[derive(Copy, Clone)]
#[repr(C)]
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 = MULTIBOOT2_HEADER_MAGIC;
let checksum = Self::calc_checksum(magic, arch, length);
Multiboot2BasicHeader {
header_magic: magic,
arch,
length,
checksum,
}
}
pub const fn verify_checksum(&self) -> bool {
let check = Self::calc_checksum(self.header_magic, self.arch, self.length);
check == self.checksum
}
pub const fn calc_checksum(magic: u32, arch: HeaderTagISA, length: u32) -> u32 {
(0x100000000 - magic as u64 - arch as u64 - length as u64) as u32
}
pub const fn header_magic(&self) -> u32 {
self.header_magic
}
pub const fn arch(&self) -> HeaderTagISA {
self.arch
}
pub const fn length(&self) -> u32 {
self.length
}
pub const fn checksum(&self) -> u32 {
self.checksum
}
pub fn tag_iter(&self) -> Multiboot2HeaderTagIter {
let base_hdr_size = size_of::<Multiboot2BasicHeader>();
if base_hdr_size == self.length as usize {
panic!("No end tag!");
}
let tag_base_addr = self as *const Multiboot2BasicHeader;
let tag_base_addr = tag_base_addr as *const u8;
let tag_base_addr = unsafe { tag_base_addr.add(base_hdr_size) };
let tag_base_addr = unsafe { tag_base_addr.add(tag_base_addr.align_offset(8)) };
let tag_base_addr = tag_base_addr as *const HeaderTag;
let tags_len = self.length as usize - base_hdr_size;
Multiboot2HeaderTagIter::new(tag_base_addr, tags_len 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 })
.field("tags", &self.tag_iter())
.finish()
}
}
#[derive(Clone)]
pub struct Multiboot2HeaderTagIter {
base: *const HeaderTag,
n: u32,
size: u32,
tag_count: u32,
end_tag_found: bool,
}
impl Multiboot2HeaderTagIter {
fn new(base: *const HeaderTag, size: u32) -> Self {
let base = base as *const u8;
let base = unsafe { base.add(base.align_offset(8)) };
let base = base as *const HeaderTag;
Self {
base,
n: 0,
size,
tag_count: 0,
end_tag_found: false,
}
}
}
impl Iterator for Multiboot2HeaderTagIter {
type Item = *const HeaderTag;
fn next(&mut self) -> Option<Self::Item> {
if self.n >= self.size {
return None;
}
let ptr = self.base as *const u8;
let ptr = unsafe { ptr.add(self.n as usize) };
let ptr = ptr as *const HeaderTag;
assert_eq!(ptr as usize % 8, 0, "must be 8-byte aligned");
let tag = unsafe { &*ptr };
assert!(
tag.size() <= 500,
"no real mb2 header should be bigger than 500bytes - probably wrong memory?! is: {}",
{ tag.size() }
);
assert!(
tag.size() >= 8,
"no real mb2 header tag is smaller than 8 bytes - probably wrong memory?! is: {}",
{ tag.size() }
);
assert!(
!self.end_tag_found,
"There is more than one end tag! Maybe the `length` property is invalid?"
);
self.n += tag.size();
self.n += self.n % 8;
self.tag_count += 1;
if tag.typ() == HeaderTagType::End {
self.end_tag_found = true;
}
assert!(self.tag_count < HeaderTagType::count(), "Invalid Multiboot2 header tags! There are more tags than technically possible! Maybe the `length` property is invalid?");
Some(ptr)
}
}
impl Debug for Multiboot2HeaderTagIter {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
let mut debug = f.debug_list();
self.clone().for_each(|t| unsafe {
let typ = (*t).typ();
if typ == HeaderTagType::End {
let entry = t as *const EndHeaderTag;
let entry = &*(entry);
debug.entry(entry);
} else if typ == HeaderTagType::InformationRequest {
let entry = t as *const InformationRequestHeaderTag<0>;
let entry = &*(entry);
debug.entry(entry);
} else if typ == HeaderTagType::Address {
let entry = t as *const AddressHeaderTag;
let entry = &*(entry);
debug.entry(entry);
} else if typ == HeaderTagType::EntryAddress {
let entry = t as *const EntryAddressHeaderTag;
let entry = &*(entry);
debug.entry(entry);
} else if typ == HeaderTagType::ConsoleFlags {
let entry = t as *const ConsoleHeaderTag;
let entry = &*(entry);
debug.entry(entry);
} else if typ == HeaderTagType::Framebuffer {
let entry = t as *const FramebufferHeaderTag;
let entry = &*(entry);
debug.entry(entry);
} else if typ == HeaderTagType::EfiBS {
let entry = t as *const EfiBootServiceHeaderTag;
let entry = &*(entry);
debug.entry(entry);
} else if typ == HeaderTagType::EntryAddressEFI32 {
let entry = t as *const EntryEfi32HeaderTag;
let entry = &*(entry);
debug.entry(entry);
} else if typ == HeaderTagType::EntryAddressEFI64 {
let entry = t as *const EntryEfi64HeaderTag;
let entry = &*(entry);
debug.entry(entry);
} else if typ == HeaderTagType::Relocatable {
let entry = t as *const RelocatableHeaderTag;
let entry = &*(entry);
debug.entry(entry);
} else {
panic!("unknown tag ({:?})!", typ);
}
});
debug.finish()
}
}
#[cfg(test)]
mod tests {
use crate::Multiboot2BasicHeader;
#[test]
fn test_assert_size() {
assert_eq!(core::mem::size_of::<Multiboot2BasicHeader>(), 4 + 4 + 4 + 4);
}
}