First stage of the preemptive context-switching plan (see ~/.claude/plans/logical-snuggling-bear.md). Pure foundation work -- every arch's ISR now saves the full register set on interrupt entry, so a trap frame is in principle sufficient to resume execution anywhere it was taken. No FORTH-visible behavior changes. amd64: added FXSAVE/FXRSTOR, closing a genuine pre-existing correctness gap (not just future-preemption prep) -- confirmed live double-precision FP code reachable from ordinary interpreter dispatch (vm_runtime.c Loop #5/#6), and the ISR previously saved zero FP/SSE state. rbp repurposed as a fixed anchor so the 16-byte-aligned FXSAVE area can be carved out of an unpredictably-aligned rsp without disturbing existing argument reads. aarch64: extended the trap frame 672->800 bytes, adding v8-v15 (AAPCS64 callee-saved, previously excluded on call-site-only reasoning that doesn't hold for an async trap). riscv64: extended the trap frame 320->512 bytes, adding s0-s11 and fs0-fs11 (the latter still correctly gated behind sstatus.FS != Off). All 3 architectures re-verified clean boot to ok> under the new frames -- amd64 through hundreds of timer ticks with FXSAVE/FXRSTOR live on every interrupt, aarch64 through 987 ticks, riscv64 clean on the now-larger FS-conditional block. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016UNhH1mhi52i6Qihh7ZV5S
228 lines
6.5 KiB
ArmAsm
228 lines
6.5 KiB
ArmAsm
/*
|
|
* isr.S - Interrupt/exception stubs for amd64
|
|
*
|
|
* Stack on entry to ISR stub (after CPU pushes):
|
|
* [rsp+0] = error code (if any, or pushed 0)
|
|
* [rsp+8] = vector (pushed by stub)
|
|
* [rsp+16] = RIP
|
|
* [rsp+24] = CS
|
|
* [rsp+32] = RFLAGS
|
|
* [rsp+40] = RSP (if privilege change)
|
|
* [rsp+48] = SS (if privilege change)
|
|
*
|
|
* For IRQs (vector >= 32), we return via IRETQ.
|
|
* For exceptions (vector < 32), we halt.
|
|
*/
|
|
|
|
.intel_syntax noprefix
|
|
.section .text
|
|
|
|
/* Mark symbols referenced from C as hidden so the C compiler uses direct
|
|
* RIP-relative addressing (LEA) rather than GOT indirection. Without this,
|
|
* -fPIC code collapses the GOT reference to a direct MOV that reads the
|
|
* symbol's *bytes* instead of its *address*, corrupting the IDT setup. */
|
|
.hidden isr_stub_table
|
|
.hidden isr_stub0
|
|
.hidden isr_stub32
|
|
.hidden isr_stub33
|
|
.hidden isr_stub_default
|
|
.hidden isr_common_entry
|
|
|
|
.macro ISR_NOERR num
|
|
.global isr_stub\num
|
|
isr_stub\num:
|
|
push 0 /* fake error code */
|
|
push \num /* vector number */
|
|
jmp isr_common_entry
|
|
.endm
|
|
|
|
.macro ISR_ERR num
|
|
.global isr_stub\num
|
|
isr_stub\num:
|
|
/* error code already on stack from CPU */
|
|
push \num /* vector number */
|
|
jmp isr_common_entry
|
|
.endm
|
|
|
|
.global isr_common_entry
|
|
isr_common_entry:
|
|
/* Save all general-purpose registers */
|
|
push rax
|
|
push rbx
|
|
push rcx
|
|
push rdx
|
|
push rsi
|
|
push rdi
|
|
push rbp
|
|
push r8
|
|
push r9
|
|
push r10
|
|
push r11
|
|
push r12
|
|
push r13
|
|
push r14
|
|
push r15
|
|
|
|
/* Stack layout now:
|
|
* [rsp+0] = r15
|
|
* [rsp+8] = r14
|
|
* ...
|
|
* [rsp+112] = rax
|
|
* [rsp+120] = vector
|
|
* [rsp+128] = error
|
|
* [rsp+136] = rip
|
|
* [rsp+144] = cs
|
|
* [rsp+152] = rflags
|
|
*/
|
|
|
|
/* FABRIC-3.md SXXVIII (Stage 0, preemptive-context-switch trap-frame
|
|
* parity, 2026-09-13): FXSAVE/FXRSTOR added here to close a real,
|
|
* pre-existing gap, not just a future-preemption nicety -- confirmed
|
|
* live double-precision FP code reachable from ordinary interpreter
|
|
* dispatch (vm_runtime.c's Loop #5/#6 physics inference, called from
|
|
* vm_tick() during word execution, not just at boot). The GPR pushes
|
|
* above only cover integer state; on x86-64 SysV all XMM registers are
|
|
* caller-saved, meaning the compiler is free to leave a live value in
|
|
* one between any two instructions of straight-line code -- an
|
|
* asynchronous interrupt is not a call site the interrupted code's own
|
|
* instructions know about, so nothing here previously guaranteed FP
|
|
* state actually survived a timer tick landing mid-computation.
|
|
*
|
|
* rbp is repurposed below as a fixed anchor pointing at "rsp right after
|
|
* the 15 GPR pushes" (rbp's own true pushed value already lives safely
|
|
* on the stack above and is restored by the ordinary `pop rbp` later) --
|
|
* FXSAVE requires a 16-byte-aligned address and rsp's alignment here is
|
|
* unpredictable (the interrupt could have landed anywhere), so the
|
|
* FXSAVE area is carved out and aligned separately, with rbp used to
|
|
* keep addressing everything else at its original, fixed offsets
|
|
* regardless of that adjustment. */
|
|
mov rbp, rsp
|
|
sub rsp, 528 /* 512-byte FXSAVE area + 16 slack for alignment */
|
|
and rsp, -16
|
|
fxsave [rsp]
|
|
|
|
/* Set up arguments for isr_common_handler(vector, error, rip, cs, rflags, cr2) */
|
|
mov rdi, [rbp + 120] /* vector */
|
|
mov rsi, [rbp + 128] /* error */
|
|
mov rdx, [rbp + 136] /* rip */
|
|
mov rcx, [rbp + 144] /* cs */
|
|
mov r8, [rbp + 152] /* rflags */
|
|
mov r9, cr2 /* cr2 */
|
|
|
|
/* Pass stack frame pointer (after pushes) as 7th argument */
|
|
push rbp
|
|
call isr_common_handler
|
|
add rsp, 8
|
|
|
|
fxrstor [rsp]
|
|
mov rsp, rbp /* collapse back to right after the GPR pushes */
|
|
|
|
/* Restore general-purpose registers */
|
|
pop r15
|
|
pop r14
|
|
pop r13
|
|
pop r12
|
|
pop r11
|
|
pop r10
|
|
pop r9
|
|
pop r8
|
|
pop rbp
|
|
pop rdi
|
|
pop rsi
|
|
pop rdx
|
|
pop rcx
|
|
pop rbx
|
|
pop rax
|
|
|
|
/* Remove vector and error code from stack */
|
|
add rsp, 16
|
|
|
|
/* Return from interrupt */
|
|
iretq
|
|
|
|
/* Exception/interrupt stubs (0-31 explicit) */
|
|
ISR_NOERR 0
|
|
ISR_NOERR 1
|
|
ISR_NOERR 2
|
|
ISR_NOERR 3
|
|
ISR_NOERR 4
|
|
ISR_NOERR 5
|
|
ISR_NOERR 6
|
|
ISR_NOERR 7
|
|
ISR_ERR 8
|
|
ISR_NOERR 9
|
|
ISR_ERR 10
|
|
ISR_ERR 11
|
|
ISR_ERR 12
|
|
ISR_ERR 13
|
|
ISR_ERR 14
|
|
ISR_NOERR 15
|
|
ISR_NOERR 16
|
|
ISR_ERR 17
|
|
ISR_NOERR 18
|
|
ISR_NOERR 19
|
|
ISR_NOERR 20
|
|
ISR_ERR 21
|
|
ISR_NOERR 22
|
|
ISR_NOERR 23
|
|
ISR_NOERR 24
|
|
ISR_NOERR 25
|
|
ISR_NOERR 26
|
|
ISR_NOERR 27
|
|
ISR_NOERR 28
|
|
ISR_NOERR 29
|
|
ISR_ERR 30
|
|
ISR_NOERR 31
|
|
|
|
/* Stub for APIC Timer (vector 0x20 = 32) */
|
|
/* Diagnostic: write raw 'I' to COM1 (0x3F8) when stub is entered.
|
|
* If 'I' never appears in the serial log, the stub is never reached
|
|
* (bad IDT entry or fault during CPU interrupt delivery). */
|
|
.global isr_stub32
|
|
isr_stub32:
|
|
push 0
|
|
push 0x20
|
|
jmp isr_common_entry
|
|
|
|
/* Stub for i8042 keyboard IRQ1, routed via I/O APIC to vector 0x21 = 33
|
|
* (item 4.3.5, FABRIC-0.md §27.5). Before this stub existed, vector 33 fell
|
|
* through to isr_stub_default below, which unconditionally reports "255"
|
|
* regardless of which IDT slot actually fired -- the CPU legitimately took
|
|
* IDT[33] (confirmed via the LAPIC's own ISR register), but isr_common_handler
|
|
* only ever saw vector 255 and treated a real keyboard IRQ as spurious,
|
|
* silently skipping EOI and leaving vector 33 permanently stuck in-service. */
|
|
.global isr_stub33
|
|
isr_stub33:
|
|
push 0
|
|
push 33
|
|
jmp isr_common_entry
|
|
|
|
/* Default stub for other vectors (34-255) */
|
|
.global isr_stub_default
|
|
isr_stub_default:
|
|
push 0 /* fake error */
|
|
push 255 /* unknown vector, will be logged */
|
|
jmp isr_common_entry
|
|
|
|
/* Use .data instead of .rodata so UEFI applies relocations */
|
|
.section .data
|
|
.align 8
|
|
.global isr_stub_table
|
|
isr_stub_table:
|
|
.quad isr_stub0, isr_stub1, isr_stub2, isr_stub3
|
|
.quad isr_stub4, isr_stub5, isr_stub6, isr_stub7
|
|
.quad isr_stub8, isr_stub9, isr_stub10, isr_stub11
|
|
.quad isr_stub12, isr_stub13, isr_stub14, isr_stub15
|
|
.quad isr_stub16, isr_stub17, isr_stub18, isr_stub19
|
|
.quad isr_stub20, isr_stub21, isr_stub22, isr_stub23
|
|
.quad isr_stub24, isr_stub25, isr_stub26, isr_stub27
|
|
.quad isr_stub28, isr_stub29, isr_stub30, isr_stub31
|
|
/* Vector 32 = APIC Timer */
|
|
.quad isr_stub32
|
|
/* Vector 33 = i8042 keyboard IRQ1 (item 4.3.5) */
|
|
.quad isr_stub33
|
|
/* Vectors 34-255 default to unknown stub */
|
|
.rept 222
|
|
.quad isr_stub_default
|
|
.endr
|