The GS base now points at an arch-owned ArchPerCpu (percpu.zig) holding
the kernel RSP and a scratch slot (for the coming syscall stub, at fixed
%gs offsets) plus the scheduler pointer. isr_common conditionally
swapgs's on entry/exit when the interrupted frame was ring 3, and
enter_user swapgs's before dropping to ring 3 — so kernel code always
sees the kernel GS base and a ring-3 `mov %ax,%gs` can no longer poison
cpuLocal(). No swapgs on ring-0 interrupts (the common case). Suite
27/27, including int 0x80 from ring 3 and faults.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Task gains kstack_top and pml4 (0 = kernel task); PerCpu tracks the
loaded CR3. A shared switchTo() publishes the incoming task's kernel
stack (TSS.rsp0) and address space (CR3, only when it changes — every
write is a full TLB flush), used by both schedule() and exit(). APs
adopt the kernel page tables explicitly rather than the caller's live
CR3. All tasks are kernel tasks today, so this is a no-op beyond the
register switch. Suite 27/27.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
max_cpus, max_tasks, kernel/IST stack sizes, and timer_hz move from scattered constants into one config module. root.zig goes back to being just the boot contract. Values unchanged; any can become a -D build option later.
Hoist max_cpus into danos (root.zig), raise it to 128, and stop reserving a static [max_cpus][16 KiB] IST array. Only the BSP's IST stack is static (needed before the allocator); each AP's is heap-allocated when it comes online. Shrinks the kernel from ~1.1 MB to ~139 KiB in ReleaseSmall.
spawnOn(entry, priority, cpu) routes to a per-core pinned queue, merged with the global queue at O(1) selection. Falls back to unpinned for an offline/invalid core.