From d41d12fe7abf617b45462eeb24ab20816ce5aaa4 Mon Sep 17 00:00:00 2001 From: Alexey Lapshin Date: Wed, 19 Apr 2023 20:18:11 +0800 Subject: [PATCH 1/9] riscv: fix & refactor triggers add/delete --- components/riscv/include/riscv/csr.h | 1 + components/riscv/include/riscv/rv_utils.h | 83 ++++++++++++++--------- 2 files changed, 51 insertions(+), 33 deletions(-) diff --git a/components/riscv/include/riscv/csr.h b/components/riscv/include/riscv/csr.h index 2d889d1a20..d4f18616f7 100644 --- a/components/riscv/include/riscv/csr.h +++ b/components/riscv/include/riscv/csr.h @@ -152,6 +152,7 @@ extern "C" { #define TDATA1_MATCH (1<<7) #define TDATA1_MATCH_V (0xF) /*R/W,Address match type :0 : Exact byte match 1 : NAPOT range match */ #define TDATA1_MATCH_S (7) +#define TDATA1_HIT_S (20) /* RISC-V CSR macros diff --git a/components/riscv/include/riscv/rv_utils.h b/components/riscv/include/riscv/rv_utils.h index 44f4b07e0c..521d810b3d 100644 --- a/components/riscv/include/riscv/rv_utils.h +++ b/components/riscv/include/riscv/rv_utils.h @@ -132,49 +132,66 @@ FORCE_INLINE_ATTR void rv_utils_set_breakpoint(int bp_num, uint32_t bp_addr) /* The code bellow sets breakpoint which will trigger `Breakpoint` exception * instead transfering control to debugger. */ RV_WRITE_CSR(tselect, bp_num); - RV_SET_CSR(CSR_TCONTROL, TCONTROL_MTE); - RV_SET_CSR(CSR_TDATA1, TDATA1_USER | TDATA1_MACHINE | TDATA1_EXECUTE); + RV_WRITE_CSR(CSR_TCONTROL, TCONTROL_MTE); + RV_WRITE_CSR(CSR_TDATA1, TDATA1_USER | TDATA1_MACHINE | TDATA1_EXECUTE); RV_WRITE_CSR(tdata2, bp_addr); } +FORCE_INLINE_ATTR void rv_utils_set_watchpoint(int wp_num, + uint32_t wp_addr, + size_t size, + bool on_read, + bool on_write) +{ + RV_WRITE_CSR(tselect, wp_num); + RV_WRITE_CSR(CSR_TCONTROL, TCONTROL_MPTE | TCONTROL_MTE); + RV_WRITE_CSR(CSR_TDATA1, TDATA1_USER | + TDATA1_MACHINE | + TDATA1_MATCH | + (on_read ? TDATA1_LOAD : 0) | + (on_write ? TDATA1_STORE : 0)); + /* From RISC-V Debug Specification: + * NAPOT (Naturally Aligned Power-Of-Two): + * Matches when the top M bits of any compare value match the top M bits of tdata2. + * M is XLEN − 1 minus the index of the least-significant bit containing 0 in tdata2. + * + * Note: Expectng that size is number power of 2 + * + * Examples for understanding how to calculate NAPOT: + * + * nnnn...nnnn0 2-byte NAPOT range + * nnnn...nnn01 4-byte NAPOT range + * nnnn...nn011 8-byte NAPOT range + * nnnn...n0111 16-byte NAPOT range + * nnnn...01111 32-byte NAPOT range + * * where n are bits from original address + */ + const uint32_t half_size = size >> 1; + uint32_t napot = wp_addr; + napot &= ~half_size; /* set the least-significant bit with zero */ + napot |= half_size - 1; /* fill all bits with ones after least-significant bit */ + RV_WRITE_CSR(tdata2, napot); +} + FORCE_INLINE_ATTR void rv_utils_clear_breakpoint(int bp_num) { RV_WRITE_CSR(tselect, bp_num); - RV_CLEAR_CSR(CSR_TCONTROL, TCONTROL_MTE); - RV_CLEAR_CSR(CSR_TDATA1, TDATA1_USER | TDATA1_MACHINE | TDATA1_EXECUTE); -} - -FORCE_INLINE_ATTR void rv_utils_set_watchpoint(int wp_num, - uint32_t wp_addr, - size_t size, - bool on_read, - bool on_write) -{ - RV_WRITE_CSR(tselect, wp_num); - RV_SET_CSR(CSR_TCONTROL, TCONTROL_MPTE | TCONTROL_MTE); - RV_SET_CSR(CSR_TDATA1, TDATA1_USER | TDATA1_MACHINE); - RV_SET_CSR_FIELD(CSR_TDATA1, (long unsigned int) TDATA1_MATCH, 1); - - // add 0 in napot encoding - uint32_t addr_napot; - addr_napot = ((uint32_t) wp_addr) | ((size >> 1) - 1); - if (on_read) { - RV_SET_CSR(CSR_TDATA1, TDATA1_LOAD); - } - if (on_write) { - RV_SET_CSR(CSR_TDATA1, TDATA1_STORE); - } - RV_WRITE_CSR(tdata2, addr_napot); + /* tdata1 is a WARL(write any read legal) register + * We can just write 0 to it + */ + RV_WRITE_CSR(CSR_TDATA1, 0); } FORCE_INLINE_ATTR void rv_utils_clear_watchpoint(int wp_num) { - RV_WRITE_CSR(tselect, wp_num); - RV_CLEAR_CSR(CSR_TCONTROL, TCONTROL_MTE); - RV_CLEAR_CSR(CSR_TDATA1, TDATA1_USER | TDATA1_MACHINE); - RV_CLEAR_CSR_FIELD(CSR_TDATA1, (long unsigned int) TDATA1_MATCH); - RV_CLEAR_CSR(CSR_TDATA1, TDATA1_MACHINE); - RV_CLEAR_CSR(CSR_TDATA1, TDATA1_LOAD | TDATA1_STORE | TDATA1_EXECUTE); + /* riscv have the same registers for breakpoints and watchpoints */ + rv_utils_clear_breakpoint(wp_num); +} + +FORCE_INLINE_ATTR bool rv_utils_is_trigger_fired(int id) +{ + RV_WRITE_CSR(tselect, id); + return (RV_READ_CSR(tdata1) >> TDATA1_HIT_S) & 1; } // ---------------------- Debugger ------------------------- From a89b799642b1853dd1fd6080d056a53bad967560 Mon Sep 17 00:00:00 2001 From: Alexey Lapshin Date: Wed, 19 Apr 2023 20:19:40 +0800 Subject: [PATCH 2/9] riscv: fix trigger add from trap handlers --- components/riscv/include/riscv/rv_utils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/riscv/include/riscv/rv_utils.h b/components/riscv/include/riscv/rv_utils.h index 521d810b3d..707c6ae7a9 100644 --- a/components/riscv/include/riscv/rv_utils.h +++ b/components/riscv/include/riscv/rv_utils.h @@ -132,7 +132,7 @@ FORCE_INLINE_ATTR void rv_utils_set_breakpoint(int bp_num, uint32_t bp_addr) /* The code bellow sets breakpoint which will trigger `Breakpoint` exception * instead transfering control to debugger. */ RV_WRITE_CSR(tselect, bp_num); - RV_WRITE_CSR(CSR_TCONTROL, TCONTROL_MTE); + RV_WRITE_CSR(CSR_TCONTROL, TCONTROL_MPTE | TCONTROL_MTE); RV_WRITE_CSR(CSR_TDATA1, TDATA1_USER | TDATA1_MACHINE | TDATA1_EXECUTE); RV_WRITE_CSR(tdata2, bp_addr); } From 36588c4b35d1f85ec41be2953ca66aa8749d2ae5 Mon Sep 17 00:00:00 2001 From: Alexey Lapshin Date: Thu, 20 Apr 2023 01:37:44 +0800 Subject: [PATCH 3/9] riscv: remove outdated macros --- components/riscv/include/riscv/csr.h | 4 ---- components/riscv/include/riscv/rv_utils.h | 10 +++++----- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/components/riscv/include/riscv/csr.h b/components/riscv/include/riscv/csr.h index d4f18616f7..77f33fa194 100644 --- a/components/riscv/include/riscv/csr.h +++ b/components/riscv/include/riscv/csr.h @@ -137,10 +137,6 @@ extern "C" { Trigger Module register fields (Debug specification) ********************************************************/ -/* tcontrol CSRs not recognized by toolchain currently */ -#define CSR_TCONTROL 0x7a5 -#define CSR_TDATA1 0x7a1 - #define TCONTROL_MTE (1<<3) /*R/W, Current M mode trigger enable bit*/ #define TCONTROL_MPTE (1<<7) /*R/W, Previous M mode trigger enable bit*/ diff --git a/components/riscv/include/riscv/rv_utils.h b/components/riscv/include/riscv/rv_utils.h index 707c6ae7a9..2b0a9a95a1 100644 --- a/components/riscv/include/riscv/rv_utils.h +++ b/components/riscv/include/riscv/rv_utils.h @@ -132,8 +132,8 @@ FORCE_INLINE_ATTR void rv_utils_set_breakpoint(int bp_num, uint32_t bp_addr) /* The code bellow sets breakpoint which will trigger `Breakpoint` exception * instead transfering control to debugger. */ RV_WRITE_CSR(tselect, bp_num); - RV_WRITE_CSR(CSR_TCONTROL, TCONTROL_MPTE | TCONTROL_MTE); - RV_WRITE_CSR(CSR_TDATA1, TDATA1_USER | TDATA1_MACHINE | TDATA1_EXECUTE); + RV_WRITE_CSR(tcontrol, TCONTROL_MPTE | TCONTROL_MTE); + RV_WRITE_CSR(tdata1, TDATA1_USER | TDATA1_MACHINE | TDATA1_EXECUTE); RV_WRITE_CSR(tdata2, bp_addr); } @@ -144,8 +144,8 @@ FORCE_INLINE_ATTR void rv_utils_set_watchpoint(int wp_num, bool on_write) { RV_WRITE_CSR(tselect, wp_num); - RV_WRITE_CSR(CSR_TCONTROL, TCONTROL_MPTE | TCONTROL_MTE); - RV_WRITE_CSR(CSR_TDATA1, TDATA1_USER | + RV_WRITE_CSR(tcontrol, TCONTROL_MPTE | TCONTROL_MTE); + RV_WRITE_CSR(tdata1, TDATA1_USER | TDATA1_MACHINE | TDATA1_MATCH | (on_read ? TDATA1_LOAD : 0) | @@ -179,7 +179,7 @@ FORCE_INLINE_ATTR void rv_utils_clear_breakpoint(int bp_num) /* tdata1 is a WARL(write any read legal) register * We can just write 0 to it */ - RV_WRITE_CSR(CSR_TDATA1, 0); + RV_WRITE_CSR(tdata1, 0); } FORCE_INLINE_ATTR void rv_utils_clear_watchpoint(int wp_num) From 9322c2b82d63d688299f71144736ee4917b44f0e Mon Sep 17 00:00:00 2001 From: Alexey Lapshin Date: Sat, 8 Apr 2023 00:09:22 +0800 Subject: [PATCH 4/9] esp_gdbstup: implement runtime gdbstub for riscv --- components/esp_gdbstub/CMakeLists.txt | 3 +- .../private_include/esp_gdbstub_common.h | 14 +- components/esp_gdbstub/src/gdbstub.c | 78 ++++++--- .../esp_gdbstub/src/gdbstub_transport.c | 3 +- .../src/port/riscv/gdbstub_riscv.c | 104 ++++++++++-- .../src/port/riscv/include/rv_decode.h | 11 ++ .../esp_gdbstub/src/port/riscv/rv_decode.c | 158 ++++++++++++++++++ .../src/port/xtensa/gdbstub-entry.S | 5 +- .../src/port/xtensa/gdbstub_xtensa.c | 6 +- components/riscv/vectors.S | 14 ++ examples/system/.build-test-rules.yml | 6 - examples/system/gdbstub/README.md | 8 +- 12 files changed, 355 insertions(+), 55 deletions(-) create mode 100644 components/esp_gdbstub/src/port/riscv/include/rv_decode.h create mode 100644 components/esp_gdbstub/src/port/riscv/rv_decode.c diff --git a/components/esp_gdbstub/CMakeLists.txt b/components/esp_gdbstub/CMakeLists.txt index 3fce599d5b..9267fbc12e 100644 --- a/components/esp_gdbstub/CMakeLists.txt +++ b/components/esp_gdbstub/CMakeLists.txt @@ -11,7 +11,8 @@ if(CONFIG_IDF_TARGET_ARCH_XTENSA) "src/port/xtensa/xt_debugexception.S") list(APPEND priv_includes "src/port/xtensa/include") elseif(CONFIG_IDF_TARGET_ARCH_RISCV) - list(APPEND srcs "src/port/riscv/gdbstub_riscv.c") + list(APPEND srcs "src/port/riscv/gdbstub_riscv.c" + "src/port/riscv/rv_decode.c") list(APPEND priv_includes "src/port/riscv/include") endif() diff --git a/components/esp_gdbstub/private_include/esp_gdbstub_common.h b/components/esp_gdbstub/private_include/esp_gdbstub_common.h index f2e93ff322..906a42f3ac 100644 --- a/components/esp_gdbstub/private_include/esp_gdbstub_common.h +++ b/components/esp_gdbstub/private_include/esp_gdbstub_common.h @@ -74,6 +74,16 @@ int esp_gdbstub_get_signal(const esp_gdbstub_frame_t *frame); */ void esp_gdbstub_frame_to_regfile(const esp_gdbstub_frame_t *frame, esp_gdbstub_gdb_regfile_t *dst); +/** + * Signal handler for debugging interrupts of the application. + */ +void esp_gdbstub_int(void *frame); + +/** + * Signal handler for transport protocol interrupts. + */ +void gdbstub_handle_uart_int(esp_gdbstub_frame_t *regs_frame); + #if CONFIG_ESP_GDBSTUB_SUPPORT_TASKS /** * Write registers from the saved frame of a given task to the GDB register file @@ -104,11 +114,13 @@ void esp_gdbstub_putchar(int c); */ void esp_gdbstub_flush(void); +#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME /** * Read a data from fifo and detect start symbol * @return 1 if break symbol was detected, or 0 if not */ int esp_gdbstub_getfifo(void); +#endif // CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME /**** GDB packet related functions ****/ @@ -144,7 +156,7 @@ void esp_gdbstub_stall_other_cpus_start(void); void esp_gdbstub_stall_other_cpus_end(void); void esp_gdbstub_clear_step(void); -void esp_gdbstub_do_step(void); +void esp_gdbstub_do_step(esp_gdbstub_frame_t *regs_frame); void esp_gdbstub_trigger_cpu(void); /** diff --git a/components/esp_gdbstub/src/gdbstub.c b/components/esp_gdbstub/src/gdbstub.c index db35b0b9fc..1d15ab9d5a 100644 --- a/components/esp_gdbstub/src/gdbstub.c +++ b/components/esp_gdbstub/src/gdbstub.c @@ -187,6 +187,25 @@ static inline void enable_all_wdts(void) } } +int getActiveTaskNum(void); +int __swrite(struct _reent *, void *, const char *, int); +int gdbstub__swrite(struct _reent *data1, void *data2, const char *buff, int len); + +volatile esp_gdbstub_frame_t *temp_regs_frame; + +#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME +static int bp_count = 0; +static int wp_count = 0; +static uint32_t bp_list[SOC_CPU_BREAKPOINTS_NUM] = {0}; +static uint32_t wp_list[SOC_CPU_WATCHPOINTS_NUM] = {0}; +static uint32_t wp_size[SOC_CPU_WATCHPOINTS_NUM] = {0}; +static esp_cpu_watchpoint_trigger_t wp_access[SOC_CPU_WATCHPOINTS_NUM] = {0}; + +static volatile bool step_in_progress = false; +static bool not_send_reason = false; +static bool process_gdb_kill = false; +static bool gdb_debug_int = false; + /** * @breef Handle UART interrupt * @@ -196,24 +215,7 @@ static inline void enable_all_wdts(void) * * @param curr_regs - actual registers frame * -*/ -static int bp_count = 0; -static int wp_count = 0; -static uint32_t bp_list[GDB_BP_SIZE] = {0}; -static uint32_t wp_list[GDB_WP_SIZE] = {0}; -static uint32_t wp_size[GDB_WP_SIZE] = {0}; -static esp_cpu_watchpoint_trigger_t wp_access[GDB_WP_SIZE] = {0}; - -static volatile bool step_in_progress = false; -static bool not_send_reason = false; -static bool process_gdb_kill = false; -static bool gdb_debug_int = false; -int getActiveTaskNum(void); -int __swrite(struct _reent *, void *, const char *, int); -int gdbstub__swrite(struct _reent *data1, void *data2, const char *buff, int len); - -volatile esp_gdbstub_frame_t *temp_regs_frame; - + */ void gdbstub_handle_uart_int(esp_gdbstub_frame_t *regs_frame) { temp_regs_frame = regs_frame; @@ -264,11 +266,9 @@ void gdbstub_handle_uart_int(esp_gdbstub_frame_t *regs_frame) if (res == -2) { esp_gdbstub_send_str_packet(NULL); } -#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME if (res == GDBSTUB_ST_CONT) { break; } -#endif /* CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME */ } { /* Resume other core */ @@ -356,16 +356,12 @@ void gdbstub_handle_debug_int(esp_gdbstub_frame_t *regs_frame) gdb_debug_int = false; } -intr_handle_t intr_handle_; -extern void _xt_gdbstub_int(void * ); - -#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME /** @brief Init gdbstub * Init uart interrupt for gdbstub * */ void esp_gdbstub_init(void) { - esp_intr_alloc(ETS_UART0_INTR_SOURCE, 0, _xt_gdbstub_int, NULL, &intr_handle_); + esp_intr_alloc(ETS_UART0_INTR_SOURCE, 0, esp_gdbstub_int, NULL, NULL); esp_gdbstub_init_dports(); } #endif /* CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME */ @@ -474,8 +470,10 @@ static void handle_M_command(const unsigned char *cmd, int len) esp_gdbstub_send_end(); } +#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME void update_breakpoints(void) { +#if CONFIG_IDF_TARGET_ARCH_XTENSA for (size_t i = 0; i < GDB_BP_SIZE; i++) { if (bp_list[i] != 0) { esp_cpu_set_breakpoint(i, (const void *)bp_list[i]); @@ -490,9 +488,33 @@ void update_breakpoints(void) esp_cpu_clear_watchpoint(i); } } +#else // CONFIG_IDF_TARGET_ARCH_XTENSA +#if (GDB_BP_SIZE != GDB_WP_SIZE) +#error "riscv have a common number of BP and WP" +#endif + /* + * On riscv we have no separated registers for setting BP and WP as we have for xtensa. + * Instead we have common registers which could be configured as BP or WP. + */ + size_t i = 0; + for (size_t b = 0; b < GDB_BP_SIZE; b++) { + if (bp_list[b] != 0) { + esp_cpu_set_breakpoint(i, (const void *)bp_list[b]); + i++; + } + } + for (size_t w = 0; w < GDB_WP_SIZE && i < GDB_WP_SIZE; w++) { + if (wp_list[w] != 0) { + esp_cpu_set_watchpoint(i, (void *)wp_list[w], wp_size[w], wp_access[w]); + i++; + } + } + for (; i < GDB_BP_SIZE; i++) { + esp_cpu_clear_breakpoint(i); + } +#endif // CONFIG_IDF_TARGET_ARCH_XTENSA } -#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME /** Write breakpoint */ static void handle_Z0_command(const unsigned char *cmd, int len) { @@ -626,7 +648,7 @@ static void handle_S_command(const unsigned char *cmd, int len) static void handle_s_command(const unsigned char *cmd, int len) { step_in_progress = true; - esp_gdbstub_do_step(); + esp_gdbstub_do_step((esp_gdbstub_frame_t *)temp_regs_frame); } /** Step ... */ @@ -888,9 +910,11 @@ static eTaskState get_task_state(size_t index) eTaskState result = eReady; TaskHandle_t handle = NULL; get_task_handle(index, &handle); +#if CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME if (gdb_debug_int == false) { result = eTaskGetState(handle); } +#endif return result; } diff --git a/components/esp_gdbstub/src/gdbstub_transport.c b/components/esp_gdbstub/src/gdbstub_transport.c index f7856a0d91..9b19a91474 100644 --- a/components/esp_gdbstub/src/gdbstub_transport.c +++ b/components/esp_gdbstub/src/gdbstub_transport.c @@ -98,6 +98,7 @@ void esp_gdbstub_flush(void) } } +#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME int esp_gdbstub_getfifo(void) { esp_gdbstub_uart_init(); @@ -115,5 +116,5 @@ int esp_gdbstub_getfifo(void) uart_ll_clr_intsts_mask(gdb_uart, UART_INTR_RXFIFO_FULL | UART_INTR_RXFIFO_TOUT); return doDebug; } - +#endif // CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME #endif // CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG diff --git a/components/esp_gdbstub/src/port/riscv/gdbstub_riscv.c b/components/esp_gdbstub/src/port/riscv/gdbstub_riscv.c index 43f9b07b45..85ddd6baae 100644 --- a/components/esp_gdbstub/src/port/riscv/gdbstub_riscv.c +++ b/components/esp_gdbstub/src/port/riscv/gdbstub_riscv.c @@ -7,8 +7,12 @@ #include #include "esp_gdbstub.h" #include "esp_gdbstub_common.h" +#include "esp_cpu.h" +#include "rv_decode.h" #include "sdkconfig.h" +extern volatile esp_gdbstub_frame_t *temp_regs_frame; + static inline void init_regfile(esp_gdbstub_gdb_regfile_t *dst) { memset(dst, 0, sizeof(*dst)); @@ -24,7 +28,7 @@ void esp_gdbstub_frame_to_regfile(const esp_gdbstub_frame_t *frame, esp_gdbstub_ memcpy(&(dst->x[1]), &frame->ra, sizeof(uint32_t) * 31); } -#ifdef CONFIG_ESP_GDBSTUB_SUPPORT_TASKS +#if CONFIG_ESP_GDBSTUB_SUPPORT_TASKS || CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME /* Represents FreeRTOS TCB structure */ typedef struct { @@ -32,6 +36,7 @@ typedef struct { /* Other members aren't needed */ } dummy_tcb_t; +#if CONFIG_ESP_GDBSTUB_SUPPORT_TASKS void esp_gdbstub_tcb_to_regfile(TaskHandle_t tcb, esp_gdbstub_gdb_regfile_t *dst) { @@ -42,40 +47,117 @@ void esp_gdbstub_tcb_to_regfile(TaskHandle_t tcb, esp_gdbstub_gdb_regfile_t *dst } #endif // CONFIG_ESP_GDBSTUB_SUPPORT_TASKS +#endif // CONFIG_ESP_GDBSTUB_SUPPORT_TASKS || CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME int esp_gdbstub_get_signal(const esp_gdbstub_frame_t *frame) { - return 5; // SIGTRAP, see IDF-2490 + switch (frame->mcause) { + case 0: /* Instruction address misaligned */ + case 1: /* Instruction access fault */ + case 2: /* Illegal instruction */ + return 4; /* SIGILL */ + case 3: /* Breakpoint */ + return 5; /* SIGTRAP */ + case 4: /* Load address misaligned */ + case 5: /* Load access fault */ + case 6: /* Store/AMO address misaligned */ + case 7: /* Store/AMO access fault */ + return 11; /* SIGSEGV */ + case 8: /* Environment call from U-mode */ + case 9: /* Environment call from S-mode */ + // case 10: /* Reserved */ + case 11: /* Environment call from M-mode */ + return 5; /* SIGTRAP */ + case 12: /* Instruction page fault */ + case 13: /* Load page fault */ + // case 14: /* Reserved */ + case 15: /* Store/AMO page fault */ + return 11; /* SIGSEGV */ + }; + + return 5; /* SIGTRAP */ } -void _xt_gdbstub_int(void *frame) +#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME +void esp_gdbstub_int(__attribute__((unused)) void *frame) +{ + /* Pointer to saved frame is in pxCurrentTCB + * See rtos_int_enter function + */ + extern void *pxCurrentTCB; + dummy_tcb_t *tcb = pxCurrentTCB; + gdbstub_handle_uart_int((esp_gdbstub_frame_t *)tcb->top_of_stack); +} + +void esp_gdbstub_init_dports(void) { } -void esp_gdbstub_init_dports() +#endif // CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME + +#if (!CONFIG_FREERTOS_UNICORE) && CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME +static bool stall_started = false; +#endif + +void esp_gdbstub_stall_other_cpus_start(void) { +#if (!CONFIG_FREERTOS_UNICORE) && CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME + if (stall_started == false) { + esp_ipc_isr_stall_other_cpu(); + stall_started = true; + } +#endif } -void esp_gdbstub_stall_other_cpus_start() +void esp_gdbstub_stall_other_cpus_end(void) { +#if (!CONFIG_FREERTOS_UNICORE) && CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME + if (stall_started == true) { + esp_ipc_isr_release_other_cpu(); + stall_started = false; + } +#endif } -void esp_gdbstub_stall_other_cpus_end() +void esp_gdbstub_clear_step(void) { +#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME + /* Setup triggers again because we removed them in esp_gdbstub_do_step() */ + update_breakpoints(); +#endif // CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME } -void esp_gdbstub_clear_step() -{ -} - -void esp_gdbstub_do_step() +void esp_gdbstub_do_step(esp_gdbstub_frame_t *frame) { +#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME + uint32_t pc = (uint32_t) frame->mepc; + uint32_t next_pc = rv_compute_next_pc(frame, pc); + esp_cpu_set_breakpoint(0, (void *) next_pc); + for (size_t i = 1; i < SOC_CPU_BREAKPOINTS_NUM; i++) { + esp_cpu_clear_breakpoint(i); + } +#endif // CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME } void esp_gdbstub_trigger_cpu(void) { +#if !CONFIG_FREERTOS_UNICORE + if (0 == esp_cpu_get_core_id()) { + esp_crosscore_int_send_gdb_call(1); + } else { + esp_crosscore_int_send_gdb_call(0); + } +#endif } void esp_gdbstub_set_register(esp_gdbstub_frame_t *frame, uint32_t reg_index, uint32_t value) { + /* RISC-V base ISA has registers x0-x31 */ + if (reg_index == 0) { /* skip zero-wired register */ + return; + } else if (reg_index < 32) { + (&frame->mepc)[reg_index] = value; + } else if (reg_index == 32) { /* register 32 is PC */ + frame->mepc = value; + } } diff --git a/components/esp_gdbstub/src/port/riscv/include/rv_decode.h b/components/esp_gdbstub/src/port/riscv/include/rv_decode.h new file mode 100644 index 0000000000..4743c61d97 --- /dev/null +++ b/components/esp_gdbstub/src/port/riscv/include/rv_decode.h @@ -0,0 +1,11 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "esp_gdbstub_common.h" + +uintptr_t rv_compute_next_pc(esp_gdbstub_frame_t *frame, uintptr_t inst_addr); diff --git a/components/esp_gdbstub/src/port/riscv/rv_decode.c b/components/esp_gdbstub/src/port/riscv/rv_decode.c new file mode 100644 index 0000000000..b555621086 --- /dev/null +++ b/components/esp_gdbstub/src/port/riscv/rv_decode.c @@ -0,0 +1,158 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "rv_decode.h" + +static inline uint32_t rv_inst_len(uint32_t inst) +{ +#ifdef __riscv_c + if ((inst & 0x3) != 0x3) + return 2; /* 16-bit instructions. */ +#endif /* __riscv_c */ + return 4; /* 32-bit instructions. */ +} + +static uint32_t rv_get_register_value(esp_gdbstub_frame_t *frame, uint32_t r_num) +{ + r_num &= 0x1F; + if (r_num == 0) { /* zero-wired */ + return 0; + } + return (&frame->mepc)[r_num]; +} + +static uint32_t rv_get_rs1_value(esp_gdbstub_frame_t *frame, uint32_t inst) +{ + return rv_get_register_value(frame, inst >> 15); +} + +static uint32_t rv_get_rs2_value(esp_gdbstub_frame_t *frame, uint32_t inst) +{ + return rv_get_register_value(frame, inst >> 20); +} + +static int32_t rv_get_branch_next_inst_offset(esp_gdbstub_frame_t *frame, uint32_t inst) +{ + uint32_t funct = (inst >> 12) & 0x7; + uint32_t rs1 = rv_get_rs1_value(frame, inst); + uint32_t rs2 = rv_get_rs2_value(frame, inst); + if ((funct == 0 && rs1 == rs2) || /* beq true */ + (funct == 1 && rs1 != rs2) || /* bne true */ + (funct == 4 && (int32_t) rs1 < (int32_t) rs2) || /* blt true */ + (funct == 5 && (int32_t) rs1 >= (int32_t) rs2) || /* bge true */ + (funct == 6 && rs1 < rs2) || /* bltu true */ + (funct == 7 && rs1 >= rs2)) { /* bgeu true */ + return ((inst >> 8 ) & 0xF ) << 1 | /* imm[4:1] */ + ((inst >> 25) & 0x3F) << 5 | /* imm[10:5] */ + ((inst >> 7 ) & 0x1 ) << 11 | /* imm[11] */ + ((inst >> 31) ? 0xFFFFF000 : 0); /* imm[12] is sign part */; + } + return rv_inst_len(inst); /* branch will not jump. Next instruction will be executed */ +} + +static int32_t rv_get_jal_next_inst_offset(uint32_t inst) +{ + return ((inst >> 21) & 0x3FF) << 1 | /* imm[10:1] */ + ((inst >> 20) & 0x1 ) << 11 | /* imm[11] */ + ((inst >> 12) & 0xFF ) << 12 | /* imm[19:12] */ + ((inst >> 31) ? 0xFFF00000 : 0); /* imm[20] is sign bit */ +} + +static uint32_t rv_get_jalr_next_inst(esp_gdbstub_frame_t *frame, uint32_t inst) +{ + uint32_t rs1 = rv_get_rs1_value(frame, inst); + int32_t imm = ((inst >> 20) & 0xFFF); /* imm[11:0] */ + imm |= (imm >> 11) ? 0xFFFFF000 : 0; /* imm[11] is sign bit */ + return rs1 + imm; +} + +#ifdef __riscv_c /* compressed riscv instruction set */ +static uint32_t rv_get_c_rs1_value(esp_gdbstub_frame_t *frame, uint32_t inst) +{ + return rv_get_register_value(frame, inst >> 7); +} + +static uint32_t rv_get_c_rs2_num(uint32_t inst) +{ + return (inst >> 2) & 0x1F; +} + +static uint32_t rv_get_c_rd_num(uint32_t inst) +{ + return (inst >> 7) & 0x1F; +} + +static int32_t rv_get_c_jal_next_inst_offset(uint32_t inst) +{ + return ((inst >> 3 ) & 0x7 ) << 1 | /* imm[3:1] */ + ((inst >> 11) & 0x1 ) << 4 | /* imm[4] */ + ((inst >> 2 ) & 0x1 ) << 5 | /* imm[5] */ + ((inst >> 7 ) & 0x1 ) << 6 | /* imm[6] */ + ((inst >> 6 ) & 0x1 ) << 7 | /* imm[7] */ + ((inst >> 9 ) & 0x3 ) << 8 | /* imm[9:8] */ + ((inst >> 8 ) & 0x1 ) << 10 | /* imm[10] */ + ((inst >> 12) & 0x1 ) << 11 | /* imm[11] */ + ((inst >> 12) & 0x1 ? 0xFFFFF000 : 0); /* imm[11] is sign part */; +} + +static int32_t rv_get_c_branch_next_inst_offset(esp_gdbstub_frame_t *frame,uint32_t inst) +{ + const int32_t rs1_value = (&frame->s0)[(inst >> 7) & 7]; + const bool is_bnez = (inst >> 13) & 1; + if ((rs1_value == 0 && !is_bnez) || + (rs1_value != 0 && is_bnez)) { + return ((inst >> 3 ) & 0x3 ) << 1 | /* imm[2:1] */ + ((inst >> 10) & 0x3 ) << 3 | /* imm[4:3] */ + ((inst >> 2 ) & 0x1 ) << 5 | /* imm[5] */ + ((inst >> 5 ) & 0x3 ) << 6 | /* imm[7:6] */ + ((inst >> 12) & 0x1 ) << 8 | /* imm[8] */ + ((inst >> 12) & 0x1 ? 0xFFFFFF00 : 0); /* imm[8] is sign part */; + } + return 2; +} +#endif /* __riscv_c */ + +uintptr_t rv_compute_next_pc(esp_gdbstub_frame_t *frame, uintptr_t inst_addr) +{ + const uint32_t inst = *((uint32_t *) inst_addr); + const uint32_t inst_len = rv_inst_len(inst); + if (inst_len == 4) { /* this is 32-bit instruction */ + switch (inst & 0x7f) { + case 0x63: /* branch */ + return inst_addr + rv_get_branch_next_inst_offset(frame, inst); + case 0x6F: /* jal */ + return inst_addr + rv_get_jal_next_inst_offset(inst); + case 0x67: /* jalr */ + return rv_get_jalr_next_inst(frame, inst); + } + } +#ifdef __riscv_c /* compressed riscv instruction set */ + const uint32_t funct3 = (inst & 0xFFFF) >> 13; + if ((inst & 3) == 1) { + switch (funct3) { + case 1: /* c.jal */ + case 5: /* c.j */ + return inst_addr + rv_get_c_jal_next_inst_offset(inst); + case 6: /* c.beqz */ + case 7: /* c.bnez */ + return inst_addr + rv_get_c_branch_next_inst_offset(frame, inst); + } + } else if ((inst & 3) == 2) { + uint32_t rs2 = rv_get_c_rs2_num(inst); + uint32_t rd = rv_get_c_rd_num(inst); + /* c.jr and c.jalr: + * + * They must have funct3 == 0b100, rd!=0 and rs2==0 + * See Table 1.6: Instruction listing for RVC, Quadrant 2 + * in The RISC-V Compressed Instruction Set Manual + */ + if (funct3 == 4 && rd != 0 && rs2 == 0) { + return rv_get_c_rs1_value(frame, inst); + } + } +#endif /* __riscv_c */ + return inst_addr + inst_len; +} diff --git a/components/esp_gdbstub/src/port/xtensa/gdbstub-entry.S b/components/esp_gdbstub/src/port/xtensa/gdbstub-entry.S index 4fba7d5c4a..adc423d3f0 100644 --- a/components/esp_gdbstub/src/port/xtensa/gdbstub-entry.S +++ b/components/esp_gdbstub/src/port/xtensa/gdbstub-entry.S @@ -9,10 +9,11 @@ .section .iram1, "ax" .global gdbstub_handle_uart_int - .global _xt_gdbstub_int + .global esp_gdbstub_int + .type esp_gdbstub_int, @function .align 4 -_xt_gdbstub_int: +esp_gdbstub_int: /* Allocate exception frame and save minimal context. */ mov a0, sp diff --git a/components/esp_gdbstub/src/port/xtensa/gdbstub_xtensa.c b/components/esp_gdbstub/src/port/xtensa/gdbstub_xtensa.c index 20b0d4d2b9..fa452d5b8a 100644 --- a/components/esp_gdbstub/src/port/xtensa/gdbstub_xtensa.c +++ b/components/esp_gdbstub/src/port/xtensa/gdbstub_xtensa.c @@ -139,7 +139,7 @@ static bool stall_started = false; /** @brief GDB stall other CPU * GDB stall other CPU * */ -void esp_gdbstub_stall_other_cpus_start() +void esp_gdbstub_stall_other_cpus_start(void) { #if CONFIG_IDF_TARGET_ARCH_XTENSA && (!CONFIG_FREERTOS_UNICORE) && CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME if (stall_started == false) { @@ -152,7 +152,7 @@ void esp_gdbstub_stall_other_cpus_start() /** @brief GDB end stall other CPU * GDB end stall other CPU * */ -void esp_gdbstub_stall_other_cpus_end() +void esp_gdbstub_stall_other_cpus_end(void) { #if CONFIG_IDF_TARGET_ARCH_XTENSA && (!CONFIG_FREERTOS_UNICORE) && CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME if (stall_started == true) { @@ -174,7 +174,7 @@ void esp_gdbstub_clear_step(void) /** @brief GDB do step * GDB do one step * */ -void esp_gdbstub_do_step(void) +void esp_gdbstub_do_step( esp_gdbstub_frame_t *frame) { // We have gdbstub uart interrupt, and if we will call step, with ICOUNTLEVEL=2 or higher, from uart interrupt, the // application will hang because it will try to step uart interrupt. That's why we have to set ICOUNTLEVEL=1 diff --git a/components/riscv/vectors.S b/components/riscv/vectors.S index 4fdc951126..5a0c8ed9ad 100644 --- a/components/riscv/vectors.S +++ b/components/riscv/vectors.S @@ -102,6 +102,9 @@ .global rtos_int_enter .global rtos_int_exit .global _global_interrupt_handler +#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME + .global gdbstub_handle_debug_int +#endif .section .exception_vectors.text /* This is the vector table. MTVEC points here. @@ -170,16 +173,27 @@ _panic_handler: * have an pseudo excause */ mv a0, sp csrr a1, mcause + /* Branches instructions don't accept immediates values, so use t1 to * store our comparator */ li t0, 0x80000000 bgeu a1, t0, _call_panic_handler sw a1, RV_STK_MCAUSE(sp) +#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME + li t0, 3 + beq a1, t0, _call_gdbstub_handler +#endif /* exception_from_panic never returns */ jal panic_from_exception /* We arrive here if the exception handler has returned. */ j _return_from_exception +#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME +_call_gdbstub_handler: + call gdbstub_handle_debug_int + j _return_from_exception +#endif + _call_panic_handler: /* Remove highest bit from mcause (a1) register and save it in the * structure */ diff --git a/examples/system/.build-test-rules.yml b/examples/system/.build-test-rules.yml index dabc8d43a6..bc7d96585e 100644 --- a/examples/system/.build-test-rules.yml +++ b/examples/system/.build-test-rules.yml @@ -60,12 +60,6 @@ examples/system/gcov: temporary: true reason: lack of runners -examples/system/gdbstub: - disable: - - if: IDF_TARGET == "esp32c2" or IDF_TARGET == "esp32h2" - temporary: true - reason: target esp32c2, esp32h2 is not supported yet - examples/system/heap_task_tracking: disable: - if: IDF_TARGET == "esp32c2" or IDF_TARGET == "esp32h2" diff --git a/examples/system/gdbstub/README.md b/examples/system/gdbstub/README.md index 45f9d2cea0..15a40c4daf 100644 --- a/examples/system/gdbstub/README.md +++ b/examples/system/gdbstub/README.md @@ -1,5 +1,5 @@ -| Supported Targets | ESP32 | ESP32-C3 | ESP32-C6 | ESP32-S2 | ESP32-S3 | -| ----------------- | ----- | -------- | -------- | -------- | -------- | +| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-S2 | ESP32-S3 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- | # GDBstub example @@ -14,7 +14,9 @@ Upon exit from GDB, the application will continue to work in IDF Monitor as befo The example can run on any commonly available ESP32 development board. There are two possible ways to execute gdbstub with GDB: from IDF Monitor and as standalone application. -gdbstub support ESP32, ESP32-S2 and ESP32-S3 chips. +GDBStub is supported for all ESP chips. + +NOTE: On chips with an integrated USB Serial/JTAG Controller, it is reasonable to use OpenOCD + GDB for debugging. ### Configure the project From 6a54c2582384965e27348ee6f54889303bd41b26 Mon Sep 17 00:00:00 2001 From: Alexey Lapshin Date: Thu, 20 Apr 2023 22:50:11 +0800 Subject: [PATCH 5/9] esp_gdbstub: add tests for riscv jump instructions decoding --- .gitlab/ci/host-test.yml | 6 + .../esp_gdbstub/test_gdbstub_host/Makefile | 9 + .../test_gdbstub_host/rv_decode/Makefile | 66 ++++ .../rv_decode/include/sdkconfig.h | 8 + .../test_gdbstub_host/rv_decode/main.cpp | 7 + .../rv_decode/test_rv_decode.cpp | 338 ++++++++++++++++++ 6 files changed, 434 insertions(+) create mode 100644 components/esp_gdbstub/test_gdbstub_host/Makefile create mode 100644 components/esp_gdbstub/test_gdbstub_host/rv_decode/Makefile create mode 100644 components/esp_gdbstub/test_gdbstub_host/rv_decode/include/sdkconfig.h create mode 100644 components/esp_gdbstub/test_gdbstub_host/rv_decode/main.cpp create mode 100644 components/esp_gdbstub/test_gdbstub_host/rv_decode/test_rv_decode.cpp diff --git a/.gitlab/ci/host-test.yml b/.gitlab/ci/host-test.yml index a83cd43b52..ee1ed88944 100644 --- a/.gitlab/ci/host-test.yml +++ b/.gitlab/ci/host-test.yml @@ -92,6 +92,12 @@ test_certificate_bundle_on_host: - cd components/mbedtls/esp_crt_bundle/test_gen_crt_bundle/ - ./test_gen_crt_bundle.py +test_gdbstub_on_host: + extends: .host_test_template + script: + - cd components/esp_gdbstub/test_gdbstub_host + - make test + test_idf_py: extends: .host_test_template diff --git a/components/esp_gdbstub/test_gdbstub_host/Makefile b/components/esp_gdbstub/test_gdbstub_host/Makefile new file mode 100644 index 0000000000..607de3b8a0 --- /dev/null +++ b/components/esp_gdbstub/test_gdbstub_host/Makefile @@ -0,0 +1,9 @@ +TOPTARGETS := all clean test coverage_report + +SUBDIRS := $(wildcard */.) + +$(TOPTARGETS): $(SUBDIRS) +$(SUBDIRS): + $(MAKE) -C $@ $(MAKECMDGOALS) + +.PHONY: $(TOPTARGETS) $(SUBDIRS) diff --git a/components/esp_gdbstub/test_gdbstub_host/rv_decode/Makefile b/components/esp_gdbstub/test_gdbstub_host/rv_decode/Makefile new file mode 100644 index 0000000000..b33d53b98f --- /dev/null +++ b/components/esp_gdbstub/test_gdbstub_host/rv_decode/Makefile @@ -0,0 +1,66 @@ +TEST_PROGRAM=test_gdbstub_rv +GDBSTUB_SRC_DIR=../.. +all: $(TEST_PROGRAM) + +SOURCE_FILES = \ + $(addprefix $(GDBSTUB_SRC_DIR)/src/, \ + port/riscv/rv_decode.c \ + ) \ + test_rv_decode.cpp \ + main.cpp + +INCLUDE_FLAGS = -I./include \ + -I$(GDBSTUB_SRC_DIR)/private_include \ + -I$(GDBSTUB_SRC_DIR)/include \ + -I$(GDBSTUB_SRC_DIR)/src/port/riscv/include \ + -I$(GDBSTUB_SRC_DIR)/../../tools/catch \ + -I$(GDBSTUB_SRC_DIR)/../esp_hw_support/include \ + -I$(GDBSTUB_SRC_DIR)/../soc/esp32c3/include \ + -I$(GDBSTUB_SRC_DIR)/../esp_common/include \ + -I$(GDBSTUB_SRC_DIR)/../riscv/include +CPPFLAGS += $(INCLUDE_FLAGS) -D__riscv_c -Wall -Werror -g --coverage +CFLAGS += $(INCLUDE_FLAGS) -D__riscv_c -Wall -Werror -g --coverage +LDFLAGS += -lstdc++ --coverage + +ifeq ($(CC),clang) +CFLAGS += -fsanitize=address +CXXFLAGS += -fsanitize=address +LDFLAGS += -fsanitize=address +endif + +OBJ_FILES = $(filter %.o, $(SOURCE_FILES:.cpp=.o) $(SOURCE_FILES:.c=.o)) + +COVERAGE_FILES = $(OBJ_FILES:.o=.gc*) + +$(TEST_PROGRAM): $(OBJ_FILES) + $(CC) -o $@ $^ $(LDFLAGS) + +$(OUTPUT_DIR): + mkdir -p $(OUTPUT_DIR) + +test: $(TEST_PROGRAM) + ./$(TEST_PROGRAM) -d yes exclude:[long] + +long-test: $(TEST_PROGRAM) + ./$(TEST_PROGRAM) -d yes + +$(COVERAGE_FILES): $(TEST_PROGRAM) long-test + +coverage.info: $(COVERAGE_FILES) + find $(GDBSTUB_SRC_DIR)/src/ -name "*.gcno" -exec gcov -r -pb {} + + lcov --capture --directory $(GDBSTUB_SRC_DIR)/src --output-file coverage.info + +coverage_report: coverage.info + genhtml coverage.info --output-directory coverage_report + @echo "Coverage report is in coverage_report/index.html" + +clean-coverage: + rm -f $(COVERAGE_FILES) *.gcov + rm -rf coverage_report/ + rm -f coverage.info + +clean: clean-coverage + rm -f $(OBJ_FILES) $(TEST_PROGRAM) + + +.PHONY: clean clean-coverage all test long-test diff --git a/components/esp_gdbstub/test_gdbstub_host/rv_decode/include/sdkconfig.h b/components/esp_gdbstub/test_gdbstub_host/rv_decode/include/sdkconfig.h new file mode 100644 index 0000000000..064cbd1118 --- /dev/null +++ b/components/esp_gdbstub/test_gdbstub_host/rv_decode/include/sdkconfig.h @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#define CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME diff --git a/components/esp_gdbstub/test_gdbstub_host/rv_decode/main.cpp b/components/esp_gdbstub/test_gdbstub_host/rv_decode/main.cpp new file mode 100644 index 0000000000..cd66dc3083 --- /dev/null +++ b/components/esp_gdbstub/test_gdbstub_host/rv_decode/main.cpp @@ -0,0 +1,7 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#define CATCH_CONFIG_MAIN +#include "catch.hpp" diff --git a/components/esp_gdbstub/test_gdbstub_host/rv_decode/test_rv_decode.cpp b/components/esp_gdbstub/test_gdbstub_host/rv_decode/test_rv_decode.cpp new file mode 100644 index 0000000000..1f624eac4d --- /dev/null +++ b/components/esp_gdbstub/test_gdbstub_host/rv_decode/test_rv_decode.cpp @@ -0,0 +1,338 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "catch.hpp" + +extern "C" { +#include "esp_gdbstub_common.h" +#include "rv_decode.h" + +esp_gdbstub_frame_t regs; +esp_gdbstub_frame_t *temp_regs_frame = ®s; +} // extern "C" + +#if 0 +#define DEBUG_PRINTF(...) printf(__VA_ARGS__) +#else +#define DEBUG_PRINTF(...) +#endif + +struct inst_list_s { + uint32_t inst; + const char *name; +}; + +struct inst_list_s rv32i_nojump[] = { + {0x31011d37, "lui"}, + {0x00000917, "auipc"}, + {0x0b1c0e03, "lb"}, + {0xc8aecc03, "lbu"}, + {0x07271103, "lh"}, + {0x02525403, "lhu"}, + {0x00392903, "lw"}, + {0x000000a3, "sb"}, + {0x00009da3, "sh"}, + {0x0000a423, "sw"}, + {0xd3060d13, "addi"}, + {0x000cad13, "slti"}, + {0x00003d13, "sltiu"}, + {0x0001ce13, "xori"}, + {0x00006013, "ori"}, + {0x00007c13, "andi"}, + {0x00099913, "slli"}, + {0x00005e13, "srli"}, + {0x4107d313, "srai"}, + {0x0000fe33, "and"}, + {0x401e0933, "sub"}, + {0x000012b3, "sll"}, + {0x0036a033, "slt"}, + {0x00853433, "sltu"}, + {0x00004033, "xor"}, + {0x00005133, "srl"}, + {0x40e7d7b3, "sra"}, + {0x000062b3, "or"}, + {0x000071b3, "and"}, + {0x0000000f, "fence"}, + {0x0000100f, "fence.i"}, + {0x00002ef3, "csrr"}, + {0x010312f3, "csrrw"}, + {0x00005473, "csrrwi"}, + {0x0001b073, "csrc"}, + {0x01007073, "csrci"}, + {0x000078f3, "csrrci"}, + {0x0002a073, "csrs"}, + {0x0000e073, "csrsi"}, + {0x000225f3, "csrrs"}}; + +struct inst_list_s rv32m_nojump[] = { + {0x025a0133, "mul"}, + {0x02031233, "mulh"}, + {0x02f437b3, "mulhu"}, + {0x03012e33, "mulhsu"}, + {0x02a6c6b3, "div"}, + {0x02f5d533, "divu"}, + {0x02a47433, "remu"}, + {0x0324e4b3, "rem"}}; + +struct inst_list_s rv32a_nojump[] = { + {0x100523af, "lr.w"}, + {0x19d2ae2f, "sc.w"}, + {0x0c07202f, "amoswap.w"}, + {0x0000222f, "amoadd.w"}, + {0x2072a2af, "amoxor.w"}, + {0x6072a2af, "amoand.w"}, + {0x4072a2af, "amoor.w"}, + {0x801c262f, "amomin.w"}}; + +struct inst_list_s rv32c_nojump[] = { + {0x8522, "c.mv"}, + {0x4200, "c.lw"}, + {0x4501, "c.li"}, + {0x6605, "c.lui"}, + {0x8e65, "c.and"}, + {0x985d, "c.andi"}, + {0x8c9d, "c.sub"}, + {0x8f41, "c.or"}, + {0x8da5, "c.xor"}, + {0x100a, "c.slli"}, + {0x0002, "c.slli64"}, + {0x8011, "c.srli"}, + {0x8081, "c.srli64"}, + {0x9459, "c.srai"}, + {0x8481, "c.srai64"}}; + + +TEST_CASE("decode rv32i instructions") +{ + uintptr_t pc; + uint32_t inst; + uintptr_t inst_addr = (uintptr_t)&inst; + + for (size_t i = 0; i < sizeof(rv32i_nojump)/sizeof(rv32i_nojump[0]); i++) { + inst = rv32i_nojump[i].inst; + DEBUG_PRINTF("testing instruction %s\n", rv32i_nojump[i].name); + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + 4); + } + + /* beq positive offset */ + /* 420147bc: 0af50b63 beq a0,a5,42014872 */ + regs.a0 = regs.a5 = 0x777; + inst = 0x0af50b63; + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + (0x42014872 - 0x420147bc)); + regs.a0 = 0x111; + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + 4); + + /* beq negative offset */ + /* 40383b48: fae789e3 beq a5,a4,40383afa */ + regs.a4 = regs.a5 = 0x777; + inst = 0xfae789e3; + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + (0x40383afa - 0x40383b48)); + regs.a4 = 0x111; + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + 4); + + /* bne positive offset */ + /* 42014798: 10f51163 bne a0,a5,4201489a */ + regs.a0 = regs.a5 = 0x777; + inst = 0x10f51163; + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + 4); + regs.a0 = 0x111; + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + (0x4201489a - 0x42014798)); + + /* bne negative offset */ + /* 4200b7e0: fed711e3 bne a4,a3,4200b7c2 */ + regs.a3 = regs.a4 = 0x777; + inst = 0xfed711e3; + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + 4); + regs.a3 = 0x111; + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + (0x4200b7c2 - 0x4200b7e0)); + + /* blt positive offset */ + /* 420117a0: 04f54763 blt a0,a5,420117ee */ + regs.a0 = regs.a5 = 0x777; + inst = 0x04f54763; + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + 4); + regs.a0++; + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + 4); + regs.a0 = 0x111; + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + (0x420117ee - 0x420117a0)); + + /* blt negative offset */ + /* 42014448: fcd5c0e3 blt a1,a3,42014408 */ + regs.a1 = regs.a3 = 0x777; + inst = 0xfcd5c0e3; + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + 4); + regs.a1++; + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + 4); + regs.a1 = 0x111; + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + (0x42014408 - 0x42014448)); + + /* bltu positive offset */ + /* 42010dfa: 00f56a63 bltu a0,a5,42010e0e */ + regs.a0 = regs.a5 = 0x777; + inst = 0x00f56a63; + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + 4); + regs.a0++; + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + 4); + regs.a0 = 0x111; + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + (0x42010e0e - 0x42010dfa)); + + /* bltu negative offset */ + /* 4200137c: f79be5e3 bltu s7,s9,420012e6 */ + regs.s7 = regs.s9 = 0x777; + inst = 0xf79be5e3; + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + 4); + regs.s7++; + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + 4); + regs.s7 = 0x111; + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + (0x420012e6 - 0x4200137c)); + + /* bge positive offset */ + /* 4200cf88: 02f55763 bge a0,a5,4200cfb6 */ + regs.a0 = regs.a5 = 0x777; + inst = 0x02f55763; + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + (0x4200cfb6 - 0x4200cf88)); + regs.a0++; + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + (0x4200cfb6 - 0x4200cf88)); + regs.a0 = 0x111; + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + 4); + + /* bge negative offset */ + /* 420001bc: fe87d6e3 bge a5,s0,420001a8 */ + regs.a5 = regs.s0 = 0x777; + inst = 0xfe87d6e3; + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + (0x420001a8 - 0x420001bc)); + regs.a5++; + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + (0x420001a8 - 0x420001bc)); + regs.a5 = 0x111; + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + 4); + + /* bgeu positive offset */ + /* 40383552: 02f57d63 bgeu a0,a5,4038358c */ + regs.a0 = regs.a5 = 0x777; + inst = 0x02f57d63; + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + (0x4038358c - 0x40383552)); + regs.a0++; + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + (0x4038358c - 0x40383552)); + regs.a0 = 0x111; + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + 4); + + /* bgeu negative offset */ + /* 42001178: fae7fbe3 bgeu a5,a4,4200112e */ + regs.a5 = regs.a4 = 0x777; + inst = 0xfae7fbe3; + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + (0x4200112e - 0x42001178)); + regs.a5++; + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + (0x4200112e - 0x42001178)); + regs.a5 = 0x111; + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + 4); + + /* jal positive offset */ + /* 4200f872: 016010ef jal ra,42010888 */ + inst = 0x016010ef; + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + (0x42010888 - 0x4200f872)); + + /* jal negative offset */ + /* 42015c94: e31ff0ef jal ra,42015ac4 */ + inst = 0xe31ff0ef; + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + (0x42015ac4 - 0x42015c94)); + + /* jalr positive offset */ + /* 4200fee0: 47c080e7 jalr 1148(ra) # 40000358 */ + regs.ra = 0x40000358 - 1148; + inst = 0x47c080e7; + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == 0x40000358); + + /* jalr negative offset */ + /* 4038779e: 8a6080e7 jalr -1882(ra) # 40000040 */ + regs.ra = 0x40000040 - (-1882); + inst = 0x8a6080e7; + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == 0x40000040); +} + +TEST_CASE("decode rv32c instructions") +{ + uintptr_t pc; + uint32_t inst; + uintptr_t inst_addr = (uintptr_t)&inst; + + for (size_t i = 0; i < sizeof(rv32c_nojump)/sizeof(rv32c_nojump[0]); i++) { + inst = rv32c_nojump[i].inst; + DEBUG_PRINTF("testing instruction %s\n", rv32i_nojump[i].name); + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + 2); + } + + /* beqz positive offset */ + /* 4200db50: c119 beqz a0,4200db56 */ + regs.a0 = 0; + inst = 0xc119; + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + (0x4200db56 - 0x4200db50)); + regs.a0 = 0xff; + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + 2); + + /* beqz negative offset */ + /* 42014886: d96d beqz a0,42014878 */ + regs.a0 = 0; + inst = 0xd96d; + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + (0x42014878 - 0x42014886)); + regs.a0 = 0xff; + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + 2); + + /* bnez positive offset */ + /* 4200ee80: e165 bnez a0,4200ef60 */ + regs.a0 = 0xff; + inst = 0xe165; + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + (0x4200ef60 - 0x4200ee80)); + regs.a0 = 0; + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + 2); + + /* bnez negative offset */ + /* 4200f472: ff29 bnez a4,4200f3cc */ + regs.a4 = 0xff; + inst = 0xff29; + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + (0x4200f3cc - 0x4200f472)); + regs.a4 = 0; + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + 2); + + /* jal positive offset */ + /* 4200ffb8: 2d5d jal 4201066e */ + inst = 0x2d5d; + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + (0x4201066e - 0x4200ffb8)); + + /* jal negative offset */ + /* 42005588: 24cd jal 4200586a */ + inst = 0x24cd; + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + (0x4200586a - 0x42005588)); + + /* 42010366: 9782 jalr a5 */ + regs.a5 = 0x88888888; + inst = 0x9782; + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == 0x88888888); +} + +TEST_CASE("decode rv32a instructions") +{ + uintptr_t pc; + uint32_t inst; + uintptr_t inst_addr = (uintptr_t)&inst; + + for (size_t i = 0; i < sizeof(rv32a_nojump)/sizeof(rv32a_nojump[0]); i++) { + inst = rv32i_nojump[i].inst; + DEBUG_PRINTF("testing instruction %s\n", rv32i_nojump[i].name); + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + 4); + } +} + +TEST_CASE("decode rv32m instructions") +{ + uintptr_t pc; + uint32_t inst; + uintptr_t inst_addr = (uintptr_t)&inst; + + for (size_t i = 0; i < sizeof(rv32m_nojump)/sizeof(rv32m_nojump[0]); i++) { + inst = rv32i_nojump[i].inst; + DEBUG_PRINTF("testing instruction %s\n", rv32i_nojump[i].name); + CHECK(rv_compute_next_pc(temp_regs_frame, inst_addr) == inst_addr + 4); + } +} From 96768d7596ce4faecb8d328a3af2dd7e91f2b79a Mon Sep 17 00:00:00 2001 From: Alexey Lapshin Date: Wed, 19 Apr 2023 15:33:32 +0800 Subject: [PATCH 6/9] test: panic: use gdb-no-python to have ability to send signals --- .../system/panic/test_panic_util/panic_dut.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tools/test_apps/system/panic/test_panic_util/panic_dut.py b/tools/test_apps/system/panic/test_panic_util/panic_dut.py index e9511b043e..020443676b 100644 --- a/tools/test_apps/system/panic/test_panic_util/panic_dut.py +++ b/tools/test_apps/system/panic/test_panic_util/panic_dut.py @@ -160,20 +160,21 @@ class PanicTestDut(IdfDut): Runs GDB and connects it to the "serial" port of the DUT. After this, the DUT expect methods can no longer be used to capture output. """ + gdb_args = ['--nx', '--quiet', '--interpreter=mi2'] if self.is_xtensa: - gdb_path = f'xtensa-{self.target}-elf-gdb' + gdb_path = 'xtensa-esp-elf-gdb-no-python' # TODO: GCC-311 + gdb_args = [f'--mcpu={self.target}'] + gdb_args else: - gdb_path = 'riscv32-esp-elf-gdb' + gdb_path = 'riscv32-esp-elf-gdb-no-python' # TODO: GCC-311 try: from pygdbmi.constants import GdbTimeoutError - default_gdb_args = ['--nx', '--quiet', '--interpreter=mi2'] - gdb_command = [gdb_path] + default_gdb_args + gdb_command = [gdb_path] + gdb_args self.gdbmi = GdbController(command=gdb_command) pygdbmi_logger = attach_logger() except ImportError: # fallback for pygdbmi<0.10.0.0. from pygdbmi.gdbcontroller import GdbTimeoutError - self.gdbmi = GdbController(gdb_path=gdb_path) + self.gdbmi = GdbController(gdb_path=gdb_path, gdb_args=gdb_args) pygdbmi_logger = self.gdbmi.logger # pygdbmi logs to console by default, make it log to a file instead From 9220eea4e4c5a23adf43d7d57c55c4a5440edaae Mon Sep 17 00:00:00 2001 From: Alexey Lapshin Date: Wed, 19 Apr 2023 15:36:26 +0800 Subject: [PATCH 7/9] test: gdbstub_runtime: initial commit --- tools/test_apps/.build-test-rules.yml | 6 + .../system/gdbstub_runtime/CMakeLists.txt | 8 ++ .../system/gdbstub_runtime/README.md | 2 + .../system/gdbstub_runtime/conftest.py | 26 ++++ .../gdbstub_runtime/main/CMakeLists.txt | 3 + .../gdbstub_runtime/main/test_app_main.c | 26 ++++ .../system/gdbstub_runtime/pytest_runtime.py | 114 ++++++++++++++++++ .../system/gdbstub_runtime/sdkconfig.defaults | 2 + 8 files changed, 187 insertions(+) create mode 100644 tools/test_apps/system/gdbstub_runtime/CMakeLists.txt create mode 100644 tools/test_apps/system/gdbstub_runtime/README.md create mode 100644 tools/test_apps/system/gdbstub_runtime/conftest.py create mode 100644 tools/test_apps/system/gdbstub_runtime/main/CMakeLists.txt create mode 100644 tools/test_apps/system/gdbstub_runtime/main/test_app_main.c create mode 100644 tools/test_apps/system/gdbstub_runtime/pytest_runtime.py create mode 100644 tools/test_apps/system/gdbstub_runtime/sdkconfig.defaults diff --git a/tools/test_apps/.build-test-rules.yml b/tools/test_apps/.build-test-rules.yml index 7ea0994d55..a0d6b22dd5 100644 --- a/tools/test_apps/.build-test-rules.yml +++ b/tools/test_apps/.build-test-rules.yml @@ -130,6 +130,12 @@ tools/test_apps/system/gdb_loadable_elf: temporary: true reason: target esp32c6, esp32h2 is not supported yet +tools/test_apps/system/gdbstub_runtime: + disable_test: + - if: IDF_TARGET in ["esp32c2", "esp32h2"] + temporary: true + reason: resolve IDF-7264 + tools/test_apps/system/longjmp_test: enable: - if: IDF_TARGET in ["esp32", "esp32s2", "esp32s3"] diff --git a/tools/test_apps/system/gdbstub_runtime/CMakeLists.txt b/tools/test_apps/system/gdbstub_runtime/CMakeLists.txt new file mode 100644 index 0000000000..d6b8539cb6 --- /dev/null +++ b/tools/test_apps/system/gdbstub_runtime/CMakeLists.txt @@ -0,0 +1,8 @@ +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +set(COMPONENTS main) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(test_gdbstub_runtime) diff --git a/tools/test_apps/system/gdbstub_runtime/README.md b/tools/test_apps/system/gdbstub_runtime/README.md new file mode 100644 index 0000000000..a8b7833fa3 --- /dev/null +++ b/tools/test_apps/system/gdbstub_runtime/README.md @@ -0,0 +1,2 @@ +| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-S2 | ESP32-S3 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- | diff --git a/tools/test_apps/system/gdbstub_runtime/conftest.py b/tools/test_apps/system/gdbstub_runtime/conftest.py new file mode 100644 index 0000000000..986d24ae12 --- /dev/null +++ b/tools/test_apps/system/gdbstub_runtime/conftest.py @@ -0,0 +1,26 @@ +# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 + +# pylint: disable=W0621 # redefined-outer-name + +import os +import sys + +import pytest +from _pytest.fixtures import FixtureRequest +from _pytest.monkeypatch import MonkeyPatch + +sys.path.append(os.path.expandvars(os.path.join('$IDF_PATH', 'tools', 'test_apps', 'system', 'panic'))) +from test_panic_util import PanicTestDut # noqa: E402 + + +@pytest.fixture(scope='module') +def monkeypatch_module(request: FixtureRequest) -> MonkeyPatch: + mp = MonkeyPatch() + request.addfinalizer(mp.undo) + return mp + + +@pytest.fixture(scope='module', autouse=True) +def replace_dut_class(monkeypatch_module: MonkeyPatch) -> None: + monkeypatch_module.setattr('pytest_embedded_idf.IdfDut', PanicTestDut) diff --git a/tools/test_apps/system/gdbstub_runtime/main/CMakeLists.txt b/tools/test_apps/system/gdbstub_runtime/main/CMakeLists.txt new file mode 100644 index 0000000000..c1aeb15ca9 --- /dev/null +++ b/tools/test_apps/system/gdbstub_runtime/main/CMakeLists.txt @@ -0,0 +1,3 @@ +idf_component_register(SRCS "test_app_main.c" + INCLUDE_DIRS "" + REQUIRES esp_gdbstub) diff --git a/tools/test_apps/system/gdbstub_runtime/main/test_app_main.c b/tools/test_apps/system/gdbstub_runtime/main/test_app_main.c new file mode 100644 index 0000000000..341bd254bf --- /dev/null +++ b/tools/test_apps/system/gdbstub_runtime/main/test_app_main.c @@ -0,0 +1,26 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +int var_1; +int var_2; + +void foo(void) +{ + var_2++; +} + +void app_main(void) +{ + printf("tested app is runnig.\n"); + while(1) { + var_1++; + if (var_1 % 10 == 0) { + foo(); + } + } +} diff --git a/tools/test_apps/system/gdbstub_runtime/pytest_runtime.py b/tools/test_apps/system/gdbstub_runtime/pytest_runtime.py new file mode 100644 index 0000000000..864828e498 --- /dev/null +++ b/tools/test_apps/system/gdbstub_runtime/pytest_runtime.py @@ -0,0 +1,114 @@ +# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 + +import os +import sys + +import pytest + +sys.path.append(os.path.expandvars(os.path.join('$IDF_PATH', 'tools', 'test_apps', 'system', 'panic'))) +from test_panic_util import PanicTestDut # noqa: E402 + + +@pytest.mark.supported_targets +@pytest.mark.temp_skip_ci(targets=['esp32c2', 'esp32h2'], reason='resolve IDF-7264') +@pytest.mark.generic +def test_gdbstub_runtime(dut: PanicTestDut) -> None: + dut.expect_exact('tested app is runnig.') + dut.write(b'\x03') # send Ctrl-C + dut.start_gdb() + + # Test breakpoint + cmd = '-break-insert --source test_app_main.c --line 23' + response = dut.find_gdb_response('done', 'result', dut.gdb_write(cmd)) + assert response is not None + cmd = '-exec-continue' + responses = dut.gdb_write(cmd) + assert dut.find_gdb_response('running', 'result', responses) is not None + if not dut.find_gdb_response('stopped', 'notify', responses): + # does not stoped on breakpoint yet + responses = dut.gdbmi.get_gdb_response(timeout_sec=3) + assert dut.find_gdb_response('stopped', 'notify', responses) is not None + payload = dut.find_gdb_response('stopped', 'notify', responses)['payload'] + assert payload['reason'] == 'breakpoint-hit' + assert payload['bkptno'] == '1' + assert payload['frame']['func'] == 'app_main' + assert payload['frame']['line'] == '23' + assert payload['stopped-threads'] == 'all' + + # Test step command + cmd = '-exec-step' + responses = dut.gdb_write(cmd) + assert dut.find_gdb_response('running', 'result', responses) is not None + if not dut.find_gdb_response('stopped', 'notify', responses): + # does not stoped on breakpoint yet + responses = dut.gdbmi.get_gdb_response(timeout_sec=3) + assert dut.find_gdb_response('stopped', 'notify', responses) is not None + payload = dut.find_gdb_response('stopped', 'notify', responses)['payload'] + assert payload['reason'] == 'end-stepping-range' + assert payload['frame']['func'] == 'foo' + assert payload['frame']['line'] == '14' + assert payload['stopped-threads'] == 'all' + + # Test finish command + cmd = '-exec-finish' + responses = dut.gdb_write(cmd) + assert dut.find_gdb_response('running', 'result', responses) is not None + if not dut.find_gdb_response('stopped', 'notify', responses): + # does not stoped on breakpoint yet + responses = dut.gdbmi.get_gdb_response(timeout_sec=3) + assert dut.find_gdb_response('stopped', 'notify', responses) is not None + payload = dut.find_gdb_response('stopped', 'notify', responses)['payload'] + assert payload['reason'] == 'function-finished' + assert payload['frame']['line'] == '23' + assert payload['frame']['func'] == 'app_main' + assert payload['stopped-threads'] == 'all' + + # Test next command + cmd = '-exec-next' + responses = dut.gdb_write(cmd) + assert dut.find_gdb_response('running', 'result', responses) is not None + if not dut.find_gdb_response('stopped', 'notify', responses): + # does not stoped on breakpoint yet + responses = dut.gdbmi.get_gdb_response(timeout_sec=3) + assert dut.find_gdb_response('stopped', 'notify', responses) is not None + payload = dut.find_gdb_response('stopped', 'notify', responses)['payload'] + assert payload['reason'] == 'end-stepping-range' + assert payload['frame']['line'] == '21' + assert payload['frame']['func'] == 'app_main' + assert payload['stopped-threads'] == 'all' + + # test delete breakpoint + cmd = '-break-delete 1' + responses = dut.gdb_write(cmd) + assert dut.find_gdb_response('done', 'result', responses) is not None + cmd = '-exec-continue' + responses = dut.gdb_write(cmd) + assert dut.find_gdb_response('running', 'result', responses) is not None + assert dut.find_gdb_response('running', 'notify', responses) is not None + + # test ctrl-c + responses = dut.gdbmi.send_signal_to_gdb(2) + # assert dut.find_gdb_response('stopped', 'notify', responses) is not None + # ?? No response? check we stopped + dut.gdb_backtrace() + + # test watchpoint + cmd = '-break-watch var_2' + responses = dut.gdb_write(cmd) + assert dut.find_gdb_response('done', 'result', responses) is not None + cmd = '-exec-continue' + responses = dut.gdb_write(cmd) + assert dut.find_gdb_response('running', 'result', responses) is not None + if not dut.find_gdb_response('stopped', 'notify', responses): + # does not stoped on breakpoint yet + responses = dut.gdbmi.get_gdb_response(timeout_sec=3) + payload = dut.find_gdb_response('stopped', 'notify', responses)['payload'] + assert payload['reason'] == 'signal-received' + assert payload['frame']['func'] == 'foo' + assert payload['stopped-threads'] == 'all' + # Uncomment this when implement send reason to gdb: GCC-313 + # + # assert payload['reason'] == 'watchpoint-trigger' + # assert int(payload['value']['new']) == int(payload['value']['old']) + 1 + # assert payload['frame']['line'] == '14' diff --git a/tools/test_apps/system/gdbstub_runtime/sdkconfig.defaults b/tools/test_apps/system/gdbstub_runtime/sdkconfig.defaults new file mode 100644 index 0000000000..9895931bc7 --- /dev/null +++ b/tools/test_apps/system/gdbstub_runtime/sdkconfig.defaults @@ -0,0 +1,2 @@ +CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME=y +CONFIG_ESP_GDBSTUB_SUPPORT_TASKS=y From 71cd5cb2a13d1e89b8e9c985b4d360f18596de2f Mon Sep 17 00:00:00 2001 From: Alexey Lapshin Date: Wed, 19 Apr 2023 20:23:15 +0800 Subject: [PATCH 8/9] esp_gdbstub: fix build for USB serial/jtag protocol --- components/esp_gdbstub/src/gdbstub_transport.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/components/esp_gdbstub/src/gdbstub_transport.c b/components/esp_gdbstub/src/gdbstub_transport.c index 9b19a91474..99ecb1da39 100644 --- a/components/esp_gdbstub/src/gdbstub_transport.c +++ b/components/esp_gdbstub/src/gdbstub_transport.c @@ -15,7 +15,7 @@ #if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG -int esp_gdbstub_getchar() +int esp_gdbstub_getchar(void) { uint8_t c; // retry the read until we succeed @@ -39,6 +39,12 @@ void esp_gdbstub_flush(void) usb_serial_jtag_ll_txfifo_flush(); } +#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME +int esp_gdbstub_getfifo(void) +{ + return 0; // TODO: IDF-7264 +} +#endif // CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME #else // CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG From af1334782c9948591458719e6cb0bcef2c29e398 Mon Sep 17 00:00:00 2001 From: Alexey Lapshin Date: Fri, 19 May 2023 12:37:53 +0800 Subject: [PATCH 9/9] esp_gdbstub: fix bp and wp numbers --- components/esp_gdbstub/src/gdbstub.c | 29 ++++++++++--------- .../src/port/riscv/include/esp_gdbstub_arch.h | 11 ------- .../port/xtensa/include/esp_gdbstub_arch.h | 11 ------- 3 files changed, 15 insertions(+), 36 deletions(-) diff --git a/components/esp_gdbstub/src/gdbstub.c b/components/esp_gdbstub/src/gdbstub.c index 1d15ab9d5a..f16790f0f6 100644 --- a/components/esp_gdbstub/src/gdbstub.c +++ b/components/esp_gdbstub/src/gdbstub.c @@ -11,6 +11,7 @@ #include "sdkconfig.h" #include +#include "soc/soc_caps.h" #include "soc/uart_reg.h" #include "soc/periph_defs.h" #include "esp_attr.h" @@ -474,14 +475,14 @@ static void handle_M_command(const unsigned char *cmd, int len) void update_breakpoints(void) { #if CONFIG_IDF_TARGET_ARCH_XTENSA - for (size_t i = 0; i < GDB_BP_SIZE; i++) { + for (size_t i = 0; i < SOC_CPU_BREAKPOINTS_NUM; i++) { if (bp_list[i] != 0) { esp_cpu_set_breakpoint(i, (const void *)bp_list[i]); } else { esp_cpu_clear_breakpoint(i); } } - for (size_t i = 0; i < GDB_WP_SIZE; i++) { + for (size_t i = 0; i < SOC_CPU_WATCHPOINTS_NUM; i++) { if (wp_list[i] != 0) { esp_cpu_set_watchpoint(i, (void *)wp_list[i], wp_size[i], wp_access[i]); } else { @@ -489,7 +490,7 @@ void update_breakpoints(void) } } #else // CONFIG_IDF_TARGET_ARCH_XTENSA -#if (GDB_BP_SIZE != GDB_WP_SIZE) +#if (SOC_CPU_BREAKPOINTS_NUM != SOC_CPU_WATCHPOINTS_NUM) #error "riscv have a common number of BP and WP" #endif /* @@ -497,19 +498,19 @@ void update_breakpoints(void) * Instead we have common registers which could be configured as BP or WP. */ size_t i = 0; - for (size_t b = 0; b < GDB_BP_SIZE; b++) { + for (size_t b = 0; b < SOC_CPU_BREAKPOINTS_NUM; b++) { if (bp_list[b] != 0) { esp_cpu_set_breakpoint(i, (const void *)bp_list[b]); i++; } } - for (size_t w = 0; w < GDB_WP_SIZE && i < GDB_WP_SIZE; w++) { + for (size_t w = 0; w < SOC_CPU_WATCHPOINTS_NUM && i < SOC_CPU_WATCHPOINTS_NUM; w++) { if (wp_list[w] != 0) { esp_cpu_set_watchpoint(i, (void *)wp_list[w], wp_size[w], wp_access[w]); i++; } } - for (; i < GDB_BP_SIZE; i++) { + for (; i < SOC_CPU_BREAKPOINTS_NUM; i++) { esp_cpu_clear_breakpoint(i); } #endif // CONFIG_IDF_TARGET_ARCH_XTENSA @@ -521,20 +522,20 @@ static void handle_Z0_command(const unsigned char *cmd, int len) cmd++; /* skip 'Z' */ cmd++; /* skip '0' */ uint32_t addr = esp_gdbstub_gethex(&cmd, -1); - if (bp_count >= GDB_BP_SIZE) { + if (bp_count >= SOC_CPU_BREAKPOINTS_NUM) { esp_gdbstub_send_str_packet("E02"); return; } bool add_bp = true; /* Check if bp already exist */ - for (size_t i = 0; i < GDB_BP_SIZE; i++) { + for (size_t i = 0; i < SOC_CPU_BREAKPOINTS_NUM; i++) { if (bp_list[i] == addr) { add_bp = false; break; } } if (true == add_bp) { - for (size_t i = 0; i < GDB_BP_SIZE; i++) { + for (size_t i = 0; i < SOC_CPU_BREAKPOINTS_NUM; i++) { if (bp_list[i] == 0) { bp_list[i] = (uint32_t)addr; bp_count++; @@ -553,7 +554,7 @@ static void handle_z0_command(const unsigned char *cmd, int len) cmd++; /* skip 'z' */ cmd++; /* skip '0' */ uint32_t addr = esp_gdbstub_gethex(&cmd, -1); - for (size_t i = 0; i < GDB_BP_SIZE; i++) { + for (size_t i = 0; i < SOC_CPU_BREAKPOINTS_NUM; i++) { if (bp_list[i] == addr) { bp_list[i] = 0; bp_count--; @@ -572,7 +573,7 @@ static void handle_Z2_command(const unsigned char *cmd, int len) cmd++; uint32_t size = esp_gdbstub_gethex(&cmd, -1); - if (wp_count >= GDB_WP_SIZE) { + if (wp_count >= SOC_CPU_WATCHPOINTS_NUM) { esp_gdbstub_send_str_packet("E02"); return; } @@ -591,7 +592,7 @@ static void handle_Z3_command(const unsigned char *cmd, int len) uint32_t addr = esp_gdbstub_gethex(&cmd, -1); cmd++; uint32_t size = esp_gdbstub_gethex(&cmd, -1); - if (wp_count >= GDB_WP_SIZE) { + if (wp_count >= SOC_CPU_WATCHPOINTS_NUM) { esp_gdbstub_send_str_packet("E02"); return; } @@ -610,7 +611,7 @@ static void handle_Z4_command(const unsigned char *cmd, int len) uint32_t addr = esp_gdbstub_gethex(&cmd, -1); cmd++; uint32_t size = esp_gdbstub_gethex(&cmd, -1); - if (wp_count >= GDB_WP_SIZE) { + if (wp_count >= SOC_CPU_WATCHPOINTS_NUM) { esp_gdbstub_send_str_packet("E02"); return; } @@ -627,7 +628,7 @@ static void handle_zx_command(const unsigned char *cmd, int len) cmd++; /* skip 'z' */ cmd++; /* skip 'x' */ uint32_t addr = esp_gdbstub_gethex(&cmd, -1); - for (size_t i = 0; i < GDB_WP_SIZE; i++) { + for (size_t i = 0; i < SOC_CPU_WATCHPOINTS_NUM; i++) { if (wp_list[i] == addr) { wp_access[i] = 0; wp_list[i] = 0; diff --git a/components/esp_gdbstub/src/port/riscv/include/esp_gdbstub_arch.h b/components/esp_gdbstub/src/port/riscv/include/esp_gdbstub_arch.h index 61169e915e..c8fc9539d9 100644 --- a/components/esp_gdbstub/src/port/riscv/include/esp_gdbstub_arch.h +++ b/components/esp_gdbstub/src/port/riscv/include/esp_gdbstub_arch.h @@ -20,17 +20,6 @@ typedef struct { uint32_t pc; } esp_gdbstub_gdb_regfile_t; - -// Amount of HW breakpoints used in GDB -#ifndef GDB_BP_SIZE -#define GDB_BP_SIZE 2 -#endif // GDB_BP_SIZE - -// Amount of HW watchpoints used in GDB -#ifndef GDB_WP_SIZE -#define GDB_WP_SIZE 2 -#endif // GDB_WP_SIZE - #ifdef __cplusplus } #endif diff --git a/components/esp_gdbstub/src/port/xtensa/include/esp_gdbstub_arch.h b/components/esp_gdbstub/src/port/xtensa/include/esp_gdbstub_arch.h index 42d52fbc21..16a38db019 100644 --- a/components/esp_gdbstub/src/port/xtensa/include/esp_gdbstub_arch.h +++ b/components/esp_gdbstub/src/port/xtensa/include/esp_gdbstub_arch.h @@ -85,17 +85,6 @@ typedef struct { } esp_gdbstub_gdb_regfile_t; - -// Amount of HW breakpoints used in GDB -#ifndef GDB_BP_SIZE -#define GDB_BP_SIZE 2 -#endif // GDB_BP_SIZE - -// Amount of HW watchpoints used in GDB -#ifndef GDB_WP_SIZE -#define GDB_WP_SIZE 2 -#endif // GDB_WP_SIZE - #ifdef __cplusplus } #endif