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,
		)
	}
}