Built paging / the kernel's own page tables (with a TSS+IST)
This commit is contained in:
+24
-3
@@ -37,6 +37,25 @@ means present, ring 0, 64-bit interrupt gate. `src/arch/x86_64/idt.zig` builds t
|
||||
table, points the first 32 vectors at their stubs, and loads it with `lidt`
|
||||
(`idt_flush`).
|
||||
|
||||
## The TSS and the double-fault stack
|
||||
|
||||
There's one more table, the **Task State Segment**. In long mode its main
|
||||
remaining job is the **Interrupt Stack Table (IST)**: an IDT gate can name an IST
|
||||
slot, and when that vector fires the CPU switches to the stack recorded there —
|
||||
*regardless* of what the interrupted stack looked like.
|
||||
|
||||
This matters most for the **double fault** (#DF, vector 8). A #DF means the CPU
|
||||
hit a fault *while trying to deliver another fault* — very often because the
|
||||
current stack pointer is bad, so pushing the exception frame itself faulted. If
|
||||
the #DF handler then tried to push onto that same bad stack, it would fault a
|
||||
third time and **triple-fault** — an instant reset. So the #DF gate is pointed at
|
||||
**IST1**, a small dedicated stack (`src/arch/x86_64/tss.zig`) that's always valid.
|
||||
|
||||
Bringing it up: fill in the TSS's IST1 pointer, publish the TSS through a
|
||||
descriptor in the GDT (`gdt.setTss`), and load it into the task register with
|
||||
`ltr`. The TSS descriptor is a 16-byte system descriptor spanning two GDT slots,
|
||||
which is why the GDT grew from three entries to five.
|
||||
|
||||
## The stubs and the trap frame
|
||||
|
||||
On an exception the CPU pushes a small frame (SS, RSP, RFLAGS, CS, RIP) and, for
|
||||
@@ -87,11 +106,13 @@ together confirm the whole path: the GDT is active (we're still executing), the
|
||||
IDT vectored to the right stub, the stub built a correct `CpuState`, and the Zig
|
||||
handler read it and reported instead of triple-faulting.
|
||||
|
||||
Separately, pointing RSP at an unmapped address and faulting forced a **double
|
||||
fault** — reported cleanly (`double fault (vector 8)`) rather than triple-faulting
|
||||
into a reset, which only works because #DF ran on IST1. That's the proof the
|
||||
TSS/IST is wired up: the handler survived a completely broken stack.
|
||||
|
||||
## What's next (not done here)
|
||||
|
||||
- **A TSS with an IST** (interrupt stack table) so the double-fault handler runs
|
||||
on a known-good stack — important because a double fault often means the current
|
||||
stack is unusable, and without an IST the handler would itself fault.
|
||||
- **Device interrupts**: program the local APIC and IO-APIC, wire a timer and the
|
||||
keyboard onto vectors ≥ 32, and (unlike exceptions) actually *return* from them
|
||||
with `iretq` — which `isr_common` already does.
|
||||
|
||||
Reference in New Issue
Block a user