diff options
Diffstat (limited to 'gnu/usr.bin/kgdb/remote.c')
-rw-r--r-- | gnu/usr.bin/kgdb/remote.c | 626 |
1 files changed, 626 insertions, 0 deletions
diff --git a/gnu/usr.bin/kgdb/remote.c b/gnu/usr.bin/kgdb/remote.c new file mode 100644 index 000000000000..b757f4b8ef76 --- /dev/null +++ b/gnu/usr.bin/kgdb/remote.c @@ -0,0 +1,626 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Van Jacobson and Steven McCanne of Lawrence Berkeley Laboratory. + * + * 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. + * + * $Header: /home/ncvs/src/gnu/usr.bin/kgdb/remote.c,v 1.2 1995/05/30 05:01:12 rgrimes Exp $; + */ + +#ifndef lint +static char sccsid[] = "@(#)remote.c 6.5 (Berkeley) 5/8/91"; +#endif /* not lint */ + +#include "param.h" + +#include <stdio.h> +#include <varargs.h> + +#include <signal.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/file.h> + +#include "defs.h" +#include "frame.h" +#include "inferior.h" +#include "wait.h" + +#include "kgdb_proto.h" + +static FILE *kiodebug; +static int icache = 1; +extern int kernel_debugging; + +static int remote_cache_valid; +static int remote_instub; + +static void remote_signal(); +static void remote_debug(); +static void print_msg(); + +static int remote_mtu; +static int (*send_msg)(); +static int (*recv_msg)(); +static void (*closelink)(); + +static u_char *inbuffer; +static u_char *outbuffer; + +/* + * Statistics. + */ +static int remote_ierrs; +static int remote_oerrs; +static int remote_seqerrs; +static int remote_spurious; + +#define PUTCMD(cmd) m_xchg(cmd, (u_char *)0, 0, (u_char *)0, (int *)0) + +/* + * Send an outbound message to the remote machine and read the reply. + * Either or both message buffers may be NULL. + */ +static int +m_xchg(type, out, outlen, in, inlen) + int type; + u_char *out; + int outlen; + u_char *in; + int *inlen; +{ + register int err, (*send)() = send_msg, (*recv)() = recv_msg; + int ack; + static int seqbit = 0; + + if (!remote_instub) { + remote_instub = 1; + PUTCMD(KGDB_EXEC); + } + + seqbit ^= KGDB_SEQ; + while (1) { + err = (*send)(type | seqbit, out, outlen); + if (err) { + ++remote_oerrs; + if (kiodebug) + remote_debug("send error %d\n", err); + } + if (kiodebug) + print_msg(type | seqbit, out, outlen, 'O'); + + recv: + err = (*recv)(&ack, in, inlen); + if (err) { + ++remote_ierrs; + if (kiodebug) + remote_debug("recv error %d\n", err); + remote_cache_valid = 0; + } else if (kiodebug) + print_msg(ack, in, inlen ? *inlen : 0, 'I'); + + if (err) + continue; + + if ((ack & KGDB_ACK) == 0 || KGDB_CMD(ack) != KGDB_CMD(type)) { + ++remote_spurious; + continue; + } + if ((ack & KGDB_SEQ) ^ seqbit) { + ++remote_seqerrs; + goto recv; + } + return ack; + } +} + +/* + * Wait for the specified message type. Discard anything else. + * (this is used by 'remote-signal' to help us resync with other side.) + */ +static void +m_recv(type, in, inlen) + int type; + u_char *in; + int *inlen; +{ + int reply, err; + + while (1) { + err = (*recv_msg)(&reply, in, inlen); + if (err) { + ++remote_ierrs; + if (kiodebug) + remote_debug("recv error %d\n", err); + } else if (kiodebug) + print_msg(reply, in, inlen ? *inlen : 0, 'I'); + + if (KGDB_CMD(reply) == type) + return; + ++remote_spurious; + } +} + +/* + * Send a message. Do not wait for *any* response from the other side. + * Some other thread of control will pick up the ack that will be generated. + */ +static void +m_send(type, buf, len) + int type; + u_char *buf; + int len; +{ + int err; + + if (!remote_instub) { + remote_instub = 1; + PUTCMD(KGDB_EXEC); + } + + err = (*send_msg)(type, buf, len); + if (err) { + ++remote_ierrs; + if (kiodebug) + remote_debug("[send error %d] ", err); + } + if (kiodebug) + print_msg(type, buf, len, 'O'); +} + +/* + * Open a connection to a remote debugger. + * NAME is the filename used for communication. + */ +void +remote_open(name, from_tty) + char *name; + int from_tty; +{ + int bufsize; + + remote_debugging = 0; + if (sl_open(name, &send_msg, &recv_msg, &closelink, &remote_mtu, + &bufsize)) + return; + if (from_tty) + printf("Remote debugging using %s\n", name); + remote_debugging = 1; + + remote_cache_valid = 0; + + inbuffer = (u_char *)malloc(bufsize); + outbuffer = (u_char *)malloc(bufsize); + + remote_signal(); + + remote_ierrs = 0; + remote_oerrs = 0; + remote_spurious = 0; +} + +/* + * Close the open connection to the remote debugger. Use this when you want + * to detach and do something else with your gdb. + */ +void +remote_close(from_tty) + int from_tty; +{ + if (!remote_debugging) + error("remote debugging not enabled"); + + remote_debugging = 0; + /* + * Take remote machine out of debug mode. + */ + (void)PUTCMD(KGDB_KILL); + (*closelink)(); + if (from_tty) + printf("Ending remote debugging\n"); + + free((char *)inbuffer); + free((char *)outbuffer); +} + +/* + * Tell the remote machine to resume. + */ +int +remote_resume(step, signal) + int step, signal; +{ + if (!step) { + (void)PUTCMD(KGDB_CONT); + remote_instub = 0; + } else { +#ifdef NO_SINGLE_STEP + single_step(0); +#else + (void)PUTCMD(KGDB_STEP); +#endif + } +} + +/* + * Wait until the remote machine stops, then return, storing status in STATUS + * just as `wait' would. + */ +int +remote_wait(status) + WAITTYPE *status; +{ + int len; + + WSETEXIT((*status), 0); + /* + * When the machine stops, it will send us a KGDB_SIGNAL message, + * so we wait for one of these. + */ + m_recv(KGDB_SIGNAL, inbuffer, &len); + WSETSTOP((*status), inbuffer[0]); +} + +/* + * Register context as of last remote_fetch_registers(). + */ +static char reg_cache[REGISTER_BYTES]; + +/* + * Read the remote registers into the block REGS. + */ +void +remote_fetch_registers(regs) + char *regs; +{ + int regno, len, rlen, ack; + u_char *cp, *ep; + + regno = -1; + do { + outbuffer[0] = regno + 1; + ack = m_xchg(remote_cache_valid ? + KGDB_REG_R|KGDB_DELTA : KGDB_REG_R, + outbuffer, 1, inbuffer, &len); + cp = inbuffer; + ep = cp + len; + while (cp < ep) { + regno = *cp++; + rlen = REGISTER_RAW_SIZE(regno); + bcopy((char *)cp, + ®_cache[REGISTER_BYTE(regno)], rlen); + cp += rlen; + } + } while (ack & KGDB_MORE); + + remote_cache_valid = 1; + bcopy(reg_cache, regs, REGISTER_BYTES); +} + +/* + * Store the remote registers from the contents of the block REGS. + */ +void +remote_store_registers(regs) + char *regs; +{ + u_char *cp, *ep; + int regno, off, rlen; + + cp = outbuffer; + ep = cp + remote_mtu; + + for (regno = 0; regno < NUM_REGS; ++regno) { + off = REGISTER_BYTE(regno); + rlen = REGISTER_RAW_SIZE(regno); + if (!remote_cache_valid || + bcmp(®s[off], ®_cache[off], rlen) != 0) { + if (cp + rlen + 1 >= ep) { + (void)m_xchg(KGDB_REG_W, + outbuffer, cp - outbuffer, + (u_char *)0, (int *)0); + cp = outbuffer; + } + *cp++ = regno; + bcopy(®s[off], cp, rlen); + cp += rlen; + } + } + if (cp != outbuffer) + (void)m_xchg(KGDB_REG_W, outbuffer, cp - outbuffer, + (u_char *)0, (int *)0); + bcopy(regs, reg_cache, REGISTER_BYTES); +} + +/* + * Store a chunk of memory into the remote host. + * 'remote_addr' is the address in the remote memory space. + * 'cp' is the address of the buffer in our space, and 'len' is + * the number of bytes. Returns an errno status. + */ +int +remote_write_inferior_memory(remote_addr, cp, len) + CORE_ADDR remote_addr; + u_char *cp; + int len; +{ + int cnt; + + while (len > 0) { + cnt = min(len, remote_mtu - 4); + bcopy((char *)&remote_addr, outbuffer, 4); + bcopy(cp, outbuffer + 4, cnt); + (void)m_xchg(KGDB_MEM_W, outbuffer, cnt + 4, inbuffer, &len); + + if (inbuffer[0]) + return inbuffer[0]; + + remote_addr += cnt; + cp += cnt; + len -= cnt; + } + return 0; +} + +/* + * Read memory data directly from the remote machine. + * 'remote_addr' is the address in the remote memory space. + * 'cp' is the address of the buffer in our space, and 'len' is + * the number of bytes. Returns an errno status. + */ +static int +remote_read_memory(remote_addr, cp, len) + CORE_ADDR remote_addr; + u_char *cp; + int len; +{ + int cnt, inlen; + + while (len > 0) { + cnt = min(len, remote_mtu - 1); + outbuffer[0] = cnt; + bcopy((char *)&remote_addr, (char *)&outbuffer[1], 4); + + (void)m_xchg(KGDB_MEM_R, outbuffer, 5, inbuffer, &inlen); + + if (inbuffer[0] != 0) + return inbuffer[0]; + + if (cnt != inlen - 1) + /* XXX */ + error("remote_read_memory() request botched"); + + bcopy((char *)&inbuffer[1], (char *)cp, cnt); + + remote_addr += cnt; + cp += cnt; + len -= cnt; + } + return 0; +} + +int +remote_read_inferior_memory(remote_addr, cp, len) + CORE_ADDR remote_addr; + char *cp; + int len; +{ + int stat = 0; + + if (icache) { + extern CORE_ADDR text_start, text_end; + CORE_ADDR xferend = remote_addr + len; + + if (remote_addr < text_end && text_start < xferend) { + /* + * at least part of this xfer is in the text + * space -- xfer the overlap from the exec file. + */ + if (remote_addr >= text_start && xferend < text_end) + return (xfer_core_file(remote_addr, cp, len)); + if (remote_addr >= text_start) { + int i = text_end - remote_addr; + + if (stat = xfer_core_file(remote_addr, cp, i)) + return (stat); + remote_addr += i; + cp += i; + len -= i; + } else if (xferend <= text_end) { + int i = xferend - text_start; + + len = text_start - remote_addr; + if (stat = xfer_core_file(text_start, + cp + len, i)) + return (stat); + } + } + } + return remote_read_memory(remote_addr, cp, len); +} + +/* + * Signal the remote machine. The remote end might be idle or it might + * already be in debug mode -- we need to handle both case. Thus, we use + * the framing character as the wakeup byte, and send a SIGNAL packet. + * If the remote host is idle, the framing character will wake it up. + * If it is in the kgdb stub, then we will get a SIGNAL reply. + */ +static void +remote_signal() +{ + if (!remote_debugging) + printf("Remote debugging not enabled.\n"); + else { + remote_instub = 0; + m_send(KGDB_SIGNAL, (u_char *)0, 0); + } +} + +static void +remote_signal_command() +{ + extern int stop_after_attach; + + if (!remote_debugging) + error("Not debugging remote."); + remote_cache_valid = 0; + remote_signal(); + restart_remote(); +} + +/* + * Print a message for debugging. + */ +static void +print_msg(type, buf, len, dir) + int type; + u_char *buf; + int len; + int dir; +{ + int i; + char *s; + + switch (KGDB_CMD(type)) { + case KGDB_MEM_R: s = "memr"; break; + case KGDB_MEM_W: s = "memw"; break; + case KGDB_REG_R: s = "regr"; break; + case KGDB_REG_W: s = "regw"; break; + case KGDB_CONT: s = "cont"; break; + case KGDB_STEP: s = "step"; break; + case KGDB_KILL: s = "kill"; break; + case KGDB_SIGNAL: s = "sig "; break; + case KGDB_EXEC: s = "exec"; break; + default: s = "unk "; break; + } + remote_debug("%c %c%c%c%c %s (%02x): ", dir, + (type & KGDB_ACK) ? 'A' : '.', + (type & KGDB_DELTA) ? 'D' : '.', + (type & KGDB_MORE) ? 'M' : '.', + (type & KGDB_SEQ) ? '-' : '+', + s, type); + if (buf) + for (i = 0; i < len; ++i) + remote_debug("%02x", buf[i]); + remote_debug("\n"); +} + +static void +set_remote_text_refs_command(arg, from_tty) + char *arg; + int from_tty; +{ + icache = !parse_binary_operation("set remote-text-refs", arg); +} + +static void +remote_debug_command(arg, from_tty) + char *arg; + int from_tty; +{ + char *name; + + if (kiodebug != 0 && kiodebug != stderr) + (void)fclose(kiodebug); + + if (arg == 0) { + kiodebug = 0; + printf("Remote debugging off.\n"); + return; + } + if (arg[0] == '-') { + kiodebug = stderr; + name = "stderr"; + } else { + kiodebug = fopen(arg, "w"); + if (kiodebug == 0) { + printf("Cannot open '%s'.\n", arg); + return; + } + name = arg; + } + printf("Remote debugging output routed to %s.\n", name); +} + +/* ARGSUSED */ +static void +remote_info(arg, from_tty) + char *arg; + int from_tty; +{ + printf("Using %s for text references.\n", + icache? "local executable" : "remote"); + printf("Protocol debugging is %s.\n", kiodebug? "on" : "off"); + printf("%d spurious input messages.\n", remote_spurious); + printf("%d input errors; %d output errors; %d sequence errors.\n", + remote_ierrs, remote_oerrs, remote_seqerrs); +} + +/* VARARGS */ +static void +remote_debug(va_alist) + va_dcl +{ + register char *cp; + va_list ap; + + va_start(ap); + cp = va_arg(ap, char *); + (void)vfprintf(kiodebug, cp, ap); + va_end(ap); + fflush(kiodebug); +} + +extern struct cmd_list_element *setlist; + +void +_initialize_remote() +{ + add_com("remote-signal", class_run, remote_signal_command, + "If remote debugging, send interrupt signal to remote."); + add_cmd("remote-text-refs", class_support, + set_remote_text_refs_command, +"Enable/disable use of local executable for text segment references.\n\ +If on, all memory read/writes go to remote.\n\ +If off, text segment reads use the local executable.", + &setlist); + + add_com("remote-debug", class_run, remote_debug_command, +"With a file name argument, enables output of remote protocol debugging\n\ +messages to said file. If file is `-', stderr is used.\n\ +With no argument, remote debugging is disabled."); + + add_info("remote", remote_info, + "Show current settings of remote debugging options."); +} + |