aarch64: split irq_spx into a real save/dispatch/restore/ERET trampoline
Punch list §25 item 0.5 complete.
irq_spx now branches (one instruction, well inside the 128-byte vector slot)
to irq_spx_trampoline, a 672-byte-frame save/restore sequence that calls a
C handler and returns via ERET. The other fifteen vectors are untouched,
still routing to the existing fatal handler.
EL selection (B3) happens once, in aarch64_install_vectors(), not per
interrupt: aarch64_current_el() (item 0.4) picks VBAR_EL1 or VBAR_EL2, and
the same answer is cached in a byte flag (el2_mode_flag) that the trampoline
reads to choose ELR_EL1/SPSR_EL1 vs ELR_EL2/SPSR_EL2 -- the two forms are
genuinely different MRS/MSR encodings, not runtime-selectable operands, so
this is the cheapest correct design: decide once at install time, branch
twice (save, restore) per interrupt afterward. VBAR_EL1 was previously
written unconditionally; this closes that half of item 0.4's known gap.
EL2 is coded from the architecture reference and cannot be boot-tested in
this environment (QEMU's aarch64 virt/EDK2 combination here yields EL1) --
reported as unverified rather than asserted as tested.
FP/SIMD save is not optional (B2, carried from item 0.4's finding that the
build has no -mgeneral-regs-only): the AAPCS64 caller-saved set -- v0-v7,
v16-v31, full 128 bits each -- plus FPSR/FPCR is saved and restored around
the C handler call. v8-v15 are callee-saved by the ABI and deliberately
excluded: the handler, being ordinary compiled C, preserves those itself.
aarch64_irq_handler() (interrupts.c) is deliberately empty. Distinguishing
which interrupt fired needs the GIC's IAR, which does not exist until item
0.6; nothing unmasks or routes any source to this vector yet, so the
function is not reachable during a normal boot. Per the item's own text,
no attempt was made to manufacture an interrupt to exercise this path early
-- 0.6 (GIC) and 0.7 (timer) are what prove it took and returned one.
Verified: every hand-computed frame offset (0, 16, 32 ... 640, frame size
0x2a0=672) checked against the actual disassembly of the built kernel, not
just visually reviewed -- save and restore sequences mirror exactly, and
aarch64_install_vectors' branch on the detected EL, the flag write, and the
trampoline's read of the same flag address all confirmed consistent. Boots
clean on real QEMU output, no regression: dict_hash 0x3d4e1daf289da94f
unchanged from the item 0.1-0.4 baseline, and the item 0.4 EL banner
("AArch64: running at EL1") still prints correctly ahead of "IDT installed.".
Only aarch64-scoped files touched (isr.S, interrupts.c) -- no shared loader
or header changed, so amd64 and riscv64 are provably unaffected; not
rebuilt for this item.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
f43f3f4482
commit
8d8f3aaae2
@@ -2057,7 +2057,7 @@ on until there is a tick on all three architectures (§16.1, §16.5).*
|
||||
`CNTHP_*_EL2`). Do not hardcode either level anywhere.
|
||||
*Done when:* the boot log states which EL was detected, on real QEMU output.
|
||||
|
||||
- [ ] **0.5 — aarch64: IRQ vector split.**
|
||||
- [x] **0.5 — aarch64: IRQ vector split.**
|
||||
Split `irq_spx` out of the shared fatal handler in `arch/aarch64/isr.S`: save `x0`–`x30`
|
||||
plus the saved-state registers (see B3 note below), call a C handler, restore, `eret`.
|
||||
The other fifteen vectors are unchanged. Note the 128-byte slot limit — the save sequence
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Capsule Block Manifest — Auto-generated
|
||||
<!-- Generated by mkcapsule --manifest 2026-08-03T22:28:54Z -->
|
||||
<!-- Generated by mkcapsule --manifest 2026-08-03T22:45:53Z -->
|
||||
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
|
||||
<!-- Hand-written justifications and immutability notes live -->
|
||||
<!-- in MANIFEST.md alongside this auto-generated index. -->
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
tick_number,elapsed_ns,tick_interval_ns,cache_hits_delta,bucket_hits_delta,word_executions_delta,hot_word_count,avg_word_heat_q48,window_width,actual_window_size,predicted_label_hits,jitter_bits,apic_ticks,time_trust_q48,variance_q48,vm_call_depth_max,hera_heat_q48,hermes_heat_q48,artemis_heat_q48
|
||||
1,10000,10000,0,0,183,4,45,731,731,0,0,0,65536,0,0,65536,0,0
|
||||
2,20000,10000,0,0,173,5,44,582,582,0,0,0,65536,0,0,65536,0,0
|
||||
3,30000,10000,0,0,184,6,60,582,582,0,0,0,65536,0,0,65536,0,0
|
||||
4,40000,10000,0,0,174,8,71,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
5,50000,10000,0,0,150,9,78,582,582,0,0,0,65536,0,0,65536,0,0
|
||||
6,60000,10000,0,0,159,11,73,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
7,70000,10000,0,0,188,15,71,685,685,0,0,0,65536,0,0,65536,0,0
|
||||
8,80000,10000,0,0,214,16,83,685,685,0,0,0,65536,0,0,65536,0,0
|
||||
9,90000,10000,0,0,216,19,82,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
10,100000,10000,0,0,222,21,84,567,567,0,0,0,65536,0,0,65536,0,0
|
||||
11,110000,10000,0,0,207,23,86,731,731,0,0,0,65536,0,0,65536,0,0
|
||||
12,120000,10000,0,0,189,26,83,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
13,130000,10000,0,0,221,28,80,731,731,0,0,0,65536,0,0,65536,0,0
|
||||
14,140000,10000,0,0,204,29,83,685,685,0,0,0,65536,0,0,65536,0,0
|
||||
15,150000,10000,0,0,203,30,82,731,731,0,0,0,65536,0,0,65536,0,0
|
||||
16,160000,10000,0,0,201,32,83,567,567,0,0,0,65536,0,0,65536,0,0
|
||||
17,170000,10000,0,0,190,34,86,731,731,0,0,0,65536,0,0,65536,0,0
|
||||
18,180000,10000,0,0,170,35,73,582,582,0,0,0,65536,0,0,65536,0,0
|
||||
19,190000,10000,0,0,154,28,44,731,731,0,0,0,65536,0,0,65536,0,0
|
||||
20,200000,10000,0,0,154,28,41,567,567,0,0,0,65536,0,0,65536,0,0
|
||||
21,210000,10000,0,0,153,32,39,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
22,220000,10000,0,0,167,35,42,582,582,0,0,0,65536,0,0,65536,0,0
|
||||
23,230000,10000,0,0,176,37,41,685,685,0,0,0,65536,0,0,65536,0,0
|
||||
24,240000,10000,0,0,175,37,39,731,731,0,0,0,65536,0,0,65536,0,0
|
||||
25,250000,10000,0,0,196,38,37,731,731,0,0,0,65536,0,0,65536,0,0
|
||||
26,260000,10000,0,0,192,41,37,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
27,270000,10000,0,0,158,40,38,685,685,0,0,0,65536,0,0,65536,0,0
|
||||
28,280000,10000,0,0,158,40,35,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
29,290000,10000,0,0,164,41,37,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
30,300000,10000,0,0,154,43,39,685,685,0,0,0,65536,0,0,65536,0,0
|
||||
31,310000,10000,0,0,172,46,35,582,582,0,0,0,65536,0,0,65536,0,0
|
||||
32,320000,10000,0,0,180,49,34,567,567,0,0,0,65536,0,0,65536,0,0
|
||||
33,330000,10000,0,0,145,51,35,567,567,0,0,0,65536,0,0,65536,0,0
|
||||
34,340000,10000,0,0,203,53,37,582,582,0,0,0,65536,0,0,65536,0,0
|
||||
35,350000,10000,0,0,154,55,39,731,731,0,0,0,65536,0,0,65536,0,0
|
||||
36,360000,10000,0,0,144,57,37,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
37,370000,10000,0,0,144,57,35,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
38,380000,10000,0,0,132,57,34,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
39,390000,10000,0,0,180,57,32,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
40,400000,10000,0,0,220,63,29,4096,0,0,0,0,65536,0,0,65536,0,0
|
||||
41,410000,10000,0,0,215,64,28,4096,24,0,0,0,65536,0,0,65536,0,0
|
||||
42,420000,10000,0,0,208,68,29,4096,24,0,0,0,65536,0,0,65536,0,0
|
||||
43,430000,10000,0,0,177,72,27,4096,24,0,0,0,65536,0,0,65536,0,0
|
||||
44,440000,10000,0,0,199,77,28,4096,24,0,0,0,65536,0,0,65536,0,0
|
||||
45,450000,10000,0,0,223,82,26,4096,30,0,0,0,65536,0,0,65536,0,0
|
||||
46,460000,10000,0,0,202,85,24,4096,67,0,0,0,65536,0,0,65536,0,0
|
||||
47,470000,10000,0,0,218,91,25,4096,198,0,0,0,65536,0,0,65536,0,0
|
||||
48,480000,10000,0,0,221,92,26,4096,350,0,0,0,65536,0,0,65536,0,0
|
||||
49,490000,10000,0,0,225,97,26,4096,528,0,0,0,65536,0,0,65536,0,0
|
||||
50,500000,10000,0,0,214,100,27,4096,694,0,0,0,65536,0,0,65536,0,0
|
||||
51,510000,10000,0,0,227,102,28,4096,883,0,0,0,65536,0,0,65536,0,0
|
||||
52,520000,10000,0,0,208,102,29,4096,1043,0,0,0,65536,0,0,65536,0,0
|
||||
53,530000,10000,0,0,207,102,30,4096,1202,0,0,0,65536,0,0,65536,0,0
|
||||
54,540000,10000,0,0,200,102,31,4096,1319,0,0,0,65536,0,0,65536,0,0
|
||||
55,550000,10000,0,0,205,103,32,4096,1464,0,0,0,65536,0,0,65536,0,0
|
||||
56,560000,10000,0,0,211,103,32,4096,1620,0,0,0,65536,0,0,65536,0,0
|
||||
57,570000,10000,0,0,193,110,33,4096,1712,0,0,0,65536,0,0,65536,0,0
|
||||
58,580000,10000,0,0,190,5,67,4096,1775,0,0,0,65536,0,0,65536,0,0
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -23,6 +23,29 @@ extern void aarch64_install_vectors(void);
|
||||
* arch/riscv64/interrupts.c. */
|
||||
extern int aarch64_current_el(void);
|
||||
|
||||
/**
|
||||
* @brief Dispatch an IRQ taken at current-EL with SPx (punch-list item 0.5).
|
||||
*
|
||||
* Called from @c irq_spx_trampoline in @c isr.S, which has already saved the
|
||||
* full caller-saved integer and FP/SIMD register sets plus @c ELR_ELx /
|
||||
* @c SPSR_ELx, and will restore all of it and @c ERET after this function
|
||||
* returns — unlike @c aarch64_exception_handler(), this path is designed to
|
||||
* return normally.
|
||||
*
|
||||
* **Deliberately empty at this item.** Distinguishing which interrupt fired
|
||||
* requires reading the GIC's Interrupt Acknowledge Register, and
|
||||
* acknowledging it requires writing EOIR — neither exists yet (item 0.6).
|
||||
* Nothing currently unmasks or routes any interrupt source to this vector,
|
||||
* so it is not reachable during a normal boot; item 0.5's own acceptance is
|
||||
* "boots with no regression," not having observed a call here (C2). This
|
||||
* function exists so the vector split is complete and linkable now, ready
|
||||
* for item 0.6 to add the IAR read / cause dispatch / EOIR write, and item
|
||||
* 0.7 to route the timer PPI to @c heartbeat_tick() through it.
|
||||
*/
|
||||
void aarch64_irq_handler(void)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Print a 64-bit value as "0xNNNNNNNNNNNNNNNN" to the kernel console.
|
||||
*
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
/*
|
||||
* isr.S (aarch64) - Exception vector table stub.
|
||||
* Installs a minimal VBAR_EL1 that routes all exceptions to a common C handler.
|
||||
*
|
||||
* Fourteen of the sixteen vectors still route to the shared fatal handler in
|
||||
* interrupts.c (unchanged by punch-list item 0.5). irq_spx -- the vector that
|
||||
* matters, since the kernel runs at current-EL with SPx (SP_EL1/SP_EL2, per
|
||||
* the detected EL) -- branches to a real trampoline: save state, call a C
|
||||
* handler, restore, ERET. Everything else keeps spinning on a fatal WFE/WFI;
|
||||
* only the interrupt path becomes returnable.
|
||||
*/
|
||||
|
||||
.section .text
|
||||
@@ -50,7 +56,9 @@ synchronous_spx:
|
||||
|
||||
.balign 128
|
||||
irq_spx:
|
||||
b aarch64_exception_handler
|
||||
/* 128-byte slot limit (32 instructions) -- the real save/restore
|
||||
* sequence does not fit inline. One branch out, well within budget. */
|
||||
b irq_spx_trampoline
|
||||
|
||||
.balign 128
|
||||
fiq_spx:
|
||||
@@ -94,10 +102,212 @@ fiq_lower_el32:
|
||||
serror_lower_el32:
|
||||
b aarch64_exception_handler
|
||||
|
||||
/* ---------------------------------------------------------------------------
|
||||
* EL selection.
|
||||
*
|
||||
* The kernel runs at exactly one exception level for its entire lifetime
|
||||
* (item 0.4's own caching rationale: CurrentEL cannot change post-boot), so
|
||||
* EL is decided **once here**, not per interrupt. aarch64_current_el()
|
||||
* (arch.c) is consulted to choose which VBAR_ELx gets the table's address,
|
||||
* and el2_mode_flag records the same answer for irq_spx_trampoline below to
|
||||
* pick ELR_EL1/SPSR_EL1 vs ELR_EL2/SPSR_EL2 without re-deriving it on every
|
||||
* interrupt.
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
.section .bss
|
||||
.balign 1
|
||||
el2_mode_flag:
|
||||
.byte 0
|
||||
|
||||
.text
|
||||
.global aarch64_install_vectors
|
||||
aarch64_install_vectors:
|
||||
stp x29, x30, [sp, #-16]!
|
||||
bl aarch64_current_el /* w0 = 1 or 2 */
|
||||
adrp x1, el2_mode_flag
|
||||
add x1, x1, :lo12:el2_mode_flag
|
||||
cmp w0, #2
|
||||
b.ne 1f
|
||||
|
||||
mov w2, #1
|
||||
strb w2, [x1]
|
||||
adr x0, aarch64_vector_table
|
||||
msr vbar_el2, x0
|
||||
isb
|
||||
b 2f
|
||||
|
||||
1: mov w2, #0
|
||||
strb w2, [x1]
|
||||
adr x0, aarch64_vector_table
|
||||
msr vbar_el1, x0
|
||||
isb
|
||||
|
||||
2: ldp x29, x30, [sp], #16
|
||||
ret
|
||||
|
||||
/* ---------------------------------------------------------------------------
|
||||
* irq_spx_trampoline — save / call C handler / restore / ERET.
|
||||
*
|
||||
* Frame layout (672 bytes, 16-byte aligned; SP is guaranteed 16-aligned at
|
||||
* any point AAPCS64 code can be asynchronously interrupted, so no extra
|
||||
* alignment handling is needed around the initial SUB):
|
||||
*
|
||||
* 0..239 x0-x29 (15 STP pairs)
|
||||
* 240 x30
|
||||
* 248 ELR_ELx
|
||||
* 256 SPSR_ELx
|
||||
* 264 FPSR
|
||||
* 272 FPCR
|
||||
* 280 (8 bytes unused -- keeps the V-register block 16-aligned)
|
||||
* 288..415 v0-v7 (4 STP Q-pairs) -- AAPCS64 caller-saved
|
||||
* 416..671 v16-v31 (8 STP Q-pairs) -- AAPCS64 caller-saved
|
||||
*
|
||||
* v8-v15 are AAPCS64 callee-saved and are deliberately not here: the C
|
||||
* handler below is ordinary compiled code and preserves those itself if it
|
||||
* touches them, per the same ABI the save list is drawn from.
|
||||
* ------------------------------------------------------------------------- */
|
||||
.equ TF_X0, 0
|
||||
.equ TF_X2, 16
|
||||
.equ TF_X4, 32
|
||||
.equ TF_X6, 48
|
||||
.equ TF_X8, 64
|
||||
.equ TF_X10, 80
|
||||
.equ TF_X12, 96
|
||||
.equ TF_X14, 112
|
||||
.equ TF_X16, 128
|
||||
.equ TF_X18, 144
|
||||
.equ TF_X20, 160
|
||||
.equ TF_X22, 176
|
||||
.equ TF_X24, 192
|
||||
.equ TF_X26, 208
|
||||
.equ TF_X28, 224
|
||||
.equ TF_X30, 240
|
||||
.equ TF_ELR, 248
|
||||
.equ TF_SPSR, 256
|
||||
.equ TF_FPSR, 264
|
||||
.equ TF_FPCR, 272
|
||||
.equ TF_V0, 288
|
||||
.equ TF_V2, 320
|
||||
.equ TF_V4, 352
|
||||
.equ TF_V6, 384
|
||||
.equ TF_V16, 416
|
||||
.equ TF_V18, 448
|
||||
.equ TF_V20, 480
|
||||
.equ TF_V22, 512
|
||||
.equ TF_V24, 544
|
||||
.equ TF_V26, 576
|
||||
.equ TF_V28, 608
|
||||
.equ TF_V30, 640
|
||||
.equ TF_SIZE, 672
|
||||
|
||||
.balign 4
|
||||
irq_spx_trampoline:
|
||||
sub sp, sp, #TF_SIZE
|
||||
|
||||
stp x0, x1, [sp, #TF_X0]
|
||||
stp x2, x3, [sp, #TF_X2]
|
||||
stp x4, x5, [sp, #TF_X4]
|
||||
stp x6, x7, [sp, #TF_X6]
|
||||
stp x8, x9, [sp, #TF_X8]
|
||||
stp x10, x11, [sp, #TF_X10]
|
||||
stp x12, x13, [sp, #TF_X12]
|
||||
stp x14, x15, [sp, #TF_X14]
|
||||
stp x16, x17, [sp, #TF_X16]
|
||||
stp x18, x19, [sp, #TF_X18]
|
||||
stp x20, x21, [sp, #TF_X20]
|
||||
stp x22, x23, [sp, #TF_X22]
|
||||
stp x24, x25, [sp, #TF_X24]
|
||||
stp x26, x27, [sp, #TF_X26]
|
||||
stp x28, x29, [sp, #TF_X28]
|
||||
str x30, [sp, #TF_X30]
|
||||
|
||||
/* Original x0-x30 are safe in memory now; free to use x0-x2 as scratch
|
||||
* for the rest of the save. EL-dependent register bank (B3): the two
|
||||
* forms are genuinely different MRS encodings, chosen from the flag
|
||||
* install_vectors set once, not re-derived per interrupt. */
|
||||
adrp x0, el2_mode_flag
|
||||
add x0, x0, :lo12:el2_mode_flag
|
||||
ldrb w0, [x0]
|
||||
cbnz w0, 1f
|
||||
|
||||
mrs x1, elr_el1
|
||||
mrs x2, spsr_el1
|
||||
b 2f
|
||||
1: mrs x1, elr_el2
|
||||
mrs x2, spsr_el2
|
||||
2: stp x1, x2, [sp, #TF_ELR]
|
||||
|
||||
/* FP/SIMD (B2): built without -mgeneral-regs-only, so the C handler may
|
||||
* use any of these. */
|
||||
mrs x1, fpsr
|
||||
mrs x2, fpcr
|
||||
stp x1, x2, [sp, #TF_FPSR]
|
||||
|
||||
stp q0, q1, [sp, #TF_V0]
|
||||
stp q2, q3, [sp, #TF_V2]
|
||||
stp q4, q5, [sp, #TF_V4]
|
||||
stp q6, q7, [sp, #TF_V6]
|
||||
stp q16, q17, [sp, #TF_V16]
|
||||
stp q18, q19, [sp, #TF_V18]
|
||||
stp q20, q21, [sp, #TF_V20]
|
||||
stp q22, q23, [sp, #TF_V22]
|
||||
stp q24, q25, [sp, #TF_V24]
|
||||
stp q26, q27, [sp, #TF_V26]
|
||||
stp q28, q29, [sp, #TF_V28]
|
||||
stp q30, q31, [sp, #TF_V30]
|
||||
|
||||
bl aarch64_irq_handler
|
||||
|
||||
ldp q0, q1, [sp, #TF_V0]
|
||||
ldp q2, q3, [sp, #TF_V2]
|
||||
ldp q4, q5, [sp, #TF_V4]
|
||||
ldp q6, q7, [sp, #TF_V6]
|
||||
ldp q16, q17, [sp, #TF_V16]
|
||||
ldp q18, q19, [sp, #TF_V18]
|
||||
ldp q20, q21, [sp, #TF_V20]
|
||||
ldp q22, q23, [sp, #TF_V22]
|
||||
ldp q24, q25, [sp, #TF_V24]
|
||||
ldp q26, q27, [sp, #TF_V26]
|
||||
ldp q28, q29, [sp, #TF_V28]
|
||||
ldp q30, q31, [sp, #TF_V30]
|
||||
|
||||
ldp x0, x1, [sp, #TF_FPSR]
|
||||
msr fpsr, x0
|
||||
msr fpcr, x1
|
||||
|
||||
/* x0-x30 are still their post-interrupt-entry values (untouched since
|
||||
* the initial save) except x0-x2, which the save sequence above used as
|
||||
* scratch -- all three are about to be overwritten by the final GP
|
||||
* restore below regardless, so clobbering them here is harmless. */
|
||||
ldp x0, x1, [sp, #TF_ELR] /* x0=ELR, x1=SPSR */
|
||||
adrp x2, el2_mode_flag
|
||||
add x2, x2, :lo12:el2_mode_flag
|
||||
ldrb w2, [x2]
|
||||
cbnz w2, 3f
|
||||
|
||||
msr elr_el1, x0
|
||||
msr spsr_el1, x1
|
||||
b 4f
|
||||
3: msr elr_el2, x0
|
||||
msr spsr_el2, x1
|
||||
4:
|
||||
|
||||
ldp x0, x1, [sp, #TF_X0]
|
||||
ldp x2, x3, [sp, #TF_X2]
|
||||
ldp x4, x5, [sp, #TF_X4]
|
||||
ldp x6, x7, [sp, #TF_X6]
|
||||
ldp x8, x9, [sp, #TF_X8]
|
||||
ldp x10, x11, [sp, #TF_X10]
|
||||
ldp x12, x13, [sp, #TF_X12]
|
||||
ldp x14, x15, [sp, #TF_X14]
|
||||
ldp x16, x17, [sp, #TF_X16]
|
||||
ldp x18, x19, [sp, #TF_X18]
|
||||
ldp x20, x21, [sp, #TF_X20]
|
||||
ldp x22, x23, [sp, #TF_X22]
|
||||
ldp x24, x25, [sp, #TF_X24]
|
||||
ldp x26, x27, [sp, #TF_X26]
|
||||
ldp x28, x29, [sp, #TF_X28]
|
||||
ldr x30, [sp, #TF_X30]
|
||||
|
||||
add sp, sp, #TF_SIZE
|
||||
eret
|
||||
|
||||
Reference in New Issue
Block a user