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