rustubs/machine/
multiboot.rs

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