1 // Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 /**
16 * @file
17 * @brief Core dump port implementation for RISC-V based boards.
18 */
19
20 #include <string.h>
21 #include <stdbool.h>
22 #include "soc/soc_memory_layout.h"
23 #include "freertos/FreeRTOS.h"
24 #include "freertos/task.h"
25 #include "riscv/rvruntime-frames.h"
26 #include "esp_rom_sys.h"
27 #include "esp_core_dump_common.h"
28 #include "esp_core_dump_port.h"
29
30 /* TAG used for logs */
31 const static DRAM_ATTR char TAG[] __attribute__((unused)) = "esp_core_dump_port";
32
33 /* Code associated to RISC-V in ELF format */
34 #define COREDUMP_EM_RISCV 0xF3
35
36 #define COREDUMP_INVALID_CAUSE_VALUE 0xFFFF
37 #define COREDUMP_FAKE_STACK_START 0x20000000U
38 #define COREDUMP_FAKE_STACK_LIMIT 0x30000000U
39
40 #if CONFIG_ESP_COREDUMP_ENABLE
41
42 #define min(a,b) ((a) < (b) ? (a) : (b))
43
44 /**
45 * Union representing the registers of the CPU as they will be written
46 * in the core dump.
47 * Registers can be adressed with their names thanks to the structure, or as
48 * an array of 32 words.
49 */
50 #define RISCV_GP_REGS_COUNT 32
51
52 typedef union {
53 struct {
54 uint32_t pc;
55 uint32_t ra;
56 uint32_t sp;
57 uint32_t gp;
58 uint32_t tp;
59 uint32_t t0;
60 uint32_t t1;
61 uint32_t t2;
62 uint32_t s0;
63 uint32_t s1;
64 uint32_t a0;
65 uint32_t a1;
66 uint32_t a2;
67 uint32_t a3;
68 uint32_t a4;
69 uint32_t a5;
70 uint32_t a6;
71 uint32_t a7;
72 uint32_t s2;
73 uint32_t s3;
74 uint32_t s4;
75 uint32_t s5;
76 uint32_t s6;
77 uint32_t s7;
78 uint32_t s8;
79 uint32_t s9;
80 uint32_t s10;
81 uint32_t s11;
82 uint32_t t3;
83 uint32_t t4;
84 uint32_t t5;
85 uint32_t t6;
86 };
87
88 uint32_t as_array[RISCV_GP_REGS_COUNT];
89 } riscv_regs;
90
91 /**
92 * The following structure represents the NOTE section in the coredump.
93 * Its type must be PR_STATUS as it contains the regsiters values and the
94 * program status.
95 * As our coredump will be used with GDB, we only need to fill the info
96 * it needs. We are going to use the macros taken from GDB's elf32-riscv.c
97 * Here is the structure GDB needs for PRSTATUS note:
98 *
99 * +---------------------------+--------------+---------------------------------+
100 * | Offset | Size (bytes) | Data |
101 * +---------------------------+--------------+---------------------------------+
102 * | PRSTATUS_OFFSET_PR_CURSIG | 2 | Signal that triggered the panic |
103 * | PRSTATUS_OFFSET_PR_PID | 4 | PID |
104 * | PRSTATUS_OFFSET_PR_REG | 32 | Registers value |
105 * +---------------------------+--------------+---------------------------------+
106 *
107 * Other fields are not strictly required by GDB, we can then replace them
108 * by a padding. Among these fields, we can find PPID, SID or system time.
109 */
110 #define PRSTATUS_SIZE 204
111 #define PRSTATUS_OFFSET_PR_CURSIG 12
112 #define PRSTATUS_OFFSET_PR_PID 24
113 #define PRSTATUS_OFFSET_PR_REG 72
114 #define ELF_GREGSET_T_SIZE 128
115
116 /* We can determine the padding thank to the previous macros */
117 #define PRSTATUS_SIG_PADDING (PRSTATUS_OFFSET_PR_CURSIG)
118 #define PRSTATUS_PID_PADDING (PRSTATUS_OFFSET_PR_PID - PRSTATUS_OFFSET_PR_CURSIG - sizeof(uint16_t))
119 #define PRSTATUS_REG_PADDING (PRSTATUS_OFFSET_PR_REG - PRSTATUS_OFFSET_PR_PID - sizeof(uint32_t))
120 #define PRSTATUS_END_PADDING (PRSTATUS_SIZE - PRSTATUS_OFFSET_PR_REG - ELF_GREGSET_T_SIZE)
121
122 typedef struct {
123 uint8_t _PADDING1[PRSTATUS_SIG_PADDING];
124 uint16_t signal;
125 uint8_t _PADDING2[PRSTATUS_PID_PADDING];
126 uint32_t pid;
127 uint8_t _PADDING3[PRSTATUS_REG_PADDING];
128 riscv_regs regs;
129 uint8_t _PADDING4[PRSTATUS_END_PADDING];
130 } riscv_prstatus;
131
132 /**
133 * Assert that our structure is designed the way we are expecting it to be.
134 */
135 _Static_assert(offsetof(riscv_prstatus, signal) == PRSTATUS_OFFSET_PR_CURSIG,
136 "Wrong offset for signal field in riscv_prstatus structure");
137 _Static_assert(offsetof(riscv_prstatus, pid) == PRSTATUS_OFFSET_PR_PID,
138 "Wrong offset for pid field in riscv_prstatus structure");
139 _Static_assert(offsetof(riscv_prstatus, regs) == PRSTATUS_OFFSET_PR_REG,
140 "Wrong offset for regs field in riscv_prstatus structure");
141 _Static_assert(sizeof(riscv_regs) == ELF_GREGSET_T_SIZE,
142 "Wrong size for riscv_regs union");
143 _Static_assert(sizeof(riscv_prstatus) == PRSTATUS_SIZE,
144 "Wrong size for riscv_prstatus structure");
145
146 /**
147 * Structure used to add some extra info inside core file.
148 */
149 typedef struct {
150 uint32_t crashed_task_tcb;
151 } riscv_extra_info_t;
152
153
154 /* Allocate the fake stack that will be used by broken tasks. */
155 static RvExcFrame s_fake_stack_frame = {
156 .mepc = COREDUMP_FAKE_STACK_START,
157 .sp = COREDUMP_FAKE_STACK_START + sizeof(RvExcFrame),
158 };
159
160 /* Keep a track of the number of fake stack distributed. Avoid giving the
161 * same fake address to two different tasks. */
162 static uint32_t s_fake_stacks_num = 0;
163
164 /* Statically initialize the extra information structure. */
165 static riscv_extra_info_t s_extra_info = { 0 };
166
esp_core_dump_port_init(panic_info_t * info)167 inline void esp_core_dump_port_init(panic_info_t *info)
168 {
169 s_extra_info.crashed_task_tcb = COREDUMP_CURR_TASK_MARKER;
170 }
171
172 /**
173 * Return the current architecture (RISC-V) ID. This will be written in the
174 * ELF file header.
175 */
esp_core_dump_get_arch_id()176 inline uint16_t esp_core_dump_get_arch_id()
177 {
178 return COREDUMP_EM_RISCV;
179 }
180
181 /**
182 * Reset fake tasks' stack counter. This lets use reuse the previously allocated
183 * fake stacks.
184 */
esp_core_dump_reset_fake_stacks(void)185 void esp_core_dump_reset_fake_stacks(void)
186 {
187 s_fake_stacks_num = 0;
188 }
189
190 /**
191 * Function generating a fake stack address for task as deep as exception
192 * frame size. It is required for GDB to take task into account but avoid
193 * backtrace of stack. The espcoredump.py script is able to recognize
194 * that task is broken.
195 */
esp_core_dump_generate_fake_stack(uint32_t * stk_addr)196 static uint32_t esp_core_dump_generate_fake_stack(uint32_t *stk_addr)
197 {
198 /* Offset of this new fake task stask. */
199 const uint32_t offset = sizeof(s_fake_stack_frame) * s_fake_stacks_num++;
200
201 /* Return the size and the fake address */
202 *stk_addr = COREDUMP_FAKE_STACK_START + offset;
203
204 return sizeof(s_fake_stack_frame);
205 }
206
207 /**
208 * Return the top of the ISR stack.
209 */
esp_core_dump_get_isr_stack_top(void)210 uint8_t* esp_core_dump_get_isr_stack_top(void)
211 {
212 extern uint8_t* xIsrStackTop;
213 return xIsrStackTop;
214 }
215
216 /**
217 * Return the end of the ISR stack .
218 */
esp_core_dump_get_isr_stack_end(void)219 uint32_t esp_core_dump_get_isr_stack_end(void)
220 {
221 uint8_t* isr_top_stack = esp_core_dump_get_isr_stack_top();
222 return (uint32_t)(isr_top_stack + (xPortGetCoreID()+1)*configISR_STACK_SIZE);
223 }
224
225 /**
226 * Check if the given stack is sane or not.
227 */
esp_core_dump_task_stack_end_is_sane(uint32_t sp)228 static inline bool esp_core_dump_task_stack_end_is_sane(uint32_t sp)
229 {
230 return esp_ptr_in_dram((void *)sp)
231 #if CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY
232 || esp_stack_ptr_in_extram(sp)
233 #endif
234 #if CONFIG_ESP_SYSTEM_ALLOW_RTC_FAST_MEM_AS_HEAP
235 || esp_ptr_in_rtc_dram_fast((void*) sp)
236 #endif
237 ;
238 }
239
esp_core_dump_check_stack(core_dump_task_header_t * task)240 bool esp_core_dump_check_stack(core_dump_task_header_t *task)
241 {
242 // Check task's stack
243 if (!esp_stack_ptr_is_sane(task->stack_start) ||
244 !esp_core_dump_task_stack_end_is_sane(task->stack_end) ||
245 (task->stack_start >= task->stack_end) ||
246 ((task->stack_end-task->stack_start) > COREDUMP_MAX_TASK_STACK_SIZE)) {
247 // Check if current task stack is corrupted
248 ESP_COREDUMP_LOG_PROCESS("Invalid stack (%x...%x)!", task->stack_start, task->stack_end);
249 return false;
250 }
251 return true;
252 }
253
254 /**
255 * Get the stack addresses (virtual and physical) and stack length of the task
256 * passed as argument.
257 * Returns the stack length.
258 */
esp_core_dump_get_stack(core_dump_task_header_t * task,uint32_t * stk_vaddr,uint32_t * stk_paddr)259 uint32_t esp_core_dump_get_stack(core_dump_task_header_t *task,
260 uint32_t* stk_vaddr, uint32_t* stk_paddr)
261 {
262 const uint32_t stack_len = abs(task->stack_start - task->stack_end);
263 const uint32_t stack_addr = min(task->stack_start, task->stack_end);
264
265 ESP_COREDUMP_DEBUG_ASSERT(stk_paddr != NULL && stk_vaddr != NULL);
266
267 /* Provide the virtual stack address for any task. */
268 *stk_vaddr = stack_addr;
269
270 if (stack_addr >= COREDUMP_FAKE_STACK_START &&
271 stack_addr < COREDUMP_FAKE_STACK_LIMIT) {
272 /* In this case, the stack address pointed by the task is a fake stack
273 * generated previously. So it doesn't really point to actual data.
274 * Thus, we must provide the address of the fake stack data. */
275 *stk_paddr = (uint32_t) &s_fake_stack_frame;
276 } else {
277 *stk_paddr = stack_addr;
278 }
279
280 return stack_len;
281 }
282
283 /**
284 * Check the task passed as a parameter.
285 */
esp_core_dump_check_task(core_dump_task_header_t * task)286 bool esp_core_dump_check_task(core_dump_task_header_t *task)
287 {
288 bool is_stack_sane = false;
289
290 if (!esp_core_dump_tcb_addr_is_sane((uint32_t)task->tcb_addr)) {
291 ESP_COREDUMP_LOG_PROCESS("Bad TCB addr=%x!", task->tcb_addr);
292 return false;
293 }
294
295 /* Check the sanity of the task's stack. If the stack is corrupted, replace
296 * it with a fake stack containing fake registers value. This is required
297 * by GDB. */
298 is_stack_sane = esp_core_dump_check_stack(task);
299
300 if (!is_stack_sane) {
301 const uint32_t stk_size = esp_core_dump_generate_fake_stack(&task->stack_start);
302 task->stack_end = (uint32_t)(task->stack_start + stk_size);
303 }
304
305 return true;
306 }
307
308 /**
309 * Check if the memory segment is sane
310 *
311 * Check the header file for more information.
312 */
esp_core_dump_mem_seg_is_sane(uint32_t addr,uint32_t sz)313 bool esp_core_dump_mem_seg_is_sane(uint32_t addr, uint32_t sz)
314 {
315 //TODO: external SRAM not supported yet
316 return (esp_ptr_in_dram((void *)addr) && esp_ptr_in_dram((void *)(addr+sz-1)))
317 || (esp_ptr_in_rtc_slow((void *)addr) && esp_ptr_in_rtc_slow((void *)(addr+sz-1)))
318 || (esp_ptr_in_rtc_dram_fast((void *)addr) && esp_ptr_in_rtc_dram_fast((void *)(addr+sz-1)))
319 || (esp_ptr_in_iram((void *)addr) && esp_ptr_in_iram((void *)(addr+sz-1)));
320 }
321
322 /**
323 * Get the task's registers dump when the panic occured.
324 * Returns the size, in bytes, of the data pointed by reg_dumps.
325 * The data pointed by reg_dump are allocated statically, thus, they must be
326 * used (or copied) before calling this function again.
327 */
esp_core_dump_get_task_regs_dump(core_dump_task_header_t * task,void ** reg_dump)328 uint32_t esp_core_dump_get_task_regs_dump(core_dump_task_header_t *task, void **reg_dump)
329 {
330 static riscv_prstatus prstatus = { 0 };
331 RvExcFrame* exc_frame = NULL;
332 uint32_t stack_vaddr = 0;
333 uint32_t stack_paddr = 0;
334 uint32_t stack_len = 0;
335
336 ESP_COREDUMP_LOG_PROCESS("Add regs for task 0x%x", task->tcb_addr);
337
338 stack_len = esp_core_dump_get_stack(task, &stack_vaddr, &stack_paddr);
339
340 if (stack_len < sizeof(RvExcFrame)) {
341 ESP_COREDUMP_LOGE("Too small stack to keep frame: %d bytes!", stack_len);
342 }
343
344 /* The RISC-V frame has been saved at the top of the stack for the
345 * pre-empted tasks. We can then retrieve the registers by performing
346 * a cast on the stack address. For the current crashed task, the stack
347 * address has been adjusted by esp_core_dump_check_task function. */
348 exc_frame = (RvExcFrame*) stack_paddr;
349
350 /* Fill the PR_STATUS structure and copy the registers from the stack frame to it. */
351 prstatus.signal = 0;
352 prstatus.pid = (uint32_t)task->tcb_addr;
353 memcpy(prstatus.regs.as_array, exc_frame, sizeof(riscv_regs));
354
355 *reg_dump = &prstatus;
356 return sizeof(riscv_prstatus);
357 }
358
359 /**
360 * Save the crashed task handle in the extra info structure.
361 */
esp_core_dump_port_set_crashed_tcb(uint32_t handle)362 void esp_core_dump_port_set_crashed_tcb(uint32_t handle) {
363 s_extra_info.crashed_task_tcb = handle;
364 }
365
366 /**
367 * Function returning the extra info to be written in the dedicated section in
368 * the core file.
369 * info must not be NULL, it will be affected to the extra info data.
370 * The size, in bytes, of the data pointed by info is returned.
371 */
esp_core_dump_get_extra_info(void ** info)372 uint32_t esp_core_dump_get_extra_info(void **info)
373 {
374 uint32_t size = 0;
375
376 if (info != NULL) {
377 size = sizeof(s_extra_info);
378 *info = &s_extra_info;
379 }
380
381 return size;
382 }
383
384 #if CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH && CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF
385
esp_core_dump_summary_parse_extra_info(esp_core_dump_summary_t * summary,void * ei_data)386 void esp_core_dump_summary_parse_extra_info(esp_core_dump_summary_t *summary, void *ei_data)
387 {
388 riscv_extra_info_t *ei = (riscv_extra_info_t *)ei_data;
389 summary->exc_tcb = ei->crashed_task_tcb;
390 ESP_COREDUMP_LOGD("Crash TCB 0x%x", summary->exc_tcb);
391 }
392
esp_core_dump_summary_parse_exc_regs(esp_core_dump_summary_t * summary,void * stack_data)393 void esp_core_dump_summary_parse_exc_regs(esp_core_dump_summary_t *summary, void *stack_data)
394 {
395 int i;
396 long *a_reg;
397 RvExcFrame *stack = (RvExcFrame *)stack_data;
398 summary->exc_pc = stack->mepc;
399 ESP_COREDUMP_LOGD("Crashing PC 0x%x", summary->exc_pc);
400
401 summary->ex_info.mstatus = stack->mstatus;
402 summary->ex_info.mtvec = stack->mtvec;
403 summary->ex_info.mcause = stack->mcause;
404 summary->ex_info.mtval = stack->mtval;
405 summary->ex_info.ra = stack->ra;
406 summary->ex_info.sp = stack->sp;
407 ESP_COREDUMP_LOGD("mstatus:0x%x mtvec:0x%x mcause:0x%x mval:0x%x RA: 0x%x SP: 0x%x",
408 stack->mstatus, stack->mtvec, stack->mcause, stack->mtval, stack->ra, stack->sp);
409 a_reg = &stack->a0;
410 for (i = 0; i < 8; i++) {
411 summary->ex_info.exc_a[i] = a_reg[i];
412 ESP_COREDUMP_LOGD("A[%d] 0x%x", i, summary->ex_info.exc_a[i]);
413 }
414 }
415
esp_core_dump_summary_parse_backtrace_info(esp_core_dump_bt_info_t * bt_info,const void * vaddr,const void * paddr,uint32_t stack_size)416 void esp_core_dump_summary_parse_backtrace_info(esp_core_dump_bt_info_t *bt_info, const void *vaddr,
417 const void *paddr, uint32_t stack_size)
418 {
419 if (!vaddr || !paddr || !bt_info) {
420 bt_info->dump_size = 0;
421 return;
422 }
423
424 /* Check whether the stack is a fake stack created during coredump generation
425 * If its a fake stack, we don't have any actual stack dump
426 */
427 if (vaddr >= (void*) COREDUMP_FAKE_STACK_START && vaddr < (void*) COREDUMP_FAKE_STACK_LIMIT) {
428 bt_info->dump_size = 0;
429 return;
430 }
431
432 /* Top of the stack consists of the context registers saved after crash,
433 * extract the value of stack pointer (SP) at the time of crash
434 */
435 RvExcFrame *stack = (RvExcFrame *) paddr;
436 uint32_t *sp = (uint32_t *)stack->sp;
437
438 /* vaddr is actual stack address when crash occurred. However that stack is now saved
439 * in the flash at a different location. Hence, we need to adjust the offset
440 * to point to correct data in the flash */
441 int offset = (uint32_t)stack - (uint32_t)vaddr;
442
443 // Skip the context saved register frame
444 uint32_t regframe_size = (uint32_t)sp - (uint32_t)vaddr;
445
446 uint32_t dump_size = MIN(stack_size - regframe_size, CONFIG_ESP_COREDUMP_SUMMARY_STACKDUMP_SIZE);
447
448 memcpy(&bt_info->stackdump[0], (uint8_t *)sp + offset, dump_size);
449 bt_info->dump_size = dump_size;
450 }
451
452 #endif /* #if CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH && CONFIG_ESP_COREDUMP_DATA_FORMAT_ELF */
453
454 #endif
455