1 /*
2  * SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <stdio.h>
8 
9 #include "spi_flash_mmap.h"
10 
11 #include "soc/extmem_reg.h"
12 #include "esp_private/panic_internal.h"
13 #include "esp_private/panic_reason.h"
14 #include "riscv/rvruntime-frames.h"
15 #include "esp_private/cache_err_int.h"
16 #include "soc/timer_periph.h"
17 
18 #if CONFIG_ESP_SYSTEM_MEMPROT_FEATURE
19 #include "esp_private/esp_memprot_internal.h"
20 #include "esp_memprot.h"
21 #endif
22 
23 #if CONFIG_ESP_SYSTEM_USE_EH_FRAME
24 #include "esp_private/eh_frame_parser.h"
25 #include "esp_private/cache_utils.h"
26 #endif
27 
28 
29 #define DIM(array) (sizeof(array)/sizeof(*array))
30 
31 /**
32  * Structure used to define a flag/bit to test in case of cache error.
33  * The message describes the cause of the error when the bit is set in
34  * a given status register.
35  */
36 typedef struct {
37     const uint32_t bit;
38     const char *msg;
39 } register_bit_t;
40 
41 /**
42  * Function to check each bits defined in the array reg_bits in the given
43  * status register. The first bit from the array to be set in the status
44  * register will have its associated message printed. This function returns
45  * true. If not bit was set in the register, it returns false.
46  * The order of the bits in the array is important as only the first bit to
47  * be set in the register will have its associated message printed.
48  */
test_and_print_register_bits(const uint32_t status,const register_bit_t * reg_bits,const uint32_t size)49 static inline bool test_and_print_register_bits(const uint32_t status,
50         const register_bit_t *reg_bits,
51         const uint32_t size)
52 {
53     /* Browse the flag/bit array and test each one with the given status
54      * register. */
55     for (int i = 0; i < size; i++) {
56         const uint32_t bit = reg_bits[i].bit;
57         if ((status & bit) == bit) {
58             /* Reason of the panic found, print the reason. */
59             panic_print_str(reg_bits[i].msg);
60             panic_print_str("\r\n");
61 
62             return true;
63         }
64     }
65 
66     /* Panic cause not found, no message was printed. */
67     return false;
68 }
69 
70 /**
71  * Function called when a cache error occurs. It prints details such as the
72  * explanation of why the panic occured.
73  */
print_cache_err_details(const void * frame)74 static inline void print_cache_err_details(const void *frame)
75 {
76 #if !CONFIG_IDF_TARGET_ESP32C6 && !CONFIG_IDF_TARGET_ESP32H2 // ESP32C6-TODO, ESP32H2-TODO: IDF-5657
77     /* Define the array that contains the status (bits) to test on the register
78      * EXTMEM_CORE0_ACS_CACHE_INT_ST_REG. each bit is accompanied by a small
79      * message.
80      * The messages have been pulled from the header file where the status bit
81      * are defined. */
82     const register_bit_t core0_acs_bits[] = {
83         {
84             .bit = EXTMEM_CORE0_DBUS_WR_ICACHE_ST,
85             .msg = "dbus tried to write cache"
86         },
87         {
88             .bit = EXTMEM_CORE0_DBUS_REJECT_ST,
89             .msg = "dbus authentication failed"
90         },
91         {
92             .bit = EXTMEM_CORE0_DBUS_ACS_MSK_ICACHE_ST,
93             .msg = "access to cache while dbus or cache is disabled"
94         },
95         {
96             .bit = EXTMEM_CORE0_IBUS_REJECT_ST,
97             .msg = "ibus authentication failed"
98         },
99         {
100             .bit = EXTMEM_CORE0_IBUS_WR_ICACHE_ST,
101             .msg = "ibus tried to write cache"
102         },
103         {
104             .bit = EXTMEM_CORE0_IBUS_ACS_MSK_ICACHE_ST,
105             .msg = "access to cache while ibus or cache is disabled"
106         },
107     };
108 
109     /* Same goes for the register EXTMEM_CACHE_ILG_INT_ST_REG and its bits. */
110     const register_bit_t cache_ilg_bits[] = {
111         {
112             .bit = EXTMEM_MMU_ENTRY_FAULT_ST,
113             .msg = "MMU entry fault"
114         },
115         {
116             .bit = EXTMEM_ICACHE_PRELOAD_OP_FAULT_ST,
117             .msg = "preload configurations fault"
118         },
119         {
120             .bit = EXTMEM_ICACHE_SYNC_OP_FAULT_ST,
121             .msg = "sync configurations fault"
122         },
123     };
124 
125     /* Read the status register EXTMEM_CORE0_ACS_CACHE_INT_ST_REG. This status
126      * register is not equal to 0 when a cache access error occured. */
127     const uint32_t core0_status = REG_READ(EXTMEM_CORE0_ACS_CACHE_INT_ST_REG);
128 
129     /* If the panic is due to a cache access error, one of the bit of the
130      * register is set. Thus, this function will return true. */
131     bool handled = test_and_print_register_bits(core0_status, core0_acs_bits, DIM(core0_acs_bits));
132 
133     /* If the panic was due to a cache illegal error, the previous call returned false and this
134      * EXTMEM_CACHE_ILG_INT_ST_REG register should not me equal to 0.
135      * Check each bit of it and print the message associated if found. */
136     if (!handled) {
137         const uint32_t cache_ilg_status = REG_READ(EXTMEM_CACHE_ILG_INT_ST_REG);
138         handled = test_and_print_register_bits(cache_ilg_status, cache_ilg_bits, DIM(cache_ilg_bits));
139 
140         /* If the error was not found, print the both registers value */
141         if (!handled) {
142             panic_print_str("EXTMEM_CORE0_ACS_CACHE_INT_ST_REG = 0x");
143             panic_print_hex(core0_status);
144             panic_print_str("\r\nEXTMEM_CACHE_ILG_INT_ST_REG = 0x");
145             panic_print_hex(cache_ilg_status);
146             panic_print_str("\r\n");
147         }
148     }
149 #endif
150 }
151 
152 
153 /**
154  * Function called when a memory protection error occurs (PMS). It prints details such as the
155  * explanation of why the panic occured.
156  */
157 #if CONFIG_ESP_SYSTEM_MEMPROT_FEATURE
158 
159 static esp_memp_intr_source_t s_memp_intr = {MEMPROT_TYPE_INVALID, -1};
160 
161 #define PRINT_MEMPROT_ERROR(err) \
162         do { \
163             panic_print_str("N/A (error "); \
164             panic_print_str(esp_err_to_name(err)); \
165             panic_print_str(")"); \
166         } while(0)
167 
print_memprot_err_details(const void * frame)168 static inline void print_memprot_err_details(const void *frame __attribute__((unused)))
169 {
170     if (s_memp_intr.mem_type == MEMPROT_TYPE_INVALID && s_memp_intr.core == -1) {
171         panic_print_str("  - no details available -\r\n");
172         return;
173     }
174 
175     //common memprot fault info
176     panic_print_str("  memory type: ");
177     panic_print_str(esp_mprot_mem_type_to_str(s_memp_intr.mem_type));
178 
179     panic_print_str("\r\n  faulting address: ");
180     void *faulting_addr;
181     esp_err_t res = esp_mprot_get_violate_addr(s_memp_intr.mem_type, &faulting_addr, s_memp_intr.core);
182     if (res == ESP_OK) {
183         panic_print_str("0x");
184         panic_print_hex((int)faulting_addr);
185     } else {
186         PRINT_MEMPROT_ERROR(res);
187     }
188 
189     panic_print_str( "\r\n  world: ");
190     esp_mprot_pms_world_t world;
191     res = esp_mprot_get_violate_world(s_memp_intr.mem_type, &world, s_memp_intr.core);
192     if (res == ESP_OK) {
193         panic_print_str(esp_mprot_pms_world_to_str(world));
194     } else {
195         PRINT_MEMPROT_ERROR(res);
196     }
197 
198     panic_print_str( "\r\n  operation type: ");
199     uint32_t operation;
200     res = esp_mprot_get_violate_operation(s_memp_intr.mem_type, &operation, s_memp_intr.core);
201     if (res == ESP_OK) {
202         panic_print_str(esp_mprot_oper_type_to_str(operation));
203     } else {
204         PRINT_MEMPROT_ERROR(res);
205     }
206 
207     if (esp_mprot_has_byte_enables(s_memp_intr.mem_type)) {
208         panic_print_str("\r\n  byte-enables: " );
209         uint32_t byte_enables;
210         res = esp_mprot_get_violate_byte_enables(s_memp_intr.mem_type, &byte_enables, s_memp_intr.core);
211         if (res == ESP_OK) {
212             panic_print_hex(byte_enables);
213         } else {
214             PRINT_MEMPROT_ERROR(res);
215         }
216     }
217 
218     panic_print_str("\r\n");
219 }
220 #endif
221 
panic_print_registers(const void * f,int core)222 void panic_print_registers(const void *f, int core)
223 {
224     uint32_t *regs = (uint32_t *)f;
225 
226     // only print ABI name
227     const char *desc[] = {
228         "MEPC    ", "RA      ", "SP      ", "GP      ", "TP      ", "T0      ", "T1      ", "T2      ",
229         "S0/FP   ", "S1      ", "A0      ", "A1      ", "A2      ", "A3      ", "A4      ", "A5      ",
230         "A6      ", "A7      ", "S2      ", "S3      ", "S4      ", "S5      ", "S6      ", "S7      ",
231         "S8      ", "S9      ", "S10     ", "S11     ", "T3      ", "T4      ", "T5      ", "T6      ",
232         "MSTATUS ", "MTVEC   ", "MCAUSE  ", "MTVAL   ", "MHARTID "
233     };
234 
235     panic_print_str("Core ");
236     panic_print_dec(((RvExcFrame *)f)->mhartid);
237     panic_print_str(" register dump:");
238 
239     for (int x = 0; x < sizeof(desc) / sizeof(desc[0]); x += 4) {
240         panic_print_str("\r\n");
241         for (int y = 0; y < 4 && x + y < sizeof(desc) / sizeof(desc[0]); y++) {
242             if (desc[x + y][0] != 0) {
243                 panic_print_str(desc[x + y]);
244                 panic_print_str(": 0x");
245                 panic_print_hex(regs[x + y]);
246                 panic_print_str("  ");
247             }
248         }
249     }
250 }
251 
252 /**
253  * This function will be called when a SoC-level panic occurs.
254  * SoC-level panics include cache errors and watchdog interrupts.
255  */
panic_soc_fill_info(void * f,panic_info_t * info)256 void panic_soc_fill_info(void *f, panic_info_t *info)
257 {
258     RvExcFrame *frame = (RvExcFrame *) f;
259 
260     /* Please keep in sync with PANIC_RSN_* defines */
261     static const char *pseudo_reason[PANIC_RSN_COUNT] = {
262         "Unknown reason",
263         "Interrupt wdt timeout on CPU0",
264 #if SOC_CPU_NUM > 1
265         "Interrupt wdt timeout on CPU1",
266 #endif
267         "Cache error",
268 #if CONFIG_ESP_SYSTEM_MEMPROT_FEATURE
269         "Memory protection fault",
270 #endif
271     };
272 
273     info->reason = pseudo_reason[0];
274     info->addr = (void *) frame->mepc;
275 
276     /* The mcause has been set by the CPU when the panic occured.
277      * All SoC-level panic will call this function, thus, this register
278      * lets us know which error was triggered. */
279     if (frame->mcause == ETS_CACHEERR_INUM) {
280         /* Panic due to a cache error, multiple cache error are possible,
281          * assign function print_cache_err_details to our structure's
282          * details field. As its name states, it will give more details
283          * about why the error happened. */
284 
285         info->core = esp_cache_err_get_cpuid();
286         info->reason = pseudo_reason[PANIC_RSN_CACHEERR];
287         info->details = print_cache_err_details;
288 
289     } else if (frame->mcause == ETS_INT_WDT_INUM) {
290         /* Watchdog interrupt occured, get the core on which it happened
291          * and update the reason/message accordingly. */
292 
293         const int core = esp_cache_err_get_cpuid();
294         info->core = core;
295         info->exception = PANIC_EXCEPTION_IWDT;
296 
297 #if SOC_CPU_NUM > 1
298         _Static_assert(PANIC_RSN_INTWDT_CPU0 + 1 == PANIC_RSN_INTWDT_CPU1,
299                        "PANIC_RSN_INTWDT_CPU1 must be equal to PANIC_RSN_INTWDT_CPU0 + 1");
300 #endif
301         info->reason = pseudo_reason[PANIC_RSN_INTWDT_CPU0 + core];
302     }
303 #if CONFIG_ESP_SYSTEM_MEMPROT_FEATURE
304     else if (frame->mcause == ETS_MEMPROT_ERR_INUM) {
305         info->reason = pseudo_reason[PANIC_RSN_MEMPROT];
306         info->details = print_memprot_err_details;
307         info->core = esp_mprot_get_active_intr(&s_memp_intr) == ESP_OK ? s_memp_intr.core : -1;
308     }
309 #endif
310 }
311 
panic_arch_fill_info(void * frame,panic_info_t * info)312 void panic_arch_fill_info(void *frame, panic_info_t *info)
313 {
314     RvExcFrame *regs = (RvExcFrame *) frame;
315     info->core = 0;
316     info->exception = PANIC_EXCEPTION_FAULT;
317 
318     //Please keep in sync with PANIC_RSN_* defines
319     static const char *reason[] = {
320         "Instruction address misaligned",
321         "Instruction access fault",
322         "Illegal instruction",
323         "Breakpoint",
324         "Load address misaligned",
325         "Load access fault",
326         "Store address misaligned",
327         "Store access fault",
328         "Environment call from U-mode",
329         "Environment call from S-mode",
330         NULL,
331         "Environment call from M-mode",
332         "Instruction page fault",
333         "Load page fault",
334         NULL,
335         "Store page fault",
336     };
337 
338     if (regs->mcause < (sizeof(reason) / sizeof(reason[0]))) {
339         if (reason[regs->mcause] != NULL) {
340             info->reason = (reason[regs->mcause]);
341         }
342     }
343 
344     info->description = "Exception was unhandled.";
345 
346     info->addr = (void *) regs->mepc;
347 }
348 
panic_print_basic_backtrace(const void * frame,int core)349 static void panic_print_basic_backtrace(const void *frame, int core)
350 {
351     // Basic backtrace
352     panic_print_str("\r\nStack memory:\r\n");
353     uint32_t sp = (uint32_t)((RvExcFrame *)frame)->sp;
354     const int per_line = 8;
355     for (int x = 0; x < 1024; x += per_line * sizeof(uint32_t)) {
356         uint32_t *spp = (uint32_t *)(sp + x);
357         panic_print_hex(sp + x);
358         panic_print_str(": ");
359         for (int y = 0; y < per_line; y++) {
360             panic_print_str("0x");
361             panic_print_hex(spp[y]);
362             panic_print_str(y == per_line - 1 ? "\r\n" : " ");
363         }
364     }
365 }
366 
panic_print_backtrace(const void * frame,int core)367 void panic_print_backtrace(const void *frame, int core)
368 {
369 #if CONFIG_ESP_SYSTEM_USE_EH_FRAME
370     if (!spi_flash_cache_enabled()) {
371         panic_print_str("\r\nWarning: SPI Flash cache is disabled, cannot process eh_frame parsing. "
372                         "Falling back to basic backtrace.\r\n");
373         panic_print_basic_backtrace(frame, core);
374     } else {
375         esp_eh_frame_print_backtrace(frame);
376     }
377 #else
378     panic_print_basic_backtrace(frame, core);
379 #endif
380 }
381 
panic_get_address(const void * f)382 uint32_t panic_get_address(const void *f)
383 {
384     return ((RvExcFrame *)f)->mepc;
385 }
386 
panic_get_cause(const void * f)387 uint32_t panic_get_cause(const void *f)
388 {
389     return ((RvExcFrame *)f)->mcause;
390 }
391 
panic_set_address(void * f,uint32_t addr)392 void panic_set_address(void *f, uint32_t addr)
393 {
394     ((RvExcFrame *)f)->mepc = addr;
395 }
396