rustubs/machine/multiboot.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
//! provide functions to parse information provided by grub multiboot
//! see docs/multiboot.txt
use crate::black_magic::flush;
use crate::defs::{mb_info_pm_addr, mb_magic};
use crate::P2V;
use core::fmt;
use core::ops::Range;
use lazy_static::lazy_static;
lazy_static! {
/// reference to the multiboot info blob provided by the bootloader. This
/// reference should be acquired via the get_mb_info() function, otherwise
/// MUST manually call the check() function before using.
///
/// note that the lazylazy_static is necessary here, because the
/// mb_info_pm_addr and mb_magic values are only initialized at runtime (in
/// startup code), hence we can't define mbinfo slice as constant during
/// compile or linking time. lalazy_static initialize the static constants
/// the first time they are accessed.
pub static ref MBOOTINFO: &'static MultibootInfo = unsafe {
&*(P2V(flush(&mb_info_pm_addr)).unwrap() as *const MultibootInfo)
};
}
pub fn get_mb_info() -> Option<&'static MultibootInfo> {
if !check() {
return None;
}
return Some(&MBOOTINFO);
}
/// this must be called before any MB info fields are used: the mb_magic should
/// be correctly set and all reserved bits in mbinfo flags should be 0.
pub fn check() -> bool {
if flush(&mb_magic) != 0x2BADB002 {
return false;
};
if P2V(flush(&mb_info_pm_addr)).is_none() {
return false;
}
// must check magic before checking flags
let f = MBOOTINFO.get_flags();
return f.check_valid();
}
#[repr(C)]
#[repr(packed)]
/// describes a a physical memory block.
pub struct MultibootMmap {
pub size: u32,
pub addr: u64,
pub len: u64,
pub mtype: u32,
}
/// defs of memory types in the multiboot's struct
impl MultibootMmap {
/// avaialble ram
pub const MTYPE_RAM: u32 = 1;
/// reserved ram
pub const MTYPE_RAM_RES: u32 = 2;
/// usable memory holding ACPI info
pub const MTYPE_ACPI: u32 = 3;
/// WHAT IS THIS 4???
pub const MTYPE_RAM_NVS: u32 = 4;
/// defective RAM
pub const MTYPE_RAM_DEFECT: u32 = 5;
pub fn get_range(&self) -> Range<u64> {
Range {
start: self.addr,
end: self.addr + self.len,
}
}
pub fn get_end(&self) -> u64 { return self.addr + self.len; }
}
#[repr(C)]
#[repr(packed)]
#[derive(Debug, Clone, Copy)]
/// present in MultibootInfo struct, if the corresponding flag is set. This describes the mmap
/// buffer (do not confuse with the mmap struct itself)
pub struct MultibootInfoMmap {
pub mmap_length: u32,
pub mmap_addr: u32,
}
/// example code to query physical memory maps: traverse the mmap structs
/// provided by bootloader.
pub fn _test_mmap() {
let mmapinfo = unsafe { MBOOTINFO.get_mmap() }.unwrap();
let buf_start = mmapinfo.mmap_addr;
let buf_len = mmapinfo.mmap_length;
let buf_end = buf_start + buf_len;
let mut curr = buf_start as u64;
loop {
if curr >= buf_end as u64 {
break;
}
let mblock = unsafe { &*(curr as *const MultibootMmap) };
curr += mblock.size as u64;
curr += 4; // mmap.size does not include the the size itself
println!("mem block {:#X?}", mblock);
}
}
#[repr(C)]
#[repr(packed)]
#[derive(Debug, Clone, Copy)]
/// describes amount of lower and upper memory. Lower memory starts from 0,
/// maximum 640 Kib. Upper memory starts from 1MiB, size maximum is addr of the
/// _first_ upper memory hole minus 1MiB (not guaranteed)
/// Both sizes have 1KiB unit.
pub struct MultibootInfoMem {
pub mem_lower: u32,
pub mem_upper: u32,
}
/// the packed members needs getter, because direct access with reference may be unaligned.
/// In this case the MultibootInfoMem members are aligned to 4 bytes (u32) which should be fine,
/// but the compiler doesn't agree ... pffff it needs to be smarter
impl MultibootInfoMem {
pub fn lower(&self) -> u32 { self.mem_lower }
pub fn upper(&self) -> u32 { self.mem_upper }
}
#[repr(C)]
#[repr(packed)]
#[derive(Debug)]
/// all fields MUST be acquired via unsafe getters, because the MB magic and reserved bits in flags
/// must be checked for validity before using. It does not suffice to check the corresponding
/// present bits in the getters.
/// Some fields are marked as padding because we don't need them (for now)
pub struct MultibootInfo {
flags: MultibootInfoFlags,
mem: MultibootInfoMem,
_pad1: [u8; 32],
mmap: MultibootInfoMmap,
_pad2: [u8; 68],
}
// TODO: expand MultibootInfo struct defs if needed.
impl MultibootInfo {
// private function. Don't use it outside the module.
fn get_flags(&self) -> MultibootInfoFlags { return self.flags; }
pub unsafe fn get_mem(&self) -> Option<MultibootInfoMem> {
if self.get_flags().contains(MultibootInfoFlags::MEM) {
return Some(self.mem);
} else {
return None;
}
}
pub unsafe fn get_mmap(&self) -> Option<MultibootInfoMmap> {
if self.get_flags().contains(MultibootInfoFlags::MMAP) {
return Some(self.mmap);
} else {
return None;
}
}
}
use bitflags::bitflags;
bitflags! {
/// the MultibootInfoFlags indicate which fields are valid in the MultibootInfo
/// atm we only need the MEM and MMAP flags for memory management info.
#[derive(Copy, Clone, Debug)]
pub struct MultibootInfoFlags: u32 {
const MEM = 1 << 0;
const BOOT_DEVICE = 1 << 1;
const CMDLINE = 1 << 2;
const MODS = 1 << 3;
const SYM_TBL = 1 << 4;
const SHDR = 1 << 5;
const MMAP = 1 << 6;
const DRIVES = 1 << 7;
const CONF_TBL = 1 << 8;
const BL_NAME = 1 << 9;
const APM_TBL = 1 << 10;
const VBE_TBL = 1 << 11;
const FRAMEBUFFER = 1 << 12;
}
}
impl MultibootInfoFlags {
/// only 13 bits of the MB info flags are defined. The other flag bits must
/// be zero for the info block to be valid.
const VALID_MASK: u32 = 0x1FFF;
pub fn check_valid(&self) -> bool {
return self.bits() <= Self::VALID_MASK;
}
}
impl fmt::Debug for MultibootMmap {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let addr = self.addr;
let len = self.len;
let mtype = self.mtype;
write!(
f,
"[{}] @ {:#X} + {:#X}",
match mtype {
MultibootMmap::MTYPE_RAM => "GOOD",
MultibootMmap::MTYPE_RAM_RES => "RESV",
MultibootMmap::MTYPE_ACPI => "ACPI",
MultibootMmap::MTYPE_RAM_NVS => "NVS ",
MultibootMmap::MTYPE_RAM_DEFECT => "BAD ",
_ => "UNKN",
},
addr,
len,
)
}
}