1
2 // Copyright 2020 Espressif Systems (Shanghai) PTE LTD
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 #include <stdio.h>
16
17 #include "soc/extmem_reg.h"
18 #include "esp_private/panic_internal.h"
19 #include "esp_private/panic_reason.h"
20 #include "riscv/rvruntime-frames.h"
21
22 #if CONFIG_IDF_TARGET_ESP32C3
23 #include "esp32c3/cache_err_int.h"
24 #endif
25 #if CONFIG_ESP_SYSTEM_MEMPROT_FEATURE
26 #include "esp32c3/memprot.h"
27 #endif
28
29
30 #define DIM(array) (sizeof(array)/sizeof(*array))
31
32 /**
33 * Structure used to define a flag/bit to test in case of cache error.
34 * The message describes the cause of the error when the bit is set in
35 * a given status register.
36 */
37 typedef struct {
38 const uint32_t bit;
39 const char *msg;
40 } register_bit_t;
41
42 /**
43 * Function to check each bits defined in the array reg_bits in the given
44 * status register. The first bit from the array to be set in the status
45 * register will have its associated message printed. This function returns
46 * true. If not bit was set in the register, it returns false.
47 * The order of the bits in the array is important as only the first bit to
48 * be set in the register will have its associated message printed.
49 */
test_and_print_register_bits(const uint32_t status,const register_bit_t * reg_bits,const uint32_t size)50 static inline bool test_and_print_register_bits(const uint32_t status,
51 const register_bit_t *reg_bits,
52 const uint32_t size)
53 {
54 /* Browse the flag/bit array and test each one with the given status
55 * register. */
56 for (int i = 0; i < size; i++) {
57 const uint32_t bit = reg_bits[i].bit;
58 if ((status & bit) == bit) {
59 /* Reason of the panic found, print the reason. */
60 panic_print_str(reg_bits[i].msg);
61 panic_print_str("\r\n");
62
63 return true;
64 }
65 }
66
67 /* Panic cause not found, no message was printed. */
68 return false;
69 }
70
71 /**
72 * Function called when a cache error occurs. It prints details such as the
73 * explanation of why the panic occured.
74 */
print_cache_err_details(const void * frame)75 static inline void print_cache_err_details(const void *frame)
76 {
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 }
150
151
152 /**
153 * Function called when a memory protection error occurs (PMS). It prints details such as the
154 * explanation of why the panic occured.
155 */
156 #if CONFIG_ESP_SYSTEM_MEMPROT_FEATURE
print_memprot_err_details(const void * frame)157 static inline void print_memprot_err_details(const void *frame __attribute__((unused)))
158 {
159 //common memprot fault info
160 mem_type_prot_t mem_type = esp_memprot_get_active_intr_memtype();
161 panic_print_str( " memory type: ");
162 panic_print_str( esp_memprot_mem_type_to_str(mem_type) );
163 panic_print_str( "\r\n faulting address: 0x");
164 panic_print_hex( esp_memprot_get_violate_addr(mem_type) );
165 panic_print_str( "\r\n world:");
166 panic_print_dec( esp_memprot_get_violate_world(mem_type) );
167
168 char operation = 0;
169 // IRAM fault: check instruction-fetch flag
170 if ( mem_type == MEMPROT_IRAM0_SRAM ) {
171 if ( esp_memprot_get_violate_loadstore(mem_type) ) {
172 operation = 'X';
173 }
174 }
175 // W/R - common
176 if ( operation == 0 ) {
177 operation = esp_memprot_get_violate_wr(mem_type) == MEMPROT_PMS_OP_WRITE ? 'W' : 'R';
178 }
179 panic_print_str( "\r\n operation type: ");
180 panic_print_char( operation );
181
182 // DRAM/DMA fault: check byte-enables
183 if ( mem_type == MEMPROT_DRAM0_SRAM ) {
184 panic_print_str("\r\n byte-enables: " );
185 panic_print_hex(esp_memprot_get_violate_byte_en(mem_type));
186 }
187
188 panic_print_str("\r\n");
189 }
190 #endif
191
panic_print_registers(const void * f,int core)192 void panic_print_registers(const void *f, int core)
193 {
194 uint32_t *regs = (uint32_t *)f;
195
196 // only print ABI name
197 const char *desc[] = {
198 "MEPC ", "RA ", "SP ", "GP ", "TP ", "T0 ", "T1 ", "T2 ",
199 "S0/FP ", "S1 ", "A0 ", "A1 ", "A2 ", "A3 ", "A4 ", "A5 ",
200 "A6 ", "A7 ", "S2 ", "S3 ", "S4 ", "S5 ", "S6 ", "S7 ",
201 "S8 ", "S9 ", "S10 ", "S11 ", "T3 ", "T4 ", "T5 ", "T6 ",
202 "MSTATUS ", "MTVEC ", "MCAUSE ", "MTVAL ", "MHARTID "
203 };
204
205 panic_print_str("Core ");
206 panic_print_dec(((RvExcFrame *)f)->mhartid);
207 panic_print_str(" register dump:");
208
209 for (int x = 0; x < sizeof(desc) / sizeof(desc[0]); x += 4) {
210 panic_print_str("\r\n");
211 for (int y = 0; y < 4 && x + y < sizeof(desc) / sizeof(desc[0]); y++) {
212 if (desc[x + y][0] != 0) {
213 panic_print_str(desc[x + y]);
214 panic_print_str(": 0x");
215 panic_print_hex(regs[x + y]);
216 panic_print_str(" ");
217 }
218 }
219 }
220 }
221
222 /**
223 * This function will be called when a SoC-level panic occurs.
224 * SoC-level panics include cache errors and watchdog interrupts.
225 */
panic_soc_fill_info(void * f,panic_info_t * info)226 void panic_soc_fill_info(void *f, panic_info_t *info)
227 {
228 RvExcFrame *frame = (RvExcFrame *) f;
229
230 /* Please keep in sync with PANIC_RSN_* defines */
231 static const char *pseudo_reason[PANIC_RSN_COUNT] = {
232 "Unknown reason",
233 "Interrupt wdt timeout on CPU0",
234 #if SOC_CPU_NUM > 1
235 "Interrupt wdt timeout on CPU1",
236 #endif
237 "Cache error",
238 #if CONFIG_ESP_SYSTEM_MEMPROT_FEATURE
239 "Memory protection fault",
240 #endif
241 };
242
243 info->reason = pseudo_reason[0];
244 info->addr = (void *) frame->mepc;
245
246 /* The mcause has been set by the CPU when the panic occured.
247 * All SoC-level panic will call this function, thus, this register
248 * lets us know which error was triggered. */
249 if (frame->mcause == ETS_CACHEERR_INUM) {
250 /* Panic due to a cache error, multiple cache error are possible,
251 * assign function print_cache_err_details to our structure's
252 * details field. As its name states, it will give more details
253 * about why the error happened. */
254
255 info->core = esp_cache_err_get_cpuid();
256 info->reason = pseudo_reason[PANIC_RSN_CACHEERR];
257 info->details = print_cache_err_details;
258
259 } else if (frame->mcause == ETS_T1_WDT_INUM) {
260 /* Watchdog interrupt occured, get the core on which it happened
261 * and update the reason/message accordingly. */
262
263 const int core = esp_cache_err_get_cpuid();
264 info->core = core;
265 info->exception = PANIC_EXCEPTION_IWDT;
266
267 #if SOC_CPU_NUM > 1
268 _Static_assert(PANIC_RSN_INTWDT_CPU0 + 1 == PANIC_RSN_INTWDT_CPU1,
269 "PANIC_RSN_INTWDT_CPU1 must be equal to PANIC_RSN_INTWDT_CPU0 + 1");
270 #endif
271 info->reason = pseudo_reason[PANIC_RSN_INTWDT_CPU0 + core];
272 }
273 #if CONFIG_ESP_SYSTEM_MEMPROT_FEATURE
274 else if ( frame->mcause == ETS_MEMPROT_ERR_INUM ) {
275
276 info->core = esp_memprot_intr_get_cpuid();
277 info->reason = pseudo_reason[PANIC_RSN_MEMPROT];
278 info->details = print_memprot_err_details;
279 }
280 #endif
281 }
282
panic_arch_fill_info(void * frame,panic_info_t * info)283 void panic_arch_fill_info(void *frame, panic_info_t *info)
284 {
285 RvExcFrame *regs = (RvExcFrame *) frame;
286 info->core = 0;
287 info->exception = PANIC_EXCEPTION_FAULT;
288
289 //Please keep in sync with PANIC_RSN_* defines
290 static const char *reason[] = {
291 "Instruction address misaligned",
292 "Instruction access fault",
293 "Illegal instruction",
294 "Breakpoint",
295 "Load address misaligned",
296 "Load access fault",
297 "Store address misaligned",
298 "Store access fault",
299 "Environment call from U-mode",
300 "Environment call from S-mode",
301 NULL,
302 "Environment call from M-mode",
303 "Instruction page fault",
304 "Load page fault",
305 NULL,
306 "Store page fault",
307 };
308
309 if (regs->mcause < (sizeof(reason) / sizeof(reason[0]))) {
310 if (reason[regs->mcause] != NULL) {
311 info->reason = (reason[regs->mcause]);
312 }
313 }
314
315 info->description = "Exception was unhandled.";
316
317 info->addr = (void *) regs->mepc;
318 info->frame = ®s;
319 }
320
panic_print_backtrace(const void * frame,int core)321 void panic_print_backtrace(const void *frame, int core)
322 {
323 // Basic backtrace
324 panic_print_str("\r\nStack memory:\r\n");
325 uint32_t sp = (uint32_t)((RvExcFrame *)frame)->sp;
326 const int per_line = 8;
327 for (int x = 0; x < 1024; x += per_line * sizeof(uint32_t)) {
328 uint32_t *spp = (uint32_t *)(sp + x);
329 panic_print_hex(sp + x);
330 panic_print_str(": ");
331 for (int y = 0; y < per_line; y++) {
332 panic_print_str("0x");
333 panic_print_hex(spp[y]);
334 panic_print_str(y == per_line - 1 ? "\r\n" : " ");
335 }
336 }
337 }
338
panic_get_address(const void * f)339 uint32_t panic_get_address(const void *f)
340 {
341 return ((RvExcFrame *)f)->mepc;
342 }
343
panic_get_cause(const void * f)344 uint32_t panic_get_cause(const void *f)
345 {
346 return ((RvExcFrame *)f)->mcause;
347 }
348
panic_set_address(void * f,uint32_t addr)349 void panic_set_address(void *f, uint32_t addr)
350 {
351 ((RvExcFrame *)f)->mepc = addr;
352 }
353