This commit add to GDBstup:

1. Multicore suupor
2. Stepping
3. Console output
4. Update example + readme
5. Jumps
This commit is contained in:
Dmitry
2021-06-07 22:54:09 +03:00
parent 3be8ed7c3b
commit a8c3fe56c8
22 changed files with 965 additions and 189 deletions

View File

@@ -6,13 +6,14 @@ idf_component_register(SRCS "src/gdbstub.c" "src/packet.c"
PRIV_INCLUDE_DIRS "private_include"
LDFRAGMENTS "linker.lf"
REQUIRES "freertos"
PRIV_REQUIRES "soc" "esp_rom")
PRIV_REQUIRES "soc" "esp_rom" "esp_system")
endif()
if(CONFIG_IDF_TARGET_ARCH_XTENSA)
target_include_directories(${COMPONENT_LIB} PUBLIC "xtensa" "${target}")
target_sources(${COMPONENT_LIB} PRIVATE "xtensa/gdbstub_xtensa.c"
"xtensa/gdbstub-entry.S"
"xtensa/xt_debugexception.S"
"esp_common/gdbstub_common.c")
elseif(CONFIG_IDF_TARGET_ARCH_RISCV)

View File

@@ -1,22 +1,16 @@
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "soc/uart_periph.h"
#include "soc/gpio_periph.h"
#include "esp_gdbstub_common.h"
#include "sdkconfig.h"
#include "hal/uart_ll.h"
#include "freertos/FreeRTOS.h"
#include "xtensa/config/specreg.h"
#define UART_NUM CONFIG_ESP_CONSOLE_UART_NUM
@@ -70,7 +64,10 @@ void esp_gdbstub_putchar(int c)
void esp_gdbstub_flush()
{
//not needed for uart
// wait until some data in transmition
while (false == uart_ll_is_tx_idle(gdb_uart))
{
}
}
int esp_gdbstub_getfifo()
@@ -87,9 +84,6 @@ int esp_gdbstub_getfifo()
if (data == 0x3) {
doDebug = 1; //Check if any of the chars is Ctrl+C. Throw away rest.
}
if (data == '+') {
doDebug = 1; //Check if any of the chars is '+'. Throw away rest.
}
fifolen--;
}
uart_ll_clr_intsts_mask(gdb_uart, UART_INTR_RXFIFO_FULL | UART_INTR_RXFIFO_TOUT);

View File

@@ -1,16 +1,8 @@
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
@@ -20,6 +12,7 @@ extern "C" {
void esp_gdbstub_init(void);
void esp_gdbstub_panic_handler(void *frame);
void update_breakpoints(void);
#ifdef __cplusplus
}

View File

@@ -1,16 +1,8 @@
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
@@ -68,7 +60,7 @@ typedef struct {
#endif // CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
} esp_gdbstub_scratch_t;
extern esp_gdbstub_scratch_t s_scratch;
/**** Functions provided by the architecture specific part ****/
/**
@@ -170,3 +162,19 @@ int esp_gdbstub_read_command(unsigned char **out_cmd, size_t *out_size);
/** Handle a command received from gdb */
int esp_gdbstub_handle_command(unsigned char *cmd, int len);
void esp_gdbstub_init_dports(void);
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_trigger_cpu(void);
/**
* Write a value to register in frame
* @param frame gdbstub frame
* @param reg_index register index, depends on architecture
* @param value 32 bit data value
*/
void esp_gdbstub_set_register(esp_gdbstub_frame_t *frame, uint32_t reg_index, uint32_t value);

View File

@@ -1,16 +1,8 @@
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
@@ -29,6 +21,17 @@ 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

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -47,10 +47,37 @@ void esp_gdbstub_tcb_to_regfile(TaskHandle_t tcb, esp_gdbstub_gdb_regfile_t *dst
int esp_gdbstub_get_signal(const esp_gdbstub_frame_t *frame)
{
return 5; // SIGTRAP, see IDF-2490
return 5; // SIGTRAP, see IDF-2490
}
void _xt_gdbstub_int(void * frame)
void _xt_gdbstub_int(void *frame)
{
}
void esp_gdbstub_init_dports()
{
}
void esp_gdbstub_stall_other_cpus_start()
{
}
void esp_gdbstub_stall_other_cpus_end()
{
}
void esp_gdbstub_clear_step()
{
}
void esp_gdbstub_do_step()
{
}
void esp_gdbstub_trigger_cpu(void)
{
}
void esp_gdbstub_set_register(esp_gdbstub_frame_t *frame, uint32_t reg_index, uint32_t value)
{
}

View File

@@ -8,16 +8,17 @@
#include "esp_gdbstub.h"
#include "esp_gdbstub_common.h"
#include "sdkconfig.h"
#include <sys/param.h>
#include "soc/uart_reg.h"
#include "soc/periph_defs.h"
#include "esp_attr.h"
#include "esp_log.h"
#include "esp_intr_alloc.h"
#include "hal/wdt_hal.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#ifdef CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
static inline int gdb_tid_to_task_index(int tid);
static inline int task_index_to_gdb_tid(int tid);
@@ -28,10 +29,19 @@ static int handle_task_commands(unsigned char *cmd, int len);
static void esp_gdbstub_send_str_as_hex(const char *str);
#endif
static void send_reason(void);
#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
static void handle_qSupported_command(const unsigned char *cmd, int len);
#endif // CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
static esp_gdbstub_scratch_t s_scratch;
static esp_gdbstub_gdb_regfile_t *gdb_local_regfile = &s_scratch.regfile;
#if (CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME || CONFIG_ESP_GDBSTUB_SUPPORT_TASKS)
static bool command_name_matches(const char *pattern, const unsigned char *ucmd, int len);
#endif // (CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME || CONFIG_ESP_GDBSTUB_SUPPORT_TASKS)
static void send_reason(void);
static char gdb_packet(char *dest_buff, char *src_buff, int len);
esp_gdbstub_scratch_t s_scratch;
esp_gdbstub_gdb_regfile_t *gdb_local_regfile = &s_scratch.regfile;
/**
* @brief panic handler
@@ -60,7 +70,7 @@ void esp_gdbstub_panic_handler(void *in_frame)
set_active_task(s_scratch.paniced_task_index);
}
}
#endif // CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
#endif /* CONFIG_ESP_GDBSTUB_SUPPORT_TASKS */
esp_gdbstub_target_init();
s_scratch.signal = esp_gdbstub_get_signal(frame);
@@ -119,7 +129,7 @@ static inline void disable_all_wdts(void)
wdt1_context_enabled = wdt_hal_is_enabled(&wdt1_context);
rtc_wdt_ctx_enabled = wdt_hal_is_enabled(&rtc_wdt_ctx);
//Task WDT is the Main Watchdog Timer of Timer Group 0
/*Task WDT is the Main Watchdog Timer of Timer Group 0 */
if (true == wdt0_context_enabled) {
wdt_hal_write_protect_disable(&wdt0_context);
wdt_hal_disable(&wdt0_context);
@@ -127,7 +137,7 @@ static inline void disable_all_wdts(void)
wdt_hal_write_protect_enable(&wdt0_context);
}
//Interupt WDT is the Main Watchdog Timer of Timer Group 1
/* Interupt WDT is the Main Watchdog Timer of Timer Group 1 */
if (true == wdt1_context_enabled) {
wdt_hal_write_protect_disable(&wdt1_context);
wdt_hal_disable(&wdt1_context);
@@ -147,13 +157,13 @@ static inline void disable_all_wdts(void)
*/
static inline void enable_all_wdts(void)
{
//Task WDT is the Main Watchdog Timer of Timer Group 0
/* Task WDT is the Main Watchdog Timer of Timer Group 0 */
if (false == wdt0_context_enabled) {
wdt_hal_write_protect_disable(&wdt0_context);
wdt_hal_enable(&wdt0_context);
wdt_hal_write_protect_enable(&wdt0_context);
}
// Interupt WDT is the Main Watchdog Timer of Timer Group 1
/* Interupt WDT is the Main Watchdog Timer of Timer Group 1 */
if (false == wdt1_context_enabled) {
wdt_hal_write_protect_disable(&wdt1_context);
wdt_hal_enable(&wdt1_context);
@@ -177,18 +187,50 @@ 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 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)
{
// Disable all enabled WDT on enter
temp_regs_frame = regs_frame;
not_send_reason = step_in_progress;
if (step_in_progress == true) {
esp_gdbstub_send_str_packet("S05");
step_in_progress = false;
}
// process_gdb_kill = false;
bp_count = 0;
wp_count = 0;
/* Disable all enabled WDT on enter */
disable_all_wdts();
int doDebug = esp_gdbstub_getfifo();
s_scratch.signal = esp_gdbstub_get_signal(regs_frame);
if (doDebug) {
process_gdb_kill = false;
/* To enable console output in GDB, we replace the default stdout->_write function */
stdout->_write = gdbstub__swrite;
stderr->_write = gdbstub__swrite;
/* Stall other core until GDB exit */
esp_gdbstub_stall_other_cpus_start();
#ifdef CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
init_task_info();
#endif// CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
#endif/* CONFIG_ESP_GDBSTUB_SUPPORT_TASKS */
esp_gdbstub_frame_to_regfile(regs_frame, gdb_local_regfile);
send_reason();
while (true) {
@@ -216,9 +258,92 @@ void gdbstub_handle_uart_int(esp_gdbstub_frame_t *regs_frame)
if (res == GDBSTUB_ST_CONT) {
break;
}
#endif // CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
#endif /* CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME */
}
{
/* Resume other core */
if (step_in_progress == false) {
esp_gdbstub_stall_other_cpus_end();
}
}
}
esp_gdbstub_getfifo();
/* This is workaround for problem with 'next' command. */
if (process_gdb_kill == false) {
esp_gdbstub_send_str("OK");
} else {
/* We flush all data before exit from GDB.*/
esp_gdbstub_flush();
}
}
void gdbstub_handle_debug_int(esp_gdbstub_frame_t *regs_frame)
{
bp_count = 0;
wp_count = 0;
temp_regs_frame = regs_frame;
gdb_debug_int = true;
not_send_reason = step_in_progress;
if (step_in_progress == true) {
esp_gdbstub_clear_step();
esp_gdbstub_send_str_packet("S05");
step_in_progress = false;
}
int doDebug = esp_gdbstub_getfifo();
s_scratch.signal = 5; /* esp_gdbstub_get_db_signal(regs_frame); */
doDebug = 1;
if (doDebug) {
#if CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
s_scratch.current_task_index = getActiveTaskNum();
#endif
process_gdb_kill = false;
/* Stall other core until GDB exit */
esp_gdbstub_stall_other_cpus_start();
#ifdef CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
init_task_info();
#endif/* CONFIG_ESP_GDBSTUB_SUPPORT_TASKS */
esp_gdbstub_frame_to_regfile(regs_frame, gdb_local_regfile);
send_reason();
while (true) {
unsigned char *cmd;
size_t size;
int res = esp_gdbstub_read_command(&cmd, &size);
if (res == '-') {
send_reason();
continue;
}
if (res > 0) {
/* character received instead of a command */
continue;
}
if (res == -2) {
esp_gdbstub_send_str_packet("E01");
continue;
}
res = esp_gdbstub_handle_command(cmd, size);
if (res == -2) {
esp_gdbstub_send_str_packet(NULL);
}
if (res == GDBSTUB_ST_CONT) {
break;
}
}
{
/* Resume other core */
if (step_in_progress == false) {
esp_gdbstub_stall_other_cpus_end();
}
}
}
esp_gdbstub_getfifo();
if (process_gdb_kill == true) {
/* We flush all data before exit from GDB.*/
esp_gdbstub_flush();
}
gdb_debug_int = false;
}
intr_handle_t intr_handle_;
@@ -231,8 +356,9 @@ extern void _xt_gdbstub_int(void * );
void esp_gdbstub_init(void)
{
esp_intr_alloc(ETS_UART0_INTR_SOURCE, 0, _xt_gdbstub_int, NULL, &intr_handle_);
esp_gdbstub_init_dports();
}
#endif // CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
#endif /* CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME */
#ifdef CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
@@ -291,9 +417,9 @@ static void handle_m_command(const unsigned char *cmd, int len)
static void handle_M_command(const unsigned char *cmd, int len)
{
intptr_t addr = (intptr_t) esp_gdbstub_gethex(&cmd, -1);
cmd++; // skip '.'
cmd++; /* skip '.' */
uint32_t size = esp_gdbstub_gethex(&cmd, -1);
cmd++; // skip ':'
cmd++; /* skip ':' */
if (esp_gdbstub_readmem(addr) < 0 || esp_gdbstub_readmem(addr + size - 1) < 0) {
esp_gdbstub_send_str_packet("E01");
@@ -308,6 +434,210 @@ static void handle_M_command(const unsigned char *cmd, int len)
esp_gdbstub_send_end();
}
void update_breakpoints(void)
{
for (size_t i = 0; i < GDB_BP_SIZE; i++) {
if (bp_list[i] != 0) {
cpu_ll_set_breakpoint(i, (uint32_t)bp_list[i]);
} else {
cpu_hal_clear_breakpoint(i);
}
}
for (size_t i = 0; i < GDB_WP_SIZE; i++) {
if (wp_list[i] != 0) {
cpu_hal_set_watchpoint(i, (void *)wp_list[i], wp_size[i], wp_access[i]);
} else {
cpu_hal_clear_watchpoint(i);
}
}
}
#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
/** Write breakpoint */
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) {
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++) {
if (bp_list[i] == addr) {
add_bp = false;
break;
}
}
if (true == add_bp) {
for (size_t i = 0; i < GDB_BP_SIZE; i++) {
if (bp_list[i] == 0) {
bp_list[i] = (uint32_t)addr;
bp_count++;
break;
}
}
}
update_breakpoints();
esp_gdbstub_trigger_cpu();
esp_gdbstub_send_str_packet("OK");
}
/** Clear breakpoint */
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++) {
if (bp_list[i] == addr) {
bp_list[i] = 0;
bp_count--;
}
}
update_breakpoints();
esp_gdbstub_trigger_cpu();
esp_gdbstub_send_str_packet("OK");
}
/** Write watchpoints write*/
static void handle_Z2_command(const unsigned char *cmd, int len)
{
cmd++; /* skip 'Z' */
cmd++; /* skip '2' */
uint32_t addr = esp_gdbstub_gethex(&cmd, -1);
cmd++;
uint32_t size = esp_gdbstub_gethex(&cmd, -1);
if (wp_count >= GDB_WP_SIZE) {
esp_gdbstub_send_str_packet("E02");
return;
}
wp_access[wp_count] = WATCHPOINT_TRIGGER_ON_WO;
wp_size[wp_count] = size;
wp_list[wp_count++] = (uint32_t)addr;
update_breakpoints();
esp_gdbstub_trigger_cpu();
esp_gdbstub_send_str_packet("OK");
}
/** Write watchpoints read*/
static void handle_Z3_command(const unsigned char *cmd, int len)
{
cmd++; /* skip 'Z' */
cmd++; /* skip '3' */
uint32_t addr = esp_gdbstub_gethex(&cmd, -1);
cmd++;
uint32_t size = esp_gdbstub_gethex(&cmd, -1);
if (wp_count >= GDB_WP_SIZE) {
esp_gdbstub_send_str_packet("E02");
return;
}
wp_access[wp_count] = WATCHPOINT_TRIGGER_ON_RO;
wp_size[wp_count] = size;
wp_list[wp_count++] = (uint32_t)addr;
update_breakpoints();
esp_gdbstub_trigger_cpu();
esp_gdbstub_send_str_packet("OK");
}
/** Write watchpoints access*/
static void handle_Z4_command(const unsigned char *cmd, int len)
{
cmd++; /* skip 'Z' */
cmd++; /* skip '4' */
uint32_t addr = esp_gdbstub_gethex(&cmd, -1);
cmd++;
uint32_t size = esp_gdbstub_gethex(&cmd, -1);
if (wp_count >= GDB_WP_SIZE) {
esp_gdbstub_send_str_packet("E02");
return;
}
wp_access[wp_count] = WATCHPOINT_TRIGGER_ON_RW;
wp_size[wp_count] = size;
wp_list[wp_count++] = (uint32_t)addr;
update_breakpoints();
esp_gdbstub_trigger_cpu();
esp_gdbstub_send_str_packet("OK");
}
/** Clear watchpoint */
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++) {
if (wp_list[i] == addr) {
wp_access[i] = 0;
wp_list[i] = 0;
}
}
update_breakpoints();
esp_gdbstub_trigger_cpu();
esp_gdbstub_send_str_packet("OK");
}
/** Step ... */
static void handle_S_command(const unsigned char *cmd, int len)
{
esp_gdbstub_send_str_packet("S05");
}
/** Step ... */
static void handle_s_command(const unsigned char *cmd, int len)
{
step_in_progress = true;
esp_gdbstub_do_step();
}
/** Step ... */
static void handle_C_command(const unsigned char *cmd, int len)
{
esp_gdbstub_send_str_packet("OK");
}
/** Set Register ... */
static void handle_P_command(const unsigned char *cmd, int len)
{
uint32_t reg_index = 0;
if (cmd[1] == '=') {
reg_index = esp_gdbstub_gethex(&cmd, 4);
cmd++;
} else if (cmd[2] == '=') {
reg_index = esp_gdbstub_gethex(&cmd, 8);
cmd++;
cmd++;
} else {
esp_gdbstub_send_str_packet("E02");
return;
}
uint32_t addr = esp_gdbstub_gethex(&cmd, -1);
/* The address comes with inverted byte order.*/
uint8_t *addr_ptr = (uint8_t *)&addr;
uint32_t p_address = 0;
uint8_t *p_addr_ptr = (uint8_t *)&p_address;
p_addr_ptr[3] = addr_ptr[0];
p_addr_ptr[2] = addr_ptr[1];
p_addr_ptr[1] = addr_ptr[2];
p_addr_ptr[0] = addr_ptr[3];
esp_gdbstub_set_register((esp_gdbstub_frame_t *)temp_regs_frame, reg_index, p_address);
/* Convert current regioster file to GDB*/
esp_gdbstub_frame_to_regfile((esp_gdbstub_frame_t *)temp_regs_frame, gdb_local_regfile);
/* Sen OK response*/
esp_gdbstub_send_str_packet("OK");
}
/** qSupported requests the communication with GUI
*/
static void handle_qSupported_command(const unsigned char *cmd, int len)
{
esp_gdbstub_send_start();
esp_gdbstub_send_str("qSupported:multiprocess+;swbreak-;hwbreak+;qRelocInsn+;fork-events+;vfork-events+;exec-events+;vContSupported+;QThreadEvents+;no-resumed+");
esp_gdbstub_send_end();
}
#endif // CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
/** Handle a command received from gdb */
int esp_gdbstub_handle_command(unsigned char *cmd, int len)
{
@@ -326,21 +656,148 @@ int esp_gdbstub_handle_command(unsigned char *cmd, int len)
} else if (cmd[0] == '?') {
/* Reply with stop reason */
send_reason();
#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
} else if (cmd[0] == 'Z') {
if (cmd[1] == '0') {
/* Write breakpoint */
handle_Z0_command(data, len - 1);
}
if (cmd[1] == '2') {
/* Write breakpoint */
handle_Z2_command(data, len - 1);
}
if (cmd[1] == '3') {
/* Write breakpoint */
handle_Z3_command(data, len - 1);
}
if (cmd[1] == '4') {
/* Write breakpoint */
handle_Z4_command(data, len - 1);
}
} else if (cmd[0] == 'z') {
/* Clear breakpoint*/
if (cmd[1] == '0') {
handle_z0_command(data, len - 1);
}
/* Clear breakpoint*/
if ((cmd[1] == '2') || (cmd[1] == '3') || (cmd[1] == '4')) {
handle_zx_command(data, len - 1);
}
} else if (cmd[0] == 'S') {
/* Step */
handle_S_command(data, len - 1);
} else if (cmd[0] == 'k') {
/* Kill GDB and continue without */
/* By exit from GDB we have to replcae stdout->_write back */
stdout->_write = __swrite;
stderr->_write = __swrite;
process_gdb_kill = true;
return GDBSTUB_ST_CONT;
} else if (cmd[0] == 's') {
/* Step and continue*/
handle_s_command(data, len - 1);
return GDBSTUB_ST_CONT;
} else if (cmd[0] == 'C') {
/* Just continue*/
handle_C_command(data, len - 1);
size_t size;
esp_gdbstub_read_command(&cmd, &size);
return GDBSTUB_ST_CONT;
} else if (cmd[0] == 'P') {
handle_P_command(data, len - 1);
} else if (cmd[0] == 'c') { //continue execution
return GDBSTUB_ST_CONT;
} else if (command_name_matches("qSupported", cmd, 10)) {
handle_qSupported_command(cmd, len);
#endif // CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
#if CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
} else if (s_scratch.state != GDBSTUB_TASK_SUPPORT_DISABLED) {
return handle_task_commands(cmd, len);
#endif // CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
} else if (strncmp((char *)cmd, "vCont;c", 7) == 0 || cmd[0] == 'c') { //continue execution
return GDBSTUB_ST_CONT;
} else {
/* Unrecognized command */
return GDBSTUB_ST_ERR;
}
return GDBSTUB_ST_OK;
}
/**
* Replace standard __swrite function for GDB
*/
/* const int buff_len = 16; */
/* static char log_buffer[16*2 + 7]; */
int gdbstub__swrite(struct _reent *data1, void *data2, const char *buff, int len)
{
const int buff_len = 16;
char log_buffer[buff_len * 2 + 7];
char s_chsum = 'O';
char *sent_data = (char *)buff;
size_t remaining = len;
size_t send_pos = 0;
while (remaining > 0) {
size_t will_send = MIN(remaining, buff_len);
remaining -= will_send;
/* prepare and set 'will_send' number of bytes */
if (will_send > 0) {
log_buffer[0] = '$';
log_buffer[1] = 'O';
s_chsum = 'O';
s_chsum += gdb_packet(&log_buffer[2], &sent_data[send_pos], will_send);
sprintf(&log_buffer[will_send * 2 + 2], "#%2.2x\n", s_chsum);
__swrite(data1, data2, log_buffer, will_send * 2 + 6);
send_pos += buff_len;
}
}
return len;
}
/** @brief Convert to ASCI
* Function convert byte value to two ASCI carecters
*/
void gdb_get_asci_char(unsigned char data, char *buff)
{
const char *hex_chars = "0123456789abcdef";
buff[0] = hex_chars[(data >> 4) & 0x0f];
buff[1] = hex_chars[(data) & 0x0f];
}
/* Everything below is related to the support for listing FreeRTOS tasks as threads in GDB */
/** @brief Prepare GDB packet
* Function build GDB asci packet and return checksum
*
* Return checksum
*/
char gdb_packet(char *dest_buff, char *src_buff, int len)
{
char s_chsum = 0;
for (size_t i = 0; i < len; i++) {
gdb_get_asci_char(src_buff[i], &dest_buff[i * 2 + 0]);
}
for (size_t i = 0; i < len * 2; i++) {
s_chsum += dest_buff[i];
}
return s_chsum;
}
#if (CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME || CONFIG_ESP_GDBSTUB_SUPPORT_TASKS)
static bool command_name_matches(const char *pattern, const unsigned char *ucmd, int len)
{
const char *cmd = (const char *) ucmd;
const char *end = cmd + len;
for (; *pattern && cmd < end; ++cmd, ++pattern) {
if (*pattern == '?') {
continue;
}
if (*pattern != *cmd) {
return false;
}
}
return *pattern == 0 && (cmd == end || *cmd == ',');
}
#endif // (CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME || CONFIG_ESP_GDBSTUB_SUPPORT_TASKS)
#ifdef CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
/* Some terminology related to task/thread indices:
@@ -386,6 +843,25 @@ static bool get_task_handle(size_t index, TaskHandle_t *handle)
return true;
}
static eTaskState get_task_state(size_t index)
{
eTaskState result = eReady;
TaskHandle_t handle;
get_task_handle(index, &handle);
if (gdb_debug_int == false) {
result = eTaskGetState(handle);
}
return result;
}
static int get_task_cpu_id(size_t index)
{
TaskHandle_t handle;
get_task_handle(index, &handle);
BaseType_t core_id = xTaskGetAffinity(handle);
return (int)core_id;
}
/** Get the index of the task running on the current CPU, and save the result */
static void find_paniced_task_index(void)
{
@@ -455,6 +931,19 @@ static void handle_qC_command(const unsigned char *cmd, int len)
esp_gdbstub_send_end();
}
int getActiveTaskNum(void)
{
for (size_t i = 0; i < s_scratch.task_count; i++) {
{
eTaskState state = get_task_state(task_index_to_gdb_tid(i));
if (state == eRunning) {
return i;
}
}
}
return s_scratch.task_count;
}
/** T command checks if the task is alive.
* Since GDB isn't going to ask about the tasks which haven't been listed by q*ThreadInfo,
* and the state of tasks can not change (no stepping allowed), simply return "OK" here.
@@ -510,27 +999,34 @@ static void handle_qThreadExtraInfo_command(const unsigned char *cmd, int len)
esp_gdbstub_send_str_as_hex((const char *)pcTaskGetName(handle));
esp_gdbstub_send_hex(' ', 8);
// Current version report only Suspended state
esp_gdbstub_send_str_as_hex("State: Suspended");
eTaskState state = get_task_state(task_index);
/* esp_gdbstub_send_str_as_hex("State: Suspended"); */
switch (state) {
case eRunning:
esp_gdbstub_send_str_as_hex("State: Running ");
esp_gdbstub_send_str_as_hex("@CPU - ");
esp_gdbstub_send_hex(get_task_cpu_id(task_index) + '0', 8);
break;
case eReady:
esp_gdbstub_send_str_as_hex("State: Ready");
break;
case eBlocked:
esp_gdbstub_send_str_as_hex("State: Blocked");
break;
case eSuspended:
esp_gdbstub_send_str_as_hex("State: Suspended");
break;
case eDeleted:
esp_gdbstub_send_str_as_hex("State: Deleted");
break;
default:
esp_gdbstub_send_str_as_hex("State: Invalid");
break;
}
esp_gdbstub_send_end();
}
bool command_name_matches(const char *pattern, const unsigned char *ucmd, int len)
{
const char *cmd = (const char *) ucmd;
const char *end = cmd + len;
for (; *pattern && cmd < end; ++cmd, ++pattern) {
if (*pattern == '?') {
continue;
}
if (*pattern != *cmd) {
return false;
}
}
return *pattern == 0 && (cmd == end || *cmd == ',');
}
/** Handle all the thread-related commands */
static int handle_task_commands(unsigned char *cmd, int len)
{
@@ -553,7 +1049,7 @@ static int handle_task_commands(unsigned char *cmd, int len)
/* Unrecognized command */
return GDBSTUB_ST_ERR;
}
} else if (strncmp((char *)cmd, "vCont;c", 7) == 0 || cmd[0] == 'c') { //continue execution
} else if (strncmp((char *)cmd, "vCont;c", 7) == 0 || cmd[0] == 'c') { /*continue execution */
return GDBSTUB_ST_CONT;
} else {
/* Unrecognized command */
@@ -562,4 +1058,4 @@ static int handle_task_commands(unsigned char *cmd, int len)
return GDBSTUB_ST_OK;
}
#endif // CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
#endif /* CONFIG_ESP_GDBSTUB_SUPPORT_TASKS */

View File

@@ -1,16 +1,8 @@
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
@@ -86,6 +78,16 @@ 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

View File

@@ -1,18 +1,25 @@
#include "freertos/xtensa_rtos.h"
/*
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
// ------------------------------------------------
.section .iram1,"ax"
#include "freertos/xtensa_rtos.h"
.global gdbstub_handle_uart_int
.global _xt_gdbstub_int
.align 4
.section .iram1, "ax"
.global gdbstub_handle_uart_int
.global _xt_gdbstub_int
.align 4
_xt_gdbstub_int:
/* Allocate exception frame and save minimal context. */
mov a0, sp
addi sp, sp, -XT_STK_FRMSZ
s32i a0, sp, XT_STK_A1
s32i a0, sp, XT_STK_EXIT
s32i a0, sp, XT_STK_A0
#if XCHAL_HAVE_WINDOWED
s32e a0, sp, -12 /* for debug backtrace */
#endif
@@ -20,8 +27,6 @@ _xt_gdbstub_int:
s32i a0, sp, XT_STK_PS
rsr a0, EPC_1 /* save interruptee's PC */
s32i a0, sp, XT_STK_PC
rsr a0, EXCSAVE_1 /* save interruptee's a0 */
s32i a0, sp, XT_STK_A0
#if XCHAL_HAVE_WINDOWED
s32e a0, sp, -16 /* for debug backtrace */
#endif
@@ -34,7 +39,20 @@ _xt_gdbstub_int:
rsr a0, EXCVADDR
s32i a0, sp, XT_STK_EXCVADDR
mov a6,sp
/* _xt_context_save seems to save the current a0, but we need the interuptees a0. Fix this. */
rsr a0, EXCSAVE_1 /* save interruptee's a0 */
s32i a0, sp, XT_STK_A0
/* Save context pointer as input parameter */
rsr a6, excsave1
rsr a3, EPS
s32i a3, sp, XT_STK_PS // store PS to the ps place
movi a3, gdbstub_handle_uart_int
callx0 a3
l32i a0, sp, XT_STK_EXIT
addi sp, sp, XT_STK_FRMSZ
ret

View File

@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@@ -10,6 +10,8 @@
#include "soc/soc_memory_layout.h"
#include "xtensa/config/specreg.h"
#include "sdkconfig.h"
#include "esp_ipc_isr.h"
#include "esp_private/crosscore_int.h"
#if !XCHAL_HAVE_WINDOWED
#warning "gdbstub_xtensa: revisit the implementation for Call0 ABI"
@@ -122,3 +124,96 @@ int esp_gdbstub_get_signal(const esp_gdbstub_frame_t *frame)
}
return (int) exccause_to_signal[frame->exccause];
}
/** @brief Init dport for GDB
* Init dport for iterprocessor communications
* */
void esp_gdbstub_init_dports(void)
{
}
#if CONFIG_IDF_TARGET_ARCH_XTENSA && (!CONFIG_FREERTOS_UNICORE) && CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
static bool stall_started = false;
#endif
/** @brief GDB stall other CPU
* GDB stall other CPU
* */
void esp_gdbstub_stall_other_cpus_start()
{
#if CONFIG_IDF_TARGET_ARCH_XTENSA && (!CONFIG_FREERTOS_UNICORE) && CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
if (stall_started == false) {
esp_ipc_isr_stall_other_cpu();
stall_started = true;
}
#endif
}
/** @brief GDB end stall other CPU
* GDB end stall other CPU
* */
void esp_gdbstub_stall_other_cpus_end()
{
#if CONFIG_IDF_TARGET_ARCH_XTENSA && (!CONFIG_FREERTOS_UNICORE) && CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
if (stall_started == true) {
esp_ipc_isr_release_other_cpu();
stall_started = false;
}
#endif
}
/** @brief GDB clear step
* GDB clear step registers
* */
void esp_gdbstub_clear_step(void)
{
WSR(ICOUNT, 0);
WSR(ICOUNTLEVEL, 0);
}
/** @brief GDB do step
* GDB do one step
* */
void esp_gdbstub_do_step(void)
{
// 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
// If we will stop by the breakpoint inside interrupt, we will handle this interrupt with ICOUNTLEVEL=ps.intlevel+1
uint32_t level = s_scratch.regfile.ps;
level &= 0x7;
level += 1;
WSR(ICOUNTLEVEL, level);
WSR(ICOUNT, -2);
}
/** @brief GDB trigger other CPU
* GDB trigger other CPU
* */
void esp_gdbstub_trigger_cpu(void)
{
#if !CONFIG_FREERTOS_UNICORE
if (0 == cpu_hal_get_core_id()) {
esp_crosscore_int_send_gdb_call(1);
} else {
esp_crosscore_int_send_gdb_call(0);
}
#endif
}
/** @brief GDB set register in frame
* Set register in frame with address to value
*
* */
void esp_gdbstub_set_register(esp_gdbstub_frame_t *frame, uint32_t reg_index, uint32_t value)
{
switch (reg_index) {
case 0:
frame->pc = value;
break;
default:
(&frame->a0)[reg_index - 1] = value;
break;
}
}

View File

@@ -0,0 +1,64 @@
/*
* SPDX-FileCopyrightText: 2020-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <xtensa/coreasm.h>
#include <xtensa/corebits.h>
#include <xtensa/config/system.h>
#include "freertos/xtensa_context.h"
.section .iram1,"ax"
.global _xt_panic_gdbstub
.type _xt_panic_gdbstub,@function
.align 4
.literal_position
.align 4
_xt_panic_gdbstub:
/* Allocate exception frame and save minimal context. */
addi sp, sp, -XT_STK_FRMSZ
s32i a0, sp, XT_STK_EXIT
s32i a0, sp, XT_STK_A0
rsr a0, PS /* save interruptee's PS */
s32i a0, sp, XT_STK_PS
rsr a0, EPC_1 /* save interruptee's PC */
s32i a0, sp, XT_STK_PC
call0 _xt_context_save /* Save full context*/
addi a7, sp, XT_STK_FRMSZ
s32i a7, sp, XT_STK_A1
s32i a12, sp, XT_STK_A12
s32i a13, sp, XT_STK_A13
/* Save exc cause and vaddr into exception frame */
rsr a0, EXCCAUSE
s32i a0, sp, XT_STK_EXCCAUSE
rsr a0, EXCVADDR
s32i a0, sp, XT_STK_EXCVADDR
/* _xt_context_save seems to save the current a0, but we need the interuptees a0. Fix this. */
rsr a0, EXCSAVE_1 /* save interruptee's a0 */
/* Set up PS for C, disable all interrupts except NMI and debug, and clear EXCM. */
movi a0, PS_INTLEVEL(5) | PS_UM | PS_WOE
wsr a0, PS
//Call gdbstub handler
rsr a0,(EPC + XCHAL_DEBUGLEVEL)
s32i a0, sp, XT_STK_PC
mov a6, sp
rsr a9, EPS_6
s32i a9, sp, XT_STK_PS // store PS to the ps place
movi a11, gdbstub_handle_debug_int
callx4 a11 /* Call interrupt handler */
l32i a0, sp, XT_STK_PC
wsr a0,(EPC + XCHAL_DEBUGLEVEL)
call0 _xt_context_restore /* Restore full context*/
l32i a12, sp, XT_STK_A12
l32i a13, sp, XT_STK_A13
l32i a0, sp, XT_STK_EXIT /* Restore return point*/
addi sp, sp, XT_STK_FRMSZ /* Restore SP*/
rfi 6 // Return from high-level interrupt