Skip to content

Conversation

@ryanbreen
Copy link
Owner

@ryanbreen ryanbreen commented Nov 26, 2025

Summary

  • Fix hardcoded kernel CR3 (0x101000) that became stale after master PML4 creation
  • Add kernel_cr3 and saved_process_cr3 fields to PerCpuData struct
  • Interrupt/syscall entry now reads kernel CR3 from gs:[72] instead of hardcoded value
  • Process CR3 saved on entry at gs:[80], restored on exit when no context switch
  • Fix SyscallFrame struct field order to match assembly push order (was reversed)
  • Fix syscall_return_to_userspace to clear next_cr3 BEFORE CR3 switch (not after)

Root Cause

The kernel was triple faulting because:

  1. per_cpu::init() stored the bootloader's CR3 (0x101000) as kernel_cr3
  2. build_master_kernel_pml4() created a new master PML4 at 0x552000
  3. Assembly code used hardcoded 0x101000, causing page faults when switching CR3

Test plan

  • cargo run -p xtask -- boot-stages passes 36/40 stages
  • Zero compilation warnings
  • Userspace syscalls work (Stage 38: "Hello from userspace!")
  • ENOSYS handling works (Stage 39: "ENOSYS OK")
  • Multiple timer interrupts preempt userspace without crashes
  • CR3 debug markers ($CR3, !CR3) show correct switching

🤖 Generated with Claude Code


Note

Use per-CPU kernel_cr3/saved_process_cr3 for CR3 switching/restoration in interrupts/syscalls, update kernel_cr3 after master PML4, fix SyscallFrame layout, and clear next_cr3 before CR3 switch.

  • Interrupts/Syscalls (assembly):
    • Swap to kernel GS early and save process CR3 to gs:[80]; read kernel CR3 from gs:[72] (removes hardcoded 0x101000).
    • On return, clear next_cr3 at gs:[64] before switching CR3; if no context switch, restore saved process CR3; ensure proper swapgs sequencing.
    • Apply same logic to first userspace entry and syscall paths; add lightweight serial markers for diagnostics.
  • Per-CPU data (kernel/src/per_cpu.rs):
    • Extend PerCpuData with kernel_cr3 (offset 72) and saved_process_cr3 (offset 80) plus constants; update size/asserts.
    • Initialize kernel_cr3 from current CR3 in init(); add get_kernel_cr3/set_kernel_cr3 accessors.
  • Memory init (kernel/src/memory/mod.rs):
    • After building master PML4, read current CR3 and update per-CPU kernel_cr3.
  • Syscall frame (kernel/src/syscall/handler.rs):
    • Reorder SyscallFrame fields to match push order (r15 at RSP+0rax at RSP+112).

Written by Cursor Bugbot for commit 3962add. This will update automatically on new commits. Configure here.

The kernel was using a hardcoded CR3 value (0x101000) in assembly code,
which became stale after build_master_kernel_pml4() created a new master
PML4 at a different address. This caused triple faults during CR3 switches.

Changes:
- Add kernel_cr3 and saved_process_cr3 fields to PerCpuData struct
- Store kernel CR3 at gs:[72] for interrupt/syscall entry to read
- Save process CR3 at gs:[80] on entry, restore on exit if no context switch
- Update kernel_cr3 in memory::init() after master PML4 is created
- Fix SyscallFrame struct field order to match assembly push order
- Fix syscall_return_to_userspace to clear next_cr3 BEFORE CR3 switch

The fix ensures interrupt handlers and syscall entry can always switch
to the correct kernel page table, and restore the process page table
on return when no context switch occurred.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@ryanbreen ryanbreen merged commit 3962add into main Nov 26, 2025
5 of 9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants