aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--src/device/arm/cortexm.s21
-rw-r--r--src/runtime/runtime_cortexm.go50
-rw-r--r--targets/cortex-m.json3
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"
}