Skip to content

Commit 11534a4

Browse files
committed
integration-test: boot multiboot2_chainloader itself also via Multiboot2
Since I don't use QEMU Multiboot1-direct boot anymore, we can use Multiboot2 here as well. This also fights some problems with Limine which doesn't want to load the Multiboot1 kernel in an UEFI environment.
1 parent b2d0ee9 commit 11534a4

File tree

13 files changed

+119
-112
lines changed

13 files changed

+119
-112
lines changed

integration-test/bins/Cargo.lock

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

integration-test/bins/multiboot2_chainloader/Cargo.toml

-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ publish = false
1212
anyhow.workspace = true
1313
log.workspace = true
1414
good_memory_allocator.workspace = true
15-
multiboot = "0.8"
1615
multiboot2.workspace = true
1716
multiboot2-header.workspace = true
1817
util.workspace = true

integration-test/bins/multiboot2_chainloader/link.ld

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ PHDRS
1111

1212
SECTIONS {
1313
/* Chainloader linked at 8M, payload at 16M */
14-
.text 8M : AT(8M) ALIGN(4K)
14+
.text 12M : AT(12M) ALIGN(4K)
1515
{
16-
KEEP(*(.multiboot_header));
16+
KEEP(*(.multiboot2_header));
1717
*(.text .text.*)
1818
} : kernel_rx
1919

integration-test/bins/multiboot2_chainloader/src/loader.rs

+53-13
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,58 @@
1+
use alloc::vec::Vec;
12
use core::ops::Deref;
23
use elf_rs::{ElfFile, ProgramHeaderEntry, ProgramType};
4+
use log::{debug, info};
35
use multiboot2::{
4-
BootLoaderNameTag, CommandLineTag, MemoryArea, MemoryAreaType, MemoryMapTag, ModuleTag,
5-
SmbiosTag,
6+
BootLoaderNameTag, CommandLineTag, EFIMemoryAreaType, MemoryArea, MemoryAreaType, MemoryMapTag,
7+
ModuleTag, SmbiosTag, TagTrait,
68
};
79

10+
fn get_free_mmap_areas(
11+
mbi: &multiboot2::BootInformation,
12+
) -> Vec<(u64 /* start */, u64 /* size */)> {
13+
match (mbi.memory_map_tag(), mbi.efi_memory_map_tag()) {
14+
(Some(mmt), None) => mmt
15+
.memory_areas()
16+
.iter()
17+
.filter(|ma| ma.typ() == MemoryAreaType::Available)
18+
.map(|ma| (ma.start_address(), ma.size()))
19+
.collect::<alloc::vec::Vec<_>>(),
20+
(None, Some(mmt)) => mmt
21+
.memory_areas()
22+
.filter(|ma| ma.ty == EFIMemoryAreaType::CONVENTIONAL)
23+
.map(|ma| (ma.phys_start, ma.page_count * 4096))
24+
.collect::<alloc::vec::Vec<_>>(),
25+
_ => panic!("Invalid memory map"),
26+
}
27+
}
28+
29+
fn assert_load_segment_fits_into_memory(
30+
start: u64,
31+
size: u64,
32+
free_areas: &[(u64 /* start */, u64 /* size */)],
33+
) {
34+
let end = start + size;
35+
let range = free_areas
36+
.iter()
37+
.find(|(a_start, a_size)| start >= *a_start && end <= a_start + a_size);
38+
if let Some(range) = range {
39+
debug!("Can load load segment (0x{start:x?}, {size:x?}) into free memory area {range:#x?}");
40+
} else {
41+
panic!("Can't load load segment (0x{start:x?}, {size:x?}) into any area!");
42+
}
43+
}
44+
845
/// Loads the first module into memory. Assumes that the module is a ELF file.
946
/// The handoff is performed according to the Multiboot2 spec.
10-
pub fn load_module(mut modules: multiboot::information::ModuleIter) -> ! {
47+
pub fn load_module(mbi: &multiboot2::BootInformation) -> ! {
48+
let mut modules = mbi.module_tags();
49+
1150
// Load the ELF from the Multiboot1 boot module.
1251
let elf_mod = modules.next().expect("Should have payload");
1352
let elf_bytes = unsafe {
1453
core::slice::from_raw_parts(
15-
elf_mod.start as *const u64 as *const u8,
16-
(elf_mod.end - elf_mod.start) as usize,
54+
elf_mod.start_address() as *const u64 as *const u8,
55+
elf_mod.module_size() as usize,
1756
)
1857
};
1958
let elf = elf_rs::Elf32::from_bytes(elf_bytes).expect("Should be valid ELF");
@@ -28,10 +67,11 @@ pub fn load_module(mut modules: multiboot::information::ModuleIter) -> ! {
2867
log::info!("Multiboot2 header:\n{hdr:#?}");
2968
}
3069

31-
// Map the load segments into memory (at their corresponding link).
70+
// Load the load segments into memory (at their corresponding link address).
3271
{
33-
let elf = elf_rs::Elf32::from_bytes(elf_bytes).expect("Should be valid ELF");
72+
let free_areas = get_free_mmap_areas(mbi);
3473
elf.program_header_iter()
74+
.inspect(|ph| assert_load_segment_fits_into_memory(ph.vaddr(), ph.memsz(), &free_areas))
3575
.filter(|ph| ph.ph_type() == ProgramType::LOAD)
3676
.for_each(|ph| {
3777
map_memory(ph);
@@ -53,9 +93,9 @@ pub fn load_module(mut modules: multiboot::information::ModuleIter) -> ! {
5393
MemoryAreaType::Reserved,
5494
)]))
5595
.add_module_tag(ModuleTag::new(
56-
elf_mod.start as u32,
57-
elf_mod.end as u32,
58-
elf_mod.string.unwrap(),
96+
elf_mod.start_address() as u32,
97+
elf_mod.end_address() as u32,
98+
elf_mod.cmdline().unwrap(),
5999
))
60100
// Test that we can add SmbiosTag multiple times.
61101
.add_tag(SmbiosTag::new(1, 1, &[1, 2, 3]).deref())
@@ -64,9 +104,9 @@ pub fn load_module(mut modules: multiboot::information::ModuleIter) -> ! {
64104
.expect("should allow tag multiple times")
65105
.build();
66106

67-
log::info!(
107+
info!(
68108
"Handing over to ELF: {}",
69-
elf_mod.string.unwrap_or("<unknown>")
109+
elf_mod.cmdline().unwrap_or("<unknown>")
70110
);
71111

72112
// handoff
@@ -84,7 +124,7 @@ pub fn load_module(mut modules: multiboot::information::ModuleIter) -> ! {
84124
/// address space. The loader assumes that the addresses to not clash with the
85125
/// loader (or anything else).
86126
fn map_memory(ph: ProgramHeaderEntry) {
87-
log::debug!("Mapping LOAD segment {ph:#?}");
127+
debug!("Mapping LOAD segment {ph:#?}");
88128
let dest_ptr = ph.vaddr() as *mut u8;
89129
let content = ph.content().expect("Should have content");
90130
unsafe { core::ptr::copy(content.as_ptr(), dest_ptr, content.len()) };

integration-test/bins/multiboot2_chainloader/src/main.rs

+18-4
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
#![feature(error_in_core)]
44

55
mod loader;
6-
mod multiboot;
76

87
extern crate alloc;
98

@@ -12,6 +11,7 @@ extern crate util;
1211

1312
use util::init_environment;
1413

14+
core::arch::global_asm!(include_str!("multiboot2_header.S"), options(att_syntax));
1515
core::arch::global_asm!(include_str!("start.S"), options(att_syntax));
1616

1717
/// Entry into the Rust code from assembly using the x86 SystemV calling
@@ -20,7 +20,21 @@ core::arch::global_asm!(include_str!("start.S"), options(att_syntax));
2020
fn rust_entry(multiboot_magic: u32, multiboot_hdr: *const u32) -> ! {
2121
init_environment();
2222
log::debug!("multiboot_hdr={multiboot_hdr:x?}, multiboot_magic=0x{multiboot_magic:x?}");
23-
let mbi = multiboot::get_mbi(multiboot_magic, multiboot_hdr as u32).unwrap();
24-
let module_iter = mbi.modules().expect("Should provide modules");
25-
loader::load_module(module_iter);
23+
assert_eq!(multiboot_magic, multiboot2::MAGIC);
24+
let mbi = unsafe { multiboot2::BootInformation::load(multiboot_hdr.cast()) }.unwrap();
25+
26+
if let Some(mmap) = mbi.efi_memory_map_tag() {
27+
log::debug!("efi memory map:",);
28+
for desc in mmap.memory_areas() {
29+
log::warn!(
30+
" start=0x{:016x?} size={:016x?} type={:?}, attr={:?}",
31+
desc.phys_start,
32+
desc.page_count * 4096,
33+
desc.ty,
34+
desc.att
35+
);
36+
}
37+
}
38+
39+
loader::load_module(&mbi);
2640
}

integration-test/bins/multiboot2_chainloader/src/multiboot.rs

-41
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# The assembly code uses the GNU Assembly (GAS) flavor with Intel noprefix
2+
# syntax.
3+
4+
# Symbol from main.rs
5+
.EXTERN start
6+
7+
.code32
8+
.align 8
9+
.section .multiboot2_header
10+
11+
mb2_header_start:
12+
.long 0xe85250d6 # magic number
13+
.long 0 # architecture 0 (protected mode i386)
14+
.long mb2_header_end - mb2_header_start # header length
15+
# checksum
16+
.long 0x100000000 - (0xe85250d6 + 0 + (mb2_header_end - mb2_header_start))
17+
18+
# REQUIRED END TAG
19+
.align 8
20+
.Lmb2_header_tag_end_start:
21+
.word 0 # type (16bit)
22+
.word 0 # flags (16bit)
23+
.long .Lmb2_header_tag_end_end - .Lmb2_header_tag_end_start # size (32bit)
24+
.Lmb2_header_tag_end_end:
25+
mb2_header_end:

integration-test/bins/multiboot2_chainloader/src/start.S

-13
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,6 @@
33

44
.code32
55

6-
.section .multiboot_header, "a", @progbits
7-
8-
/*
9-
* Multiboot v1 Header.
10-
* Required so that we can be booted by QEMU via the "-kernel" parameter.
11-
*/
12-
.align 8
13-
.global multiboot_header
14-
multiboot_header:
15-
.long 0x1badb002
16-
.long 0x0
17-
.long -0x1badb002
18-
196
.section .text
207

218
.global start

integration-test/bins/multiboot2_payload/link.ld

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ PHDRS
1111

1212
SECTIONS {
1313
/* Chainloader linked at 8M, payload at 16M */
14-
.text 16M : AT(16M) ALIGN(4K)
14+
.text 24M : AT(24M) ALIGN(4K)
1515
{
16-
*(.multiboot2_header)
16+
KEEP(*(.multiboot2_header));
1717
*(.text .text.*)
1818
} : kernel_rx
1919

integration-test/bins/multiboot2_payload/src/multiboot2_header.S

-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
# Multiboot2 Header definition.
21
# The assembly code uses the GNU Assembly (GAS) flavor with Intel noprefix
32
# syntax.
43

integration-test/bins/util/src/allocator.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
use good_memory_allocator::SpinLockedAllocator;
22

3-
#[repr(align(0x4000))]
4-
struct Align16K<T>(T);
3+
#[repr(align(0x1000))]
4+
struct PageAlign<T>(T);
55

6-
/// 16 KiB naturally aligned backing storage for heap.
7-
static mut HEAP: Align16K<[u8; 0x4000]> = Align16K([0; 0x4000]);
6+
/// 16 KiB page-aligned backing storage for heap.
7+
static mut HEAP: PageAlign<[u8; 0x4000]> = PageAlign([0; 0x4000]);
88

99
#[global_allocator]
1010
static ALLOCATOR: SpinLockedAllocator = SpinLockedAllocator::empty();

0 commit comments

Comments
 (0)