Branch: milestone/M1 (M2 commits applied on top of M1_STABLE)
Status: COMPLETE (5 commits, smoke-tested)
M2 replaces the ad-hoc M1 stack placement (kernel stack in .bss, IST stacks
as raw physical pointer casts) with a fully guarded, physmap-aware stack model.
All stacks now live in a dedicated virtual region at 0xFFFFFF8000000000
(PML4[511]). Each stack has explicit guard pages (not-present PTEs) above and
below its usable region, mapped at 4 KB granularity from the start.
Before (M1):
- 16 KB static array in
.bss(boot.asm: stack_bottom / stack_top) - Guard page attempted post-hoc via
vmm_setup_kernel_guard_pages() - Guard silently skipped because
stack_bottom - PAGEoverlapped.data
After (M2):
VA Content
──────────────────────────────────────────────────────────
0xFFFFFF8000000000 PT[0] NOT-PRESENT ← guard_lo
0xFFFFFF8000001000 PT[1] RW/NX/P ← stack frame 0
0xFFFFFF8000002000 PT[2] RW/NX/P ← stack frame 1
0xFFFFFF8000003000 PT[3] RW/NX/P ← stack frame 2
0xFFFFFF8000004000 PT[4] RW/NX/P ← stack frame 3
0xFFFFFF8000005000 PT[5] NOT-PRESENT ← implicit (unmapped)
0xFFFFFF8000006000 PT[6] NOT-PRESENT ← guard_hi (explicit)
RSP_INIT = 0xFFFFFF8000005000 (end of last usable page, NOT a guard)
TSS.rsp0 = 0xFFFFFF8000005000
Before (M1):
pmm_alloc_frame()× 2 per IST, returned physical address cast directly to virtual (identity mapping assumption)- No guard pages; contiguous frame layout made guards impossible
- IST pointers = physical addresses reinterpreted as VAs
After (M2):
IST1 (Double Fault, IDT IST=1):
guard_lo : 0xFFFFFF800000F000 (NP)
usable : 0xFFFFFF8000010000..0xFFFFFF8000011FFF (8 KB)
top : 0xFFFFFF8000012000 → TSS.ist1
guard_hi : 0xFFFFFF8000013000 (NP)
IST2 (Page Fault, IDT IST=2):
guard_lo : 0xFFFFFF8000014000 (NP)
usable : 0xFFFFFF8000015000..0xFFFFFF8000016FFF (8 KB)
top : 0xFFFFFF8000017000 → TSS.ist2
guard_hi : 0xFFFFFF8000018000 (NP)
IST3 (General Protection Fault, IDT IST=3):
guard_lo : 0xFFFFFF8000019000 (NP)
usable : 0xFFFFFF800001A000..0xFFFFFF800001BFFF (8 KB)
top : 0xFFFFFF800001C000 → TSS.ist3
guard_hi : 0xFFFFFF800001D000 (NP)
All IST stacks share the same PT as the kernel main stack (one 2 MB region).
M2 reorders early boot to make physmap available before TSS/IDT:
Phase 1 (old .bss stack — interrupts disabled throughout):
pmm_init*()
vmm_init()
vmm_init_physmap() ← MOVED before tss_init
vmm_alloc_kernel_stack() ← NEW
tss_init(new_rsp) ← REDESIGNED (vmm_map-based IST)
trampoline_switch_stack() ← NEW (switches RSP, calls phase2)
Phase 2 (new guarded kernel stack):
idt_init() ← STI happens here
heap_init() / sched / drivers / fs / shell
NOTE(M2-B): Steps 3–6 (physmap through stack switch) run without a valid IDT or TSS. Any CPU exception there causes a triple fault. These functions are deterministic (no external I/O, no complex control flow) and do not fault under correct operation. A minimal fallback IDT (no STI) can be added in M3 for safer early-boot debugging if needed.
| Function | File | Purpose |
|---|---|---|
vmm_alloc_kernel_stack() |
mm/vmm.c |
Allocate + map 16 KB kernel stack with guards |
vmm_alloc_ist_stack(...) |
mm/vmm.c |
Allocate + map one IST stack with guards |
m2_mark_guard(addr) |
mm/vmm.c |
Force a PTE to 0 (NP) + INVLPG |
m2_map_stack_pages(base,n) |
mm/vmm.c |
Bulk-map n pages to fresh PMM frames |
trampoline_switch_stack |
arch/x86/idt_asm.asm |
Switch RSP + tail-call |
kernel_main_phase2() |
kernel/kernel.c |
Phase-2 init on new stack |
| Function | File | Change |
|---|---|---|
tss_init(uint64_t rsp0) |
arch/x86/tss.c |
New signature; IST via vmm_map |
tss_get_ist_bases() |
arch/x86/tss.c |
Returns virtual tops, not physical |
kernel_main() |
kernel/kernel.c |
Shortened to phase-1 only |
vmm_setup_kernel_guard_pages() |
mm/vmm.h |
Marked DEPRECATED; not called |
e522212— M2 constants +vmm_alloc_kernel_stack()8293bee—trampoline_switch_stackassembly trampoline2dfb3c5— Redesigntss_init()with virtual IST mappingcd409c2— Splitkernel_main+ reorder init- (this commit) — Cleanup, deprecation notes, devlog
make iso
qemu-system-x86_64 \
-cdrom myos.iso \
-debugcon stdio \
-global isa-debugcon.iobase=0xe9 \
-no-reboot -no-shutdown \
-m 256MExpected [M2] lines in serial output:
[M2] Allocating kernel stack in dedicated virtual region...
[M2] Kernel stack mapped:
usable : 0xFFFFFF8000001000 .. 0xFFFFFF8000004FFF (16 KB)
RSP_INIT: 0xFFFFFF8000005000
guard_lo: 0xFFFFFF8000000000 (NP)
guard_hi: 0xFFFFFF8000006000 (NP)
[M2] IST1 mapped: ...
[M2] IST2 mapped: ...
[M2] IST3 mapped: ...
[M2] Initializing TSS with guarded IST stacks...
[M2] TSS.ist1: 0xFFFFFF8000012000
[M2] TSS.ist2: 0xFFFFFF8000017000
[M2] TSS.ist3: 0xFFFFFF800001C000
[M2][OK] TSS loaded with guarded IST stacks
[M2] Stack switch complete. RSP= 0xFFFFFF8000004...
make uefi
./run_uefi.shOr manually:
qemu-system-x86_64 \
-machine q35 \
-bios /usr/share/ovmf/OVMF.fd \
-drive format=raw,file=fat:rw:dist/ \
-debugcon stdio \
-global isa-debugcon.iobase=0xe9 \
-serial file:/tmp/secos_uefi.log \
-no-reboot -no-shutdown \
-m 256MLog location: /tmp/secos_uefi.log
- Identity map [0, 512 MB) untouched
- PMM frame bitmap layout unchanged
- No heap dependency in early boot
- UEFI and Multiboot2 paths both work
- STI not called until after TSS is fully loaded
# Multiboot2 / GRUB
tools/smoke.sh --mb2 [--timeout 20] [--log /tmp/secos_mb2.log]
# UEFI / OVMF / q35
tools/smoke.sh --uefi [--timeout 25] [--log /tmp/secos_uefi.log]The script builds the target, launches QEMU headless (-display none,
-no-reboot -no-shutdown), and interprets the exit code:
| Exit code | Meaning | Result |
|---|---|---|
| 124 | timeout(1) killed QEMU after N seconds — kernel alive |
PASS |
| 0 | QEMU exited on its own — guest reset (triple fault + -no-reboot) |
FAIL |
| other | QEMU error or dependency missing | FAIL |
Two lines are emitted to ISA debugcon (port 0xE9) early in the boot
sequence, captured by QEMU -debugcon file:/tmp/...:
SECoS build 20260217150022 git:0dad8e0
[M2] Stack switch ok. RSP=0xFFFFFF8000004F58
| Marker | Emitted from | When |
|---|---|---|
SECoS build … git:… |
kernel_main() phase 1 |
Immediately after terminal_initialize() |
[M2] Stack switch ok. RSP=… |
kernel_main_phase2() |
After trampoline, before idt_init() |
Presence of both lines in the log confirms: (1) kernel reached C entry, and (2) the full M2 stack switch sequence completed without faulting.
If only line 1 is present, the fault occurred between physmap init and the
trampoline (the NOTE(M2-B) window). If neither line is present, the fault
occurred in assembly before kernel_main().
Mode Timeout QEMU exit RSP Result
MB2 20s 124 0xFFFFFF8000004F58 PASS
UEFI 25s 124 0xFFFFFF8000004F58 PASS
| File | Purpose |
|---|---|
lib/debugcon.h |
Inline ISA debugcon helpers (debugcon_putchar, _writestring, _print_hex) |
tools/smoke.sh |
Repeatable smoke test runner (--mb2 / --uefi) |
run_uefi.sh |
Wrapper for make run-uefi target → calls tools/smoke.sh --uefi |