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
getPersistedData(void)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
getInitedPersistedData(void)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
cpuInit(void)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
cpuPackSrBits(uint32_t * dstLo,uint32_t * dstHi,uint32_t sr,uint32_t hfsr,uint32_t cfsr)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
cpuUnpackSrBits(uint32_t srcLo,uint32_t srcHi,uint32_t * srP,uint32_t * hfsrP,uint32_t * cfsrP)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
cpuDbxDump(struct RamPersistedDataAndDropbox * dbx)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
cpuInitLate(void)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
cpuRamPersistentBitGet(uint32_t which)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
cpuRamPersistentBitSet(uint32_t which,bool on)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
cpuIntsOff(void)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
cpuIntsOn(void)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
cpuIntsRestore(uint64_t state)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
syscallHandler(uintptr_t * excRegs)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);
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
logHardFault(uintptr_t * excRegs,uintptr_t * otherRegs,bool tinyStack,uint32_t code)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);
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);
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