| /* |
| * Copyright (c) 2003 The Regents of The University of Michigan |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer; |
| * redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution; |
| * neither the name of the copyright holders nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| /* |
| * 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 |
| * contributed to Berkeley. |
| * |
| * 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. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * 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. |
| * 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. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| * |
| * @(#)kgdb_stub.c 8.4 (Berkeley) 1/12/94 |
| */ |
| |
| /*- |
| * Copyright (c) 2001 The NetBSD Foundation, Inc. |
| * All rights reserved. |
| * |
| * This code is derived from software contributed to The NetBSD Foundation |
| * by Jason R. Thorpe. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * 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. |
| * 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. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
| * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
| * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| * POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| /* |
| * $NetBSD: kgdb_stub.c,v 1.8 2001/07/07 22:58:00 wdk Exp $ |
| * |
| * Taken from NetBSD |
| * |
| * "Stub" to allow remote cpu to debug over a serial line using gdb. |
| */ |
| |
| #include <sys/signal.h> |
| |
| #include <unistd.h> |
| |
| #include <cstdio> |
| #include <string> |
| |
| #include "cpu/exec_context.hh" |
| #include "base/intmath.hh" |
| #include "base/kgdb.h" |
| |
| #include "mem/functional_mem/physical_memory.hh" |
| #include "base/remote_gdb.hh" |
| #include "base/socket.hh" |
| #include "base/trace.hh" |
| #include "targetarch/vtophys.hh" |
| #include "sim/system.hh" |
| #include "cpu/static_inst.hh" |
| |
| using namespace std; |
| |
| #ifdef DEBUG |
| RemoteGDB *theDebugger = NULL; |
| |
| void |
| debugger() |
| { |
| if (theDebugger) |
| theDebugger->trap(ALPHA_KENTRY_IF); |
| } |
| #endif |
| |
| /////////////////////////////////////////////////////////// |
| // |
| // |
| // |
| |
| GDBListener::Event::Event(GDBListener *l, int fd, int e) |
| : PollEvent(fd, e), listener(l) |
| {} |
| |
| void |
| GDBListener::Event::process(int revent) |
| { |
| listener->accept(); |
| } |
| |
| GDBListener::GDBListener(RemoteGDB *g, int p) |
| : event(NULL), gdb(g), port(p) |
| {} |
| |
| GDBListener::~GDBListener() |
| { |
| if (event) |
| delete event; |
| } |
| |
| void |
| GDBListener::listen() |
| { |
| while (!listener.listen(port, true)) { |
| DPRINTF(RGDB, "GDBListener(listen): Can't bind port %d\n", port); |
| port++; |
| } |
| |
| cerr << "Listening for remote gdb connection on port " << port << endl; |
| event = new Event(this, listener.getfd(), POLLIN); |
| pollQueue.schedule(event); |
| } |
| |
| void |
| GDBListener::accept() |
| { |
| if (!listener.islistening()) |
| panic("GDBListener(accept): cannot accept a connection if we're not listening!"); |
| |
| int sfd = listener.accept(true); |
| |
| if (sfd != -1) { |
| if (gdb->isattached()) |
| close(sfd); |
| else |
| gdb->attach(sfd); |
| } |
| } |
| |
| /////////////////////////////////////////////////////////// |
| // |
| // |
| // |
| 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) |
| {} |
| |
| void |
| RemoteGDB::Event::process(int revent) |
| { gdb->trap(ALPHA_KENTRY_IF); } |
| |
| RemoteGDB::RemoteGDB(System *_system, ExecContext *c) |
| : event(NULL), fd(-1), active(false), attached(false), |
| system(_system), pmem(_system->physmem), context(c) |
| { |
| #ifdef DEBUG |
| theDebugger = this; |
| #endif |
| memset(gdbregs, 0, sizeof(gdbregs)); |
| } |
| |
| RemoteGDB::~RemoteGDB() |
| { |
| if (event) |
| delete event; |
| } |
| |
| bool |
| RemoteGDB::isattached() |
| { return attached; } |
| |
| void |
| RemoteGDB::attach(int f) |
| { |
| fd = f; |
| |
| event = new Event(this, fd, POLLIN); |
| pollQueue.schedule(event); |
| |
| attached = true; |
| DPRINTFN("remote gdb attached\n"); |
| } |
| |
| void |
| RemoteGDB::detach() |
| { |
| attached = false; |
| close(fd); |
| fd = -1; |
| |
| pollQueue.remove(event); |
| DPRINTFN("remote gdb detached\n"); |
| } |
| |
| 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"; |
| } |
| } |
| |
| /////////////////////////////////////////////////////////// |
| // RemoteGDB::acc |
| // |
| // Determine if the mapping at va..(va+len) is valid. |
| // |
| bool |
| RemoteGDB::acc(Addr va, size_t len) |
| { |
| Addr last_va; |
| Addr pte; |
| |
| va = alpha_trunc_page(va); |
| last_va = alpha_round_page(va + len); |
| |
| do { |
| if (va < ALPHA_K0SEG_BASE) { |
| DPRINTF(RGDB, "RGDB(acc): Mapping is invalid %#x < K0SEG\n", va); |
| return false; |
| } |
| |
| if (va < ALPHA_K1SEG_BASE) { |
| if (va < (ALPHA_K0SEG_BASE + pmem->getSize())) { |
| DPRINTF(RGDB, "RGDB(acc): Mapping is valid K0SEG <= " |
| "%#x < K0SEG + size\n", va); |
| return true; |
| } else { |
| DPRINTF(RGDB, "RGDB(acc): Mapping is invalid %#x < K0SEG\n", |
| va); |
| return false; |
| } |
| } |
| |
| Addr ptbr = context->regs.ipr[AlphaISA::IPR_PALtemp20]; |
| pte = kernel_pte_lookup(pmem, ptbr, va); |
| if (!pte || !entry_valid(pmem->phys_read_qword(pte))) { |
| DPRINTF(RGDB, "RGDB(acc): %#x pte is invalid\n", va); |
| return false; |
| } |
| va += ALPHA_PGBYTES; |
| } while (va < last_va); |
| |
| DPRINTF(RGDB, "RGDB(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) |
| { |
| switch (type) { |
| 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; |
| } |
| } |
| |
| /////////////////////////////////////////////////////////// |
| // RemoteGDB::getregs |
| // |
| // Translate the kernel debugger register format into |
| // the GDB register format. |
| void |
| RemoteGDB::getregs() |
| { |
| memset(gdbregs, 0, sizeof(gdbregs)); |
| memcpy(&gdbregs[KGDB_REG_V0], context->regs.intRegFile, 32 * sizeof(uint64_t)); |
| #ifdef KGDB_FP_REGS |
| memcpy(&gdbregs[KGDB_REG_F0], context->regs.floatRegFile.q, |
| 32 * sizeof(uint64_t)); |
| #endif |
| gdbregs[KGDB_REG_PC] = context->regs.pc; |
| } |
| |
| /////////////////////////////////////////////////////////// |
| // RemoteGDB::setregs |
| // |
| // Translate the GDB register format into the kernel |
| // debugger register format. |
| // |
| void |
| RemoteGDB::setregs() |
| { |
| memcpy(context->regs.intRegFile, &gdbregs[KGDB_REG_V0], 32 * sizeof(uint64_t)); |
| #ifdef KGDB_FP_REGS |
| memcpy(context->regs.floatRegFile.q, &gdbregs[KGDB_REG_F0], |
| 32 * sizeof(uint64_t)); |
| #endif |
| context->regs.pc = gdbregs[KGDB_REG_PC]; |
| } |
| |
| void |
| RemoteGDB::setTempBreakpoint(TempBreakpoint &bkpt, Addr addr) |
| { |
| DPRINTF(RGDB, "RGDB(setTempBreakpoint): addr=%#x\n", addr); |
| |
| bkpt.address = addr; |
| insertHardBreak(addr, 4); |
| } |
| |
| void |
| RemoteGDB::clearTempBreakpoint(TempBreakpoint &bkpt) |
| { |
| DPRINTF(RGDB, "RGDB(setTempBreakpoint): addr=%#x\n", |
| bkpt.address); |
| |
| |
| removeHardBreak(bkpt.address, 4); |
| bkpt.address = 0; |
| } |
| |
| void |
| RemoteGDB::clearSingleStep() |
| { |
| DPRINTF(RGDB, "clearSingleStep bt_addr=%#x nt_addr=%#x\n", |
| takenBkpt.address, notTakenBkpt.address); |
| |
| if (takenBkpt.address != 0) |
| clearTempBreakpoint(takenBkpt); |
| |
| if (notTakenBkpt.address != 0) |
| clearTempBreakpoint(notTakenBkpt); |
| } |
| |
| void |
| RemoteGDB::setSingleStep() |
| { |
| Addr pc = context->regs.pc; |
| 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<TheISA> 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; |
| } |
| |
| DPRINTF(RGDB, "setSingleStep bt_addr=%#x nt_addr=%#x\n", |
| takenBkpt.address, notTakenBkpt.address); |
| |
| setTempBreakpoint(notTakenBkpt, npc); |
| |
| if (set_bt) |
| setTempBreakpoint(takenBkpt, bpc); |
| } |
| |
| ///////////////////////// |
| // |
| // |
| |
| uint8_t |
| RemoteGDB::getbyte() |
| { |
| uint8_t b; |
| ::read(fd, &b, 1); |
| return b; |
| } |
| |
| void |
| RemoteGDB::putbyte(uint8_t b) |
| { |
| ::write(fd, &b, 1); |
| } |
| |
| // Send a packet to gdb |
| void |
| RemoteGDB::send(const char *bp) |
| { |
| const char *p; |
| uint8_t csum, c; |
| |
| // DPRINTF(RGDB, "RGDB(send): %s\n", bp); |
| |
| 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); |
| } |
| |
| // Receive a packet from gdb |
| int |
| RemoteGDB::recv(char *bp, int maxlen) |
| { |
| char *p; |
| int c, csum; |
| int len; |
| |
| do { |
| p = bp; |
| csum = len = 0; |
| while ((c = getbyte()) != KGDB_START) |
| ; |
| |
| while ((c = getbyte()) != KGDB_END && len < maxlen) { |
| c &= 0x7f; |
| csum += c; |
| *p++ = c; |
| len++; |
| } |
| csum &= 0xff; |
| *p = '\0'; |
| |
| if (len >= maxlen) { |
| putbyte(KGDB_BADP); |
| continue; |
| } |
| |
| csum -= digit2i(getbyte()) * 16; |
| csum -= digit2i(getbyte()); |
| |
| if (csum == 0) { |
| putbyte(KGDB_GOODP); |
| // Sequence present? |
| if (bp[2] == ':') { |
| putbyte(bp[0]); |
| putbyte(bp[1]); |
| len -= 3; |
| bcopy(bp + 3, bp, len); |
| } |
| break; |
| } |
| putbyte(KGDB_BADP); |
| } while (1); |
| |
| // DPRINTF(RGDB, "RGDB(recv): %s: %s\n", gdb_command(*bp), bp); |
| |
| return (len); |
| } |
| |
| // Read bytes from kernel address space for debugger. |
| bool |
| RemoteGDB::read(Addr vaddr, size_t size, char *data) |
| { |
| static Addr lastaddr = 0; |
| static size_t lastsize = 0; |
| |
| uint8_t *maddr; |
| |
| if (vaddr < 10) { |
| DPRINTF(RGDB, "\nRGDB(read): reading memory location zero!\n"); |
| vaddr = lastaddr + lastsize; |
| } |
| |
| DPRINTF(RGDB, "RGDB(read): addr=%#x, size=%d", vaddr, size); |
| #if TRACING_ON |
| char *d = data; |
| size_t s = size; |
| #endif |
| |
| lastaddr = vaddr; |
| lastsize = size; |
| |
| size_t count = min((Addr)size, |
| VMPageSize - (vaddr & (VMPageSize - 1))); |
| |
| maddr = vtomem(context, vaddr, count); |
| memcpy(data, maddr, count); |
| |
| vaddr += count; |
| data += count; |
| size -= count; |
| |
| while (size >= VMPageSize) { |
| maddr = vtomem(context, vaddr, count); |
| memcpy(data, maddr, VMPageSize); |
| |
| vaddr += VMPageSize; |
| data += VMPageSize; |
| size -= VMPageSize; |
| } |
| |
| if (size > 0) { |
| maddr = vtomem(context, vaddr, count); |
| memcpy(data, maddr, size); |
| } |
| |
| #if TRACING_ON |
| if (DTRACE(RGDB)) { |
| char buf[1024]; |
| mem2hex(buf, d, s); |
| cprintf(": %s\n", buf); |
| } |
| #endif |
| |
| return true; |
| } |
| |
| // Write bytes to kernel address space for debugger. |
| bool |
| RemoteGDB::write(Addr vaddr, size_t size, const char *data) |
| { |
| static Addr lastaddr = 0; |
| static size_t lastsize = 0; |
| |
| uint8_t *maddr; |
| |
| if (vaddr < 10) { |
| DPRINTF(RGDB, "RGDB(write): writing memory location zero!\n"); |
| vaddr = lastaddr + lastsize; |
| } |
| |
| if (DTRACE(RGDB)) { |
| char buf[1024]; |
| mem2hex(buf, data, size); |
| cprintf("RGDB(write): addr=%#x, size=%d: %s\n", vaddr, size, buf); |
| } |
| |
| lastaddr = vaddr; |
| lastsize = size; |
| |
| size_t count = min((Addr)size, |
| VMPageSize - (vaddr & (VMPageSize - 1))); |
| |
| maddr = vtomem(context, vaddr, count); |
| memcpy(maddr, data, count); |
| |
| vaddr += count; |
| data += count; |
| size -= count; |
| |
| while (size >= VMPageSize) { |
| maddr = vtomem(context, vaddr, count); |
| memcpy(maddr, data, VMPageSize); |
| |
| vaddr += VMPageSize; |
| data += VMPageSize; |
| size -= VMPageSize; |
| } |
| |
| if (size > 0) { |
| maddr = vtomem(context, vaddr, count); |
| memcpy(maddr, data, size); |
| } |
| |
| #ifdef IMB |
| alpha_pal_imb(); |
| #endif |
| |
| return true; |
| } |
| |
| |
| PCEventQueue *RemoteGDB::getPcEventQueue() |
| { |
| return &system->pcEventQueue; |
| } |
| |
| |
| RemoteGDB::HardBreakpoint::HardBreakpoint(RemoteGDB *_gdb, Addr pc) |
| : PCEvent(_gdb->getPcEventQueue(), "HardBreakpoint Event", pc), |
| gdb(_gdb), refcount(0) |
| { |
| DPRINTF(RGDB, "creating hardware breakpoint at %#x\n", evpc); |
| schedule(); |
| } |
| |
| void |
| RemoteGDB::HardBreakpoint::process(ExecContext *xc) |
| { |
| DPRINTF(RGDB, "handling hardware breakpoint at %#x\n", pc()); |
| |
| if (xc == gdb->context) |
| gdb->trap(ALPHA_KENTRY_IF); |
| } |
| |
| bool |
| RemoteGDB::insertSoftBreak(Addr addr, size_t len) |
| { |
| if (len != sizeof(MachInst)) |
| panic("invalid length\n"); |
| |
| return insertHardBreak(addr, len); |
| } |
| |
| bool |
| RemoteGDB::removeSoftBreak(Addr addr, size_t len) |
| { |
| if (len != sizeof(MachInst)) |
| panic("invalid length\n"); |
| |
| return removeHardBreak(addr, len); |
| } |
| |
| bool |
| RemoteGDB::insertHardBreak(Addr addr, size_t len) |
| { |
| if (len != sizeof(MachInst)) |
| panic("invalid length\n"); |
| |
| DPRINTF(RGDB, "inserting hardware breakpoint at %#x\n", addr); |
| |
| HardBreakpoint *&bkpt = hardBreakMap[addr]; |
| if (bkpt == 0) |
| bkpt = new HardBreakpoint(this, addr); |
| |
| bkpt->refcount++; |
| |
| return true; |
| |
| #if 0 |
| break_iter_t i = hardBreakMap.find(addr); |
| if (i == hardBreakMap.end()) { |
| HardBreakpoint *bkpt = new HardBreakpoint(this, addr); |
| hardBreakMap[addr] = bkpt; |
| i = hardBreakMap.insert(make_pair(addr, bkpt)); |
| if (i == hardBreakMap.end()) |
| return false; |
| } |
| |
| (*i).second->refcount++; |
| #endif |
| } |
| |
| bool |
| RemoteGDB::removeHardBreak(Addr addr, size_t len) |
| { |
| if (len != sizeof(MachInst)) |
| panic("invalid length\n"); |
| |
| DPRINTF(RGDB, "removing hardware breakpoint at %#x\n", addr); |
| |
| break_iter_t i = hardBreakMap.find(addr); |
| if (i == hardBreakMap.end()) |
| return false; |
| |
| HardBreakpoint *hbp = (*i).second; |
| if (--hbp->refcount == 0) { |
| delete hbp; |
| hardBreakMap.erase(i); |
| } |
| |
| return true; |
| } |
| |
| const char * |
| break_type(char c) |
| { |
| 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"; |
| } |
| } |
| |
| // 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) |
| { |
| 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(RGDB, "RGDB(trap): PC=%#x NPC=%#x\n", |
| context->regs.pc, context->regs.npc); |
| |
| 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) { |
| if (!IS_BREAKPOINT_TRAP(type, 0)) { |
| // No debugger active -- let trap handle this. |
| return false; |
| } |
| active = true; |
| } else { |
| // Tell remote host that an exception has occurred. |
| sprintf((char *)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". |
| sprintf((char *)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; |
| |
| 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->regs.pc = val; |
| context->regs.npc = val + sizeof(MachInst); |
| } |
| clearSingleStep(); |
| goto out; |
| |
| case KGDB_CONT: |
| if (p - data < datalen) { |
| val = hex2i(&p); |
| context->regs.pc = val; |
| context->regs.npc = val + sizeof(MachInst); |
| } |
| clearSingleStep(); |
| goto out; |
| |
| case KGDB_ASYNC_STEP: |
| subcmd = hex2i(&p); |
| if (*p++ == ';') { |
| val = hex2i(&p); |
| context->regs.pc = val; |
| context->regs.npc = val + sizeof(MachInst); |
| } |
| setSingleStep(); |
| goto out; |
| |
| case KGDB_STEP: |
| if (p - data < datalen) { |
| val = hex2i(&p); |
| context->regs.pc = val; |
| context->regs.npc = val + sizeof(MachInst); |
| } |
| setSingleStep(); |
| goto out; |
| |
| case KGDB_CLR_HW_BKPT: |
| subcmd = *p++; |
| if (*p++ != ',') send("E0D"); |
| val = hex2i(&p); |
| if (*p++ != ',') send("E0D"); |
| len = hex2i(&p); |
| |
| DPRINTF(RGDB, "kgdb: clear %s, addr=%#x, len=%d\n", |
| break_type(subcmd), val, len); |
| |
| ret = false; |
| |
| switch (subcmd) { |
| case '0': // software breakpoint |
| ret = removeSoftBreak(val, len); |
| break; |
| |
| case '1': // hardware breakpoint |
| ret = removeHardBreak(val, len); |
| break; |
| |
| case '2': // write watchpoint |
| case '3': // read watchpoint |
| case '4': // access watchpoint |
| default: // unknown |
| send(""); |
| break; |
| } |
| |
| send(ret ? "OK" : "E0C"); |
| continue; |
| |
| case KGDB_SET_HW_BKPT: |
| subcmd = *p++; |
| if (*p++ != ',') send("E0D"); |
| val = hex2i(&p); |
| if (*p++ != ',') send("E0D"); |
| len = hex2i(&p); |
| |
| DPRINTF(RGDB, "kgdb: set %s, addr=%#x, len=%d\n", |
| break_type(subcmd), val, len); |
| |
| ret = false; |
| |
| switch (subcmd) { |
| case '0': // software breakpoint |
| ret = insertSoftBreak(val, len); |
| break; |
| |
| case '1': // hardware breakpoint |
| ret = insertHardBreak(val, len); |
| break; |
| |
| case '2': // write watchpoint |
| case '3': // read watchpoint |
| case '4': // access watchpoint |
| default: // unknown |
| send(""); |
| break; |
| } |
| |
| send(ret ? "OK" : "E0C"); |
| continue; |
| |
| case 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(RGDB, "kgdb: Unsupported command: %s\n", |
| gdb_command(command)); |
| DDUMP(RGDB, (uint8_t *)data, datalen); |
| send(""); |
| continue; |
| |
| default: |
| // Unknown command. |
| DPRINTF(RGDB, "kgdb: Unknown command: %c(%#x)\n", |
| command, command); |
| send(""); |
| continue; |
| |
| |
| } |
| } |
| |
| 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) |
| { |
| 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(void *vdst, const void *vsrc, int len) |
| { |
| char *dst = (char *)vdst; |
| const char *src = (const char *)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(void *vdst, const char *src, int maxlen) |
| { |
| 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; |
| } |
| 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); |
| } |
| |