risc-v2/gdb-server.c

1117 lines
30 KiB
C
Raw Permalink Normal View History

2019-11-11 17:15:06 +08:00
/*
Copyright (c) 2019 zoomdy@163.com
RV-LINK is licensed under the Mulan PSL v1.
You can use this software according to the terms and conditions of the Mulan PSL v1.
You may obtain a copy of Mulan PSL v1 at:
http://license.coscl.org.cn/MulanPSL
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
PURPOSE.
See the Mulan PSL v1 for more details.
*/
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include "rvl-app-config.h"
#include "rvl-link-config.h"
#include "rvl-target-config.h"
#include "riscv_encoding.h"
#include "rvl-led.h"
#include "rvl-serial.h"
#include "rvl-link.h"
#include "gdb-server.h"
#include "rvl-target.h"
#include "gdb-serial.h"
typedef int16_t gdb_server_tid_t;
typedef struct gdb_server_s
{
struct pt pt_server;
struct pt pt_cmd;
struct pt pt_cmd_sub;
struct pt pt_sub_routine;
const char *cmd;
size_t cmd_len;
char *res;
bool target_running;
bool gdb_connected;
rvl_target_halt_info_t halt_info;
rvl_target_error_t target_error;
rvl_target_addr_t mem_addr;
size_t mem_len;
uint8_t mem_buffer[GDB_SERIAL_RESPONSE_BUFFER_SIZE];
int flash_err;
int i;
rvl_target_reg_t regs[RVL_TARGET_CONFIG_REG_NUM];
rvl_target_reg_t reg_tmp;
int reg_tmp_num;
gdb_server_tid_t tid_g;
gdb_server_tid_t tid_G;
gdb_server_tid_t tid_m;
gdb_server_tid_t tid_M;
gdb_server_tid_t tid_c;
rvl_target_breakpoint_type_t breakpoint_type;
rvl_target_addr_t breakpoint_addr;
int breakpoint_kind;
int breakpoint_err;
}gdb_server_t;
static gdb_server_t gdb_server_i;
#define self gdb_server_i
PT_THREAD(gdb_server_cmd_q(void));
PT_THREAD(gdb_server_cmd_qSupported(void));
void gdb_server_cmd_qxfer_features_read_target_xml(void);
void gdb_server_cmd_qxfer_memory_map_read(void);
PT_THREAD(gdb_server_cmd_qRcmd(void));
PT_THREAD(gdb_server_cmd_Q(void));
PT_THREAD(gdb_server_cmd_H(void));
PT_THREAD(gdb_server_cmd_g(void));
PT_THREAD(gdb_server_cmd_G(void));
PT_THREAD(gdb_server_cmd_k(void));
PT_THREAD(gdb_server_cmd_c(void));
PT_THREAD(gdb_server_cmd_s(void));
PT_THREAD(gdb_server_cmd_m(void));
PT_THREAD(gdb_server_cmd_M(void));
PT_THREAD(gdb_server_cmd_X(void));
PT_THREAD(gdb_server_cmd_p(void));
PT_THREAD(gdb_server_cmd_P(void));
PT_THREAD(gdb_server_cmd_z(void));
PT_THREAD(gdb_server_cmd_Z(void));
PT_THREAD(gdb_server_cmd_v(void));
PT_THREAD(gdb_server_cmd_vFlashErase(void));
PT_THREAD(gdb_server_cmd_vFlashWrite(void));
PT_THREAD(gdb_server_cmd_vFlashDone(void));
PT_THREAD(gdb_server_cmd_vMustReplyEmpty(void));
PT_THREAD(gdb_server_cmd_question_mark(void));
PT_THREAD(gdb_server_cmd_ctrl_c(void));
PT_THREAD(gdb_server_connected(void));
PT_THREAD(gdb_server_disconnected(void));
static void gdb_server_target_run(bool run);
static void gdb_server_reply_ok(void);
static void gdb_server_reply_empty(void);
static void gdb_server_reply_err(int err);
static void bin_to_hex(const uint8_t *bin, char *hex, size_t nbyte);
static void word_to_hex_le(uint32_t word, char *hex);
static void hex_to_bin(const char *hex, uint8_t *bin, size_t nbyte);
static void hex_to_word_le(const char *hex, uint32_t *word);
static size_t bin_decode(const uint8_t* xbin, uint8_t* bin, size_t xbin_len);
void gdb_server_init(void)
{
PT_INIT(&self.pt_server);
PT_INIT(&self.pt_cmd);
PT_INIT(&self.pt_cmd_sub);
PT_INIT(&self.pt_sub_routine);
gdb_server_target_run(false);
self.gdb_connected = false;
}
PT_THREAD(gdb_server_poll(void))
{
char c;
size_t ret, len;
PT_BEGIN(&self.pt_server);
while (1) {
PT_YIELD(&self.pt_server);
if(self.gdb_connected && self.target_running) {
ret = rvl_serial_getchar(&c);
if(ret > 0) {
self.res[0] = 'O';
len = 0;
do {
bin_to_hex((uint8_t*)&c, &self.res[1 + len * 2], 1);
len++;
ret = rvl_serial_getchar(&c);
} while (ret > 0);
gdb_serial_response_done(len * 2 + 1, GDB_SERIAL_SEND_FLAG_ALL);
PT_WAIT_UNTIL(&self.pt_server, (self.res = gdb_serial_response_buffer()) != NULL);
}
self.cmd = gdb_serial_command_buffer();
if(self.cmd != NULL) {
self.cmd_len = gdb_serial_command_length();
if(*self.cmd == '\x03' && self.cmd_len == 1) {
PT_WAIT_THREAD(&self.pt_server, gdb_server_cmd_ctrl_c());
gdb_server_target_run(false);
strncpy(self.res, "T02", GDB_SERIAL_RESPONSE_BUFFER_SIZE);
gdb_serial_response_done(3, GDB_SERIAL_SEND_FLAG_ALL);
}
gdb_serial_command_done();
}
if(self.target_running) {
PT_WAIT_THREAD(&self.pt_server, rvl_target_halt_check(&self.halt_info));
if(self.halt_info.reason != rvl_target_halt_reason_running) {
gdb_server_target_run(false);
if(self.halt_info.reason == rvl_target_halt_reason_other) {
strncpy(self.res, "T05", GDB_SERIAL_RESPONSE_BUFFER_SIZE);
} else if(self.halt_info.reason == rvl_target_halt_reason_write_watchpoint) {
snprintf(self.res, GDB_SERIAL_RESPONSE_BUFFER_SIZE, "T05watch:%x;", (unsigned int)self.halt_info.addr);
} else if(self.halt_info.reason == rvl_target_halt_reason_read_watchpoint) {
snprintf(self.res, GDB_SERIAL_RESPONSE_BUFFER_SIZE, "T05rwatch:%x;", (unsigned int)self.halt_info.addr);
} else if(self.halt_info.reason == rvl_target_halt_reason_access_watchpoint) {
snprintf(self.res, GDB_SERIAL_RESPONSE_BUFFER_SIZE, "T05awatch:%x;", (unsigned int)self.halt_info.addr);
} else if(self.halt_info.reason == rvl_target_halt_reason_hardware_breakpoint) {
strncpy(self.res, "T05hwbreak:;", GDB_SERIAL_RESPONSE_BUFFER_SIZE);
} else if(self.halt_info.reason == rvl_target_halt_reason_software_breakpoint) {
strncpy(self.res, "T05swbreak:;", GDB_SERIAL_RESPONSE_BUFFER_SIZE);
} else {
}
gdb_serial_response_done(strlen(self.res), GDB_SERIAL_SEND_FLAG_ALL);
}
}
} else {
PT_WAIT_UNTIL(&self.pt_server, (self.cmd = gdb_serial_command_buffer()) != NULL);
self.cmd_len = gdb_serial_command_length();
PT_WAIT_UNTIL(&self.pt_server, (self.res = gdb_serial_response_buffer()) != NULL);
c = *self.cmd;
if(c == 'q') {
PT_WAIT_THREAD(&self.pt_server, gdb_server_cmd_q());
} else if(c == 'Q') {
PT_WAIT_THREAD(&self.pt_server, gdb_server_cmd_Q());
} else if(c == 'H') {
PT_WAIT_THREAD(&self.pt_server, gdb_server_cmd_H());
} else if(c == '?') {
PT_WAIT_THREAD(&self.pt_server, gdb_server_cmd_question_mark());
} else if(c == 'g') {
PT_WAIT_THREAD(&self.pt_server, gdb_server_cmd_g());
} else if(c == 'G') {
PT_WAIT_THREAD(&self.pt_server, gdb_server_cmd_G());
} else if(c == 'k') {
PT_WAIT_THREAD(&self.pt_server, gdb_server_cmd_k());
} else if(c == 'c') {
PT_WAIT_THREAD(&self.pt_server, gdb_server_cmd_c());
} else if(c == 'm') {
PT_WAIT_THREAD(&self.pt_server, gdb_server_cmd_m());
} else if(c == 'M') {
PT_WAIT_THREAD(&self.pt_server, gdb_server_cmd_M());
} else if(c == 'X') {
PT_WAIT_THREAD(&self.pt_server, gdb_server_cmd_X());
} else if(c == 'p') {
PT_WAIT_THREAD(&self.pt_server, gdb_server_cmd_p());
} else if(c == 'P') {
PT_WAIT_THREAD(&self.pt_server, gdb_server_cmd_P());
} else if(c == 's') {
PT_WAIT_THREAD(&self.pt_server, gdb_server_cmd_s());
} else if(c == 'z') {
PT_WAIT_THREAD(&self.pt_server, gdb_server_cmd_z());
} else if(c == 'Z') {
PT_WAIT_THREAD(&self.pt_server, gdb_server_cmd_Z());
} else if(c == 'v') {
PT_WAIT_THREAD(&self.pt_server, gdb_server_cmd_v());
} else {
gdb_server_reply_empty();
}
gdb_serial_command_done();
}
}
PT_END(&self.pt_server);
}
/*
* q name params...
* General query (q) and set (Q).
*/
PT_THREAD(gdb_server_cmd_q(void))
{
PT_BEGIN(&self.pt_cmd);
if(strncmp(self.cmd, "qSupported:", 11) == 0){
PT_WAIT_THREAD(&self.pt_cmd, gdb_server_cmd_qSupported());
} else if(strncmp(self.cmd, "qXfer:features:read:target.xml:", 31) == 0){
gdb_server_cmd_qxfer_features_read_target_xml();
} else if(strncmp(self.cmd, "qXfer:memory-map:read::", 23) == 0){
gdb_server_cmd_qxfer_memory_map_read();
} else if(strncmp(self.cmd, "qRcmd,", 6) == 0){
PT_WAIT_THREAD(&self.pt_cmd, gdb_server_cmd_qRcmd());
} else {
gdb_server_reply_empty();
}
PT_END(&self.pt_cmd);
}
/*
* qSupported [:gdbfeature [;gdbfeature]... ]
* Tell the remote stub about features supported by gdb, and query the stub for
* features it supports.
*/
PT_THREAD(gdb_server_cmd_qSupported(void))
{
const char qSupported_res[] =
"PacketSize=405"
";QStartNoAckMode+"
";qXfer:features:read+"
";qXfer:memory-map:read+"
";swbreak+"
";hwbreak+"
;
PT_BEGIN(&self.pt_cmd_sub);
gdb_serial_no_ack_mode(false);
strncpy(self.res, qSupported_res, GDB_SERIAL_RESPONSE_BUFFER_SIZE);
gdb_serial_response_done(strlen(qSupported_res), GDB_SERIAL_SEND_FLAG_ALL);
PT_WAIT_THREAD(&self.pt_cmd_sub, gdb_server_connected());
PT_END(&self.pt_cmd_sub);
}
/*
* qXfer:features:read:target.xml
*/
void gdb_server_cmd_qxfer_features_read_target_xml(void)
{
size_t target_xml_len;
const char *target_xml_str;
unsigned int read_addr;
unsigned int read_len;
sscanf(&self.cmd[31], "%x,%x", &read_addr, &read_len);
if(read_len > GDB_SERIAL_RESPONSE_BUFFER_SIZE) {
read_len = GDB_SERIAL_RESPONSE_BUFFER_SIZE;
}
target_xml_len = rvl_target_get_target_xml_len();
if(read_len >= target_xml_len - read_addr) {
read_len = target_xml_len - read_addr;
self.res[0] = 'l';
} else {
self.res[0] = 'm';
}
target_xml_str = rvl_target_get_target_xml();
strncpy(&self.res[1], &target_xml_str[read_addr], read_len);
gdb_serial_response_done(read_len + 1, GDB_SERIAL_SEND_FLAG_ALL);
}
/*
* qXfer:memory-map:read::
*/
void gdb_server_cmd_qxfer_memory_map_read(void)
{
size_t memory_map_len;
const rvl_target_memory_t* memory_map;
size_t res_len;
/*
*
*/
memory_map_len = rvl_target_get_memory_map_len();
memory_map = rvl_target_get_memory_map();
res_len = 0;
res_len += snprintf(&self.res[0], GDB_SERIAL_RESPONSE_BUFFER_SIZE, "l<memory-map>");
for(self.i = 0; self.i < memory_map_len; self.i++) {
#if RVL_TARGET_CONFIG_ADDR_WIDTH == 32
res_len += snprintf(&self.res[res_len], GDB_SERIAL_RESPONSE_BUFFER_SIZE - res_len,
"<memory type=\"%s\" start=\"0x%x\" length=\"0x%x\"",
memory_map[self.i].type == rvl_target_memory_type_flash ? "flash" : "ram",
(unsigned int)memory_map[self.i].start, (unsigned int)memory_map[self.i].length);
if(memory_map[self.i].type == rvl_target_memory_type_flash) {
res_len += snprintf(&self.res[res_len], GDB_SERIAL_RESPONSE_BUFFER_SIZE - res_len,
"><property name=\"blocksize\">0x%x</property></memory>",
(unsigned int)memory_map[self.i].blocksize);
} else {
res_len += snprintf(&self.res[res_len], GDB_SERIAL_RESPONSE_BUFFER_SIZE - res_len,
"/>");
}
#else
#error FIXME
#endif
}
res_len += snprintf(&self.res[res_len], GDB_SERIAL_RESPONSE_BUFFER_SIZE - res_len,
"</memory-map>");
gdb_serial_response_done(res_len, GDB_SERIAL_SEND_FLAG_ALL);
}
/*
* qRcmd,command
* command (hex encoded) is passed to the local interpreter for execution.
*/
PT_THREAD(gdb_server_cmd_qRcmd(void))
{
char c;
size_t ret, len;
const char * err_str;
uint32_t err_pc;
const char unspported_monitor_command[] = ":( unsupported monitor command!\n";
PT_BEGIN(&self.pt_cmd_sub);
ret = rvl_serial_getchar(&c);
if(ret > 0) {
self.res[0] = 'O';
len = 0;
do {
bin_to_hex((uint8_t*)&c, &self.res[1 + len * 2], 1);
len++;
ret = rvl_serial_getchar(&c);
} while (ret > 0);
gdb_serial_response_done(len * 2 + 1, GDB_SERIAL_SEND_FLAG_ALL);
PT_WAIT_UNTIL(&self.pt_cmd_sub, (self.res = gdb_serial_response_buffer()) != NULL);
}
self.mem_len = (self.cmd_len - 6) / 2;
hex_to_bin(&self.cmd[6], self.mem_buffer, self.mem_len);
self.mem_buffer[self.mem_len] = 0;
if(strncmp((char*)self.mem_buffer, "reset", 5) == 0) {
PT_WAIT_THREAD(&self.pt_cmd_sub, rvl_target_reset());
gdb_server_reply_ok();
} else if(strncmp((char*)self.mem_buffer, "halt", 4) == 0) {
gdb_server_reply_ok();
} else if(strncmp((char*)self.mem_buffer, "show error", 10) == 0) {
rvl_target_get_error(&err_str, &err_pc);
snprintf((char*)self.mem_buffer, GDB_SERIAL_RESPONSE_BUFFER_SIZE,
"RV-LINK ERROR: %s, @0x%08x\r\n", err_str, (unsigned int)err_pc);
len = strlen((char*)self.mem_buffer);
self.res[0] = 'O';
bin_to_hex(self.mem_buffer, &self.res[1], len);
gdb_serial_response_done(len * 2 + 1, GDB_SERIAL_SEND_FLAG_ALL);
PT_WAIT_UNTIL(&self.pt_cmd_sub, (self.res = gdb_serial_response_buffer()) != NULL);
gdb_server_reply_ok();
} else {
bin_to_hex((uint8_t*)unspported_monitor_command, self.res, sizeof(unspported_monitor_command) - 1);
gdb_serial_response_done((sizeof(unspported_monitor_command) - 1) * 2, GDB_SERIAL_SEND_FLAG_ALL);
}
PT_END(&self.pt_cmd_sub);
}
/*
* Q name params...
* General query (q) and set (Q).
*/
PT_THREAD(gdb_server_cmd_Q(void))
{
PT_BEGIN(&self.pt_cmd);
if(strncmp(self.cmd, "QStartNoAckMode", 15) == 0){
gdb_server_reply_ok();
gdb_serial_no_ack_mode(true);
} else {
gdb_server_reply_empty();
}
PT_END(&self.pt_cmd);
}
/*
* H op thread-id
* Set thread for subsequent operations (m, M, g, G, et.al.).
*/
PT_THREAD(gdb_server_cmd_H(void))
{
unsigned int n;
char cmd;
gdb_server_tid_t tid;
PT_BEGIN(&self.pt_cmd);
cmd = self.cmd[1];
if(cmd == 'g' || cmd == 'G' || cmd == 'm' || cmd == 'M' || cmd == 'c'){
sscanf(&self.cmd[2], "%x", &n);
gdb_server_reply_ok();
tid = (gdb_server_tid_t)n;
if(cmd == 'g') {
self.tid_g = tid;
} else if(cmd == 'G') {
self.tid_G = tid;
} else if(cmd == 'm') {
self.tid_m = tid;
} else if(cmd == 'M'){
self.tid_M = tid;
} else {
self.tid_c = tid;
}
} else {
gdb_server_reply_empty();
}
PT_END(&self.pt_cmd);
}
/*
* ?
* Indicate the reason the target halted. The reply is the same as for step and continue.
*/
PT_THREAD(gdb_server_cmd_question_mark(void))
{
PT_BEGIN(&self.pt_cmd);
strncpy(self.res, "S02", GDB_SERIAL_RESPONSE_BUFFER_SIZE);
gdb_serial_response_done(3, GDB_SERIAL_SEND_FLAG_ALL);
PT_END(&self.pt_cmd);
}
/*
* g
* Read general registers.
*/
PT_THREAD(gdb_server_cmd_g(void))
{
int i;
PT_BEGIN(&self.pt_cmd);
PT_WAIT_THREAD(&self.pt_cmd, rvl_target_read_core_registers(&self.regs[0]));
for(i = 0; i < RVL_TARGET_CONFIG_REG_NUM; i++) {
word_to_hex_le(self.regs[i], &self.res[i * (RVL_TARGET_CONFIG_REG_WIDTH / 8 * 2)]);
}
gdb_serial_response_done(RVL_TARGET_CONFIG_REG_WIDTH / 8 * 2 * RVL_TARGET_CONFIG_REG_NUM, GDB_SERIAL_SEND_FLAG_ALL);
PT_END(&self.pt_cmd);
}
/*
* G XX...
* Write general registers.
*/
PT_THREAD(gdb_server_cmd_G(void))
{
int i;
PT_BEGIN(&self.pt_cmd);
for(i = 0; i < RVL_TARGET_CONFIG_REG_NUM; i++) {
hex_to_word_le(&self.cmd[i * (RVL_TARGET_CONFIG_REG_WIDTH / 8 * 2) + 1], &self.regs[i]);
}
PT_WAIT_THREAD(&self.pt_cmd, rvl_target_write_core_registers(&self.regs[0]));
gdb_server_reply_ok();
PT_END(&self.pt_cmd);
}
/*
* p n
* Read the value of register n; n is in hex.
*/
PT_THREAD(gdb_server_cmd_p(void))
{
PT_BEGIN(&self.pt_cmd);
sscanf(&self.cmd[1], "%x", &self.reg_tmp_num);
PT_WAIT_THREAD(&self.pt_cmd, rvl_target_read_register(&self.reg_tmp, self.reg_tmp_num));
word_to_hex_le(self.reg_tmp, &self.res[0]);
gdb_serial_response_done(RVL_TARGET_CONFIG_REG_WIDTH / 8 * 2, GDB_SERIAL_SEND_FLAG_ALL);
PT_END(&self.pt_cmd);
}
/*
* P n...=r...
* Write register n... with value r... The register number n is in hexadecimal,
* and r... contains two hex digits for each byte in the register (target byte order).
*/
PT_THREAD(gdb_server_cmd_P(void))
{
const char *p;
PT_BEGIN(&self.pt_cmd);
sscanf(&self.cmd[1], "%x", &self.reg_tmp_num);
p = strchr(&self.cmd[1], '=');
p++;
hex_to_word_le(p, &self.reg_tmp);
PT_WAIT_THREAD(&self.pt_cmd, rvl_target_write_register(self.reg_tmp, self.reg_tmp_num));
gdb_server_reply_ok();
PT_END(&self.pt_cmd);
}
/*
* m addr,length
* Read length addressable memory units starting at address addr.
* Note that addr may not be aligned to any particular boundary.
*/
PT_THREAD(gdb_server_cmd_m(void))
{
char *p;
PT_BEGIN(&self.pt_cmd);
p = strchr(&self.cmd[1], ',');
p++;
#if RVL_TARGET_CONFIG_ADDR_WIDTH == 32
sscanf(&self.cmd[1], "%x", (unsigned int*)(&self.mem_addr));
#else
#error
#endif
sscanf(p, "%x", (unsigned int*)(&self.mem_len));
if(self.mem_len > sizeof(self.mem_buffer)) {
self.mem_len = sizeof(self.mem_buffer);
}
PT_WAIT_THREAD(&self.pt_cmd, rvl_target_read_memory(self.mem_buffer, self.mem_addr, self.mem_len));
bin_to_hex(self.mem_buffer, self.res, self.mem_len);
gdb_serial_response_done(self.mem_len * 2, GDB_SERIAL_SEND_FLAG_ALL);
PT_END(&self.pt_cmd);
}
/*
* M addr,length:XX...
* Write length addressable memory units starting at address addr.
*/
PT_THREAD(gdb_server_cmd_M(void))
{
const char *p;
PT_BEGIN(&self.pt_cmd);
sscanf(&self.cmd[1], "%x,%x", (unsigned int*)(&self.mem_addr), (unsigned int*)(&self.mem_len));
p = strchr(&self.cmd[1], ':');
p++;
if(self.mem_len > sizeof(self.mem_buffer)) {
self.mem_len = sizeof(self.mem_buffer);
}
hex_to_bin(p, self.mem_buffer, self.mem_len);
PT_WAIT_THREAD(&self.pt_cmd, rvl_target_write_memory(self.mem_buffer, self.mem_addr, self.mem_len));
gdb_server_reply_ok();
PT_END(&self.pt_cmd);
}
/*
* X addr,length:XX...
* Write data to memory, where the data is transmitted in binary.
*/
PT_THREAD(gdb_server_cmd_X(void))
{
const char *p;
size_t length;
PT_BEGIN(&self.pt_cmd);
sscanf(&self.cmd[1], "%x,%x", (unsigned int*)(&self.mem_addr), (unsigned int*)(&self.mem_len));
if(self.mem_len == 0) {
gdb_server_reply_ok();
PT_EXIT(&self.pt_cmd);
}
p = strchr(&self.cmd[1], ':');
p++;
if(self.mem_len > sizeof(self.mem_buffer)) {
self.mem_len = sizeof(self.mem_buffer);
}
length = self.cmd_len - ((size_t)p - (size_t)self.cmd);
bin_decode((uint8_t*)p, self.mem_buffer, length);
PT_WAIT_THREAD(&self.pt_cmd, rvl_target_write_memory(self.mem_buffer, self.mem_addr, self.mem_len));
gdb_server_reply_ok();
PT_END(&self.pt_cmd);
}
/*
* k
* Kill request.
*/
PT_THREAD(gdb_server_cmd_k(void))
{
PT_BEGIN(&self.pt_cmd);
gdb_server_reply_ok();
PT_WAIT_THREAD(&self.pt_cmd, gdb_server_disconnected());
PT_END(&self.pt_cmd);
}
/*
* c [addr]
* Continue at addr, which is the address to resume. If addr is omitted, resume
* at current address.
*/
PT_THREAD(gdb_server_cmd_c(void))
{
PT_BEGIN(&self.pt_cmd);
PT_WAIT_THREAD(&self.pt_cmd, rvl_target_resume());
gdb_server_target_run(true);
PT_END(&self.pt_cmd);
}
/*
* s [addr]
* Single step, resuming at addr. If addr is omitted, resume at same address.
*/
PT_THREAD(gdb_server_cmd_s(void))
{
PT_BEGIN(&self.pt_cmd);
PT_WAIT_THREAD(&self.pt_cmd, rvl_target_step());
gdb_server_target_run(true);
PT_END(&self.pt_cmd);
}
/*
* z type,addr,kind
* Insert (Z) or remove (z) a type breakpoint or watchpoint starting at address
* address of kind kind.
*/
PT_THREAD(gdb_server_cmd_z(void))
{
int type, addr, kind;
PT_BEGIN(&self.pt_cmd);
sscanf(self.cmd, "z%x,%x,%x", &type, &addr, &kind);
self.breakpoint_type = type;
self.breakpoint_addr = addr;
self.breakpoint_kind = kind;
PT_WAIT_THREAD(&self.pt_cmd, rvl_target_remove_breakpoint(
self.breakpoint_type, self.breakpoint_addr, self.breakpoint_kind, &self.breakpoint_err));
if(self.breakpoint_err == 0) {
gdb_server_reply_ok();
} else {
gdb_server_reply_err(self.breakpoint_err);
}
PT_END(&self.pt_cmd);
}
/*
* Z type,addr,kind
* Insert (Z) or remove (z) a type breakpoint or watchpoint starting at address
* address of kind kind.
*/
PT_THREAD(gdb_server_cmd_Z(void))
{
int type, addr, kind;
PT_BEGIN(&self.pt_cmd);
sscanf(self.cmd, "Z%x,%x,%x", &type, &addr, &kind);
self.breakpoint_type = type;
self.breakpoint_addr = addr;
self.breakpoint_kind = kind;
PT_WAIT_THREAD(&self.pt_cmd, rvl_target_insert_breakpoint(
self.breakpoint_type, self.breakpoint_addr, self.breakpoint_kind, &self.breakpoint_err));
if(self.breakpoint_err == 0) {
gdb_server_reply_ok();
} else {
gdb_server_reply_err(self.breakpoint_err);
}
PT_END(&self.pt_cmd);
}
/*
* Ctrl+C
*/
PT_THREAD(gdb_server_cmd_ctrl_c(void))
{
PT_BEGIN(&self.pt_cmd);
PT_WAIT_THREAD(&self.pt_cmd, rvl_target_halt());
PT_END(&self.pt_cmd);
}
/*
* v
* Packets starting with v are identified by a multi-letter name.
*/
PT_THREAD(gdb_server_cmd_v(void))
{
PT_BEGIN(&self.pt_cmd);
if(strncmp(self.cmd, "vFlashErase:", 12) == 0) {
PT_WAIT_THREAD(&self.pt_cmd, gdb_server_cmd_vFlashErase());
} else if(strncmp(self.cmd, "vFlashWrite:", 12) == 0) {
PT_WAIT_THREAD(&self.pt_cmd, gdb_server_cmd_vFlashWrite());
} else if(strncmp(self.cmd, "vFlashDone", 10) == 0) {
PT_WAIT_THREAD(&self.pt_cmd, gdb_server_cmd_vFlashDone());
} else if(strncmp(self.cmd, "vMustReplyEmpty", 15) == 0) {
PT_WAIT_THREAD(&self.pt_cmd, gdb_server_cmd_vMustReplyEmpty());
} else {
gdb_server_reply_empty();
}
PT_END(&self.pt_cmd);
}
/*
* vFlashErase:addr,length
* Direct the stub to erase length bytes of flash starting at addr.
*/
PT_THREAD(gdb_server_cmd_vFlashErase(void))
{
int addr, length;
PT_BEGIN(&self.pt_cmd_sub);
sscanf(&self.cmd[12], "%x,%x", &addr, &length);
self.mem_addr = addr;
self.mem_len = length;
PT_WAIT_THREAD(&self.pt_cmd_sub, rvl_target_flash_erase(self.mem_addr, self.mem_len, &self.flash_err));
if(self.flash_err == 0) {
gdb_server_reply_ok();
} else {
gdb_server_reply_err(self.flash_err);
PT_WAIT_THREAD(&self.pt_cmd_sub, gdb_server_disconnected());
}
PT_END(&self.pt_cmd_sub);
}
/*
* vFlashWrite:addr:XX...
* Direct the stub to write data to flash address addr.
* The data is passed in binary form using the same encoding as for the X packet.
*/
PT_THREAD(gdb_server_cmd_vFlashWrite(void))
{
int addr;
const char *p;
size_t length;
PT_BEGIN(&self.pt_cmd_sub);
sscanf(&self.cmd[12], "%x", &addr);
self.mem_addr = addr;
p = strchr(&self.cmd[12], ':');
p++;
length = self.cmd_len - ((size_t)p - (size_t)self.cmd);
self.mem_len = bin_decode((uint8_t*)p, self.mem_buffer, length);
PT_WAIT_THREAD(&self.pt_cmd_sub, rvl_target_flash_write(self.mem_addr, self.mem_len, self.mem_buffer, &self.flash_err));
if(self.flash_err == 0) {
gdb_server_reply_ok();
} else {
gdb_server_reply_err(self.flash_err);
PT_WAIT_THREAD(&self.pt_cmd_sub, gdb_server_disconnected());
}
PT_END(&self.pt_cmd_sub);
}
/*
* vFlashDone
* Indicate to the stub that flash programming operation is finished.
*/
PT_THREAD(gdb_server_cmd_vFlashDone(void))
{
PT_BEGIN(&self.pt_cmd_sub);
PT_WAIT_THREAD(&self.pt_cmd_sub, rvl_target_flash_done());
gdb_server_reply_ok();
PT_END(&self.pt_cmd_sub);
}
/*
* vMustReplyEmpty
* RV-LINK vMustReplyEmpty
*/
PT_THREAD(gdb_server_cmd_vMustReplyEmpty(void))
{
size_t len;
char c;
PT_BEGIN(&self.pt_cmd_sub);
if(self.target_error == rvl_target_error_none) {
gdb_server_reply_empty();
} else {
len = 0;
while(rvl_serial_getchar(&c)) {
self.res[len] = c;
len++;
}
gdb_serial_response_done(len, GDB_SERIAL_SEND_FLAG_ALL);
PT_WAIT_THREAD(&self.pt_cmd_sub, gdb_server_disconnected());
}
PT_END(&self.pt_cmd_sub);
}
static void gdb_server_reply_ok(void)
{
strncpy(self.res, "OK", GDB_SERIAL_RESPONSE_BUFFER_SIZE);
gdb_serial_response_done(2, GDB_SERIAL_SEND_FLAG_ALL);
}
static void gdb_server_reply_empty(void)
{
gdb_serial_response_done(0, GDB_SERIAL_SEND_FLAG_ALL);
}
static void gdb_server_reply_err(int err)
{
snprintf(self.res, GDB_SERIAL_RESPONSE_BUFFER_SIZE, "E%02x", err);
gdb_serial_response_done(strlen(self.res), GDB_SERIAL_SEND_FLAG_ALL);
}
PT_THREAD(gdb_server_connected(void))
{
char c;
PT_BEGIN(&self.pt_sub_routine);
while(rvl_serial_getchar(&c)) {};
rvl_led_gdb_connect(1);
self.gdb_connected = true;
gdb_server_target_run(false);
rvl_target_init();
PT_WAIT_THREAD(&self.pt_sub_routine, rvl_target_init_post(&self.target_error));
if(self.target_error == rvl_target_error_none) {
PT_WAIT_THREAD(&self.pt_sub_routine, rvl_target_halt());
PT_WAIT_THREAD(&self.pt_sub_routine, rvl_target_init_after_halted(&self.target_error));
}
if(self.target_error != rvl_target_error_none) {
switch(self.target_error) {
case rvl_target_error_line:
rvl_serial_puts("\nRV-LINK ERROR: the target is not connected!\n");
break;
case rvl_target_error_compat:
rvl_serial_puts("\nRV-LINK ERROR: the target is not supported, upgrade RV-LINK firmware!\n");
break;
case rvl_target_error_debug_module:
rvl_serial_puts("\nRV-LINK ERROR: something wrong with debug module!\n");
break;
case rvl_target_error_protected:
rvl_serial_puts("\nRV-LINK ERROR: the target under protected! disable protection then try again.\n");
break;
default:
rvl_serial_puts("\nRV-LINK ERROR: unknown error!\n");
break;
}
}
rvl_serial_puts("RV-LINK " RVL_APP_CONFIG_GDB_SERVER_VERSION ": ");
rvl_serial_puts(RVL_LINK_CONFIG_NAME);
rvl_serial_puts(", configed for ");
rvl_serial_puts(rvl_target_get_name());
rvl_serial_puts(" family.\n");
PT_END(&self.pt_sub_routine);
}
PT_THREAD(gdb_server_disconnected(void))
{
PT_BEGIN(&self.pt_sub_routine);
if(self.target_running == false) {
if(self.target_error != rvl_target_error_line) {
PT_WAIT_THREAD(&self.pt_sub_routine, rvl_target_resume());
gdb_server_target_run(true);
}
}
if(self.target_error != rvl_target_error_line) {
PT_WAIT_THREAD(&self.pt_sub_routine, rvl_target_fini_pre());
}
rvl_target_fini();
self.gdb_connected = false;
rvl_led_gdb_connect(0);
PT_END(&self.pt_sub_routine);
}
static void gdb_server_target_run(bool run)
{
self.target_running = run;
rvl_led_target_run(run);
}
static void bin_to_hex(const uint8_t *bin, char *hex, size_t nbyte)
{
size_t i;
uint8_t hi;
uint8_t lo;
for(i = 0; i < nbyte; i++) {
hi = (*bin >> 4) & 0xf;
lo = *bin & 0xf;
if(hi < 10) {
*hex = '0' + hi;
} else {
*hex = 'a' + hi - 10;
}
hex++;
if(lo < 10) {
*hex = '0' + lo;
} else {
*hex = 'a' + lo - 10;
}
hex++;
bin++;
}
}
static void word_to_hex_le(uint32_t word, char *hex)
{
uint8_t bytes[4];
bytes[0] = word & 0xff;
bytes[1] = (word >> 8) & 0xff;
bytes[2] = (word >> 16) & 0xff;
bytes[3] = (word >> 24) & 0xff;
bin_to_hex(bytes, hex, 4);
}
static void hex_to_bin(const char *hex, uint8_t *bin, size_t nbyte)
{
size_t i;
uint8_t hi, lo;
for(i = 0; i < nbyte; i++) {
if(hex[i * 2] <= '9') {
hi = hex[i * 2] - '0';
} else if(hex[i * 2] <= 'F') {
hi = hex[i * 2] - 'A' + 10;
} else {
hi = hex[i * 2] - 'a' + 10;
}
if(hex[i * 2 + 1] <= '9') {
lo = hex[i * 2 + 1] - '0';
} else if(hex[i * 2 + 1] <= 'F') {
lo = hex[i * 2 + 1] - 'A' + 10;
} else {
lo = hex[i * 2 + 1] - 'a' + 10;
}
bin[i] = (hi << 4) | lo;
}
}
static void hex_to_word_le(const char *hex, uint32_t *word)
{
uint8_t bytes[4];
hex_to_bin(hex, bytes, 4);
*word = bytes[0] | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24);
}
static size_t bin_decode(const uint8_t* xbin, uint8_t* bin, size_t xbin_len)
{
size_t bin_len = 0;
size_t i;
int escape_found = 0;
for(i = 0; i < xbin_len; i++){
if(xbin[i] == 0x7d) {
escape_found = 1;
} else {
if(escape_found) {
bin[bin_len] = xbin[i] ^ 0x20;
escape_found = 0;
} else {
bin[bin_len] = xbin[i];
}
bin_len++;
}
}
return bin_len;
}