github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/runtime/runtime_cortexm_hardfault_debug.go (about)

     1  //go:build cortexm && !atsamd21 && !nrf51
     2  
     3  package runtime
     4  
     5  import (
     6  	"device/arm"
     7  	"unsafe"
     8  )
     9  
    10  const (
    11  	SCB_CFSR_KnownFault = arm.SCB_CFSR_IACCVIOL | arm.SCB_CFSR_DACCVIOL |
    12  		arm.SCB_CFSR_MUNSTKERR | arm.SCB_CFSR_MSTKERR | arm.SCB_CFSR_MLSPERR |
    13  		arm.SCB_CFSR_IBUSERR | arm.SCB_CFSR_PRECISERR | arm.SCB_CFSR_IMPRECISERR |
    14  		arm.SCB_CFSR_UNSTKERR | arm.SCB_CFSR_STKERR | arm.SCB_CFSR_LSPERR |
    15  		arm.SCB_CFSR_UNDEFINSTR | arm.SCB_CFSR_INVSTATE | arm.SCB_CFSR_INVPC |
    16  		arm.SCB_CFSR_NOCP | arm.SCB_CFSR_UNALIGNED | arm.SCB_CFSR_DIVBYZERO
    17  )
    18  
    19  // See runtime_cortexm_hardfault.go
    20  //
    21  //go:export handleHardFault
    22  func handleHardFault(sp *interruptStack) {
    23  	fault := GetFaultStatus()
    24  	spValid := !fault.Bus().ImpreciseDataBusError()
    25  
    26  	print("fatal error: ")
    27  	if spValid && uintptr(unsafe.Pointer(sp)) < 0x20000000 {
    28  		print("stack overflow? ")
    29  	}
    30  	if fault.Mem().InstructionAccessViolation() {
    31  		print("instruction access violation")
    32  	}
    33  	if fault.Mem().DataAccessViolation() {
    34  		print("data access violation")
    35  	}
    36  	if fault.Mem().WhileUnstackingException() {
    37  		print(" while unstacking exception")
    38  	}
    39  	if fault.Mem().WileStackingException() {
    40  		print(" while stacking exception")
    41  	}
    42  	if fault.Mem().DuringFPLazyStatePres() {
    43  		print(" during floating-point lazy state preservation")
    44  	}
    45  
    46  	if fault.Bus().InstructionBusError() {
    47  		print("instruction bus error")
    48  	}
    49  	if fault.Bus().PreciseDataBusError() {
    50  		print("data bus error (precise)")
    51  	}
    52  	if fault.Bus().ImpreciseDataBusError() {
    53  		print("data bus error (imprecise)")
    54  	}
    55  	if fault.Bus().WhileUnstackingException() {
    56  		print(" while unstacking exception")
    57  	}
    58  	if fault.Bus().WhileStackingException() {
    59  		print(" while stacking exception")
    60  	}
    61  	if fault.Bus().DuringFPLazyStatePres() {
    62  		print(" during floating-point lazy state preservation")
    63  	}
    64  
    65  	if fault.Usage().UndefinedInstruction() {
    66  		print("undefined instruction")
    67  	}
    68  	if fault.Usage().IllegalUseOfEPSR() {
    69  		print("illegal use of the EPSR")
    70  	}
    71  	if fault.Usage().IllegalExceptionReturn() {
    72  		print("illegal load of EXC_RETURN to the PC")
    73  	}
    74  	if fault.Usage().AttemptedToAccessCoprocessor() {
    75  		print("coprocessor access violation")
    76  	}
    77  	if fault.Usage().UnalignedMemoryAccess() {
    78  		print("unaligned memory access")
    79  	}
    80  	if fault.Usage().DivideByZero() {
    81  		print("divide by zero")
    82  	}
    83  
    84  	if fault.Unknown() {
    85  		print("unknown hard fault")
    86  	}
    87  
    88  	if addr, ok := fault.Mem().Address(); ok {
    89  		print(" with fault address ", addr)
    90  	}
    91  
    92  	if addr, ok := fault.Bus().Address(); ok {
    93  		print(" with bus fault address ", addr)
    94  	}
    95  	if spValid {
    96  		print(" with sp=", sp)
    97  		if uintptr(unsafe.Pointer(&sp.PC)) >= 0x20000000 {
    98  			// Only print the PC if it points into memory.
    99  			// It may not point into memory during a stack overflow, so check that
   100  			// first before accessing the stack.
   101  			print(" pc=", sp.PC)
   102  		}
   103  	}
   104  	println()
   105  	abort()
   106  }
   107  
   108  // Descriptions are sourced from the K66 SVD and
   109  // http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0552a/Cihcfefj.html
   110  
   111  // GetFaultStatus reads the System Control Block Configurable Fault Status
   112  // Register and returns it as a FaultStatus.
   113  func GetFaultStatus() FaultStatus {
   114  	return FaultStatus(arm.SCB.CFSR.Get())
   115  }
   116  
   117  type FaultStatus uint32
   118  type MemFaultStatus uint32
   119  type BusFaultStatus uint32
   120  type UsageFaultStatus uint32
   121  
   122  func (fs FaultStatus) Mem() MemFaultStatus     { return MemFaultStatus(fs) }
   123  func (fs FaultStatus) Bus() BusFaultStatus     { return BusFaultStatus(fs) }
   124  func (fs FaultStatus) Usage() UsageFaultStatus { return UsageFaultStatus(fs) }
   125  
   126  // Unknown returns true if the cause of the fault is not know
   127  func (fs FaultStatus) Unknown() bool {
   128  	return fs&SCB_CFSR_KnownFault == 0
   129  }
   130  
   131  // InstructionAccessViolation: the processor attempted an instruction fetch from
   132  // a location that does not permit execution
   133  //
   134  // "This fault occurs on any access to an XN region, even when the MPU is
   135  // disabled or not present.
   136  //
   137  // When this bit is 1, the PC value stacked for the exception return points to
   138  // the faulting instruction. The processor has not written a fault address to
   139  // the MMAR."
   140  func (fs MemFaultStatus) InstructionAccessViolation() bool {
   141  	return fs&arm.SCB_CFSR_IACCVIOL != 0
   142  }
   143  
   144  // DataAccessViolation: the processor attempted a load or store at a location
   145  // that does not permit the operation
   146  //
   147  // "When this bit is 1, the PC value stacked for the exception return points to
   148  // the faulting instruction. The processor has loaded the MMAR with the address
   149  // of the attempted access."
   150  func (fs MemFaultStatus) DataAccessViolation() bool {
   151  	return fs&arm.SCB_CFSR_DACCVIOL != 0
   152  }
   153  
   154  // WhileUnstackingException: unstack for an exception return has caused one or
   155  // more access violations
   156  //
   157  // "This fault is chained to the handler. This means that when this bit is 1,
   158  // the original return stack is still present. The processor has not adjusted
   159  // the SP from the failing return, and has not performed a new save. The
   160  // processor has not written a fault address to the MMAR."
   161  func (fs MemFaultStatus) WhileUnstackingException() bool {
   162  	return fs&arm.SCB_CFSR_MUNSTKERR != 0
   163  }
   164  
   165  // WileStackingException: stacking for an exception entry has caused one or more
   166  // access violations
   167  //
   168  // "When this bit is 1, the SP is still adjusted but the values in the context
   169  // area on the stack might be incorrect. The processor has not written a fault
   170  // address to the MMAR."
   171  func (fs MemFaultStatus) WileStackingException() bool {
   172  	return fs&arm.SCB_CFSR_MSTKERR != 0
   173  }
   174  
   175  // DuringFPLazyStatePres: A MemManage fault occurred during floating-point lazy
   176  // state preservation
   177  func (fs MemFaultStatus) DuringFPLazyStatePres() bool {
   178  	return fs&arm.SCB_CFSR_MLSPERR != 0
   179  }
   180  
   181  // InstructionBusError: instruction bus error
   182  //
   183  // "The processor detects the instruction bus error on prefetching an
   184  // instruction, but it sets the IBUSERR flag to 1 only if it attempts to issue
   185  // the faulting instruction.
   186  //
   187  // When the processor sets this bit is 1, it does not write a fault address to
   188  // the BFAR."
   189  func (fs BusFaultStatus) InstructionBusError() bool {
   190  	return fs&arm.SCB_CFSR_IBUSERR != 0
   191  }
   192  
   193  // PreciseDataBusError: a data bus error has occurred, and the PC value stacked
   194  // for the exception return points to the instruction that caused the fault
   195  func (fs BusFaultStatus) PreciseDataBusError() bool {
   196  	return fs&arm.SCB_CFSR_PRECISERR != 0
   197  }
   198  
   199  // ImpreciseDataBusError: a data bus error has occurred, but the return address
   200  // in the stack frame is not related to the instruction that caused the error
   201  //
   202  // "When the processor sets this bit to 1, it does not write a fault address to
   203  // the BFAR.
   204  //
   205  // This is an asynchronous fault. Therefore, if it is detected when the priority
   206  // of the current process is higher than the BusFault priority, the BusFault
   207  // becomes pending and becomes active only when the processor returns from all
   208  // higher priority processes. If a precise fault occurs before the processor
   209  // enters the handler for the imprecise BusFault, the handler detects both
   210  // IMPRECISERR set to 1 and one of the precise fault status bits set to 1."
   211  func (fs BusFaultStatus) ImpreciseDataBusError() bool {
   212  	return fs&arm.SCB_CFSR_IMPRECISERR != 0
   213  }
   214  
   215  // WhileUnstackingException: unstack for an exception return has caused one or
   216  // more BusFaults
   217  //
   218  // "This fault is chained to the handler. This means that when the processor
   219  // sets this bit to 1, the original return stack is still present. The processor
   220  // does not adjust the SP from the failing return, does not performed a new
   221  // save, and does not write a fault address to the BFAR."
   222  func (fs BusFaultStatus) WhileUnstackingException() bool {
   223  	return fs&arm.SCB_CFSR_UNSTKERR != 0
   224  }
   225  
   226  // WhileStackingException: stacking for an exception entry has caused one or
   227  // more BusFaults
   228  //
   229  // "When the processor sets this bit to 1, the SP is still adjusted but the
   230  // values in the context area on the stack might be incorrect. The processor
   231  // does not write a fault address to the BFAR."
   232  func (fs BusFaultStatus) WhileStackingException() bool {
   233  	return fs&arm.SCB_CFSR_STKERR != 0
   234  }
   235  
   236  // DuringFPLazyStatePres: A bus fault occurred during floating-point lazy state
   237  // preservation
   238  func (fs BusFaultStatus) DuringFPLazyStatePres() bool {
   239  	return fs&arm.SCB_CFSR_LSPERR != 0
   240  }
   241  
   242  // UndefinedInstruction: the processor has attempted to execute an undefined
   243  // instruction
   244  //
   245  // "When this bit is set to 1, the PC value stacked for the exception return
   246  // points to the undefined instruction.
   247  //
   248  // An undefined instruction is an instruction that the processor cannot decode."
   249  func (fs UsageFaultStatus) UndefinedInstruction() bool {
   250  	return fs&arm.SCB_CFSR_UNDEFINSTR != 0
   251  }
   252  
   253  // IllegalUseOfEPSR: the processor has attempted to execute an instruction that
   254  // makes illegal use of the EPSR
   255  //
   256  // "When this bit is set to 1, the PC value stacked for the exception return
   257  // points to the instruction that attempted the illegal use of the EPSR.
   258  //
   259  // This bit is not set to 1 if an undefined instruction uses the EPSR."
   260  func (fs UsageFaultStatus) IllegalUseOfEPSR() bool {
   261  	return fs&arm.SCB_CFSR_INVSTATE != 0
   262  }
   263  
   264  // IllegalExceptionReturn: the processor has attempted an illegal load of
   265  // EXC_RETURN to the PC
   266  //
   267  // "When this bit is set to 1, the PC value stacked for the exception return
   268  // points to the instruction that tried to perform the illegal load of the PC."
   269  func (fs UsageFaultStatus) IllegalExceptionReturn() bool {
   270  	return fs&arm.SCB_CFSR_INVPC != 0
   271  }
   272  
   273  // AttemptedToAccessCoprocessor: the processor has attempted to access a
   274  // coprocessor
   275  func (fs UsageFaultStatus) AttemptedToAccessCoprocessor() bool {
   276  	return fs&arm.SCB_CFSR_NOCP != 0
   277  }
   278  
   279  // UnalignedMemoryAccess: the processor has made an unaligned memory access
   280  //
   281  // "Enable trapping of unaligned accesses by setting the UNALIGN_TRP bit in the
   282  // CCR to 1.
   283  //
   284  // Unaligned LDM, STM, LDRD, and STRD instructions always fault irrespective of
   285  // the setting of UNALIGN_TRP."
   286  func (fs UsageFaultStatus) UnalignedMemoryAccess() bool {
   287  	return fs&arm.SCB_CFSR_UNALIGNED != 0
   288  }
   289  
   290  // DivideByZero: the processor has executed an SDIV or UDIV instruction with a
   291  // divisor of 0
   292  //
   293  // "When the processor sets this bit to 1, the PC value stacked for the
   294  // exception return points to the instruction that performed the divide by zero.
   295  //
   296  // Enable trapping of divide by zero by setting the DIV_0_TRP bit in the CCR to
   297  // 1."
   298  func (fs UsageFaultStatus) DivideByZero() bool {
   299  	return fs&arm.SCB_CFSR_DIVBYZERO != 0
   300  }
   301  
   302  // Address returns the MemManage Fault Address Register if the fault status
   303  // indicates the address is valid.
   304  //
   305  // "If a MemManage fault occurs and is escalated to a HardFault because of
   306  // priority, the HardFault handler must set this bit to 0. This prevents
   307  // problems on return to a stacked active MemManage fault handler whose MMAR
   308  // value has been overwritten."
   309  func (fs MemFaultStatus) Address() (uintptr, bool) {
   310  	if fs&arm.SCB_CFSR_MMARVALID == 0 {
   311  		return 0, false
   312  	} else {
   313  		return uintptr(arm.SCB.MMFAR.Get()), true
   314  	}
   315  }
   316  
   317  // Address returns the BusFault Address Register if the fault status
   318  // indicates the address is valid.
   319  //
   320  // "The processor sets this bit to 1 after a BusFault where the address is
   321  // known. Other faults can set this bit to 0, such as a MemManage fault
   322  // occurring later.
   323  //
   324  // If a BusFault occurs and is escalated to a hard fault because of priority,
   325  // the hard fault handler must set this bit to 0. This prevents problems if
   326  // returning to a stacked active BusFault handler whose BFAR value has been
   327  // overwritten.""
   328  func (fs BusFaultStatus) Address() (uintptr, bool) {
   329  	if fs&arm.SCB_CFSR_BFARVALID == 0 {
   330  		return 0, false
   331  	} else {
   332  		return uintptr(arm.SCB.BFAR.Get()), true
   333  	}
   334  }