From ff65afd003331b912ef672551e5dc3da52b0d868 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sat, 13 Sep 2025 18:32:15 +0200 Subject: [PATCH] Don't depend on the stack setup by the bootloader This way we can choose our own size for the stack and don't have to identity map it manually. Also this way the bootloader doesn't have to change the stack pointer right before calling into the kernel (which it currently does in an unsound way) --- src/arch/aarch64/start.rs | 16 +++++++++++++++- src/arch/riscv64/start.rs | 18 +++++++++++++----- src/arch/x86_shared/start.rs | 29 ++++++++++++++++++++++++++--- src/startup/memory.rs | 5 ----- src/startup/mod.rs | 5 ----- 5 files changed, 54 insertions(+), 19 deletions(-) diff --git a/src/arch/aarch64/start.rs b/src/arch/aarch64/start.rs index cd0b6828b6..a4f71cdb0d 100644 --- a/src/arch/aarch64/start.rs +++ b/src/arch/aarch64/start.rs @@ -1,9 +1,10 @@ //! This function is where the kernel sets up IRQ handlers -//! It is increcibly unsafe, and should be minimal in nature +//! It is incredibly unsafe, and should be minimal in nature //! It must create the IDT with the correct entries, those entries are //! defined in other files inside of the `arch` module use core::{ arch::global_asm, + cell::SyncUnsafeCell, slice, sync::atomic::{AtomicBool, Ordering}, }; @@ -22,6 +23,12 @@ static mut DATA_TEST_NONZERO: usize = 0xFFFF_FFFF_FFFF_FFFF; pub static AP_READY: AtomicBool = AtomicBool::new(false); static BSP_READY: AtomicBool = AtomicBool::new(false); +#[repr(C, align(16))] +struct StackAlign(T); + +static STACK: SyncUnsafeCell> = + SyncUnsafeCell::new(StackAlign([0; 128 * 1024])); + global_asm!(" .globl kstart kstart: @@ -33,6 +40,11 @@ global_asm!(" ldr x9, [x9, :lo12:{data_test_nonzero}] cbz x9, .Lkstart_crash + adrp x1, {stack} + add x1, x1, :lo12:{stack} + mov x2, {stack_size}-16 + add sp, x1, x2 + mov lr, 0 b {start} @@ -42,6 +54,8 @@ global_asm!(" ", bss_test_zero = sym BSS_TEST_ZERO, data_test_nonzero = sym DATA_TEST_NONZERO, + stack = sym STACK, + stack_size = const size_of_val(&STACK), start = sym start, ); diff --git a/src/arch/riscv64/start.rs b/src/arch/riscv64/start.rs index 3af8e917cc..bbce183fe9 100644 --- a/src/arch/riscv64/start.rs +++ b/src/arch/riscv64/start.rs @@ -1,5 +1,6 @@ use core::{ arch::{asm, global_asm}, + cell::SyncUnsafeCell, sync::atomic::{AtomicUsize, Ordering}, }; @@ -36,6 +37,12 @@ fn get_boot_hart_id(env: &[u8]) -> Option { None } +#[repr(C, align(16))] +struct StackAlign(T); + +static STACK: SyncUnsafeCell> = + SyncUnsafeCell::new(StackAlign([0; 128 * 1024])); + global_asm!(" .globl kstart kstart: @@ -49,6 +56,10 @@ global_asm!(" ld t0, {data_test_nonzero} beqz t0, .Lkstart_crash + .Lpcrel_hi0: + auipc sp, %pcrel_hi({stack}+{stack_size}-16) + addi sp, sp, %pcrel_lo(.Lpcrel_hi0) + li ra, 0 j {start} @@ -57,17 +68,14 @@ global_asm!(" ", bss_test_zero = sym BSS_TEST_ZERO, data_test_nonzero = sym DATA_TEST_NONZERO, + stack = sym STACK, + stack_size = const size_of_val(&STACK), start = sym start, ); /// The entry to Rust, all things must be initialized unsafe extern "C" fn start(args_ptr: *const KernelArgs) -> ! { unsafe { - asm!( - "sd x0, -16(fp)", // and stop frame walker here - "sd x0, -8(fp)", - ); - let bootstrap = { let args = args_ptr.read(); diff --git a/src/arch/x86_shared/start.rs b/src/arch/x86_shared/start.rs index b6160f74fe..24315b8845 100644 --- a/src/arch/x86_shared/start.rs +++ b/src/arch/x86_shared/start.rs @@ -1,5 +1,5 @@ //! This function is where the kernel sets up IRQ handlers -//! It is increcibly unsafe, and should be minimal in nature +//! It is incredibly unsafe, and should be minimal in nature //! It must create the IDT with the correct entries, those entries are //! defined in other files inside of the `arch` module use core::{ @@ -30,6 +30,12 @@ static DATA_TEST_NONZERO: SyncUnsafeCell = SyncUnsafeCell::new(usize::MAX pub static AP_READY: AtomicBool = AtomicBool::new(false); static BSP_READY: AtomicBool = AtomicBool::new(false); +#[repr(C, align(16))] +struct StackAlign(T); + +static STACK: SyncUnsafeCell> = + SyncUnsafeCell::new(StackAlign([0; 128 * 1024])); + #[cfg(target_arch = "x86")] global_asm!(" .globl kstart @@ -40,6 +46,11 @@ global_asm!(" cmp dword ptr [{data_test_nonzero}], 0 je .Lkstart_crash + mov eax, [esp + 4] + lea esp, [{stack}+{stack_size}-16] + mov [esp + 4], eax + mov [esp + 8], esp + jmp {start} .Lkstart_crash: @@ -48,6 +59,8 @@ global_asm!(" ", bss_test_zero = sym BSS_TEST_ZERO, data_test_nonzero = sym DATA_TEST_NONZERO, + stack = sym STACK, + stack_size = const size_of_val(&STACK), start = sym start, ); @@ -61,6 +74,14 @@ global_asm!(" cmp qword ptr [rip + {data_test_nonzero}], 0 je .Lkstart_crash + // Note: The System V ABI requires the stack to be aligned to 16 bytes + // before the call instruction. As we jump rather than call it has to + // be offset by 8 bytes. Additionally reserve a bit more space at the + // end of the stack to ensure that the start function returns to + // address 0. + lea rsp, [rip + {stack}+{stack_size}-24] + mov rsi, rsp + jmp {start} .Lkstart_crash: @@ -69,11 +90,13 @@ global_asm!(" ", bss_test_zero = sym BSS_TEST_ZERO, data_test_nonzero = sym DATA_TEST_NONZERO, + stack = sym STACK, + stack_size = const size_of_val(&STACK), start = sym start, ); /// The entry to Rust, all things must be initialized -unsafe extern "C" fn start(args_ptr: *const KernelArgs) -> ! { +unsafe extern "C" fn start(args_ptr: *const KernelArgs, stack_end: usize) -> ! { unsafe { let bootstrap = { let args = args_ptr.read(); @@ -88,7 +111,7 @@ unsafe extern "C" fn start(args_ptr: *const KernelArgs) -> ! { args.print(); // Set up GDT - gdt::init_bsp(args.stack_base as usize + args.stack_size as usize); + gdt::init_bsp(stack_end); // Set up IDT idt::init_bsp(); diff --git a/src/startup/memory.rs b/src/startup/memory.rs index eec03bdb9b..28d3d0186c 100644 --- a/src/startup/memory.rs +++ b/src/startup/memory.rs @@ -155,11 +155,6 @@ fn register_memory_from_kernel_args(args: &KernelArgs) { args.kernel_size as usize, BootloaderMemoryKind::Kernel, ); - register_memory_region( - args.stack_base as usize, - args.stack_size as usize, - BootloaderMemoryKind::IdentityMap, - ); register_memory_region( args.env_base as usize, args.env_size as usize, diff --git a/src/startup/mod.rs b/src/startup/mod.rs index 16e33b1ae7..1499ddde94 100644 --- a/src/startup/mod.rs +++ b/src/startup/mod.rs @@ -40,11 +40,6 @@ impl KernelArgs { { self.kernel_base }, self.kernel_base + self.kernel_size ); - info!( - "Stack: {:X}:{:X}", - { self.stack_base }, - self.stack_base + self.stack_size - ); info!( "Env: {:X}:{:X}", { self.env_base },