Home | History | Annotate | Download | only in cortexm4
      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