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