Skip to content

Commit cc2972e

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 bf54c87 commit cc2972e

File tree

12 files changed

+107
-99
lines changed

12 files changed

+107
-99
lines changed

integration-test/bins/Cargo.lock

-16
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

+52-12
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,58 @@
11
use alloc::boxed::Box;
2+
use alloc::vec::Vec;
23
use elf_rs::{ElfFile, ProgramHeaderEntry, ProgramType};
4+
use log::{debug, info};
35
use multiboot2::{
4-
BootLoaderNameTag, CommandLineTag, MaybeDynSized, MemoryArea, MemoryAreaType, MemoryMapTag,
5-
ModuleTag, SmbiosTag,
6+
BootLoaderNameTag, CommandLineTag, EFIMemoryAreaType, MaybeDynSized, MemoryArea,
7+
MemoryAreaType, MemoryMapTag, ModuleTag, SmbiosTag,
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+
(_, 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!("No usable 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(ModuleTag::new(
56-
elf_mod.start as u32,
57-
elf_mod.end as u32,
58-
elf_mod.string.unwrap(),
96+
elf_mod.start_address(),
97+
elf_mod.end_address(),
98+
elf_mod.cmdline().unwrap(),
5999
))
60100
// Test that we can add SmbiosTag multiple times.
61101
.add_smbios(SmbiosTag::new(1, 1, &[1, 2, 3]))
@@ -66,7 +106,7 @@ pub fn load_module(mut modules: multiboot::information::ModuleIter) -> ! {
66106

67107
log::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/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();

integration-test/run.sh

+3-2
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,12 @@ function fn_build_rust_bins() {
4444
cargo build --release --verbose
4545
cd -
4646

47+
echo "Verifying multiboot2_chainloader ..."
4748
test -f $BINS_DIR/multiboot2_chainloader
4849
file --brief $BINS_DIR/multiboot2_chainloader | grep -q "ELF 32-bit LSB executable"
49-
# For simplicity, the chainloader itself boots via Multiboot 1. Sufficient.
50-
grub-file --is-x86-multiboot $BINS_DIR/multiboot2_chainloader
50+
grub-file --is-x86-multiboot2 $BINS_DIR/multiboot2_chainloader
5151

52+
echo "Verifying multiboot2_payload ..."
5253
test -f $BINS_DIR/multiboot2_payload
5354
file --brief $BINS_DIR/multiboot2_payload | grep -q "ELF 32-bit LSB executable"
5455
grub-file --is-x86-multiboot2 $BINS_DIR/multiboot2_payload

integration-test/tests/02-boot-loader-and-chainload/limine.cfg

+1-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@ VERBOSE=yes
44
INTERFACE_BRANDING=integration-test
55

66
:integration-test
7-
# For simplicity reasons, the loader itself boots via Multiboot 1. Sufficient.
8-
PROTOCOL=multiboot
7+
PROTOCOL=multiboot2
98
KERNEL_PATH=boot:///kernel
109
KERNEL_CMDLINE=some kernel cmdline
1110
MODULE_PATH=boot:///payload

0 commit comments

Comments
 (0)