mirror of
https://github.com/google/pebble.git
synced 2025-05-06 18:01:41 -04:00
183 lines
6.4 KiB
C
183 lines
6.4 KiB
C
/*
|
|
* Copyright 2024 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "kernel/logging_private.h"
|
|
#include "system/die.h"
|
|
#include "system/reboot_reason.h"
|
|
#include "system/reset.h"
|
|
#include "util/attributes.h"
|
|
#include "util/bitset.h"
|
|
#include "util/size.h"
|
|
#include "util/string.h"
|
|
|
|
#define CMSIS_COMPATIBLE
|
|
#include <mcu.h>
|
|
|
|
#include <inttypes.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
|
|
#include "kernel/pebble_tasks.h"
|
|
|
|
void fault_handler_dump_stacked_args(char buffer[80], unsigned int* stacked_args) {
|
|
unsigned int stacked_r0 = ((unsigned long) stacked_args[0]);
|
|
unsigned int stacked_r1 = ((unsigned long) stacked_args[1]);
|
|
unsigned int stacked_r2 = ((unsigned long) stacked_args[2]);
|
|
unsigned int stacked_r3 = ((unsigned long) stacked_args[3]);
|
|
|
|
unsigned int stacked_r12 = ((unsigned long) stacked_args[4]);
|
|
unsigned int stacked_lr = ((unsigned long) stacked_args[5]);
|
|
unsigned int stacked_pc = ((unsigned long) stacked_args[6]);
|
|
unsigned int stacked_psr = ((unsigned long) stacked_args[7]);
|
|
|
|
PBL_LOG_FROM_FAULT_HANDLER_FMT(buffer, 80, "R0 = 0x%x", stacked_r0);
|
|
PBL_LOG_FROM_FAULT_HANDLER_FMT(buffer, 80, "R1 = 0x%x", stacked_r1);
|
|
PBL_LOG_FROM_FAULT_HANDLER_FMT(buffer, 80, "R2 = 0x%x", stacked_r2);
|
|
PBL_LOG_FROM_FAULT_HANDLER_FMT(buffer, 80, "R3 = 0x%x", stacked_r3);
|
|
PBL_LOG_FROM_FAULT_HANDLER_FMT(buffer, 80, "R12 = 0x%x", stacked_r12);
|
|
PBL_LOG_FROM_FAULT_HANDLER_FMT(buffer, 80, "SP = %p", &stacked_args[8]);
|
|
PBL_LOG_FROM_FAULT_HANDLER_FMT(
|
|
buffer, 80, "LR [R14] = 0x%x subroutine call return address", stacked_lr);
|
|
PBL_LOG_FROM_FAULT_HANDLER_FMT(
|
|
buffer, 80, "PC [R15] = 0x%x program counter", stacked_pc);
|
|
PBL_LOG_FROM_FAULT_HANDLER_FMT(buffer, 80, "PSR = 0x%x", stacked_psr);
|
|
|
|
//BREAKPOINT;
|
|
|
|
// NOTE: If you want to get a stack trace at this point. Set a breakpoint here (you can compile in the above
|
|
// BREAKPOINT call if you want) and issue the following commands in gdb:
|
|
// set var $sp=<value of SP above>
|
|
// set var $lr=<value of LR above>
|
|
// set var $pc=<value of PC above>
|
|
// bt
|
|
}
|
|
|
|
typedef struct IndexToName {
|
|
unsigned int index;
|
|
const char *name;
|
|
} IndexToName;
|
|
|
|
static void print_set_indexes(char buffer[80], const uint8_t *bitset, const IndexToName *mappings, unsigned int num_mappings) {
|
|
for (unsigned int i = 0; i < num_mappings; ++i) {
|
|
if (bitset8_get(bitset, mappings[i].index)) {
|
|
PBL_LOG_FROM_FAULT_HANDLER_FMT(buffer, 80, " %s = yes", mappings[i].name);
|
|
}
|
|
}
|
|
}
|
|
|
|
void fault_handler_dump_cfsr(char buffer[80]) {
|
|
// See http://infocenter.arm.com/help/topic/com.arm.doc.dui0552a/DUI0552A_cortex_m3_dgug.pdf for the register
|
|
// definition.
|
|
|
|
const uint32_t cfsr = SCB->CFSR;
|
|
PBL_LOG_FROM_FAULT_HANDLER_FMT(buffer, 80, "CFSR (Configurable Fault) = 0x%"PRIx32, cfsr);
|
|
|
|
// Usage Fault Status Register
|
|
const uint16_t ufsr = (cfsr >> 16) & 0xffff;
|
|
static const IndexToName ufsr_mappings[] = {
|
|
{ 9, "DIVBYZERO" },
|
|
{ 8, "UNALIGNED" },
|
|
{ 3, "NOCP" },
|
|
{ 2, "INVPC" },
|
|
{ 1, "INVSTATE" },
|
|
{ 0, "UNDEFINSTR" }
|
|
};
|
|
if (ufsr != 0) {
|
|
PBL_LOG_FROM_FAULT_HANDLER(" Usage Fault Status Register:");
|
|
print_set_indexes(buffer, (const uint8_t*) &ufsr, ufsr_mappings, ARRAY_LENGTH(ufsr_mappings));
|
|
}
|
|
|
|
// Bus Fault Status Register
|
|
const uint8_t bfsr = (cfsr >> 8) & 0xff;
|
|
static const IndexToName bfsr_mappings[] = {
|
|
{ 4, "STKERR" },
|
|
{ 3, "UNSTKERR" },
|
|
{ 2, "IMPRECISERR" },
|
|
{ 1, "PRECISERR" },
|
|
{ 0, "IBUSERR" },
|
|
};
|
|
if (bfsr != 0) {
|
|
PBL_LOG_FROM_FAULT_HANDLER(" Bus Fault Status Register:");
|
|
|
|
if (bfsr & (1 << 7)) {
|
|
PBL_LOG_FROM_FAULT_HANDLER_FMT(buffer, 80, " BFARVALID = yes 0x%"PRIx32, SCB->BFAR);
|
|
}
|
|
|
|
print_set_indexes(buffer, &bfsr, bfsr_mappings, ARRAY_LENGTH(bfsr_mappings));
|
|
}
|
|
|
|
// Memory Management Fault Status Register
|
|
const uint8_t mmfsr = cfsr & 0xff;
|
|
static const IndexToName mmfsr_mappings[] = {
|
|
{ 4, "MSTKERR" },
|
|
{ 3, "MUNSTKERR" },
|
|
{ 1, "DACCVIOL" },
|
|
{ 0, "IACCVIOL" }
|
|
};
|
|
if (mmfsr != 0) {
|
|
PBL_LOG_FROM_FAULT_HANDLER(" Memory Management Fault Status Register:");
|
|
|
|
if (mmfsr & (1 << 7)) {
|
|
PBL_LOG_FROM_FAULT_HANDLER_FMT(buffer, 80, " MMFARVALID = yes 0x%"PRIx32, SCB->MMFAR);
|
|
}
|
|
|
|
print_set_indexes(buffer, &mmfsr, mmfsr_mappings, ARRAY_LENGTH(mmfsr_mappings));
|
|
}
|
|
}
|
|
|
|
void fault_handler_dump(char buffer[80], unsigned int *stacked_args) {
|
|
fault_handler_dump_stacked_args(buffer, stacked_args);
|
|
fault_handler_dump_cfsr(buffer);
|
|
PBL_LOG_FROM_FAULT_HANDLER_FMT(
|
|
buffer, 80, "Task: %s", pebble_task_get_name(pebble_task_get_current()));
|
|
}
|
|
|
|
static void hard_fault_handler_c(unsigned int* hardfault_args) {
|
|
// Log the lr instead of the pc. We frequently crash due to PC being madness. While the lr may be a little further
|
|
// than the actual crash, it should give us enough context.
|
|
const unsigned int stacked_lr = ((unsigned long) hardfault_args[5]);
|
|
RebootReason reason = { .code = RebootReasonCode_HardFault, .extra = stacked_lr };
|
|
reboot_reason_set(&reason);
|
|
|
|
// Yay, ripping stuff from the internet!
|
|
// http://blog.frankvh.com/2011/12/07/cortex-m3-m4-hard-fault-handler/
|
|
char buffer[80];
|
|
|
|
// To inspect the SCB in GDB: p (*((SCB_Type *) 0xE000ED00))
|
|
|
|
PBL_LOG_FROM_FAULT_HANDLER("\r\n\r\n[Hard fault handler - You dun goofed]");
|
|
|
|
PBL_LOG_FROM_FAULT_HANDLER_FMT(
|
|
buffer, 80, "SHCSR (System Handler) = 0x%"PRIx32, SCB->SHCSR);
|
|
PBL_LOG_FROM_FAULT_HANDLER_FMT(
|
|
buffer, 80, "HFSR (Hard Fault) = 0x%"PRIx32, SCB->HFSR);
|
|
PBL_LOG_FROM_FAULT_HANDLER_FMT(
|
|
buffer, 80, " Forced = %s", bool_to_str(SCB->HFSR & SCB_HFSR_FORCED_Msk));
|
|
|
|
fault_handler_dump(buffer, hardfault_args);
|
|
|
|
reset_due_to_software_failure();
|
|
}
|
|
|
|
void HardFault_Handler(void) {
|
|
// Grab the stack pointer, shove it into a register and call
|
|
// the c function above.
|
|
__asm("tst lr, #4\n"
|
|
"ite eq\n"
|
|
"mrseq r0, msp\n"
|
|
"mrsne r0, psp\n"
|
|
"b %0\n" :: "i" (hard_fault_handler_c));
|
|
}
|