/*
+ * Copyright (c) 2018 ARM Limited
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder. You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
+ * Copyright 2015 LabWare
+ * Copyright 2014 Google, Inc.
* Copyright (c) 2002-2005 The Regents of The University of Michigan
* All rights reserved.
*
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * Authors: Nathan Binkert
*/
/*
- * Copyright (c) 1990, 1993
- * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1990, 1993 The Regents of the University of California
+ * All rights reserved
*
* This software was developed by the Computer Systems Engineering group
* at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
*
* All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Lawrence Berkeley Laboratories.
+ * This product includes software developed by the University of
+ * California, Lawrence Berkeley Laboratories.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * @(#)kgdb_stub.c 8.4 (Berkeley) 1/12/94
+ * @(#)kgdb_stub.c 8.4 (Berkeley) 1/12/94
*/
/*-
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
- * This product includes software developed by the NetBSD
- * Foundation, Inc. and its contributors.
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
* 4. Neither the name of The NetBSD Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* "Stub" to allow remote cpu to debug over a serial line using gdb.
*/
-#include <sys/signal.h>
+#include "base/remote_gdb.hh"
-#include <string>
+#include <sys/signal.h>
#include <unistd.h>
-#include "config/full_system.hh"
-
-#if FULL_SYSTEM
-#include "arch/vtophys.hh"
-#endif
+#include <csignal>
+#include <cstdint>
+#include <cstdio>
+#include <sstream>
+#include <string>
#include "base/intmath.hh"
-#include "base/remote_gdb.hh"
#include "base/socket.hh"
#include "base/trace.hh"
-#include "cpu/thread_context.hh"
+#include "config/the_isa.hh"
+#include "cpu/base.hh"
#include "cpu/static_inst.hh"
-//#include "mem/physical.hh"
+#include "cpu/thread_context.hh"
+#include "debug/GDBAll.hh"
#include "mem/port.hh"
-#include "mem/translating_port.hh"
+#include "mem/port_proxy.hh"
+#include "sim/full_system.hh"
#include "sim/system.hh"
using namespace std;
using namespace TheISA;
-#ifndef NDEBUG
+static const char GDBStart = '$';
+static const char GDBEnd = '#';
+static const char GDBGoodP = '+';
+static const char GDBBadP = '-';
+
vector<BaseRemoteGDB *> debuggers;
-void
-debugger()
-{
- static int current_debugger = -1;
- if (current_debugger >= 0 && current_debugger < debuggers.size()) {
- BaseRemoteGDB *gdb = debuggers[current_debugger];
- if (!gdb->isattached())
- gdb->listener->accept();
- if (gdb->isattached())
- gdb->trap(SIGILL);
+class HardBreakpoint : public PCEvent
+{
+ private:
+ BaseRemoteGDB *gdb;
+
+ public:
+ int refcount;
+
+ public:
+ HardBreakpoint(BaseRemoteGDB *_gdb, PCEventScope *s, Addr pc)
+ : PCEvent(s, "HardBreakpoint Event", pc),
+ gdb(_gdb), refcount(0)
+ {
+ DPRINTF(GDBMisc, "creating hardware breakpoint at %#x\n", evpc);
}
-}
-#endif
-///////////////////////////////////////////////////////////
-//
-//
-//
+ const std::string name() const override { return gdb->name() + ".hwbkpt"; }
-GDBListener::Event::Event(GDBListener *l, int fd, int e)
- : PollEvent(fd, e), listener(l)
-{}
+ void
+ process(ThreadContext *tc) override
+ {
+ DPRINTF(GDBMisc, "handling hardware breakpoint at %#x\n", pc());
-void
-GDBListener::Event::process(int revent)
+ if (tc == gdb->tc)
+ gdb->trap(SIGTRAP);
+ }
+};
+
+namespace {
+
+// Exception to throw when the connection to the client is broken.
+struct BadClient
{
- listener->accept();
-}
+ const char *warning;
+ BadClient(const char *_warning=NULL) : warning(_warning)
+ {}
+};
-GDBListener::GDBListener(BaseRemoteGDB *g, int p)
- : event(NULL), gdb(g), port(p)
+// Exception to throw when an error needs to be reported to the client.
+struct CmdError
{
- assert(!gdb->listener);
- gdb->listener = this;
-}
+ string error;
+ CmdError(std::string _error) : error(_error)
+ {}
+};
-GDBListener::~GDBListener()
+// Exception to throw when something isn't supported.
+class Unsupported {};
+
+// Convert a hex digit into an integer.
+// This returns -1 if the argument passed is no valid hex digit.
+int
+digit2i(char c)
{
- if (event)
- delete event;
+ if (c >= '0' && c <= '9')
+ return (c - '0');
+ else if (c >= 'a' && c <= 'f')
+ return (c - 'a' + 10);
+ else if (c >= 'A' && c <= 'F')
+ return (c - 'A' + 10);
+ else
+ return (-1);
}
-string
-GDBListener::name()
+// Convert the low 4 bits of an integer into an hex digit.
+char
+i2digit(int n)
{
- return gdb->name() + ".listener";
+ return ("0123456789abcdef"[n & 0x0f]);
}
+// Convert a byte array into an hex string.
void
-GDBListener::listen()
+mem2hex(char *vdst, const char *vsrc, int len)
{
- while (!listener.listen(port, true)) {
- DPRINTF(GDBMisc, "Can't bind port %d\n", port);
- port++;
- }
+ char *dst = vdst;
+ const char *src = vsrc;
- event = new Event(this, listener.getfd(), POLLIN);
- pollQueue.schedule(event);
+ while (len--) {
+ *dst++ = i2digit(*src >> 4);
+ *dst++ = i2digit(*src++);
+ }
+ *dst = '\0';
+}
-#ifndef NDEBUG
- gdb->number = debuggers.size();
- debuggers.push_back(gdb);
-#endif
+// Convert an hex string into a byte array.
+// This returns a pointer to the character following the last valid
+// hex digit. If the string ends in the middle of a byte, NULL is
+// returned.
+const char *
+hex2mem(char *vdst, const char *src, int maxlen)
+{
+ char *dst = vdst;
+ int msb, lsb;
-#ifndef NDEBUG
- ccprintf(cerr, "%d: %s: listening for remote gdb #%d on port %d\n",
- curTick, name(), gdb->number, port);
-#else
- ccprintf(cerr, "%d: %s: listening for remote gdb on port %d\n",
- curTick, name(), port);
-#endif
+ while (*src && maxlen--) {
+ msb = digit2i(*src++);
+ if (msb < 0)
+ return (src - 1);
+ lsb = digit2i(*src++);
+ if (lsb < 0)
+ return (NULL);
+ *dst++ = (msb << 4) | lsb;
+ }
+ return src;
}
-void
-GDBListener::accept()
+// Convert an hex string into an integer.
+// This returns a pointer to the character following the last valid
+// hex digit.
+Addr
+hex2i(const char **srcp)
{
- if (!listener.islistening())
- panic("GDBListener::accept(): cannot accept if we're not listening!");
-
- int sfd = listener.accept(true);
+ const char *src = *srcp;
+ Addr r = 0;
+ int nibble;
- if (sfd != -1) {
- if (gdb->isattached())
- close(sfd);
- else
- gdb->attach(sfd);
+ while ((nibble = digit2i(*src)) >= 0) {
+ r *= 16;
+ r += nibble;
+ src++;
}
+ *srcp = src;
+ return r;
}
-BaseRemoteGDB::Event::Event(BaseRemoteGDB *g, int fd, int e)
- : PollEvent(fd, e), gdb(g)
-{}
+enum GdbBreakpointType {
+ GdbSoftBp = '0',
+ GdbHardBp = '1',
+ GdbWriteWp = '2',
+ GdbReadWp = '3',
+ GdbAccWp = '4',
+};
-void
-BaseRemoteGDB::Event::process(int revent)
+#ifndef NDEBUG
+const char *
+break_type(char c)
{
- if (revent & POLLIN)
- gdb->trap(SIGILL);
- else if (revent & POLLNVAL)
- gdb->detach();
+ switch(c) {
+ case GdbSoftBp: return "software breakpoint";
+ case GdbHardBp: return "hardware breakpoint";
+ case GdbWriteWp: return "write watchpoint";
+ case GdbReadWp: return "read watchpoint";
+ case GdbAccWp: return "access watchpoint";
+ default: return "unknown breakpoint/watchpoint";
+ }
}
+#endif
+
+std::map<Addr, HardBreakpoint *> hardBreakMap;
-BaseRemoteGDB::BaseRemoteGDB(System *_system, ThreadContext *c, size_t cacheSize)
- : event(NULL), listener(NULL), number(-1), fd(-1),
- active(false), attached(false),
- system(_system), pmem(_system->physmem), context(c),
- gdbregs(cacheSize)
+}
+
+BaseRemoteGDB::BaseRemoteGDB(System *_system, ThreadContext *c, int _port) :
+ connectEvent(nullptr), dataEvent(nullptr), _port(_port), fd(-1),
+ active(false), attached(false), sys(_system), tc(c),
+ trapEvent(this), singleStepEvent(*this)
{
- memset(gdbregs.regs, 0, gdbregs.bytes());
+ debuggers.push_back(this);
}
BaseRemoteGDB::~BaseRemoteGDB()
{
- if (event)
- delete event;
+ delete connectEvent;
+ delete dataEvent;
}
string
BaseRemoteGDB::name()
{
- return system->name() + ".remote_gdb";
+ return sys->name() + ".remote_gdb";
}
-bool
-BaseRemoteGDB::isattached()
-{ return attached; }
+void
+BaseRemoteGDB::listen()
+{
+ if (ListenSocket::allDisabled()) {
+ warn_once("Sockets disabled, not accepting gdb connections");
+ return;
+ }
+
+ while (!listener.listen(_port, true)) {
+ DPRINTF(GDBMisc, "Can't bind port %d\n", _port);
+ _port++;
+ }
+
+ connectEvent = new ConnectEvent(this, listener.getfd(), POLLIN);
+ pollQueue.schedule(connectEvent);
+
+ ccprintf(cerr, "%d: %s: listening for remote gdb on port %d\n",
+ curTick(), name(), _port);
+}
+
+void
+BaseRemoteGDB::connect()
+{
+ panic_if(!listener.islistening(),
+ "Cannot accept GDB connections if we're not listening!");
+
+ int sfd = listener.accept(true);
+
+ if (sfd != -1) {
+ if (isAttached())
+ close(sfd);
+ else
+ attach(sfd);
+ }
+}
+
+int
+BaseRemoteGDB::port() const
+{
+ panic_if(!listener.islistening(),
+ "Remote GDB port is unknown until listen() has been called.\n");
+ return _port;
+}
void
BaseRemoteGDB::attach(int f)
{
fd = f;
- event = new Event(this, fd, POLLIN);
- pollQueue.schedule(event);
+ dataEvent = new DataEvent(this, fd, POLLIN);
+ pollQueue.schedule(dataEvent);
attached = true;
DPRINTFN("remote gdb attached\n");
BaseRemoteGDB::detach()
{
attached = false;
+ active = false;
+ clearSingleStep();
close(fd);
fd = -1;
- pollQueue.remove(event);
+ pollQueue.remove(dataEvent);
DPRINTFN("remote gdb detached\n");
}
-const char *
-BaseRemoteGDB::gdb_command(char cmd)
-{
- switch (cmd) {
- case GDBSignal: return "KGDB_SIGNAL";
- case GDBSetBaud: return "KGDB_SET_BAUD";
- case GDBSetBreak: return "KGDB_SET_BREAK";
- case GDBCont: return "KGDB_CONT";
- case GDBAsyncCont: return "KGDB_ASYNC_CONT";
- case GDBDebug: return "KGDB_DEBUG";
- case GDBDetach: return "KGDB_DETACH";
- case GDBRegR: return "KGDB_REG_R";
- case GDBRegW: return "KGDB_REG_W";
- case GDBSetThread: return "KGDB_SET_THREAD";
- case GDBCycleStep: return "KGDB_CYCLE_STEP";
- case GDBSigCycleStep: return "KGDB_SIG_CYCLE_STEP";
- case GDBKill: return "KGDB_KILL";
- case GDBMemW: return "KGDB_MEM_W";
- case GDBMemR: return "KGDB_MEM_R";
- case GDBSetReg: return "KGDB_SET_REG";
- case GDBReadReg: return "KGDB_READ_REG";
- case GDBQueryVar: return "KGDB_QUERY_VAR";
- case GDBSetVar: return "KGDB_SET_VAR";
- case GDBReset: return "KGDB_RESET";
- case GDBStep: return "KGDB_STEP";
- case GDBAsyncStep: return "KGDB_ASYNC_STEP";
- case GDBThreadAlive: return "KGDB_THREAD_ALIVE";
- case GDBTargetExit: return "KGDB_TARGET_EXIT";
- case GDBBinaryDload: return "KGDB_BINARY_DLOAD";
- case GDBClrHwBkpt: return "KGDB_CLR_HW_BKPT";
- case GDBSetHwBkpt: return "KGDB_SET_HW_BKPT";
- case GDBStart: return "KGDB_START";
- case GDBEnd: return "KGDB_END";
- case GDBGoodP: return "KGDB_GOODP";
- case GDBBadP: return "KGDB_BADP";
- default: return "KGDB_UNKNOWN";
+// This function does all command processing for interfacing to a
+// remote gdb. Note that the error codes are ignored by gdb at
+// present, but might eventually become meaningful. (XXX) It might
+// makes sense to use POSIX errno values, because that is what the
+// gdb/remote.c functions want to return.
+bool
+BaseRemoteGDB::trap(int type)
+{
+
+ if (!attached)
+ return false;
+
+ DPRINTF(GDBMisc, "trap: PC=%s\n", tc->pcState());
+
+ clearSingleStep();
+
+ /*
+ * The first entry to this function is normally through
+ * a breakpoint trap in kgdb_connect(), in which case we
+ * must advance past the breakpoint because gdb will not.
+ *
+ * On the first entry here, we expect that gdb is not yet
+ * listening to us, so just enter the interaction loop.
+ * After the debugger is "active" (connected) it will be
+ * waiting for a "signaled" message from us.
+ */
+ if (!active) {
+ active = true;
+ } else {
+ // Tell remote host that an exception has occurred.
+ send(csprintf("S%02x", type).c_str());
+ }
+
+ // Stick frame regs into our reg cache.
+ regCachePtr = gdbRegs();
+ regCachePtr->getRegs(tc);
+
+ GdbCommand::Context cmdCtx;
+ cmdCtx.type = type;
+ std::vector<char> data;
+
+ for (;;) {
+ try {
+ recv(data);
+ if (data.size() == 1)
+ throw BadClient();
+ cmdCtx.cmd_byte = data[0];
+ cmdCtx.data = data.data() + 1;
+ // One for sentinel, one for cmd_byte.
+ cmdCtx.len = data.size() - 2;
+
+ auto cmdIt = command_map.find(cmdCtx.cmd_byte);
+ if (cmdIt == command_map.end()) {
+ DPRINTF(GDBMisc, "Unknown command: %c(%#x)\n",
+ cmdCtx.cmd_byte, cmdCtx.cmd_byte);
+ throw Unsupported();
+ }
+ cmdCtx.cmd = &(cmdIt->second);
+
+ if (!(this->*(cmdCtx.cmd->func))(cmdCtx))
+ break;
+
+ } catch (BadClient &e) {
+ if (e.warning)
+ warn(e.warning);
+ detach();
+ break;
+ } catch (Unsupported &e) {
+ send("");
+ } catch (CmdError &e) {
+ send(e.error.c_str());
+ } catch (...) {
+ panic("Unrecognzied GDB exception.");
+ }
}
+
+ return true;
}
-/////////////////////////
-//
-//
+void
+BaseRemoteGDB::incomingData(int revent)
+{
+ if (trapEvent.scheduled()) {
+ warn("GDB trap event has already been scheduled!");
+ return;
+ }
+
+ if (revent & POLLIN) {
+ trapEvent.type(SIGILL);
+ scheduleInstCommitEvent(&trapEvent, 0);
+ } else if (revent & POLLNVAL) {
+ descheduleInstCommitEvent(&trapEvent);
+ detach();
+ }
+}
uint8_t
BaseRemoteGDB::getbyte()
{
uint8_t b;
- ::read(fd, &b, 1);
- return b;
-}
+ if (::read(fd, &b, sizeof(b)) == sizeof(b))
+ return b;
-void
-BaseRemoteGDB::putbyte(uint8_t b)
-{
- ::write(fd, &b, 1);
+ throw BadClient("Couldn't read data from debugger.");
}
-// Send a packet to gdb
void
-BaseRemoteGDB::send(const char *bp)
+BaseRemoteGDB::putbyte(uint8_t b)
{
- const char *p;
- uint8_t csum, c;
+ if (::write(fd, &b, sizeof(b)) == sizeof(b))
+ return;
- DPRINTF(GDBSend, "send: %s\n", bp);
-
- do {
- p = bp;
- //Start sending a packet
- putbyte(GDBStart);
- //Send the contents, and also keep a check sum.
- for (csum = 0; (c = *p); p++) {
- putbyte(c);
- csum += c;
- }
- //Send the ending character.
- putbyte(GDBEnd);
- //Sent the checksum.
- putbyte(i2digit(csum >> 4));
- putbyte(i2digit(csum));
- //Try transmitting over and over again until the other end doesn't send an
- //error back.
- } while ((c = getbyte() & 0x7f) == GDBBadP);
+ throw BadClient("Couldn't write data to the debugger.");
}
// Receive a packet from gdb
-int
-BaseRemoteGDB::recv(char *bp, int maxlen)
+void
+BaseRemoteGDB::recv(std::vector<char>& bp)
{
- char *p;
- int c, csum;
- int len;
+ uint8_t c;
+ int csum;
+ bp.resize(0);
do {
- p = bp;
- csum = len = 0;
- //Find the beginning of a packet
- while ((c = getbyte()) != GDBStart)
- ;
-
- //Read until you find the end of the data in the packet, and keep
- //track of the check sum.
- while ((c = getbyte()) != GDBEnd && len < maxlen) {
+ csum = 0;
+ // Find the beginning of a packet
+ while ((c = getbyte()) != GDBStart);
+
+ // Read until you find the end of the data in the packet, and keep
+ // track of the check sum.
+ while (true) {
+ c = getbyte();
+ if (c == GDBEnd)
+ break;
c &= 0x7f;
csum += c;
- *p++ = c;
- len++;
+ bp.push_back(c);
}
- //Mask the check sum, and terminate the command string.
+ // Mask the check sum.
csum &= 0xff;
- *p = '\0';
-
- //If the command was too long, report an error.
- if (len >= maxlen) {
- putbyte(GDBBadP);
- continue;
- }
- //Bring in the checksum. If the check sum matches, csum will be 0.
+ // Bring in the checksum. If the check sum matches, csum will be 0.
csum -= digit2i(getbyte()) * 16;
csum -= digit2i(getbyte());
- //If the check sum was correct
+ // If the check sum was correct
if (csum == 0) {
- //Report that the packet was received correctly
+ // Report that the packet was received correctly
putbyte(GDBGoodP);
// Sequence present?
- if (bp[2] == ':') {
+ if (bp.size() > 2 && bp[2] == ':') {
putbyte(bp[0]);
putbyte(bp[1]);
- len -= 3;
- memcpy(bp, bp+3, len);
+ auto begin = std::begin(bp);
+ bp.erase(begin, std::next(begin, 3));
}
break;
}
- //Otherwise, report that there was a mistake.
+ // Otherwise, report that there was a mistake.
putbyte(GDBBadP);
} while (1);
+ // Sentinel.
+ bp.push_back('\0');
+ DPRINTF(GDBRecv, "recv: %s\n", bp.data());
+}
- DPRINTF(GDBRecv, "recv: %s: %s\n", gdb_command(*bp), bp);
+// Send a packet to gdb
+void
+BaseRemoteGDB::send(const char *bp)
+{
+ const char *p;
+ uint8_t csum, c;
+
+ DPRINTF(GDBSend, "send: %s\n", bp);
- return (len);
+ do {
+ p = bp;
+ // Start sending a packet
+ putbyte(GDBStart);
+ // Send the contents, and also keep a check sum.
+ for (csum = 0; (c = *p); p++) {
+ putbyte(c);
+ csum += c;
+ }
+ // Send the ending character.
+ putbyte(GDBEnd);
+ // Send the checksum.
+ putbyte(i2digit(csum >> 4));
+ putbyte(i2digit(csum));
+ // Try transmitting over and over again until the other end doesn't
+ // send an error back.
+ c = getbyte();
+ } while ((c & 0x7f) == GDBBadP);
}
// Read bytes from kernel address space for debugger.
bool
BaseRemoteGDB::read(Addr vaddr, size_t size, char *data)
{
- static Addr lastaddr = 0;
- static size_t lastsize = 0;
-
- if (vaddr < 10) {
- DPRINTF(GDBRead, "read: reading memory location zero!\n");
- vaddr = lastaddr + lastsize;
- }
-
DPRINTF(GDBRead, "read: addr=%#x, size=%d", vaddr, size);
-#if FULL_SYSTEM
- VirtualPort *port = context->getVirtPort(context);
-#else
- TranslatingPort *port = context->getMemPort();
-#endif
- port->readBlob(vaddr, (uint8_t*)data, size);
-#if FULL_SYSTEM
- context->delVirtPort(port);
-#else
- delete port;
-#endif
+ PortProxy &proxy = tc->getVirtProxy();
+ proxy.readBlob(vaddr, data, size);
#if TRACING_ON
if (DTRACE(GDBRead)) {
bool
BaseRemoteGDB::write(Addr vaddr, size_t size, const char *data)
{
- static Addr lastaddr = 0;
- static size_t lastsize = 0;
-
- if (vaddr < 10) {
- DPRINTF(GDBWrite, "write: writing memory location zero!\n");
- vaddr = lastaddr + lastsize;
- }
-
if (DTRACE(GDBWrite)) {
DPRINTFN("write: addr=%#x, size=%d", vaddr, size);
if (DTRACE(GDBExtra)) {
} else
DPRINTFNR("\n");
}
-#if FULL_SYSTEM
- VirtualPort *port = context->getVirtPort(context);
-#else
- TranslatingPort *port = context->getMemPort();
-#endif
- port->writeBlob(vaddr, (uint8_t*)data, size);
-#if FULL_SYSTEM
- context->delVirtPort(port);
-#else
- delete port;
-#endif
+ PortProxy &proxy = tc->getVirtProxy();
+ proxy.writeBlob(vaddr, data, size);
return true;
}
-PCEventQueue *BaseRemoteGDB::getPcEventQueue()
+void
+BaseRemoteGDB::singleStep()
{
- return &system->pcEventQueue;
+ if (!singleStepEvent.scheduled())
+ scheduleInstCommitEvent(&singleStepEvent, 1);
+ trap(SIGTRAP);
}
-BaseRemoteGDB::HardBreakpoint::HardBreakpoint(BaseRemoteGDB *_gdb, Addr pc)
- : PCEvent(_gdb->getPcEventQueue(), "HardBreakpoint Event", pc),
- gdb(_gdb), refcount(0)
+void
+BaseRemoteGDB::clearSingleStep()
{
- DPRINTF(GDBMisc, "creating hardware breakpoint at %#x\n", evpc);
+ descheduleInstCommitEvent(&singleStepEvent);
}
void
-BaseRemoteGDB::HardBreakpoint::process(ThreadContext *tc)
+BaseRemoteGDB::setSingleStep()
{
- DPRINTF(GDBMisc, "handling hardware breakpoint at %#x\n", pc());
-
- if (tc == gdb->context)
- gdb->trap(SIGTRAP);
+ if (!singleStepEvent.scheduled())
+ scheduleInstCommitEvent(&singleStepEvent, 1);
}
-bool
+void
BaseRemoteGDB::insertSoftBreak(Addr addr, size_t len)
{
- if (len != sizeof(TheISA::MachInst))
- panic("invalid length\n");
+ if (!checkBpLen(len))
+ throw BadClient("Invalid breakpoint length\n");
return insertHardBreak(addr, len);
}
-bool
+void
BaseRemoteGDB::removeSoftBreak(Addr addr, size_t len)
{
- if (len != sizeof(MachInst))
- panic("invalid length\n");
+ if (!checkBpLen(len))
+ throw BadClient("Invalid breakpoint length.\n");
return removeHardBreak(addr, len);
}
-bool
+void
BaseRemoteGDB::insertHardBreak(Addr addr, size_t len)
{
- if (len != sizeof(MachInst))
- panic("invalid length\n");
+ if (!checkBpLen(len))
+ throw BadClient("Invalid breakpoint length\n");
- DPRINTF(GDBMisc, "inserting hardware breakpoint at %#x\n", addr);
+ DPRINTF(GDBMisc, "Inserting hardware breakpoint at %#x\n", addr);
HardBreakpoint *&bkpt = hardBreakMap[addr];
if (bkpt == 0)
- bkpt = new HardBreakpoint(this, addr);
+ bkpt = new HardBreakpoint(this, sys, addr);
bkpt->refcount++;
-
- return true;
}
-bool
+void
BaseRemoteGDB::removeHardBreak(Addr addr, size_t len)
{
- if (len != sizeof(MachInst))
- panic("invalid length\n");
+ if (!checkBpLen(len))
+ throw BadClient("Invalid breakpoint length\n");
- DPRINTF(GDBMisc, "removing hardware breakpoint at %#x\n", addr);
+ DPRINTF(GDBMisc, "Removing hardware breakpoint at %#x\n", addr);
- break_iter_t i = hardBreakMap.find(addr);
+ auto i = hardBreakMap.find(addr);
if (i == hardBreakMap.end())
- return false;
+ throw CmdError("E0C");
HardBreakpoint *hbp = (*i).second;
if (--hbp->refcount == 0) {
delete hbp;
hardBreakMap.erase(i);
}
+}
- return true;
+void
+BaseRemoteGDB::clearTempBreakpoint(Addr &bkpt)
+{
+ DPRINTF(GDBMisc, "setTempBreakpoint: addr=%#x\n", bkpt);
+ removeHardBreak(bkpt, sizeof(TheISA::MachInst));
+ bkpt = 0;
}
void
}
void
-BaseRemoteGDB::clearTempBreakpoint(Addr &bkpt)
+BaseRemoteGDB::scheduleInstCommitEvent(Event *ev, int delta)
{
- DPRINTF(GDBMisc, "setTempBreakpoint: addr=%#x\n", bkpt);
- removeHardBreak(bkpt, sizeof(TheISA::MachInst));
- bkpt = 0;
+ // Here "ticks" aren't simulator ticks which measure time, they're
+ // instructions committed by the CPU.
+ tc->scheduleInstCountEvent(ev, tc->getCurrentInstCount() + delta);
}
-const char *
-BaseRemoteGDB::break_type(char c)
+void
+BaseRemoteGDB::descheduleInstCommitEvent(Event *ev)
{
- switch(c) {
- case '0': return "software breakpoint";
- case '1': return "hardware breakpoint";
- case '2': return "write watchpoint";
- case '3': return "read watchpoint";
- case '4': return "access watchpoint";
- default: return "unknown breakpoint/watchpoint";
- }
+ if (ev->scheduled())
+ tc->descheduleInstCountEvent(ev);
}
-// This function does all command processing for interfacing to a
-// remote gdb. Note that the error codes are ignored by gdb at
-// present, but might eventually become meaningful. (XXX) It might
-// makes sense to use POSIX errno values, because that is what the
-// gdb/remote.c functions want to return.
+std::map<char, BaseRemoteGDB::GdbCommand> BaseRemoteGDB::command_map = {
+ // last signal
+ { '?', { "KGDB_SIGNAL", &BaseRemoteGDB::cmd_signal } },
+ // set baud (deprecated)
+ { 'b', { "KGDB_SET_BAUD", &BaseRemoteGDB::cmd_unsupported } },
+ // set breakpoint (deprecated)
+ { 'B', { "KGDB_SET_BREAK", &BaseRemoteGDB::cmd_unsupported } },
+ // resume
+ { 'c', { "KGDB_CONT", &BaseRemoteGDB::cmd_cont } },
+ // continue with signal
+ { 'C', { "KGDB_ASYNC_CONT", &BaseRemoteGDB::cmd_async_cont } },
+ // toggle debug flags (deprecated)
+ { 'd', { "KGDB_DEBUG", &BaseRemoteGDB::cmd_unsupported } },
+ // detach remote gdb
+ { 'D', { "KGDB_DETACH", &BaseRemoteGDB::cmd_detach } },
+ // read general registers
+ { 'g', { "KGDB_REG_R", &BaseRemoteGDB::cmd_reg_r } },
+ // write general registers
+ { 'G', { "KGDB_REG_W", &BaseRemoteGDB::cmd_reg_w } },
+ // set thread
+ { 'H', { "KGDB_SET_THREAD", &BaseRemoteGDB::cmd_set_thread } },
+ // step a single cycle
+ { 'i', { "KGDB_CYCLE_STEP", &BaseRemoteGDB::cmd_unsupported } },
+ // signal then cycle step
+ { 'I', { "KGDB_SIG_CYCLE_STEP", &BaseRemoteGDB::cmd_unsupported } },
+ // kill program
+ { 'k', { "KGDB_KILL", &BaseRemoteGDB::cmd_detach } },
+ // read memory
+ { 'm', { "KGDB_MEM_R", &BaseRemoteGDB::cmd_mem_r } },
+ // write memory
+ { 'M', { "KGDB_MEM_W", &BaseRemoteGDB::cmd_mem_w } },
+ // read register
+ { 'p', { "KGDB_READ_REG", &BaseRemoteGDB::cmd_unsupported } },
+ // write register
+ { 'P', { "KGDB_SET_REG", &BaseRemoteGDB::cmd_unsupported } },
+ // query variable
+ { 'q', { "KGDB_QUERY_VAR", &BaseRemoteGDB::cmd_query_var } },
+ // set variable
+ { 'Q', { "KGDB_SET_VAR", &BaseRemoteGDB::cmd_unsupported } },
+ // reset system (deprecated)
+ { 'r', { "KGDB_RESET", &BaseRemoteGDB::cmd_unsupported } },
+ // step
+ { 's', { "KGDB_STEP", &BaseRemoteGDB::cmd_step } },
+ // signal and step
+ { 'S', { "KGDB_ASYNC_STEP", &BaseRemoteGDB::cmd_async_step } },
+ // find out if the thread is alive
+ { 'T', { "KGDB_THREAD_ALIVE", &BaseRemoteGDB::cmd_unsupported } },
+ // target exited
+ { 'W', { "KGDB_TARGET_EXIT", &BaseRemoteGDB::cmd_unsupported } },
+ // write memory
+ { 'X', { "KGDB_BINARY_DLOAD", &BaseRemoteGDB::cmd_unsupported } },
+ // remove breakpoint or watchpoint
+ { 'z', { "KGDB_CLR_HW_BKPT", &BaseRemoteGDB::cmd_clr_hw_bkpt } },
+ // insert breakpoint or watchpoint
+ { 'Z', { "KGDB_SET_HW_BKPT", &BaseRemoteGDB::cmd_set_hw_bkpt } },
+};
+
bool
-BaseRemoteGDB::trap(int type)
+BaseRemoteGDB::checkBpLen(size_t len)
{
- uint64_t val;
- size_t datalen, len;
- char data[GDBPacketBufLen + 1];
- char *buffer;
- const char *p;
- char command, subcmd;
- string var;
- bool ret;
+ return len == sizeof(MachInst);
+}
- if (!attached)
- return false;
+bool
+BaseRemoteGDB::cmd_unsupported(GdbCommand::Context &ctx)
+{
+ DPRINTF(GDBMisc, "Unsupported command: %s\n", ctx.cmd->name);
+ DDUMP(GDBMisc, ctx.data, ctx.len);
+ throw Unsupported();
+}
- buffer = (char*)malloc(gdbregs.bytes() * 2 + 256);
- DPRINTF(GDBMisc, "trap: PC=%#x NPC=%#x\n",
- context->readPC(), context->readNextPC());
+bool
+BaseRemoteGDB::cmd_signal(GdbCommand::Context &ctx)
+{
+ send(csprintf("S%02x", ctx.type).c_str());
+ return true;
+}
+bool
+BaseRemoteGDB::cmd_cont(GdbCommand::Context &ctx)
+{
+ const char *p = ctx.data;
+ if (ctx.len) {
+ Addr newPc = hex2i(&p);
+ tc->pcState(newPc);
+ }
clearSingleStep();
+ return false;
+}
- /*
- * The first entry to this function is normally through
- * a breakpoint trap in kgdb_connect(), in which case we
- * must advance past the breakpoint because gdb will not.
- *
- * On the first entry here, we expect that gdb is not yet
- * listening to us, so just enter the interaction loop.
- * After the debugger is "active" (connected) it will be
- * waiting for a "signaled" message from us.
- */
- if (!active)
- active = true;
- else
- // Tell remote host that an exception has occurred.
- snprintf((char *)buffer, sizeof(buffer), "S%02x", type);
- send(buffer);
-
- // Stick frame regs into our reg cache.
- getregs();
-
- for (;;) {
- datalen = recv(data, sizeof(data));
- data[sizeof(data) - 1] = 0; // Sentinel
- command = data[0];
- subcmd = 0;
- p = data + 1;
- switch (command) {
-
- case GDBSignal:
- // if this command came from a running gdb, answer it --
- // the other guy has no way of knowing if we're in or out
- // of this loop when he issues a "remote-signal".
- snprintf((char *)buffer, sizeof(buffer),
- "S%02x", type);
- send(buffer);
- continue;
-
- case GDBRegR:
- if (2 * gdbregs.bytes() > sizeof(buffer))
- panic("buffer too small");
-
- mem2hex(buffer, gdbregs.regs, gdbregs.bytes());
- send(buffer);
- continue;
-
- case GDBRegW:
- p = hex2mem(gdbregs.regs, p, gdbregs.bytes());
- if (p == NULL || *p != '\0')
- send("E01");
- else {
- setregs();
- send("OK");
- }
- continue;
-
-#if 0
- case GDBSetReg:
- val = hex2i(&p);
- if (*p++ != '=') {
- send("E01");
- continue;
- }
- if (val < 0 && val >= KGDB_NUMREGS) {
- send("E01");
- continue;
- }
-
- gdbregs.regs[val] = hex2i(&p);
- setregs();
- send("OK");
-
- continue;
-#endif
-
- case GDBMemR:
- val = hex2i(&p);
- if (*p++ != ',') {
- send("E02");
- continue;
- }
- len = hex2i(&p);
- if (*p != '\0') {
- send("E03");
- continue;
- }
- if (len > sizeof(buffer)) {
- send("E04");
- continue;
- }
- if (!acc(val, len)) {
- send("E05");
- continue;
- }
-
- if (read(val, (size_t)len, (char *)buffer)) {
- // variable length array would be nice, but C++ doesn't
- // officially support those...
- char *temp = new char[2*len+1];
- mem2hex(temp, buffer, len);
- send(temp);
- delete [] temp;
- } else {
- send("E05");
- }
- continue;
-
- case GDBMemW:
- val = hex2i(&p);
- if (*p++ != ',') {
- send("E06");
- continue;
- }
- len = hex2i(&p);
- if (*p++ != ':') {
- send("E07");
- continue;
- }
- if (len > datalen - (p - data)) {
- send("E08");
- continue;
- }
- p = hex2mem(buffer, p, sizeof(buffer));
- if (p == NULL) {
- send("E09");
- continue;
- }
- if (!acc(val, len)) {
- send("E0A");
- continue;
- }
- if (write(val, (size_t)len, (char *)buffer))
- send("OK");
- else
- send("E0B");
- continue;
-
- case GDBSetThread:
- subcmd = *p++;
- val = hex2i(&p);
- if (val == 0)
- send("OK");
- else
- send("E01");
- continue;
-
- case GDBDetach:
- case GDBKill:
- active = false;
- clearSingleStep();
- detach();
- goto out;
-
- case GDBAsyncCont:
- subcmd = hex2i(&p);
- if (*p++ == ';') {
- val = hex2i(&p);
- context->setPC(val);
- context->setNextPC(val + sizeof(MachInst));
- }
- clearSingleStep();
- goto out;
-
- case GDBCont:
- if (p - data < datalen) {
- val = hex2i(&p);
- context->setPC(val);
- context->setNextPC(val + sizeof(MachInst));
- }
- clearSingleStep();
- goto out;
-
- case GDBAsyncStep:
- subcmd = hex2i(&p);
- if (*p++ == ';') {
- val = hex2i(&p);
- context->setPC(val);
- context->setNextPC(val + sizeof(MachInst));
- }
- setSingleStep();
- goto out;
-
- case GDBStep:
- if (p - data < datalen) {
- val = hex2i(&p);
- context->setPC(val);
- context->setNextPC(val + sizeof(MachInst));
- }
- setSingleStep();
- goto out;
-
- case GDBClrHwBkpt:
- subcmd = *p++;
- if (*p++ != ',') send("E0D");
- val = hex2i(&p);
- if (*p++ != ',') send("E0D");
- len = hex2i(&p);
-
- DPRINTF(GDBMisc, "clear %s, addr=%#x, len=%d\n",
- break_type(subcmd), val, len);
-
- ret = false;
-
- switch (subcmd) {
- case '0': // software breakpoint
- ret = removeSoftBreak(val, len);
- break;
-
- case '1': // hardware breakpoint
- ret = removeHardBreak(val, len);
- break;
-
- case '2': // write watchpoint
- case '3': // read watchpoint
- case '4': // access watchpoint
- default: // unknown
- send("");
- break;
- }
-
- send(ret ? "OK" : "E0C");
- continue;
-
- case GDBSetHwBkpt:
- subcmd = *p++;
- if (*p++ != ',') send("E0D");
- val = hex2i(&p);
- if (*p++ != ',') send("E0D");
- len = hex2i(&p);
+bool
+BaseRemoteGDB::cmd_async_cont(GdbCommand::Context &ctx)
+{
+ const char *p = ctx.data;
+ hex2i(&p);
+ if (*p++ == ';') {
+ Addr newPc = hex2i(&p);
+ tc->pcState(newPc);
+ }
+ clearSingleStep();
+ return false;
+}
- DPRINTF(GDBMisc, "set %s, addr=%#x, len=%d\n",
- break_type(subcmd), val, len);
+bool
+BaseRemoteGDB::cmd_detach(GdbCommand::Context &ctx)
+{
+ detach();
+ return false;
+}
- ret = false;
+bool
+BaseRemoteGDB::cmd_reg_r(GdbCommand::Context &ctx)
+{
+ char buf[2 * regCachePtr->size() + 1];
+ buf[2 * regCachePtr->size()] = '\0';
+ mem2hex(buf, regCachePtr->data(), regCachePtr->size());
+ send(buf);
+ return true;
+}
- switch (subcmd) {
- case '0': // software breakpoint
- ret = insertSoftBreak(val, len);
- break;
+bool
+BaseRemoteGDB::cmd_reg_w(GdbCommand::Context &ctx)
+{
+ const char *p = ctx.data;
+ p = hex2mem(regCachePtr->data(), p, regCachePtr->size());
+ if (p == NULL || *p != '\0')
+ throw CmdError("E01");
- case '1': // hardware breakpoint
- ret = insertHardBreak(val, len);
- break;
+ regCachePtr->setRegs(tc);
+ send("OK");
- case '2': // write watchpoint
- case '3': // read watchpoint
- case '4': // access watchpoint
- default: // unknown
- send("");
- break;
- }
+ return true;
+}
- send(ret ? "OK" : "E0C");
- continue;
-
- case GDBQueryVar:
- var = string(p, datalen - 1);
- if (var == "C")
- send("QC0");
- else
- send("");
- continue;
-
- case GDBSetBaud:
- case GDBSetBreak:
- case GDBDebug:
- case GDBCycleStep:
- case GDBSigCycleStep:
- case GDBReadReg:
- case GDBSetVar:
- case GDBReset:
- case GDBThreadAlive:
- case GDBTargetExit:
- case GDBBinaryDload:
- // Unsupported command
- DPRINTF(GDBMisc, "Unsupported command: %s\n",
- gdb_command(command));
- DDUMP(GDBMisc, (uint8_t *)data, datalen);
- send("");
- continue;
+bool
+BaseRemoteGDB::cmd_set_thread(GdbCommand::Context &ctx)
+{
+ const char *p = ctx.data + 1; // Ignore the subcommand byte.
+ if (hex2i(&p) != 0)
+ throw CmdError("E01");
+ send("OK");
+ return true;
+}
- default:
- // Unknown command.
- DPRINTF(GDBMisc, "Unknown command: %c(%#x)\n",
- command, command);
- send("");
- continue;
+bool
+BaseRemoteGDB::cmd_mem_r(GdbCommand::Context &ctx)
+{
+ const char *p = ctx.data;
+ Addr addr = hex2i(&p);
+ if (*p++ != ',')
+ throw CmdError("E02");
+ size_t len = hex2i(&p);
+ if (*p != '\0')
+ throw CmdError("E03");
+ if (!acc(addr, len))
+ throw CmdError("E05");
+
+ char buf[len];
+ if (!read(addr, len, buf))
+ throw CmdError("E05");
+
+ char temp[2 * len + 1];
+ temp[2 * len] = '\0';
+ mem2hex(temp, buf, len);
+ send(temp);
+ return true;
+}
+bool
+BaseRemoteGDB::cmd_mem_w(GdbCommand::Context &ctx)
+{
+ const char *p = ctx.data;
+ Addr addr = hex2i(&p);
+ if (*p++ != ',')
+ throw CmdError("E06");
+ size_t len = hex2i(&p);
+ if (*p++ != ':')
+ throw CmdError("E07");
+ if (len * 2 > ctx.len - (p - ctx.data))
+ throw CmdError("E08");
+ char buf[len];
+ p = (char *)hex2mem(buf, p, len);
+ if (p == NULL)
+ throw CmdError("E09");
+ if (!acc(addr, len))
+ throw CmdError("E0A");
+ if (!write(addr, len, buf))
+ throw CmdError("E0B");
+ send("OK");
+ return true;
+}
+bool
+BaseRemoteGDB::cmd_query_var(GdbCommand::Context &ctx)
+{
+ string s(ctx.data, ctx.len - 1);
+ string xfer_read_prefix = "Xfer:features:read:";
+ if (s.rfind("Supported:", 0) == 0) {
+ std::ostringstream oss;
+ // This reply field mandatory. We can receive arbitrarily
+ // long packets, so we could choose it to be arbitrarily large.
+ // This is just an arbitrary filler value that seems to work.
+ oss << "PacketSize=1024";
+ for (const auto& feature : availableFeatures())
+ oss << ';' << feature;
+ send(oss.str().c_str());
+ } else if (s.rfind(xfer_read_prefix, 0) == 0) {
+ size_t offset, length;
+ auto value_string = s.substr(xfer_read_prefix.length());
+ auto colon_pos = value_string.find(':');
+ auto comma_pos = value_string.find(',');
+ if (colon_pos == std::string::npos || comma_pos == std::string::npos)
+ throw CmdError("E00");
+ std::string annex;
+ if (!getXferFeaturesRead(value_string.substr(0, colon_pos), annex))
+ throw CmdError("E00");
+ try {
+ offset = std::stoull(
+ value_string.substr(colon_pos + 1, comma_pos), NULL, 16);
+ length = std::stoull(
+ value_string.substr(comma_pos + 1), NULL, 16);
+ } catch (std::invalid_argument& e) {
+ throw CmdError("E00");
+ } catch (std::out_of_range& e) {
+ throw CmdError("E00");
}
+ std::string encoded;
+ encodeXferResponse(annex, encoded, offset, length);
+ send(encoded.c_str());
+ } else if (s == "C") {
+ send("QC0");
+ } else {
+ throw Unsupported();
}
-
- out:
- free(buffer);
return true;
}
-// Convert a hex digit into an integer.
-// This returns -1 if the argument passed is no valid hex digit.
-int
-BaseRemoteGDB::digit2i(char c)
+std::vector<std::string>
+BaseRemoteGDB::availableFeatures() const
{
- if (c >= '0' && c <= '9')
- return (c - '0');
- else if (c >= 'a' && c <= 'f')
- return (c - 'a' + 10);
- else if (c >= 'A' && c <= 'F')
+ return {};
+};
- return (c - 'A' + 10);
- else
- return (-1);
+bool
+BaseRemoteGDB::getXferFeaturesRead(
+ const std::string &annex, std::string &output)
+{
+ return false;
}
-// Convert the low 4 bits of an integer into an hex digit.
-char
-BaseRemoteGDB::i2digit(int n)
+void
+BaseRemoteGDB::encodeBinaryData(
+ const std::string &unencoded, std::string &encoded) const
{
- return ("0123456789abcdef"[n & 0x0f]);
+ for (const char& c : unencoded) {
+ if (c == '$' || c == '#' || c == '}' || c == '*') {
+ encoded += '}';
+ encoded += c ^ 0x20;
+ } else {
+ encoded += c;
+ }
+ }
}
-// Convert a byte array into an hex string.
void
-BaseRemoteGDB::mem2hex(void *vdst, const void *vsrc, int len)
+BaseRemoteGDB::encodeXferResponse(const std::string &unencoded,
+ std::string &encoded, size_t offset, size_t unencoded_length) const
{
- char *dst = (char *)vdst;
- const char *src = (const char *)vsrc;
+ if (offset + unencoded_length < unencoded.length())
+ encoded += 'm';
+ else
+ encoded += 'l';
+ encodeBinaryData(unencoded.substr(offset, unencoded_length), encoded);
+}
- while (len--) {
- *dst++ = i2digit(*src >> 4);
- *dst++ = i2digit(*src++);
+bool
+BaseRemoteGDB::cmd_async_step(GdbCommand::Context &ctx)
+{
+ const char *p = ctx.data;
+ hex2i(&p); // Ignore the subcommand byte.
+ if (*p++ == ';') {
+ Addr newPc = hex2i(&p);
+ tc->pcState(newPc);
}
- *dst = '\0';
+ setSingleStep();
+ return false;
}
-// Convert an hex string into a byte array.
-// This returns a pointer to the character following the last valid
-// hex digit. If the string ends in the middle of a byte, NULL is
-// returned.
-const char *
-BaseRemoteGDB::hex2mem(void *vdst, const char *src, int maxlen)
+bool
+BaseRemoteGDB::cmd_step(GdbCommand::Context &ctx)
{
- char *dst = (char *)vdst;
- int msb, lsb;
-
- while (*src && maxlen--) {
- msb = digit2i(*src++);
- if (msb < 0)
- return (src - 1);
- lsb = digit2i(*src++);
- if (lsb < 0)
- return (NULL);
- *dst++ = (msb << 4) | lsb;
+ if (ctx.len) {
+ const char *p = ctx.data;
+ Addr newPc = hex2i(&p);
+ tc->pcState(newPc);
}
- return (src);
+ setSingleStep();
+ return false;
}
-// Convert an hex string into an integer.
-// This returns a pointer to the character following the last valid
-// hex digit.
-Addr
-BaseRemoteGDB::hex2i(const char **srcp)
+bool
+BaseRemoteGDB::cmd_clr_hw_bkpt(GdbCommand::Context &ctx)
{
- const char *src = *srcp;
- Addr r = 0;
- int nibble;
-
- while ((nibble = digit2i(*src)) >= 0) {
- r *= 16;
- r += nibble;
- src++;
+ const char *p = ctx.data;
+ char subcmd = *p++;
+ if (*p++ != ',')
+ throw CmdError("E0D");
+ Addr addr = hex2i(&p);
+ if (*p++ != ',')
+ throw CmdError("E0D");
+ size_t len = hex2i(&p);
+
+ DPRINTF(GDBMisc, "clear %s, addr=%#x, len=%d\n",
+ break_type(subcmd), addr, len);
+
+ switch (subcmd) {
+ case GdbSoftBp:
+ removeSoftBreak(addr, len);
+ break;
+ case GdbHardBp:
+ removeHardBreak(addr, len);
+ break;
+ case GdbWriteWp:
+ case GdbReadWp:
+ case GdbAccWp:
+ default: // unknown
+ throw Unsupported();
}
- *srcp = src;
- return (r);
+ send("OK");
+
+ return true;
}
+bool
+BaseRemoteGDB::cmd_set_hw_bkpt(GdbCommand::Context &ctx)
+{
+ const char *p = ctx.data;
+ char subcmd = *p++;
+ if (*p++ != ',')
+ throw CmdError("E0D");
+ Addr addr = hex2i(&p);
+ if (*p++ != ',')
+ throw CmdError("E0D");
+ size_t len = hex2i(&p);
+
+ DPRINTF(GDBMisc, "set %s, addr=%#x, len=%d\n",
+ break_type(subcmd), addr, len);
+
+ switch (subcmd) {
+ case GdbSoftBp:
+ insertSoftBreak(addr, len);
+ break;
+ case GdbHardBp:
+ insertHardBreak(addr, len);
+ break;
+ case GdbWriteWp:
+ case GdbReadWp:
+ case GdbAccWp:
+ default: // unknown
+ throw Unsupported();
+ }
+ send("OK");
+
+ return true;
+}