diff --git a/Cargo.lock b/Cargo.lock index ebd038e9..1b0f5566 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -90,6 +90,7 @@ dependencies = [ "test_kernel_min_stack", "test_kernel_pie", "test_kernel_ramdisk", + "test_kernel_stack_address", "test_kernel_write_usable_memory", ] @@ -810,6 +811,15 @@ dependencies = [ "x86_64 0.15.2", ] +[[package]] +name = "test_kernel_stack_address" +version = "0.1.0" +dependencies = [ + "bootloader_api", + "uart_16550 0.2.18", + "x86_64 0.15.2", +] + [[package]] name = "test_kernel_write_usable_memory" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 1609a6d1..440566e6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,6 +61,7 @@ test_kernel_min_stack = { path = "tests/test_kernels/min_stack", artifact = "bin test_kernel_lower_memory_free = { path = "tests/test_kernels/lower_memory_free", artifact = "bin", target = "x86_64-unknown-none" } test_kernel_write_usable_memory = { path = "tests/test_kernels/write_usable_memory", artifact = "bin", target = "x86_64-unknown-none" } test_kernel_fixed_kernel_address = { path = "tests/test_kernels/fixed_kernel_address", artifact = "bin", target = "x86_64-unknown-none" } +test_kernel_stack_address = { path = "tests/test_kernels/stack_address", artifact = "bin", target = "x86_64-unknown-none" } [profile.dev] panic = "abort" diff --git a/api/src/info.rs b/api/src/info.rs index d934c532..a3637c80 100644 --- a/api/src/info.rs +++ b/api/src/info.rs @@ -62,6 +62,10 @@ pub struct BootInfo { pub kernel_len: u64, /// Virtual address of the loaded kernel image. pub kernel_image_offset: u64, + /// Virtual address of the kernel stack bottom. + pub kernel_stack_bottom: u64, + /// Size of the kernel stack + pub kernel_stack_len: u64, #[doc(hidden)] pub _test_sentinel: u64, @@ -85,6 +89,8 @@ impl BootInfo { kernel_addr: 0, kernel_len: 0, kernel_image_offset: 0, + kernel_stack_bottom: 0, + kernel_stack_len: 0, _test_sentinel: 0, } } diff --git a/common/src/lib.rs b/common/src/lib.rs index 1ebe9aa0..e2a188b0 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -415,6 +415,7 @@ where Mappings { framebuffer: framebuffer_virt_addr, entry_point, + stack_bottom: stack_start.start_address(), // Use the configured stack size, even if it's not page-aligned. However, we // need to align it down to the next 16-byte boundary because the System V // ABI requires a 16-byte stack alignment. @@ -438,6 +439,7 @@ where pub struct Mappings { /// The entry point address of the kernel. pub entry_point: VirtAddr, + pub stack_bottom: VirtAddr, /// The (exclusive) end address of the kernel stack. pub stack_top: VirtAddr, /// Keeps track of used entries in the level 4 page table, useful for finding a free @@ -579,6 +581,8 @@ where info.kernel_addr = mappings.kernel_slice_start.as_u64(); info.kernel_len = mappings.kernel_slice_len as _; info.kernel_image_offset = mappings.kernel_image_offset.as_u64(); + info.kernel_stack_bottom = mappings.stack_bottom.as_u64(); + info.kernel_stack_len = config.kernel_stack_size; info._test_sentinel = boot_config._test_sentinel; info }); diff --git a/tests/stack_address.rs b/tests/stack_address.rs new file mode 100644 index 00000000..84b7365f --- /dev/null +++ b/tests/stack_address.rs @@ -0,0 +1,6 @@ +use bootloader_test_runner::run_test_kernel; + +#[test] +fn basic_boot() { + run_test_kernel(env!("CARGO_BIN_FILE_TEST_KERNEL_STACK_ADDRESS_basic_boot")); +} diff --git a/tests/test_kernels/Cargo.lock b/tests/test_kernels/Cargo.lock index ea6968ed..1670fd69 100644 --- a/tests/test_kernels/Cargo.lock +++ b/tests/test_kernels/Cargo.lock @@ -111,6 +111,15 @@ dependencies = [ "x86_64 0.15.2", ] +[[package]] +name = "test_kernel_stack_address" +version = "0.1.0" +dependencies = [ + "bootloader_api", + "uart_16550", + "x86_64 0.15.2", +] + [[package]] name = "test_kernel_write_usable_memory" version = "0.1.0" diff --git a/tests/test_kernels/Cargo.toml b/tests/test_kernels/Cargo.toml index 80daa869..f781d25c 100644 --- a/tests/test_kernels/Cargo.toml +++ b/tests/test_kernels/Cargo.toml @@ -13,6 +13,7 @@ members = [ "lower_memory_free", "write_usable_memory", "fixed_kernel_address", + "stack_address", ] [profile.release] diff --git a/tests/test_kernels/stack_address/Cargo.toml b/tests/test_kernels/stack_address/Cargo.toml new file mode 100644 index 00000000..28b59198 --- /dev/null +++ b/tests/test_kernels/stack_address/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "test_kernel_stack_address" +version = "0.1.0" +edition = "2024" + +[target.'cfg(target_arch = "x86_64")'.dependencies] +bootloader_api = { path = "../../../api" } +x86_64 = { version = "0.15.2", default-features = false, features = [ + "instructions", +] } +uart_16550 = "0.2.10" diff --git a/tests/test_kernels/stack_address/src/bin/basic_boot.rs b/tests/test_kernels/stack_address/src/bin/basic_boot.rs new file mode 100644 index 00000000..c2ee31ae --- /dev/null +++ b/tests/test_kernels/stack_address/src/bin/basic_boot.rs @@ -0,0 +1,32 @@ +#![no_std] // don't link the Rust standard library +#![no_main] // disable all Rust-level entry points + +use bootloader_api::{BootInfo, entry_point}; +use test_kernel_stack_address::{BOOTLOADER_CONFIG, QemuExitCode, exit_qemu}; + +entry_point!(kernel_main, config = &BOOTLOADER_CONFIG); + +fn kernel_main(boot_info: &'static mut BootInfo) -> ! { + let x: i32 = 42; + let vaddr = &x as *const _ as usize as u64; + + assert_ne!(boot_info.kernel_stack_bottom, 0); + assert_eq!( + boot_info.kernel_stack_len, + BOOTLOADER_CONFIG.kernel_stack_size + ); + assert!(vaddr >= boot_info.kernel_stack_bottom); + assert!(vaddr < boot_info.kernel_stack_bottom + boot_info.kernel_stack_len); + + exit_qemu(QemuExitCode::Success); +} + +/// This function is called on panic. +#[cfg(not(test))] +#[panic_handler] +fn panic(info: &core::panic::PanicInfo) -> ! { + use core::fmt::Write; + + let _ = writeln!(test_kernel_stack_address::serial(), "PANIC: {info}"); + exit_qemu(QemuExitCode::Failed); +} diff --git a/tests/test_kernels/stack_address/src/lib.rs b/tests/test_kernels/stack_address/src/lib.rs new file mode 100644 index 00000000..9a8212e2 --- /dev/null +++ b/tests/test_kernels/stack_address/src/lib.rs @@ -0,0 +1,37 @@ +#![no_std] + +use bootloader_api::{BootloaderConfig, config::Mapping}; + +pub const KERNEL_ADDR: u64 = 0x1987_6543_0000; + +pub const BOOTLOADER_CONFIG: BootloaderConfig = { + let mut config = BootloaderConfig::new_default(); + config.mappings.kernel_base = Mapping::FixedAddress(KERNEL_ADDR); + config +}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u32)] +pub enum QemuExitCode { + Success = 0x10, + Failed = 0x11, +} + +pub fn exit_qemu(exit_code: QemuExitCode) -> ! { + use x86_64::instructions::{nop, port::Port}; + + unsafe { + let mut port = Port::new(0xf4); + port.write(exit_code as u32); + } + + loop { + nop(); + } +} + +pub fn serial() -> uart_16550::SerialPort { + let mut port = unsafe { uart_16550::SerialPort::new(0x3F8) }; + port.init(); + port +}