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