sim, arch, base: Refactor the base remote GDB class.

Fold the GDBListener class into the main BaseRemoteGDB class, move
around a bunch of functions, convert a lot of internal functions to
be private, move some functions into the .cc, make some functions
non-virtual which didn't really need to be overridden.

Change-Id: Id0832b730b0fdfb2eababa5067e72c66de1c147d
Reviewed-on: https://gem5-review.googlesource.com/7422
Reviewed-by: Jason Lowe-Power <jason@lowepower.com>
Reviewed-by: Gabe Black <gabeblack@google.com>
Maintainer: Gabe Black <gabeblack@google.com>
diff --git a/src/arch/alpha/remote_gdb.cc b/src/arch/alpha/remote_gdb.cc
index a9ec4cf..f3eafc0 100644
--- a/src/arch/alpha/remote_gdb.cc
+++ b/src/arch/alpha/remote_gdb.cc
@@ -144,9 +144,11 @@
 using namespace std;
 using namespace AlphaISA;
 
-RemoteGDB::RemoteGDB(System *_system, ThreadContext *tc)
-    : BaseRemoteGDB(_system, tc)
+RemoteGDB::RemoteGDB(System *_system, ThreadContext *tc, int _port)
+    : BaseRemoteGDB(_system, tc, _port)
 {
+    warn_once("Breakpoints do not work in Alpha PAL mode.\n"
+              "      See PCEventQueue::doService() in cpu/pc_event.cc.\n");
 }
 
 /*
@@ -165,7 +167,7 @@
 
     do  {
         if (IsK0Seg(va)) {
-            if (va < (K0SegBase + system->memSize())) {
+            if (va < (K0SegBase + system()->memSize())) {
                 DPRINTF(GDBAcc, "acc:   Mapping is valid  K0SEG <= "
                         "%#x < K0SEG + size\n", va);
                 return true;
@@ -187,9 +189,9 @@
         if (PcPAL(va) || va < 0x10000)
             return true;
 
-        Addr ptbr = context->readMiscRegNoEffect(IPR_PALtemp20);
+        Addr ptbr = context()->readMiscRegNoEffect(IPR_PALtemp20);
         PageTableEntry pte =
-            kernel_pte_lookup(context->getPhysProxy(), ptbr, va);
+            kernel_pte_lookup(context()->getPhysProxy(), ptbr, va);
         if (!pte.valid()) {
             DPRINTF(GDBAcc, "acc:   %#x pte is invalid\n", va);
             return false;
@@ -247,31 +249,10 @@
     context->pcState(r.pc);
 }
 
-// Write bytes to kernel address space for debugger.
-bool
-RemoteGDB::write(Addr vaddr, size_t size, const char *data)
+
+BaseGdbRegCache*
+RemoteGDB::gdbRegs()
 {
-    if (BaseRemoteGDB::write(vaddr, size, data)) {
-#ifdef IMB
-        alpha_pal_imb();
-#endif
-        return true;
-    } else {
-        return false;
-    }
-}
-
-
-void
-RemoteGDB::insertHardBreak(Addr addr, size_t len)
-{
-    warn_once("Breakpoints do not work in Alpha PAL mode.\n"
-              "      See PCEventQueue::doService() in cpu/pc_event.cc.\n");
-    BaseRemoteGDB::insertHardBreak(addr, len);
-}
-
-RemoteGDB::BaseGdbRegCache*
-RemoteGDB::gdbRegs() {
-            return new AlphaGdbRegCache(this);
+    return new AlphaGdbRegCache(this);
 }
 
diff --git a/src/arch/alpha/remote_gdb.hh b/src/arch/alpha/remote_gdb.hh
index c8ed709..1e99b5f 100644
--- a/src/arch/alpha/remote_gdb.hh
+++ b/src/arch/alpha/remote_gdb.hh
@@ -51,9 +51,6 @@
   protected:
     // Machine memory
     bool acc(Addr addr, size_t len) override;
-    bool write(Addr addr, size_t size, const char *data) override;
-
-    void insertHardBreak(Addr addr, size_t len) override;
 
     class AlphaGdbRegCache : public BaseGdbRegCache
     {
@@ -70,11 +67,15 @@
         size_t size() const { return sizeof(r); }
         void getRegs(ThreadContext*);
         void setRegs(ThreadContext*) const;
-        const std::string name() const { return gdb->name() + ".AlphaGdbRegCache"; }
+        const std::string
+        name() const
+        {
+            return gdb->name() + ".AlphaGdbRegCache";
+        }
     };
 
   public:
-    RemoteGDB(System *system, ThreadContext *context);
+    RemoteGDB(System *system, ThreadContext *context, int _port);
     BaseGdbRegCache *gdbRegs() override;
 };
 
diff --git a/src/arch/arm/remote_gdb.cc b/src/arch/arm/remote_gdb.cc
index 6dc68b1..f127341 100644
--- a/src/arch/arm/remote_gdb.cc
+++ b/src/arch/arm/remote_gdb.cc
@@ -164,8 +164,8 @@
 using namespace std;
 using namespace ArmISA;
 
-RemoteGDB::RemoteGDB(System *_system, ThreadContext *tc)
-    : BaseRemoteGDB(_system, tc), regCache32(this), regCache64(this)
+RemoteGDB::RemoteGDB(System *_system, ThreadContext *tc, int _port)
+    : BaseRemoteGDB(_system, tc, _port), regCache32(this), regCache64(this)
 {
 }
 
@@ -177,7 +177,7 @@
 {
     if (FullSystem) {
         for (ChunkGenerator gen(va, len, PageBytes); !gen.done(); gen.next()) {
-            if (!virtvalid(context, gen.addr())) {
+            if (!virtvalid(context(), gen.addr())) {
                 DPRINTF(GDBAcc, "acc:   %#x mapping is invalid\n", va);
                 return false;
             }
@@ -189,7 +189,7 @@
         TlbEntry entry;
         //Check to make sure the first byte is mapped into the processes address
         //space.
-        if (context->getProcessPtr()->pTable->lookup(va, entry))
+        if (context()->getProcessPtr()->pTable->lookup(va, entry))
             return true;
         return false;
     }
@@ -301,10 +301,10 @@
     context->setMiscRegNoEffect(MISCREG_CPSR, r.cpsr);
 }
 
-RemoteGDB::BaseGdbRegCache*
+BaseGdbRegCache*
 RemoteGDB::gdbRegs()
 {
-    if (inAArch64(context))
+    if (inAArch64(context()))
         return &regCache64;
     else
         return &regCache32;
diff --git a/src/arch/arm/remote_gdb.hh b/src/arch/arm/remote_gdb.hh
index 328fbad..e5d50ee 100644
--- a/src/arch/arm/remote_gdb.hh
+++ b/src/arch/arm/remote_gdb.hh
@@ -115,7 +115,7 @@
     AArch64GdbRegCache regCache64;
 
   public:
-    RemoteGDB(System *_system, ThreadContext *tc);
+    RemoteGDB(System *_system, ThreadContext *tc, int _port);
     BaseGdbRegCache *gdbRegs();
 };
 } // namespace ArmISA
diff --git a/src/arch/mips/remote_gdb.cc b/src/arch/mips/remote_gdb.cc
index 2cc2d77..e17f8fd 100644
--- a/src/arch/mips/remote_gdb.cc
+++ b/src/arch/mips/remote_gdb.cc
@@ -151,8 +151,8 @@
 using namespace std;
 using namespace MipsISA;
 
-RemoteGDB::RemoteGDB(System *_system, ThreadContext *tc)
-    : BaseRemoteGDB(_system, tc), regCache(this)
+RemoteGDB::RemoteGDB(System *_system, ThreadContext *tc, int _port)
+    : BaseRemoteGDB(_system, tc, _port), regCache(this)
 {
 }
 
@@ -168,7 +168,7 @@
     if (FullSystem)
         panic("acc not implemented for MIPS FS!");
     else
-        return context->getProcessPtr()->pTable->lookup(va, entry);
+        return context()->getProcessPtr()->pTable->lookup(va, entry);
 }
 
 void
@@ -205,7 +205,8 @@
     context->setFloatRegBits(FLOATREG_FIR, r.fir);
 }
 
-RemoteGDB::BaseGdbRegCache*
-RemoteGDB::gdbRegs() {
+BaseGdbRegCache*
+RemoteGDB::gdbRegs()
+{
     return &regCache;
 }
diff --git a/src/arch/mips/remote_gdb.hh b/src/arch/mips/remote_gdb.hh
index fba55d8..169754a 100644
--- a/src/arch/mips/remote_gdb.hh
+++ b/src/arch/mips/remote_gdb.hh
@@ -80,7 +80,7 @@
     MipsGdbRegCache regCache;
 
   public:
-    RemoteGDB(System *_system, ThreadContext *tc);
+    RemoteGDB(System *_system, ThreadContext *tc, int _port);
     BaseGdbRegCache *gdbRegs();
 };
 
diff --git a/src/arch/power/remote_gdb.cc b/src/arch/power/remote_gdb.cc
index c85aa38..7bdd3ba 100644
--- a/src/arch/power/remote_gdb.cc
+++ b/src/arch/power/remote_gdb.cc
@@ -151,8 +151,8 @@
 using namespace std;
 using namespace PowerISA;
 
-RemoteGDB::RemoteGDB(System *_system, ThreadContext *tc)
-    : BaseRemoteGDB(_system, tc), regCache(this)
+RemoteGDB::RemoteGDB(System *_system, ThreadContext *tc, int _port)
+    : BaseRemoteGDB(_system, tc, _port), regCache(this)
 {
 }
 
@@ -171,7 +171,7 @@
     if (FullSystem)
         panic("acc not implemented for POWER FS!");
     else
-        return context->getProcessPtr()->pTable->lookup(va, entry);
+        return context()->getProcessPtr()->pTable->lookup(va, entry);
 }
 
 void
@@ -216,8 +216,9 @@
     context->setIntReg(INTREG_XER, betoh(r.xer));
 }
 
-RemoteGDB::BaseGdbRegCache*
-RemoteGDB::gdbRegs() {
+BaseGdbRegCache*
+RemoteGDB::gdbRegs()
+{
     return &regCache;
 }
 
diff --git a/src/arch/power/remote_gdb.hh b/src/arch/power/remote_gdb.hh
index 9fefb34..2894fc1 100644
--- a/src/arch/power/remote_gdb.hh
+++ b/src/arch/power/remote_gdb.hh
@@ -79,7 +79,7 @@
     PowerGdbRegCache regCache;
 
   public:
-    RemoteGDB(System *_system, ThreadContext *tc);
+    RemoteGDB(System *_system, ThreadContext *tc, int _port);
     BaseGdbRegCache *gdbRegs();
 };
 
diff --git a/src/arch/riscv/remote_gdb.cc b/src/arch/riscv/remote_gdb.cc
index 3488c81..4f423fc 100644
--- a/src/arch/riscv/remote_gdb.cc
+++ b/src/arch/riscv/remote_gdb.cc
@@ -148,8 +148,8 @@
 using namespace std;
 using namespace RiscvISA;
 
-RemoteGDB::RemoteGDB(System *_system, ThreadContext *tc)
-    : BaseRemoteGDB(_system, tc), regCache(this)
+RemoteGDB::RemoteGDB(System *_system, ThreadContext *tc, int _port)
+    : BaseRemoteGDB(_system, tc, _port), regCache(this)
 {
 }
 
@@ -160,7 +160,7 @@
     if (FullSystem)
         panic("acc not implemented for RISCV FS!");
     else
-        return context->getProcessPtr()->pTable->lookup(va, entry);
+        return context()->getProcessPtr()->pTable->lookup(va, entry);
 }
 
 void
@@ -199,7 +199,8 @@
         context->setMiscReg(i, r.csr[i - ExplicitCSRs]);
 }
 
-RemoteGDB::BaseGdbRegCache*
-RemoteGDB::gdbRegs() {
+BaseGdbRegCache*
+RemoteGDB::gdbRegs()
+{
     return &regCache;
 }
diff --git a/src/arch/riscv/remote_gdb.hh b/src/arch/riscv/remote_gdb.hh
index 4b9d6e7..739cb5a 100644
--- a/src/arch/riscv/remote_gdb.hh
+++ b/src/arch/riscv/remote_gdb.hh
@@ -85,7 +85,7 @@
     RiscvGdbRegCache regCache;
 
   public:
-    RemoteGDB(System *_system, ThreadContext *tc);
+    RemoteGDB(System *_system, ThreadContext *tc, int _port);
     BaseGdbRegCache *gdbRegs();
 };
 
diff --git a/src/arch/sparc/remote_gdb.cc b/src/arch/sparc/remote_gdb.cc
index 3f4df0d..baec0e7 100644
--- a/src/arch/sparc/remote_gdb.cc
+++ b/src/arch/sparc/remote_gdb.cc
@@ -147,8 +147,8 @@
 using namespace std;
 using namespace SparcISA;
 
-RemoteGDB::RemoteGDB(System *_system, ThreadContext *c)
-    : BaseRemoteGDB(_system, c), regCache32(this), regCache64(this)
+RemoteGDB::RemoteGDB(System *_system, ThreadContext *c, int _port)
+    : BaseRemoteGDB(_system, c, _port), regCache32(this), regCache64(this)
 {}
 
 ///////////////////////////////////////////////////////////
@@ -170,7 +170,7 @@
         TlbEntry entry;
         // Check to make sure the first byte is mapped into the processes
         // address space.
-        if (context->getProcessPtr()->pTable->lookup(va, entry))
+        if (context()->getProcessPtr()->pTable->lookup(va, entry))
             return true;
         return false;
     }
@@ -244,10 +244,10 @@
 }
 
 
-RemoteGDB::BaseGdbRegCache*
+BaseGdbRegCache*
 RemoteGDB::gdbRegs()
 {
-    PSTATE pstate = context->readMiscReg(MISCREG_PSTATE);
+    PSTATE pstate = context()->readMiscReg(MISCREG_PSTATE);
     if (pstate.am) {
         return &regCache32;
     } else {
diff --git a/src/arch/sparc/remote_gdb.hh b/src/arch/sparc/remote_gdb.hh
index 653f0b1..f0ddd54 100644
--- a/src/arch/sparc/remote_gdb.hh
+++ b/src/arch/sparc/remote_gdb.hh
@@ -69,7 +69,11 @@
         size_t size() const { return sizeof(r); }
         void getRegs(ThreadContext*);
         void setRegs(ThreadContext*) const;
-        const std::string name() const { return gdb->name() + ".SPARCGdbRegCache"; }
+        const std::string
+        name() const
+        {
+            return gdb->name() + ".SPARCGdbRegCache";
+        }
     };
 
     class SPARC64GdbRegCache : public BaseGdbRegCache
@@ -91,14 +95,18 @@
         size_t size() const { return sizeof(r); }
         void getRegs(ThreadContext*);
         void setRegs(ThreadContext*) const;
-        const std::string name() const { return gdb->name() + ".SPARC64GdbRegCache"; }
+        const std::string
+        name() const
+        {
+            return gdb->name() + ".SPARC64GdbRegCache";
+        }
     };
 
     SPARCGdbRegCache regCache32;
     SPARC64GdbRegCache regCache64;
 
   public:
-    RemoteGDB(System *_system, ThreadContext *tc);
+    RemoteGDB(System *_system, ThreadContext *tc, int _port);
     BaseGdbRegCache *gdbRegs();
 };
 } // namespace SparcISA
diff --git a/src/arch/x86/remote_gdb.cc b/src/arch/x86/remote_gdb.cc
index a6fdabd..175cabb 100644
--- a/src/arch/x86/remote_gdb.cc
+++ b/src/arch/x86/remote_gdb.cc
@@ -64,8 +64,8 @@
 using namespace std;
 using namespace X86ISA;
 
-RemoteGDB::RemoteGDB(System *_system, ThreadContext *c) :
-    BaseRemoteGDB(_system, c), regCache32(this), regCache64(this)
+RemoteGDB::RemoteGDB(System *_system, ThreadContext *c, int _port) :
+    BaseRemoteGDB(_system, c, _port), regCache32(this), regCache64(this)
 {}
 
 bool
@@ -73,9 +73,9 @@
 {
     if (FullSystem) {
         Walker *walker = dynamic_cast<TLB *>(
-            context->getDTBPtr())->getWalker();
+            context()->getDTBPtr())->getWalker();
         unsigned logBytes;
-        Fault fault = walker->startFunctional(context, va, logBytes,
+        Fault fault = walker->startFunctional(context(), va, logBytes,
                                               BaseTLB::Read);
         if (fault != NoFault)
             return false;
@@ -84,19 +84,19 @@
         if ((va & ~mask(logBytes)) == (endVa & ~mask(logBytes)))
             return true;
 
-        fault = walker->startFunctional(context, endVa, logBytes,
+        fault = walker->startFunctional(context(), endVa, logBytes,
                                         BaseTLB::Read);
         return fault == NoFault;
     } else {
         TlbEntry entry;
-        return context->getProcessPtr()->pTable->lookup(va, entry);
+        return context()->getProcessPtr()->pTable->lookup(va, entry);
     }
 }
 
-RemoteGDB::BaseGdbRegCache*
+BaseGdbRegCache*
 RemoteGDB::gdbRegs()
 {
-    HandyM5Reg m5reg = context->readMiscRegNoEffect(MISCREG_M5_REG);
+    HandyM5Reg m5reg = context()->readMiscRegNoEffect(MISCREG_M5_REG);
     if (m5reg.submode == SixtyFourBitMode)
         return &regCache64;
     else
diff --git a/src/arch/x86/remote_gdb.hh b/src/arch/x86/remote_gdb.hh
index f1cbcbe..0cae4fe 100644
--- a/src/arch/x86/remote_gdb.hh
+++ b/src/arch/x86/remote_gdb.hh
@@ -143,7 +143,7 @@
     AMD64GdbRegCache regCache64;
 
   public:
-    RemoteGDB(System *system, ThreadContext *context);
+    RemoteGDB(System *system, ThreadContext *context, int _port);
     BaseGdbRegCache *gdbRegs();
 };
 } // namespace X86ISA
diff --git a/src/base/remote_gdb.cc b/src/base/remote_gdb.cc
index 6ed5957..09796f1 100644
--- a/src/base/remote_gdb.cc
+++ b/src/base/remote_gdb.cc
@@ -154,179 +154,236 @@
 
 static const int GDBPacketBufLen = 1024;
 
-#ifndef NDEBUG
 vector<BaseRemoteGDB *> debuggers;
 
-void
-debugger()
+class HardBreakpoint : public PCEvent
 {
-    static int current_debugger = -1;
-    if (current_debugger >= 0 && current_debugger < (int)debuggers.size()) {
-        BaseRemoteGDB *gdb = debuggers[current_debugger];
-        if (!gdb->isattached())
-            gdb->listener->accept();
-        if (gdb->isattached())
-            gdb->trap(SIGILL);
+  private:
+    BaseRemoteGDB *gdb;
+
+  public:
+    int refcount;
+
+  public:
+    HardBreakpoint(BaseRemoteGDB *_gdb, PCEventQueue *q, Addr pc)
+        : PCEvent(q, "HardBreakpoint Event", pc),
+          gdb(_gdb), refcount(0)
+    {
+        DPRINTF(GDBMisc, "creating hardware breakpoint at %#x\n", evpc);
+    }
+
+    const std::string name() const { return gdb->name() + ".hwbkpt"; }
+
+    void
+    process(ThreadContext *tc) override
+    {
+        DPRINTF(GDBMisc, "handling hardware breakpoint at %#x\n", pc());
+
+        if (tc == gdb->tc)
+            gdb->trap(SIGTRAP);
+    }
+};
+
+namespace {
+
+// Exception to throw when the connection to the client is broken.
+struct BadClient
+{
+    const char *warning;
+    BadClient(const char *_warning=NULL) : warning(_warning)
+    {}
+};
+
+// Exception to throw when an error needs to be reported to the client.
+struct CmdError
+{
+    string error;
+    CmdError(std::string _error) : error(_error)
+    {}
+};
+
+// 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 (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);
+}
+
+// Convert the low 4 bits of an integer into an hex digit.
+char
+i2digit(int n)
+{
+    return ("0123456789abcdef"[n & 0x0f]);
+}
+
+// Convert a byte array into an hex string.
+void
+mem2hex(char *vdst, const char *vsrc, int len)
+{
+    char *dst = vdst;
+    const char *src = vsrc;
+
+    while (len--) {
+        *dst++ = i2digit(*src >> 4);
+        *dst++ = i2digit(*src++);
+    }
+    *dst = '\0';
+}
+
+// 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;
+
+    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;
+}
+
+// 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)
+{
+    const char *src = *srcp;
+    Addr r = 0;
+    int nibble;
+
+    while ((nibble = digit2i(*src)) >= 0) {
+        r *= 16;
+        r += nibble;
+        src++;
+    }
+    *srcp = src;
+    return r;
+}
+
+enum GdbBreakpointType {
+    GdbSoftBp = '0',
+    GdbHardBp = '1',
+    GdbWriteWp = '2',
+    GdbReadWp = '3',
+    GdbAccWp = '4',
+};
+
+const char *
+break_type(char c)
+{
+    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;
 
-GDBListener::InputEvent::InputEvent(GDBListener *l, int fd, int e)
-    : PollEvent(fd, e), listener(l)
-{}
-
-void
-GDBListener::InputEvent::process(int revent)
+EventQueue *
+getComInstEventQueue(ThreadContext *tc)
 {
-    listener->accept();
+    return tc->getCpuPtr()->comInstEventQueue[tc->threadId()];
 }
 
-GDBListener::GDBListener(BaseRemoteGDB *g, int p)
-    : inputEvent(NULL), gdb(g), port(p)
-{
-    assert(!gdb->listener);
-    gdb->listener = this;
 }
 
-GDBListener::~GDBListener()
+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)
 {
-    delete inputEvent;
+    debuggers.push_back(this);
+}
+
+BaseRemoteGDB::~BaseRemoteGDB()
+{
+    delete connectEvent;
+    delete dataEvent;
 }
 
 string
-GDBListener::name()
+BaseRemoteGDB::name()
 {
-    return gdb->name() + ".listener";
+    return sys->name() + ".remote_gdb";
 }
 
 void
-GDBListener::listen()
+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++;
+    while (!listener.listen(_port, true)) {
+        DPRINTF(GDBMisc, "Can't bind port %d\n", _port);
+        _port++;
     }
 
-    inputEvent = new InputEvent(this, listener.getfd(), POLLIN);
-    pollQueue.schedule(inputEvent);
+    connectEvent = new ConnectEvent(this, listener.getfd(), POLLIN);
+    pollQueue.schedule(connectEvent);
 
-#ifndef NDEBUG
-    gdb->number = debuggers.size();
-    debuggers.push_back(gdb);
-#endif
-
-#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
+             curTick(), name(), _port);
 }
 
 void
-GDBListener::accept()
+BaseRemoteGDB::connect()
 {
-    if (!listener.islistening())
-        panic("GDBListener::accept(): cannot accept if we're not listening!");
+    panic_if(!listener.islistening(),
+             "Cannot accept GDB connections if we're not listening!");
 
     int sfd = listener.accept(true);
 
     if (sfd != -1) {
-        if (gdb->isattached())
+        if (isAttached())
             close(sfd);
         else
-            gdb->attach(sfd);
+            attach(sfd);
     }
 }
 
 int
-GDBListener::getPort() const
+BaseRemoteGDB::port() const
 {
     panic_if(!listener.islistening(),
-             "Remote GDB port is unknown until GDBListener::listen() has "
-             "been called.\n");
-
-    return port;
+             "Remote GDB port is unknown until listen() has been called.\n");
+    return _port;
 }
 
-BaseRemoteGDB::InputEvent::InputEvent(BaseRemoteGDB *g, int fd, int e)
-    : PollEvent(fd, e), gdb(g)
-{}
-
-void
-BaseRemoteGDB::InputEvent::process(int revent)
-{
-    if (gdb->trapEvent.scheduled()) {
-        warn("GDB trap event has already been scheduled! "
-             "Ignoring this input event.");
-        return;
-    }
-
-    if (revent & POLLIN) {
-        gdb->trapEvent.type(SIGILL);
-        gdb->scheduleInstCommitEvent(&gdb->trapEvent, 0);
-    } else if (revent & POLLNVAL) {
-        gdb->descheduleInstCommitEvent(&gdb->trapEvent);
-        gdb->detach();
-    }
-}
-
-void
-BaseRemoteGDB::TrapEvent::process()
-{
-    gdb->trap(_type);
-}
-
-void
-BaseRemoteGDB::processSingleStepEvent()
-{
-    if (!singleStepEvent.scheduled())
-        scheduleInstCommitEvent(&singleStepEvent, 1);
-    trap(SIGTRAP);
-}
-
-BaseRemoteGDB::BaseRemoteGDB(System *_system, ThreadContext *c) :
-        inputEvent(NULL), trapEvent(this), listener(NULL), number(-1),
-        fd(-1), active(false), attached(false), system(_system),
-        context(c),
-        singleStepEvent([this]{ processSingleStepEvent(); }, name())
-{
-}
-
-BaseRemoteGDB::~BaseRemoteGDB()
-{
-    if (inputEvent)
-        delete inputEvent;
-}
-
-string
-BaseRemoteGDB::name()
-{
-    return system->name() + ".remote_gdb";
-}
-
-bool
-BaseRemoteGDB::isattached()
-{ return attached; }
-
 void
 BaseRemoteGDB::attach(int f)
 {
     fd = f;
 
-    inputEvent = new InputEvent(this, fd, POLLIN);
-    pollQueue.schedule(inputEvent);
+    dataEvent = new DataEvent(this, fd, POLLIN);
+    pollQueue.schedule(dataEvent);
 
     attached = true;
     DPRINTFN("remote gdb attached\n");
@@ -341,13 +398,106 @@
     close(fd);
     fd = -1;
 
-    pollQueue.remove(inputEvent);
+    pollQueue.remove(dataEvent);
     DPRINTFN("remote gdb detached\n");
 }
 
-/////////////////////////
-//
-//
+// 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);
+
+    char data[GDBPacketBufLen + 1];
+    GdbCommand::Context cmdCtx;
+    cmdCtx.type = type;
+    cmdCtx.data = &data[1];
+
+    for (;;) {
+        try {
+            size_t datalen = recv(data, sizeof(data));
+            if (datalen < 1)
+                throw BadClient();
+
+            data[datalen] = 0; // Sentinel
+            cmdCtx.cmd_byte = data[0];
+            cmdCtx.len = datalen - 1;
+
+            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()
@@ -368,35 +518,6 @@
     throw BadClient("Couldn't write data to the debugger.");
 }
 
-// Send a packet to gdb
-void
-BaseRemoteGDB::send(const char *bp)
-{
-    const char *p;
-    uint8_t csum, c;
-
-    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);
-        // 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);
-}
-
 // Receive a packet from gdb
 int
 BaseRemoteGDB::recv(char *bp, int maxlen)
@@ -460,6 +581,35 @@
     return len;
 }
 
+// Send a packet to gdb
+void
+BaseRemoteGDB::send(const char *bp)
+{
+    const char *p;
+    uint8_t csum, c;
+
+    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);
+        // 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)
@@ -475,10 +625,10 @@
     DPRINTF(GDBRead, "read:  addr=%#x, size=%d", vaddr, size);
 
     if (FullSystem) {
-        FSTranslatingPortProxy &proxy = context->getVirtProxy();
+        FSTranslatingPortProxy &proxy = tc->getVirtProxy();
         proxy.readBlob(vaddr, (uint8_t*)data, size);
     } else {
-        SETranslatingPortProxy &proxy = context->getMemProxy();
+        SETranslatingPortProxy &proxy = tc->getMemProxy();
         proxy.readBlob(vaddr, (uint8_t*)data, size);
     }
 
@@ -518,10 +668,10 @@
             DPRINTFNR("\n");
     }
     if (FullSystem) {
-        FSTranslatingPortProxy &proxy = context->getVirtProxy();
+        FSTranslatingPortProxy &proxy = tc->getVirtProxy();
         proxy.writeBlob(vaddr, (uint8_t*)data, size);
     } else {
-        SETranslatingPortProxy &proxy = context->getMemProxy();
+        SETranslatingPortProxy &proxy = tc->getMemProxy();
         proxy.writeBlob(vaddr, (uint8_t*)data, size);
     }
 
@@ -529,6 +679,14 @@
 }
 
 void
+BaseRemoteGDB::singleStep()
+{
+    if (!singleStepEvent.scheduled())
+        scheduleInstCommitEvent(&singleStepEvent, 1);
+    trap(SIGTRAP);
+}
+
+void
 BaseRemoteGDB::clearSingleStep()
 {
     descheduleInstCommitEvent(&singleStepEvent);
@@ -541,56 +699,6 @@
         scheduleInstCommitEvent(&singleStepEvent, 1);
 }
 
-PCEventQueue *BaseRemoteGDB::getPcEventQueue()
-{
-    return &system->pcEventQueue;
-}
-
-EventQueue *
-BaseRemoteGDB::getComInstEventQueue()
-{
-    BaseCPU *cpu = context->getCpuPtr();
-    return cpu->comInstEventQueue[context->threadId()];
-}
-
-void
-BaseRemoteGDB::scheduleInstCommitEvent(Event *ev, int delta)
-{
-    EventQueue *eq = getComInstEventQueue();
-    // Here "ticks" aren't simulator ticks which measure time, they're
-    // instructions committed by the CPU.
-    eq->schedule(ev, eq->getCurTick() + delta);
-}
-
-void
-BaseRemoteGDB::descheduleInstCommitEvent(Event *ev)
-{
-    if (ev->scheduled())
-        getComInstEventQueue()->deschedule(ev);
-}
-
-bool
-BaseRemoteGDB::checkBpLen(size_t len)
-{
-    return len == sizeof(MachInst);
-}
-
-BaseRemoteGDB::HardBreakpoint::HardBreakpoint(BaseRemoteGDB *_gdb, Addr pc)
-    : PCEvent(_gdb->getPcEventQueue(), "HardBreakpoint Event", pc),
-      gdb(_gdb), refcount(0)
-{
-    DPRINTF(GDBMisc, "creating hardware breakpoint at %#x\n", evpc);
-}
-
-void
-BaseRemoteGDB::HardBreakpoint::process(ThreadContext *tc)
-{
-    DPRINTF(GDBMisc, "handling hardware breakpoint at %#x\n", pc());
-
-    if (tc == gdb->context)
-        gdb->trap(SIGTRAP);
-}
-
 void
 BaseRemoteGDB::insertSoftBreak(Addr addr, size_t len)
 {
@@ -619,7 +727,7 @@
 
     HardBreakpoint *&bkpt = hardBreakMap[addr];
     if (bkpt == 0)
-        bkpt = new HardBreakpoint(this, addr);
+        bkpt = new HardBreakpoint(this, &sys->pcEventQueue, addr);
 
     bkpt->refcount++;
 }
@@ -632,7 +740,7 @@
 
     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())
         throw CmdError("E0C");
 
@@ -644,13 +752,6 @@
 }
 
 void
-BaseRemoteGDB::setTempBreakpoint(Addr bkpt)
-{
-    DPRINTF(GDBMisc, "setTempBreakpoint: addr=%#x\n", bkpt);
-    insertHardBreak(bkpt, sizeof(TheISA::MachInst));
-}
-
-void
 BaseRemoteGDB::clearTempBreakpoint(Addr &bkpt)
 {
     DPRINTF(GDBMisc, "setTempBreakpoint: addr=%#x\n", bkpt);
@@ -658,28 +759,30 @@
     bkpt = 0;
 }
 
-enum GdbBreakpointType {
-    GdbSoftBp = '0',
-    GdbHardBp = '1',
-    GdbWriteWp = '2',
-    GdbReadWp = '3',
-    GdbAccWp = '4',
-};
-
-const char *
-BaseRemoteGDB::break_type(char c)
+void
+BaseRemoteGDB::setTempBreakpoint(Addr bkpt)
 {
-    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";
-    }
+    DPRINTF(GDBMisc, "setTempBreakpoint: addr=%#x\n", bkpt);
+    insertHardBreak(bkpt, sizeof(TheISA::MachInst));
 }
 
-std::map<char, GdbCommand> BaseRemoteGDB::command_map = {
+void
+BaseRemoteGDB::scheduleInstCommitEvent(Event *ev, int delta)
+{
+    EventQueue *eq = getComInstEventQueue(tc);
+    // Here "ticks" aren't simulator ticks which measure time, they're
+    // instructions committed by the CPU.
+    eq->schedule(ev, eq->getCurTick() + delta);
+}
+
+void
+BaseRemoteGDB::descheduleInstCommitEvent(Event *ev)
+{
+    if (ev->scheduled())
+        getComInstEventQueue(tc)->deschedule(ev);
+}
+
+std::map<char, BaseRemoteGDB::GdbCommand> BaseRemoteGDB::command_map = {
     // last signal
     { '?', { "KGDB_SIGNAL", &BaseRemoteGDB::cmd_signal } },
     // set baud (deprecated)
@@ -736,6 +839,11 @@
     { 'Z', { "KGDB_SET_HW_BKPT", &BaseRemoteGDB::cmd_set_hw_bkpt } },
 };
 
+bool
+BaseRemoteGDB::checkBpLen(size_t len)
+{
+    return len == sizeof(MachInst);
+}
 
 bool
 BaseRemoteGDB::cmd_unsupported(GdbCommand::Context &ctx)
@@ -759,7 +867,7 @@
     const char *p = ctx.data;
     if (ctx.len) {
         Addr newPc = hex2i(&p);
-        context->pcState(newPc);
+        tc->pcState(newPc);
     }
     clearSingleStep();
     return false;
@@ -772,7 +880,7 @@
     hex2i(&p);
     if (*p++ == ';') {
         Addr newPc = hex2i(&p);
-        context->pcState(newPc);
+        tc->pcState(newPc);
     }
     clearSingleStep();
     return false;
@@ -803,7 +911,7 @@
     if (p == NULL || *p != '\0')
         throw CmdError("E01");
 
-    regCachePtr->setRegs(context);
+    regCachePtr->setRegs(tc);
     send("OK");
 
     return true;
@@ -883,7 +991,7 @@
     hex2i(&p); // Ignore the subcommand byte.
     if (*p++ == ';') {
         Addr newPc = hex2i(&p);
-        context->pcState(newPc);
+        tc->pcState(newPc);
     }
     setSingleStep();
     return false;
@@ -895,7 +1003,7 @@
     if (ctx.len) {
         const char *p = ctx.data;
         Addr newPc = hex2i(&p);
-        context->pcState(newPc);
+        tc->pcState(newPc);
     }
     setSingleStep();
     return false;
@@ -966,161 +1074,3 @@
 
     return true;
 }
-
-
-// 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", context->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(context);
-
-    char data[GDBPacketBufLen + 1];
-    GdbCommand::Context cmdCtx;
-    cmdCtx.type = type;
-    cmdCtx.data = &data[1];
-
-    for (;;) {
-        try {
-            size_t datalen = recv(data, sizeof(data));
-            if (datalen < 1)
-                throw BadClient();
-
-            data[datalen] = 0; // Sentinel
-            cmdCtx.cmd_byte = data[0];
-            cmdCtx.len = datalen - 1;
-
-            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;
-}
-
-// Convert a hex digit into an integer.
-// This returns -1 if the argument passed is no valid hex digit.
-int
-BaseRemoteGDB::digit2i(char c)
-{
-    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);
-}
-
-// Convert the low 4 bits of an integer into an hex digit.
-char
-BaseRemoteGDB::i2digit(int n)
-{
-    return ("0123456789abcdef"[n & 0x0f]);
-}
-
-// Convert a byte array into an hex string.
-void
-BaseRemoteGDB::mem2hex(char *vdst, const char *vsrc, int len)
-{
-    char *dst = vdst;
-    const char *src = vsrc;
-
-    while (len--) {
-        *dst++ = i2digit(*src >> 4);
-        *dst++ = i2digit(*src++);
-    }
-    *dst = '\0';
-}
-
-// 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(char *vdst, const char *src, int maxlen)
-{
-    char *dst = 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;
-    }
-    return src;
-}
-
-// 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)
-{
-    const char *src = *srcp;
-    Addr r = 0;
-    int nibble;
-
-    while ((nibble = digit2i(*src)) >= 0) {
-        r *= 16;
-        r += nibble;
-        src++;
-    }
-    *srcp = src;
-    return r;
-}
diff --git a/src/base/remote_gdb.hh b/src/base/remote_gdb.hh
index 121faaf..27b4ab4 100644
--- a/src/base/remote_gdb.hh
+++ b/src/base/remote_gdb.hh
@@ -49,68 +49,210 @@
 class System;
 class ThreadContext;
 
-class GDBListener;
-
 class BaseRemoteGDB;
+class HardBreakpoint;
 
-struct GdbCommand
+/**
+ * Concrete subclasses of this abstract class represent how the
+ * register values are transmitted on the wire.  Usually each
+ * architecture should define one subclass, but there can be more
+ * if there is more than one possible wire format.  For example,
+ * ARM defines both AArch32GdbRegCache and AArch64GdbRegCache.
+ */
+class BaseGdbRegCache
 {
   public:
-    struct Context
-    {
-        const GdbCommand *cmd;
-        char cmd_byte;
-        int type;
-        char *data;
-        int len;
-    };
 
-    typedef bool (BaseRemoteGDB::*Func)(Context &ctx);
+    /**
+     * Return the pointer to the raw bytes buffer containing the
+     * register values.  Each byte of this buffer is literally
+     * encoded as two hex digits in the g or G RSP packet.
+     */
+    virtual char *data() const = 0;
 
-    const char * const name;
-    const Func func;
+    /**
+     * Return the size of the raw buffer, in bytes
+     * (i.e., half of the number of digits in the g/G packet).
+     */
+    virtual size_t size() const = 0;
 
-    GdbCommand(const char *_name, Func _func) : name(_name), func(_func)
+    /**
+     * Fill the raw buffer from the registers in the ThreadContext.
+     */
+    virtual void getRegs(ThreadContext*) = 0;
+
+    /**
+     * Set the ThreadContext's registers from the values
+     * in the raw buffer.
+     */
+    virtual void setRegs(ThreadContext*) const = 0;
+
+    /**
+     * Return the name to use in places like DPRINTF.
+     * Having each concrete superclass redefine this member
+     * is useful in situations where the class of the regCache
+     * can change on the fly.
+     */
+    virtual const std::string name() const = 0;
+
+    BaseGdbRegCache(BaseRemoteGDB *g) : gdb(g)
     {}
+    virtual ~BaseGdbRegCache()
+    {}
+
+  protected:
+    BaseRemoteGDB *gdb;
 };
 
 class BaseRemoteGDB
 {
+    friend class HardBreakpoint;
+  public:
+
+    /*
+     * Interface to other parts of the simulator.
+     */
+    BaseRemoteGDB(System *system, ThreadContext *context, int _port);
+    virtual ~BaseRemoteGDB();
+
+    std::string name();
+
+    void listen();
+    void connect();
+
+    int port() const;
+
+    void attach(int fd);
+    void detach();
+    bool isAttached() { return attached; }
+
+    void replaceThreadContext(ThreadContext *_tc) { tc = _tc; }
+
+    bool trap(int type);
+    bool breakpoint() { return trap(SIGTRAP); }
+
   private:
-    friend void debugger();
-    friend class GDBListener;
+    /*
+     * Connection to the external GDB.
+     */
+    void incomingData(int revent);
+    void connectWrapper(int revent) { connect(); }
 
-  protected:
-    /// Exception to throw when the connection to the client is broken.
-    struct BadClient
+    template <void (BaseRemoteGDB::*F)(int revent)>
+    class SocketEvent : public PollEvent
     {
-        const char *warning;
-        BadClient(const char *_warning=NULL) : warning(_warning)
+      protected:
+        BaseRemoteGDB *gdb;
+
+      public:
+        SocketEvent(BaseRemoteGDB *gdb, int fd, int e) :
+            PollEvent(fd, e), gdb(gdb)
         {}
+
+        void process(int revent) { (gdb->*F)(revent); }
     };
-    /// Exception to throw when an error needs to be reported to the client.
-    struct CmdError
+
+    typedef SocketEvent<&BaseRemoteGDB::connectWrapper> ConnectEvent;
+    typedef SocketEvent<&BaseRemoteGDB::incomingData> DataEvent;
+
+    friend ConnectEvent;
+    friend DataEvent;
+
+    ConnectEvent *connectEvent;
+    DataEvent *dataEvent;
+
+    ListenSocket listener;
+    int _port;
+
+    // The socket commands come in through.
+    int fd;
+
+    // Transfer data to/from GDB.
+    uint8_t getbyte();
+    void putbyte(uint8_t b);
+
+    int recv(char *data, int len);
+    void send(const char *data);
+
+    /*
+     * Simulator side debugger state.
+     */
+    bool active;
+    bool attached;
+
+    System *sys;
+    ThreadContext *tc;
+
+    BaseGdbRegCache *regCachePtr;
+
+    class TrapEvent : public Event
     {
-        std::string error;
-        CmdError(std::string _error) : error(_error)
+      protected:
+        int _type;
+        BaseRemoteGDB *gdb;
+
+      public:
+        TrapEvent(BaseRemoteGDB *g) : gdb(g)
         {}
+
+        void type(int t) { _type = t; }
+        void process() { gdb->trap(_type); }
+    } trapEvent;
+
+    /*
+     * The interface to the simulated system.
+     */
+    // Machine memory.
+    bool read(Addr addr, size_t size, char *data);
+    bool write(Addr addr, size_t size, const char *data);
+
+    template <class T> T read(Addr addr);
+    template <class T> void write(Addr addr, T data);
+
+    // Single step.
+    void singleStep();
+    EventWrapper<BaseRemoteGDB, &BaseRemoteGDB::singleStep> singleStepEvent;
+
+    void clearSingleStep();
+    void setSingleStep();
+
+    /// Schedule an event which will be triggered "delta" instructions later.
+    void scheduleInstCommitEvent(Event *ev, int delta);
+    /// Deschedule an instruction count based event.
+    void descheduleInstCommitEvent(Event *ev);
+
+    // Breakpoints.
+    void insertSoftBreak(Addr addr, size_t len);
+    void removeSoftBreak(Addr addr, size_t len);
+    void insertHardBreak(Addr addr, size_t len);
+    void removeHardBreak(Addr addr, size_t len);
+
+    void clearTempBreakpoint(Addr &bkpt);
+    void setTempBreakpoint(Addr bkpt);
+
+    /*
+     * GDB commands.
+     */
+    struct GdbCommand
+    {
+      public:
+        struct Context
+        {
+            const GdbCommand *cmd;
+            char cmd_byte;
+            int type;
+            char *data;
+            int len;
+        };
+
+        typedef bool (BaseRemoteGDB::*Func)(Context &ctx);
+
+        const char * const name;
+        const Func func;
+
+        GdbCommand(const char *_name, Func _func) : name(_name), func(_func) {}
     };
-    /// Exception to throw when something isn't supported.
-    class Unsupported {};
 
-    // Helper functions
-  protected:
-    int digit2i(char);
-    char i2digit(int);
-    Addr hex2i(const char **);
-    // Address formats, break types, and gdb commands may change
-    // between architectures, so they're defined as virtual
-    // functions.
-    virtual void mem2hex(char *, const char *, int);
-    virtual const char * hex2mem(char *, const char *, int);
-    virtual const char * break_type(char c);
-
-  protected:
     static std::map<char, GdbCommand> command_map;
 
     bool cmd_unsupported(GdbCommand::Context &ctx);
@@ -131,183 +273,15 @@
     bool cmd_set_hw_bkpt(GdbCommand::Context &ctx);
 
   protected:
-    class InputEvent : public PollEvent
-    {
-      protected:
-        BaseRemoteGDB *gdb;
+    ThreadContext *context() { return tc; }
+    System *system() { return sys; }
 
-      public:
-        InputEvent(BaseRemoteGDB *g, int fd, int e);
-        void process(int revent);
-    };
-
-    class TrapEvent : public Event
-    {
-      protected:
-        int _type;
-        BaseRemoteGDB *gdb;
-
-      public:
-        TrapEvent(BaseRemoteGDB *g) : gdb(g)
-        {}
-
-        void type(int t) { _type = t; }
-        void process();
-    };
-
-    friend class InputEvent;
-    InputEvent *inputEvent;
-    TrapEvent trapEvent;
-    GDBListener *listener;
-    int number;
-
-  protected:
-    // The socket commands come in through
-    int fd;
-
-  protected:
-    bool active;
-    bool attached;
-
-    System *system;
-    ThreadContext *context;
-
-  protected:
-    /**
-     * Concrete subclasses of this abstract class represent how the
-     * register values are transmitted on the wire.  Usually each
-     * architecture should define one subclass, but there can be more
-     * if there is more than one possible wire format.  For example,
-     * ARM defines both AArch32GdbRegCache and AArch64GdbRegCache.
-     */
-    class BaseGdbRegCache
-    {
-      public:
-
-        /**
-         * Return the pointer to the raw bytes buffer containing the
-         * register values.  Each byte of this buffer is literally
-         * encoded as two hex digits in the g or G RSP packet.
-         */
-        virtual char *data() const = 0;
-
-        /**
-         * Return the size of the raw buffer, in bytes
-         * (i.e., half of the number of digits in the g/G packet).
-         */
-        virtual size_t size() const = 0;
-
-        /**
-         * Fill the raw buffer from the registers in the ThreadContext.
-         */
-        virtual void getRegs(ThreadContext*) = 0;
-
-        /**
-         * Set the ThreadContext's registers from the values
-         * in the raw buffer.
-         */
-        virtual void setRegs(ThreadContext*) const = 0;
-
-        /**
-         * Return the name to use in places like DPRINTF.
-         * Having each concrete superclass redefine this member
-         * is useful in situations where the class of the regCache
-         * can change on the fly.
-         */
-        virtual const std::string name() const = 0;
-
-        BaseGdbRegCache(BaseRemoteGDB *g) : gdb(g)
-        {}
-        virtual ~BaseGdbRegCache()
-        {}
-
-      protected:
-        BaseRemoteGDB *gdb;
-    };
-
-    BaseGdbRegCache *regCachePtr;
-
-  protected:
-    uint8_t getbyte();
-    void putbyte(uint8_t b);
-
-    int recv(char *data, int len);
-    void send(const char *data);
-
-  protected:
-    // Machine memory
-    virtual bool read(Addr addr, size_t size, char *data);
-    virtual bool write(Addr addr, size_t size, const char *data);
-
-    template <class T> T read(Addr addr);
-    template <class T> void write(Addr addr, T data);
-
-  public:
-    BaseRemoteGDB(System *system, ThreadContext *context);
-    virtual ~BaseRemoteGDB();
-    virtual BaseGdbRegCache *gdbRegs() = 0;
-
-    void replaceThreadContext(ThreadContext *tc) { context = tc; }
-
-    void attach(int fd);
-    void detach();
-    bool isattached();
-
-    virtual bool acc(Addr addr, size_t len) = 0;
-    bool trap(int type);
-    virtual bool breakpoint()
-    {
-        return trap(SIGTRAP);
-    }
-
-    void processSingleStepEvent();
-    EventFunctionWrapper singleStepEvent;
-
-    void clearSingleStep();
-    void setSingleStep();
-
-    PCEventQueue *getPcEventQueue();
-    EventQueue *getComInstEventQueue();
-
-    /// Schedule an event which will be triggered "delta" instructions later.
-    void scheduleInstCommitEvent(Event *ev, int delta);
-    /// Deschedule an instruction count based event.
-    void descheduleInstCommitEvent(Event *ev);
-
-  protected:
+    // To be implemented by subclasses.
     virtual bool checkBpLen(size_t len);
 
-    class HardBreakpoint : public PCEvent
-    {
-      private:
-        BaseRemoteGDB *gdb;
+    virtual BaseGdbRegCache *gdbRegs() = 0;
 
-      public:
-        int refcount;
-
-      public:
-        HardBreakpoint(BaseRemoteGDB *_gdb, Addr addr);
-        const std::string name() const { return gdb->name() + ".hwbkpt"; }
-
-        virtual void process(ThreadContext *tc);
-    };
-    friend class HardBreakpoint;
-
-    typedef std::map<Addr, HardBreakpoint *> break_map_t;
-    typedef break_map_t::iterator break_iter_t;
-    break_map_t hardBreakMap;
-
-    void insertSoftBreak(Addr addr, size_t len);
-    void removeSoftBreak(Addr addr, size_t len);
-    virtual void insertHardBreak(Addr addr, size_t len);
-    void removeHardBreak(Addr addr, size_t len);
-
-  protected:
-    void clearTempBreakpoint(Addr &bkpt);
-    void setTempBreakpoint(Addr bkpt);
-
-  public:
-    std::string name();
+    virtual bool acc(Addr addr, size_t len) = 0;
 };
 
 template <class T>
@@ -322,38 +296,8 @@
 template <class T>
 inline void
 BaseRemoteGDB::write(Addr addr, T data)
-{ write(addr, sizeof(T), (const char *)&data); }
-
-class GDBListener
 {
-  protected:
-    class InputEvent : public PollEvent
-    {
-      protected:
-        GDBListener *listener;
-
-      public:
-        InputEvent(GDBListener *l, int fd, int e);
-        void process(int revent);
-    };
-
-    friend class InputEvent;
-    InputEvent *inputEvent;
-
-  protected:
-    ListenSocket listener;
-    BaseRemoteGDB *gdb;
-    int port;
-
-  public:
-    GDBListener(BaseRemoteGDB *g, int p);
-    ~GDBListener();
-
-    void accept();
-    void listen();
-    std::string name();
-
-    int getPort() const;
-};
+    write(addr, sizeof(T), (const char *)&data);
+}
 
 #endif /* __REMOTE_GDB_H__ */
diff --git a/src/sim/system.cc b/src/sim/system.cc
index 2f047f2..ed01e0e 100644
--- a/src/sim/system.cc
+++ b/src/sim/system.cc
@@ -261,16 +261,15 @@
 #if THE_ISA != NULL_ISA
     int port = getRemoteGDBPort();
     if (port) {
-        RemoteGDB *rgdb = new RemoteGDB(this, tc);
-        GDBListener *gdbl = new GDBListener(rgdb, port + id);
-        gdbl->listen();
+        RemoteGDB *rgdb = new RemoteGDB(this, tc, port + id);
+        rgdb->listen();
 
         BaseCPU *cpu = tc->getCpuPtr();
         if (cpu->waitForRemoteGDB()) {
             inform("%s: Waiting for a remote GDB connection on port %d.\n",
-                   cpu->name(), gdbl->getPort());
+                   cpu->name(), rgdb->port());
 
-            gdbl->accept();
+            rgdb->connect();
         }
         if (remoteGDB.size() <= id) {
             remoteGDB.resize(id + 1);
diff --git a/src/sim/system.hh b/src/sim/system.hh
index acd3108..5b0c178 100644
--- a/src/sim/system.hh
+++ b/src/sim/system.hh
@@ -75,7 +75,6 @@
 #endif
 
 class BaseRemoteGDB;
-class GDBListener;
 class KvmVM;
 class ObjectFile;
 class ThreadContext;
@@ -491,7 +490,6 @@
 
   public:
     std::vector<BaseRemoteGDB *> remoteGDB;
-    std::vector<GDBListener *> gdbListen;
     bool breakpoint();
 
   public: