diff options
-rw-r--r-- | src/device/arm/cortexm.s | 21 | ||||
-rw-r--r-- | src/runtime/runtime_cortexm.go | 50 | ||||
-rw-r--r-- | targets/cortex-m.json | 3 |
3 files changed, 74 insertions, 0 deletions
diff --git a/src/device/arm/cortexm.s b/src/device/arm/cortexm.s new file mode 100644 index 000000000..29d6a2691 --- /dev/null +++ b/src/device/arm/cortexm.s @@ -0,0 +1,21 @@ +.syntax unified + +.section .text.HardFault_Handler +.global HardFault_Handler +.type HardFault_Handler, %function +HardFault_Handler: + // Put the old stack pointer in the first argument, for easy debugging. This + // is especially useful on Cortex-M0, which supports far fewer debug + // facilities. + mov r0, sp + + // Load the default stack pointer from address 0 so that we can call normal + // functions again that expect a working stack. However, it will corrupt the + // old stack so the function below must not attempt to recover from this + // fault. + movs r3, #0 + ldr r3, [r3] + mov sp, r3 + + // Continue handling this error in Go. + bl handleHardFault diff --git a/src/runtime/runtime_cortexm.go b/src/runtime/runtime_cortexm.go index 4a7c1244a..909206e97 100644 --- a/src/runtime/runtime_cortexm.go +++ b/src/runtime/runtime_cortexm.go @@ -41,11 +41,61 @@ func preinit() { } func abort() { + // disable all interrupts + arm.DisableInterrupts() + + // lock up forever for { arm.Asm("wfi") } } +// The stack layout at the moment an interrupt occurs. +// Registers can be accessed if the stack pointer is cast to a pointer to this +// struct. +type interruptStack struct { + R0 uintptr + R1 uintptr + R2 uintptr + R3 uintptr + R12 uintptr + LR uintptr + PC uintptr + PSR uintptr +} + +// This function is called at HardFault. +// Before this function is called, the stack pointer is reset to the initial +// stack pointer (loaded from addres 0x0) and the previous stack pointer is +// passed as an argument to this function. This allows for easy inspection of +// the stack the moment a HardFault occurs, but it means that the stack will be +// corrupted by this function and thus this handler must not attempt to recover. +// +// For details, see: +// https://community.arm.com/developer/ip-products/system/f/embedded-forum/3257/debugging-a-cortex-m0-hard-fault +// https://blog.feabhas.com/2013/02/developing-a-generic-hard-fault-handler-for-arm-cortex-m3cortex-m4/ +//go:export handleHardFault +func handleHardFault(sp *interruptStack) { + print("fatal error: ") + if uintptr(unsafe.Pointer(sp)) < 0x20000000 { + print("stack overflow") + } else { + // TODO: try to find the cause of the hard fault. Especially on + // Cortex-M3 and higher it is possible to find more detailed information + // in special status registers. + print("HardFault") + } + print(" with sp=", sp) + if uintptr(unsafe.Pointer(&sp.PC)) >= 0x20000000 { + // Only print the PC if it points into memory. + // It may not point into memory during a stack overflow, so check that + // first before accessing the stack. + print(" pc=", sp.PC) + } + println() + abort() +} + // Implement memset for LLVM and compiler-rt. //go:export memset func libc_memset(ptr unsafe.Pointer, c byte, size uintptr) { diff --git a/targets/cortex-m.json b/targets/cortex-m.json index e9db29788..a8e8f612a 100644 --- a/targets/cortex-m.json +++ b/targets/cortex-m.json @@ -19,5 +19,8 @@ "ldflags": [ "--gc-sections" ], + "extra-files": [ + "src/device/arm/cortexm.s" + ], "gdb": "arm-none-eabi-gdb" } |