Per-process address spaces (AddressSpace = a PML4 with an empty user
half and the shared kernel half copied in; create/destroy in paging.zig)
with CR3 switched on context switch and TSS.rsp0/kernel_rsp published per
switch. The GS base now points at an arch per-CPU block and every ring
transition observes the swapgs discipline, so a ring-3 `mov %ax,%gs` can
no longer poison per-CPU access. syscall/sysret is the primary user entry
(int 0x80 kept as a test path); one handler, installed once at boot,
serves both and dispatches on whether the caller is a scheduled process
or a borrowed test thread. spawnProcess loads an ELF into a fresh address
space and schedules it; exit frees the address space after switching to
the kernel tables. New `process` test: init runs twice as a real process
(create/exit/recreate) on its own page tables, coexisting with a kernel
task under preemption. Suite 28/28.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.