/* Remote debugging interface for MIPS remote debugging protocol.
- Copyright 1993, 1994 Free Software Foundation, Inc.
+ Copyright 1993, 1994, 1995 Free Software Foundation, Inc.
Contributed by Cygnus Support. Written by Ian Lance Taylor
<ian@cygnus.com>.
#include "remote-utils.h"
#include <signal.h>
+#ifdef ANSI_PROTOTYPES
+#include <stdarg.h>
+#else
#include <varargs.h>
+#endif
+
+extern char *mips_read_processor_type PARAMS ((void));
+
+extern void mips_set_processor_type_command PARAMS ((char *, int));
+
\f
/* Prototypes for local functions. */
-static int
-mips_readchar PARAMS ((int timeout));
+static int mips_readchar PARAMS ((int timeout));
-static int
-mips_receive_header PARAMS ((unsigned char *hdr, int *pgarbage, int ch,
- int timeout));
+static int mips_receive_header PARAMS ((unsigned char *hdr, int *pgarbage,
+ int ch, int timeout));
-static int
-mips_receive_trailer PARAMS ((unsigned char *trlr, int *pgarbage, int *pch,
- int timeout));
+static int mips_receive_trailer PARAMS ((unsigned char *trlr, int *pgarbage,
+ int *pch, int timeout));
static int mips_cksum PARAMS ((const unsigned char *hdr,
const unsigned char *data,
int len));
-static void
-mips_send_packet PARAMS ((const char *s, int get_ack));
+static void mips_send_packet PARAMS ((const char *s, int get_ack));
static int mips_receive_packet PARAMS ((char *buff, int throw_error,
int timeout));
-static int
-mips_request PARAMS ((char cmd, unsigned int addr, unsigned int data,
- int *perr, timeout));
+static int mips_request PARAMS ((char cmd, unsigned int addr,
+ unsigned int data, int *perr, int timeout));
-static void
-mips_initialize PARAMS ((void));
+static void mips_initialize PARAMS ((void));
-static void
-mips_open PARAMS ((char *name, int from_tty));
+static void mips_open PARAMS ((char *name, int from_tty));
-static void
-mips_close PARAMS ((int quitting));
+static void mips_close PARAMS ((int quitting));
-static void
-mips_detach PARAMS ((char *args, int from_tty));
+static void mips_detach PARAMS ((char *args, int from_tty));
static void mips_resume PARAMS ((int pid, int step,
enum target_signal siggnal));
-static int
-mips_wait PARAMS ((int pid, WAITTYPE *status));
+static int mips_wait PARAMS ((int pid, struct target_waitstatus *status));
-static int
-mips_map_regno PARAMS ((int regno));
+static int mips_map_regno PARAMS ((int regno));
-static void
-mips_fetch_registers PARAMS ((int regno));
+static void mips_fetch_registers PARAMS ((int regno));
-static void
-mips_prepare_to_store PARAMS ((void));
+static void mips_prepare_to_store PARAMS ((void));
-static void
-mips_store_registers PARAMS ((int regno));
+static void mips_store_registers PARAMS ((int regno));
-static int
-mips_fetch_word PARAMS ((CORE_ADDR addr));
+static int mips_fetch_word PARAMS ((CORE_ADDR addr));
-static void
-mips_store_word PARAMS ((CORE_ADDR addr, int value));
+static int mips_store_word PARAMS ((CORE_ADDR addr, int value,
+ char *old_contents));
-static int
-mips_xfer_memory PARAMS ((CORE_ADDR memaddr, char *myaddr, int len,
- int write, struct target_ops *ignore));
+static int mips_xfer_memory PARAMS ((CORE_ADDR memaddr, char *myaddr, int len,
+ int write, struct target_ops *ignore));
-static void
-mips_files_info PARAMS ((struct target_ops *ignore));
+static void mips_files_info PARAMS ((struct target_ops *ignore));
-static void
-mips_load PARAMS ((char *args, int from_tty));
+static void mips_create_inferior PARAMS ((char *execfile, char *args,
+ char **env));
-static void
-mips_create_inferior PARAMS ((char *execfile, char *args, char **env));
-
-static void
-mips_mourn_inferior PARAMS ((void));
+static void mips_mourn_inferior PARAMS ((void));
/* A forward declaration. */
extern struct target_ops mips_ops;
The value is
0x40 + seq
An acknowlegment packet contains the sequence number of the
- packet being acknowledged plus 1 module 64. Data packets are
+ packet being acknowledged plus 1 modulo 64. Data packets are
transmitted in sequence. There may only be one outstanding
unacknowledged data packet at a time. The sequence numbers
are independent in each direction. If an acknowledgement for
all hell to break loose--the rest of GDB will tend to get left in an
inconsistent state. */
-static void NORETURN
+static NORETURN void
+#ifdef ANSI_PROTOTYPES
+mips_error (char *string, ...)
+#else
mips_error (va_alist)
va_dcl
+#endif
{
va_list args;
- char *string;
+#ifdef ANSI_PROTOTYPES
+ va_start (args, string);
+#else
+ char *string;
va_start (args);
+ string = va_arg (args, char *);
+#endif
+
target_terminal_ours ();
wrap_here(""); /* Force out any buffered output */
gdb_flush (gdb_stdout);
if (error_pre_print)
fprintf_filtered (gdb_stderr, error_pre_print);
- string = va_arg (args, char *);
vfprintf_filtered (gdb_stderr, string, args);
fprintf_filtered (gdb_stderr, "\n");
va_end (args);
int ch;
static int state = 0;
static char nextstate[5] = { '<', 'I', 'D', 'T', '>' };
+#ifdef MAINTENANCE_CMDS
+ int i;
+ i = timeout;
+ if (i == -1 && watchdog > 0)
+ i = watchdog;
+#endif
+
+ if (state == 5)
+ timeout = 1;
ch = SERIAL_READCHAR (mips_desc, timeout);
+#ifdef MAINTENANCE_CMDS
+ if (ch == SERIAL_TIMEOUT && timeout == -1) /* Watchdog went off */
+ {
+ target_mourn_inferior ();
+ error ("Watchdog has expired. Target detached.\n");
+ }
+#endif
if (ch == SERIAL_EOF)
mips_error ("End of file from remote");
if (ch == SERIAL_ERROR)
mips_error ("Error reading from remote: %s", safe_strerror (errno));
if (sr_get_debug () > 1)
{
+ /* Don't use _filtered; we can't deal with a QUIT out of
+ target_wait, and I think this might be called from there. */
if (ch != SERIAL_TIMEOUT)
- printf_filtered ("Read '%c' %d 0x%x\n", ch, ch, ch);
+ printf_unfiltered ("Read '%c' %d 0x%x\n", ch, ch, ch);
else
- printf_filtered ("Timed out in read\n");
+ printf_unfiltered ("Timed out in read\n");
}
/* If we have seen <IDT> and we either time out, or we see a @
&& ! mips_initializing)
{
if (sr_get_debug () > 0)
- printf_filtered ("Reinitializing MIPS debugging mode\n");
- SERIAL_WRITE (mips_desc, "\rdb tty0\r", sizeof "\rdb tty0\r" - 1);
+ /* Don't use _filtered; we can't deal with a QUIT out of
+ target_wait, and I think this might be called from there. */
+ printf_unfiltered ("Reinitializing MIPS debugging mode\n");
+ SERIAL_WRITE (mips_desc, "\015db tty0\015", sizeof "\015db tty0\015" - 1);
sleep (1);
mips_need_reply = 0;
state = 0;
- mips_error ("Remote board reset");
+ /* At this point, about the only thing we can do is abort the command
+ in progress and get back to command level as quickly as possible. */
+
+ error ("Remote board reset, debug protocol re-initialized.");
}
if (ch == nextstate[state])
{
/* Printing the character here lets the user of gdb see
what the program is outputting, if the debugging is
- being done on the console port. FIXME: Perhaps this
- should be filtered? */
+ being done on the console port. Don't use _filtered;
+ we can't deal with a QUIT out of target_wait. */
if (! mips_initializing || sr_get_debug () > 0)
{
- putchar_unfiltered (ch);
+ if (ch < 0x20 && ch != '\n')
+ {
+ putchar_unfiltered ('^');
+ putchar_unfiltered (ch + 0x40);
+ }
+ else
+ putchar_unfiltered (ch);
gdb_flush (gdb_stdout);
}
if (sr_get_debug () > 0)
{
+ /* Don't use _filtered; we can't deal with a QUIT out of
+ target_wait, and I think this might be called from there. */
packet[HDR_LENGTH + len + TRLR_LENGTH] = '\0';
- printf_filtered ("Writing \"%s\"\n", packet + 1);
+ printf_unfiltered ("Writing \"%s\"\n", packet + 1);
}
if (SERIAL_WRITE (mips_desc, packet,
{
hdr[HDR_LENGTH] = '\0';
trlr[TRLR_LENGTH] = '\0';
- printf_filtered ("Got ack %d \"%s%s\"\n",
+ /* Don't use _filtered; we can't deal with a QUIT out of
+ target_wait, and I think this might be called from there. */
+ printf_unfiltered ("Got ack %d \"%s%s\"\n",
HDR_GET_SEQ (hdr), hdr + 1, trlr);
}
/* An acknowledgement is probably a duplicate; ignore it. */
if (! HDR_IS_DATA (hdr))
{
+ /* Don't use _filtered; we can't deal with a QUIT out of
+ target_wait, and I think this might be called from there. */
if (sr_get_debug () > 0)
- printf_filtered ("Ignoring unexpected ACK\n");
+ printf_unfiltered ("Ignoring unexpected ACK\n");
continue;
}
/* If this is the wrong sequence number, ignore it. */
if (HDR_GET_SEQ (hdr) != mips_receive_seq)
{
+ /* Don't use _filtered; we can't deal with a QUIT out of
+ target_wait, and I think this might be called from there. */
if (sr_get_debug () > 0)
- printf_filtered ("Ignoring sequence number %d (want %d)\n",
+ printf_unfiltered ("Ignoring sequence number %d (want %d)\n",
HDR_GET_SEQ (hdr), mips_receive_seq);
continue;
}
if (i < len)
{
+ /* Don't use _filtered; we can't deal with a QUIT out of
+ target_wait, and I think this might be called from there. */
if (sr_get_debug () > 0)
- printf_filtered ("Got new SYN after %d chars (wanted %d)\n",
+ printf_unfiltered ("Got new SYN after %d chars (wanted %d)\n",
i, len);
continue;
}
}
if (err == -2)
{
+ /* Don't use _filtered; we can't deal with a QUIT out of
+ target_wait, and I think this might be called from there. */
if (sr_get_debug () > 0)
- printf_filtered ("Got SYN when wanted trailer\n");
+ printf_unfiltered ("Got SYN when wanted trailer\n");
continue;
}
break;
if (sr_get_debug () > 0)
- printf_filtered ("Bad checksum; data %d, trailer %d\n",
+ /* Don't use _filtered; we can't deal with a QUIT out of
+ target_wait, and I think this might be called from there. */
+ printf_unfiltered ("Bad checksum; data %d, trailer %d\n",
mips_cksum (hdr, buff, len),
TRLR_GET_CKSUM (trlr));
if (sr_get_debug () > 0)
{
ack[HDR_LENGTH + TRLR_LENGTH] = '\0';
- printf_filtered ("Writing ack %d \"%s\"\n", mips_receive_seq,
+ /* Don't use _filtered; we can't deal with a QUIT out of
+ target_wait, and I think this might be called from there. */
+ printf_unfiltered ("Writing ack %d \"%s\"\n", mips_receive_seq,
ack + 1);
}
if (sr_get_debug () > 0)
{
buff[len] = '\0';
- printf_filtered ("Got packet \"%s\"\n", buff);
+ /* Don't use _filtered; we can't deal with a QUIT out of
+ target_wait, and I think this might be called from there. */
+ printf_unfiltered ("Got packet \"%s\"\n", buff);
}
/* We got the packet. Send an acknowledgement. */
if (sr_get_debug () > 0)
{
ack[HDR_LENGTH + TRLR_LENGTH] = '\0';
- printf_filtered ("Writing ack %d \"%s\"\n", mips_receive_seq,
+ /* Don't use _filtered; we can't deal with a QUIT out of
+ target_wait, and I think this might be called from there. */
+ printf_unfiltered ("Writing ack %d \"%s\"\n", mips_receive_seq,
ack + 1);
}
char rcmd;
int rerrflg;
int rresponse;
-
+
if (cmd != '\0')
{
if (mips_need_reply)
return rresponse;
}
+static void
+mips_initialize_cleanups (arg)
+ PTR arg;
+{
+ mips_initializing = 0;
+}
+
/* Initialize a new connection to the MIPS board, and make sure we are
really connected. */
char cr;
char buff[DATA_MAXLEN + 1];
int err;
+ struct cleanup *old_cleanups = make_cleanup (mips_initialize_cleanups, NULL);
+ /* What is this code doing here? I don't see any way it can happen, and
+ it might mean mips_initializing didn't get cleared properly.
+ So I'll make it a warning. */
if (mips_initializing)
- return;
+ {
+ warning ("internal error: mips_initialize called twice");
+ return;
+ }
mips_initializing = 1;
/* The board seems to want to send us a packet. I don't know what
it means. The packet seems to be triggered by a carriage return
character, although perhaps any character would do. */
- cr = '\r';
+ cr = '\015';
+ /* FIXME check the result from this */
SERIAL_WRITE (mips_desc, &cr, 1);
if (mips_receive_packet (buff, 0, 3) < 0)
cc = '\003';
SERIAL_WRITE (mips_desc, &cc, 1);
sleep (2);
- SERIAL_WRITE (mips_desc, "\rdb tty0\r", sizeof "\rdb tty0\r" - 1);
+ SERIAL_WRITE (mips_desc, "\015db tty0\015", sizeof "\015db tty0\015" - 1);
sleep (1);
- cr = '\r';
+ cr = '\015';
SERIAL_WRITE (mips_desc, &cr, 1);
}
mips_receive_packet (buff, 1, 3);
- mips_initializing = 0;
+ do_cleanups (old_cleanups);
/* If this doesn't call error, we have connected; we don't care if
the request itself succeeds or fails. */
char *name;
int from_tty;
{
+ char *ptype;
+
if (name == 0)
error (
"To open a MIPS remote debugging connection, you need to specify what serial\n\
if (mips_desc == (serial_t) NULL)
perror_with_name (name);
+ if (baud_rate != -1)
+ {
+ if (SERIAL_SETBAUDRATE (mips_desc, baud_rate))
+ {
+ SERIAL_CLOSE (mips_desc);
+ perror_with_name (name);
+ }
+ }
+
SERIAL_RAW (mips_desc);
mips_is_open = 1;
if (from_tty)
printf_unfiltered ("Remote MIPS debugging using %s\n", name);
- push_target (&mips_ops); /* Switch to using remote target now */
+
+ /* Switch to using remote target now. */
+ push_target (&mips_ops);
/* FIXME: Should we call start_remote here? */
+
+ /* Try to figure out the processor model if possible. */
+ ptype = mips_read_processor_type ();
+ if (ptype)
+ mips_set_processor_type_command (strsave (ptype), 0);
+
+/* This is really the job of start_remote however, that makes an assumption
+ that the target is about to print out a status message of some sort. That
+ doesn't happen here (in fact, it may not be possible to get the monitor to
+ send the appropriate packet). */
+
+ flush_cached_frames ();
+ registers_changed ();
+ stop_pc = read_pc ();
+ set_current_frame (create_new_frame (read_fp (), stop_pc));
+ select_frame (get_current_frame (), 0);
+ print_stack_frame (selected_frame, -1, 1);
}
/* Close a connection to the remote board. */
mips_receive_wait);
}
+/* Return the signal corresponding to SIG, where SIG is the number which
+ the MIPS protocol uses for the signal. */
+enum target_signal
+mips_signal_from_protocol (sig)
+ int sig;
+{
+ /* We allow a few more signals than the IDT board actually returns, on
+ the theory that there is at least *some* hope that perhaps the numbering
+ for these signals is widely agreed upon. */
+ if (sig <= 0
+ || sig > 31)
+ return TARGET_SIGNAL_UNKNOWN;
+
+ /* Don't want to use target_signal_from_host because we are converting
+ from MIPS signal numbers, not host ones. Our internal numbers
+ match the MIPS numbers for the signals the board can return, which
+ are: SIGINT, SIGSEGV, SIGBUS, SIGILL, SIGFPE, SIGTRAP. */
+ return (enum target_signal) sig;
+}
+
/* Wait until the remote stops, and return a wait status. */
static int
/* Translate a MIPS waitstatus. We use constants here rather than WTERMSIG
and so on, because the constants we want here are determined by the
MIPS protocol and have nothing to do with what host we are running on. */
- if ((rstatus & 0x377) == 0)
+ if ((rstatus & 0377) == 0)
{
status->kind = TARGET_WAITKIND_EXITED;
status->value.integer = (((rstatus) >> 8) & 0377);
}
- else if ((rstatus & 0x377) == 0x177)
+ else if ((rstatus & 0377) == 0177)
{
status->kind = TARGET_WAITKIND_STOPPED;
- /* Don't want to use target_signal_from_host because we are converting
- from MIPS signal numbers, not host ones. Our internal numbers
- match the MIPS numbers for the signals the board can return, which
- are: SIGINT, SIGSEGV, SIGBUS, SIGILL, SIGFPE, SIGTRAP. */
- status->value.sig = (enum target_signal) (((rstatus) >> 8) & 0377);
+ status->value.sig = mips_signal_from_protocol (((rstatus) >> 8) & 0377);
}
else
{
status->kind = TARGET_WAITKIND_SIGNALLED;
- /* Don't want to use target_signal_from_host because we are converting
- from MIPS signal numbers, not host ones. Our internal numbers
- match the MIPS numbers for the signals the board can return, which
- are: SIGINT, SIGSEGV, SIGBUS, SIGILL, SIGFPE, SIGTRAP. */
- status->value.sig = (enum target_signal) (rstatus & 0x177);
+ status->value.sig = mips_signal_from_protocol (rstatus & 0177);
}
return 0;
return;
}
- val = mips_request ('r', (unsigned int) mips_map_regno (regno),
- (unsigned int) 0, &err, mips_receive_wait);
- if (err)
- mips_error ("Can't read register %d: %s", regno, safe_strerror (errno));
+ if (regno == FP_REGNUM || regno == ZERO_REGNUM)
+ /* FP_REGNUM on the mips is a hack which is just supposed to read
+ zero (see also mips-nat.c). */
+ val = 0;
+ else
+ {
+ val = mips_request ('r', (unsigned int) mips_map_regno (regno),
+ (unsigned int) 0, &err, mips_receive_wait);
+ if (err)
+ mips_error ("Can't read register %d: %s", regno,
+ safe_strerror (errno));
+ }
{
char buf[MAX_REGISTER_RAW_SIZE];
return val;
}
-/* Store a word to the target board. */
+/* Store a word to the target board. Returns errno code or zero for
+ success. If OLD_CONTENTS is non-NULL, put the old contents of that
+ memory location there. */
-static void
-mips_store_word (addr, val)
+static int
+mips_store_word (addr, val, old_contents)
CORE_ADDR addr;
int val;
+ char *old_contents;
{
int err;
+ unsigned int oldcontents;
- mips_request ('D', (unsigned int) addr, (unsigned int) val, &err,
- mips_receive_wait);
+ oldcontents = mips_request ('D', (unsigned int) addr, (unsigned int) val,
+ &err,
+ mips_receive_wait);
if (err)
{
/* Data space failed; try instruction space. */
- mips_request ('I', (unsigned int) addr, (unsigned int) val, &err,
- mips_receive_wait);
+ oldcontents = mips_request ('I', (unsigned int) addr,
+ (unsigned int) val, &err,
+ mips_receive_wait);
if (err)
- mips_error ("Can't write address 0x%x: %s", addr, safe_strerror (errno));
+ return errno;
}
+ if (old_contents != NULL)
+ store_unsigned_integer (old_contents, 4, oldcontents);
+ return 0;
}
/* Read or write LEN bytes from inferior memory at MEMADDR,
/* Allocate buffer of that many longwords. */
register char *buffer = alloca (count * 4);
+ int status;
+
if (write)
{
/* Fill start and end extra bytes of buffer with existing data. */
for (i = 0; i < count; i++, addr += 4)
{
- mips_store_word (addr, extract_unsigned_integer (&buffer[i*4], 4));
+ status = mips_store_word (addr,
+ extract_unsigned_integer (&buffer[i*4], 4),
+ NULL);
+ /* Report each kilobyte (we download 32-bit words at a time) */
+ if (i % 256 == 255)
+ {
+ printf_unfiltered ("*");
+ fflush (stdout);
+ }
+ if (status)
+ {
+ errno = status;
+ return 0;
+ }
/* FIXME: Do we want a QUIT here? */
}
+ if (count >= 256)
+ printf_unfiltered ("\n");
}
else
{
CORE_ADDR entry_pt;
if (args && *args)
- mips_error ("Can't pass arguments to remote MIPS board.");
+ {
+ warning ("\
+Can't pass arguments to remote MIPS board; arguments ignored.");
+ /* And don't try to use them on the next "run" command. */
+ execute_command ("set args", 0);
+ }
if (execfile == 0 || exec_bfd == 0)
- mips_error ("No exec file specified");
+ error ("No executable file specified");
entry_pt = (CORE_ADDR) bfd_get_start_address (exec_bfd);
generic_mourn_inferior ();
}
\f
+/* We can write a breakpoint and read the shadow contents in one
+ operation. */
+
+/* The IDT board uses an unusual breakpoint value, and sometimes gets
+ confused when it sees the usual MIPS breakpoint instruction. */
+
+#define BREAK_INSN (0x00000a0d)
+#define BREAK_INSN_SIZE (4)
+
+/* Insert a breakpoint on targets that don't have any better breakpoint
+ support. We read the contents of the target location and stash it,
+ then overwrite it with a breakpoint instruction. ADDR is the target
+ location in the target machine. CONTENTS_CACHE is a pointer to
+ memory allocated for saving the target contents. It is guaranteed
+ by the caller to be long enough to save sizeof BREAKPOINT bytes (this
+ is accomplished via BREAKPOINT_MAX). */
+
+static int
+mips_insert_breakpoint (addr, contents_cache)
+ CORE_ADDR addr;
+ char *contents_cache;
+{
+ int status;
+
+ return mips_store_word (addr, BREAK_INSN, contents_cache);
+}
+
+static int
+mips_remove_breakpoint (addr, contents_cache)
+ CORE_ADDR addr;
+ char *contents_cache;
+{
+ return target_write_memory (addr, contents_cache, BREAK_INSN_SIZE);
+}
+\f
/* The target vector. */
struct target_ops mips_ops =
{
"mips", /* to_shortname */
"Remote MIPS debugging over serial line", /* to_longname */
- "Debug a board using the MIPS remote debugging protocol over a serial line.\n\
-Specify the serial device it is connected to (e.g., /dev/ttya).", /* to_doc */
+ "\
+Debug a board using the MIPS remote debugging protocol over a serial line.\n\
+The argument is the device it is connected to or, if it contains a colon,\n\
+HOST:PORT to access a board over a network", /* to_doc */
mips_open, /* to_open */
mips_close, /* to_close */
NULL, /* to_attach */
mips_prepare_to_store, /* to_prepare_to_store */
mips_xfer_memory, /* to_xfer_memory */
mips_files_info, /* to_files_info */
- NULL, /* to_insert_breakpoint */
- NULL, /* to_remove_breakpoint */
+ mips_insert_breakpoint, /* to_insert_breakpoint */
+ mips_remove_breakpoint, /* to_remove_breakpoint */
NULL, /* to_terminal_init */
NULL, /* to_terminal_inferior */
NULL, /* to_terminal_ours_for_output */
mips_mourn_inferior, /* to_mourn_inferior */
NULL, /* to_can_run */
NULL, /* to_notice_signals */
+ 0, /* to_thread_alive */
+ 0, /* to_stop */
process_stratum, /* to_stratum */
NULL, /* to_next */
1, /* to_has_all_memory */