Skip to content

Commit 64d8535

Browse files
committed
Add support for legacy multiboot
Unfortunately, we can't use this with direct grub boot as that only supports 32bit multiboot images. Still, there may be some value in using this for network booting, etc.
1 parent fbc8b1f commit 64d8535

10 files changed

+201
-12
lines changed

mythril/Cargo.lock

+7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

mythril/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ num_enum = { version = "0.5.0", default-features = false }
1919
x86 = "0.34.0"
2020
linked_list_allocator = "0.8.1"
2121
log = { version = "0.4.8", default-features = false }
22+
multiboot = "0.3.0"
2223
multiboot2 = "0.9.0"
2324
raw-cpuid = "8.1.1"
2425
rlibc = "1.0.0"

mythril/build.rs

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ fn main() {
1212
build
1313
.file("src/vm.S")
1414
.file("src/boot.S")
15+
.file("src/multiboot_header.S")
1516
.file("src/multiboot2_header.S")
1617
.file("src/ap_startup.S")
1718
.include("asm_include/")

mythril/linker.ld

+8-6
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,19 @@ ENTRY(_start)
33
SECTIONS {
44
. = 0x8000;
55

6+
.boot :
7+
{
8+
/* ensure that the multiboot header is at the beginning */
9+
KEEP(*(.multiboot_header))
10+
}
11+
612
.ap_startup ALIGN(4K) : ALIGN(4K)
713
{
814
KEEP(*(.ap_startup .ap_startup.*))
915
}
1016

1117
. = 1M;
1218

13-
.boot ALIGN(4K) : ALIGN(4K)
14-
{
15-
/* ensure that the multiboot header is at the beginning */
16-
KEEP(*(.multiboot_header))
17-
}
18-
1919
PER_CORE_START = .;
2020
.per_core :
2121
{
@@ -47,4 +47,6 @@ SECTIONS {
4747
*(.bss .bss.*)
4848
*(COMMON)
4949
}
50+
51+
END_OF_BINARY = .;
5052
}

mythril/src/boot.S

+21-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ PAGE_HIERARCHY:
99
align PAGE_SIZE
1010
resb PAGE_HIERARCHY_SIZE
1111

12-
extern kmain_multiboot2
12+
extern kmain_early
1313

1414
; The stack used before launching the guests. After that, the
1515
; stack will be the one set up in the VMCS
@@ -66,6 +66,14 @@ global GDT64_DATA
6666
GDT64_DATA:
6767
dq GDT64.data
6868

69+
global IS_MULTIBOOT_BOOT
70+
IS_MULTIBOOT_BOOT:
71+
db 0
72+
73+
global IS_MULTIBOOT2_BOOT
74+
IS_MULTIBOOT2_BOOT:
75+
db 0
76+
6977
[BITS 32]
7078

7179
section .text.map_page_directory
@@ -94,6 +102,17 @@ _start:
94102

95103
push ebx
96104

105+
106+
cmp eax, 0x36d76289 ; Multiboot2 indicator
107+
je .multiboot2_boot_type
108+
mov byte [IS_MULTIBOOT_BOOT], 1
109+
jmp .post_boot_type
110+
111+
.multiboot2_boot_type:
112+
mov byte [IS_MULTIBOOT2_BOOT], 1
113+
114+
.post_boot_type:
115+
97116
; Zero out the buffer.
98117
; Since we are doing a rep stosd, count should be bytes/4.
99118
push edi ; REP STOSD alters DI.
@@ -215,4 +234,4 @@ trampoline:
215234
mov edx, 0
216235
mov fs, edx
217236

218-
jmp kmain_multiboot2
237+
jmp kmain_early

mythril/src/kmain.rs

+19-4
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use crate::ioapic;
77
use crate::linux;
88
use crate::logger;
99
use crate::memory;
10+
use crate::multiboot;
1011
use crate::multiboot2;
1112
use crate::percore;
1213
use crate::physdev;
@@ -25,6 +26,9 @@ extern "C" {
2526
static mut AP_STACK_ADDR: u64;
2627
static mut AP_IDX: u64;
2728
static mut AP_READY: u8;
29+
30+
static IS_MULTIBOOT_BOOT: u8;
31+
static IS_MULTIBOOT2_BOOT: u8;
2832
}
2933

3034
// Temporary helper function to create a vm for a single core
@@ -158,15 +162,26 @@ pub extern "C" fn ap_entry(_ap_data: &ap::ApData) -> ! {
158162
static LOGGER: logger::DirectLogger = logger::DirectLogger::new();
159163

160164
#[no_mangle]
161-
pub unsafe extern "C" fn kmain_multiboot2(multiboot_info_addr: usize) -> ! {
165+
pub unsafe extern "C" fn kmain_early(multiboot_info_addr: usize) -> ! {
162166
// Setup our (com0) logger
163167
log::set_logger(&LOGGER)
164168
.map(|()| log::set_max_level(log::LevelFilter::Debug))
165169
.expect("Failed to set logger");
166170

167-
let boot_info = multiboot2::early_init_multiboot2(
168-
memory::HostPhysAddr::new(multiboot_info_addr as u64),
169-
);
171+
let boot_info = if IS_MULTIBOOT_BOOT == 1 {
172+
debug!("Multiboot1 detected");
173+
multiboot::early_init_multiboot(memory::HostPhysAddr::new(
174+
multiboot_info_addr as u64,
175+
))
176+
} else if IS_MULTIBOOT2_BOOT == 1 {
177+
debug!("Multiboot2 detected");
178+
multiboot2::early_init_multiboot2(memory::HostPhysAddr::new(
179+
multiboot_info_addr as u64,
180+
))
181+
} else {
182+
panic!("Unknown boot method");
183+
};
184+
170185
kmain(boot_info)
171186
}
172187

mythril/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ pub mod linux;
3939
pub mod lock;
4040
pub mod logger;
4141
pub mod memory;
42+
pub mod multiboot;
4243
pub mod multiboot2;
4344
pub mod percore;
4445
pub mod physdev;

mythril/src/multiboot.rs

+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
use crate::boot_info::{self, BootInfo};
2+
use crate::global_alloc;
3+
use crate::memory::HostPhysAddr;
4+
use alloc::vec::Vec;
5+
6+
extern "C" {
7+
pub static MULTIBOOT_HEADER_START: u32;
8+
pub static MULTIBOOT_HEADER_END: u32;
9+
10+
// The _value_ of the last byte of the mythril binary. The
11+
// address of this symbol is the actual end.
12+
pub static END_OF_BINARY: u8;
13+
}
14+
15+
// NOTE: see multiboot2::header_location for more information
16+
pub fn header_location() -> (u32, u32) {
17+
unsafe { (MULTIBOOT_HEADER_START, MULTIBOOT_HEADER_END) }
18+
}
19+
20+
fn setup_global_alloc_region<'a, F>(
21+
info: &'a multiboot::Multiboot<'a, F>,
22+
) -> (u64, u64)
23+
where
24+
F: Fn(u64, usize) -> Option<&'a [u8]>,
25+
{
26+
let regions = info
27+
.memory_regions()
28+
.expect("Missing multiboot memory regions");
29+
30+
let available = regions.filter_map(|region| match region.memory_type() {
31+
multiboot::MemoryType::Available => Some((
32+
region.base_address(),
33+
region.base_address() + region.length(),
34+
)),
35+
_ => None,
36+
});
37+
38+
debug!("Modules:");
39+
let modules =
40+
info.modules()
41+
.expect("No multiboot modules found")
42+
.map(|module| {
43+
debug!(" 0x{:x}-0x{:x}", module.start, module.end);
44+
(module.start, module.end)
45+
});
46+
47+
// Avoid allocating over the actual mythril binary (just use 0 as the start
48+
// for now).
49+
let mythril_bounds =
50+
[(0 as u64, unsafe { &END_OF_BINARY as *const u8 as u64 })];
51+
debug!(
52+
"Mythril binary bounds: 0x{:x}-0x{:x}",
53+
mythril_bounds[0].0, mythril_bounds[0].1
54+
);
55+
56+
let excluded = modules.chain(mythril_bounds.iter().copied());
57+
58+
// TODO(alschwalm): For now, we just use the portion of the largest available
59+
// region that is above the highest excluded region.
60+
let max_excluded = excluded
61+
.max_by(|left, right| left.1.cmp(&right.1))
62+
.expect("No max excluded region");
63+
64+
let largest_region = available
65+
.max_by(|left, right| (left.1 - left.0).cmp(&(right.1 - right.0)))
66+
.expect("No largest region");
67+
68+
if largest_region.0 > max_excluded.1 {
69+
largest_region
70+
} else if max_excluded.1 > largest_region.0
71+
&& max_excluded.1 < largest_region.1
72+
{
73+
(max_excluded.1, largest_region.1)
74+
} else {
75+
panic!("Unable to find suitable global alloc region")
76+
}
77+
}
78+
79+
pub fn early_init_multiboot(addr: HostPhysAddr) -> BootInfo {
80+
fn multiboot_addr_translate<'a>(
81+
paddr: u64,
82+
size: usize,
83+
) -> Option<&'a [u8]> {
84+
unsafe { Some(core::slice::from_raw_parts(paddr as *const u8, size)) }
85+
}
86+
let multiboot_info = unsafe {
87+
multiboot::Multiboot::new(addr.as_u64(), multiboot_addr_translate)
88+
.expect("Failed to create Multiboot structure")
89+
};
90+
91+
let alloc_region = setup_global_alloc_region(&multiboot_info);
92+
93+
info!(
94+
"Allocating from 0x{:x}-{:x}",
95+
alloc_region.0, alloc_region.1
96+
);
97+
98+
unsafe {
99+
global_alloc::Allocator::allocate_from(alloc_region.0, alloc_region.1);
100+
}
101+
102+
let modules = multiboot_info
103+
.modules()
104+
.expect("No multiboot modules found")
105+
.map(|module| boot_info::BootModule {
106+
address: HostPhysAddr::new(module.start),
107+
size: (module.end - module.start) as usize,
108+
identifier: module.string.map(alloc::string::String::from),
109+
})
110+
.collect::<Vec<_>>();
111+
112+
BootInfo {
113+
modules: modules,
114+
rsdp: None,
115+
}
116+
}

mythril/src/multiboot2_header.S

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ DEFAULT REL
44

55
section .multiboot_header
66
header_start:
7+
align 8
78
dd 0xe85250d6 ; magic number (multiboot 2)
89
dd 0 ; architecture 0 (protected mode i386)
910
dd header_end - header_start ; header length

mythril/src/multiboot_header.S

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
DEFAULT REL
2+
3+
[BITS 32]
4+
5+
section .multiboot_header
6+
multiboot_header_start:
7+
align 4
8+
dd 0x1BADB002 ; magic number (multiboot 1)
9+
dd 0 ; flags
10+
; checksum
11+
dd -(0x1BADB002 + 0)
12+
dd 0
13+
dd 0
14+
dd 0
15+
dd 0
16+
dd 0
17+
18+
multiboot_header_end:
19+
20+
global MULTIBOOT_HEADER_START
21+
MULTIBOOT_HEADER_START:
22+
dd multiboot_header_start
23+
24+
global MULTIBOOT_HEADER_END
25+
MULTIBOOT_HEADER_END:
26+
dd multiboot_header_end

0 commit comments

Comments
 (0)