Implement custom abort and fault handler for debugging

Этот коммит содержится в:
Ethan Reesor 2020-06-25 22:00:30 -05:00 коммит произвёл Ron Evans
родитель 4750635a20
коммит 04d097f4ea
5 изменённых файлов: 390 добавлений и 43 удалений

Просмотреть файл

@ -3,7 +3,6 @@
package runtime
import (
"device/arm"
"unsafe"
)
@ -40,16 +39,6 @@ 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.
@ -63,35 +52,3 @@ type interruptStack struct {
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/
//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()
}

17
src/runtime/runtime_cortexm_abort.go Обычный файл
Просмотреть файл

@ -0,0 +1,17 @@
// +build cortexm,!nxp
package runtime
import (
"device/arm"
)
func abort() {
// disable all interrupts
arm.DisableInterrupts()
// lock up forever
for {
arm.Asm("wfi")
}
}

39
src/runtime/runtime_cortexm_hardfault.go Обычный файл
Просмотреть файл

@ -0,0 +1,39 @@
// +build cortexm,!nxp
package runtime
import (
"unsafe"
)
// 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/
//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()
}

Просмотреть файл

@ -0,0 +1,304 @@
// +build nxp
package runtime
import (
"device/nxp"
"unsafe"
)
const (
SystemControl_CFSR_KnownFault = nxp.SystemControl_CFSR_IACCVIOL | nxp.SystemControl_CFSR_DACCVIOL | nxp.SystemControl_CFSR_MUNSTKERR | nxp.SystemControl_CFSR_MSTKERR | nxp.SystemControl_CFSR_MLSPERR | nxp.SystemControl_CFSR_IBUSERR | nxp.SystemControl_CFSR_PRECISERR | nxp.SystemControl_CFSR_IMPRECISERR | nxp.SystemControl_CFSR_UNSTKERR | nxp.SystemControl_CFSR_STKERR | nxp.SystemControl_CFSR_LSPERR | nxp.SystemControl_CFSR_UNDEFINSTR | nxp.SystemControl_CFSR_INVSTATE | nxp.SystemControl_CFSR_INVPC | nxp.SystemControl_CFSR_NOCP | nxp.SystemControl_CFSR_UNALIGNED | nxp.SystemControl_CFSR_DIVBYZERO
)
// See runtime_cortexm_hardfault.go
//go:export handleHardFault
func handleHardFault(sp *interruptStack) {
fault := GetFaultStatus()
spValid := !fault.Bus().ImpreciseDataBusError()
print("fatal error: ")
if spValid && uintptr(unsafe.Pointer(sp)) < 0x20000000 {
print("stack overflow? ")
}
if fault.Mem().InstructionAccessViolation() {
print("instruction access violation")
}
if fault.Mem().DataAccessViolation() {
print("data access violation")
}
if fault.Mem().WhileUnstackingException() {
print(" while unstacking exception")
}
if fault.Mem().WileStackingException() {
print(" while stacking exception")
}
if fault.Mem().DuringFPLazyStatePres() {
print(" during floating-point lazy state preservation")
}
if fault.Bus().InstructionBusError() {
print("instruction bus error")
}
if fault.Bus().PreciseDataBusError() {
print("data bus error (precise)")
}
if fault.Bus().ImpreciseDataBusError() {
print("data bus error (imprecise)")
}
if fault.Bus().WhileUnstackingException() {
print(" while unstacking exception")
}
if fault.Bus().WhileStackingException() {
print(" while stacking exception")
}
if fault.Bus().DuringFPLazyStatePres() {
print(" during floating-point lazy state preservation")
}
if fault.Usage().UndefinedInstruction() {
print("undefined instruction")
}
if fault.Usage().IllegalUseOfEPSR() {
print("illegal use of the EPSR")
}
if fault.Usage().IllegalExceptionReturn() {
print("illegal load of EXC_RETURN to the PC")
}
if fault.Usage().AttemptedToAccessCoprocessor() {
print("coprocessor access violation")
}
if fault.Usage().UnalignedMemoryAccess() {
print("unaligned memory access")
}
if fault.Usage().DivideByZero() {
print("divide by zero")
}
if fault.Unknown() {
print("unknown hard fault")
}
if addr, ok := fault.Mem().Address(); ok {
print(" with fault address ", addr)
}
if addr, ok := fault.Bus().Address(); ok {
print(" with bus fault address ", addr)
}
if spValid {
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()
}
// Descriptions are sourced from the K66 SVD and
// http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0552a/Cihcfefj.html
// GetFaultStatus reads the System Control Block Configurable Fault Status
// Register and returns it as a FaultStatus.
func GetFaultStatus() FaultStatus { return FaultStatus(nxp.SystemControl.CFSR.Get()) }
type FaultStatus uint32
type MemFaultStatus uint32
type BusFaultStatus uint32
type UsageFaultStatus uint32
func (fs FaultStatus) Mem() MemFaultStatus { return MemFaultStatus(fs) }
func (fs FaultStatus) Bus() BusFaultStatus { return BusFaultStatus(fs) }
func (fs FaultStatus) Usage() UsageFaultStatus { return UsageFaultStatus(fs) }
// Unknown returns true if the cause of the fault is not know
func (fs FaultStatus) Unknown() bool { return fs&SystemControl_CFSR_KnownFault == 0 }
// InstructionAccessViolation: the processor attempted an instruction fetch from
// a location that does not permit execution
//
// "This fault occurs on any access to an XN region, even when the MPU is
// disabled or not present.
//
// When this bit is 1, the PC value stacked for the exception return points to
// the faulting instruction. The processor has not written a fault address to
// the MMAR."
func (fs MemFaultStatus) InstructionAccessViolation() bool {
return fs&nxp.SystemControl_CFSR_IACCVIOL != 0
}
// DataAccessViolation: the processor attempted a load or store at a location
// that does not permit the operation
//
// "When this bit is 1, the PC value stacked for the exception return points to
// the faulting instruction. The processor has loaded the MMAR with the address
// of the attempted access."
func (fs MemFaultStatus) DataAccessViolation() bool { return fs&nxp.SystemControl_CFSR_DACCVIOL != 0 }
// WhileUnstackingException: unstack for an exception return has caused one or
// more access violations
//
// "This fault is chained to the handler. This means that when this bit is 1,
// the original return stack is still present. The processor has not adjusted
// the SP from the failing return, and has not performed a new save. The
// processor has not written a fault address to the MMAR."
func (fs MemFaultStatus) WhileUnstackingException() bool {
return fs&nxp.SystemControl_CFSR_MUNSTKERR != 0
}
// WileStackingException: stacking for an exception entry has caused one or more
// access violations
//
// "When this bit is 1, the SP is still adjusted but the values in the context
// area on the stack might be incorrect. The processor has not written a fault
// address to the MMAR."
func (fs MemFaultStatus) WileStackingException() bool { return fs&nxp.SystemControl_CFSR_MSTKERR != 0 }
// DuringFPLazyStatePres: A MemManage fault occurred during floating-point lazy
// state preservation
func (fs MemFaultStatus) DuringFPLazyStatePres() bool { return fs&nxp.SystemControl_CFSR_MLSPERR != 0 }
// InstructionBusError: instruction bus error
//
// "The processor detects the instruction bus error on prefetching an
// instruction, but it sets the IBUSERR flag to 1 only if it attempts to issue
// the faulting instruction.
//
// When the processor sets this bit is 1, it does not write a fault address to
// the BFAR."
func (fs BusFaultStatus) InstructionBusError() bool { return fs&nxp.SystemControl_CFSR_IBUSERR != 0 }
// PreciseDataBusError: a data bus error has occurred, and the PC value stacked
// for the exception return points to the instruction that caused the fault
func (fs BusFaultStatus) PreciseDataBusError() bool { return fs&nxp.SystemControl_CFSR_PRECISERR != 0 }
// ImpreciseDataBusError: a data bus error has occurred, but the return address
// in the stack frame is not related to the instruction that caused the error
//
// "When the processor sets this bit to 1, it does not write a fault address to
// the BFAR.
//
// This is an asynchronous fault. Therefore, if it is detected when the priority
// of the current process is higher than the BusFault priority, the BusFault
// becomes pending and becomes active only when the processor returns from all
// higher priority processes. If a precise fault occurs before the processor
// enters the handler for the imprecise BusFault, the handler detects both
// IMPRECISERR set to 1 and one of the precise fault status bits set to 1."
func (fs BusFaultStatus) ImpreciseDataBusError() bool {
return fs&nxp.SystemControl_CFSR_IMPRECISERR != 0
}
// WhileUnstackingException: unstack for an exception return has caused one or
// more BusFaults
//
// "This fault is chained to the handler. This means that when the processor
// sets this bit to 1, the original return stack is still present. The processor
// does not adjust the SP from the failing return, does not performed a new
// save, and does not write a fault address to the BFAR."
func (fs BusFaultStatus) WhileUnstackingException() bool {
return fs&nxp.SystemControl_CFSR_UNSTKERR != 0
}
// WhileStackingException: stacking for an exception entry has caused one or
// more BusFaults
//
// "When the processor sets this bit to 1, the SP is still adjusted but the
// values in the context area on the stack might be incorrect. The processor
// does not write a fault address to the BFAR."
func (fs BusFaultStatus) WhileStackingException() bool { return fs&nxp.SystemControl_CFSR_STKERR != 0 }
// DuringFPLazyStatePres: A bus fault occurred during floating-point lazy state
// preservation
func (fs BusFaultStatus) DuringFPLazyStatePres() bool { return fs&nxp.SystemControl_CFSR_LSPERR != 0 }
// UndefinedInstruction: the processor has attempted to execute an undefined
// instruction
//
// "When this bit is set to 1, the PC value stacked for the exception return
// points to the undefined instruction.
//
// An undefined instruction is an instruction that the processor cannot decode."
func (fs UsageFaultStatus) UndefinedInstruction() bool {
return fs&nxp.SystemControl_CFSR_UNDEFINSTR != 0
}
// IllegalUseOfEPSR: the processor has attempted to execute an instruction that
// makes illegal use of the EPSR
//
// "When this bit is set to 1, the PC value stacked for the exception return
// points to the instruction that attempted the illegal use of the EPSR.
//
// This bit is not set to 1 if an undefined instruction uses the EPSR."
func (fs UsageFaultStatus) IllegalUseOfEPSR() bool { return fs&nxp.SystemControl_CFSR_INVSTATE != 0 }
// IllegalExceptionReturn: the processor has attempted an illegal load of
// EXC_RETURN to the PC
//
// "When this bit is set to 1, the PC value stacked for the exception return
// points to the instruction that tried to perform the illegal load of the PC."
func (fs UsageFaultStatus) IllegalExceptionReturn() bool { return fs&nxp.SystemControl_CFSR_INVPC != 0 }
// AttemptedToAccessCoprocessor: the processor has attempted to access a
// coprocessor
func (fs UsageFaultStatus) AttemptedToAccessCoprocessor() bool {
return fs&nxp.SystemControl_CFSR_NOCP != 0
}
// UnalignedMemoryAccess: the processor has made an unaligned memory access
//
// "Enable trapping of unaligned accesses by setting the UNALIGN_TRP bit in the
// CCR to 1.
//
// Unaligned LDM, STM, LDRD, and STRD instructions always fault irrespective of
// the setting of UNALIGN_TRP."
func (fs UsageFaultStatus) UnalignedMemoryAccess() bool {
return fs&nxp.SystemControl_CFSR_UNALIGNED != 0
}
// DivideByZero: the processor has executed an SDIV or UDIV instruction with a
// divisor of 0
//
// "When the processor sets this bit to 1, the PC value stacked for the
// exception return points to the instruction that performed the divide by zero.
//
// Enable trapping of divide by zero by setting the DIV_0_TRP bit in the CCR to
// 1."
func (fs UsageFaultStatus) DivideByZero() bool { return fs&nxp.SystemControl_CFSR_DIVBYZERO != 0 }
// Address returns the MemManage Fault Address Register if the fault status
// indicates the address is valid.
//
// "If a MemManage fault occurs and is escalated to a HardFault because of
// priority, the HardFault handler must set this bit to 0. This prevents
// problems on return to a stacked active MemManage fault handler whose MMAR
// value has been overwritten."
func (fs MemFaultStatus) Address() (uintptr, bool) {
if fs&nxp.SystemControl_CFSR_MMARVALID == 0 {
return 0, false
} else {
return uintptr(nxp.SystemControl.MMFAR.Get()), true
}
}
// Address returns the BusFault Address Register if the fault status
// indicates the address is valid.
//
// "The processor sets this bit to 1 after a BusFault where the address is
// known. Other faults can set this bit to 0, such as a MemManage fault
// occurring later.
//
// If a BusFault occurs and is escalated to a hard fault because of priority,
// the hard fault handler must set this bit to 0. This prevents problems if
// returning to a stacked active BusFault handler whose BFAR value has been
// overwritten.""
func (fs BusFaultStatus) Address() (uintptr, bool) {
if fs&nxp.SystemControl_CFSR_BFARVALID == 0 {
return 0, false
} else {
return uintptr(nxp.SystemControl.BFAR.Get()), true
}
}

Просмотреть файл

@ -235,3 +235,33 @@ func putchar(c byte) {
// ???
const asyncScheduler = false
func abort() {
println("!!! ABORT !!!")
m := arm.DisableInterrupts()
arm.Asm("mov r12, #1")
arm.Asm("msr basepri, r12") // only execute interrupts of priority 0
nxp.SystemControl.SHPR3.ClearBits(nxp.SystemControl_SHPR3_PRI_15_Msk) // set systick to priority 0
arm.EnableInterrupts(m)
machine.LED.Configure(machine.PinConfig{Mode: machine.PinOutput})
var v bool
for {
machine.LED.Set(v)
v = !v
t := millisSinceBoot()
for millisSinceBoot()-t < 60 {
arm.Asm("wfi")
}
// keep polling some communication while in fault
// mode, so we don't completely die.
// machine.PollUSB(&machine.USB0)
machine.PollUART(&machine.UART0)
machine.PollUART(&machine.UART1)
machine.PollUART(&machine.UART2)
}
}