esp32s3: fix register-window corruption under interrupt load

Remove the C3 bluetooth hook addresses from esp32s3.ld (on the S3 they
point into the ROM md5/crc thunk table, and being bare assignments they
also shadowed the blob's own definitions), keep the interrupt frame clear
of the 16-byte windowed-ABI save area below SP, and make tinygo_swapTask
hold INTLEVEL across the stack switch while keeping the running frame's
WINDOWSTART bit set.

Signed-off-by: deadprogram <ron@hybridgroup.com>
This commit is contained in:
deadprogram
2026-08-20 09:02:28 +02:00
committed by Ron Evans
parent f71b63053c
commit 242f34d71a
4 changed files with 1089 additions and 21 deletions
+19 -14
View File
@@ -1,5 +1,8 @@
//go:build tinygo
// See Xtensa ISA Reference Manual for details.
// https://www.cadence.com/content/dam/cadence-www/global/en_US/documents/tools/silicon-solutions/compute-ip/isa-summary.pdf
.section .text.tinygo_startTask,"ax",@progbits
.global tinygo_startTask
.type tinygo_startTask, %function
@@ -68,14 +71,12 @@ tinygo_swapTask:
// After the recursive spill returns, the physical register file still
// has WindowStart bits set for the spill helper frames.
// We will clear WindowStart completely (to 0) right before the retw.n
// below, after the stack switch is done. This prevents stale overflow
// when the new goroutine's calls rotate back into these panes.
// These are dropped below, once the stack switch is done.
// Restore interrupts.
l32i a4, sp, 4 // reload saved PS
wsr.ps a4
rsync
// Mask interrupts until WindowStart matches the new stack, or one lands in
// the gap and clobbers the a0-a3 that retw.n reloads.
// Xtensa ISA Reference Manual, RETW, section 8.3.253, p.565.
l32i a4, sp, 4 // reload saved PS, restored below
// At this point, the following is true:
// WindowStart == 1 << WindowBase
@@ -97,16 +98,20 @@ tinygo_swapTask:
// register also stores the parent register window.
l32i.n a0, sp, 0
// Clear ALL WindowStart bits. With all windows spilled to the stack,
// we must ensure no stale WS bits remain: the retw.n below will trigger
// underflow4 to load the new goroutine's registers from the new stack
// (which sets the appropriate WS bit via rfwu). Any stale WS bits
// (from spill helpers or the old goroutine) would cause spurious
// overflows of garbage register values into memory.
movi a5, 0
// Drop every WindowStart bit but this window's: stale bits overflow garbage,
// and an all-zero WindowStart marks the running frame dead.
// Xtensa ISA Reference Manual, WINDOWSTART (SR 73), Table 5-148.
rsr a6, WINDOWBASE
movi a5, 1
ssl a6
sll a5, a5 // a5 = 1 << WindowBase
wsr a5, WINDOWSTART
rsync
// Window state now matches the new stack, so unmask interrupts.
wsr.ps a4
rsync
// Return into the new stack. This instruction will trigger a window
// underflow, reloading the saved registers from the stack.
retw.n
+1 -1
View File
@@ -9,7 +9,7 @@ package task
// a7: stack frame pointer (optional, normally unused in TinyGo)
// Sources:
// http://cholla.mmto.org/esp8266/xtensa.html
// https://0x04.net/~mwk/doc/xtensa.pdf
// https://www.cadence.com/content/dam/cadence-www/global/en_US/documents/tools/silicon-solutions/compute-ip/isa-summary.pdf
import (
"unsafe"