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