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