blackmagic/src/gdb_main.c

461 lines
12 KiB
C
Raw Normal View History

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
* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "platform.h"
#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"
#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; }
static unsigned char pbuf[BUF_SIZE];
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);
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;
bool single_step = false;
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);
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();
uint32_t arm_regs[cur_target->regs_size];
2011-02-04 07:23:52 +00:00
target_regs_read(cur_target, (void*)arm_regs);
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 */
uint32_t addr, len;
2011-02-04 07:23:52 +00:00
ERROR_IF_NO_TARGET();
sscanf(pbuf, "m%08lX,%08lX", &addr, &len);
2011-02-04 07:23:52 +00:00
DEBUG("m packet: addr = %08lX, len = %08lX\n", addr, len);
uint8_t mem[len];
2011-02-04 07:23:52 +00:00
if(((addr & 3) == 0) && ((len & 3) == 0))
target_mem_read_words(cur_target, (void*)mem, addr, len);
else
target_mem_read_bytes(cur_target, (void*)mem, addr, len);
if(target_check_error(cur_target))
gdb_putpacket("E01", 3);
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();
uint32_t arm_regs[cur_target->regs_size];
2011-02-04 07:23:52 +00:00
unhexify((void*)arm_regs, &pbuf[1], cur_target->regs_size);
target_regs_write(cur_target, arm_regs);
gdb_putpacket("OK", 2);
break;
2012-11-03 06:59:01 +00:00
}
case 'M': { /* 'M addr,len:XX': Write len bytes to addr */
uint32_t addr, len;
2011-02-04 07:23:52 +00:00
int hex;
ERROR_IF_NO_TARGET();
sscanf(pbuf, "M%08lX,%08lX:%n", &addr, &len, &hex);
2011-02-04 07:23:52 +00:00
DEBUG("M packet: addr = %08lX, len = %08lX\n", addr, len);
uint8_t mem[len];
2011-02-04 07:23:52 +00:00
unhexify(mem, pbuf + hex, len);
if(((addr & 3) == 0) && ((len & 3) == 0))
2011-02-04 07:23:52 +00:00
target_mem_write_words(cur_target, addr, (void*)mem, len);
else
2011-02-04 07:23:52 +00:00
target_mem_write_bytes(cur_target, addr, (void*)mem, len);
if(target_check_error(cur_target))
gdb_putpacket("E01", 3);
else
gdb_putpacket("OK", 2);
break;
2012-11-03 06:59:01 +00:00
}
case 's': /* 's [addr]': Single step [start at addr] */
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);
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. */
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;
}
/* Wait for target halt */
while(!(sig = target_halt_wait(cur_target))) {
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);
}
}
2011-02-04 07:23:52 +00:00
SET_RUN_STATE(0);
/* 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 */
gdb_putpacket_f("T%02Xwatch:%08X;", sig, watch_addr);
2011-02-21 07:57:56 +00:00
} else {
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
}
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. */
/* 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'
* packet unless we answer 'OK' here.
2011-02-04 07:23:52 +00:00
*/
gdb_putpacket("OK", 2);
break;
2012-11-03 06:59:01 +00:00
case 0x04:
case 'D': /* GDB 'detach' command. */
if(cur_target)
target_detach(cur_target);
2011-02-04 07:23:52 +00:00
last_target = cur_target;
cur_target = NULL;
gdb_putpacket("OK", 2);
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) {
cur_target = target_attach(last_target,
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 */
uint32_t addr, len;
2011-02-04 07:23:52 +00:00
int bin;
ERROR_IF_NO_TARGET();
sscanf(pbuf, "X%08lX,%08lX:%n", &addr, &len, &bin);
2011-02-04 07:23:52 +00:00
DEBUG("X packet: addr = %08lX, len = %08lX\n", addr, len);
if(((addr & 3) == 0) && ((len & 3) == 0))
2011-02-04 07:23:52 +00:00
target_mem_write_words(cur_target, addr, (void*)pbuf+bin, len);
else
2011-02-04 07:23:52 +00:00
target_mem_write_bytes(cur_target, addr, (void*)pbuf+bin, len);
if(target_check_error(cur_target))
gdb_putpacket("E01", 3);
else
gdb_putpacket("OK", 2);
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 */
case 'z': { /* z type,addr,len: Clear breakpoint packet */
2011-02-04 07:23:52 +00:00
uint8_t set = (pbuf[0]=='Z')?1:0;
int type, len;
uint32_t addr;
2011-02-04 07:23:52 +00:00
int ret;
ERROR_IF_NO_TARGET();
/* I have no idea why this doesn't work. Seems to work
* with real sscanf() though... */
//sscanf(pbuf, "%*[zZ]%hhd,%08lX,%hhd", &type, &addr, &len);
2011-02-04 07:23:52 +00:00
type = pbuf[1] - '0';
sscanf(pbuf + 2, ",%08lX,%d", &addr, &len);
2011-02-04 07:23:52 +00:00
switch(type) {
2012-11-03 06:59:01 +00:00
case 1: /* Hardware breakpoint */
2011-02-04 07:23:52 +00:00
if(!cur_target->set_hw_bp) { /* Not supported */
gdb_putpacket("", 0);
break;
}
2012-11-03 06:59:01 +00:00
if(set)
ret = target_set_hw_bp(cur_target, addr);
else
ret = target_clear_hw_bp(cur_target, addr);
2011-02-04 07:23:52 +00:00
2012-11-03 06:59:01 +00:00
if(!ret)
gdb_putpacket("OK", 2);
else
gdb_putpacket("E01", 3);
2011-02-04 07:23:52 +00:00
break;
2012-11-03 06:59:01 +00:00
case 2:
case 3:
case 4:
2011-02-04 07:23:52 +00:00
if(!cur_target->set_hw_wp) { /* Not supported */
gdb_putpacket("", 0);
break;
}
2012-11-03 06:59:01 +00:00
if(set)
ret = target_set_hw_wp(cur_target, type, addr, len);
else
ret = target_clear_hw_wp(cur_target, type, addr, len);
2011-02-04 07:23:52 +00:00
2012-11-03 06:59:01 +00:00
if(!ret)
gdb_putpacket("OK", 2);
else
gdb_putpacket("E01", 3);
2011-02-04 07:23:52 +00:00
break;
2012-11-03 06:59:01 +00:00
default:
2011-02-04 07:23:52 +00:00
gdb_putpacket("", 0);
}
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
default: /* Packet not implemented */
DEBUG("*** Unsupported packet: %s\n", pbuf);
2011-02-04 07:23:52 +00:00
gdb_putpacket("", 0);
}
}
}
static void
handle_q_string_reply(const char *str, const char *param)
{
unsigned long addr, len;
2012-11-03 06:59:01 +00:00
if (sscanf(param, "%08lX,%08lX", &addr, &len) != 2) {
gdb_putpacketz("E01");
return;
}
if (addr < strlen (str)) {
uint8_t reply[len+2];
reply[0] = 'm';
strncpy (reply + 1, &str[addr], len);
if(len > strlen(&str[addr]))
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)
{
uint32_t addr, alen;
2011-02-04 07:23:52 +00:00
if(!strncmp(packet, "qRcmd,", 6)) {
unsigned char *data;
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 */
int c = command_process(cur_target, data);
if(c < 0)
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. */
cur_target = target_attach(last_target,
gdb_target_destroy_callback);
2011-02-04 07:23:52 +00:00
}
if((!cur_target) || (!cur_target->xml_mem_map)) {
gdb_putpacketz("E01");
return;
}
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. */
cur_target = target_attach(last_target,
gdb_target_destroy_callback);
}
if((!cur_target) || (!cur_target->tdesc)) {
2011-02-04 07:23:52 +00:00
gdb_putpacketz("E01");
return;
}
handle_q_string_reply(cur_target->tdesc, packet + 31);
} else if (sscanf(packet, "qCRC:%08lX,%08lX", &addr, &alen) == 2) {
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
} 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)
{
unsigned long addr, len;
2011-02-04 07:23:52 +00:00
int bin;
static uint8_t flash_mode = 0;
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;
for(t = target_list, i = 1; t; t = t->next, i++)
2011-02-04 07:23:52 +00:00
if(i == addr) {
cur_target = target_attach(t,
gdb_target_destroy_callback);
2011-02-04 07:23:52 +00:00
break;
}
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) {
cur_target = target_attach(last_target,
gdb_target_destroy_callback);
2011-02-04 07:23:52 +00:00
target_reset(cur_target);
gdb_putpacketz("T05");
} else gdb_putpacketz("E01");
} 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; }
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");
} 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);
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;
} else {
DEBUG("*** Unsupported packet: %s\n", packet);
2011-02-04 07:23:52 +00:00
gdb_putpacket("", 0);
}
2011-02-04 07:23:52 +00:00
}