Skip to content

Commit d0d4c26

Browse files
authored
Merge pull request #90 from mythril-hypervisor/multiboot
Add support for legacy multiboot
2 parents 456d2dd + 64d8535 commit d0d4c26

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
@@ -152,15 +156,26 @@ pub extern "C" fn ap_entry(_ap_data: &ap::ApData) -> ! {
152156
static LOGGER: logger::DirectLogger = logger::DirectLogger::new();
153157

154158
#[no_mangle]
155-
pub unsafe extern "C" fn kmain_multiboot2(multiboot_info_addr: usize) -> ! {
159+
pub unsafe extern "C" fn kmain_early(multiboot_info_addr: usize) -> ! {
156160
// Setup our (com0) logger
157161
log::set_logger(&LOGGER)
158162
.map(|()| log::set_max_level(log::LevelFilter::Debug))
159163
.expect("Failed to set logger");
160164

161-
let boot_info = multiboot2::early_init_multiboot2(
162-
memory::HostPhysAddr::new(multiboot_info_addr as u64),
163-
);
165+
let boot_info = if IS_MULTIBOOT_BOOT == 1 {
166+
debug!("Multiboot1 detected");
167+
multiboot::early_init_multiboot(memory::HostPhysAddr::new(
168+
multiboot_info_addr as u64,
169+
))
170+
} else if IS_MULTIBOOT2_BOOT == 1 {
171+
debug!("Multiboot2 detected");
172+
multiboot2::early_init_multiboot2(memory::HostPhysAddr::new(
173+
multiboot_info_addr as u64,
174+
))
175+
} else {
176+
panic!("Unknown boot method");
177+
};
178+
164179
kmain(boot_info)
165180
}
166181

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)