1/* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include <plat/cmsis.h> 18#include <plat/plat.h> 19#include <plat/pwr.h> 20#include <plat/wdt.h> 21#include <syscall.h> 22#include <string.h> 23#include <seos.h> 24#include <heap.h> 25#include <cpu.h> 26#include <util.h> 27#include <reset.h> 28 29 30#define HARD_FAULT_DROPBOX_MAGIC_MASK 0xFFFFC000 31#define HARD_FAULT_DROPBOX_MAGIC_VAL 0x31414000 32#define HARD_FAULT_DROPBOX_MAGIC_HAVE_DROP 0x00002000 33#define HARD_FAULT_DROPBOX_MAGIC_DATA_MASK 0x00001FFF 34 35struct RamPersistedDataAndDropbox { 36 uint32_t magic; // and part of dropbox 37 uint32_t r[16]; 38 uint32_t sr_hfsr_cfsr_lo; 39 uint32_t bits; 40 uint16_t tid; 41 uint16_t trig:2; 42 uint16_t RFU:14; 43}; 44 45/* //if your device persists ram, you can use this instead: 46 * static struct RamPersistedDataAndDropbox* getPersistedData(void) 47 * { 48 * static struct RamPersistedDataAndDropbox __attribute__((section(".neverinit"))) dbx; 49 * return &dbx; 50 * } 51 */ 52 53static struct RamPersistedDataAndDropbox* getPersistedData(void) 54{ 55 uint32_t bytes = 0; 56 void *loc = platGetPersistentRamStore(&bytes); 57 58 return bytes >= sizeof(struct RamPersistedDataAndDropbox) ? (struct RamPersistedDataAndDropbox*)loc : NULL; 59} 60 61static struct RamPersistedDataAndDropbox* getInitedPersistedData(void) 62{ 63 struct RamPersistedDataAndDropbox* dbx = getPersistedData(); 64 65 if ((dbx->magic & HARD_FAULT_DROPBOX_MAGIC_MASK) != HARD_FAULT_DROPBOX_MAGIC_VAL) { 66 dbx->bits = 0; 67 dbx->magic = HARD_FAULT_DROPBOX_MAGIC_VAL; 68 } 69 70 return dbx; 71} 72 73void cpuInit(void) 74{ 75 /* set SVC to be highest possible priority */ 76 NVIC_SetPriority(SVCall_IRQn, 0xff); 77 78 /* FPU on */ 79 SCB->CPACR |= 0x00F00000; 80} 81 82//pack all our SR regs into 45 bits 83static void cpuPackSrBits(uint32_t *dstLo, uint32_t *dstHi, uint32_t sr, uint32_t hfsr, uint32_t cfsr) 84{ 85 //mask of useful bits: 86 // SR: 11111111 00000000 11111101 11111111 (total of 23 bits) 87 // HFSR: 01000000 00000000 00000000 00000010 (total of 2 bits) 88 // CFSR: 00000011 00001111 10111111 10111111 (total of 20 bits) 89 // so our total is 45 bits. we pack this into 2 longs (for now) 90 91 sr &= 0xFF00FDFF; 92 hfsr &= 0x40000002; 93 cfsr &= 0x030FBFBF; 94 95 *dstLo = sr | ((cfsr << 4) & 0x00FF0000) | (hfsr >> 12) | (hfsr << 8); 96 *dstHi = ((cfsr & 0x01000000) >> 18) | ((cfsr & 0x02000000) >> 13) | (cfsr & 0x00000fff); 97} 98 99//unpack the SR bits 100static void cpuUnpackSrBits(uint32_t srcLo, uint32_t srcHi, uint32_t *srP, uint32_t *hfsrP, uint32_t *cfsrP) 101{ 102 *srP = srcLo & 0xFF00FDFF; 103 *hfsrP = ((srcLo << 12) & 0x40000000) | ((srcLo >> 8) & 0x00000002); 104 *cfsrP = ((srcLo & 0x00FB0000) >> 4) | (srcHi & 0x0FBF) | ((srcHi << 13) & 0x02000000) | ((srcHi << 18) & 0x01000000); 105} 106 107static void cpuDbxDump(struct RamPersistedDataAndDropbox *dbx) 108{ 109 uint32_t i, hfsr, cfsr, sr, code; 110 const char *trigName; 111 static const char *trigNames[] = { "UNKNOWN", "HARD FAULT", "WDT" }; 112 113 if (dbx) { 114 for (i = 0; i < 8; i++) 115 osLog(LOG_ERROR, " R%02lu = 0x%08lX R%02lu = 0x%08lX\n", 116 i, dbx->r[i], i + 8, dbx->r[i+8]); 117 118 cpuUnpackSrBits(dbx->sr_hfsr_cfsr_lo, dbx->magic & HARD_FAULT_DROPBOX_MAGIC_DATA_MASK, &sr, &hfsr, &cfsr); 119 120 osLog(LOG_ERROR, " xPSR = 0x%08lX HFSR = 0x%08lX\n", sr, hfsr); 121 osLog(LOG_ERROR, " CFSR = 0x%08lX BITS = 0x%08lX\n", cfsr, dbx->bits); 122 // reboot source (if known), reported as TRIG 123 // so far we have 2 reboot sources reported here: 124 // 1 - HARD FAULT 125 // 2 - WDT 126 code = dbx->trig; 127 trigName = trigNames[code < ARRAY_SIZE(trigNames) ? code : 0]; 128 osLog(LOG_ERROR, " TID = 0x%04" PRIX16 " TRIG = 0x%04" PRIX16 " [%s]\n", dbx->tid, dbx->trig, trigName); 129 } 130} 131 132void cpuInitLate(void) 133{ 134 struct RamPersistedDataAndDropbox *dbx = getInitedPersistedData(); 135 uint32_t reason = pwrResetReason(); 136 const char *reasonDesc = ""; 137 enum LogLevel level = LOG_INFO; 138 139 // if we detected WDT reboot reason, we are likely not having data in dropbox 140 // All we can do is report that WDT reset happened 141 if ((reason & (RESET_WINDOW_WATCHDOG|RESET_INDEPENDENT_WATCHDOG)) != 0) { 142 reasonDesc = "; HW WDT RESET"; 143 level = LOG_ERROR; 144 } 145 146 osLog(level, "Reboot reason: 0x%08" PRIX32 "%s\n", reason, reasonDesc); 147 148 /* print and clear dropbox */ 149 if (dbx->magic & HARD_FAULT_DROPBOX_MAGIC_HAVE_DROP) { 150 osLog(LOG_INFO, "Dropbox not empty. Contents:\n"); 151 cpuDbxDump(dbx); 152 } 153 dbx->magic &=~ HARD_FAULT_DROPBOX_MAGIC_HAVE_DROP; 154} 155 156bool cpuRamPersistentBitGet(uint32_t which) 157{ 158 struct RamPersistedDataAndDropbox *dbx = getInitedPersistedData(); 159 160 return (which < CPU_NUM_PERSISTENT_RAM_BITS) && ((dbx->bits >> which) & 1); 161} 162 163void cpuRamPersistentBitSet(uint32_t which, bool on) 164{ 165 struct RamPersistedDataAndDropbox *dbx = getInitedPersistedData(); 166 167 if (which < CPU_NUM_PERSISTENT_RAM_BITS) { 168 if (on) 169 dbx->bits |= (1ULL << which); 170 else 171 dbx->bits &=~ (1ULL << which); 172 } 173} 174 175uint64_t cpuIntsOff(void) 176{ 177 uint32_t state; 178 179 asm volatile ( 180 "mrs %0, PRIMASK \n" 181 "cpsid i \n" 182 :"=r"(state) 183 ); 184 185 return state; 186} 187 188uint64_t cpuIntsOn(void) 189{ 190 uint32_t state; 191 192 asm volatile ( 193 "mrs %0, PRIMASK \n" 194 "cpsie i \n" 195 :"=r"(state) 196 ); 197 198 return state; 199} 200 201void cpuIntsRestore(uint64_t state) 202{ 203 204 asm volatile( 205 "msr PRIMASK, %0 \n" 206 ::"r"((uint32_t)state) 207 ); 208} 209 210/* 211 * Stack layout for HW interrupt handlers [ARM7-M] 212 * =============================================== 213 * orig_SP[8...25] = FPU state (S0..S15, FPSCR, Reserved) [if enabled] 214 * orig_SP[7] = xPSR 215 * orig_SP[6] = ReturnAddress 216 * orig_SP[5] = LR (R14) 217 * orig_SP[4] = R12 218 * orig_SP[3..0] = R3..R0 219 * 220 * upon entry, new value of LR is calculated as follows: 221 * LR := 0xFFFFFFE0 | <Control.FPCA ? 0 : 0x10> | <Mode_Handler ? 0 : 8> | <SPSEL ? 4 : 0> | 0x01 222 * 223 * upon exit, PC value is interpreted according to the same rule (see LR above) 224 * if bits 28..31 of PC equal 0xF, return from interrupt is executed 225 * this way, "bx lr" would perform return from interrupt to the previous state 226 */ 227 228static void __attribute__((used)) syscallHandler(uintptr_t *excRegs) 229{ 230 uint16_t *svcPC = ((uint16_t *)(excRegs[6])) - 1; 231 uint32_t svcNo = (*svcPC) & 0xFF; 232 uint32_t syscallNr = excRegs[0]; 233 SyscallFunc handler; 234 va_list args_long = *(va_list*)(excRegs + 1); 235 uintptr_t *fastParams = excRegs + 1; 236 va_list args_fast = *(va_list*)(&fastParams); 237 238 if (svcNo > 1) 239 osLog(LOG_WARN, "Unknown SVC 0x%02lX called at 0x%08lX\n", svcNo, (unsigned long)svcPC); 240 else if (!(handler = syscallGetHandler(syscallNr))) 241 osLog(LOG_WARN, "Unknown syscall 0x%08lX called at 0x%08lX\n", (unsigned long)syscallNr, (unsigned long)svcPC); 242 else 243 handler(excRegs, svcNo ? args_fast : args_long); 244} 245 246void SVC_Handler(void); 247void __attribute__((naked)) SVC_Handler(void) 248{ 249 asm volatile( 250 "tst lr, #4 \n" 251 "ite eq \n" 252 "mrseq r0, msp \n" 253 "mrsne r0, psp \n" 254 "b syscallHandler \n" 255 ); 256} 257 258static void __attribute__((used)) logHardFault(uintptr_t *excRegs, uintptr_t* otherRegs, bool tinyStack, uint32_t code) 259{ 260 struct RamPersistedDataAndDropbox *dbx = getInitedPersistedData(); 261 uint32_t i, hi; 262 263 wdtPing(); 264 265 for (i = 0; i < 4; i++) 266 dbx->r[i] = excRegs[i]; 267 for (i = 0; i < 8; i++) 268 dbx->r[i + 4] = otherRegs[i]; 269 dbx->r[12] = excRegs[4]; 270 dbx->r[13] = (uint32_t)(excRegs + 8); 271 dbx->r[14] = excRegs[5]; 272 dbx->r[15] = excRegs[6]; 273 274 cpuPackSrBits(&dbx->sr_hfsr_cfsr_lo, &hi, excRegs[7], SCB->HFSR, SCB->CFSR); 275 dbx->magic |= HARD_FAULT_DROPBOX_MAGIC_HAVE_DROP | (hi & HARD_FAULT_DROPBOX_MAGIC_DATA_MASK); 276 dbx->tid = osGetCurrentTid(); 277 dbx->trig = code; 278 279 if (!tinyStack) { 280 osLog(LOG_ERROR, "*HARD FAULT*\n"); 281 cpuDbxDump(dbx); 282 } 283 284 NVIC_SystemReset(); 285} 286 287static uint32_t __attribute__((used)) hfStack[16]; 288 289void cpuCommonFaultCode(void); 290void __attribute__((naked)) cpuCommonFaultCode(void) 291{ 292 // r0 contains Fault IRQ code 293 asm volatile( 294 "ldr r3, =__stack_bottom + 64 \n" 295 "cmp sp, r3 \n" 296 "itte le \n" 297 "ldrle sp, =hfStack + 64 \n" 298 "movle r2, #1 \n" 299 "movgt r2, #0 \n" 300 "mov r3, r0 \n" 301 "tst lr, #4 \n" 302 "ite eq \n" 303 "mrseq r0, msp \n" 304 "mrsne r0, psp \n" 305 "push {r4-r11} \n" 306 "mov r1, sp \n" 307 "b logHardFault \n" 308 ); 309} 310 311void HardFault_Handler(void); 312void __attribute__((naked)) HardFault_Handler(void) 313{ 314 asm volatile( 315 "mov r0, #1 \n" 316 "b cpuCommonFaultCode \n" 317 ); 318} 319