From 397c701d456ca8051ef28244c8235cf95b4ffb41 Mon Sep 17 00:00:00 2001 From: vasilito Date: Fri, 10 Jul 2026 22:48:21 +0300 Subject: [PATCH] kernel: map and set user stack for initial bootstrap init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The initial bootstrap init (started directly by the kernel via usermode_bootstrap) had RSP = 0 from InterruptStack::init(), which does NOT set RSP. This caused any push/call in the binary to fault on address 0. The Rust panic handler caught the resulting page fault and generated ud2 — visible as 'Invalid opcode fault' at a low RIP before main() ever runs. This was the root cause of the init crash. Fix: - Map 8 pages (32 KiB) of user stack just above the initfs image - Set RSP to the top of the stack - Add debug log showing the mapped region The stack is intentionally small (32 KiB) — enough for Rust _start to run but small enough to detect stack overflow immediately. Once init calls fexec_impl to spawn the real init, the real init gets its own full 8 MB stack via the redox-rt exec path. --- src/syscall/process.rs | 51 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/src/syscall/process.rs b/src/syscall/process.rs index c4f98f5182..2b310a0d3a 100644 --- a/src/syscall/process.rs +++ b/src/syscall/process.rs @@ -293,7 +293,55 @@ pub unsafe fn usermode_bootstrap(bootstrap: &Bootstrap, token: &mut CleanLockTok debug!("Bootstrap entry point: {:X}", bootstrap_entry); assert_ne!(bootstrap_entry, 0); - // Start in a minimal environment without any stack. + // Map a minimal user stack for the bootstrap process. Without this, + // RSP defaults to 0 from InterruptStack::init() and the first `push` or + // `call` in the binary triggers a page fault at address 0, which the + // C startup turns into `ud2` (Rust panic handler) — visible as + // "Invalid opcode fault" at a low RIP before main() ever runs. + // + // The stack is mapped just above the initfs image in user virtual + // memory. The top-of-stack (RSP initial value) points to the END of + // the mapped region, since x86-64 stacks grow downward. + const BOOTSTRAP_STACK_PAGES: usize = 8; // 32 KiB — enough for Rust _start + let stack_base_vaddr = PAGE_SIZE + bootstrap.page_count * PAGE_SIZE; + let stack_top_vaddr = stack_base_vaddr + BOOTSTRAP_STACK_PAGES * PAGE_SIZE; + { + let addr_space = Arc::clone( + context::current() + .read(token.token()) + .addr_space() + .expect("expected bootstrap context to have an address space"), + ); + let page_count = + NonZeroUsize::new(BOOTSTRAP_STACK_PAGES).expect("BOOTSTRAP_STACK_PAGES must be non-zero"); + let stack_base = Page::containing_address(VirtualAddress::new(stack_base_vaddr)); + let _base_page = { + let mut lock_token = token.token(); + let mut addr_space_lock = addr_space.acquire_write(lock_token.downgrade()); + addr_space_lock + .mmap( + &addr_space, + Some(stack_base), + page_count, + MapFlags::MAP_FIXED_NOREPLACE | MapFlags::PROT_READ | MapFlags::PROT_WRITE, + None, + |page, flags, mapper, flusher| { + let span = PageSpan::new(page, BOOTSTRAP_STACK_PAGES); + Ok(Grant::zeroed( + span, + flags, + mapper, + flusher, + )?) + }, + ) + .map_err(|e| format!("failed to mmap bootstrap stack: {e:?}"))?; + }; + debug!( + "Bootstrap stack mapped at {:#x}..{:#x} ({} pages)", + stack_base_vaddr, stack_top_vaddr, BOOTSTRAP_STACK_PAGES + ); + } let ctx = context::current(); let mut lock = ctx.write(token.token()); @@ -303,6 +351,7 @@ pub unsafe fn usermode_bootstrap(bootstrap: &Bootstrap, token: &mut CleanLockTok { regs.init(); regs.set_instr_pointer(bootstrap_entry.try_into().unwrap()); + regs.set_stack_pointer(stack_top_vaddr); } }