2011-02-04 07:23:52 +00:00
|
|
|
/*
|
|
|
|
* This file is part of the Black Magic Debug project.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2011 Black Sphere Technologies Ltd.
|
|
|
|
* Written by Gareth McMullin <gareth@blacksphere.co.nz>
|
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* This file implements the GDB Remote Serial Debugging protocol as
|
2012-11-03 06:52:09 +00:00
|
|
|
* described in "Debugging with GDB" build from GDB source.
|
2011-02-04 07:23:52 +00:00
|
|
|
*
|
|
|
|
* Originally written for GDB 6.8, updated and tested with GDB 7.2.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "general.h"
|
|
|
|
#include "hex_utils.h"
|
|
|
|
#include "gdb_if.h"
|
|
|
|
#include "gdb_packet.h"
|
|
|
|
#include "gdb_main.h"
|
|
|
|
#include "jtagtap.h"
|
|
|
|
#include "jtag_scan.h"
|
|
|
|
#include "adiv5.h"
|
|
|
|
#include "target.h"
|
|
|
|
#include "command.h"
|
2011-12-17 08:46:22 +00:00
|
|
|
#include "crc32.h"
|
2011-02-04 07:23:52 +00:00
|
|
|
|
|
|
|
#define BUF_SIZE 1024
|
|
|
|
|
|
|
|
#define ERROR_IF_NO_TARGET() \
|
|
|
|
if(!cur_target) { gdb_putpacketz("EFF"); break; }
|
|
|
|
|
2015-03-14 03:35:39 +00:00
|
|
|
static char pbuf[BUF_SIZE];
|
2011-02-04 07:23:52 +00:00
|
|
|
|
2012-06-27 09:26:08 +00:00
|
|
|
static target *cur_target;
|
|
|
|
static target *last_target;
|
|
|
|
|
2011-02-04 07:23:52 +00:00
|
|
|
static void handle_q_packet(char *packet, int len);
|
|
|
|
static void handle_v_packet(char *packet, int len);
|
2012-11-03 07:38:27 +00:00
|
|
|
static void handle_z_packet(char *packet, int len);
|
2011-02-04 07:23:52 +00:00
|
|
|
|
2012-06-27 09:26:08 +00:00
|
|
|
static void gdb_target_destroy_callback(target *t)
|
|
|
|
{
|
|
|
|
if (cur_target == t)
|
|
|
|
cur_target = NULL;
|
|
|
|
|
|
|
|
if (last_target == t)
|
|
|
|
last_target = NULL;
|
|
|
|
}
|
|
|
|
|
2011-02-04 07:23:52 +00:00
|
|
|
void
|
|
|
|
gdb_main(void)
|
|
|
|
{
|
|
|
|
int size;
|
2012-11-03 06:52:09 +00:00
|
|
|
bool single_step = false;
|
2013-04-14 17:58:49 +00:00
|
|
|
char last_activity = 0;
|
2011-02-04 07:23:52 +00:00
|
|
|
|
|
|
|
DEBUG("Entring GDB protocol main loop\n");
|
|
|
|
/* GDB protocol main loop */
|
|
|
|
while(1) {
|
|
|
|
SET_IDLE_STATE(1);
|
|
|
|
size = gdb_getpacket(pbuf, BUF_SIZE);
|
|
|
|
SET_IDLE_STATE(0);
|
2013-04-14 17:58:49 +00:00
|
|
|
continue_activity:
|
2011-02-04 07:23:52 +00:00
|
|
|
switch(pbuf[0]) {
|
2012-11-03 06:59:01 +00:00
|
|
|
/* Implementation of these is mandatory! */
|
|
|
|
case 'g': { /* 'g': Read general registers */
|
2011-02-04 07:23:52 +00:00
|
|
|
ERROR_IF_NO_TARGET();
|
2013-06-17 03:46:26 +00:00
|
|
|
uint8_t arm_regs[cur_target->regs_size];
|
|
|
|
target_regs_read(cur_target, arm_regs);
|
2011-11-26 01:07:46 +00:00
|
|
|
gdb_putpacket(hexify(pbuf, (void*)arm_regs, cur_target->regs_size), cur_target->regs_size * 2);
|
2011-02-04 07:23:52 +00:00
|
|
|
break;
|
2012-11-03 06:59:01 +00:00
|
|
|
}
|
|
|
|
case 'm': { /* 'm addr,len': Read len bytes from addr */
|
2012-11-03 06:52:09 +00:00
|
|
|
uint32_t addr, len;
|
2011-02-04 07:23:52 +00:00
|
|
|
ERROR_IF_NO_TARGET();
|
2015-03-12 03:31:01 +00:00
|
|
|
sscanf(pbuf, "m%" SCNx32 ",%" SCNx32, &addr, &len);
|
|
|
|
DEBUG("m packet: addr = %" PRIx32 ", len = %" PRIx32 "\n", addr, len);
|
2012-11-03 06:52:09 +00:00
|
|
|
uint8_t mem[len];
|
2015-03-15 21:31:57 +00:00
|
|
|
target_mem_read(cur_target, mem, addr, len);
|
2011-02-04 07:23:52 +00:00
|
|
|
if(target_check_error(cur_target))
|
2012-11-03 07:38:27 +00:00
|
|
|
gdb_putpacketz("E01");
|
2011-02-04 07:23:52 +00:00
|
|
|
else
|
|
|
|
gdb_putpacket(hexify(pbuf, mem, len), len*2);
|
|
|
|
break;
|
2012-11-03 06:59:01 +00:00
|
|
|
}
|
|
|
|
case 'G': { /* 'G XX': Write general registers */
|
2011-02-04 07:23:52 +00:00
|
|
|
ERROR_IF_NO_TARGET();
|
2013-06-17 03:46:26 +00:00
|
|
|
uint8_t arm_regs[cur_target->regs_size];
|
|
|
|
unhexify(arm_regs, &pbuf[1], cur_target->regs_size);
|
2011-02-04 07:23:52 +00:00
|
|
|
target_regs_write(cur_target, arm_regs);
|
2012-11-03 07:38:27 +00:00
|
|
|
gdb_putpacketz("OK");
|
2011-02-04 07:23:52 +00:00
|
|
|
break;
|
2012-11-03 06:59:01 +00:00
|
|
|
}
|
|
|
|
case 'M': { /* 'M addr,len:XX': Write len bytes to addr */
|
2012-11-03 06:52:09 +00:00
|
|
|
uint32_t addr, len;
|
2011-02-04 07:23:52 +00:00
|
|
|
int hex;
|
|
|
|
ERROR_IF_NO_TARGET();
|
2015-03-12 03:31:01 +00:00
|
|
|
sscanf(pbuf, "M%" SCNx32 ",%" SCNx32 ":%n", &addr, &len, &hex);
|
|
|
|
DEBUG("M packet: addr = %" PRIx32 ", len = %" PRIx32 "\n", addr, len);
|
2012-11-03 06:52:09 +00:00
|
|
|
uint8_t mem[len];
|
2011-02-04 07:23:52 +00:00
|
|
|
unhexify(mem, pbuf + hex, len);
|
2015-03-15 21:31:57 +00:00
|
|
|
target_mem_write(cur_target, addr, mem, len);
|
2011-02-04 07:23:52 +00:00
|
|
|
if(target_check_error(cur_target))
|
2012-11-03 07:38:27 +00:00
|
|
|
gdb_putpacketz("E01");
|
2011-02-04 07:23:52 +00:00
|
|
|
else
|
2012-11-03 07:38:27 +00:00
|
|
|
gdb_putpacketz("OK");
|
2011-02-04 07:23:52 +00:00
|
|
|
break;
|
2012-11-03 06:59:01 +00:00
|
|
|
}
|
|
|
|
case 's': /* 's [addr]': Single step [start at addr] */
|
2012-11-03 06:52:09 +00:00
|
|
|
single_step = true;
|
2011-02-04 07:23:52 +00:00
|
|
|
// Fall through to resume target
|
2012-11-03 06:59:01 +00:00
|
|
|
case 'c': /* 'c [addr]': Continue [at addr] */
|
2011-02-04 07:23:52 +00:00
|
|
|
if(!cur_target) {
|
|
|
|
gdb_putpacketz("X1D");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
target_halt_resume(cur_target, single_step);
|
|
|
|
SET_RUN_STATE(1);
|
2012-11-03 06:52:09 +00:00
|
|
|
single_step = false;
|
2011-02-04 07:23:52 +00:00
|
|
|
// Fall through to wait for target halt
|
2012-11-03 06:59:01 +00:00
|
|
|
case '?': { /* '?': Request reason for target halt */
|
2011-02-04 07:23:52 +00:00
|
|
|
/* This packet isn't documented as being mandatory,
|
|
|
|
* but GDB doesn't work without it. */
|
2012-06-30 08:29:08 +00:00
|
|
|
uint32_t watch_addr;
|
|
|
|
int sig;
|
2011-02-04 07:23:52 +00:00
|
|
|
|
|
|
|
if(!cur_target) {
|
|
|
|
/* Report "target exited" if no target */
|
|
|
|
gdb_putpacketz("W00");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2013-04-15 02:47:11 +00:00
|
|
|
last_activity = pbuf[0];
|
2011-02-04 07:23:52 +00:00
|
|
|
/* Wait for target halt */
|
2012-11-03 06:52:09 +00:00
|
|
|
while(!(sig = target_halt_wait(cur_target))) {
|
2011-07-02 08:47:39 +00:00
|
|
|
unsigned char c = gdb_if_getchar_to(0);
|
|
|
|
if((c == '\x03') || (c == '\x04')) {
|
2011-02-04 07:23:52 +00:00
|
|
|
target_halt_request(cur_target);
|
2013-04-14 17:58:49 +00:00
|
|
|
last_activity = 's';
|
2011-02-04 07:23:52 +00:00
|
|
|
}
|
2011-07-02 08:47:39 +00:00
|
|
|
}
|
2011-02-04 07:23:52 +00:00
|
|
|
SET_RUN_STATE(0);
|
2013-04-14 17:58:49 +00:00
|
|
|
|
2013-04-15 02:47:11 +00:00
|
|
|
/* Negative signal indicates we're in a syscall */
|
|
|
|
if (sig < 0)
|
|
|
|
break;
|
2013-04-14 17:58:49 +00:00
|
|
|
|
2015-03-22 21:05:12 +00:00
|
|
|
/* Target disappeared */
|
|
|
|
if (cur_target == NULL) {
|
|
|
|
gdb_putpacket_f("X%02X", sig);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2011-02-04 07:23:52 +00:00
|
|
|
/* Report reason for halt */
|
2011-02-21 07:57:56 +00:00
|
|
|
if(target_check_hw_wp(cur_target, &watch_addr)) {
|
2011-02-04 07:23:52 +00:00
|
|
|
/* Watchpoint hit */
|
2012-06-30 08:29:08 +00:00
|
|
|
gdb_putpacket_f("T%02Xwatch:%08X;", sig, watch_addr);
|
2011-02-21 07:57:56 +00:00
|
|
|
} else {
|
2012-06-30 08:29:08 +00:00
|
|
|
gdb_putpacket_f("T%02X", sig);
|
2011-02-21 07:57:56 +00:00
|
|
|
}
|
2011-02-04 07:23:52 +00:00
|
|
|
break;
|
2012-11-03 06:59:01 +00:00
|
|
|
}
|
2013-04-14 17:58:49 +00:00
|
|
|
case 'F': { /* Semihosting call finished */
|
2013-04-15 02:47:11 +00:00
|
|
|
int retcode, errcode, items;
|
2013-04-14 17:58:49 +00:00
|
|
|
char c, *p;
|
|
|
|
if (pbuf[1] == '-')
|
|
|
|
p = &pbuf[2];
|
|
|
|
else
|
|
|
|
p = &pbuf[1];
|
2013-04-15 02:47:11 +00:00
|
|
|
items = sscanf(p, "%x,%x,%c", &retcode, &errcode, &c);
|
|
|
|
if (pbuf[1] == '-')
|
|
|
|
retcode = -retcode;
|
|
|
|
|
|
|
|
target_hostio_reply(cur_target, retcode, errcode);
|
2013-04-14 17:58:49 +00:00
|
|
|
|
|
|
|
/* if break is requested */
|
|
|
|
if (items == 3 && c == 'C') {
|
|
|
|
gdb_putpacketz("T02");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
pbuf[0] = last_activity;
|
|
|
|
goto continue_activity;
|
|
|
|
}
|
2011-02-04 07:23:52 +00:00
|
|
|
|
2012-11-03 06:59:01 +00:00
|
|
|
/* Optional GDB packet support */
|
|
|
|
case '!': /* Enable Extended GDB Protocol. */
|
2012-11-03 06:52:09 +00:00
|
|
|
/* This doesn't do anything, we support the extended
|
2011-02-04 07:23:52 +00:00
|
|
|
* protocol anyway, but GDB will never send us a 'R'
|
2012-11-03 06:52:09 +00:00
|
|
|
* packet unless we answer 'OK' here.
|
2011-02-04 07:23:52 +00:00
|
|
|
*/
|
2012-11-03 07:38:27 +00:00
|
|
|
gdb_putpacketz("OK");
|
2011-02-04 07:23:52 +00:00
|
|
|
break;
|
|
|
|
|
2012-11-03 06:59:01 +00:00
|
|
|
case 0x04:
|
2014-10-16 20:19:34 +00:00
|
|
|
case 'D': /* GDB 'detach' command. */
|
2012-11-03 06:52:09 +00:00
|
|
|
if(cur_target)
|
2011-11-12 06:15:52 +00:00
|
|
|
target_detach(cur_target);
|
2011-02-04 07:23:52 +00:00
|
|
|
last_target = cur_target;
|
|
|
|
cur_target = NULL;
|
2012-11-03 07:38:27 +00:00
|
|
|
gdb_putpacketz("OK");
|
2011-02-04 07:23:52 +00:00
|
|
|
break;
|
|
|
|
|
2012-11-03 06:59:01 +00:00
|
|
|
case 'k': /* Kill the target */
|
2011-02-04 07:23:52 +00:00
|
|
|
if(cur_target) {
|
|
|
|
target_reset(cur_target);
|
|
|
|
target_detach(cur_target);
|
|
|
|
last_target = cur_target;
|
|
|
|
cur_target = NULL;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2012-11-03 06:59:01 +00:00
|
|
|
case 'r': /* Reset the target system */
|
|
|
|
case 'R': /* Restart the target program */
|
2011-02-04 07:23:52 +00:00
|
|
|
if(cur_target)
|
|
|
|
target_reset(cur_target);
|
|
|
|
else if(last_target) {
|
2012-11-03 06:52:09 +00:00
|
|
|
cur_target = target_attach(last_target,
|
2012-06-27 09:26:08 +00:00
|
|
|
gdb_target_destroy_callback);
|
2011-02-04 07:23:52 +00:00
|
|
|
target_reset(cur_target);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2012-11-03 06:59:01 +00:00
|
|
|
case 'X': { /* 'X addr,len:XX': Write binary data to addr */
|
2012-11-03 06:52:09 +00:00
|
|
|
uint32_t addr, len;
|
2011-02-04 07:23:52 +00:00
|
|
|
int bin;
|
|
|
|
ERROR_IF_NO_TARGET();
|
2015-03-12 03:31:01 +00:00
|
|
|
sscanf(pbuf, "X%" SCNx32 ",%" SCNx32 ":%n", &addr, &len, &bin);
|
|
|
|
DEBUG("X packet: addr = %" PRIx32 ", len = %" PRIx32 "\n", addr, len);
|
2015-03-15 21:31:57 +00:00
|
|
|
target_mem_write(cur_target, addr, pbuf+bin, len);
|
2011-02-04 07:23:52 +00:00
|
|
|
if(target_check_error(cur_target))
|
2012-11-03 07:38:27 +00:00
|
|
|
gdb_putpacketz("E01");
|
2011-02-04 07:23:52 +00:00
|
|
|
else
|
2012-11-03 07:38:27 +00:00
|
|
|
gdb_putpacketz("OK");
|
2011-02-04 07:23:52 +00:00
|
|
|
break;
|
2012-11-03 06:59:01 +00:00
|
|
|
}
|
2011-02-04 07:23:52 +00:00
|
|
|
|
2012-11-03 06:59:01 +00:00
|
|
|
case 'q': /* General query packet */
|
2011-02-04 07:23:52 +00:00
|
|
|
handle_q_packet(pbuf, size);
|
|
|
|
break;
|
|
|
|
|
2012-11-03 06:59:01 +00:00
|
|
|
case 'v': /* General query packet */
|
2011-02-04 07:23:52 +00:00
|
|
|
handle_v_packet(pbuf, size);
|
|
|
|
break;
|
|
|
|
|
2012-11-03 06:59:01 +00:00
|
|
|
/* These packet implement hardware break-/watchpoints */
|
|
|
|
case 'Z': /* Z type,addr,len: Set breakpoint packet */
|
2012-11-03 07:38:27 +00:00
|
|
|
case 'z': /* z type,addr,len: Clear breakpoint packet */
|
2011-02-04 07:23:52 +00:00
|
|
|
ERROR_IF_NO_TARGET();
|
2012-11-03 07:38:27 +00:00
|
|
|
handle_z_packet(pbuf, size);
|
2011-02-04 07:23:52 +00:00
|
|
|
break;
|
|
|
|
|
2012-11-03 06:59:01 +00:00
|
|
|
default: /* Packet not implemented */
|
2011-12-18 04:01:10 +00:00
|
|
|
DEBUG("*** Unsupported packet: %s\n", pbuf);
|
2012-11-03 07:38:27 +00:00
|
|
|
gdb_putpacketz("");
|
2011-02-04 07:23:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-11-26 01:07:46 +00:00
|
|
|
static void
|
|
|
|
handle_q_string_reply(const char *str, const char *param)
|
|
|
|
{
|
|
|
|
unsigned long addr, len;
|
2012-11-03 06:59:01 +00:00
|
|
|
|
2013-04-14 17:56:17 +00:00
|
|
|
if (sscanf(param, "%08lx,%08lx", &addr, &len) != 2) {
|
2011-11-26 01:07:46 +00:00
|
|
|
gdb_putpacketz("E01");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (addr < strlen (str)) {
|
2015-03-14 03:35:39 +00:00
|
|
|
char reply[len+2];
|
2011-11-26 01:07:46 +00:00
|
|
|
reply[0] = 'm';
|
|
|
|
strncpy (reply + 1, &str[addr], len);
|
2012-11-03 06:52:09 +00:00
|
|
|
if(len > strlen(&str[addr]))
|
2011-11-26 01:07:46 +00:00
|
|
|
len = strlen(&str[addr]);
|
|
|
|
gdb_putpacket(reply, len + 1);
|
|
|
|
} else if (addr == strlen (str)) {
|
|
|
|
gdb_putpacketz("l");
|
|
|
|
} else
|
|
|
|
gdb_putpacketz("E01");
|
|
|
|
}
|
2011-02-04 07:23:52 +00:00
|
|
|
|
|
|
|
static void
|
|
|
|
handle_q_packet(char *packet, int len)
|
|
|
|
{
|
2011-12-17 08:46:22 +00:00
|
|
|
uint32_t addr, alen;
|
|
|
|
|
2011-02-04 07:23:52 +00:00
|
|
|
if(!strncmp(packet, "qRcmd,", 6)) {
|
2015-03-14 03:35:39 +00:00
|
|
|
char *data;
|
2011-02-04 07:23:52 +00:00
|
|
|
int datalen;
|
|
|
|
|
|
|
|
/* calculate size and allocate buffer for command */
|
|
|
|
datalen = (len - 6) / 2;
|
|
|
|
data = alloca(datalen+1);
|
|
|
|
/* dehexify command */
|
|
|
|
unhexify(data, packet+6, datalen);
|
|
|
|
data[datalen] = 0; /* add terminating null */
|
|
|
|
|
2012-06-27 09:26:08 +00:00
|
|
|
int c = command_process(cur_target, data);
|
2012-11-03 06:52:09 +00:00
|
|
|
if(c < 0)
|
2012-06-24 09:37:10 +00:00
|
|
|
gdb_putpacketz("");
|
|
|
|
else if(c == 0)
|
|
|
|
gdb_putpacketz("OK");
|
|
|
|
else
|
|
|
|
gdb_putpacketz("E");
|
2011-02-04 07:23:52 +00:00
|
|
|
|
|
|
|
} else if (!strncmp (packet, "qSupported", 10)) {
|
|
|
|
/* Query supported protocol features */
|
2011-11-26 04:04:48 +00:00
|
|
|
gdb_putpacket_f("PacketSize=%X;qXfer:memory-map:read+;qXfer:features:read+", BUF_SIZE);
|
2011-02-04 07:23:52 +00:00
|
|
|
|
|
|
|
} else if (strncmp (packet, "qXfer:memory-map:read::", 23) == 0) {
|
|
|
|
/* Read target XML memory map */
|
|
|
|
if((!cur_target) && last_target) {
|
|
|
|
/* Attach to last target if detached. */
|
2012-11-03 06:52:09 +00:00
|
|
|
cur_target = target_attach(last_target,
|
2012-06-27 09:26:08 +00:00
|
|
|
gdb_target_destroy_callback);
|
2011-02-04 07:23:52 +00:00
|
|
|
}
|
|
|
|
if((!cur_target) || (!cur_target->xml_mem_map)) {
|
|
|
|
gdb_putpacketz("E01");
|
|
|
|
return;
|
|
|
|
}
|
2011-11-26 01:07:46 +00:00
|
|
|
handle_q_string_reply(cur_target->xml_mem_map, packet + 23);
|
|
|
|
|
|
|
|
} else if (strncmp (packet, "qXfer:features:read:target.xml:", 31) == 0) {
|
|
|
|
/* Read target description */
|
|
|
|
if((!cur_target) && last_target) {
|
|
|
|
/* Attach to last target if detached. */
|
2012-11-03 06:52:09 +00:00
|
|
|
cur_target = target_attach(last_target,
|
2012-06-27 09:26:08 +00:00
|
|
|
gdb_target_destroy_callback);
|
2011-11-26 01:07:46 +00:00
|
|
|
}
|
|
|
|
if((!cur_target) || (!cur_target->tdesc)) {
|
2011-02-04 07:23:52 +00:00
|
|
|
gdb_putpacketz("E01");
|
2011-11-26 01:07:46 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
handle_q_string_reply(cur_target->tdesc, packet + 31);
|
2015-03-12 03:31:01 +00:00
|
|
|
} else if (sscanf(packet, "qCRC:%" PRIx32 ",%" PRIx32, &addr, &alen) == 2) {
|
2011-12-17 08:46:22 +00:00
|
|
|
if(!cur_target) {
|
|
|
|
gdb_putpacketz("E01");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
gdb_putpacket_f("C%lx", generic_crc32(cur_target, addr, alen));
|
2011-02-04 07:23:52 +00:00
|
|
|
|
2012-06-27 09:26:08 +00:00
|
|
|
} else {
|
|
|
|
DEBUG("*** Unsupported packet: %s\n", packet);
|
|
|
|
gdb_putpacket("", 0);
|
|
|
|
}
|
2011-02-04 07:23:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
handle_v_packet(char *packet, int plen)
|
|
|
|
{
|
2011-02-06 19:32:33 +00:00
|
|
|
unsigned long addr, len;
|
2011-02-04 07:23:52 +00:00
|
|
|
int bin;
|
|
|
|
static uint8_t flash_mode = 0;
|
|
|
|
|
2013-04-14 17:56:17 +00:00
|
|
|
if (sscanf(packet, "vAttach;%08lx", &addr) == 1) {
|
2011-02-04 07:23:52 +00:00
|
|
|
/* Attach to remote target processor */
|
|
|
|
target *t;
|
|
|
|
uint32_t i;
|
2012-11-03 03:42:46 +00:00
|
|
|
for(t = target_list, i = 1; t; t = t->next, i++)
|
2011-02-04 07:23:52 +00:00
|
|
|
if(i == addr) {
|
2012-11-03 03:42:46 +00:00
|
|
|
cur_target = target_attach(t,
|
2012-06-27 09:26:08 +00:00
|
|
|
gdb_target_destroy_callback);
|
2011-02-04 07:23:52 +00:00
|
|
|
break;
|
|
|
|
}
|
2012-11-03 03:42:46 +00:00
|
|
|
if(cur_target)
|
|
|
|
gdb_putpacketz("T05");
|
|
|
|
else
|
2011-02-04 07:23:52 +00:00
|
|
|
gdb_putpacketz("E01");
|
|
|
|
|
|
|
|
} else if (!strcmp(packet, "vRun;")) {
|
|
|
|
/* Run target program. For us (embedded) this means reset. */
|
|
|
|
if(cur_target) {
|
|
|
|
target_reset(cur_target);
|
|
|
|
gdb_putpacketz("T05");
|
|
|
|
} else if(last_target) {
|
2012-11-03 06:52:09 +00:00
|
|
|
cur_target = target_attach(last_target,
|
2012-06-27 09:26:08 +00:00
|
|
|
gdb_target_destroy_callback);
|
2015-01-16 21:52:19 +00:00
|
|
|
|
|
|
|
/* If we were able to attach to the target again */
|
|
|
|
if (cur_target) {
|
|
|
|
target_reset(cur_target);
|
2015-01-17 18:35:36 +00:00
|
|
|
gdb_putpacketz("T05");
|
|
|
|
} else gdb_putpacketz("E01");
|
2015-01-16 21:52:19 +00:00
|
|
|
|
2011-02-04 07:23:52 +00:00
|
|
|
} else gdb_putpacketz("E01");
|
|
|
|
|
2013-04-14 17:56:17 +00:00
|
|
|
} else if (sscanf(packet, "vFlashErase:%08lx,%08lx", &addr, &len) == 2) {
|
2011-02-04 07:23:52 +00:00
|
|
|
/* Erase Flash Memory */
|
|
|
|
DEBUG("Flash Erase %08lX %08lX\n", addr, len);
|
|
|
|
if(!cur_target) { gdb_putpacketz("EFF"); return; }
|
|
|
|
|
2012-11-03 06:52:09 +00:00
|
|
|
if(!flash_mode) {
|
2011-02-04 07:23:52 +00:00
|
|
|
/* Reset target if first flash command! */
|
|
|
|
/* This saves us if we're interrupted in IRQ context */
|
|
|
|
target_reset(cur_target);
|
|
|
|
flash_mode = 1;
|
|
|
|
}
|
|
|
|
if(target_flash_erase(cur_target, addr, len) == 0)
|
|
|
|
gdb_putpacketz("OK");
|
|
|
|
else
|
|
|
|
gdb_putpacketz("EFF");
|
|
|
|
|
2013-04-14 17:56:17 +00:00
|
|
|
} else if (sscanf(packet, "vFlashWrite:%08lx:%n", &addr, &bin) == 1) {
|
2011-02-04 07:23:52 +00:00
|
|
|
/* Write Flash Memory */
|
|
|
|
len = plen - bin;
|
|
|
|
DEBUG("Flash Write %08lX %08lX\n", addr, len);
|
2012-06-18 08:53:06 +00:00
|
|
|
if(cur_target && target_flash_write(cur_target, addr, (void*)packet + bin, len) == 0)
|
2011-02-04 07:23:52 +00:00
|
|
|
gdb_putpacketz("OK");
|
|
|
|
else
|
|
|
|
gdb_putpacketz("EFF");
|
|
|
|
|
|
|
|
} else if (!strcmp(packet, "vFlashDone")) {
|
|
|
|
/* Commit flash operations. */
|
|
|
|
gdb_putpacketz("OK");
|
|
|
|
flash_mode = 0;
|
|
|
|
|
2012-06-27 09:26:08 +00:00
|
|
|
} else {
|
|
|
|
DEBUG("*** Unsupported packet: %s\n", packet);
|
2011-02-04 07:23:52 +00:00
|
|
|
gdb_putpacket("", 0);
|
2012-06-27 09:26:08 +00:00
|
|
|
}
|
2011-02-04 07:23:52 +00:00
|
|
|
}
|
|
|
|
|
2012-11-03 07:38:27 +00:00
|
|
|
static void
|
|
|
|
handle_z_packet(char *packet, int plen)
|
|
|
|
{
|
|
|
|
(void)plen;
|
|
|
|
|
|
|
|
uint8_t set = (packet[0] == 'Z') ? 1 : 0;
|
|
|
|
int type, len;
|
|
|
|
uint32_t addr;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* I have no idea why this doesn't work. Seems to work
|
|
|
|
* with real sscanf() though... */
|
|
|
|
//sscanf(packet, "%*[zZ]%hhd,%08lX,%hhd", &type, &addr, &len);
|
|
|
|
type = packet[1] - '0';
|
2015-03-12 03:31:01 +00:00
|
|
|
sscanf(packet + 2, ",%" PRIx32 ",%d", &addr, &len);
|
2012-11-03 07:38:27 +00:00
|
|
|
switch(type) {
|
|
|
|
case 1: /* Hardware breakpoint */
|
|
|
|
if(!cur_target->set_hw_bp) { /* Not supported */
|
|
|
|
gdb_putpacketz("");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if(set)
|
|
|
|
ret = target_set_hw_bp(cur_target, addr);
|
|
|
|
else
|
|
|
|
ret = target_clear_hw_bp(cur_target, addr);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
case 3:
|
|
|
|
case 4:
|
|
|
|
if(!cur_target->set_hw_wp) { /* Not supported */
|
|
|
|
gdb_putpacketz("");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if(set)
|
|
|
|
ret = target_set_hw_wp(cur_target, type, addr, len);
|
|
|
|
else
|
|
|
|
ret = target_clear_hw_wp(cur_target, type, addr, len);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
gdb_putpacketz("");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!ret)
|
|
|
|
gdb_putpacketz("OK");
|
|
|
|
else
|
|
|
|
gdb_putpacketz("E01");
|
|
|
|
}
|
|
|
|
|