Stage 0: trap-frame parity across all 3 arches (FABRIC-3.md §XXVIII)
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
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
2a30212bd3
commit
15672ce17c
+86
@@ -3467,3 +3467,89 @@ project's known pattern of occasional riscv64/TCG timing flakiness (see the unex
|
|||||||
and aarch64 Stadium/COOL-scaling entries elsewhere in this doc), not assumed to be caused by
|
and aarch64 Stadium/COOL-scaling entries elsewhere in this doc), not assumed to be caused by
|
||||||
this session's changes given the sector mismatch, but also not confirmed benign.
|
this session's changes given the sector mismatch, but also not confirmed benign.
|
||||||
|
|
||||||
|
## XXVIII. Preemptive context switching between VMs -- staged plan approved, Stage 0 starting
|
||||||
|
(2026-09-13)
|
||||||
|
|
||||||
|
**New thread, deliberately scoped as a multi-session effort, not a single pass.** Bob wants
|
||||||
|
context switching between VMs -- preemptive (timer-interrupt-triggered), driven by a *new*
|
||||||
|
Compudynamics-style physics signal purpose-built for "who runs next" (not a repurposing of the
|
||||||
|
existing word-level execution-heat metric, and explicitly not a classic ready-queue/priority
|
||||||
|
scheduler), in scope for ANY live VM including dynamically-birthed WIREBIND per-identity VMs,
|
||||||
|
not just the fixed Tripod fleet. Full plan: `/home/rajames/.claude/plans/logical-snuggling-bear.md`
|
||||||
|
(the canonical reference; summarized here for the permanent record).
|
||||||
|
|
||||||
|
**The load-bearing discovery that reframed the whole task:** there is no per-VM native stack
|
||||||
|
anywhere in this codebase today. Every VM's execution -- including nested VM-EXEC dispatch into
|
||||||
|
another VM's own dictionary -- runs as plain recursive C on the *one shared kernel C stack*
|
||||||
|
(`vm_interpret()`, `vm_core.c:1043`; `execute_colon_word()`, `vm_core.c:685`; `g_log_attrib_vm`'s
|
||||||
|
save/restore, `vm_core.c:1039/1048-1049/1082`, exists precisely because of this shared-stack
|
||||||
|
nesting -- see §XXVII). Real preemption is therefore not "add an interrupt hook," it is "build
|
||||||
|
lightweight kernel threads," with the interrupt hook as the last, smallest piece. No
|
||||||
|
context-switch/coroutine/setjmp-longjmp primitive exists anywhere in `src/starkernel/` (confirmed
|
||||||
|
by exhaustive grep) -- this is greenfield. `VM-STEP` (`mama_forth_words.c:637-685`), despite being
|
||||||
|
documented as "the Compudynamics context-switch primitive," is purely cooperative, same-C-stack,
|
||||||
|
zero register/stack save -- not a usable building block, and its doc comment is not to be
|
||||||
|
inherited by the real primitive.
|
||||||
|
|
||||||
|
**Staged with explicit go/no-go gates, not attempted as one pass**, given the concrete UAF risk a
|
||||||
|
"suspend mid-execution" capability creates the moment it becomes real: Stage 0 (trap-frame parity
|
||||||
|
across all 3 arches -- amd64 already saves the full integer GPR set, aarch64/riscv64 currently
|
||||||
|
save only caller-saved regs per ABI convention and need extending, pure cost, zero new behavior)
|
||||||
|
-> Stage 1 (per-VM native stacks allocated at birth, following `arena.c`'s guard-page pattern,
|
||||||
|
nothing executes on them yet) -> Stage 2 (the actual save/restore switch primitive, cooperative
|
||||||
|
only, no timer, proven between Hera and one live Tripod VM -- this stage also closes a real,
|
||||||
|
already-confirmed unguarded-kill UAF: `mama_word_kill()`/`capsule_vm_kill()` today free any live
|
||||||
|
non-Hera VM unconditionally, with no check for "is there a live saved frame parked here") ->
|
||||||
|
Stage 3 (timer-driven preemption, Tripod fleet only, the new run-readiness signal built to the
|
||||||
|
same single-writer-mainline/single-reader-ISR discipline `heartbeat.c` already uses for
|
||||||
|
`heartbeat_next_period_ns()` under its own §21.1 ruling -- reusing that sanctioned pattern for a
|
||||||
|
second variable, not inventing new locking or overturning the ruling itself). **Stage 4
|
||||||
|
(extending preemption to WIREBIND identity VMs) is deliberately a ratified-decision-only step in
|
||||||
|
this plan, not implemented in the current pass** -- `capsule_wirebind_unclean_detach()`
|
||||||
|
(`capsule_wirebind.c:266-351`) tears a VM down on *asynchronous hardware device removal*, a path
|
||||||
|
under no FORTH-level control; if that VM were parked mid-instruction by the Stage 3 switcher when
|
||||||
|
a device gets physically yanked, today's teardown would free memory a suspended context still
|
||||||
|
points into. The fixed Tripod fleet has no equivalent async teardown path, which is exactly why
|
||||||
|
Stage 3 stops short of WIREBIND scope. Recommended resolution when Stage 4 is picked up: mark the
|
||||||
|
torn-down-while-switched-out VM as a tombstone rather than freeing it immediately, and let the
|
||||||
|
Stage 3 switcher itself perform the deferred free the next time it would have tried to resume
|
||||||
|
that VM -- keeping the async device path non-blocking while keeping "who frees a parked frame"
|
||||||
|
under the switcher's own control, not a hardware interrupt's.
|
||||||
|
|
||||||
|
**Also corrects one assumption from initial exploration:** `VM_STATE_STOPPED` (`capsule_run.h:90`)
|
||||||
|
is not unused as first thought -- `mama_word_start()` (`mama_forth_words.c:392-419`) sets it after
|
||||||
|
a VM's own `STOP` word cleanly unwinds its C stack back to `START`'s frame. STOPPED means "no live
|
||||||
|
native frame, safe to free," a cooperative top-level-boundary state -- it cannot represent "a live
|
||||||
|
saved frame parked mid-instruction," so Stage 2 introduces a genuinely new, distinct marker
|
||||||
|
instead of overloading it.
|
||||||
|
|
||||||
|
Each stage gets its own commit and its own full 3-architecture acceptance boot before the next
|
||||||
|
stage begins, per this project's usual discipline -- no bundling. Stage 0 starts now.
|
||||||
|
|
||||||
|
**Stage 0 CLOSED, same day.** Trap-frame parity across all 3 arches:
|
||||||
|
|
||||||
|
- **amd64** (`isr.S`, `isr_common_entry`): audit turned up a genuine, pre-existing correctness
|
||||||
|
gap, not just a future-preemption nicety -- confirmed live `double` arithmetic 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 existing ISR saved all 15 integer
|
||||||
|
GPRs but zero FP/SSE state; since every XMM register is caller-saved under x86-64 SysV and an
|
||||||
|
interrupt is not a call site the interrupted code's own instructions know about, nothing
|
||||||
|
previously guaranteed FP state survived a timer tick landing mid-computation. Closed with
|
||||||
|
FXSAVE/FXRSTOR, using `rbp` repurposed as a fixed anchor (its own true value already safe on
|
||||||
|
the stack, restored later by the ordinary `pop rbp`) so the 16-byte-aligned FXSAVE area can be
|
||||||
|
carved out of an unpredictably-aligned `rsp` without disturbing any of the existing
|
||||||
|
`[rsp+N]`-relative argument reconstruction, which becomes `[rbp+N]`-relative instead.
|
||||||
|
- **aarch64** (`isr.S`, `irq_spx_trampoline`): extended the 672-byte trap frame to 800 bytes,
|
||||||
|
adding v8-v15 (previously excluded as AAPCS64 callee-saved, on the same call-site-only
|
||||||
|
reasoning the amd64 gap shared).
|
||||||
|
- **riscv64** (`isr.S`, `riscv64_trap_entry`): extended the 320-byte trap frame to 512 bytes,
|
||||||
|
adding integer s0-s11 and FP fs0-fs11 (the latter still correctly gated behind the existing
|
||||||
|
`sstatus.FS != Off` check, alongside the pre-existing caller-saved FP half).
|
||||||
|
|
||||||
|
All three still boot to `ok>` clean -- amd64 through hundreds of timer ticks with FXSAVE/FXRSTOR
|
||||||
|
live on every single interrupt (not just the timer), aarch64 through 987 ticks with the extended
|
||||||
|
v8-v15 save/restore, riscv64 clean on the FS-conditional block now carrying 24 more registers.
|
||||||
|
No FORTH-visible behavior changed, as intended -- this stage was purely "make the trap frame
|
||||||
|
complete," nothing yet reads or writes any of it beyond the ISR's own entry/exit. Stage 1 (per-VM
|
||||||
|
native stacks) is next.
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Capsule Block Manifest — Auto-generated
|
# Capsule Block Manifest — Auto-generated
|
||||||
<!-- Generated by mkcapsule --manifest 2026-09-13T12:53:27Z -->
|
<!-- Generated by mkcapsule --manifest 2026-09-13T13:21:10Z -->
|
||||||
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
|
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
|
||||||
<!-- Hand-written justifications and immutability notes live -->
|
<!-- Hand-written justifications and immutability notes live -->
|
||||||
<!-- in MANIFEST.md alongside this auto-generated index. -->
|
<!-- in MANIFEST.md alongside this auto-generated index. -->
|
||||||
|
|||||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -148,7 +148,7 @@ aarch64_install_vectors:
|
|||||||
/* ---------------------------------------------------------------------------
|
/* ---------------------------------------------------------------------------
|
||||||
* irq_spx_trampoline — save / call C handler / restore / ERET.
|
* irq_spx_trampoline — save / call C handler / restore / ERET.
|
||||||
*
|
*
|
||||||
* Frame layout (672 bytes, 16-byte aligned; SP is guaranteed 16-aligned at
|
* Frame layout (800 bytes, 16-byte aligned; SP is guaranteed 16-aligned at
|
||||||
* any point AAPCS64 code can be asynchronously interrupted, so no extra
|
* any point AAPCS64 code can be asynchronously interrupted, so no extra
|
||||||
* alignment handling is needed around the initial SUB):
|
* alignment handling is needed around the initial SUB):
|
||||||
*
|
*
|
||||||
@@ -160,11 +160,20 @@ aarch64_install_vectors:
|
|||||||
* 272 FPCR
|
* 272 FPCR
|
||||||
* 280 (8 bytes unused -- keeps the V-register block 16-aligned)
|
* 280 (8 bytes unused -- keeps the V-register block 16-aligned)
|
||||||
* 288..415 v0-v7 (4 STP Q-pairs) -- AAPCS64 caller-saved
|
* 288..415 v0-v7 (4 STP Q-pairs) -- AAPCS64 caller-saved
|
||||||
* 416..671 v16-v31 (8 STP Q-pairs) -- AAPCS64 caller-saved
|
* 416..543 v8-v15 (4 STP Q-pairs) -- AAPCS64 callee-saved
|
||||||
|
* 544..799 v16-v31 (8 STP Q-pairs) -- AAPCS64 caller-saved
|
||||||
*
|
*
|
||||||
* v8-v15 are AAPCS64 callee-saved and are deliberately not here: the C
|
* FABRIC-3.md SXXVIII (Stage 0, preemptive-context-switch trap-frame parity,
|
||||||
* handler below is ordinary compiled code and preserves those itself if it
|
* 2026-09-13): v8-v15 now saved too. They are AAPCS64 callee-saved, so
|
||||||
* touches them, per the same ABI the save list is drawn from.
|
* ordinary compiled code (the C handler below, or anything else called
|
||||||
|
* through a normal ABI boundary) already preserves them across a *call* --
|
||||||
|
* but an interrupt is not a call the interrupted code's own compiled
|
||||||
|
* instructions know about. A value the interrupted function is genuinely
|
||||||
|
* live-using in v8-v15 at the moment of interrupt has no ABI guarantee of
|
||||||
|
* surviving an async trap at all; only a frame that saves everything makes
|
||||||
|
* "resume execution anywhere it was taken" actually true. This trap frame
|
||||||
|
* is not yet copied into any per-VM saved context (that's Stage 2) -- this
|
||||||
|
* stage only makes the frame itself complete.
|
||||||
* ------------------------------------------------------------------------- */
|
* ------------------------------------------------------------------------- */
|
||||||
.equ TF_X0, 0
|
.equ TF_X0, 0
|
||||||
.equ TF_X2, 16
|
.equ TF_X2, 16
|
||||||
@@ -190,15 +199,19 @@ aarch64_install_vectors:
|
|||||||
.equ TF_V2, 320
|
.equ TF_V2, 320
|
||||||
.equ TF_V4, 352
|
.equ TF_V4, 352
|
||||||
.equ TF_V6, 384
|
.equ TF_V6, 384
|
||||||
.equ TF_V16, 416
|
.equ TF_V8, 416
|
||||||
.equ TF_V18, 448
|
.equ TF_V10, 448
|
||||||
.equ TF_V20, 480
|
.equ TF_V12, 480
|
||||||
.equ TF_V22, 512
|
.equ TF_V14, 512
|
||||||
.equ TF_V24, 544
|
.equ TF_V16, 544
|
||||||
.equ TF_V26, 576
|
.equ TF_V18, 576
|
||||||
.equ TF_V28, 608
|
.equ TF_V20, 608
|
||||||
.equ TF_V30, 640
|
.equ TF_V22, 640
|
||||||
.equ TF_SIZE, 672
|
.equ TF_V24, 672
|
||||||
|
.equ TF_V26, 704
|
||||||
|
.equ TF_V28, 736
|
||||||
|
.equ TF_V30, 768
|
||||||
|
.equ TF_SIZE, 800
|
||||||
|
|
||||||
.balign 4
|
.balign 4
|
||||||
irq_spx_trampoline:
|
irq_spx_trampoline:
|
||||||
@@ -247,6 +260,10 @@ irq_spx_trampoline:
|
|||||||
stp q2, q3, [sp, #TF_V2]
|
stp q2, q3, [sp, #TF_V2]
|
||||||
stp q4, q5, [sp, #TF_V4]
|
stp q4, q5, [sp, #TF_V4]
|
||||||
stp q6, q7, [sp, #TF_V6]
|
stp q6, q7, [sp, #TF_V6]
|
||||||
|
stp q8, q9, [sp, #TF_V8]
|
||||||
|
stp q10, q11, [sp, #TF_V10]
|
||||||
|
stp q12, q13, [sp, #TF_V12]
|
||||||
|
stp q14, q15, [sp, #TF_V14]
|
||||||
stp q16, q17, [sp, #TF_V16]
|
stp q16, q17, [sp, #TF_V16]
|
||||||
stp q18, q19, [sp, #TF_V18]
|
stp q18, q19, [sp, #TF_V18]
|
||||||
stp q20, q21, [sp, #TF_V20]
|
stp q20, q21, [sp, #TF_V20]
|
||||||
@@ -262,6 +279,10 @@ irq_spx_trampoline:
|
|||||||
ldp q2, q3, [sp, #TF_V2]
|
ldp q2, q3, [sp, #TF_V2]
|
||||||
ldp q4, q5, [sp, #TF_V4]
|
ldp q4, q5, [sp, #TF_V4]
|
||||||
ldp q6, q7, [sp, #TF_V6]
|
ldp q6, q7, [sp, #TF_V6]
|
||||||
|
ldp q8, q9, [sp, #TF_V8]
|
||||||
|
ldp q10, q11, [sp, #TF_V10]
|
||||||
|
ldp q12, q13, [sp, #TF_V12]
|
||||||
|
ldp q14, q15, [sp, #TF_V14]
|
||||||
ldp q16, q17, [sp, #TF_V16]
|
ldp q16, q17, [sp, #TF_V16]
|
||||||
ldp q18, q19, [sp, #TF_V18]
|
ldp q18, q19, [sp, #TF_V18]
|
||||||
ldp q20, q21, [sp, #TF_V20]
|
ldp q20, q21, [sp, #TF_V20]
|
||||||
|
|||||||
@@ -75,20 +75,48 @@ isr_common_entry:
|
|||||||
* [rsp+152] = rflags
|
* [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) */
|
/* Set up arguments for isr_common_handler(vector, error, rip, cs, rflags, cr2) */
|
||||||
mov rdi, [rsp + 120] /* vector */
|
mov rdi, [rbp + 120] /* vector */
|
||||||
mov rsi, [rsp + 128] /* error */
|
mov rsi, [rbp + 128] /* error */
|
||||||
mov rdx, [rsp + 136] /* rip */
|
mov rdx, [rbp + 136] /* rip */
|
||||||
mov rcx, [rsp + 144] /* cs */
|
mov rcx, [rbp + 144] /* cs */
|
||||||
mov r8, [rsp + 152] /* rflags */
|
mov r8, [rbp + 152] /* rflags */
|
||||||
mov r9, cr2 /* cr2 */
|
mov r9, cr2 /* cr2 */
|
||||||
|
|
||||||
/* Pass stack frame pointer (after pushes) as 7th argument */
|
/* Pass stack frame pointer (after pushes) as 7th argument */
|
||||||
mov rax, rsp
|
push rbp
|
||||||
push rax
|
|
||||||
call isr_common_handler
|
call isr_common_handler
|
||||||
add rsp, 8
|
add rsp, 8
|
||||||
|
|
||||||
|
fxrstor [rsp]
|
||||||
|
mov rsp, rbp /* collapse back to right after the GPR pushes */
|
||||||
|
|
||||||
/* Restore general-purpose registers */
|
/* Restore general-purpose registers */
|
||||||
pop r15
|
pop r15
|
||||||
pop r14
|
pop r14
|
||||||
|
|||||||
@@ -16,22 +16,33 @@
|
|||||||
.hidden riscv64_install_vectors
|
.hidden riscv64_install_vectors
|
||||||
|
|
||||||
/* ---------------------------------------------------------------------------
|
/* ---------------------------------------------------------------------------
|
||||||
* Trap frame layout (offsets from sp), 320 bytes, 16-byte aligned.
|
* Trap frame layout (offsets from sp), 512 bytes, 16-byte aligned.
|
||||||
*
|
*
|
||||||
* Only the LP64D psABI's *caller-saved* registers are saved: a C handler
|
* FABRIC-3.md SXXVIII (Stage 0, preemptive-context-switch trap-frame parity,
|
||||||
* preserves callee-saved registers itself, and the interrupted code already
|
* 2026-09-13): originally only the LP64D psABI's *caller-saved* registers
|
||||||
* expects caller-saved registers to be clobbered across a call.
|
* were saved here, on the reasoning that a C handler preserves callee-saved
|
||||||
|
* registers itself. That reasoning holds for an ordinary function call, but
|
||||||
|
* not for an asynchronous trap: the interrupted code's own compiled
|
||||||
|
* instructions have no call site to justify losing state at, so a value
|
||||||
|
* genuinely live in a callee-saved register at the moment of interrupt has
|
||||||
|
* no ABI guarantee of surviving. s0-s11/fs0-fs11 are now saved too, so this
|
||||||
|
* frame is, in principle, sufficient to resume execution anywhere it was
|
||||||
|
* taken. Not yet copied into any per-VM saved context (that's Stage 2) --
|
||||||
|
* this stage only makes the frame itself complete.
|
||||||
*
|
*
|
||||||
* integer caller-saved (16): ra, t0-t6, a0-a7
|
* integer caller-saved (16): ra, t0-t6, a0-a7
|
||||||
|
* integer callee-saved (12): s0-s11
|
||||||
* FP caller-saved (20): ft0-ft11, fa0-fa7 + fcsr
|
* FP caller-saved (20): ft0-ft11, fa0-fa7 + fcsr
|
||||||
|
* FP callee-saved (12): fs0-fs11
|
||||||
* supervisor CSRs (2): sepc, sstatus
|
* supervisor CSRs (2): sepc, sstatus
|
||||||
*
|
*
|
||||||
* The FP half is saved only when sstatus.FS != Off. Nothing in boot.S or
|
* The FP half (caller- and callee-saved alike) is saved only when
|
||||||
* kernel_entry.S programs FS, so its state is whatever firmware left; touching
|
* sstatus.FS != Off. Nothing in boot.S or kernel_entry.S programs FS, so its
|
||||||
* an f-register while FS == Off raises an illegal-instruction trap, which
|
* state is whatever firmware left; touching an f-register while FS == Off
|
||||||
* inside the trap handler would be unrecoverable. The kernel image does
|
* raises an illegal-instruction trap, which inside the trap handler would be
|
||||||
* contain real FP code (fld/fmul.d/fcvt on the Q48.16 and statistics paths),
|
* unrecoverable. The kernel image does contain real FP code (fld/fmul.d/fcvt
|
||||||
* so the save cannot simply be omitted -- it has to be conditional.
|
* on the Q48.16 and statistics paths), so the save cannot simply be omitted
|
||||||
|
* -- it has to be conditional.
|
||||||
* ------------------------------------------------------------------------- */
|
* ------------------------------------------------------------------------- */
|
||||||
.equ TF_RA, 0
|
.equ TF_RA, 0
|
||||||
.equ TF_T0, 8
|
.equ TF_T0, 8
|
||||||
@@ -49,13 +60,15 @@
|
|||||||
.equ TF_A5, 104
|
.equ TF_A5, 104
|
||||||
.equ TF_A6, 112
|
.equ TF_A6, 112
|
||||||
.equ TF_A7, 120
|
.equ TF_A7, 120
|
||||||
.equ TF_SEPC, 128
|
.equ TF_S0, 128 /* s0-s11 : 128 .. 216 */
|
||||||
.equ TF_SSTATUS,136
|
.equ TF_SEPC, 224
|
||||||
.equ TF_FCSR, 144
|
.equ TF_SSTATUS,232
|
||||||
.equ TF_F0, 152 /* ft0-ft7 : 152 .. 208 */
|
.equ TF_FCSR, 240
|
||||||
.equ TF_FA0, 216 /* fa0-fa7 : 216 .. 272 */
|
.equ TF_F0, 248 /* ft0-ft7 : 248 .. 304 */
|
||||||
.equ TF_FT8, 280 /* ft8-ft11 : 280 .. 304 */
|
.equ TF_FA0, 312 /* fa0-fa7 : 312 .. 368 */
|
||||||
.equ TF_SIZE, 320
|
.equ TF_FT8, 376 /* ft8-ft11 : 376 .. 400 */
|
||||||
|
.equ TF_FS0, 408 /* fs0-fs11 : 408 .. 496 */
|
||||||
|
.equ TF_SIZE, 512
|
||||||
|
|
||||||
/* sstatus.FS occupies bits [14:13]; 0 = Off */
|
/* sstatus.FS occupies bits [14:13]; 0 = Off */
|
||||||
.equ SSTATUS_FS_SHIFT, 13
|
.equ SSTATUS_FS_SHIFT, 13
|
||||||
@@ -85,6 +98,19 @@ riscv64_trap_entry:
|
|||||||
sd a6, TF_A6(sp)
|
sd a6, TF_A6(sp)
|
||||||
sd a7, TF_A7(sp)
|
sd a7, TF_A7(sp)
|
||||||
|
|
||||||
|
sd s0, TF_S0+0(sp)
|
||||||
|
sd s1, TF_S0+8(sp)
|
||||||
|
sd s2, TF_S0+16(sp)
|
||||||
|
sd s3, TF_S0+24(sp)
|
||||||
|
sd s4, TF_S0+32(sp)
|
||||||
|
sd s5, TF_S0+40(sp)
|
||||||
|
sd s6, TF_S0+48(sp)
|
||||||
|
sd s7, TF_S0+56(sp)
|
||||||
|
sd s8, TF_S0+64(sp)
|
||||||
|
sd s9, TF_S0+72(sp)
|
||||||
|
sd s10, TF_S0+80(sp)
|
||||||
|
sd s11, TF_S0+88(sp)
|
||||||
|
|
||||||
csrr t0, sepc
|
csrr t0, sepc
|
||||||
sd t0, TF_SEPC(sp)
|
sd t0, TF_SEPC(sp)
|
||||||
csrr t0, sstatus
|
csrr t0, sstatus
|
||||||
@@ -117,6 +143,18 @@ riscv64_trap_entry:
|
|||||||
fsd ft9, TF_FT8+8(sp)
|
fsd ft9, TF_FT8+8(sp)
|
||||||
fsd ft10, TF_FT8+16(sp)
|
fsd ft10, TF_FT8+16(sp)
|
||||||
fsd ft11, TF_FT8+24(sp)
|
fsd ft11, TF_FT8+24(sp)
|
||||||
|
fsd fs0, TF_FS0+0(sp)
|
||||||
|
fsd fs1, TF_FS0+8(sp)
|
||||||
|
fsd fs2, TF_FS0+16(sp)
|
||||||
|
fsd fs3, TF_FS0+24(sp)
|
||||||
|
fsd fs4, TF_FS0+32(sp)
|
||||||
|
fsd fs5, TF_FS0+40(sp)
|
||||||
|
fsd fs6, TF_FS0+48(sp)
|
||||||
|
fsd fs7, TF_FS0+56(sp)
|
||||||
|
fsd fs8, TF_FS0+64(sp)
|
||||||
|
fsd fs9, TF_FS0+72(sp)
|
||||||
|
fsd fs10, TF_FS0+80(sp)
|
||||||
|
fsd fs11, TF_FS0+88(sp)
|
||||||
1:
|
1:
|
||||||
|
|
||||||
/* scause bit 63 set => interrupt (negative when read as signed). */
|
/* scause bit 63 set => interrupt (negative when read as signed). */
|
||||||
@@ -161,6 +199,18 @@ riscv64_trap_entry:
|
|||||||
fld ft9, TF_FT8+8(sp)
|
fld ft9, TF_FT8+8(sp)
|
||||||
fld ft10, TF_FT8+16(sp)
|
fld ft10, TF_FT8+16(sp)
|
||||||
fld ft11, TF_FT8+24(sp)
|
fld ft11, TF_FT8+24(sp)
|
||||||
|
fld fs0, TF_FS0+0(sp)
|
||||||
|
fld fs1, TF_FS0+8(sp)
|
||||||
|
fld fs2, TF_FS0+16(sp)
|
||||||
|
fld fs3, TF_FS0+24(sp)
|
||||||
|
fld fs4, TF_FS0+32(sp)
|
||||||
|
fld fs5, TF_FS0+40(sp)
|
||||||
|
fld fs6, TF_FS0+48(sp)
|
||||||
|
fld fs7, TF_FS0+56(sp)
|
||||||
|
fld fs8, TF_FS0+64(sp)
|
||||||
|
fld fs9, TF_FS0+72(sp)
|
||||||
|
fld fs10, TF_FS0+80(sp)
|
||||||
|
fld fs11, TF_FS0+88(sp)
|
||||||
3:
|
3:
|
||||||
csrw sstatus, t0
|
csrw sstatus, t0
|
||||||
ld t0, TF_SEPC(sp)
|
ld t0, TF_SEPC(sp)
|
||||||
@@ -183,6 +233,19 @@ riscv64_trap_entry:
|
|||||||
ld a6, TF_A6(sp)
|
ld a6, TF_A6(sp)
|
||||||
ld a7, TF_A7(sp)
|
ld a7, TF_A7(sp)
|
||||||
|
|
||||||
|
ld s0, TF_S0+0(sp)
|
||||||
|
ld s1, TF_S0+8(sp)
|
||||||
|
ld s2, TF_S0+16(sp)
|
||||||
|
ld s3, TF_S0+24(sp)
|
||||||
|
ld s4, TF_S0+32(sp)
|
||||||
|
ld s5, TF_S0+40(sp)
|
||||||
|
ld s6, TF_S0+48(sp)
|
||||||
|
ld s7, TF_S0+56(sp)
|
||||||
|
ld s8, TF_S0+64(sp)
|
||||||
|
ld s9, TF_S0+72(sp)
|
||||||
|
ld s10, TF_S0+80(sp)
|
||||||
|
ld s11, TF_S0+88(sp)
|
||||||
|
|
||||||
addi sp, sp, TF_SIZE
|
addi sp, sp, TF_SIZE
|
||||||
sret
|
sret
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user