I'm developing an OS in Rust, I'm still relatively new to it. I have a custom UEFI bootloader that boots my WIP kernel, they both use my own custom boot protocol.
Details hidden; out of date, see discussion below
The kernel entry point signature is as follows:
type EntryPoint = fn (&'static boot_proto::BootData) -> !;
BootData is defined as the following:
pub const CURRENT_REVISION: u64 = 0x1;
#[derive(Debug)]
pub struct BootData {
pub revision: u64,
pub tags: &'static [tags::TagType],
}
impl BootData {
pub fn new(tags: &'static [tags::TagType]) -> Self {
Self {
revision: CURRENT_REVISION,
tags,
}
}
}
Take into consideration the following example kernel code:
static SERIAL: Mutex<SerialWriter> = Mutex::new(SerialWriter::new(0x3F8));
#[no_mangle]
fn kernel_main(boot_data: &'static boot_proto::BootData) -> ! {
let mut serial = SERIAL.lock();
writeln!(serial, "Bootloader data: {:#X?}", boot_data).unwrap();
assert_eq!(explosion.revision, boot_proto::CURRENT_REVISION);
loop {
unsafe { asm!("hlt") };
}
}
A portion of the boot loader code is the following:
// [..]
let mut explosion = Box::new(boot_proto::BootData::new(Default::default()));
let mut tags = Vec::with_capacity(3);
// A bunch of initialization here, then an exit boot services call
explosion.tags = tags.leak();
unsafe {
asm!("cli");
core::mem::transmute::<_, fn(&'static kaboom::ExplosionResult) -> !>(
elf.header().entry_point() as *const (),
)(Box::leak(explosion));
};
Now that you know the behaviour of both ends, let's inspect the serial output:
Bootloader data: BootData {
revision: 0x6972646C,
tags: [
ACPI(
0x0000000007ed1f80,
),
ACPI(
0xafafafafafafafaf,
),
ACPI(
0x0000000000010001,
),
ACPI(
0x82e3000010202020,
),
ACPI(
0xafafafafafafafaf,
),
ACPI(
0xafafafafafafafaf,
),
ACPI(
0xafafafaf6c617470,
),
ACPI(
0xafafafafafafafaf,
),
ACPI(
0xafafafafafafafaf,
),
ACPI(
0xafafafafafafafaf,
),
],
}
That doesn't look good, but why is this happening?
The boot loader clearly is passing the correct data.