diff options
Diffstat (limited to 'gdb/remote-array.c')
-rw-r--r-- | gdb/remote-array.c | 1403 |
1 files changed, 1403 insertions, 0 deletions
diff --git a/gdb/remote-array.c b/gdb/remote-array.c new file mode 100644 index 0000000..7f381cd --- /dev/null +++ b/gdb/remote-array.c @@ -0,0 +1,1403 @@ +/* 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 */ +} + |