use core::cmp;
use core::convert::TryInto;
use core::fmt;
use core::fmt::Debug;
use core::mem::{size_of, transmute};
use core::slice;
use core::str;
pub const SIGNATURE_EAX: u32 = 0x2BADB002;
pub type PAddr = u64;
pub trait MemoryManagement {
unsafe fn paddr_to_slice(&self, addr: PAddr, length: usize) -> Option<&'static [u8]>;
unsafe fn allocate(&mut self, length: usize) -> Option<(PAddr, &mut [u8])>;
unsafe fn deallocate(&mut self, addr: PAddr);
}
pub struct Multiboot<'a, 'b> {
header: &'a mut MultibootInfo,
memory_management: &'b mut dyn MemoryManagement,
}
#[repr(C)]
#[derive(Default)]
pub struct MultibootInfo {
flags: u32,
mem_lower: u32,
mem_upper: u32,
boot_device: BootDevice,
cmdline: u32,
mods_count: u32,
mods_addr: u32,
symbols: Symbols,
mmap_length: u32,
mmap_addr: u32,
drives_length: u32,
drives_addr: u32,
_config_table: u32,
boot_loader_name: u32,
_apm_table: u32,
_vbe_control_info: u32,
_vbe_mode_info: u32,
_vbe_mode: u16,
_vbe_interface_seg: u16,
_vbe_interface_off: u16,
_vbe_interface_len: u16,
framebuffer_table: FramebufferTable,
}
impl<'a, 'b> Multiboot<'a, 'b> {
pub unsafe fn from_ptr(
mboot_ptr: PAddr,
memory_management: &'b mut dyn MemoryManagement,
) -> Option<Multiboot<'a, 'b>> {
memory_management
.paddr_to_slice(mboot_ptr, size_of::<MultibootInfo>())
.map(move |inner| {
let info = &mut *(inner.as_ptr() as *mut MultibootInfo);
Multiboot {
header: info,
memory_management,
}
})
}
pub fn from_ref(
info: &'a mut MultibootInfo,
memory_management: &'b mut dyn MemoryManagement,
) -> Self {
Self {
header: info,
memory_management,
}
}
unsafe fn cast<T>(&self, addr: PAddr) -> Option<&T> {
self.memory_management
.paddr_to_slice(addr, size_of::<T>())
.map(|inner| &*(inner.as_ptr() as *const T))
}
unsafe fn convert_c_string(&self, string: PAddr) -> Option<&'a str> {
if string == 0 {
return None;
}
let mut len = 0;
let mut ptr = string;
while let Some(byte) = self.memory_management.paddr_to_slice(ptr, 1) {
if byte == [0] {
break;
}
ptr += 1;
len += 1;
}
self.memory_management
.paddr_to_slice(string, len)
.map(|slice| str::from_utf8_unchecked(slice))
}
unsafe fn convert_to_c_string(&mut self, string: Option<&str>) -> u32 {
match string {
Some(s) => {
let bytes = s.bytes();
let len = bytes.len();
let (addr, slice) = self.memory_management.allocate(len + 1).unwrap();
for (src, dst) in bytes.chain(core::iter::once(0)).zip(slice.iter_mut()) {
*dst = src;
}
addr.try_into().unwrap()
}
None => 0,
}
}
flag!(
doc = "If true, then the `mem_upper` and `mem_lower` fields are valid.",
has_memory_bounds,
0
);
flag!(
doc = "If true, then the `boot_device` field is valid.",
has_boot_device,
1
);
flag!(
doc = "If true, then the `cmdline` field is valid.",
has_cmdline,
2
);
flag!(
doc = "If true, then the `mods_addr` and `mods_count` fields are valid.",
has_modules,
3
);
flag!(
doc = "If true, then the `syms` field is valid and contains AOut symbols.",
has_aout_symbols,
4
);
flag!(
doc = "If true, then the `syms` field is valid and containts ELF symbols.",
has_elf_symbols,
5
);
flag!(
doc = "If true, then the `mmap_addr` and `mmap_length` fields are valid.",
has_memory_map,
6
);
flag!(
doc = "If true, then the `drives_addr` and `drives_length` fields are valid.",
has_drives,
7
);
flag!(
doc = "If true, then the `config_table` field is valid.",
has_config_table,
8
);
flag!(
doc = "If true, then the `boot_loader_name` field is valid.",
has_boot_loader_name,
9
);
flag!(
doc = "If true, then the `apm_table` field is valid.",
has_apm_table,
10
);
flag!(
doc = "If true, then the `vbe_*` fields are valid.",
has_vbe,
11
);
flag!(
doc = "If true, then the framebuffer table is valid.",
has_framebuffer_table,
12
);
pub fn lower_memory_bound(&self) -> Option<u32> {
match self.has_memory_bounds() {
true => Some(self.header.mem_lower),
false => None,
}
}
pub fn upper_memory_bound(&self) -> Option<u32> {
match self.has_memory_bounds() {
true => Some(self.header.mem_upper),
false => None,
}
}
pub fn set_memory_bounds(&mut self, bounds: Option<(u32, u32)>) {
self.set_has_memory_bounds(bounds.is_some());
if let Some((lower, upper)) = bounds {
self.header.mem_lower = lower;
self.header.mem_upper = upper;
}
}
pub fn boot_device(&self) -> Option<BootDevice> {
match self.has_boot_device() {
true => Some(self.header.boot_device.clone()),
false => None,
}
}
pub fn command_line(&self) -> Option<&'a str> {
if self.has_cmdline() {
unsafe { self.convert_c_string(self.header.cmdline as PAddr) }
} else {
None
}
}
pub fn set_command_line(&mut self, cmdline: Option<&str>) {
if self.has_cmdline() {
unsafe {
self.memory_management
.deallocate(self.header.cmdline.into())
};
}
self.set_has_cmdline(cmdline.is_some());
self.header.cmdline = unsafe { self.convert_to_c_string(cmdline) };
}
pub fn boot_loader_name(&self) -> Option<&'a str> {
if self.has_boot_loader_name() {
unsafe { self.convert_c_string(self.header.boot_loader_name as PAddr) }
} else {
None
}
}
pub fn set_boot_loader_name(&mut self, name: Option<&str>) {
if self.has_boot_loader_name() {
unsafe {
self.memory_management
.deallocate(self.header.boot_loader_name.into())
};
}
self.set_has_boot_loader_name(name.is_some());
self.header.boot_loader_name = unsafe { self.convert_to_c_string(name) };
}
pub fn modules(&'a self) -> Option<ModuleIter<'a, 'b>> {
if self.has_modules() {
unsafe {
self.memory_management
.paddr_to_slice(
self.header.mods_addr as PAddr,
self.header.mods_count as usize * size_of::<MBModule>(),
)
.map(|slice| {
let ptr = transmute(slice.as_ptr());
let mods = slice::from_raw_parts(ptr, self.header.mods_count as usize);
ModuleIter { mb: self, mods }
})
}
} else {
None
}
}
pub fn set_modules(&mut self, modules: Option<&[Module]>) {
if self.has_modules() {
unsafe {
if let Some(mods) = self.memory_management.paddr_to_slice(
self.header.mods_addr.into(),
self.header.mods_count as usize * core::mem::size_of::<MBModule>(),
) {
let mods = slice::from_raw_parts(
mods.as_ptr().cast::<MBModule>(),
self.header.mods_count as usize,
);
for module in mods {
self.memory_management.deallocate(module.string.into());
}
self.memory_management
.deallocate(self.header.mods_addr.into());
}
}
}
self.set_has_modules(modules.is_some());
if let Some(mods) = modules {
let len = mods.len();
self.header.mods_count = mods.len().try_into().unwrap();
self.header.mods_addr = unsafe {
let (addr, slice) = self
.memory_management
.allocate(len * core::mem::size_of::<MBModule>())
.unwrap();
let slice = slice::from_raw_parts_mut(slice.as_mut_ptr().cast::<MBModule>(), len);
for (src, dst) in mods.iter().zip(slice.iter_mut()) {
*dst = MBModule {
start: src.start.try_into().unwrap(),
end: src.end.try_into().unwrap(),
string: self.convert_to_c_string(src.string),
reserved: 0,
}
}
addr.try_into().unwrap()
};
}
}
pub fn symbols(&self) -> Option<SymbolType> {
if self.has_elf_symbols() & self.has_aout_symbols() {
return None;
}
if self.has_elf_symbols() {
return Some(SymbolType::Elf(unsafe { self.header.symbols.elf }));
}
if self.has_aout_symbols() {
return Some(SymbolType::AOut(unsafe { self.header.symbols.aout }));
}
None
}
pub fn set_symbols(&mut self, symbols: Option<SymbolType>) {
match symbols {
None => {
self.set_has_aout_symbols(false);
self.set_has_elf_symbols(false);
}
Some(SymbolType::AOut(a)) => {
self.set_has_aout_symbols(true);
self.set_has_elf_symbols(false);
self.header.symbols.aout = a;
}
Some(SymbolType::Elf(e)) => {
self.set_has_aout_symbols(false);
self.set_has_elf_symbols(true);
self.header.symbols.elf = e;
}
}
}
pub fn memory_regions(&'a self) -> Option<MemoryMapIter<'a, 'b>> {
match self.has_memory_map() {
true => {
let start = self.header.mmap_addr;
let end = self.header.mmap_addr + self.header.mmap_length;
Some(MemoryMapIter {
current: start,
end,
mb: self,
})
}
false => None,
}
}
pub fn set_memory_regions(&mut self, regions: Option<(PAddr, usize)>) {
self.set_has_memory_map(regions.is_some());
if let Some((addr, count)) = regions {
self.header.mmap_addr = addr.try_into().unwrap();
self.header.mmap_length = (count * core::mem::size_of::<MemoryEntry>())
.try_into()
.unwrap();
}
}
pub fn find_highest_address(&self) -> PAddr {
let end = cmp::max(
self.header.cmdline as u64 + self.command_line().map_or(0, |f| f.len()) as u64,
self.header.boot_loader_name as u64
+ self.boot_loader_name().map_or(0, |f| f.len()) as u64,
)
.max(match self.symbols() {
Some(SymbolType::Elf(e)) => (e.addr + e.num * e.size) as u64,
Some(SymbolType::AOut(a)) => {
(a.addr + a.tabsize + a.strsize + 2 * core::mem::size_of::<u32>() as u32) as u64
}
None => 0,
})
.max((self.header.mmap_addr + self.header.mmap_length) as u64)
.max((self.header.drives_addr + self.header.drives_length) as u64)
.max(
self.header.mods_addr as u64
+ self.header.mods_count as u64 * core::mem::size_of::<MBModule>() as u64,
)
.max(
self.modules()
.into_iter()
.flatten()
.map(|m| m.end)
.max()
.unwrap_or(0),
);
round_up!(end, 4096)
}
pub fn framebuffer_table(&self) -> Option<&FramebufferTable> {
if self.has_framebuffer_table() {
Some(&self.header.framebuffer_table)
} else {
None
}
}
pub fn set_framebuffer_table(&mut self, table: Option<FramebufferTable>) {
self.set_has_framebuffer_table(table.is_some());
self.header.framebuffer_table = match table {
Some(t) => t,
None => FramebufferTable::default(),
};
}
}
#[derive(Debug, Clone)]
#[repr(C)]
pub struct BootDevice {
pub drive: u8,
pub partition1: u8,
pub partition2: u8,
pub partition3: u8,
}
impl BootDevice {
pub fn partition1_is_valid(&self) -> bool {
self.partition1 != 0xff
}
pub fn partition2_is_valid(&self) -> bool {
self.partition2 != 0xff
}
pub fn partition3_is_valid(&self) -> bool {
self.partition3 != 0xff
}
}
impl Default for BootDevice {
fn default() -> Self {
Self {
drive: 0xff,
partition1: 0xff,
partition2: 0xff,
partition3: 0xff,
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum MemoryType {
Available = 1,
Reserved = 2,
ACPI = 3,
NVS = 4,
Defect = 5,
}
#[derive(Clone, Copy)]
#[repr(C, packed)]
pub struct MemoryEntry {
size: u32,
base_addr: u64,
length: u64,
mtype: u32,
}
impl Debug for MemoryEntry {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let size = self.size;
let base_addr = self.base_addr;
let length = self.length;
let mtype = self.mtype;
write!(
f,
"MemoryEntry {{ size: {}, base_addr: {}, length: {}, mtype: {} }}",
size, base_addr, length, mtype
)
}
}
impl Default for MemoryEntry {
fn default() -> Self {
Self::new(0, 0, MemoryType::Reserved)
}
}
impl MemoryEntry {
pub fn new(base_addr: PAddr, length: PAddr, ty: MemoryType) -> Self {
let size = (core::mem::size_of::<MemoryEntry>() - core::mem::size_of::<u32>())
.try_into()
.unwrap();
assert_eq!(size, 20);
Self {
size,
base_addr,
length,
mtype: ty as u32,
}
}
pub fn base_address(&self) -> PAddr {
self.base_addr as PAddr
}
pub fn length(&self) -> u64 {
self.length
}
pub fn memory_type(&self) -> MemoryType {
match self.mtype {
1 => MemoryType::Available,
3 => MemoryType::ACPI,
4 => MemoryType::NVS,
5 => MemoryType::Defect,
_ => MemoryType::Reserved,
}
}
}
pub struct MemoryMapIter<'a, 'b> {
mb: &'a Multiboot<'a, 'b>,
current: u32,
end: u32,
}
impl<'a, 'b> Iterator for MemoryMapIter<'a, 'b> {
type Item = &'a MemoryEntry;
#[inline]
fn next(&mut self) -> Option<&'a MemoryEntry> {
if self.current < self.end {
unsafe {
self.mb
.cast(self.current as PAddr)
.map(|region: &'a MemoryEntry| {
self.current += region.size + 4;
region
})
}
} else {
None
}
}
}
#[repr(C)]
struct MBModule {
start: u32,
end: u32,
string: u32,
reserved: u32,
}
impl Debug for MBModule {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"MBModule {{ start: {}, end: {}, string: {}, reserved: {} }}",
self.start, self.end, self.string, self.reserved
)
}
}
#[derive(Debug)]
pub struct Module<'a> {
pub start: PAddr,
pub end: PAddr,
pub string: Option<&'a str>,
}
impl<'a> Module<'a> {
pub fn new(start: PAddr, end: PAddr, name: Option<&'a str>) -> Module {
Module {
start,
end,
string: name,
}
}
}
pub struct ModuleIter<'a, 'b> {
mb: &'a Multiboot<'a, 'b>,
mods: &'a [MBModule],
}
impl<'a, 'b> Iterator for ModuleIter<'a, 'b> {
type Item = Module<'a>;
#[inline]
fn next(&mut self) -> Option<Module<'a>> {
self.mods.split_first().map(|(first, rest)| {
self.mods = rest;
unsafe {
Module::new(
first.start as PAddr,
first.end as PAddr,
self.mb.convert_c_string(first.string as PAddr),
)
}
})
}
}
#[repr(C)]
union Symbols {
aout: AOutSymbols,
elf: ElfSymbols,
_bindgen_union_align: [u32; 4usize],
}
#[derive(Debug, Copy, Clone)]
pub enum SymbolType {
AOut(AOutSymbols),
Elf(ElfSymbols),
}
impl Default for Symbols {
fn default() -> Self {
Self {
elf: ElfSymbols::default(),
}
}
}
#[repr(C)]
#[derive(Default, Copy, Clone)]
pub struct AOutSymbols {
tabsize: u32,
strsize: u32,
addr: u32,
reserved: u32,
}
impl Debug for AOutSymbols {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"AOutSymbols {{ tabsize: {}, strsize: {}, addr: {} }}",
self.tabsize, self.strsize, self.addr
)
}
}
#[repr(C)]
#[derive(Default, Copy, Clone)]
pub struct ElfSymbols {
num: u32,
size: u32,
addr: u32,
shndx: u32,
}
impl Debug for ElfSymbols {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"ElfSymbols {{ num: {}, size: {}, addr: {}, shndx: {} }}",
self.num, self.size, self.addr, self.shndx
)
}
}
impl ElfSymbols {
pub fn from_addr(num: u32, size: u32, addr: PAddr, shndx: u32) -> Self {
Self {
num,
size,
shndx,
addr: addr.try_into().unwrap(),
}
}
}
#[repr(C)]
#[derive(Default)]
pub struct FramebufferTable {
pub addr: u64,
pub pitch: u32,
pub width: u32,
pub height: u32,
pub bpp: u8,
ty: u8,
color_info: ColorInfo,
}
impl fmt::Debug for FramebufferTable {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("FramebufferTable")
.field("addr", &self.addr)
.field("pitch", &self.pitch)
.field("width", &self.width)
.field("height", &self.height)
.field("bpp", &self.bpp)
.field("color_info", &self.color_info())
.finish()
}
}
impl FramebufferTable {
pub fn new(
addr: u64,
pitch: u32,
width: u32,
height: u32,
bpp: u8,
color_info_type: ColorInfoType,
) -> Self {
let (ty, color_info) = match color_info_type {
ColorInfoType::Palette(palette) => (0, ColorInfo { palette }),
ColorInfoType::Rgb(rgb) => (1, ColorInfo { rgb }),
ColorInfoType::Text => (2, ColorInfo::default()),
};
Self {
addr,
pitch,
width,
height,
bpp,
ty,
color_info,
}
}
pub fn color_info(&self) -> Option<ColorInfoType> {
unsafe {
match self.ty {
0 => Some(ColorInfoType::Palette(self.color_info.palette)),
1 => Some(ColorInfoType::Rgb(self.color_info.rgb)),
2 => Some(ColorInfoType::Text),
_ => None,
}
}
}
}
#[derive(Debug)]
pub enum ColorInfoType {
Palette(ColorInfoPalette),
Rgb(ColorInfoRgb),
Text,
}
#[repr(C)]
union ColorInfo {
palette: ColorInfoPalette,
rgb: ColorInfoRgb,
_union_align: [u32; 2usize],
}
impl Default for ColorInfo {
fn default() -> Self {
Self {
palette: ColorInfoPalette {
palette_addr: 0,
palette_num_colors: 0,
},
}
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct ColorInfoPalette {
palette_addr: u32,
palette_num_colors: u16,
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct ColorInfoRgb {
pub red_field_position: u8,
pub red_mask_size: u8,
pub green_field_position: u8,
pub green_mask_size: u8,
pub blue_field_position: u8,
pub blue_mask_size: u8,
}