diff options
Diffstat (limited to 'sys/gdb/gdb_main.c')
-rw-r--r-- | sys/gdb/gdb_main.c | 261 |
1 files changed, 261 insertions, 0 deletions
diff --git a/sys/gdb/gdb_main.c b/sys/gdb/gdb_main.c new file mode 100644 index 0000000..025c5b8 --- /dev/null +++ b/sys/gdb/gdb_main.c @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2004 Marcel Moolenaar + * All rights reserved. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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 AUTHORS 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kdb.h> +#include <sys/kernel.h> +#include <sys/pcpu.h> +#include <sys/proc.h> +#include <sys/reboot.h> + +#include <machine/gdb_machdep.h> +#include <machine/kdb.h> + +#include <gdb/gdb.h> +#include <gdb/gdb_int.h> + +static dbbe_init_f gdb_init; +static dbbe_trap_f gdb_trap; + +KDB_BACKEND(gdb, gdb_init, NULL, gdb_trap); + +GDB_DBGPORT(null, NULL, NULL, NULL, NULL, NULL, NULL); +SET_DECLARE(gdb_dbgport_set, struct gdb_dbgport); + +struct gdb_dbgport *gdb_cur = NULL; + +static int +gdb_init(void) +{ + struct gdb_dbgport *dp, **iter; + int cur_pri, pri; + + gdb_cur = NULL; + cur_pri = -1; + SET_FOREACH(iter, gdb_dbgport_set) { + dp = *iter; + pri = (dp->gdb_probe != NULL) ? dp->gdb_probe() : -1; + dp->gdb_active = (pri >= 0) ? 0 : -1; + if (pri > cur_pri) { + cur_pri = pri; + gdb_cur = dp; + } + } + if (gdb_cur != NULL) { + printf("GDB: debug ports:"); + SET_FOREACH(iter, gdb_dbgport_set) { + dp = *iter; + if (dp->gdb_active == 0) + printf(" %s", dp->gdb_name); + } + printf("\n"); + } else + printf("GDB: no debug ports present\n"); + if (gdb_cur != NULL) { + gdb_cur->gdb_init(); + printf("GDB: current port: %s\n", gdb_cur->gdb_name); + } + if (gdb_cur != NULL) + cur_pri = (boothowto & RB_GDB) ? 2 : 0; + else + cur_pri = -1; + return (cur_pri); +} + +static int +gdb_trap(int type, int code) +{ + struct thread *thr_iter; + + /* + * Send a T packet. We currently do not support watchpoints (the + * awatch, rwatch or watch elements). + */ + gdb_tx_begin('T'); + gdb_tx_hex(gdb_cpu_signal(type, code), 2); + gdb_tx_varhex(GDB_REG_PC); + gdb_tx_char(':'); + gdb_tx_reg(GDB_REG_PC); + gdb_tx_char(';'); + gdb_tx_str("thread:"); + gdb_tx_varhex((long)kdb_thread->td_tid); + gdb_tx_char(';'); + gdb_tx_end(); /* XXX check error condition. */ + + thr_iter = NULL; + while (gdb_rx_begin() == 0) { + printf("GDB: got '%s'\n", gdb_rxp); + switch (gdb_rx_char()) { + case '?': /* Last signal. */ + gdb_tx_begin('S'); + gdb_tx_hex(gdb_cpu_signal(type, code), 2); + gdb_tx_end(); + break; + case 'c': { /* Continue. */ + uintmax_t addr; + if (!gdb_rx_varhex(&addr)) + gdb_cpu_setreg(GDB_REG_PC, addr); + kdb_cpu_clear_singlestep(); + return (1); + } + case 'C': { /* Continue with signal. */ + uintmax_t addr, sig; + if (!gdb_rx_varhex(&sig) && gdb_rx_char() == ';' && + !gdb_rx_varhex(&addr)) + gdb_cpu_setreg(GDB_REG_PC, addr); + kdb_cpu_clear_singlestep(); + return (1); + } + case 'g': { /* Read registers. */ + size_t r; + gdb_tx_begin(0); + for (r = 0; r < GDB_NREGS; r++) + gdb_tx_reg(r); + gdb_tx_end(); + break; + } + case 'G': /* Write registers. */ + gdb_tx_err(0); + break; + case 'H': { /* Set thread. */ + intmax_t tid; + struct thread *thr; + gdb_rx_char(); + gdb_rx_varhex(&tid); + if (tid > 0) { + thr = kdb_thr_lookup(tid); + if (thr == NULL) { + gdb_tx_err(ENOENT); + break; + } + kdb_thr_select(thr); + } + gdb_tx_ok(); + break; + } + case 'k': /* Kill request. */ + kdb_cpu_clear_singlestep(); + return (1); + case 'm': { /* Read memory. */ + uintmax_t addr, size; + if (gdb_rx_varhex(&addr) || gdb_rx_char() != ',' || + gdb_rx_varhex(&size)) { + gdb_tx_err(EINVAL); + break; + } + gdb_tx_begin(0); + if (gdb_tx_mem((char *)(uintptr_t)addr, size)) + gdb_tx_end(); + else + gdb_tx_err(EIO); + break; + } + case 'M': { /* Write memory. */ + uintmax_t addr, size; + if (gdb_rx_varhex(&addr) || gdb_rx_char() != ',' || + gdb_rx_varhex(&size) || gdb_rx_char() != ':') { + gdb_tx_err(EINVAL); + break; + } + if (gdb_rx_mem((char *)(uintptr_t)addr, size) == 0) + gdb_tx_err(EIO); + else + gdb_tx_ok(); + break; + } + case 'P': { /* Write register. */ + uintmax_t reg, val; + if (gdb_rx_varhex(®) || gdb_rx_char() != '=' || + gdb_rx_varhex(&val)) { + gdb_tx_err(EINVAL); + break; + } + gdb_cpu_setreg(reg, val); + gdb_tx_ok(); + break; + } + case 'q': /* General query. */ + if (gdb_rx_equal("fThreadInfo")) { + thr_iter = kdb_thr_first(); + gdb_tx_begin('m'); + gdb_tx_hex((long)thr_iter->td_tid, 8); + gdb_tx_end(); + } else if (gdb_rx_equal("sThreadInfo")) { + if (thr_iter == NULL) { + gdb_tx_err(ENXIO); + break; + } + thr_iter = kdb_thr_next(thr_iter); + if (thr_iter != NULL) { + gdb_tx_begin('m'); + gdb_tx_hex((long)thr_iter->td_tid, 8); + gdb_tx_end(); + } else { + gdb_tx_begin('l'); + gdb_tx_end(); + } + } else if (!gdb_cpu_query()) + gdb_tx_empty(); + break; + case 's': { /* Step. */ + uintmax_t addr; + if (!gdb_rx_varhex(&addr)) + gdb_cpu_setreg(GDB_REG_PC, addr); + kdb_cpu_set_singlestep(); + return (1); + } + case 'S': { /* Step with signal. */ + uintmax_t addr, sig; + if (!gdb_rx_varhex(&sig) && gdb_rx_char() == ';' && + !gdb_rx_varhex(&addr)) + gdb_cpu_setreg(GDB_REG_PC, addr); + kdb_cpu_set_singlestep(); + return (1); + } + case 'T': { /* Thread alive. */ + intmax_t tid; + gdb_rx_varhex(&tid); + if (kdb_thr_lookup(tid) != NULL) + gdb_tx_ok(); + else + gdb_tx_err(ENOENT); + break; + } + case -1: + /* Empty command. Treat as unknown command. */ + /* FALLTHROUGH */ + default: + /* Unknown command. Send empty response. */ + gdb_tx_empty(); + break; + } + } + return (0); +} |