use core::convert::TryInto;
use core::fmt;
pub const MULTIBOOT_HEADER_MAGIC: u32 = 0x1BADB002;
#[derive(Copy, Clone)]
pub struct Header {
header: MultibootHeader,
pub header_start: u32,
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
struct MultibootHeader {
magic: u32,
flags: u32,
checksum: u32,
addresses: MultibootAddresses,
video_mode: MultibootVideoMode,
}
impl Header {
pub fn from_slice(buffer: &[u8]) -> Option<Self> {
let (header, header_start) = Self::find_header(buffer)?;
assert_eq!(header.magic, MULTIBOOT_HEADER_MAGIC);
assert_eq!(
header
.magic
.wrapping_add(header.flags)
.wrapping_add(header.checksum),
0
);
Some(Self {
header,
header_start,
})
}
fn find_header(buffer: &[u8]) -> Option<(MultibootHeader, u32)> {
let magic_index = match buffer.chunks_exact(4).take(8192 / 4).position(|vals| {
u32::from_le_bytes(vals.try_into().unwrap()) == MULTIBOOT_HEADER_MAGIC
}) {
Some(idx) => idx * 4,
None => return None,
};
const HEADER_SIZE: usize = core::mem::size_of::<MultibootHeader>();
let mut header_bytes: [u8; HEADER_SIZE] = [0; HEADER_SIZE];
buffer
.iter()
.skip(magic_index)
.zip(header_bytes.iter_mut())
.for_each(|(&buf, arr)| {
*arr = buf;
});
let header =
unsafe { core::mem::transmute::<[u8; HEADER_SIZE], MultibootHeader>(header_bytes) };
Some((header, magic_index as u32))
}
flag!(
doc = "If true, then the modules have to be page aligned.",
wants_modules_page_aligned,
0
);
flag!(
doc = "If true, memory information must be passed.",
wants_memory_information,
1
);
flag!(
doc = "If true, then the `mode_type`, `width`, `height` and `depth` fields are valid and
video information has to be passed.",
has_video_mode,
2
);
flag!(
doc = "If true, then the `header_addr`, `load_addr`, `load_end_addr`, `bss_end_addr`
and `entry_addr` fields are valid and must be used to load the kernel.",
has_multiboot_addresses,
16
);
pub fn get_addresses(&self) -> Option<MultibootAddresses> {
if self.has_multiboot_addresses() {
assert!(self.header.addresses.load_address <= self.header.addresses.header_address);
Some(self.header.addresses)
} else {
None
}
}
pub fn get_preferred_video_mode(&self) -> Option<MultibootVideoMode> {
if self.has_video_mode() {
Some(self.header.video_mode)
} else {
None
}
}
}
impl fmt::Debug for Header {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Header")
.field(
"wants_modules_page_aligned",
&self.wants_modules_page_aligned(),
)
.field("wants_memory_information", &self.wants_memory_information())
.field("addresses", &self.get_addresses())
.field("video_mode", &self.get_preferred_video_mode())
.finish()
}
}
#[derive(Debug, Copy, Clone)]
#[repr(C)]
pub struct MultibootAddresses {
pub header_address: u32,
pub load_address: u32,
pub load_end_address: u32,
pub bss_end_address: u32,
pub entry_address: u32,
}
impl MultibootAddresses {
pub fn compute_load_offset(&self, header_start: u32) -> u32 {
header_start - (self.header_address - self.load_address)
}
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct MultibootVideoMode {
mode_type: u32,
pub width: u32,
pub height: u32,
depth: u32,
}
impl MultibootVideoMode {
pub fn mode_type(&self) -> Option<VideoModeType> {
match self.mode_type {
0 => Some(VideoModeType::LinearGraphics),
1 => Some(VideoModeType::TextMode),
_ => None,
}
}
pub fn depth(&self) -> Option<u32> {
if self.mode_type() == Some(VideoModeType::LinearGraphics) {
Some(self.depth)
} else {
None
}
}
}
impl fmt::Debug for MultibootVideoMode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("MultibootVideoMode")
.field("mode_type", &self.mode_type())
.field("width", &self.width)
.field("height", &self.height)
.field("depth", &self.depth())
.finish()
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum VideoModeType {
LinearGraphics,
TextMode,
}