/* * Copyright (c) 1990, 1991, 1992 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice and this paragraph are * duplicated in all such forms and that any documentation, * advertising materials, and other materials related to such * distribution and use acknowledge that the software was developed * by the University of California, Lawrence Berkeley Laboratory, * Berkeley, CA. The name of the University may not be used to * endorse or promote products derived from this software without * specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #ifndef lint static char rcsid[] = "$Header: remote.c,v 1.3 93/04/26 16:23:50 mccanne Exp $"; #endif #include "defs.h" #include <varargs.h> #include <signal.h> #include <sys/types.h> #include <sys/ioctl.h> #include <sys/file.h> #include "target.h" #include "frame.h" #include "inferior.h" #include "wait.h" #include "gdbcmd.h" #include "remote.h" #include "kgdb_proto.h" extern struct target_ops remote_ops; /* Forward decl */ static int remote_debugging; static int remote_quiet; static int to_clamp = 8000; static int to_base = 1000; /* * Eponentially back off a timer value. Clamp the value at to_clamp. */ #define BACKOFF(to) ((2 * (to) < to_clamp) ? 2 * (to) : to_clamp) static FILE *kiodebug; static int icache = 1; static int remote_cache_valid; static int remote_instub; static void remote_signal(); static void remote_debug(); static void print_msg(); static struct remote_fn remote_fn; 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, nrecv; register int (*send)() = remote_fn.send, (*recv)() = remote_fn.recv; int ack; u_long to = to_base; static int seqbit = 0; if (!remote_instub) { remote_instub = 1; (void)PUTCMD(KGDB_EXEC); } seqbit ^= KGDB_SEQ; while (1) { nrecv = 0; err = (*send)(type | seqbit, out, outlen); if (err) { ++remote_oerrs; if (kiodebug) remote_debug("send error %d\n", err); /* XXX shouldn't we retry the !@#$^*ing send? */ } if (kiodebug) print_msg(type | seqbit, out, outlen, 'O'); again: err = (*recv)(&ack, in, inlen, to); if (err != 0) { ++remote_ierrs; if (kiodebug) remote_debug("recv error %d\n", err); remote_cache_valid = 0; if (err == EKGDB_TIMEOUT) { to = BACKOFF(to); if (to >= to_clamp) error("remote host not responding"); continue; } if (++nrecv < 5) /* * Try to receive five times before * retransmitting. */ goto again; continue; } else if (kiodebug) print_msg(ack, in, inlen ? *inlen : 0, 'I'); if ((ack & KGDB_ACK) == 0 || KGDB_CMD(ack) != KGDB_CMD(type)) { ++remote_spurious; continue; } if ((ack & KGDB_SEQ) ^ seqbit) { ++remote_seqerrs; goto again; } 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 = (*remote_fn.recv)(&reply, in, inlen, -1); 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; (void)PUTCMD(KGDB_EXEC); } err = (*remote_fn.send)(type, buf, len); if (err) { ++remote_ierrs; if (kiodebug) remote_debug("[send error %d] ", err); } if (kiodebug) print_msg(type, buf, len, 'O'); } void start_remote() { init_wait_for_inferior(); remote_go(); } void restart_remote() { init_wait_for_inferior_keep_brkpts(); #ifdef notdef inferior_pid = 3; #endif remote_go(); } static void remote_bail(arg) void *arg; { printf("Remote attach interrupted.\n"); remote_quiet = 1; pop_target(); } /* * 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; { extern void sl_open(), fp_open(); void (*func)(); struct cleanup *chain; if (name == 0) error ( "\ To open a remote debug connection, you need to specify what serial\n\ device is attached to the remote system (e.g. /dev/ttya).\ "); if (remote_debugging) error("Already remote debugging. Detach first.\n"); target_preopen(from_tty); /* Right now, we only support fastpath and serial debugging */ #ifndef NOFASTPATH if (*name == '@') { ++name; func = fp_open; } else #endif func = sl_open; bzero((caddr_t)&remote_fn, sizeof(remote_fn)); (*func)(name, &remote_fn); if (from_tty) printf("Remote debugging using %s\n", name); remote_debugging = 1; remote_cache_valid = 0; /* remote protocol limits max data to one byte */ if (remote_fn.maxdata > 255) remote_fn.maxdata = 255; /* allocate msg buffers */ inbuffer = (u_char *)malloc(remote_fn.rpcsize); outbuffer = (u_char *)malloc(remote_fn.rpcsize); chain = make_cleanup(remote_bail, 0); push_target(&remote_ops); remote_ierrs = 0; remote_oerrs = 0; remote_spurious = 0; /* * XXX * Signal the remote kernel and set things up * so gdb views it as a process. */ remote_signal(); start_remote(); discard_cleanups(chain); } /* * Take the remote kernel out of debugging mode. */ void remote_kill() { /* XXX * Clear all breakpoints. * It's a very, very bad idea to go away leaving * breakpoints in a remote kernel or to leave it * stopped at a breakpoint. */ clear_breakpoints(); /* * Take remote machine out of debug mode. */ (void)PUTCMD(KGDB_KILL); remote_debugging = 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_detach(args, from_tty) char *args; int from_tty; { if (args) { if (args[0] == 'q') remote_quiet = 1; else error("Bad argument to remote \"detach\"."); } /* this will invoke remote_close() */ pop_target(); if (from_tty) printf("Ending remote debugging.\n"); } void remote_close(quitting) int quitting; { #ifdef notdef if (!remote_debugging) error("Remote debugging not enabled"); #endif if (!remote_quiet) { remote_quiet = 0; remote_kill(); } else remote_debugging = 0; (void)(*remote_fn.close)(); free((caddr_t)inbuffer); free((caddr_t)outbuffer); } /* * Tell the remote machine to resume. */ void remote_resume(step, sig) int step, sig; { #ifdef notdef if (sig != 0) error("Cannot signal a remote system"); #endif 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]); /* * Let the machine dependent module have a chance to * lookup current process context etc. */ set_curproc(); return (0); } /* * Register context as of last remote_fetch_registers(). */ static char reg_cache[REGISTER_BYTES]; extern char registers[]; /* * Read the remote registers into the block REGS. */ /* XXX Currently we just read all the registers, so we don't use regno. */ /* ARGSUSED */ void remote_fetch_registers(unused_regno) int unused_regno; { 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((caddr_t)cp, ®_cache[REGISTER_BYTE(regno)], rlen); cp += rlen; } } while (ack & KGDB_MORE); remote_cache_valid = 1; bcopy(reg_cache, registers, REGISTER_BYTES); } /* * Store the remote registers from the contents of the block REGS. */ /* XXX Currently we just read all the registers, so we don't use regno. */ /* ARGSUSED */ void remote_store_registers(unused_regno) int unused_regno; { char *regs = registers; u_char *cp, *ep; int regno, off, rlen; cp = outbuffer; ep = cp + remote_fn.maxdata; 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], (caddr_t)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); } /* * XXX DOES THE NEW PROTOCOL NEED THIS? * Prepare to store registers. Since we send them all, we have to * read out the ones we don't want to change first. */ void remote_prepare_to_store () { remote_fetch_registers(-1); } /* * 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(addr, cp, len) CORE_ADDR addr; u_char *cp; int len; { int cnt; while (len > 0) { /* XXX assumes sizeof(CORE_ADDR) is 4? */ cnt = min(len, remote_fn.maxdata - 4); bcopy((caddr_t)&addr, (caddr_t)outbuffer, 4); SWAP_TARGET_AND_HOST(outbuffer, 4); bcopy((caddr_t)cp, (caddr_t)outbuffer + 4, cnt); (void)m_xchg(KGDB_MEM_W, outbuffer, cnt + 4, inbuffer, &len); if (inbuffer[0]) return (inbuffer[0]); addr += cnt; cp += cnt; len -= cnt; } return 0; } /* * Read memory data directly from the remote machine. * '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. */ /* XXX is this really needed? */ static int remote_read(addr, cp, len) CORE_ADDR addr; u_char *cp; int len; { int cnt, inlen; while (len > 0) { cnt = min(len, remote_fn.maxdata); outbuffer[0] = cnt; bcopy((caddr_t)&addr, (caddr_t)&outbuffer[1], 4); SWAP_TARGET_AND_HOST(&outbuffer[1], 4); (void)m_xchg(KGDB_MEM_R, outbuffer, 5, inbuffer, &inlen); if (inlen < 1) error("remote_read(): remote protocol botch"); /* Return errno from remote side */ if (inbuffer[0]) return (inbuffer[0]); --inlen; if (inlen <= 0) error("remote_read(): inlen too small (%d)", inlen); if (inlen > cnt) { printf( "remote_read(): warning: asked for %d, got %d\n", cnt, inlen); inlen = cnt; } bcopy((caddr_t)&inbuffer[1], (caddr_t)cp, inlen); addr += inlen; cp += inlen; len -= inlen; } return (0); } static int remote_read_text(addr, cp, len, target) CORE_ADDR addr; char *cp; int len; struct target_ops *target; { register int cc; /* * Look down the target stack (we're on top) for the text file. * If it can transfer the whole chunk, let it. Otherwise, * revert to the remote call. */ for (; target != 0; target = target->to_next) { if (target->to_stratum != file_stratum) continue; cc = target->to_xfer_memory(addr, cp, len, 0, target); if (cc == len) return (0); } return (remote_read(addr, cp, len)); } /* * Read or write LEN bytes from inferior memory at MEMADDR, transferring * to or from debugger address MYADDR. Write to inferior if SHOULD_WRITE is * nonzero. Returns length of data written or read; 0 for error. */ /* ARGSUSED */ int remote_xfer_memory(addr, cp, len, should_write, target) register CORE_ADDR addr; register char *cp; register int len; int should_write; struct target_ops *target; { register int st; if (should_write) st = remote_write(addr, cp, len); else { #ifdef NEED_TEXT_START_END extern CORE_ADDR text_start, text_end; st = (icache && addr >= text_start && addr + len <= text_end) ? remote_read_text(addr, cp, len, target) : remote_read(addr, cp, len); #else st = remote_read(addr, cp, len); #endif } return ((st == 0) ? len : 0); } /* * 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 agent not active.\n"); else { remote_instub = 0; m_send(KGDB_SIGNAL, (u_char *)0, 0); } } static void remote_signal_command() { if (!remote_debugging) error("Remote agent not active.\n"); 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 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_files_info() { 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); } /* Define the target subroutine names */ struct target_ops remote_ops = { "remote", /* shortname */ "Remote serial target in gdb-specific protocol", /* longname */ "Debug a remote host, using a gdb-specific protocol.\n\ Specify the target as an argument (e.g. /dev/ttya for a serial link).\n\ Use \"detach\" or \"quit\" to end remote debugging gracefully, which\n\ removes breakpoints from and resumes the remote kernel.\n\ Use \"detach quiet\" to end remote debugging with no negotiation.\n\ This latter method is desirable, for instance, when the remote kernel\n\ has crashed and messages from gdb could wreak havoc.\n", remote_open, /* open */ remote_close, /* close */ 0, /* attach */ remote_detach, /* detach */ remote_resume, /* resume */ remote_wait, /* wait */ remote_fetch_registers, /* fetch_registers */ remote_store_registers, /* store_registers */ remote_prepare_to_store, /* prepare_to_store */ remote_xfer_memory, /* xfer_memory */ remote_files_info, /* files_info */ 0, /* insert_breakpoint */ 0, /* remove_breakpoint */ 0, /* terminal_init */ 0, /* terminal_inferior */ 0, /* terminal_ours_for_output */ 0, /* terminal_ours */ 0, /* terminal_info */ remote_kill, /* kill */ 0, /* load */ 0, /* lookup_symbol */ 0, /* create_inferior */ 0, /* mourn_inferior */ 0, /* can_run */ 0, /* notice_signals */ process_stratum, /* stratum */ 0, /* next */ 1, /* has_all_memory */ 1, /* has_memory */ 1, /* has_stack */ 1, /* has_registers */ 1, /* has_execution XXX can't start an inferior */ 0, /* sections */ 0, /* sections_end */ OPS_MAGIC, /* magic */ }; void _initialize_remote () { struct cmd_list_element *c; extern struct cmd_list_element *setlist; add_com("remote-signal", class_run, remote_signal_command, "If remote debugging, send interrupt signal to remote."); c = add_set_cmd ("remote-text-refs", class_support, var_boolean, (char *)&icache, "Set 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_show_from_set (c, &showlist); c = add_set_cmd ("remote-timeout", class_support, var_uinteger, (char *)&to_base, "Set remote timeout interval (in msec). The gdb remote protocol\n\ uses an exponential backoff retransmit timer that begins initialized\n\ to this value (on each transmission).", &setlist); add_show_from_set (c, &showlist); 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."); #ifdef notdef add_info("remote", remote_info, "Show current settings of remote debugging options."); #endif add_target (&remote_ops); }