summaryrefslogtreecommitdiff
path: root/firmware/os/cpu/cortexm4/cpu.c
blob: 927db35906802ab4385d7d90a84cab853879f4ab (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
/*
 * 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 <plat/cmsis.h>
#include <plat/plat.h>
#include <plat/pwr.h>
#include <plat/wdt.h>
#include <syscall.h>
#include <string.h>
#include <seos.h>
#include <heap.h>
#include <cpu.h>
#include <util.h>
#include <reset.h>


#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 | <Control.FPCA ? 0 : 0x10> | <Mode_Handler ? 0 : 8> | <SPSEL ? 4 : 0> | 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"
    );
}