--- /dev/null
+/* Remote debugging interface for Array Tech RAID controller..
+ Copyright 90, 91, 92, 93, 94, 1995 Free Software Foundation, Inc.
+ Contributed by Cygnus Support. Written by Rob Savoye for Cygnus.
+
+ This module talks to a debug monitor called 'MONITOR', which
+ We communicate with MONITOR via either a direct serial line, or a TCP
+ (or possibly TELNET) stream to a terminal multiplexor,
+ which in turn talks to the target board.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "defs.h"
+#include "gdbcore.h"
+#include "target.h"
+#include "wait.h"
+#include <varargs.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/types.h>
+#include "command.h"
+#include "serial.h"
+#include "monitor.h"
+#include "remote-utils.h"
+
+extern int baud_rate;
+
+static const char hexchars[]="0123456789abcdef";
+static char *hex2mem();
+
+#define SREC_SIZE 160
+
+#define SWAP_TARGET_AND_HOST(buffer,len) \
+ do \
+ { \
+ if (TARGET_BYTE_ORDER != HOST_BYTE_ORDER) \
+ { \
+ char tmp; \
+ char *p = (char *)(buffer); \
+ char *q = ((char *)(buffer)) + len - 1; \
+ for (; p < q; p++, q--) \
+ { \
+ tmp = *q; \
+ *q = *p; \
+ *p = tmp; \
+ } \
+ } \
+ } \
+ while (0)
+
+static void debuglogs();
+static void array_open();
+static void array_close();
+static void array_detach();
+static void array_attach();
+static void array_resume();
+static void array_fetch_register();
+static void array_store_register();
+static void array_fetch_registers();
+static void array_store_registers();
+static void array_prepare_to_store();
+static void array_files_info();
+static void array_kill();
+static void array_create_inferior();
+static void array_mourn_inferior();
+static void make_gdb_packet();
+static int array_xfer_memory();
+static int array_wait();
+static int array_insert_breakpoint();
+static int array_remove_breakpoint();
+static int tohex();
+static int to_hex();
+static int from_hex();
+static int array_send_packet();
+static int array_get_packet();
+static unsigned long ascii2hexword();
+static char *hexword2ascii();
+
+extern char *version;
+
+#define LOG_FILE "monitor.log"
+#if defined (LOG_FILE)
+FILE *log_file;
+#endif
+
+static int timeout = 30;
+/* Having this larger than 400 causes us to be incompatible with m68k-stub.c
+ and i386-stub.c. Normally, no one would notice because it only matters
+ for writing large chunks of memory (e.g. in downloads). Also, this needs
+ to be more than 400 if required to hold the registers (see below, where
+ we round it up based on REGISTER_BYTES). */
+#define PBUFSIZ 400
+
+/*
+ * Descriptor for I/O to remote machine. Initialize it to NULL so that
+ * array_open knows that we don't have a file open when the program starts.
+ */
+serial_t array_desc = NULL;
+
+/*
+ * this array of registers need to match the indexes used by GDB. The
+ * whole reason this exists is cause the various ROM monitors use
+ * different strings than GDB does, and doesn't support all the
+ * registers either. So, typing "info reg sp" becomes a "r30".
+ */
+extern char *tmp_mips_processor_type;
+extern int mips_set_processor_type();
+
+static struct target_ops array_ops = {
+ "array", /* to_shortname */
+ /* to_longname */
+ "Debug using the standard GDB remote protocol for the Array Tech target.",
+ /* to_doc */
+ "Debug using the standard GDB remote protocol for the Array Tech target.\n\
+Specify the serial device it is connected to (e.g. /dev/ttya).",
+ array_open, /* to_open */
+ array_close, /* to_close */
+ NULL, /* to_attach */
+ array_detach, /* to_detach */
+ array_resume, /* to_resume */
+ array_wait, /* to_wait */
+ array_fetch_registers, /* to_fetch_registers */
+ array_store_registers, /* to_store_registers */
+ array_prepare_to_store, /* to_prepare_to_store */
+ array_xfer_memory, /* to_xfer_memory */
+ array_files_info, /* to_files_info */
+ array_insert_breakpoint, /* to_insert_breakpoint */
+ array_remove_breakpoint, /* to_remove_breakpoint */
+ 0, /* to_terminal_init */
+ 0, /* to_terminal_inferior */
+ 0, /* to_terminal_ours_for_output */
+ 0, /* to_terminal_ours */
+ 0, /* to_terminal_info */
+ array_kill, /* to_kill */
+ 0, /* to_load */
+ 0, /* to_lookup_symbol */
+ array_create_inferior, /* to_create_inferior */
+ array_mourn_inferior, /* to_mourn_inferior */
+ 0, /* to_can_run */
+ 0, /* to_notice_signals */
+ 0, /* to_stop */
+ process_stratum, /* to_stratum */
+ 0, /* to_next */
+ 1, /* to_has_all_memory */
+ 1, /* to_has_memory */
+ 1, /* to_has_stack */
+ 1, /* to_has_registers */
+ 1, /* to_has_execution */
+ 0, /* sections */
+ 0, /* sections_end */
+ OPS_MAGIC /* to_magic */
+};
+
+/*
+ * printf_monitor -- send data to monitor. Works just like printf.
+ */
+static void
+printf_monitor(va_alist)
+ va_dcl
+{
+ va_list args;
+ char *pattern;
+ char buf[PBUFSIZ];
+ int i;
+
+ va_start(args);
+
+ pattern = va_arg(args, char *);
+
+ vsprintf(buf, pattern, args);
+
+ debuglogs (1, "printf_monitor(), Sending: \"%s\".", buf);
+
+ if (strlen(buf) > PBUFSIZ)
+ error ("printf_monitor(): string too long");
+ if (SERIAL_WRITE(array_desc, buf, strlen(buf)))
+ fprintf(stderr, "SERIAL_WRITE failed: %s\n", safe_strerror(errno));
+}
+/*
+ * write_monitor -- send raw data to monitor.
+ */
+static void
+write_monitor(data, len)
+ char data[];
+ int len;
+{
+ if (SERIAL_WRITE(array_desc, data, len))
+ fprintf(stderr, "SERIAL_WRITE failed: %s\n", safe_strerror(errno));
+
+ *(data + len+1) = '\0';
+ debuglogs (1, "write_monitor(), Sending: \"%s\".", data);
+
+}
+
+/*
+ * debuglogs -- deal with debugging info to multiple sources. This takes
+ * two real args, the first one is the level to be compared against
+ * the sr_get_debug() value, the second arg is a printf buffer and args
+ * to be formatted and printed. A CR is added after each string is printed.
+ */
+static void
+debuglogs(va_alist)
+ va_dcl
+{
+ va_list args;
+ char *pattern, *p;
+ unsigned char buf[PBUFSIZ];
+ char newbuf[PBUFSIZ];
+ int level, i;
+
+ va_start(args);
+
+ level = va_arg(args, int); /* get the debug level */
+ if ((level <0) || (level > 100)) {
+ error ("Bad argument passed to debuglogs(), needs debug level");
+ return;
+ }
+
+ pattern = va_arg(args, char *); /* get the printf style pattern */
+
+ vsprintf(buf, pattern, args); /* format the string */
+
+ /* convert some characters so it'll look right in the log */
+ p = newbuf;
+ for (i = 0 ; buf[i] != '\0'; i++) {
+ if (i > PBUFSIZ)
+ error ("Debug message too long");
+ switch (buf[i]) {
+ case '\n': /* newlines */
+ *p++ = '\\';
+ *p++ = 'n';
+ continue;
+ case '\r': /* carriage returns */
+ *p++ = '\\';
+ *p++ = 'r';
+ continue;
+ case '\033': /* escape */
+ *p++ = '\\';
+ *p++ = 'e';
+ continue;
+ case '\t': /* tab */
+ *p++ = '\\';
+ *p++ = 't';
+ continue;
+ case '\b': /* backspace */
+ *p++ = '\\';
+ *p++ = 'b';
+ continue;
+ default: /* no change */
+ *p++ = buf[i];
+ }
+
+ if (buf[i] < 26) { /* modify control characters */
+ *p++ = '^';
+ *p++ = buf[i] + 'A';
+ continue;
+ }
+ if (buf[i] >= 128) { /* modify control characters */
+ *p++ = '!';
+ *p++ = buf[i] + 'A';
+ continue;
+ }
+ }
+ *p = '\0'; /* terminate the string */
+
+ if (sr_get_debug() > level)
+ printf_unfiltered ("%s\n", newbuf);
+
+#ifdef LOG_FILE /* write to the monitor log */
+ if (log_file != 0x0) {
+ fputs (newbuf, log_file);
+ fputc ('\n', log_file);
+ fflush (log_file);
+ }
+#endif
+}
+
+/* readchar -- read a character from the remote system, doing all the fancy
+ * timeout stuff.
+ */
+static int
+readchar(timeout)
+ int timeout;
+{
+ int c;
+
+ c = SERIAL_READCHAR(array_desc, abs(timeout));
+
+ if (sr_get_debug() > 5) {
+ putchar(c & 0x7f);
+ debuglogs (5, "readchar: timeout = %d\n", timeout);
+ }
+
+#ifdef LOG_FILE
+ if (isascii (c))
+ putc(c & 0x7f, log_file);
+#endif
+
+ if (c >= 0)
+ return c & 0x7f;
+
+ if (c == SERIAL_TIMEOUT) {
+ if (timeout <= 0)
+ return c; /* Polls shouldn't generate timeout errors */
+ error("Timeout reading from remote system.");
+#ifdef LOG_FILE
+ fputs ("ERROR: Timeout reading from remote system", log_file);
+#endif
+ }
+ perror_with_name("readchar");
+}
+
+/*
+ * expect -- scan input from the remote system, until STRING is found.
+ * If DISCARD is non-zero, then discard non-matching input, else print
+ * it out. Let the user break out immediately.
+ */
+static void
+expect (string, discard)
+ char *string;
+ int discard;
+{
+ char *p = string;
+ int c;
+
+
+ debuglogs (1, "Expecting \"%s\".", string);
+
+ immediate_quit = 1;
+ while (1) {
+ c = readchar(timeout);
+ if (!isascii (c))
+ continue;
+ if (c == *p++) {
+ if (*p == '\0') {
+ immediate_quit = 0;
+ debuglogs (4, "Matched");
+ return;
+ }
+ } else {
+ if (!discard) {
+ fputc_unfiltered (c, gdb_stdout);
+ }
+ p = string;
+ }
+ }
+}
+
+/* Keep discarding input until we see the MONITOR array_cmds->prompt.
+
+ The convention for dealing with the expect_prompt is that you
+ o give your command
+ o *then* wait for the expect_prompt.
+
+ Thus the last thing that a procedure does with the serial line
+ will be an expect_prompt(). Exception: array_resume does not
+ wait for the expect_prompt, because the terminal is being handed over
+ to the inferior. However, the next thing which happens after that
+ is a array_wait which does wait for the expect_prompt.
+ Note that this includes abnormal exit, e.g. error(). This is
+ necessary to prevent getting into states from which we can't
+ recover. */
+static void
+expect_prompt(discard)
+ int discard;
+{
+ expect (expect_prompt, discard);
+}
+
+/*
+ * junk -- ignore junk characters. Returns a 1 if junk, 0 otherwise
+ */
+static int
+junk(ch)
+ char ch;
+{
+ switch (ch) {
+ case '\0':
+ case ' ':
+ case '-':
+ case '\t':
+ case '\r':
+ case '\n':
+ if (sr_get_debug() > 5)
+ debuglogs (5, "Ignoring \'%c\'.", ch);
+ return 1;
+ default:
+ if (sr_get_debug() > 5)
+ debuglogs (5, "Accepting \'%c\'.", ch);
+ return 0;
+ }
+}
+
+/*
+ * get_hex_digit -- Get a hex digit from the remote system & return its value.
+ * If ignore is nonzero, ignore spaces, newline & tabs.
+ */
+static int
+get_hex_digit(ignore)
+ int ignore;
+{
+ static int ch;
+ while (1) {
+ ch = readchar(timeout);
+ if (junk(ch))
+ continue;
+ if (sr_get_debug() > 4) {
+ debuglogs (4, "get_hex_digit() got a 0x%x(%c)", ch, ch);
+ } else {
+#ifdef LOG_FILE /* write to the monitor log */
+ if (log_file != 0x0) {
+ fputs ("get_hex_digit() got a 0x", log_file);
+ fputc (ch, log_file);
+ fputc ('\n', log_file);
+ fflush (log_file);
+ }
+#endif
+ }
+
+ if (ch >= '0' && ch <= '9')
+ return ch - '0';
+ else if (ch >= 'A' && ch <= 'F')
+ return ch - 'A' + 10;
+ else if (ch >= 'a' && ch <= 'f')
+ return ch - 'a' + 10;
+ else if (ch == ' ' && ignore)
+ ;
+ else {
+ expect_prompt(1);
+ debuglogs (4, "Invalid hex digit from remote system. (0x%x)", ch);
+ error("Invalid hex digit from remote system. (0x%x)", ch);
+ }
+ }
+}
+
+/* get_hex_byte -- Get a byte from monitor and put it in *BYT.
+ * Accept any number leading spaces.
+ */
+static void
+get_hex_byte (byt)
+ char *byt;
+{
+ int val;
+
+ val = get_hex_digit (1) << 4;
+ debuglogs (4, "get_hex_byte() -- Read first nibble 0x%x", val);
+
+ val |= get_hex_digit (0);
+ debuglogs (4, "get_hex_byte() -- Read second nibble 0x%x", val);
+ *byt = val;
+
+ debuglogs (4, "get_hex_byte() -- Read a 0x%x", val);
+}
+
+/*
+ * get_hex_word -- Get N 32-bit words from remote, each preceded by a space,
+ * and put them in registers starting at REGNO.
+ */
+static int
+get_hex_word ()
+{
+ long val, newval;
+ int i;
+
+ val = 0;
+
+#if 0
+ if (HOST_BYTE_ORDER == BIG_ENDIAN) {
+#endif
+ for (i = 0; i < 8; i++)
+ val = (val << 4) + get_hex_digit (i == 0);
+#if 0
+ } else {
+ for (i = 7; i >= 0; i--)
+ val = (val << 4) + get_hex_digit (i == 0);
+ }
+#endif
+
+ debuglogs (4, "get_hex_word() got a 0x%x for a %s host.", val, (HOST_BYTE_ORDER == BIG_ENDIAN) ? "big endian" : "little endian");
+
+ return val;
+}
+
+/* This is called not only when we first attach, but also when the
+ user types "run" after having attached. */
+static void
+array_create_inferior (execfile, args, env)
+ char *execfile;
+ char *args;
+ char **env;
+{
+ int entry_pt;
+
+ if (args && *args)
+ error("Can't pass arguments to remote MONITOR process");
+
+ if (execfile == 0 || exec_bfd == 0)
+ error("No exec file specified");
+
+ entry_pt = (int) bfd_get_start_address (exec_bfd);
+
+/* The "process" (board) is already stopped awaiting our commands, and
+ the program is already downloaded. We just set its PC and go. */
+
+ clear_proceed_status ();
+
+ /* Tell wait_for_inferior that we've started a new process. */
+ init_wait_for_inferior ();
+
+ /* Set up the "saved terminal modes" of the inferior
+ based on what modes we are starting it with. */
+ target_terminal_init ();
+
+ /* Install inferior's terminal modes. */
+ target_terminal_inferior ();
+
+ /* insert_step_breakpoint (); FIXME, do we need this? */
+
+ /* Let 'er rip... */
+ proceed ((CORE_ADDR)entry_pt, TARGET_SIGNAL_DEFAULT, 0);
+}
+
+/*
+ * array_open -- open a connection to a remote debugger.
+ * NAME is the filename used for communication.
+ */
+static int baudrate = 9600;
+static char dev_name[100];
+
+static void
+array_open(args, name, from_tty)
+ char *args;
+ char *name;
+ int from_tty;
+{
+ char packet[PBUFSIZ];
+
+ if (args == NULL)
+ error ("Use `target %s DEVICE-NAME' to use a serial port, or \n\
+`target %s HOST-NAME:PORT-NUMBER' to use a network connection.", name, name);
+
+/* if (is_open) */
+ array_close(0);
+
+ tmp_mips_processor_type = "lsi33k"; /* change the default from r3051 */
+ mips_set_processor_type_command ("lsi33k", 0);
+
+ strcpy(dev_name, args);
+ array_desc = SERIAL_OPEN(dev_name);
+
+ if (array_desc == NULL)
+ perror_with_name(dev_name);
+
+ if (baud_rate != -1) {
+ if (SERIAL_SETBAUDRATE (array_desc, baud_rate)) {
+ SERIAL_CLOSE (array_desc);
+ perror_with_name (name);
+ }
+ }
+
+ SERIAL_RAW(array_desc);
+
+#if defined (LOG_FILE)
+ log_file = fopen (LOG_FILE, "w");
+ if (log_file == NULL)
+ perror_with_name (LOG_FILE);
+ fprintf_filtered (log_file, "GDB %s (%s", version);
+ fprintf_filtered (log_file, " --target %s)\n", array_ops.to_shortname);
+ fprintf_filtered (log_file, "Remote target %s connected to %s\n\n", array_ops.to_shortname, dev_name);
+#endif
+
+ /* see if the target is alive. For a ROM monitor, we can just try to force the
+ expect_prompt to print a few times. For the GDB remote protocol, the application
+ being debugged is sitting at a breakpoint and waiting for GDB to initialize
+ the connection. We force it to give us an empty packet to see if it's alive.
+ */
+ debuglogs (3, "Trying to ACK the target's debug stub");
+ /* unless your are on the new hardware, the old board won't initialize
+ because the '+' doesn't flush output like it does on the new ROMS.
+ */
+ printf_monitor ("+"); /* ask for the last signal */
+ expect_prompt(1); /* See if we get a expect_prompt */
+ make_gdb_packet (packet, "?"); /* ask for a bogus packet */
+ if (array_send_packet (packet) == 0)
+ error ("Couldn't transmit packet\n");
+ printf_monitor ("+\n"); /* force it to flush stdout */
+ expect_prompt(1); /* See if we get a expect_prompt */
+
+ if (from_tty)
+ printf("Remote target %s connected to %s\n", array_ops.to_shortname, dev_name);
+}
+
+/*
+ * array_close -- Close out all files and local state before this
+ * target loses control.
+ */
+
+static void
+array_close (quitting)
+ int quitting;
+{
+ SERIAL_CLOSE(array_desc);
+ array_desc = NULL;
+
+ debuglogs (1, "array_close (quitting=%d)", quitting);
+
+#if defined (LOG_FILE)
+ if (log_file) {
+ if (ferror(log_file))
+ printf_filtered ("Error writing log file.\n");
+ if (fclose(log_file) != 0)
+ printf_filtered ("Error closing log file.\n");
+ }
+#endif
+}
+
+/*
+ * array_detach -- terminate the open connection to the remote
+ * debugger. Use this when you want to detach and do something
+ * else with your gdb.
+ */
+static void
+array_detach (from_tty)
+ int from_tty;
+{
+
+ debuglogs (1, "array_detach ()");
+
+ pop_target(); /* calls array_close to do the real work */
+ if (from_tty)
+ printf ("Ending remote %s debugging\n", target_shortname);
+}
+
+/*
+ * array_attach -- attach GDB to the target.
+ */
+static void
+array_attach (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ if (from_tty)
+ printf ("Starting remote %s debugging\n", target_shortname);
+
+ debuglogs (1, "array_attach (args=%s)", args);
+
+ printf_monitor ("go %x\n");
+ /* swallow the echo. */
+ expect ("go %x\n", 1);
+}
+
+/*
+ * array_resume -- Tell the remote machine to resume.
+ */
+static void
+array_resume (pid, step, sig)
+ int pid, step;
+ enum target_signal sig;
+{
+ debuglogs (1, "array_resume (step=%d, sig=%d)", step, sig);
+
+ if (step) {
+ printf_monitor ("s\n");
+ } else {
+ printf_monitor ("go");
+ }
+}
+
+/*
+ * array_wait -- Wait until the remote machine stops, then return,
+ * storing status in status just as `wait' would.
+ */
+static int
+array_wait (pid, status)
+ int pid;
+ struct target_waitstatus *status;
+{
+ int old_timeout = timeout;
+
+ debuglogs(1, "array_wait (), printing extraneous text.");
+
+ status->kind = TARGET_WAITKIND_EXITED;
+ status->value.integer = 0;
+
+ timeout = 0; /* Don't time out -- user program is running. */
+
+ expect_prompt(0); /* Wait for expect_prompt, outputting extraneous text */
+ debuglogs (4, "array_wait(), got the expect_prompt.");
+
+ status->kind = TARGET_WAITKIND_STOPPED;
+ status->value.sig = TARGET_SIGNAL_TRAP;
+
+
+
+ timeout = old_timeout;
+
+ return 0;
+}
+
+/*
+ * array_fetch_registers -- read the remote registers into the
+ * block regs.
+ */
+static void
+array_fetch_registers (ignored)
+ int ignored;
+{
+ int regno, i;
+ char *p;
+ unsigned char packet[PBUFSIZ];
+ char regs[REGISTER_BYTES];
+
+ debuglogs (1, "array_fetch_registers (ignored=%d)\n", ignored);
+
+ memset (packet, 0, PBUFSIZ);
+ /* Unimplemented registers read as all bits zero. */
+ memset (regs, 0, REGISTER_BYTES);
+ make_gdb_packet (packet, "g");
+ if (array_send_packet (packet) == 0)
+ error ("Couldn't transmit packet\n");
+ if (array_get_packet (packet) == 0)
+ error ("Couldn't receive packet\n");
+ /* FIXME: read bytes from packet */
+ debuglogs (4, "array_fetch_registers: Got a \"%s\" back\n", packet);
+ for (regno = 0; regno <= PC_REGNUM+4; regno++) {
+ /* supply register stores in target byte order, so swap here */
+ /* FIXME: convert from ASCII hex to raw bytes */
+ i = ascii2hexword (packet + (regno * 8));
+ debuglogs (5, "Adding register %d = %x\n", regno, i);
+ SWAP_TARGET_AND_HOST (&i, 4);
+ supply_register (regno, (char *)&i);
+ }
+}
+
+/*
+ * This is unused by targets like this one that use a
+ * protocol based on GDB's remote protocol.
+ */
+static void
+array_fetch_register (ignored)
+ int ignored;
+{
+ array_fetch_registers ();
+}
+
+/*
+ * Get all the registers from the targets. They come back in a large array.
+ */
+static void
+array_store_registers (ignored)
+ int ignored;
+{
+ int regno;
+ unsigned long i;
+ char packet[PBUFSIZ];
+ char buf[PBUFSIZ];
+ char num[9];
+
+ debuglogs (1, "array_store_registers()");
+
+ memset (packet, 0, PBUFSIZ);
+ memset (buf, 0, PBUFSIZ);
+ buf[0] = 'G';
+
+ /* Unimplemented registers read as all bits zero. */
+ /* FIXME: read bytes from packet */
+ for (regno = 0; regno < 41; regno++) { /* FIXME */
+ /* supply register stores in target byte order, so swap here */
+ /* FIXME: convert from ASCII hex to raw bytes */
+ i = (unsigned long)read_register (regno);
+ hexword2ascii (num, i);
+ strcpy (buf+(regno * 8)+1, num);
+ }
+ *(buf + (regno * 8) + 2) = 0;
+ make_gdb_packet (packet, buf);
+ if (array_send_packet (packet) == 0)
+ error ("Couldn't transmit packet\n");
+ if (array_get_packet (packet) == 0)
+ error ("Couldn't receive packet\n");
+
+ registers_changed ();
+}
+
+/*
+ * This is unused by targets like this one that use a
+ * protocol based on GDB's remote protocol.
+ */
+static void
+array_store_register (ignored)
+ int ignored;
+{
+ array_store_registers ();
+}
+
+/* Get ready to modify the registers array. On machines which store
+ individual registers, this doesn't need to do anything. On machines
+ which store all the registers in one fell swoop, this makes sure
+ that registers contains all the registers from the program being
+ debugged. */
+
+static void
+array_prepare_to_store ()
+{
+ /* Do nothing, since we can store individual regs */
+}
+
+static void
+array_files_info ()
+{
+ printf ("\tAttached to %s at %d baud.\n",
+ dev_name, baudrate);
+}
+
+/*
+ * array_write_inferior_memory -- Copy LEN bytes of data from debugger
+ * memory at MYADDR to inferior's memory at MEMADDR. Returns length moved.
+ */
+static int
+array_write_inferior_memory (memaddr, myaddr, len)
+ CORE_ADDR memaddr;
+ unsigned char *myaddr;
+ int len;
+{
+ unsigned long i;
+ int j;
+ char packet[PBUFSIZ];
+ char buf[PBUFSIZ];
+ char num[9];
+ char *p;
+
+ debuglogs (1, "array_write_inferior_memory (memaddr=0x%x, myaddr=0x%x, len=%d)", memaddr, myaddr, len);
+ memset (buf, '\0', PBUFSIZ); /* this also sets the string terminator */
+ p = buf;
+
+ *p++ = 'M'; /* The command to write memory */
+ hexword2ascii (num, memaddr); /* convert the address */
+ strcpy (p, num); /* copy the address */
+ p += 8;
+ *p++ = ','; /* add comma delimeter */
+ hexword2ascii (num, len); /* Get the length as a 4 digit number */
+ *p++ = num[4];
+ *p++ = num[5];
+ *p++ = num[6];
+ *p++ = num[7];
+ *p++ = ':'; /* add the colon delimeter */
+ for (j = 0; j < len; j++) { /* copy the data in after converting it */
+ *p++ = tohex ((myaddr[j] >> 4) & 0xf);
+ *p++ = tohex (myaddr[j] & 0xf);
+ }
+
+ make_gdb_packet (packet, buf);
+ if (array_send_packet (packet) == 0)
+ error ("Couldn't transmit packet\n");
+ if (array_get_packet (packet) == 0)
+ error ("Couldn't receive packet\n");
+
+ return len;
+}
+
+/*
+ * array_read_inferior_memory -- read LEN bytes from inferior memory
+ * at MEMADDR. Put the result at debugger address MYADDR. Returns
+ * length moved.
+ */
+static int
+array_read_inferior_memory(memaddr, myaddr, len)
+ CORE_ADDR memaddr;
+ char *myaddr;
+ int len;
+{
+ int i, j;
+ char buf[20];
+ char packet[PBUFSIZ];
+
+ /* Number of bytes read so far. */
+ int count;
+
+ /* Starting address of this pass. */
+ unsigned long startaddr;
+
+ /* Starting address of this pass. */
+ unsigned long endaddr;
+
+ /* Number of bytes to read in this pass. */
+ int len_this_pass;
+
+ debuglogs (1, "array_read_inferior_memory (memaddr=0x%x, myaddr=0x%x, len=%d)", memaddr, myaddr, len);
+
+ /* Note that this code works correctly if startaddr is just less
+ than UINT_MAX (well, really CORE_ADDR_MAX if there was such a
+ thing). That is, something like
+ array_read_bytes (CORE_ADDR_MAX - 4, foo, 4)
+ works--it never adds len To memaddr and gets 0. */
+ /* However, something like
+ array_read_bytes (CORE_ADDR_MAX - 3, foo, 4)
+ doesn't need to work. Detect it and give up if there's an attempt
+ to do that. */
+ if (((memaddr - 1) + len) < memaddr) {
+ errno = EIO;
+ return 0;
+ }
+
+ startaddr = memaddr;
+ count = 0;
+ while (count < len) {
+ len_this_pass = 16;
+ if ((startaddr % 16) != 0)
+ len_this_pass -= startaddr % 16;
+ if (len_this_pass > (len - count))
+ len_this_pass = (len - count);
+
+ debuglogs (3, "Display %d bytes at %x for Big Endian host", len_this_pass, startaddr);
+
+ for (i = 0; i < len_this_pass; i++) {
+ sprintf (buf, "m%08x,%04x", startaddr, len_this_pass);
+ make_gdb_packet (packet, buf);
+ if (array_send_packet (packet) == 0)
+ error ("Couldn't transmit packet\n");
+ if (array_get_packet (packet) == 0)
+ error ("Couldn't receive packet\n");
+ if (*packet == 0)
+ error ("Got no data in the GDB packet\n");
+ debuglogs (4, "array_read_inferior: Got a \"%s\" back\n", packet);
+ for (j = 0; j < len_this_pass ; j++) { /* extract the byte values */
+ myaddr[count++] = from_hex (*(packet+(j*2))) * 16 + from_hex (*(packet+(j*2)+1));
+ debuglogs (5, "myaddr set to %x\n", myaddr[count-1]);
+ }
+ startaddr += 1;
+ }
+
+ }
+ return len;
+}
+
+/* FIXME-someday! merge these two. */
+static int
+array_xfer_memory (memaddr, myaddr, len, write, target)
+ CORE_ADDR memaddr;
+ char *myaddr;
+ int len;
+ int write;
+ struct target_ops *target; /* ignored */
+{
+ if (write)
+ return array_write_inferior_memory (memaddr, myaddr, len);
+ else
+ return array_read_inferior_memory (memaddr, myaddr, len);
+}
+
+static void
+array_kill (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ return; /* ignore attempts to kill target system */
+}
+
+/* Clean up when a program exits.
+ The program actually lives on in the remote processor's RAM, and may be
+ run again without a download. Don't leave it full of breakpoint
+ instructions. */
+
+static void
+array_mourn_inferior ()
+{
+ remove_breakpoints ();
+ generic_mourn_inferior (); /* Do all the proper things now */
+}
+
+#define MAX_ARRAY_BREAKPOINTS 16
+
+extern int memory_breakpoint_size;
+static CORE_ADDR breakaddr[MAX_ARRAY_BREAKPOINTS] = {0};
+
+/*
+ * array_insert_breakpoint -- add a breakpoint
+ */
+static int
+array_insert_breakpoint (addr, shadow)
+ CORE_ADDR addr;
+ char *shadow;
+{
+ int i;
+
+ debuglogs (1, "array_insert_breakpoint() addr = 0x%x", addr);
+
+ for (i = 0; i <= MAX_ARRAY_BREAKPOINTS; i++) {
+ if (breakaddr[i] == 0) {
+ breakaddr[i] = addr;
+ if (sr_get_debug() > 4)
+ printf ("Breakpoint at %x\n", addr);
+ array_read_inferior_memory(addr, shadow, memory_breakpoint_size);
+ printf_monitor("brk 0x%x\n", addr);
+ expect_prompt(1);
+ return 0;
+ }
+ }
+
+ fprintf(stderr, "Too many breakpoints (> 16) for monitor\n");
+ return 1;
+}
+
+/*
+ * _remove_breakpoint -- Tell the monitor to remove a breakpoint
+ */
+static int
+array_remove_breakpoint (addr, shadow)
+ CORE_ADDR addr;
+ char *shadow;
+{
+ int i;
+
+ debuglogs (1, "array_remove_breakpoint() addr = 0x%x", addr);
+
+ for (i = 0; i < MAX_ARRAY_BREAKPOINTS; i++) {
+ if (breakaddr[i] == addr) {
+ breakaddr[i] = 0;
+ /* some monitors remove breakpoints based on the address */
+ printf_monitor("unbrk %x\n", i);
+ expect_prompt(1);
+ return 0;
+ }
+ }
+ fprintf(stderr, "Can't find breakpoint associated with 0x%x\n", addr);
+ return 1;
+}
+
+static void
+array_stop ()
+{
+ debuglogs (1, "array_stop()");
+ printf_monitor("\003");
+ expect_prompt(1);
+}
+
+/*
+ * array_command -- put a command string, in args, out to MONITOR.
+ * Output from MONITOR is placed on the users terminal until the
+ * expect_prompt is seen. FIXME
+ */
+static void
+monitor_command (args, fromtty)
+ char *args;
+ int fromtty;
+{
+ debuglogs (1, "monitor_command (args=%s)", args);
+
+ if (array_desc == NULL)
+ error("monitor target not open.");
+
+ if (!args)
+ error("Missing command.");
+
+ printf_monitor ("%s\n", args);
+ expect_prompt(0);
+}
+
+/*
+ * make_gdb_packet -- make a GDB packet. The data is always ASCII.
+ * A debug packet whose contents are <data>
+ * is encapsulated for transmission in the form:
+ *
+ * $ <data> # CSUM1 CSUM2
+ *
+ * <data> must be ASCII alphanumeric and cannot include characters
+ * '$' or '#'. If <data> starts with two characters followed by
+ * ':', then the existing stubs interpret this as a sequence number.
+ *
+ * CSUM1 and CSUM2 are ascii hex representation of an 8-bit
+ * checksum of <data>, the most significant nibble is sent first.
+ * the hex digits 0-9,a-f are used.
+ *
+ */
+static void
+make_gdb_packet (buf, data)
+ char *buf, *data;
+{
+ int i;
+ unsigned char csum = 0;
+ int cnt;
+ char *p;
+
+ debuglogs (3, "make_gdb_packet(%s)\n", data);
+ cnt = strlen (data);
+ if (cnt > PBUFSIZ)
+ error ("make_gdb_packet(): to much data\n");
+
+ /* start with the packet header */
+ p = buf;
+ *p++ = '$';
+
+ /* calculate the checksum */
+ for (i = 0; i < cnt; i++) {
+ csum += data[i];
+ *p++ = data[i];
+ }
+
+ /* terminate the data with a '#' */
+ *p++ = '#';
+
+ /* add the checksum as two ascii digits */
+ *p++ = tohex ((csum >> 4) & 0xf);
+ *p++ = tohex (csum & 0xf);
+ *p = 0x0; /* Null terminator on string */
+}
+
+/*
+ * array_send_packet -- send a GDB packet to the target with error handling. We
+ * get a '+' (ACK) back if the packet is received and the checksum
+ * matches. Otherwise a '-' (NAK) is returned. It returns a 1 for a
+ * successful transmition, or a 0 for a failure.
+ */
+static int
+array_send_packet (packet)
+ char *packet;
+{
+ int c, retries, i;
+ char junk[PBUFSIZ];
+
+ retries = 0;
+
+#if 0
+ /* scan the packet to make sure it only contains valid characters.
+ this may sound silly, but sometimes a garbled packet will hang
+ the target board. We scan the whole thing, then print the error
+ message.
+ */
+ for (i = 0; i < strlen(packet); i++) {
+ debuglogs (5, "array_send_packet(): Scanning \'%c\'\n", packet[i]);
+ /* legit hex numbers or command */
+ if ((isxdigit(packet[i])) || (isalpha(packet[i])))
+ continue;
+ switch (packet[i]) {
+ case '+': /* ACK */
+ case '-': /* NAK */
+ case '#': /* end of packet */
+ case '$': /* start of packet */
+ continue;
+ default: /* bogus character */
+ retries++;
+ debuglogs (4, "array_send_packet(): Found a non-ascii digit \'%c\' in the packet.\n", packet[i]);
+ }
+ }
+#endif
+
+ if (retries > 0)
+ error ("Can't send packet, found %d non-ascii characters", retries);
+
+ /* ok, try to send the packet */
+ retries = 0;
+ while (retries++ <= 10) {
+ printf_monitor ("%s", packet);
+
+ /* read until either a timeout occurs (-2) or '+' is read */
+ while (retries <= 10) {
+ c = readchar (-timeout);
+ debuglogs (3, "Reading a GDB protocol packet... Got a '%c'\n", c);
+ switch (c) {
+ case '+':
+ debuglogs (3, "Got Ack\n");
+ return 1;
+ case SERIAL_TIMEOUT:
+ debuglogs (3, "Timed out reading serial port\n");
+ printf_monitor("+"); /* resync with the monitor */
+ expect_prompt(1); /* See if we get a expect_prompt */
+ break; /* Retransmit buffer */
+ case '-':
+ debuglogs (3, "Got NAK\n");
+ printf_monitor("+"); /* resync with the monitor */
+ expect_prompt(1); /* See if we get a expect_prompt */
+ break;
+ case '$':
+ /* it's probably an old response, or the echo of our command.
+ * just gobble up the packet and ignore it.
+ */
+ debuglogs (3, "Got a junk packet\n");
+ i = 0;
+ do {
+ c = readchar (timeout);
+ junk[i++] = c;
+ } while (c != '#');
+ c = readchar (timeout);
+ junk[i++] = c;
+ c = readchar (timeout);
+ junk[i++] = c;
+ junk[i++] = '\0';
+ debuglogs (3, "Reading a junk packet, got a \"%s\"\n", junk);
+ continue; /* Now, go look for next packet */
+ default:
+ continue;
+ }
+ retries++;
+ debuglogs (3, "Retransmitting packet \"%s\"\n", packet);
+ break; /* Here to retransmit */
+ }
+ } /* outer while */
+ return 0;
+}
+
+/*
+ * array_get_packet -- get a GDB packet from the target. Basically we read till we
+ * see a '#', then check the checksum. It returns a 1 if it's gotten a
+ * packet, or a 0 it the packet wasn't transmitted correctly.
+ */
+static int
+array_get_packet (packet)
+ char *packet;
+{
+ int c;
+ int retries;
+ unsigned char csum;
+ unsigned char pktcsum;
+ char *bp;
+
+ csum = 0;
+ bp = packet;
+
+ memset (packet, 1, PBUFSIZ);
+ retries = 0;
+ while (retries <= 10) {
+ do {
+ c = readchar (timeout);
+ if (c == SERIAL_TIMEOUT) {
+ debuglogs (3, "array_get_packet: got time out from serial port.\n");
+ }
+ debuglogs (3, "Waiting for a '$', got a %c\n", c);
+ } while (c != '$');
+
+ retries = 0;
+ while (retries <= 10) {
+ c = readchar (timeout);
+ debuglogs (3, "array_get_packet: got a '%c'\n", c);
+ switch (c) {
+ case SERIAL_TIMEOUT:
+ debuglogs (3, "Timeout in mid-packet, retrying\n");
+ return 0;
+ case '$':
+ debuglogs (3, "Saw new packet start in middle of old one\n");
+ return 0; /* Start a new packet, count retries */
+ case '#':
+ *bp = '\0';
+ pktcsum = from_hex (readchar (timeout)) << 4;
+ pktcsum |= from_hex (readchar (timeout));
+ if (csum == 0)
+ debuglogs (3, "\nGDB packet checksum zero, must be a bogus packet\n");
+ if (csum == pktcsum) {
+ debuglogs (3, "\nGDB packet checksum correct, packet data is \"%s\",\n", packet);
+ printf_monitor ("+");
+ expect_prompt (1);
+ return 1;
+ }
+ debuglogs (3, "Bad checksum, sentsum=0x%x, csum=0x%x\n", pktcsum, csum);
+ return 0;
+ case '*': /* Run length encoding */
+ debuglogs (5, "Run length encoding in packet\n");
+ csum += c;
+ c = readchar (timeout);
+ csum += c;
+ c = c - ' ' + 3; /* Compute repeat count */
+
+ if (c > 0 && c < 255 && bp + c - 1 < packet + PBUFSIZ - 1) {
+ memset (bp, *(bp - 1), c);
+ bp += c;
+ continue;
+ }
+ *bp = '\0';
+ printf_filtered ("Repeat count %d too large for buffer.\n", c);
+ return 0;
+
+ default:
+ if ((!isxdigit(c)) && (!ispunct(c)))
+ debuglogs (4, "Got a non-ascii digit \'%c\'.\\n", c);
+ if (bp < packet + PBUFSIZ - 1) {
+ *bp++ = c;
+ csum += c;
+ continue;
+ }
+
+ *bp = '\0';
+ puts_filtered ("Remote packet too long.\n");
+ return 0;
+ }
+ }
+ }
+}
+
+/*
+ * ascii2hexword -- convert an ascii number represented by 8 digits to a hex value.
+ */
+static unsigned long
+ascii2hexword (mem)
+ unsigned char *mem;
+{
+ unsigned long val;
+ int i;
+ char buf[9];
+
+ val = 0;
+ for (i = 0; i < 8; i++) {
+ val <<= 4;
+ if (mem[i] >= 'A' && mem[i] <= 'F')
+ val = val + mem[i] - 'A' + 10;
+ if (mem[i] >= 'a' && mem[i] <= 'f')
+ val = val + mem[i] - 'a' + 10;
+ if (mem[i] >= '0' && mem[i] <= '9')
+ val = val + mem[i] - '0';
+ buf[i] = mem[i];
+ }
+ buf[8] = '\0';
+ debuglogs (4, "ascii2hexword() got a 0x%x from %s(%x).\n", val, buf, mem);
+ return val;
+}
+
+/*
+ * ascii2hexword -- convert a hex value to an ascii number represented by 8
+ * digits.
+ */
+static char*
+hexword2ascii (mem, num)
+ unsigned char *mem;
+ unsigned long num;
+{
+ int i;
+ unsigned char ch;
+
+ debuglogs (4, "hexword2ascii() converting %x ", num);
+ for (i = 7; i >= 0; i--) {
+ mem[i] = tohex ((num >> 4) & 0xf);
+ mem[i] = tohex (num & 0xf);
+ num = num >> 4;
+ }
+ mem[8] = '\0';
+ debuglogs (4, "\tto a %s", mem);
+}
+
+/* Convert hex digit A to a number. */
+static int
+from_hex (a)
+ int a;
+{
+ if (a == 0)
+ return 0;
+
+ debuglogs (4, "from_hex got a 0x%x(%c)\n",a,a);
+ if (a >= '0' && a <= '9')
+ return a - '0';
+ if (a >= 'a' && a <= 'f')
+ return a - 'a' + 10;
+ if (a >= 'A' && a <= 'F')
+ return a - 'A' + 10;
+ else {
+ error ("Reply contains invalid hex digit 0x%x", a);
+ }
+}
+
+/* Convert number NIB to a hex digit. */
+static int
+tohex (nib)
+ int nib;
+{
+ if (nib < 10)
+ return '0'+nib;
+ else
+ return 'a'+nib-10;
+}
+
+/*
+ * _initialize_remote_monitors -- setup a few addtitional commands that
+ * are usually only used by monitors.
+ */
+void
+_initialize_remote_monitors ()
+{
+ /* generic monitor command */
+ add_com ("monitor", class_obscure, monitor_command,
+ "Send a command to the debug monitor.");
+
+}
+
+/*
+ * _initialize_array -- do any special init stuff for the target.
+ */
+void
+_initialize_array ()
+{
+ add_target (&array_ops);
+ baud_rate = 4800; /* this is the only supported baud rate */
+}
+