kernel: map and set user stack for initial bootstrap init

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.
This commit is contained in:
2026-07-10 22:48:21 +03:00
parent 62d5c08d06
commit 397c701d45
+50 -1
View File
@@ -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);
}
}