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 #include <string.h>
16 #include "esp_gdbstub.h"
17 #include "esp_gdbstub_common.h"
18 #include "sdkconfig.h"
19 
20 #include "soc/uart_reg.h"
21 #include "soc/periph_defs.h"
22 #include "esp_attr.h"
23 #include "esp_intr_alloc.h"
24 #include "hal/wdt_hal.h"
25 #include "freertos/FreeRTOS.h"
26 #include "freertos/task.h"
27 
28 
29 #ifdef CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
30 static inline int gdb_tid_to_task_index(int tid);
31 static inline int task_index_to_gdb_tid(int tid);
32 static void init_task_info(void);
33 static void find_paniced_task_index(void);
34 static void set_active_task(size_t index);
35 static int handle_task_commands(unsigned char *cmd, int len);
36 static void esp_gdbstub_send_str_as_hex(const char *str);
37 #endif
38 
39 static void send_reason(void);
40 
41 static esp_gdbstub_scratch_t s_scratch;
42 static esp_gdbstub_gdb_regfile_t *gdb_local_regfile = &s_scratch.regfile;
43 
44 /**
45  * @brief panic handler
46 */
esp_gdbstub_panic_handler(void * in_frame)47 void esp_gdbstub_panic_handler(void *in_frame)
48 {
49     esp_gdbstub_frame_t *frame = (esp_gdbstub_frame_t *)in_frame;
50 #ifndef CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
51     esp_gdbstub_frame_to_regfile(frame, &s_scratch.regfile);
52 #else
53     if (s_scratch.state == GDBSTUB_STARTED) {
54         /* We have re-entered GDB Stub. Try disabling task support. */
55         s_scratch.state = GDBSTUB_TASK_SUPPORT_DISABLED;
56         /* Flush any pending GDB packet (this creates a garbage value) */
57         esp_gdbstub_send_end();
58     } else if (s_scratch.state == GDBSTUB_NOT_STARTED) {
59         s_scratch.state = GDBSTUB_STARTED;
60         /* Save the paniced frame and get the list of tasks */
61         memcpy(&s_scratch.paniced_frame, frame, sizeof(*frame));
62         init_task_info();
63         find_paniced_task_index();
64         /* Current task is the paniced task */
65         if (s_scratch.paniced_task_index == GDBSTUB_CUR_TASK_INDEX_UNKNOWN) {
66             set_active_task(0);
67         } else {
68             set_active_task(s_scratch.paniced_task_index);
69         }
70     }
71 #endif // CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
72 
73     esp_gdbstub_target_init();
74     s_scratch.signal = esp_gdbstub_get_signal(frame);
75     send_reason();
76     while (true) {
77         unsigned char *cmd;
78         size_t size;
79         int res = esp_gdbstub_read_command(&cmd, &size);
80         if (res > 0) {
81             /* character received instead of a command */
82             continue;
83         }
84         if (res == GDBSTUB_ST_ERR) {
85             esp_gdbstub_send_str_packet("E01");
86             continue;
87         }
88         res = esp_gdbstub_handle_command(cmd, size);
89         if (res == GDBSTUB_ST_ERR) {
90             esp_gdbstub_send_str_packet(NULL);
91         }
92     }
93 }
94 
95 /**
96  * Set interrupt reason to GDB
97 */
send_reason(void)98 static void send_reason(void)
99 {
100     esp_gdbstub_send_start();
101     esp_gdbstub_send_char('T');
102     esp_gdbstub_send_hex(s_scratch.signal, 8);
103     esp_gdbstub_send_end();
104 }
105 
106 /**
107  * Swap bytes in word
108 */
gdbstub_hton(uint32_t i)109 static uint32_t gdbstub_hton(uint32_t i)
110 {
111     return __builtin_bswap32(i);
112 }
113 
114 static wdt_hal_context_t rtc_wdt_ctx = {.inst = WDT_RWDT, .rwdt_dev = &RTCCNTL};
115 static wdt_hal_context_t wdt0_context = {.inst = WDT_MWDT0, .mwdt_dev = &TIMERG0};
116 static wdt_hal_context_t wdt1_context = {.inst = WDT_MWDT1, .mwdt_dev = &TIMERG1};
117 
118 static bool wdt0_context_enabled = false;
119 static bool wdt1_context_enabled = false;
120 static bool rtc_wdt_ctx_enabled = false;
121 /**
122  * Disable all enabled WDTs
123  */
disable_all_wdts(void)124 static inline void disable_all_wdts(void)
125 {
126     wdt0_context_enabled = wdt_hal_is_enabled(&wdt0_context);
127     wdt1_context_enabled = wdt_hal_is_enabled(&wdt1_context);
128     rtc_wdt_ctx_enabled = wdt_hal_is_enabled(&rtc_wdt_ctx);
129 
130     //Task WDT is the Main Watchdog Timer of Timer Group 0
131     if (true == wdt0_context_enabled) {
132         wdt_hal_write_protect_disable(&wdt0_context);
133         wdt_hal_disable(&wdt0_context);
134         wdt_hal_feed(&wdt0_context);
135         wdt_hal_write_protect_enable(&wdt0_context);
136     }
137 
138     //Interupt WDT is the Main Watchdog Timer of Timer Group 1
139     if (true == wdt1_context_enabled) {
140         wdt_hal_write_protect_disable(&wdt1_context);
141         wdt_hal_disable(&wdt1_context);
142         wdt_hal_feed(&wdt1_context);
143         wdt_hal_write_protect_enable(&wdt1_context);
144     }
145     if (true == rtc_wdt_ctx_enabled) {
146         wdt_hal_write_protect_disable(&rtc_wdt_ctx);
147         wdt_hal_disable(&rtc_wdt_ctx);
148         wdt_hal_feed(&rtc_wdt_ctx);
149         wdt_hal_write_protect_enable(&rtc_wdt_ctx);
150     }
151 }
152 
153 /**
154  * Enable all enabled WDTs
155  */
enable_all_wdts(void)156 static inline void enable_all_wdts(void)
157 {
158     //Task WDT is the Main Watchdog Timer of Timer Group 0
159     if (false == wdt0_context_enabled) {
160         wdt_hal_write_protect_disable(&wdt0_context);
161         wdt_hal_enable(&wdt0_context);
162         wdt_hal_write_protect_enable(&wdt0_context);
163     }
164     // Interupt WDT is the Main Watchdog Timer of Timer Group 1
165     if (false == wdt1_context_enabled) {
166         wdt_hal_write_protect_disable(&wdt1_context);
167         wdt_hal_enable(&wdt1_context);
168         wdt_hal_write_protect_enable(&wdt1_context);
169     }
170 
171     if (false == rtc_wdt_ctx_enabled) {
172         wdt_hal_write_protect_disable(&rtc_wdt_ctx);
173         wdt_hal_enable(&rtc_wdt_ctx);
174         wdt_hal_write_protect_enable(&rtc_wdt_ctx);
175     }
176 }
177 
178 /**
179  * @breef Handle UART interrupt
180  *
181  * Handle UART interrupt for gdbstub. The function disable WDT.
182  * If Ctrl+C combination detected (0x03), then application will start to process incoming GDB messages.
183  * The gdbstub will stay in this interrupt until continue command will be received ($c#63).
184  *
185  * @param curr_regs - actual registers frame
186  *
187 */
gdbstub_handle_uart_int(esp_gdbstub_frame_t * regs_frame)188 void gdbstub_handle_uart_int(esp_gdbstub_frame_t *regs_frame)
189 {
190     // Disable all enabled WDT on enter
191     disable_all_wdts();
192 
193     int doDebug = esp_gdbstub_getfifo();
194     s_scratch.signal = esp_gdbstub_get_signal(regs_frame);
195 
196     if (doDebug) {
197 #ifdef CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
198         init_task_info();
199 #endif// CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
200         esp_gdbstub_frame_to_regfile(regs_frame, gdb_local_regfile);
201         send_reason();
202         while (true) {
203             unsigned char *cmd;
204             size_t size;
205 
206             int res = esp_gdbstub_read_command(&cmd, &size);
207             if (res == '-') {
208                 send_reason();
209                 continue;
210             }
211             if (res > 0) {
212                 /* character received instead of a command */
213                 continue;
214             }
215             if (res == -2) {
216                 esp_gdbstub_send_str_packet("E01");
217                 continue;
218             }
219             res = esp_gdbstub_handle_command(cmd, size);
220             if (res == -2) {
221                 esp_gdbstub_send_str_packet(NULL);
222             }
223 #ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
224             if (res == GDBSTUB_ST_CONT) {
225                 break;
226             }
227 #endif // CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
228         }
229     }
230 }
231 
232 intr_handle_t intr_handle_;
233 extern void _xt_gdbstub_int(void * );
234 
235 #ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
236 /** @brief Init gdbstub
237  * Init uart interrupt for gdbstub
238  * */
esp_gdbstub_init(void)239 void esp_gdbstub_init(void)
240 {
241     esp_intr_alloc(ETS_UART0_INTR_SOURCE, 0, _xt_gdbstub_int, NULL, &intr_handle_);
242 }
243 #endif // CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
244 
245 #ifdef CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
246 
247 /** Send string as a het to uart */
esp_gdbstub_send_str_as_hex(const char * str)248 static void esp_gdbstub_send_str_as_hex(const char *str)
249 {
250     while (*str) {
251         esp_gdbstub_send_hex(*str, 8);
252         str++;
253     }
254 }
255 #endif
256 
257 /** Send all registers to gdb */
handle_g_command(const unsigned char * cmd,int len)258 static void handle_g_command(const unsigned char *cmd, int len)
259 {
260     uint32_t *p = (uint32_t *) &s_scratch.regfile;
261     esp_gdbstub_send_start();
262     for (int i = 0; i < sizeof(s_scratch.regfile) / sizeof(*p); ++i) {
263         esp_gdbstub_send_hex(gdbstub_hton(*p++), 32);
264     }
265     esp_gdbstub_send_end();
266 }
267 
268 /** Receive register values from gdb */
handle_G_command(const unsigned char * cmd,int len)269 static void handle_G_command(const unsigned char *cmd, int len)
270 {
271     uint32_t *p = (uint32_t *) &s_scratch.regfile;
272     for (int i = 0; i < sizeof(s_scratch.regfile) / sizeof(*p); ++i) {
273         *p++ = gdbstub_hton(esp_gdbstub_gethex(&cmd, 32));
274     }
275     esp_gdbstub_send_str_packet("OK");
276 }
277 
278 /** Read memory to gdb */
handle_m_command(const unsigned char * cmd,int len)279 static void handle_m_command(const unsigned char *cmd, int len)
280 {
281     intptr_t addr = (intptr_t) esp_gdbstub_gethex(&cmd, -1);
282     cmd++;
283     uint32_t size = esp_gdbstub_gethex(&cmd, -1);
284 
285     if (esp_gdbstub_readmem(addr) < 0 || esp_gdbstub_readmem(addr + size - 1) < 0) {
286         esp_gdbstub_send_str_packet("E01");
287         return;
288     }
289 
290     esp_gdbstub_send_start();
291     for (int i = 0; i < size; ++i) {
292         int b = esp_gdbstub_readmem(addr++);
293         esp_gdbstub_send_hex(b, 8);
294     }
295     esp_gdbstub_send_end();
296 }
297 
298 /** Write memory from gdb */
handle_M_command(const unsigned char * cmd,int len)299 static void handle_M_command(const unsigned char *cmd, int len)
300 {
301     intptr_t addr = (intptr_t) esp_gdbstub_gethex(&cmd, -1);
302     cmd++; // skip '.'
303     uint32_t size = esp_gdbstub_gethex(&cmd, -1);
304     cmd++; // skip ':'
305 
306     if (esp_gdbstub_readmem(addr) < 0 || esp_gdbstub_readmem(addr + size - 1) < 0) {
307         esp_gdbstub_send_str_packet("E01");
308         return;
309     }
310     for (int k = 0; k < size; k++) {
311         esp_gdbstub_writemem(addr, esp_gdbstub_gethex(&cmd, 8));
312         addr++;
313     }
314     esp_gdbstub_send_start();
315     esp_gdbstub_send_str_packet("OK");
316     esp_gdbstub_send_end();
317 }
318 
319 /** Handle a command received from gdb */
esp_gdbstub_handle_command(unsigned char * cmd,int len)320 int esp_gdbstub_handle_command(unsigned char *cmd, int len)
321 {
322     unsigned char *data = cmd + 1;
323     if (cmd[0] == 'g') {
324         handle_g_command(data, len - 1);
325     } else if (cmd[0] == 'G') {
326         /* receive content for all registers from gdb */
327         handle_G_command(data, len - 1);
328     } else if (cmd[0] == 'm') {
329         /* read memory to gdb */
330         handle_m_command(data, len - 1);
331     } else if (cmd[0] == 'M') {
332         /* write to memory from GDB */
333         handle_M_command(data, len - 1);
334     } else if (cmd[0] == '?') {
335         /* Reply with stop reason */
336         send_reason();
337 #if CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
338     } else if (s_scratch.state != GDBSTUB_TASK_SUPPORT_DISABLED) {
339         return handle_task_commands(cmd, len);
340 #endif // CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
341     } else if (strncmp((char *)cmd, "vCont;c", 7) == 0 || cmd[0] == 'c') { //continue execution
342         return GDBSTUB_ST_CONT;
343     } else {
344         /* Unrecognized command */
345         return GDBSTUB_ST_ERR;
346     }
347     return GDBSTUB_ST_OK;
348 }
349 
350 /* Everything below is related to the support for listing FreeRTOS tasks as threads in GDB */
351 
352 #ifdef CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
353 
354 /* Some terminology related to task/thread indices:
355  *
356  * "task index" — Index used here in gdbstub.c to enumberate all the tasks.
357  *                Range is from 0 to <number of tasks> - 1.
358  *                The order is defined by uxTaskGetSnapshotAll.
359  *
360  * "GDB TID" — Thread ID used by GDB internally and in the remote protocol.
361  *             IDs of real threads can be any positive values other than 0.
362  *             For example, OpenOCD uses the TCB pointer (cast to a 32-bit int) as GDB TID.
363  *             Values 0 and -1, when used in place of TID in the remote protocol
364  *             have special meanings:
365  *             -1: indicates all threads, may be used in 'Hc' command
366  *              0: indicates an arbitrary process or thread (GDB client doesn't care, server decides)
367  *
368  * Since GDB TIDs are arbitrary, except that 0 is reserved, we set them using a simple rule:
369  *    GDB TID = task index + 1.
370  * The two functions below perform the conversions between the kinds of IDs.
371  */
gdb_tid_to_task_index(int tid)372 static inline int gdb_tid_to_task_index(int tid)
373 {
374     return tid - 1;
375 }
376 
task_index_to_gdb_tid(int tid)377 static inline int task_index_to_gdb_tid(int tid)
378 {
379     return tid + 1;
380 }
381 
init_task_info(void)382 static void init_task_info(void)
383 {
384     unsigned tcb_size;
385     s_scratch.task_count = uxTaskGetSnapshotAll(s_scratch.tasks, GDBSTUB_TASKS_NUM, &tcb_size);
386 }
387 
get_task_handle(size_t index,TaskHandle_t * handle)388 static bool get_task_handle(size_t index, TaskHandle_t *handle)
389 {
390     if (index >= s_scratch.task_count) {
391         return false;
392     }
393     *handle = (TaskHandle_t) s_scratch.tasks[index].pxTCB;
394     return true;
395 }
396 
397 /** Get the index of the task running on the current CPU, and save the result */
find_paniced_task_index(void)398 static void find_paniced_task_index(void)
399 {
400     TaskHandle_t cur_handle = (TaskHandle_t)xTaskGetCurrentTaskHandleForCPU(xPortGetCoreID());
401     TaskHandle_t handle;
402     for (int i = 0; i < s_scratch.task_count; i++) {
403         if (get_task_handle(i, &handle) && cur_handle == handle) {
404             s_scratch.paniced_task_index = i;
405             return;
406         }
407     }
408     s_scratch.paniced_task_index = GDBSTUB_CUR_TASK_INDEX_UNKNOWN;
409 }
410 
411 /** Select the current task and update the regfile */
set_active_task(size_t index)412 static void set_active_task(size_t index)
413 {
414     if (index == s_scratch.paniced_task_index) {
415         /* Have to get the registers from exception frame */
416         esp_gdbstub_frame_to_regfile(&s_scratch.paniced_frame, &s_scratch.regfile);
417     } else {
418         /* Get the registers from TCB.
419          * FIXME: for the task currently running on the other CPU, extracting the registers from TCB
420          * isn't valid. Need to use some IPC mechanism to obtain the registers of the other CPU.
421          */
422         TaskHandle_t handle = NULL;
423         get_task_handle(index, &handle);
424         if (handle != NULL) {
425             esp_gdbstub_tcb_to_regfile(handle, &s_scratch.regfile);
426         }
427     }
428     s_scratch.current_task_index = index;
429 }
430 
431 /** H command sets the "current task" for the purpose of further commands */
handle_H_command(const unsigned char * cmd,int len)432 static void handle_H_command(const unsigned char *cmd, int len)
433 {
434     const char *ret = "OK";
435     if (cmd[1] == 'g') {
436         cmd += 2;
437         int requested_tid = esp_gdbstub_gethex(&cmd, -1);
438         int requested_task_index = gdb_tid_to_task_index(requested_tid);
439         if (requested_tid == 0) {
440             /* 0 means "arbitrary process or thread", keep the current thread */
441         } else if (requested_task_index >= s_scratch.task_count ||
442                    requested_tid == -1) { /* -1 means "all threads", which doesn't make sense for "Hg" */
443             ret = "E00";
444         } else {
445             set_active_task(requested_task_index);
446         }
447         esp_gdbstub_send_str_packet(ret);
448     } else if (cmd[1] == 'c') {
449         /* Select the task for "continue" and "step" operations. No-op in post-mortem mode. */
450         /* Note that the argument may be -1 to indicate "all threads" or 0 to indicate "any thread" */
451         esp_gdbstub_send_str_packet(ret);
452     } else {
453         esp_gdbstub_send_str_packet(NULL);
454     }
455 }
456 
457 /** qC returns the current thread ID */
handle_qC_command(const unsigned char * cmd,int len)458 static void handle_qC_command(const unsigned char *cmd, int len)
459 {
460     esp_gdbstub_send_start();
461     esp_gdbstub_send_str("QC");
462     esp_gdbstub_send_hex(task_index_to_gdb_tid(s_scratch.current_task_index), 32);
463     esp_gdbstub_send_end();
464 }
465 
466 /** T command checks if the task is alive.
467  *  Since GDB isn't going to ask about the tasks which haven't been listed by q*ThreadInfo,
468  *  and the state of tasks can not change (no stepping allowed), simply return "OK" here.
469  */
handle_T_command(const unsigned char * cmd,int len)470 static void handle_T_command(const unsigned char *cmd, int len)
471 {
472     esp_gdbstub_send_str_packet("OK");
473 }
474 
475 /** Called by qfThreadInfo and qsThreadInfo handlers */
send_single_thread_info(int task_index)476 static void send_single_thread_info(int task_index)
477 {
478     esp_gdbstub_send_start();
479     esp_gdbstub_send_str("m");
480     esp_gdbstub_send_hex(task_index_to_gdb_tid(task_index), 32);
481     esp_gdbstub_send_end();
482 }
483 
484 /** qfThreadInfo requests the start of the thread list, qsThreadInfo (below) is repeated to
485  *  get the subsequent threads.
486  */
handle_qfThreadInfo_command(const unsigned char * cmd,int len)487 static void handle_qfThreadInfo_command(const unsigned char *cmd, int len)
488 {
489     assert(s_scratch.task_count > 0);  /* There should be at least one task */
490     send_single_thread_info(0);
491     s_scratch.thread_info_index = 1;
492 }
493 
handle_qsThreadInfo_command(const unsigned char * cmd,int len)494 static void handle_qsThreadInfo_command(const unsigned char *cmd, int len)
495 {
496     int task_index = s_scratch.thread_info_index;
497     if (task_index == s_scratch.task_count) {
498         /* No more tasks */
499         esp_gdbstub_send_str_packet("l");
500         return;
501     }
502     send_single_thread_info(s_scratch.thread_info_index);
503     s_scratch.thread_info_index++;
504 }
505 
506 /** qThreadExtraInfo requests the thread name */
handle_qThreadExtraInfo_command(const unsigned char * cmd,int len)507 static void handle_qThreadExtraInfo_command(const unsigned char *cmd, int len)
508 {
509     cmd += sizeof("qThreadExtraInfo,") - 1;
510     int task_index = gdb_tid_to_task_index(esp_gdbstub_gethex(&cmd, -1));
511     TaskHandle_t handle;
512     if (!get_task_handle(task_index, &handle)) {
513         esp_gdbstub_send_str_packet("E01");
514         return;
515     }
516     esp_gdbstub_send_start();
517     esp_gdbstub_send_str_as_hex("Name: ");
518     esp_gdbstub_send_str_as_hex((const char *)pcTaskGetTaskName(handle));
519     esp_gdbstub_send_hex(' ', 8);
520 
521     // Current version report only Suspended state
522     esp_gdbstub_send_str_as_hex("State: Suspended");
523 
524     esp_gdbstub_send_end();
525 }
526 
command_name_matches(const char * pattern,const unsigned char * ucmd,int len)527 bool command_name_matches(const char *pattern, const unsigned char *ucmd, int len)
528 {
529     const char *cmd = (const char *) ucmd;
530     const char *end = cmd + len;
531     for (; *pattern && cmd < end; ++cmd, ++pattern) {
532         if (*pattern == '?') {
533             continue;
534         }
535         if (*pattern != *cmd) {
536             return false;
537         }
538     }
539     return *pattern == 0 && (cmd == end || *cmd == ',');
540 }
541 
542 /** Handle all the thread-related commands */
handle_task_commands(unsigned char * cmd,int len)543 static int handle_task_commands(unsigned char *cmd, int len)
544 {
545     if (cmd[0] == 'H') {
546         /* Continue with task */
547         handle_H_command(cmd, len);
548     } else if (cmd[0] == 'T') {
549         /* Task alive check */
550         handle_T_command(cmd, len);
551     } else if (cmd[0] == 'q') {
552         if (command_name_matches("qfThreadInfo", cmd, len)) {
553             handle_qfThreadInfo_command(cmd, len);
554         } else if (command_name_matches("qsThreadInfo", cmd, len)) {
555             handle_qsThreadInfo_command(cmd, len);
556         } else if (command_name_matches("qC", cmd, len)) {
557             handle_qC_command(cmd, len);
558         } else if (command_name_matches("qThreadExtraInfo", cmd, len)) {
559             handle_qThreadExtraInfo_command(cmd, len);
560         } else {
561             /* Unrecognized command */
562             return GDBSTUB_ST_ERR;
563         }
564     } else if (strncmp((char *)cmd, "vCont;c", 7) == 0 || cmd[0] == 'c') { //continue execution
565         return GDBSTUB_ST_CONT;
566     } else {
567         /* Unrecognized command */
568         return GDBSTUB_ST_ERR;
569     }
570     return GDBSTUB_ST_OK;
571 }
572 
573 #endif // CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
574