systemc: Add a bunch of missing overrides to the systemc headers.
[gem5.git] / src / base / remote_gdb.cc
index 0d3b73b1eeb091e1a1f89ebb9c00d95a06b8ffb4..9a1f7bf2bbacbdc55b2b6015b47af99c3e3e5b5e 100644 (file)
@@ -1,4 +1,17 @@
 /*
+ * 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.
  *
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  * Authors: Nathan Binkert
+ *          Boris Shingarov
  */
 
 /*
- * 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
@@ -38,8 +52,8 @@
  *
  * 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
@@ -51,8 +65,8 @@
  *    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.
@@ -69,7 +83,7 @@
  * 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 "base/remote_gdb.hh"
+
 #include <sys/signal.h>
+#include <unistd.h>
 
+#include <csignal>
+#include <cstdint>
+#include <cstdio>
+#include <sstream>
 #include <string>
-#include <unistd.h>
 
 #include "arch/vtophys.hh"
 #include "base/intmath.hh"
-#include "base/kgdb.h"
-#include "base/remote_gdb.hh"
 #include "base/socket.hh"
 #include "base/trace.hh"
-#include "config/full_system.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/fs_translating_port_proxy.hh"
 #include "mem/port.hh"
+#include "mem/se_translating_port_proxy.hh"
+#include "sim/full_system.hh"
 #include "sim/system.hh"
 
 using namespace std;
 using namespace TheISA;
 
-#ifndef NDEBUG
-vector<RemoteGDB *> debuggers;
-int current_debugger = -1;
+static const char GDBStart = '$';
+static const char GDBEnd = '#';
+static const char GDBGoodP = '+';
+static const char GDBBadP = '-';
 
-void
-debugger()
+vector<BaseRemoteGDB *> debuggers;
+
+class HardBreakpoint : public PCEvent
 {
-    if (current_debugger >= 0 && current_debugger < debuggers.size()) {
-        RemoteGDB *gdb = debuggers[current_debugger];
-        if (!gdb->isattached())
-            gdb->listener->accept();
-        if (gdb->isattached())
-            gdb->trap(ALPHA_KENTRY_IF);
+  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(RemoteGDB *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)
+    {}
+};
+
+// Exception to throw when something isn't supported.
+class Unsupported {};
 
-GDBListener::~GDBListener()
+// 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;
 }
 
-///////////////////////////////////////////////////////////
-//
-//
-//
-int digit2i(char);
-char i2digit(int);
-void mem2hex(void *, const void *, int);
-const char *hex2mem(void *, const char *, int);
-Addr hex2i(const char **);
-
-RemoteGDB::Event::Event(RemoteGDB *g, int fd, int e)
-    : PollEvent(fd, e), gdb(g)
-{}
+enum GdbBreakpointType {
+    GdbSoftBp = '0',
+    GdbHardBp = '1',
+    GdbWriteWp = '2',
+    GdbReadWp = '3',
+    GdbAccWp = '4',
+};
 
-void
-RemoteGDB::Event::process(int revent)
+#ifndef NDEBUG
+const char *
+break_type(char c)
 {
-    if (revent & POLLIN)
-        gdb->trap(ALPHA_KENTRY_IF);
-    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;
+
 }
 
-RemoteGDB::RemoteGDB(System *_system, ThreadContext *c)
-    : event(NULL), listener(NULL), number(-1), fd(-1),
-      active(false), attached(false),
-      system(_system), pmem(_system->physmem), context(c)
+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, 0, sizeof(gdbregs));
+    debuggers.push_back(this);
 }
 
-RemoteGDB::~RemoteGDB()
+BaseRemoteGDB::~BaseRemoteGDB()
 {
-    if (event)
-        delete event;
+    delete connectEvent;
+    delete dataEvent;
 }
 
 string
-RemoteGDB::name()
+BaseRemoteGDB::name()
 {
-    return system->name() + ".remote_gdb";
+    return sys->name() + ".remote_gdb";
 }
 
-bool
-RemoteGDB::isattached()
-{ return attached; }
-
 void
-RemoteGDB::attach(int f)
+BaseRemoteGDB::listen()
 {
-    fd = f;
+    if (ListenSocket::allDisabled()) {
+        warn_once("Sockets disabled, not accepting gdb connections");
+        return;
+    }
 
-    event = new Event(this, fd, POLLIN);
-    pollQueue.schedule(event);
+    while (!listener.listen(_port, true)) {
+        DPRINTF(GDBMisc, "Can't bind port %d\n", _port);
+        _port++;
+    }
 
-    attached = true;
-    DPRINTFN("remote gdb attached\n");
+    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
-RemoteGDB::detach()
+BaseRemoteGDB::connect()
 {
-    attached = false;
-    close(fd);
-    fd = -1;
+    panic_if(!listener.islistening(),
+             "Cannot accept GDB connections if we're not listening!");
 
-    pollQueue.remove(event);
-    DPRINTFN("remote gdb detached\n");
-}
+    int sfd = listener.accept(true);
 
-const char *
-gdb_command(char cmd)
-{
-    switch (cmd) {
-      case KGDB_SIGNAL: return "KGDB_SIGNAL";
-      case KGDB_SET_BAUD: return "KGDB_SET_BAUD";
-      case KGDB_SET_BREAK: return "KGDB_SET_BREAK";
-      case KGDB_CONT: return "KGDB_CONT";
-      case KGDB_ASYNC_CONT: return "KGDB_ASYNC_CONT";
-      case KGDB_DEBUG: return "KGDB_DEBUG";
-      case KGDB_DETACH: return "KGDB_DETACH";
-      case KGDB_REG_R: return "KGDB_REG_R";
-      case KGDB_REG_W: return "KGDB_REG_W";
-      case KGDB_SET_THREAD: return "KGDB_SET_THREAD";
-      case KGDB_CYCLE_STEP: return "KGDB_CYCLE_STEP";
-      case KGDB_SIG_CYCLE_STEP: return "KGDB_SIG_CYCLE_STEP";
-      case KGDB_KILL: return "KGDB_KILL";
-      case KGDB_MEM_W: return "KGDB_MEM_W";
-      case KGDB_MEM_R: return "KGDB_MEM_R";
-      case KGDB_SET_REG: return "KGDB_SET_REG";
-      case KGDB_READ_REG: return "KGDB_READ_REG";
-      case KGDB_QUERY_VAR: return "KGDB_QUERY_VAR";
-      case KGDB_SET_VAR: return "KGDB_SET_VAR";
-      case KGDB_RESET: return "KGDB_RESET";
-      case KGDB_STEP: return "KGDB_STEP";
-      case KGDB_ASYNC_STEP: return "KGDB_ASYNC_STEP";
-      case KGDB_THREAD_ALIVE: return "KGDB_THREAD_ALIVE";
-      case KGDB_TARGET_EXIT: return "KGDB_TARGET_EXIT";
-      case KGDB_BINARY_DLOAD: return "KGDB_BINARY_DLOAD";
-      case KGDB_CLR_HW_BKPT: return "KGDB_CLR_HW_BKPT";
-      case KGDB_SET_HW_BKPT: return "KGDB_SET_HW_BKPT";
-      case KGDB_START: return "KGDB_START";
-      case KGDB_END: return "KGDB_END";
-      case KGDB_GOODP: return "KGDB_GOODP";
-      case KGDB_BADP: return "KGDB_BADP";
-      default: return "KGDB_UNKNOWN";
+    if (sfd != -1) {
+        if (isAttached())
+            close(sfd);
+        else
+            attach(sfd);
     }
 }
 
-///////////////////////////////////////////////////////////
-// RemoteGDB::acc
-//
-//     Determine if the mapping at va..(va+len) is valid.
-//
-bool
-RemoteGDB::acc(Addr va, size_t len)
-{
-    Addr last_va;
-
-    va = TheISA::TruncPage(va);
-    last_va = TheISA::RoundPage(va + len);
-
-    do  {
-        if (TheISA::IsK0Seg(va)) {
-            if (va < (TheISA::K0SegBase + pmem->size())) {
-                DPRINTF(GDBAcc, "acc:   Mapping is valid  K0SEG <= "
-                        "%#x < K0SEG + size\n", va);
-                return true;
-            } else {
-                DPRINTF(GDBAcc, "acc:   Mapping invalid %#x > K0SEG + size\n",
-                        va);
-                return false;
-            }
-        }
-
-    /**
-     * This code says that all accesses to palcode (instruction and data)
-     * are valid since there isn't a va->pa mapping because palcode is
-     * accessed physically. At some point this should probably be cleaned up
-     * but there is no easy way to do it.
-     */
-
-        if (AlphaISA::PcPAL(va) || va < 0x10000)
-            return true;
-
-        Addr ptbr = context->readMiscReg(AlphaISA::IPR_PALtemp20);
-        TheISA::PageTableEntry pte = TheISA::kernel_pte_lookup(context->getPhysPort(), ptbr, va);
-        if (!pte.valid()) {
-            DPRINTF(GDBAcc, "acc:   %#x pte is invalid\n", va);
-            return false;
-        }
-        va += TheISA::PageBytes;
-    } while (va < last_va);
-
-    DPRINTF(GDBAcc, "acc:   %#x mapping is valid\n", va);
-    return true;
-}
-
-///////////////////////////////////////////////////////////
-// RemoteGDB::signal
-//
-//     Translate a trap number into a Unix-compatible signal number.
-//     (GDB only understands Unix signal numbers.)
-//
 int
-RemoteGDB::signal(int type)
+BaseRemoteGDB::port() const
 {
-    switch (type) {
-      case ALPHA_KENTRY_INT:
-        return (SIGTRAP);
-
-      case ALPHA_KENTRY_UNA:
-        return (SIGBUS);
-
-      case ALPHA_KENTRY_ARITH:
-        return (SIGFPE);
-
-      case ALPHA_KENTRY_IF:
-        return (SIGILL);
-
-      case ALPHA_KENTRY_MM:
-        return (SIGSEGV);
-
-      default:
-        panic("unknown signal type");
-        return 0;
-    }
+    panic_if(!listener.islistening(),
+             "Remote GDB port is unknown until listen() has been called.\n");
+    return _port;
 }
 
-///////////////////////////////////////////////////////////
-// RemoteGDB::getregs
-//
-//     Translate the kernel debugger register format into
-//     the GDB register format.
 void
-RemoteGDB::getregs()
+BaseRemoteGDB::attach(int f)
 {
-    memset(gdbregs, 0, sizeof(gdbregs));
+    fd = f;
 
-    gdbregs[KGDB_REG_PC] = context->readPC();
+    dataEvent = new DataEvent(this, fd, POLLIN);
+    pollQueue.schedule(dataEvent);
 
-    // @todo: Currently this is very Alpha specific.
-    if (AlphaISA::PcPAL(gdbregs[KGDB_REG_PC])) {
-        for (int i = 0; i < TheISA::NumIntArchRegs; ++i) {
-            gdbregs[i] = context->readIntReg(AlphaISA::reg_redir[i]);
-        }
-    } else {
-        for (int i = 0; i < TheISA::NumIntArchRegs; ++i) {
-            gdbregs[i] = context->readIntReg(i);
-        }
-    }
-
-#ifdef KGDB_FP_REGS
-    for (int i = 0; i < TheISA::NumFloatArchRegs; ++i) {
-        gdbregs[i + KGDB_REG_F0] = context->readFloatRegBits(i);
-    }
-#endif
+    attached = true;
+    DPRINTFN("remote gdb attached\n");
 }
 
-///////////////////////////////////////////////////////////
-// RemoteGDB::setregs
-//
-//     Translate the GDB register format into the kernel
-//     debugger register format.
-//
 void
-RemoteGDB::setregs()
+BaseRemoteGDB::detach()
 {
-    // @todo: Currently this is very Alpha specific.
-    if (AlphaISA::PcPAL(gdbregs[KGDB_REG_PC])) {
-        for (int i = 0; i < TheISA::NumIntArchRegs; ++i) {
-            context->setIntReg(AlphaISA::reg_redir[i], gdbregs[i]);
-        }
-    } else {
-        for (int i = 0; i < TheISA::NumIntArchRegs; ++i) {
-            context->setIntReg(i, gdbregs[i]);
-        }
-    }
+    attached = false;
+    active = false;
+    clearSingleStep();
+    close(fd);
+    fd = -1;
 
-#ifdef KGDB_FP_REGS
-    for (int i = 0; i < TheISA::NumFloatArchRegs; ++i) {
-        context->setFloatRegBits(i, gdbregs[i + KGDB_REG_F0]);
-    }
-#endif
-    context->setPC(gdbregs[KGDB_REG_PC]);
+    pollQueue.remove(dataEvent);
+    DPRINTFN("remote gdb detached\n");
 }
 
-void
-RemoteGDB::setTempBreakpoint(TempBreakpoint &bkpt, Addr addr)
+// 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)
 {
-    DPRINTF(GDBMisc, "setTempBreakpoint: addr=%#x\n", addr);
 
-    bkpt.address = addr;
-    insertHardBreak(addr, 4);
-}
+    if (!attached)
+        return false;
 
-void
-RemoteGDB::clearTempBreakpoint(TempBreakpoint &bkpt)
-{
-    DPRINTF(GDBMisc, "setTempBreakpoint: addr=%#x\n",
-            bkpt.address);
+    DPRINTF(GDBMisc, "trap: PC=%s\n", tc->pcState());
 
+    clearSingleStep();
 
-    removeHardBreak(bkpt.address, 4);
-    bkpt.address = 0;
-}
+    /*
+     * 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());
+    }
 
-void
-RemoteGDB::clearSingleStep()
-{
-    DPRINTF(GDBMisc, "clearSingleStep bt_addr=%#x nt_addr=%#x\n",
-            takenBkpt.address, notTakenBkpt.address);
+    // 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;
 
-    if (takenBkpt.address != 0)
-        clearTempBreakpoint(takenBkpt);
+        } 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.");
+        }
+    }
 
-    if (notTakenBkpt.address != 0)
-        clearTempBreakpoint(notTakenBkpt);
+    return true;
 }
 
 void
-RemoteGDB::setSingleStep()
+BaseRemoteGDB::incomingData(int revent)
 {
-    Addr pc = context->readPC();
-    Addr npc, bpc;
-    bool set_bt = false;
-
-    npc = pc + sizeof(MachInst);
-
-    // User was stopped at pc, e.g. the instruction at pc was not
-    // executed.
-    MachInst inst = read<MachInst>(pc);
-    StaticInstPtr si(inst);
-    if (si->hasBranchTarget(pc, context, bpc)) {
-        // Don't bother setting a breakpoint on the taken branch if it
-        // is the same as the next pc
-        if (bpc != npc)
-            set_bt = true;
+    if (trapEvent.scheduled()) {
+        warn("GDB trap event has already been scheduled!");
+        return;
     }
 
-    DPRINTF(GDBMisc, "setSingleStep bt_addr=%#x nt_addr=%#x\n",
-            takenBkpt.address, notTakenBkpt.address);
-
-    setTempBreakpoint(notTakenBkpt, npc);
-
-    if (set_bt)
-        setTempBreakpoint(takenBkpt, bpc);
+    if (revent & POLLIN) {
+        trapEvent.type(SIGILL);
+        scheduleInstCommitEvent(&trapEvent, 0);
+    } else if (revent & POLLNVAL) {
+        descheduleInstCommitEvent(&trapEvent);
+        detach();
+    }
 }
 
-/////////////////////////
-//
-//
-
 uint8_t
-RemoteGDB::getbyte()
+BaseRemoteGDB::getbyte()
 {
     uint8_t b;
-    ::read(fd, &b, 1);
-    return b;
-}
+    if (::read(fd, &b, sizeof(b)) == sizeof(b))
+        return b;
 
-void
-RemoteGDB::putbyte(uint8_t b)
-{
-    ::write(fd, &b, 1);
+    throw BadClient("Couldn't read data from debugger.");
 }
 
-// Send a packet to gdb
 void
-RemoteGDB::send(const char *bp)
+BaseRemoteGDB::putbyte(uint8_t b)
 {
-    const char *p;
-    uint8_t csum, c;
-
-    DPRINTF(GDBSend, "send:  %s\n", bp);
+    if (::write(fd, &b, sizeof(b)) == sizeof(b))
+        return;
 
-    do {
-        p = bp;
-        putbyte(KGDB_START);
-        for (csum = 0; (c = *p); p++) {
-            putbyte(c);
-            csum += c;
-        }
-        putbyte(KGDB_END);
-        putbyte(i2digit(csum >> 4));
-        putbyte(i2digit(csum));
-    } while ((c = getbyte() & 0x7f) == KGDB_BADP);
+    throw BadClient("Couldn't write data to the debugger.");
 }
 
 // Receive a packet from gdb
-int
-RemoteGDB::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;
-        while ((c = getbyte()) != KGDB_START)
-            ;
-
-        while ((c = getbyte()) != KGDB_END && 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);
         }
-        csum &= 0xff;
-        *p = '\0';
 
-        if (len >= maxlen) {
-            putbyte(KGDB_BADP);
-            continue;
-        }
+        // Mask the check sum.
+        csum &= 0xff;
 
+        // 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 (csum == 0) {
-            putbyte(KGDB_GOODP);
+            // 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;
-                bcopy(bp + 3, bp, len);
+                auto begin = std::begin(bp);
+                bp.erase(begin, std::next(begin, 3));
             }
             break;
         }
-        putbyte(KGDB_BADP);
+        // 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
-RemoteGDB::read(Addr vaddr, size_t size, char *data)
+BaseRemoteGDB::read(Addr vaddr, size_t size, char *data)
 {
     static Addr lastaddr = 0;
     static size_t lastsize = 0;
@@ -642,9 +618,8 @@ RemoteGDB::read(Addr vaddr, size_t size, char *data)
 
     DPRINTF(GDBRead, "read:  addr=%#x, size=%d", vaddr, size);
 
-    VirtualPort *vp = context->getVirtPort(context);
-    vp->readBlob(vaddr, (uint8_t*)data, size);
-    context->delVirtPort(vp);
+    PortProxy &proxy = tc->getVirtProxy();
+    proxy.readBlob(vaddr, data, size);
 
 #if TRACING_ON
     if (DTRACE(GDBRead)) {
@@ -662,7 +637,7 @@ RemoteGDB::read(Addr vaddr, size_t size, char *data)
 
 // Write bytes to kernel address space for debugger.
 bool
-RemoteGDB::write(Addr vaddr, size_t size, const char *data)
+BaseRemoteGDB::write(Addr vaddr, size_t size, const char *data)
 {
     static Addr lastaddr = 0;
     static size_t lastsize = 0;
@@ -681,500 +656,478 @@ RemoteGDB::write(Addr vaddr, size_t size, const char *data)
         } else
             DPRINTFNR("\n");
     }
-    VirtualPort *vp = context->getVirtPort(context);
-    vp->writeBlob(vaddr, (uint8_t*)data, size);
-    context->delVirtPort(vp);
-
-#ifdef IMB
-    alpha_pal_imb();
-#endif
+    PortProxy &proxy = tc->getVirtProxy();
+    proxy.writeBlob(vaddr, data, size);
 
     return true;
 }
 
-
-PCEventQueue *RemoteGDB::getPcEventQueue()
+void
+BaseRemoteGDB::singleStep()
 {
-    return &system->pcEventQueue;
+    if (!singleStepEvent.scheduled())
+        scheduleInstCommitEvent(&singleStepEvent, 1);
+    trap(SIGTRAP);
 }
 
-
-RemoteGDB::HardBreakpoint::HardBreakpoint(RemoteGDB *_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
-RemoteGDB::HardBreakpoint::process(ThreadContext *tc)
+BaseRemoteGDB::setSingleStep()
 {
-    DPRINTF(GDBMisc, "handling hardware breakpoint at %#x\n", pc());
-
-    if (tc == gdb->context)
-        gdb->trap(ALPHA_KENTRY_INT);
+    if (!singleStepEvent.scheduled())
+        scheduleInstCommitEvent(&singleStepEvent, 1);
 }
 
-bool
-RemoteGDB::insertSoftBreak(Addr addr, size_t len)
+void
+BaseRemoteGDB::insertSoftBreak(Addr addr, size_t len)
 {
-    if (len != sizeof(MachInst))
-        panic("invalid length\n");
+    if (!checkBpLen(len))
+        throw BadClient("Invalid breakpoint length\n");
 
     return insertHardBreak(addr, len);
 }
 
-bool
-RemoteGDB::removeSoftBreak(Addr addr, size_t len)
+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
-RemoteGDB::insertHardBreak(Addr addr, size_t len)
+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
-RemoteGDB::removeHardBreak(Addr addr, size_t len)
+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;
 }
 
-const char *
-break_type(char c)
+void
+BaseRemoteGDB::clearTempBreakpoint(Addr &bkpt)
 {
-    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";
-    }
+    DPRINTF(GDBMisc, "setTempBreakpoint: addr=%#x\n", bkpt);
+    removeHardBreak(bkpt, sizeof(TheISA::MachInst));
+    bkpt = 0;
 }
 
-// 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
-RemoteGDB::trap(int type)
+void
+BaseRemoteGDB::setTempBreakpoint(Addr bkpt)
 {
-    uint64_t val;
-    size_t datalen, len;
-    char data[KGDB_BUFLEN + 1];
-    char buffer[sizeof(gdbregs) * 2 + 256];
-    char temp[KGDB_BUFLEN];
-    const char *p;
-    char command, subcmd;
-    string var;
-    bool ret;
-
-    if (!attached)
-        return false;
-
-    DPRINTF(GDBMisc, "trap: PC=%#x NPC=%#x\n",
-            context->readPC(), context->readNextPC());
-
-    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.
-        snprintf((char *)buffer, sizeof(buffer), "S%02x", signal(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 KGDB_SIGNAL:
-            // 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", signal(type));
-            send(buffer);
-            continue;
-
-          case KGDB_REG_R:
-            if (2 * sizeof(gdbregs) > sizeof(buffer))
-                panic("buffer too small");
-
-            mem2hex(buffer, gdbregs, sizeof(gdbregs));
-            send(buffer);
-            continue;
-
-          case KGDB_REG_W:
-            p = hex2mem(gdbregs, p, sizeof(gdbregs));
-            if (p == NULL || *p != '\0')
-                send("E01");
-            else {
-                setregs();
-                send("OK");
-            }
-            continue;
-
-#if 0
-          case KGDB_SET_REG:
-            val = hex2i(&p);
-            if (*p++ != '=') {
-                send("E01");
-                continue;
-            }
-            if (val < 0 && val >= KGDB_NUMREGS) {
-                send("E01");
-                continue;
-            }
-
-            gdbregs[val] = hex2i(&p);
-            setregs();
-            send("OK");
-
-            continue;
-#endif
-
-          case KGDB_MEM_R:
-            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)) {
-              mem2hex(temp, buffer, len);
-              send(temp);
-            } else {
-              send("E05");
-            }
-            continue;
+    DPRINTF(GDBMisc, "setTempBreakpoint: addr=%#x\n", bkpt);
+    insertHardBreak(bkpt, sizeof(TheISA::MachInst));
+}
 
-          case KGDB_MEM_W:
-            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 KGDB_SET_THREAD:
-            subcmd = *p++;
-            val = hex2i(&p);
-            if (val == 0)
-                send("OK");
-            else
-                send("E01");
-            continue;
-
-          case KGDB_DETACH:
-          case KGDB_KILL:
-            active = false;
-            clearSingleStep();
-            detach();
-            goto out;
-
-          case KGDB_ASYNC_CONT:
-            subcmd = hex2i(&p);
-            if (*p++ == ';') {
-                val = hex2i(&p);
-                context->setPC(val);
-                context->setNextPC(val + sizeof(MachInst));
-            }
-            clearSingleStep();
-            goto out;
-
-          case KGDB_CONT:
-            if (p - data < datalen) {
-                val = hex2i(&p);
-                context->setPC(val);
-                context->setNextPC(val + sizeof(MachInst));
-            }
-            clearSingleStep();
-            goto out;
-
-          case KGDB_ASYNC_STEP:
-            subcmd = hex2i(&p);
-            if (*p++ == ';') {
-                val = hex2i(&p);
-                context->setPC(val);
-                context->setNextPC(val + sizeof(MachInst));
-            }
-            setSingleStep();
-            goto out;
-
-          case KGDB_STEP:
-            if (p - data < datalen) {
-                val = hex2i(&p);
-                context->setPC(val);
-                context->setNextPC(val + sizeof(MachInst));
-            }
-            setSingleStep();
-            goto out;
+void
+BaseRemoteGDB::scheduleInstCommitEvent(Event *ev, int delta)
+{
+    // Here "ticks" aren't simulator ticks which measure time, they're
+    // instructions committed by the CPU.
+    tc->scheduleInstCountEvent(ev, tc->getCurrentInstCount() + delta);
+}
 
-          case KGDB_CLR_HW_BKPT:
-            subcmd = *p++;
-            if (*p++ != ',') send("E0D");
-            val = hex2i(&p);
-            if (*p++ != ',') send("E0D");
-            len = hex2i(&p);
+void
+BaseRemoteGDB::descheduleInstCommitEvent(Event *ev)
+{
+    if (ev->scheduled())
+        tc->descheduleInstCountEvent(ev);
+}
 
-            DPRINTF(GDBMisc, "clear %s, addr=%#x, len=%d\n",
-                    break_type(subcmd), val, len);
+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 } },
+};
 
-            ret = false;
+bool
+BaseRemoteGDB::checkBpLen(size_t len)
+{
+    return len == sizeof(MachInst);
+}
 
-            switch (subcmd) {
-              case '0': // software breakpoint
-                ret = removeSoftBreak(val, len);
-                break;
+bool
+BaseRemoteGDB::cmd_unsupported(GdbCommand::Context &ctx)
+{
+    DPRINTF(GDBMisc, "Unsupported command: %s\n", ctx.cmd->name);
+    DDUMP(GDBMisc, ctx.data, ctx.len);
+    throw Unsupported();
+}
 
-              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;
-            }
+bool
+BaseRemoteGDB::cmd_signal(GdbCommand::Context &ctx)
+{
+    send(csprintf("S%02x", ctx.type).c_str());
+    return true;
+}
 
-            send(ret ? "OK" : "E0C");
-            continue;
+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;
+}
 
-          case KGDB_SET_HW_BKPT:
-            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 KGDB_QUERY_VAR:
-            var = string(p, datalen - 1);
-            if (var == "C")
-                send("QC0");
-            else
-                send("");
-            continue;
-
-          case KGDB_SET_BAUD:
-          case KGDB_SET_BREAK:
-          case KGDB_DEBUG:
-          case KGDB_CYCLE_STEP:
-          case KGDB_SIG_CYCLE_STEP:
-          case KGDB_READ_REG:
-          case KGDB_SET_VAR:
-          case KGDB_RESET:
-          case KGDB_THREAD_ALIVE:
-          case KGDB_TARGET_EXIT:
-          case KGDB_BINARY_DLOAD:
-            // 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:
     return true;
 }
 
-// Convert a hex digit into an integer.
-// This returns -1 if the argument passed is no valid hex digit.
-int
-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
-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
-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 *
-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
-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;
+}