feat(gdbstub): bring GDB stub from esp-idf

Commit ID: 758db1e0
This commit is contained in:
dongheng
2019-10-28 15:44:39 +08:00
committed by Dong Heng
parent 3d394bd924
commit 56747578d2
12 changed files with 1027 additions and 0 deletions

View File

@ -0,0 +1,13 @@
idf_build_get_property(target IDF_TARGET)
set(esp_gdbstub_srcs "src/gdbstub.c"
"src/packet.c"
"${target}/gdbstub_${target}.c"
"xtensa/gdbstub_xtensa.c")
idf_component_register(SRCS "${esp_gdbstub_srcs}"
INCLUDE_DIRS "include"
PRIV_INCLUDE_DIRS "private_include" "${target}" "xtensa"
LDFRAGMENTS "linker.lf"
REQUIRES "freertos"
PRIV_REQUIRES "soc" "xtensa" "esp_rom")

View File

@ -0,0 +1,25 @@
menu "GDB Stub"
# Hidden option which is selected from the "Panic handler behavior"
# menu in the target component.
config ESP_GDBSTUB_ENABLED
bool
config ESP_GDBSTUB_SUPPORT_TASKS
bool "Enable listing FreeRTOS tasks through GDB Stub"
depends on ESP_GDBSTUB_ENABLED
default y
help
If enabled, GDBStub can supply the list of FreeRTOS tasks to GDB.
Thread list can be queried from GDB using 'info threads' command.
Note that if GDB task lists were corrupted, this feature may not work.
If GDBStub fails, try disabling this feature.
config ESP_GDBSTUB_MAX_TASKS
int "Maximum number of tasks supported by GDB Stub"
default 32
depends on ESP_GDBSTUB_SUPPORT_TASKS
help
Set the number of tasks which GDB Stub will support.
endmenu

View File

@ -0,0 +1,4 @@
COMPONENT_ADD_INCLUDEDIRS := include
COMPONENT_PRIV_INCLUDEDIRS := private_include esp32 xtensa
COMPONENT_SRCDIRS := src esp32 xtensa
COMPONENT_ADD_LDFRAGMENTS += linker.lf

View File

@ -0,0 +1,51 @@
// 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.
#include "soc/uart_periph.h"
#include "soc/gpio_periph.h"
#include "esp_gdbstub_common.h"
#include "sdkconfig.h"
#define UART_NUM CONFIG_CONSOLE_UART_NUM
void esp_gdbstub_target_init(void)
{
}
int esp_gdbstub_getchar(void)
{
while (REG_GET_FIELD(UART_STATUS_REG(UART_NUM), UART_RXFIFO_CNT) == 0) {
;
}
return REG_READ(UART_FIFO_REG(UART_NUM));
}
void esp_gdbstub_putchar(int c)
{
while (REG_GET_FIELD(UART_STATUS_REG(UART_NUM), UART_TXFIFO_CNT) >= 126) {
;
}
REG_WRITE(UART_FIFO_REG(UART_NUM), c);
}
int esp_gdbstub_readmem(intptr_t addr)
{
if (addr < 0x20000000 || addr >= 0x80000000) {
/* see cpu_configure_region_protection */
return -1;
}
uint32_t val_aligned = *(uint32_t *)(addr & (~3));
uint32_t shift = (addr & 3) * 8;
return (val_aligned >> shift) & 0xff;
}

View File

@ -0,0 +1,18 @@
// Copyright 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.
#pragma once
/* Number of extra TIE defined registers, not listed in the XCHAL */
#define GDBSTUB_EXTRA_TIE_SIZE 0

View File

@ -0,0 +1,27 @@
// 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.
#pragma once
#include "esp_gdbstub_arch.h"
#ifdef __cplusplus
extern "C" {
#endif
void esp_gdbstub_panic_handler(esp_gdbstub_frame_t *frame) __attribute__((noreturn));
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,7 @@
[mapping:esp_gdbstub]
archive: libesp_gdbstub.a
entries:
if ESP32_PANIC_HANDLER_IRAM = y:
* (noflash_text)
else:
* (default)

View File

@ -0,0 +1,151 @@
// 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.
#pragma once
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include "esp_gdbstub.h"
#include "sdkconfig.h"
#ifdef CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#endif // CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
/* Internal error codes used by the routines that parse the incoming gdb packet */
#define GDBSTUB_ST_ENDPACKET -1
#define GDBSTUB_ST_ERR -2
#define GDBSTUB_ST_OK -3
/* Special task index values */
#define GDBSTUB_CUR_TASK_INDEX_UNKNOWN -1
/* Cab be set to a lower value in gdbstub_target_config.h */
#ifndef GDBSTUB_CMD_BUFLEN
#define GDBSTUB_CMD_BUFLEN 512
#endif
#if CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
typedef enum {
GDBSTUB_NOT_STARTED,
GDBSTUB_STARTED,
GDBSTUB_TASK_SUPPORT_DISABLED
} esp_gdbstub_state_t;
#define GDBSTUB_TASKS_NUM CONFIG_ESP_GDBSTUB_MAX_TASKS
#endif // CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
/* gdbstub temporary run-time data, stored in .bss to reduce stack usage */
typedef struct {
esp_gdbstub_gdb_regfile_t regfile;
int signal;
#if CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
esp_gdbstub_state_t state;
int task_count;
int paniced_task_index;
int current_task_index;
int thread_info_index; //!< index of the last task passed to qsThreadInfo
esp_gdbstub_frame_t paniced_frame;
TaskSnapshot_t tasks[GDBSTUB_TASKS_NUM]; // TODO: add an API to get snapshots one by one
#endif // CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
} esp_gdbstub_scratch_t;
/**** Functions provided by the architecture specific part ****/
/**
* @param frame exception frame pointer
* @return the appropriate "signal" number for the given exception cause
*/
int esp_gdbstub_get_signal(const esp_gdbstub_frame_t *frame);
/**
* Write registers from the exception frame to the GDB register file
* @param frame exception frame to parse
* @param dst pointer to the GDB register file
*/
void esp_gdbstub_frame_to_regfile(const esp_gdbstub_frame_t *frame, esp_gdbstub_gdb_regfile_t *dst);
#if CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
/**
* Write registers from the saved frame of a given task to the GDB register file
* @param tcb pointer to the TCB of the task
* @param dst pointer to the GDB register file
*/
void esp_gdbstub_tcb_to_regfile(TaskHandle_t tcb, esp_gdbstub_gdb_regfile_t *dst);
#endif // CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
/**** Functions provided by the target specific part ****/
/**
* Do target-specific initialization before gdbstub can start communicating.
* This may involve, for example, configuring the UART.
*/
void esp_gdbstub_target_init(void);
/**
* Receive a byte from the GDB client. Blocks until a byte is available.
* @return received byte
*/
int esp_gdbstub_getchar(void);
/**
* Send a byte to the GDB client
* @param c byte to send
*/
void esp_gdbstub_putchar(int c);
/**
* Read a byte from target memory
* @param ptr address
* @return byte value, or GDBSTUB_ST_ERR if the address is not readable
*/
int esp_gdbstub_readmem(intptr_t addr);
/**** GDB packet related functions ****/
/** Begin a packet */
void esp_gdbstub_send_start(void);
/** Send a character as part of the packet */
void esp_gdbstub_send_char(char c);
/** Send a string as part of the packet */
void esp_gdbstub_send_str(const char *s);
/** Send a hex value as part of the packet */
void esp_gdbstub_send_hex(int val, int bits);
/** Finish sending the packet */
void esp_gdbstub_send_end(void);
/** Send a packet with a string as content */
void esp_gdbstub_send_str_packet(const char* str);
/** Get a hex value from the gdb packet */
uint32_t esp_gdbstub_gethex(const unsigned char **ptr, int bits);
/** Read, unescape, and validate the incoming GDB command */
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);

View File

@ -0,0 +1,345 @@
// 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.
#include <string.h>
#include "esp_gdbstub.h"
#include "esp_gdbstub_common.h"
#include "sdkconfig.h"
#ifdef CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
static void init_task_info(void);
static void find_paniced_task_index(void);
static int handle_task_commands(unsigned char *cmd, int len);
#endif
static void send_reason(void);
static esp_gdbstub_scratch_t s_scratch;
void esp_gdbstub_panic_handler(esp_gdbstub_frame_t *frame)
{
#ifndef CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
esp_gdbstub_frame_to_regfile(frame, &s_scratch.regfile);
#else
if (s_scratch.state == GDBSTUB_STARTED) {
/* We have re-entered GDB Stub. Try disabling task support. */
s_scratch.state = GDBSTUB_TASK_SUPPORT_DISABLED;
/* Flush any pending GDB packet (this creates a garbage value) */
esp_gdbstub_send_end();
} else if (s_scratch.state == GDBSTUB_NOT_STARTED) {
s_scratch.state = GDBSTUB_STARTED;
/* Save the paniced frame and get the list of tasks */
memcpy(&s_scratch.paniced_frame, frame, sizeof(*frame));
esp_gdbstub_frame_to_regfile(frame, &s_scratch.regfile);
init_task_info();
find_paniced_task_index();
/* Current task is the paniced task */
if (s_scratch.paniced_task_index == GDBSTUB_CUR_TASK_INDEX_UNKNOWN) {
s_scratch.current_task_index = 0;
}
}
#endif // CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
esp_gdbstub_target_init();
s_scratch.signal = esp_gdbstub_get_signal(frame);
send_reason();
while (true) {
unsigned char *cmd;
size_t size;
int res = esp_gdbstub_read_command(&cmd, &size);
if (res > 0) {
/* character received instead of a command */
continue;
}
if (res == GDBSTUB_ST_ERR) {
esp_gdbstub_send_str_packet("E01");
continue;
}
res = esp_gdbstub_handle_command(cmd, size);
if (res == GDBSTUB_ST_ERR) {
esp_gdbstub_send_str_packet(NULL);
}
}
}
static void send_reason(void)
{
esp_gdbstub_send_start();
esp_gdbstub_send_char('T');
esp_gdbstub_send_hex(s_scratch.signal, 8);
esp_gdbstub_send_end();
}
static uint32_t gdbstub_hton(uint32_t i)
{
return __builtin_bswap32(i);
}
/** Send all registers to gdb */
static void handle_g_command(const unsigned char* cmd, int len)
{
uint32_t *p = (uint32_t *) &s_scratch.regfile;
esp_gdbstub_send_start();
for (int i = 0; i < sizeof(s_scratch.regfile) / sizeof(*p); ++i) {
esp_gdbstub_send_hex(gdbstub_hton(*p++), 32);
}
esp_gdbstub_send_end();
}
/** Receive register values from gdb */
static void handle_G_command(const unsigned char* cmd, int len)
{
uint32_t *p = (uint32_t *) &s_scratch.regfile;
for (int i = 0; i < sizeof(s_scratch.regfile) / sizeof(*p); ++i) {
*p++ = gdbstub_hton(esp_gdbstub_gethex(&cmd, 32));
}
esp_gdbstub_send_str_packet("OK");
}
/** Read memory to gdb */
static void handle_m_command(const unsigned char* cmd, int len)
{
intptr_t addr = (intptr_t) esp_gdbstub_gethex(&cmd, -1);
cmd++;
uint32_t size = esp_gdbstub_gethex(&cmd, -1);
if (esp_gdbstub_readmem(addr) < 0 || esp_gdbstub_readmem(addr + size - 1) < 0) {
esp_gdbstub_send_str_packet("E01");
return;
}
esp_gdbstub_send_start();
for (int i = 0; i < size; ++i) {
int b = esp_gdbstub_readmem(addr++);
esp_gdbstub_send_hex(b, 8);
}
esp_gdbstub_send_end();
}
/** Handle a command received from gdb */
int esp_gdbstub_handle_command(unsigned char *cmd, int len)
{
unsigned char *data = cmd + 1;
if (cmd[0] == 'g')
{
handle_g_command(data, len - 1);
} else if (cmd[0] == 'G') {
/* receive content for all registers from gdb */
handle_G_command(data, len - 1);
} else if (cmd[0] == 'm') {
/* read memory to gdb */
handle_m_command(data, len - 1);
} else if (cmd[0] == '?') {
/* Reply with stop reason */
send_reason();
#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 {
/* Unrecognized command */
return GDBSTUB_ST_ERR;
}
return GDBSTUB_ST_OK;
}
/* Everything below is related to the support for listing FreeRTOS tasks as threads in GDB */
#ifdef CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
static void init_task_info(void)
{
unsigned tcb_size;
s_scratch.task_count = uxTaskGetSnapshotAll(s_scratch.tasks, GDBSTUB_TASKS_NUM, &tcb_size);
}
static bool get_task_handle(size_t index, TaskHandle_t *handle)
{
if (index >= s_scratch.task_count) {
return false;
}
*handle = (TaskHandle_t) s_scratch.tasks[index].pxTCB;
return true;
}
/** Get the index of the task running on the current CPU, and save the result */
static void find_paniced_task_index(void)
{
TaskHandle_t cur_handle = xTaskGetCurrentTaskHandleForCPU(xPortGetCoreID());
TaskHandle_t handle;
for (int i = 0; i < s_scratch.task_count; i++) {
if (get_task_handle(i, &handle) && cur_handle == handle) {
s_scratch.paniced_task_index = i;
return;
}
}
s_scratch.paniced_task_index = GDBSTUB_CUR_TASK_INDEX_UNKNOWN;
}
/** H command sets the "current task" for the purpose of further commands */
static void handle_H_command(const unsigned char* cmd, int len)
{
if (cmd[1] == 'g' || cmd[1] == 'c') {
const char *ret = "OK";
cmd += 2;
int requested_task_index = esp_gdbstub_gethex(&cmd, -1);
if (requested_task_index == s_scratch.paniced_task_index ||
(requested_task_index == 0 && s_scratch.current_task_index == GDBSTUB_CUR_TASK_INDEX_UNKNOWN)) {
/* Get the registers of the paniced task */
esp_gdbstub_frame_to_regfile(&s_scratch.paniced_frame, &s_scratch.regfile);
} else if (requested_task_index > s_scratch.task_count) {
ret = "E00";
} else {
TaskHandle_t handle = NULL;
get_task_handle(requested_task_index, &handle);
/* FIXME: for the task currently running on the other CPU, extracting the registers from TCB
* isn't valid. Need to use some IPC mechanism to obtain the registers of the other CPU
*/
if (handle != NULL) {
esp_gdbstub_tcb_to_regfile(handle, &s_scratch.regfile);
}
}
esp_gdbstub_send_str_packet(ret);
} else {
esp_gdbstub_send_str_packet(NULL);
}
}
/** qC returns the current thread ID */
static void handle_qC_command(const unsigned char* cmd, int len)
{
esp_gdbstub_send_start();
esp_gdbstub_send_str("QC");
esp_gdbstub_send_hex(s_scratch.current_task_index, 32);
esp_gdbstub_send_end();
}
/** 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.
*/
static void handle_T_command(const unsigned char* cmd, int len)
{
esp_gdbstub_send_str_packet("OK");
}
/** qfThreadInfo requests the start of the thread list, qsThreadInfo (below) is repeated to
* get the subsequent threads.
*/
static void handle_qfThreadInfo_command(const unsigned char* cmd, int len)
{
/* The first task in qfThreadInfo reply is going to be the one which GDB will request to stop.
* Therefore it has to be the paniced task.
* Reply with the paniced task index, and later skip over this index while handling qsThreadInfo
*/
esp_gdbstub_send_start();
esp_gdbstub_send_str("m");
esp_gdbstub_send_hex(s_scratch.paniced_task_index, 32);
esp_gdbstub_send_end();
s_scratch.thread_info_index = 0;
}
static void handle_qsThreadInfo_command(const unsigned char* cmd, int len)
{
int next_task_index = ++s_scratch.thread_info_index;
if (next_task_index == s_scratch.task_count) {
/* No more tasks */
esp_gdbstub_send_str_packet("l");
return;
}
if (next_task_index == s_scratch.paniced_task_index) {
/* Have already sent this one in the reply to qfThreadInfo, skip over it */
handle_qsThreadInfo_command(cmd, len);
return;
}
esp_gdbstub_send_start();
esp_gdbstub_send_str("m");
esp_gdbstub_send_hex(next_task_index, 32);
esp_gdbstub_send_end();
}
/** qThreadExtraInfo requests the thread name */
static void handle_qThreadExtraInfo_command(const unsigned char* cmd, int len)
{
cmd += sizeof("qThreadExtraInfo,") - 1;
int task_index = esp_gdbstub_gethex(&cmd, -1);
TaskHandle_t handle;
if (!get_task_handle(task_index, &handle)) {
esp_gdbstub_send_str_packet("E01");
return;
}
esp_gdbstub_send_start();
const char* task_name = pcTaskGetTaskName(handle);
while (*task_name) {
esp_gdbstub_send_hex(*task_name, 8);
task_name++;
}
/** TODO: add "Running" or "Suspended" and "CPU0" or "CPU1" */
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)
{
if (cmd[0] == 'H') {
/* Continue with task */
handle_H_command(cmd, len);
} else if (cmd[0] == 'T') {
/* Task alive check */
handle_T_command(cmd, len);
} else if (cmd[0] == 'q') {
if (command_name_matches("qfThreadInfo", cmd, len)) {
handle_qfThreadInfo_command(cmd, len);
} else if (command_name_matches("qsThreadInfo", cmd, len)) {
handle_qsThreadInfo_command(cmd, len);
} else if (command_name_matches("qC", cmd, len)) {
handle_qC_command(cmd, len);
} else if (command_name_matches("qThreadExtraInfo", cmd, len)) {
handle_qThreadExtraInfo_command(cmd, len);
} else {
/* Unrecognized command */
return GDBSTUB_ST_ERR;
}
} else {
/* Unrecognized command */
return GDBSTUB_ST_ERR;
}
return GDBSTUB_ST_OK;
}
#endif // CONFIG_ESP_GDBSTUB_SUPPORT_TASKS

View File

@ -0,0 +1,177 @@
// 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.
#include "esp_gdbstub_common.h"
// GDB command input buffer
static unsigned char s_cmd[GDBSTUB_CMD_BUFLEN];
// Running checksum of the output packet
static char s_chsum;
// Send the start of a packet; reset checksum calculation.
void esp_gdbstub_send_start(void)
{
s_chsum = 0;
esp_gdbstub_putchar('$');
}
// Send a char as part of a packet
void esp_gdbstub_send_char(char c)
{
if (c == '#' || c == '$' || c == '}' || c == '*') {
esp_gdbstub_putchar('}');
esp_gdbstub_putchar(c ^ 0x20);
s_chsum += (c ^ 0x20) + '}';
} else {
esp_gdbstub_putchar(c);
s_chsum += c;
}
}
// Send a string as part of a packet
void esp_gdbstub_send_str(const char *c)
{
while (*c != 0) {
esp_gdbstub_send_char(*c);
c++;
}
}
// Send a hex val as part of a packet.
// 'bits'/4 dictates the number of hex chars sent.
void esp_gdbstub_send_hex(int val, int bits)
{
const char* hex_chars = "0123456789abcdef";
for (int i = bits; i > 0; i -= 4) {
esp_gdbstub_send_char(hex_chars[(val >> (i - 4)) & 0xf]);
}
}
// Finish sending a packet.
void esp_gdbstub_send_end(void)
{
esp_gdbstub_putchar('#');
esp_gdbstub_send_hex(s_chsum, 8);
}
// Send a packet with a string as content
void esp_gdbstub_send_str_packet(const char* str)
{
esp_gdbstub_send_start();
if (str != NULL) {
esp_gdbstub_send_str(str);
}
esp_gdbstub_send_end();
}
// Grab a hex value from the gdb packet. Ptr will get positioned on the end
// of the hex string, as far as the routine has read into it. Bits/4 indicates
// the max amount of hex chars it gobbles up. Bits can be -1 to eat up as much
// hex chars as possible.
uint32_t esp_gdbstub_gethex(const unsigned char **ptr, int bits)
{
int i;
int no;
uint32_t v = 0;
char c;
no = bits / 4;
if (bits == -1) {
no = 64;
}
for (i = 0; i < no; i++) {
c = **ptr;
(*ptr)++;
if (c >= '0' && c <= '9') {
v <<= 4;
v |= (c - '0');
} else if (c >= 'A' && c <= 'F') {
v <<= 4;
v |= (c - 'A') + 10;
} else if (c >= 'a' && c <= 'f') {
v <<= 4;
v |= (c - 'a') + 10;
} else if (c == '#') {
if (bits == -1) {
(*ptr)--;
return v;
}
return GDBSTUB_ST_ENDPACKET;
} else {
if (bits == -1) {
(*ptr)--;
return v;
}
return GDBSTUB_ST_ERR;
}
}
return v;
}
// Lower layer: grab a command packet and check the checksum
// Calls gdbHandleCommand on the packet if the checksum is OK
// Returns GDBSTUB_ST_OK on success, GDBSTUB_ST_ERR when checksum fails, a
// character if it is received instead of the GDB packet
// start char.
int esp_gdbstub_read_command(unsigned char **out_cmd, size_t *out_size)
{
unsigned char c;
unsigned char chsum = 0;
unsigned char sentchs[2];
int p = 0;
c = esp_gdbstub_getchar();
if (c != '$') {
return c;
}
while (1) {
c = esp_gdbstub_getchar();
if (c == '#') {
// end of packet, checksum follows
s_cmd[p] = 0;
break;
}
chsum += c;
if (c == '$') {
// restart packet?
chsum = 0;
p = 0;
continue;
}
if (c == '}') {
//escape the next char
c = esp_gdbstub_getchar();
chsum += c;
c ^= 0x20;
}
s_cmd[p++] = c;
if (p >= GDBSTUB_CMD_BUFLEN) {
return GDBSTUB_ST_ERR;
}
}
// A # has been received. Get and check the received chsum.
sentchs[0] = esp_gdbstub_getchar();
sentchs[1] = esp_gdbstub_getchar();
const unsigned char* c_ptr = &sentchs[0];
unsigned char rchsum = esp_gdbstub_gethex(&c_ptr, 8);
if (rchsum != chsum) {
esp_gdbstub_putchar('-');
return GDBSTUB_ST_ERR;
} else {
esp_gdbstub_putchar('+');
*out_cmd = s_cmd;
*out_size = p;
return GDBSTUB_ST_OK;
}
}

View File

@ -0,0 +1,91 @@
// 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.
#pragma once
#include <stdint.h>
#include "freertos/xtensa_context.h"
#include "gdbstub_target_config.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef XtExcFrame esp_gdbstub_frame_t;
/* GDB regfile structure, configuration dependent */
typedef struct {
uint32_t pc;
uint32_t a[XCHAL_NUM_AREGS];
#if XCHAL_HAVE_LOOPS
uint32_t lbeg;
uint32_t lend;
uint32_t lcount;
#endif
uint32_t sar;
#if XCHAL_HAVE_WINDOWED
uint32_t windowbase;
uint32_t windowstart;
#endif
uint32_t configid0;
uint32_t configid1;
uint32_t ps;
#if XCHAL_HAVE_THREADPTR
uint32_t threadptr;
#endif
#if XCHAL_HAVE_BOOLEANS
uint32_t br;
#endif
#if XCHAL_HAVE_S32C1I
uint32_t scompare1;
#endif
#if XCHAL_HAVE_MAC16
uint32_t acclo;
uint32_t acchi;
uint32_t m0;
uint32_t m1;
uint32_t m2;
uint32_t m3;
#endif
#if XCHAL_HAVE_DFP_ACCEL
uint32_t expstate;
uint32_t f64r_lo;
uint32_t f64r_hi;
uint32_t f64s;
#endif
#if XCHAL_HAVE_FP
uint32_t f[16];
uint32_t fcr;
uint32_t fsr;
#endif
#if GDBSTUB_EXTRA_TIE_SIZE > 0
uint32_t tie[GDBSTUB_EXTRA_TIE_SIZE];
#endif
} esp_gdbstub_gdb_regfile_t;
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,118 @@
// 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.
#include <string.h>
#include "esp_gdbstub.h"
#include "esp_gdbstub_common.h"
#include "soc/cpu.h"
#include "soc/soc_memory_layout.h"
#include "sdkconfig.h"
#if !XCHAL_HAVE_WINDOWED
#warning "gdbstub_xtensa: revisit the implementation for Call0 ABI"
#endif
static void init_regfile(esp_gdbstub_gdb_regfile_t *dst)
{
memset(dst, 0, sizeof(*dst));
}
static void update_regfile_common(esp_gdbstub_gdb_regfile_t *dst)
{
if (dst->a[0] & 0x8000000U) {
dst->a[0] = (dst->a[0] & 0x3fffffffU) | 0x40000000U;
}
if (!esp_stack_ptr_is_sane(dst->a[1])) {
dst->a[1] = 0xDEADBEEF;
}
dst->windowbase = 0;
dst->windowstart = 0x1;
RSR(CONFIGID0, dst->configid0);
RSR(CONFIGID1, dst->configid1);
}
void esp_gdbstub_frame_to_regfile(const esp_gdbstub_frame_t *frame, esp_gdbstub_gdb_regfile_t *dst)
{
init_regfile(dst);
const uint32_t *a_regs = (const uint32_t *) &frame->a0;
dst->pc = (frame->pc & 0x3fffffffU) | 0x40000000U;
for (int i = 0; i < 16; i++) {
dst->a[i] = a_regs[i];
}
for (int i = 16; i < 64; i++) {
dst->a[i] = 0xDEADBEEF;
}
#if XCHAL_HAVE_LOOPS
dst->lbeg = frame->lbeg;
dst->lend = frame->lend;
dst->lcount = frame->lcount;
#endif
dst->ps = (frame->ps & PS_UM) ? (frame->ps & ~PS_EXCM) : frame->ps;
dst->sar = frame->sar;
update_regfile_common(dst);
}
#ifdef CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
static void solicited_frame_to_regfile(const XtSolFrame *frame, esp_gdbstub_gdb_regfile_t *dst)
{
init_regfile(dst);
const uint32_t *a_regs = (const uint32_t *) &frame->a0;
dst->pc = (frame->pc & 0x3fffffffU) | 0x40000000U;
/* only 4 registers saved in the solicited frame */
for (int i = 0; i < 4; i++) {
dst->a[i] = a_regs[i];
}
for (int i = 4; i < 64; i++) {
dst->a[i] = 0xDEADBEEF;
}
dst->ps = (frame->ps & PS_UM) ? (frame->ps & ~PS_EXCM) : frame->ps;
update_regfile_common(dst);
}
/* Represents FreeRTOS TCB structure */
typedef struct {
uint8_t *top_of_stack;
/* Other members aren't needed */
} dummy_tcb_t;
void esp_gdbstub_tcb_to_regfile(TaskHandle_t tcb, esp_gdbstub_gdb_regfile_t *dst)
{
const dummy_tcb_t *dummy_tcb = (const dummy_tcb_t *) tcb;
const XtExcFrame *frame = (XtExcFrame *) dummy_tcb->top_of_stack;
if (frame->exit != 0) {
esp_gdbstub_frame_to_regfile(frame, dst);
} else {
const XtSolFrame *taskFrame = (const XtSolFrame *) dummy_tcb->top_of_stack;
solicited_frame_to_regfile(taskFrame, dst);
}
}
#endif // CONFIG_ESP_GDBSTUB_SUPPORT_TASKS
int esp_gdbstub_get_signal(const esp_gdbstub_frame_t *frame)
{
const char exccause_to_signal[] = {4, 31, 11, 11, 2, 6, 8, 0, 6, 7, 0, 0, 7, 7, 7, 7};
if (frame->exccause > sizeof(exccause_to_signal)) {
return 11;
}
return (int) exccause_to_signal[frame->exccause];
}