Skip to content

esp32s3: fix register-window corruption under interrupt load - #5600

Merged
deadprogram merged 1 commit into
devfrom
esp32s3-ble
Aug 23, 2026
Merged

esp32s3: fix register-window corruption under interrupt load#5600
deadprogram merged 1 commit into
devfrom
esp32s3-ble

Conversation

@deadprogram

Copy link
Copy Markdown
Member

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.

Needed for BLE support on esp32s3.

@github-actions

github-actions Bot commented Aug 20, 2026

Copy link
Copy Markdown

Size difference with the dev branch:

Binary size difference
 flash                          ram
 before   after   diff          before   after   diff
  19684   19684      0   0.00%    7052    7052      0   0.00% tinygo build -size short -o ./build/test.hex -target=feather-rp2040 ./examples/adafruit4650
  63228   63228      0   0.00%    6788    6788      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/adt7410/main.go
  10024   10024      0   0.00%    5348    5348      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/adxl345/main.go
  14628   14628      0   0.00%    7396    7396      0   0.00% tinygo build -size short -o ./build/test.hex -target=pybadge ./examples/amg88xx
  10184   10184      0   0.00%    5348    5348      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/apa102/main.go
  12592   12592      0   0.00%    7172    7172      0   0.00% tinygo build -size short -o ./build/test.hex -target=nano-33-ble ./examples/apds9960/proximity/main.go
  11136   11136      0   0.00%    5360    5360      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/apa102/itsybitsy-m0/main.go
   7588    7588      0   0.00%    2312    2312      0   0.00% tinygo build -size short -o ./build/test.hex -target=microbit ./examples/at24cx/main.go
   9272    9272      0   0.00%    5340    5340      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/bh1750/main.go
   8660    8660      0   0.00%    5340    5340      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/blinkm/main.go
  71408   71408      0   0.00%    3656    3656      0   0.00% tinygo build -size short -o ./build/test.hex -target=pinetime     ./examples/bma42x/main.go
  67120   67120      0   0.00%    6804    6804      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/bmi160/main.go
  28952   28952      0   0.00%    5380    5380      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/bmp180/main.go
  65720   65720      0   0.00%    6828    6828      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/bmp280/main.go
  13080   13080      0   0.00%    5412    5412      0   0.00% tinygo build -size short -o ./build/test.hex -target=trinket-m0 ./examples/bmp388/main.go
  24024   24024      0   0.00%    6220    6220      0   0.00% tinygo build -size short -o ./build/test.hex -target=metro-rp2350 ./examples/bno08x/i2c/main.go
   8008    8008      0   0.00%    3344    3344      0   0.00% tinygo build -size short -o ./build/test.hex -target=bluepill ./examples/ds1307/sram/main.go
  22116   22116      0   0.00%    3548    3548      0   0.00% tinygo build -size short -o ./build/test.hex -target=bluepill ./examples/ds1307/time/main.go
  30556   30556      0   0.00%    5576    5576      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/ds3231/alarms/main.go
  44492   44492      0   0.00%    5576    5576      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/ds3231/basic/main.go
   4592    4592      0   0.00%    2272    2272      0   0.00% tinygo build -size short -o ./build/test.hex -target=microbit ./examples/easystepper/main.go
  71884   71884      0   0.00%    7588    7588      0   0.00% tinygo build -size short -o ./build/test.hex -target=itsybitsy-m0 ./examples/flash/console/spi
  67996   67996      0   0.00%    9628    9628      0   0.00% tinygo build -size short -o ./build/test.hex -target=pyportal ./examples/flash/console/qspi
   7128    7128      0   0.00%    2276    2276      0   0.00% tinygo build -size short -o ./build/test.hex -target=microbit ./examples/gc9a01/main.go
 694072  694072      0   0.00%  131564  131564      0   0.00%

@deadprogram deadprogram added this to the 0.42.0 milestone Aug 20, 2026
@dkegel-fastly
dkegel-fastly requested a lite review from Copilot August 20, 2026 15:20
@dkegel-fastly

Copy link
Copy Markdown
Contributor

let's see what copilot makes of the assembly :-)

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens ESP32-S3 Xtensa windowed-ABI handling under heavy interrupt/task-switch load to prevent register-window corruption, which is required for stable BLE operation.

Changes:

  • Updates esp32s3.ld ROM symbol definitions for Bluetooth/clock/interrupt usage and avoids defining C3-style BT hook symbols on S3.
  • Adjusts the level-1 interrupt/exception handler to reserve extra stack space to avoid clobbering the windowed-ABI save area and adds a dedicated MOVSP/alloca exception handler.
  • Refines tinygo_swapTask to keep INTLEVEL masked across the stack switch and preserve the running frame’s WINDOWSTART bit.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
targets/esp32s3.ld Adds/organizes ESP32-S3 ROM symbols needed for BLE and removes problematic hook symbol definitions.
targets/esp32s3-interrupts.S Enlarges the exception frame to preserve the ABI save gap and adds MOVSP/alloca handling.
src/internal/task/task_stack_esp32.S Prevents window-save-area corruption during task stack switching by adjusting interrupt masking and WINDOWSTART.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/internal/task/task_stack_esp32.S Outdated
Comment on lines 69 to 73
@@ -72,10 +72,13 @@ tinygo_swapTask:
// below, after the stack switch is done. This prevents stale overflow
// when the new goroutine's calls rotate back into these panes.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

Comment thread src/internal/task/task_stack_esp32.S Outdated
Comment on lines +75 to +80
// Reload the saved PS, but do NOT restore it yet: everything from the stack
// switch below through the retw.n must stay masked. Once sp points at the
// new task's stack, the memory below sp is that task's save area -- the
// a0-a3 the retw.n is about to reload through a window underflow. An
// interrupt landing in that gap builds its frame there and overwrites them,
// and a garbage a0 makes the next retw an Illegal Instruction.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

Comment thread targets/esp32s3-interrupts.S Outdated
addi a0, a1, -96 // a0 = new frame pointer
// 64: SAR 68: EPC1 72: PS 76: WINDOWBASE 80: WINDOWSTART
//
// 128, not the 88 the layout needs: the 16 bytes below the interrupted SP

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

@deadprogram

Copy link
Copy Markdown
Member Author

@dkegel-fastly just updated to accomodate the feedback items.

@soypat

soypat commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Lots of comments with no explicit reference in them. Would you mind adding references? i.e: "we use ARM M9 Specification (https://arm.manuf.com/specm9) [...] as seen in section 3.9 blabla" and "we setup goroutine stack registers so they don't spill (See src/runtime/goroutine_stack.go). Else I have no way of checking validity without going through the same deep investigative process you've already done for this PR.

@deadprogram

Copy link
Copy Markdown
Member Author

Updated comments to reduce wordiness and add references. Very good call on that, @soypat

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm having a hard time finding this reference. the ISA ref manual I have comes from here:https://0x04.net/~mwk/doc/xtensa.pdf#page=622.08. There are not 253 subsubsections. Page 565 talks of XORB instruction which does not seem to be the same thing this references. Please provide a source for the edition of the manual you used

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:https://0x04.

Next time try the official docs maybe? I am not sure where that website obtained its PDF from

The ESP32 processor architecture (Xtensa) is from Tensilica (now called Cadence).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a link to the official ISA docs and updated the PR accordingly.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Official docs just don't appear in google 🤷 that's why I was asking where you were getting your info. We previously got our docs from this site (it's the site you just replaced in your most recent commit)

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>
@deadprogram

Copy link
Copy Markdown
Member Author

Thank you for review @soypat now merging.

@deadprogram
deadprogram merged commit 242f34d into dev Aug 23, 2026
26 checks passed
@deadprogram
deadprogram deleted the esp32s3-ble branch August 23, 2026 19:29
@jeremyvisser

Copy link
Copy Markdown

Even though the PR only mentions esp32s3, this also solved severe crash problems on esp32 which started in #5510 but is now stable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants