runtime: improve hardfault handler and stack reporting on Cortex-M

- Add reference to the current goroutine stack (PSP) when showing the hardfault info on Cortex-M.
This commit is contained in:
Konstantin Sharlaimov
2026-05-10 19:57:18 +02:00
committed by Ron Evans
parent 7cbbfd6fc5
commit 8e36d2758b
5 changed files with 39 additions and 1 deletions
+1 -1
View File
@@ -44,7 +44,7 @@ func TestBinarySize(t *testing.T) {
// microcontrollers // microcontrollers
{"hifive1b", "examples/echo", 3817, 299, 0, 2252}, {"hifive1b", "examples/echo", 3817, 299, 0, 2252},
{"microbit", "examples/serial", 2820, 356, 8, 2248}, {"microbit", "examples/serial", 2820, 356, 8, 2248},
{"wioterminal", "examples/pininterrupt", 7206, 1510, 120, 7248}, {"wioterminal", "examples/pininterrupt", 7286, 1534, 120, 7248},
// TODO: also check wasm. Right now this is difficult, because // TODO: also check wasm. Right now this is difficult, because
// wasm binaries are run through wasm-opt and therefore the // wasm binaries are run through wasm-opt and therefore the
+5
View File
@@ -42,3 +42,8 @@ func SystemStack() uintptr {
runtimePanic("scheduler is disabled") runtimePanic("scheduler is disabled")
return 0 // unreachable return 0 // unreachable
} }
func GoroutineStack() uintptr {
// No separate goroutine stack without a scheduler.
return 0
}
+11
View File
@@ -11,3 +11,14 @@ uintptr_t SystemStack() {
); );
return sp; return sp;
} }
uintptr_t GoroutineStack() {
uintptr_t sp;
asm volatile(
"mrs %0, PSP"
: "=r"(sp)
:
: "memory"
);
return sp;
}
+7
View File
@@ -70,3 +70,10 @@ func (s *state) pause() {
// //
//export SystemStack //export SystemStack
func SystemStack() uintptr func SystemStack() uintptr
// GoroutineStack returns the PSP (goroutine stack pointer). When a fault
// occurs in goroutine context, the hardware saves the exception frame to PSP;
// reading PSP+0x18 gives the actual faulting PC.
//
//export GoroutineStack
func GoroutineStack() uintptr
@@ -4,6 +4,7 @@ package runtime
import ( import (
"device/arm" "device/arm"
"internal/task"
"unsafe" "unsafe"
) )
@@ -106,6 +107,20 @@ func HardFault_Handler() {
print(" pc=", sp.PC) print(" pc=", sp.PC)
} }
} }
// PSP holds the actual exception frame when the fault occurred in a
// goroutine (thread mode, PSP active). The MSP-based sp above is the
// scheduler's stack and its PC is garbage in that case.
pspVal := task.GoroutineStack()
// heapEndSymbol is defined in baremetal.go; we also ensure that the goroutine exception frame is within addressable memory.
if pspVal >= 0x20000000 && pspVal < (uintptr(unsafe.Pointer(&heapEndSymbol))-uintptr(unsafe.Sizeof(interruptStack{}))) {
pspFrame := (*interruptStack)(unsafe.Pointer(pspVal))
print(" psp=", pspFrame)
print(" psp_pc=", pspFrame.PC)
print(" psp_lr=", pspFrame.LR)
}
println() println()
abort() abort()
} }