/* * Copyright (C) 2016 The Android Open Source Project * * 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 #include #include #include #include #include #include #include #include #include #include #define HARD_FAULT_DROPBOX_MAGIC_MASK 0xFFFFC000 #define HARD_FAULT_DROPBOX_MAGIC_VAL 0x31414000 #define HARD_FAULT_DROPBOX_MAGIC_HAVE_DROP 0x00002000 #define HARD_FAULT_DROPBOX_MAGIC_DATA_MASK 0x00001FFF union TidTrig { struct { uint32_t tid : 16; uint32_t trig : 2; uint32_t RFU : 14; }; uint32_t rw; }; // These registers only support word accesses (r/w) // Make sure to only use uint32_t's struct RamPersistedDataAndDropbox { uint32_t magic; // and part of dropbox uint32_t r[16]; uint32_t sr_hfsr_cfsr_lo; uint32_t bits; union TidTrig tid_trig; // only access via tid_trig.rw }; /* //if your device persists ram, you can use this instead: * static struct RamPersistedDataAndDropbox* getPersistedData(void) * { * static struct RamPersistedDataAndDropbox __attribute__((section(".neverinit"))) dbx; * return &dbx; * } */ static struct RamPersistedDataAndDropbox* getPersistedData(void) { uint32_t bytes = 0; void *loc = platGetPersistentRamStore(&bytes); return bytes >= sizeof(struct RamPersistedDataAndDropbox) ? (struct RamPersistedDataAndDropbox*)loc : NULL; } static struct RamPersistedDataAndDropbox* getInitedPersistedData(void) { struct RamPersistedDataAndDropbox* dbx = getPersistedData(); if ((dbx->magic & HARD_FAULT_DROPBOX_MAGIC_MASK) != HARD_FAULT_DROPBOX_MAGIC_VAL) { dbx->bits = 0; dbx->magic = HARD_FAULT_DROPBOX_MAGIC_VAL; } return dbx; } void cpuInit(void) { /* set SVC to be highest possible priority */ NVIC_SetPriority(SVCall_IRQn, 0xff); /* FPU on */ SCB->CPACR |= 0x00F00000; } //pack all our SR regs into 45 bits static void cpuPackSrBits(uint32_t *dstLo, uint32_t *dstHi, uint32_t sr, uint32_t hfsr, uint32_t cfsr) { //mask of useful bits: // SR: 11111111 00000000 11111101 11111111 (total of 23 bits) // HFSR: 01000000 00000000 00000000 00000010 (total of 2 bits) // CFSR: 00000011 00001111 10111111 10111111 (total of 20 bits) // so our total is 45 bits. we pack this into 2 longs (for now) sr &= 0xFF00FDFF; hfsr &= 0x40000002; cfsr &= 0x030FBFBF; *dstLo = sr | ((cfsr << 4) & 0x00FF0000) | (hfsr >> 12) | (hfsr << 8); *dstHi = ((cfsr & 0x01000000) >> 18) | ((cfsr & 0x02000000) >> 13) | (cfsr & 0x00000fff); } //unpack the SR bits static void cpuUnpackSrBits(uint32_t srcLo, uint32_t srcHi, uint32_t *srP, uint32_t *hfsrP, uint32_t *cfsrP) { *srP = srcLo & 0xFF00FDFF; *hfsrP = ((srcLo << 12) & 0x40000000) | ((srcLo >> 8) & 0x00000002); *cfsrP = ((srcLo & 0x00FB0000) >> 4) | (srcHi & 0x0FBF) | ((srcHi << 13) & 0x02000000) | ((srcHi << 18) & 0x01000000); } static void cpuDbxDump(struct RamPersistedDataAndDropbox *dbx) { uint32_t i, hfsr, cfsr, sr; union TidTrig tid_trig; const char *trigName; static const char *trigNames[] = { "UNKNOWN", "HARD FAULT", "WDT", "MPU" }; if (dbx) { for (i = 0; i < 8; i++) osLog(LOG_ERROR, " R%02lu = 0x%08lX R%02lu = 0x%08lX\n", i, dbx->r[i], i + 8, dbx->r[i+8]); cpuUnpackSrBits(dbx->sr_hfsr_cfsr_lo, dbx->magic & HARD_FAULT_DROPBOX_MAGIC_DATA_MASK, &sr, &hfsr, &cfsr); osLog(LOG_ERROR, " xPSR = 0x%08lX HFSR = 0x%08lX\n", sr, hfsr); osLog(LOG_ERROR, " CFSR = 0x%08lX BITS = 0x%08lX\n", cfsr, dbx->bits); // reboot source (if known), reported as TRIG // so far we have 3 reboot sources reported here: // 1 - HARD FAULT // 2 - WDT // 3 - MPU tid_trig.rw = dbx->tid_trig.rw; trigName = trigNames[tid_trig.trig < ARRAY_SIZE(trigNames) ? tid_trig.trig : 0]; osLog(LOG_ERROR, " TID = 0x%04" PRIX16 " TRIG = 0x%04" PRIX16 " [%s]\n\n", tid_trig.tid, tid_trig.trig, trigName); } } void cpuInitLate(void) { struct RamPersistedDataAndDropbox *dbx = getInitedPersistedData(); uint32_t reason = pwrResetReason(); const char *reasonDesc = ""; enum LogLevel level = LOG_INFO; // if we detected WDT reboot reason, we are likely not having data in dropbox // All we can do is report that WDT reset happened if ((reason & (RESET_WINDOW_WATCHDOG|RESET_INDEPENDENT_WATCHDOG)) != 0) { reasonDesc = "; HW WDT RESET"; level = LOG_ERROR; } osLog(level, "Reboot reason: 0x%08" PRIX32 "%s\n", reason, reasonDesc); /* print and clear dropbox */ if (dbx->magic & HARD_FAULT_DROPBOX_MAGIC_HAVE_DROP) { osLog(LOG_INFO, "Dropbox not empty. Contents:\n"); cpuDbxDump(dbx); } dbx->magic &=~ HARD_FAULT_DROPBOX_MAGIC_HAVE_DROP; } bool cpuRamPersistentBitGet(uint32_t which) { struct RamPersistedDataAndDropbox *dbx = getInitedPersistedData(); return (which < CPU_NUM_PERSISTENT_RAM_BITS) && ((dbx->bits >> which) & 1); } void cpuRamPersistentBitSet(uint32_t which, bool on) { struct RamPersistedDataAndDropbox *dbx = getInitedPersistedData(); if (which < CPU_NUM_PERSISTENT_RAM_BITS) { if (on) dbx->bits |= (1ULL << which); else dbx->bits &=~ (1ULL << which); } } uint64_t cpuIntsOff(void) { uint32_t state; asm volatile ( "mrs %0, PRIMASK \n" "cpsid i \n" :"=r"(state) ); return state; } uint64_t cpuIntsOn(void) { uint32_t state; asm volatile ( "mrs %0, PRIMASK \n" "cpsie i \n" :"=r"(state) ); return state; } void cpuIntsRestore(uint64_t state) { asm volatile( "msr PRIMASK, %0 \n" ::"r"((uint32_t)state) ); } /* * Stack layout for HW interrupt handlers [ARM7-M] * =============================================== * orig_SP[8...25] = FPU state (S0..S15, FPSCR, Reserved) [if enabled] * orig_SP[7] = xPSR * orig_SP[6] = ReturnAddress * orig_SP[5] = LR (R14) * orig_SP[4] = R12 * orig_SP[3..0] = R3..R0 * * upon entry, new value of LR is calculated as follows: * LR := 0xFFFFFFE0 | | | | 0x01 * * upon exit, PC value is interpreted according to the same rule (see LR above) * if bits 28..31 of PC equal 0xF, return from interrupt is executed * this way, "bx lr" would perform return from interrupt to the previous state */ static void __attribute__((used)) syscallHandler(uintptr_t *excRegs) { uint16_t *svcPC = ((uint16_t *)(excRegs[6])) - 1; uint32_t svcNo = (*svcPC) & 0xFF; uint32_t syscallNr = excRegs[0]; SyscallFunc handler; va_list args_long = *(va_list*)(excRegs + 1); uintptr_t *fastParams = excRegs + 1; va_list args_fast = *(va_list*)(&fastParams); if (svcNo > 1) osLog(LOG_WARN, "Unknown SVC 0x%02lX called at 0x%08lX\n", svcNo, (unsigned long)svcPC); else if (!(handler = syscallGetHandler(syscallNr))) osLog(LOG_WARN, "Unknown syscall 0x%08lX called at 0x%08lX\n", (unsigned long)syscallNr, (unsigned long)svcPC); else handler(excRegs, svcNo ? args_fast : args_long); } void SVC_Handler(void); void __attribute__((naked)) SVC_Handler(void) { asm volatile( "tst lr, #4 \n" "ite eq \n" "mrseq r0, msp \n" "mrsne r0, psp \n" "b syscallHandler \n" ); } static void __attribute__((used)) logHardFault(uintptr_t *excRegs, uintptr_t* otherRegs, bool tinyStack, uint32_t code) { struct RamPersistedDataAndDropbox *dbx = getInitedPersistedData(); uint32_t i, hi; union TidTrig tid_trig; wdtPing(); for (i = 0; i < 4; i++) dbx->r[i] = excRegs[i]; for (i = 0; i < 8; i++) dbx->r[i + 4] = otherRegs[i]; dbx->r[12] = excRegs[4]; dbx->r[13] = (uint32_t)(excRegs + 8); dbx->r[14] = excRegs[5]; dbx->r[15] = excRegs[6]; cpuPackSrBits(&dbx->sr_hfsr_cfsr_lo, &hi, excRegs[7], SCB->HFSR, SCB->CFSR); dbx->magic |= HARD_FAULT_DROPBOX_MAGIC_HAVE_DROP | (hi & HARD_FAULT_DROPBOX_MAGIC_DATA_MASK); tid_trig.tid = osGetCurrentTid(); tid_trig.trig = code; dbx->tid_trig.rw = tid_trig.rw; if (!tinyStack) { osLog(LOG_ERROR, "*HARD FAULT*\n"); cpuDbxDump(dbx); } NVIC_SystemReset(); } static uint32_t __attribute__((used)) hfStack[16]; void cpuCommonFaultCode(void); void __attribute__((naked)) cpuCommonFaultCode(void) { // r0 contains Fault IRQ code asm volatile( "ldr r3, =__stack_bottom + 64 \n" "cmp sp, r3 \n" "itte le \n" "ldrle sp, =hfStack + 64 \n" "movle r2, #1 \n" "movgt r2, #0 \n" "mov r3, r0 \n" "tst lr, #4 \n" "ite eq \n" "mrseq r0, msp \n" "mrsne r0, psp \n" "push {r4-r11} \n" "mov r1, sp \n" "b logHardFault \n" ); } void HardFault_Handler(void); void __attribute__((naked)) HardFault_Handler(void) { asm volatile( "mov r0, #1 \n" "b cpuCommonFaultCode \n" ); }