diff options
Diffstat (limited to 'gdb/ocd.c')
-rw-r--r-- | gdb/ocd.c | 1444 |
1 files changed, 1444 insertions, 0 deletions
diff --git a/gdb/ocd.c b/gdb/ocd.c new file mode 100644 index 0000000..343fa62 --- /dev/null +++ b/gdb/ocd.c @@ -0,0 +1,1444 @@ +/* Target communications support for Macraigor Systems' On-Chip Debugging + Copyright 1996, 1997 Free Software Foundation, Inc. + +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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "defs.h" +#include "gdbcore.h" +#include "gdb_string.h" +#include <fcntl.h> +#include "frame.h" +#include "inferior.h" +#include "bfd.h" +#include "symfile.h" +#include "target.h" +#include "wait.h" +#include "gdbcmd.h" +#include "objfiles.h" +#include "gdb-stabs.h" +#include "dcache.h" +#include <sys/types.h> +#include <signal.h> +#include "serial.h" +#include "ocd.h" + +/* Prototypes for local functions */ + +static int ocd_read_bytes PARAMS ((CORE_ADDR memaddr, + char *myaddr, int len)); + +static int ocd_start_remote PARAMS ((PTR dummy)); + +static int readchar PARAMS ((int timeout)); + +static void reset_packet PARAMS ((void)); + +static void output_packet PARAMS ((void)); + +static int get_quoted_char PARAMS ((int timeout)); + +static void put_quoted_char PARAMS ((int c)); + +static void ocd_interrupt PARAMS ((int signo)); + +static void ocd_interrupt_twice PARAMS ((int signo)); + +static void interrupt_query PARAMS ((void)); + +static unsigned char * ocd_do_command PARAMS ((int cmd, int *statusp, int *lenp)); + +static void ocd_put_packet PARAMS ((unsigned char *packet, int pktlen)); + +static unsigned char * ocd_get_packet PARAMS ((int cmd, int *pktlen, int timeout)); + +static struct target_ops *current_ops = NULL; + +static int last_run_status; + +/* This was 5 seconds, which is a long time to sit and wait. + Unless this is going though some terminal server or multiplexer or + other form of hairy serial connection, I would think 2 seconds would + be plenty. */ + +#if 0 +/* FIXME: Change to allow option to set timeout value on a per target + basis. */ +static int remote_timeout = 2; +#endif + +/* Descriptor for I/O to remote machine. Initialize it to NULL so that + ocd_open knows that we don't have a file open when the program + starts. */ +static serial_t ocd_desc = NULL; + +void +ocd_error (s, error_code) + char *s; + int error_code; +{ + char buf[100]; + + fputs_filtered (s, gdb_stderr); + fputs_filtered (" ", gdb_stderr); + + switch (error_code) + { + case 0x1: s = "Unknown fault"; break; + case 0x2: s = "Power failed"; break; + case 0x3: s = "Cable disconnected"; break; + case 0x4: s = "Couldn't enter OCD mode"; break; + case 0x5: s = "Target stuck in reset"; break; + case 0x6: s = "OCD hasn't been initialized"; break; + case 0x7: s = "Write verify failed"; break; + case 0x8: s = "Reg buff error (during MPC5xx fp reg read/write)"; break; + case 0x9: s = "Invalid CPU register access attempt failed"; break; + case 0x11: s = "Bus error"; break; + case 0x12: s = "Checksum error"; break; + case 0x13: s = "Illegal command"; break; + case 0x14: s = "Parameter error"; break; + case 0x15: s = "Internal error"; break; + case 0x80: s = "Flash erase error"; break; + default: + sprintf (buf, "Unknown error code %d", error_code); + s = buf; + } + + error (s); +} + +/* Return nonzero if the thread TH is still alive on the remote system. */ + +int +ocd_thread_alive (th) + int th; +{ + return 1; +} + +/* Clean up connection to a remote debugger. */ + +/* ARGSUSED */ +void +ocd_close (quitting) + int quitting; +{ + if (ocd_desc) + SERIAL_CLOSE (ocd_desc); + ocd_desc = NULL; +} + +/* Stub for catch_errors. */ + +static int +ocd_start_remote (dummy) + PTR dummy; +{ + unsigned char buf[10], *p; + int pktlen; + int status; + int error_code; + int speed; + enum ocd_target_type target_type; + + target_type = *(enum ocd_target_type*)dummy; + + immediate_quit = 1; /* Allow user to interrupt it */ + + SERIAL_SEND_BREAK (ocd_desc); /* Wake up the wiggler */ + + speed = 80; /* Divide clock by 4000 */ + + buf[0] = OCD_INIT; + buf[1] = speed >> 8; + buf[2] = speed & 0xff; + buf[3] = target_type; + ocd_put_packet (buf, 4); /* Init OCD params */ + p = ocd_get_packet (buf[0], &pktlen, remote_timeout); + + if (pktlen < 2) + error ("Truncated response packet from OCD device"); + + status = p[1]; + error_code = p[2]; + + if (error_code != 0) + ocd_error ("OCD_INIT:", error_code); + + ocd_do_command (OCD_AYT, &status, &pktlen); + + p = ocd_do_command (OCD_GET_VERSION, &status, &pktlen); + + printf_unfiltered ("[Wiggler version %x.%x, capability 0x%x]\n", + p[0], p[1], (p[2] << 16) | p[3]); + +#if 0 + /* Reset the target */ + + ocd_do_command (OCD_RESET_RUN, &status, &pktlen); +/* ocd_do_command (OCD_RESET, &status, &pktlen);*/ +#endif + + /* If processor is still running, stop it. */ + + if (!(status & OCD_FLAG_BDM)) + ocd_stop (); + +#if 1 + /* When using a target box, we want to asynchronously return status when + target stops. The OCD_SET_CTL_FLAGS command is ignored by Wigglers.dll + when using a parallel Wiggler */ + buf[0] = OCD_SET_CTL_FLAGS; + buf[1] = 0; + buf[2] = 1; + ocd_put_packet (buf, 3); + + p = ocd_get_packet (buf[0], &pktlen, remote_timeout); + + if (pktlen < 2) + error ("Truncated response packet from OCD device"); + + status = p[1]; + error_code = p[2]; + + if (error_code != 0) + ocd_error ("OCD_SET_CTL_FLAGS:", error_code); +#endif + + immediate_quit = 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); + + buf[0] = OCD_LOG_FILE; + buf[1] = 3; /* close existing WIGGLERS.LOG */ + ocd_put_packet (buf, 2); + p = ocd_get_packet (buf[0], &pktlen, remote_timeout); + + buf[0] = OCD_LOG_FILE; + buf[1] = 2; /* append to existing WIGGLERS.LOG */ + ocd_put_packet (buf, 2); + p = ocd_get_packet (buf[0], &pktlen, remote_timeout); + + return 1; +} + +/* Open a connection to a remote debugger. + NAME is the filename used for communication. */ + +static DCACHE *ocd_dcache; + +void +ocd_open (name, from_tty, target_type, ops) + char *name; + int from_tty; + enum ocd_target_type target_type; + struct target_ops *ops; +{ + unsigned char buf[10], *p; + int status; + int pktlen; + + if (name == 0) + error ("To open an OCD connection, you need to specify the\n\ +device the OCD device is attached to (e.g. /dev/ttya)."); + + target_preopen (from_tty); + + current_ops = ops; + + unpush_target (current_ops); + + ocd_dcache = dcache_init (ocd_read_bytes, ocd_write_bytes); + + if (strncmp(name,"wiggler",7) == 0) + { + ocd_desc = SERIAL_OPEN ("ocd"); + if (!ocd_desc) + perror_with_name (name); + + buf[0] = OCD_LOG_FILE; + buf[1] = 1; /* open new or overwrite existing WIGGLERS.LOG */ + ocd_put_packet (buf, 2); + p = ocd_get_packet (buf[0], &pktlen, remote_timeout); + + buf[0] = OCD_SET_CONNECTION; + buf[1] = 0x01; /* atoi (name[11]); */ + ocd_put_packet (buf, 2); + p = ocd_get_packet (buf[0], &pktlen, remote_timeout); + } + else /* not using Wigglers.dll */ + { + ocd_desc = SERIAL_OPEN (name); + if (!ocd_desc) + perror_with_name (name); + } + + if (baud_rate != -1) + { + if (SERIAL_SETBAUDRATE (ocd_desc, baud_rate)) + { + SERIAL_CLOSE (ocd_desc); + perror_with_name (name); + } + } + + SERIAL_RAW (ocd_desc); + + /* If there is something sitting in the buffer we might take it as a + response to a command, which would be bad. */ + SERIAL_FLUSH_INPUT (ocd_desc); + + if (from_tty) + { + puts_filtered ("Remote target wiggler connected to "); + puts_filtered (name); + puts_filtered ("\n"); + } + push_target (current_ops); /* Switch to using remote target now */ + + /* Without this, some commands which require an active target (such as kill) + won't work. This variable serves (at least) double duty as both the pid + of the target process (if it has such), and as a flag indicating that a + target is active. These functions should be split out into seperate + variables, especially since GDB will someday have a notion of debugging + several processes. */ + + inferior_pid = 42000; + /* Start the remote connection; if error (0), discard this target. + In particular, if the user quits, be sure to discard it + (we'd be in an inconsistent state otherwise). */ + if (!catch_errors (ocd_start_remote, &target_type, + "Couldn't establish connection to remote target\n", + RETURN_MASK_ALL)) + { + pop_target(); + error ("Failed to connect to OCD."); + } +} + +/* This takes a program previously attached to and detaches it. After + this is done, GDB can be used to debug some other program. We + better not have left any breakpoints in the target program or it'll + die when it hits one. */ + +void +ocd_detach (args, from_tty) + char *args; + int from_tty; +{ + if (args) + error ("Argument given to \"detach\" when remotely debugging."); + + pop_target (); + if (from_tty) + puts_filtered ("Ending remote debugging.\n"); +} + +/* Tell the remote machine to resume. */ + +void +ocd_resume (pid, step, siggnal) + int pid, step; + enum target_signal siggnal; +{ + int pktlen; + + dcache_flush (ocd_dcache); + + if (step) + ocd_do_command (OCD_STEP, &last_run_status, &pktlen); + else + ocd_do_command (OCD_RUN, &last_run_status, &pktlen); +} + +void +ocd_stop () +{ + int status; + int pktlen; + + ocd_do_command (OCD_STOP, &status, &pktlen); + + if (!(status & OCD_FLAG_BDM)) + error ("Can't stop target via BDM"); +} + +static volatile int ocd_interrupt_flag; + +/* Send ^C to target to halt it. Target will respond, and send us a + packet. */ + +static void +ocd_interrupt (signo) + int signo; +{ + /* If this doesn't work, try more severe steps. */ + signal (signo, ocd_interrupt_twice); + + if (remote_debug) + printf_unfiltered ("ocd_interrupt called\n"); + + { + char buf[1]; + + ocd_stop (); + buf[0] = OCD_AYT; + ocd_put_packet (buf, 1); + ocd_interrupt_flag = 1; + } +} + +static void (*ofunc)(); + +/* The user typed ^C twice. */ +static void +ocd_interrupt_twice (signo) + int signo; +{ + signal (signo, ofunc); + + interrupt_query (); + + signal (signo, ocd_interrupt); +} + +/* Ask the user what to do when an interrupt is received. */ + +static void +interrupt_query () +{ + target_terminal_ours (); + + if (query ("Interrupted while waiting for the program.\n\ +Give up (and stop debugging it)? ")) + { + target_mourn_inferior (); + return_to_top_level (RETURN_QUIT); + } + + target_terminal_inferior (); +} + +/* If nonzero, ignore the next kill. */ +static int kill_kludge; + +/* Wait until the remote machine stops, then return, + storing status in STATUS just as `wait' would. + Returns "pid" (though it's not clear what, if anything, that + means in the case of this target). */ + +int +ocd_wait () +{ + unsigned char *p; + int error_code; + int pktlen; + char buf[1]; + + ocd_interrupt_flag = 0; + + /* Target might already be stopped by the time we get here. */ + /* If we aren't already stopped, we need to loop until we've dropped + back into BDM mode */ + + while (!(last_run_status & OCD_FLAG_BDM)) + { + buf[0] = OCD_AYT; + ocd_put_packet (buf, 1); + p = ocd_get_packet (OCD_AYT, &pktlen, -1); + + ofunc = (void (*)()) signal (SIGINT, ocd_interrupt); + signal (SIGINT, ofunc); + + if (pktlen < 2) + error ("Truncated response packet from OCD device"); + + last_run_status = p[1]; + error_code = p[2]; + + if (error_code != 0) + ocd_error ("target_wait:", error_code); + + if (last_run_status & OCD_FLAG_PWF) + error ("OCD device lost VCC at BDM interface."); + else if (last_run_status & OCD_FLAG_CABLE_DISC) + error ("OCD device cable appears to have been disconnected."); + } + + if (ocd_interrupt_flag) + return 1; + else + return 0; +} + +/* Read registers from the OCD device. Specify the starting and ending + register number. Return the number of regs actually read in *NUMREGS. + Returns a pointer to a static array containing the register contents. */ + +unsigned char * +ocd_read_bdm_registers (first_bdm_regno, last_bdm_regno, reglen) + int first_bdm_regno; + int last_bdm_regno; + int *reglen; +{ + unsigned char buf[10]; + int i; + unsigned char *p; + unsigned char *regs; + int error_code, status; + int pktlen; + + buf[0] = OCD_READ_REGS; + buf[1] = first_bdm_regno >> 8; + buf[2] = first_bdm_regno & 0xff; + buf[3] = last_bdm_regno >> 8; + buf[4] = last_bdm_regno & 0xff; + + ocd_put_packet (buf, 5); + p = ocd_get_packet (OCD_READ_REGS, &pktlen, remote_timeout); + + status = p[1]; + error_code = p[2]; + + if (error_code != 0) + ocd_error ("read_bdm_registers:", error_code); + + i = p[3]; + if (i == 0) + i = 256; + + if (i > pktlen - 4 + || ((i & 3) != 0)) + error ("Register block size bad: %d", i); + + *reglen = i; + + regs = p + 4; + + return regs; +} + +/* Read register BDM_REGNO and returns its value ala read_register() */ + +CORE_ADDR +ocd_read_bdm_register (bdm_regno) + int bdm_regno; +{ + int reglen; + unsigned char *p; + CORE_ADDR regval; + + p = ocd_read_bdm_registers (bdm_regno, bdm_regno, ®len); + regval = extract_unsigned_integer (p, reglen); + + return regval; +} + +void +ocd_write_bdm_registers (first_bdm_regno, regptr, reglen) + int first_bdm_regno; + unsigned char *regptr; + int reglen; +{ + unsigned char *buf; + unsigned char *p; + int error_code, status; + int pktlen; + + buf = alloca (4 + reglen); + + buf[0] = OCD_WRITE_REGS; + buf[1] = first_bdm_regno >> 8; + buf[2] = first_bdm_regno & 0xff; + buf[3] = reglen; + memcpy (buf + 4, regptr, reglen); + + ocd_put_packet (buf, 4 + reglen); + p = ocd_get_packet (OCD_WRITE_REGS, &pktlen, remote_timeout); + + if (pktlen < 3) + error ("Truncated response packet from OCD device"); + + status = p[1]; + error_code = p[2]; + + if (error_code != 0) + ocd_error ("ocd_write_bdm_registers:", error_code); +} + +void +ocd_write_bdm_register (bdm_regno, reg) + int bdm_regno; + CORE_ADDR reg; +{ + unsigned char buf[4]; + + store_unsigned_integer (buf, 4, reg); + + ocd_write_bdm_registers (bdm_regno, buf, 4); +} + +void +ocd_prepare_to_store () +{ +} + +/* Write memory data directly to the remote machine. + This does not inform the data cache; the data cache uses this. + MEMADDR is the address in the remote memory space. + MYADDR is the address of the buffer in our space. + LEN is the number of bytes. + + Returns number of bytes transferred, or 0 for error. */ + +static int write_mem_command = OCD_WRITE_MEM; + +int +ocd_write_bytes (memaddr, myaddr, len) + CORE_ADDR memaddr; + char *myaddr; + int len; +{ + char buf[256 + 10]; + unsigned char *p; + int origlen; + + origlen = len; + + buf[0] = write_mem_command; + buf[5] = 1; /* Write as bytes */ + buf[6] = 0; /* Don't verify */ + + while (len > 0) + { + int numbytes; + int pktlen; + int status, error_code; + + numbytes = min (len, 256 - 8); + + buf[1] = memaddr >> 24; + buf[2] = memaddr >> 16; + buf[3] = memaddr >> 8; + buf[4] = memaddr; + + buf[7] = numbytes; + + memcpy (&buf[8], myaddr, numbytes); + ocd_put_packet (buf, 8 + numbytes); + p = ocd_get_packet (OCD_WRITE_MEM, &pktlen, remote_timeout); + if (pktlen < 3) + error ("Truncated response packet from OCD device"); + + status = p[1]; + error_code = p[2]; + + if (error_code == 0x11) /* Got a bus error? */ + { + CORE_ADDR error_address; + + error_address = p[3] << 24; + error_address |= p[4] << 16; + error_address |= p[5] << 8; + error_address |= p[6]; + numbytes = error_address - memaddr; + + len -= numbytes; + + errno = EIO; + + break; + } + else if (error_code != 0) + ocd_error ("ocd_write_bytes:", error_code); + + len -= numbytes; + memaddr += numbytes; + myaddr += numbytes; + } + + return origlen - len; +} + +/* Read memory data directly from the remote machine. + This does not use the data cache; the data cache uses this. + MEMADDR is the address in the remote memory space. + MYADDR is the address of the buffer in our space. + LEN is the number of bytes. + + Returns number of bytes transferred, or 0 for error. */ + +static int +ocd_read_bytes (memaddr, myaddr, len) + CORE_ADDR memaddr; + char *myaddr; + int len; +{ + char buf[256 + 10]; + unsigned char *p; + int origlen; + + origlen = len; + + buf[0] = OCD_READ_MEM; + buf[5] = 1; /* Read as bytes */ + + while (len > 0) + { + int numbytes; + int pktlen; + int status, error_code; + + numbytes = min (len, 256 - 7); + + buf[1] = memaddr >> 24; + buf[2] = memaddr >> 16; + buf[3] = memaddr >> 8; + buf[4] = memaddr; + + buf[6] = numbytes; + + ocd_put_packet (buf, 7); + p = ocd_get_packet (OCD_READ_MEM, &pktlen, remote_timeout); + if (pktlen < 4) + error ("Truncated response packet from OCD device"); + + status = p[1]; + error_code = p[2]; + + if (error_code == 0x11) /* Got a bus error? */ + { + CORE_ADDR error_address; + + error_address = p[3] << 24; + error_address |= p[4] << 16; + error_address |= p[5] << 8; + error_address |= p[6]; + numbytes = error_address - memaddr; + + len -= numbytes; + + errno = EIO; + + break; + } + else if (error_code != 0) + ocd_error ("ocd_read_bytes:", error_code); + + memcpy (myaddr, &p[4], numbytes); + + len -= numbytes; + memaddr += numbytes; + myaddr += numbytes; + } + + return origlen - 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 +ocd_xfer_memory (memaddr, myaddr, len, should_write, target) + CORE_ADDR memaddr; + char *myaddr; + int len; + int should_write; + struct target_ops *target; /* ignored */ +{ + return dcache_xfer_memory (ocd_dcache, memaddr, myaddr, len, should_write); +} + +void +ocd_files_info (ignore) + struct target_ops *ignore; +{ + puts_filtered ("Debugging a target over a serial line.\n"); +} + +/* Stuff for dealing with the packets which are part of this protocol. + See comment at top of file for details. */ + +/* Read a single character from the remote side, handling wierd errors. */ + +static int +readchar (timeout) + int timeout; +{ + int ch; + + ch = SERIAL_READCHAR (ocd_desc, timeout); + + switch (ch) + { + case SERIAL_EOF: + error ("Remote connection closed"); + case SERIAL_ERROR: + perror_with_name ("Remote communication error"); + case SERIAL_TIMEOUT: + default: + return ch; + } +} + +#if 0 +/* Read a character from the data stream, dequoting as necessary. SYN is + treated special. Any SYNs appearing in the data stream are returned as the + distinct value RAW_SYN (which has a value > 8 bits and therefore cannot be + mistaken for real data). */ + +static int +get_quoted_char (timeout) + int timeout; +{ + int ch; + + ch = readchar (timeout); + + switch (ch) + { + case SERIAL_TIMEOUT: + error ("Timeout in mid-packet, aborting"); + case SYN: + return RAW_SYN; + case DLE: + ch = readchar (timeout); + if (ch == SYN) + return RAW_SYN; + return ch & ~0100; + default: + return ch; + } +} + +static unsigned char pkt[256 * 2 + 10], *pktp; /* Worst case */ + +static void +reset_packet () +{ + pktp = pkt; +} + +static void +output_packet () +{ + if (SERIAL_WRITE (ocd_desc, pkt, pktp - pkt)) + perror_with_name ("output_packet: write failed"); + + reset_packet (); +} + +/* Output a quoted character. SYNs and DLEs are quoted. Everything else goes + through untouched. */ + +static void +put_quoted_char (c) + int c; +{ + switch (c) + { + case SYN: + case DLE: + *pktp++ = DLE; + c |= 0100; + } + + *pktp++ = c; +} + +/* Send a packet to the OCD device. The packet framed by a SYN character, + a byte count and a checksum. The byte count only counts the number of + bytes between the count and the checksum. A count of zero actually + means 256. Any SYNs within the packet (including the checksum and + count) must be quoted. The quote character must be quoted as well. + Quoting is done by replacing the character with the two-character sequence + DLE, {char} | 0100. Note that the quoting mechanism has no effect on the + byte count. */ + +static void +stu_put_packet (buf, len) + unsigned char *buf; + int len; +{ + unsigned char checksum; + unsigned char c; + + if (len == 0 || len > 256) + abort (); /* Can't represent 0 length packet */ + + reset_packet (); + + checksum = 0; + + put_quoted_char (RAW_SYN); + + c = len; + + do + { + checksum += c; + + put_quoted_char (c); + + c = *buf++; + } + while (len-- > 0); + + put_quoted_char (-checksum & 0xff); + + output_packet (); +} + +#else + +/* Send a packet to the OCD device. The packet framed by a SYN character, + a byte count and a checksum. The byte count only counts the number of + bytes between the count and the checksum. A count of zero actually + means 256. Any SYNs within the packet (including the checksum and + count) must be quoted. The quote character must be quoted as well. + Quoting is done by replacing the character with the two-character sequence + DLE, {char} | 0100. Note that the quoting mechanism has no effect on the + byte count. */ + +static void +ocd_put_packet (buf, len) + unsigned char *buf; + int len; +{ + unsigned char checksum; + unsigned char c; + unsigned char *packet, *packet_ptr; + + packet = alloca (len + 1 + 1); /* packet + SYN + checksum */ + packet_ptr = packet; + + checksum = 0; + + *packet_ptr++ = 0x55; + + while (len-- > 0) + { + c = *buf++; + + checksum += c; + *packet_ptr++ = c; + } + + *packet_ptr++ = -checksum; + if (SERIAL_WRITE (ocd_desc, packet, packet_ptr - packet)) + perror_with_name ("output_packet: write failed"); +} +#endif + +#if 0 +/* Get a packet from the OCD device. Timeout is only enforced for the + first byte of the packet. Subsequent bytes are expected to arrive in + time <= remote_timeout. Returns a pointer to a static buffer containing + the payload of the packet. *LENP contains the length of the packet. +*/ + +static unsigned char * +stu_get_packet (cmd, lenp, timeout) + unsigned char cmd; + int *lenp; +{ + int ch; + int len; + static unsigned char buf[256 + 10], *p; + unsigned char checksum; + + find_packet: + + ch = get_quoted_char (timeout); + + if (ch < 0) + error ("get_packet (readchar): %d", ch); + + if (ch != RAW_SYN) + goto find_packet; + + found_syn: /* Found the start of a packet */ + + p = buf; + checksum = 0; + + len = get_quoted_char (remote_timeout); + + if (len == RAW_SYN) + goto found_syn; + + checksum += len; + + if (len == 0) + len = 256; + + len++; /* Include checksum */ + + while (len-- > 0) + { + ch = get_quoted_char (remote_timeout); + if (ch == RAW_SYN) + goto found_syn; + + *p++ = ch; + checksum += ch; + } + + if (checksum != 0) + goto find_packet; + + if (cmd != buf[0]) + error ("Response phase error. Got 0x%x, expected 0x%x", buf[0], cmd); + + *lenp = p - buf - 1; + return buf; +} + +#else + +/* Get a packet from the OCD device. Timeout is only enforced for the + first byte of the packet. Subsequent bytes are expected to arrive in + time <= remote_timeout. Returns a pointer to a static buffer containing + the payload of the packet. *LENP contains the length of the packet. +*/ + +static unsigned char * +ocd_get_packet (cmd, lenp, timeout) + int cmd; + int *lenp; +{ + int ch; + int len; + int i; + static unsigned char packet[512]; + unsigned char *packet_ptr; + unsigned char checksum; + + ch = readchar (timeout); + + if (ch < 0) + error ("ocd_get_packet (readchar): %d", ch); + + if (ch != 0x55) + error ("ocd_get_packet (readchar): %d", ch); + +/* Found the start of a packet */ + + packet_ptr = packet; + checksum = 0; + +/* Read command char. That sort of tells us how long the packet is. */ + + ch = readchar (timeout); + + if (ch < 0) + error ("ocd_get_packet (readchar): %d", ch); + + *packet_ptr++ = ch; + checksum += ch; + +/* Get status. */ + + ch = readchar (timeout); + + if (ch < 0) + error ("ocd_get_packet (readchar): %d", ch); + *packet_ptr++ = ch; + checksum += ch; + +/* Get error code. */ + + ch = readchar (timeout); + + if (ch < 0) + error ("ocd_get_packet (readchar): %d", ch); + *packet_ptr++ = ch; + checksum += ch; + + switch (ch) /* Figure out length of packet */ + { + case 0x7: /* Write verify error? */ + len = 8; /* write address, value read back */ + break; + case 0x11: /* Bus error? */ + /* write address, read flag */ + case 0x15: /* Internal error */ + len = 5; /* error code, vector */ + break; + default: /* Error w/no params */ + len = 0; + break; + case 0x0: /* Normal result */ + switch (packet[0]) + { + case OCD_AYT: /* Are You There? */ + case OCD_SET_BAUD_RATE: /* Set Baud Rate */ + case OCD_INIT: /* Initialize OCD device */ + case OCD_SET_SPEED: /* Set Speed */ + case OCD_SET_FUNC_CODE: /* Set Function Code */ + case OCD_SET_CTL_FLAGS: /* Set Control Flags */ + case OCD_SET_BUF_ADDR: /* Set Register Buffer Address */ + case OCD_RUN: /* Run Target from PC */ + case OCD_RUN_ADDR: /* Run Target from Specified Address */ + case OCD_STOP: /* Stop Target */ + case OCD_RESET_RUN: /* Reset Target and Run */ + case OCD_RESET: /* Reset Target and Halt */ + case OCD_STEP: /* Single Step */ + case OCD_WRITE_REGS: /* Write Register */ + case OCD_WRITE_MEM: /* Write Memory */ + case OCD_FILL_MEM: /* Fill Memory */ + case OCD_MOVE_MEM: /* Move Memory */ + case OCD_WRITE_INT_MEM: /* Write Internal Memory */ + case OCD_JUMP: /* Jump to Subroutine */ + case OCD_ERASE_FLASH: /* Erase flash memory */ + case OCD_PROGRAM_FLASH: /* Write flash memory */ + case OCD_EXIT_MON: /* Exit the flash programming monitor */ + case OCD_ENTER_MON: /* Enter the flash programming monitor */ + case OCD_LOG_FILE: /* Make Wigglers.dll save Wigglers.log */ + case OCD_SET_CONNECTION: /* Set type of connection in Wigglers.dll */ + len = 0; + break; + case OCD_GET_VERSION: /* Get Version */ + len = 10; + break; + case OCD_GET_STATUS_MASK: /* Get Status Mask */ + len = 1; + break; + case OCD_GET_CTRS: /* Get Error Counters */ + case OCD_READ_REGS: /* Read Register */ + case OCD_READ_MEM: /* Read Memory */ + case OCD_READ_INT_MEM: /* Read Internal Memory */ + len = 257; + break; + default: + error ("ocd_get_packet: unknown packet type 0x%x\n", ch); + } + } + + if (len == 257) /* Byte stream? */ + { /* Yes, byte streams contain the length */ + ch = readchar (timeout); + + if (ch < 0) + error ("ocd_get_packet (readchar): %d", ch); + *packet_ptr++ = ch; + checksum += ch; + len = ch; + if (len == 0) + len = 256; + } + + while (len-- >= 0) /* Do rest of packet and checksum */ + { + ch = readchar (timeout); + + if (ch < 0) + error ("ocd_get_packet (readchar): %d", ch); + *packet_ptr++ = ch; + checksum += ch; + } + + if (checksum != 0) + error ("ocd_get_packet: bad packet checksum"); + + if (cmd != -1 && cmd != packet[0]) + error ("Response phase error. Got 0x%x, expected 0x%x", packet[0], cmd); + + *lenp = packet_ptr - packet - 1; /* Subtract checksum byte */ + return packet; +} +#endif + +/* Execute a simple (one-byte) command. Returns a pointer to the data + following the error code. */ + +static unsigned char * +ocd_do_command (cmd, statusp, lenp) + int cmd; + int *statusp; + int *lenp; +{ + unsigned char buf[100], *p; + int status, error_code; + char errbuf[100]; + + unsigned char logbuf[100]; + int logpktlen; + + buf[0] = cmd; + ocd_put_packet (buf, 1); /* Send command */ + p = ocd_get_packet (*buf, lenp, remote_timeout); + + if (*lenp < 3) + error ("Truncated response packet from OCD device"); + + status = p[1]; + error_code = p[2]; + + if (error_code != 0) + { + sprintf (errbuf, "ocd_do_command (0x%x):", cmd); + ocd_error (errbuf, error_code); + } + + if (status & OCD_FLAG_PWF) + error ("OCD device can't detect VCC at BDM interface."); + else if (status & OCD_FLAG_CABLE_DISC) + error ("BDM cable appears to be disconnected."); + + *statusp = status; + + logbuf[0] = OCD_LOG_FILE; + logbuf[1] = 3; /* close existing WIGGLERS.LOG */ + ocd_put_packet (logbuf, 2); + ocd_get_packet (logbuf[0], &logpktlen, remote_timeout); + + logbuf[0] = OCD_LOG_FILE; + logbuf[1] = 2; /* append to existing WIGGLERS.LOG */ + ocd_put_packet (logbuf, 2); + ocd_get_packet (logbuf[0], &logpktlen, remote_timeout); + + return p + 3; +} + +void +ocd_kill () +{ + /* For some mysterious reason, wait_for_inferior calls kill instead of + mourn after it gets TARGET_WAITKIND_SIGNALLED. Work around it. */ + if (kill_kludge) + { + kill_kludge = 0; + target_mourn_inferior (); + return; + } + + /* Don't wait for it to die. I'm not really sure it matters whether + we do or not. */ + target_mourn_inferior (); +} + +void +ocd_mourn () +{ + unpush_target (current_ops); + generic_mourn_inferior (); +} + +/* All we actually do is set the PC to the start address of exec_bfd, and start + the program at that point. */ + +void +ocd_create_inferior (exec_file, args, env) + char *exec_file; + char *args; + char **env; +{ + if (args && (*args != '\000')) + error ("Args are not supported by BDM."); + + clear_proceed_status (); + proceed (bfd_get_start_address (exec_bfd), TARGET_SIGNAL_0, 0); +} + +void +ocd_load (args, from_tty) + char *args; + int from_tty; +{ + generic_load (args, from_tty); + + inferior_pid = 0; + +/* This is necessary because many things were based on the PC at the time that + we attached to the monitor, which is no longer valid now that we have loaded + new code (and just changed the PC). Another way to do this might be to call + normal_stop, except that the stack may not be valid, and things would get + horribly confused... */ + + clear_symtab_users (); +} + +/* This should be defined for each target */ +/* But we want to be able to compile this file for some configurations + not yet supported fully */ + +#define BDM_BREAKPOINT {0x0,0x0,0x0,0x0} /* For ppc 8xx */ +#if 0 +#define BDM_BREAKPOINT {0x4a,0xfa} /* BGND insn used for CPU32 */ +#endif + +/* BDM (at least on CPU32) uses a different breakpoint */ + +int +ocd_insert_breakpoint (addr, contents_cache) + CORE_ADDR addr; + char *contents_cache; +{ + static char break_insn[] = BDM_BREAKPOINT; + int val; + + val = target_read_memory (addr, contents_cache, sizeof (break_insn)); + + if (val == 0) + val = target_write_memory (addr, break_insn, sizeof (break_insn)); + + return val; +} + +int +ocd_remove_breakpoint (addr, contents_cache) + CORE_ADDR addr; + char *contents_cache; +{ + static char break_insn[] = BDM_BREAKPOINT; + int val; + + val = target_write_memory (addr, contents_cache, sizeof (break_insn)); + + return val; +} + +static void +bdm_command (args, from_tty) + char *args; + int from_tty; +{ + error ("bdm command must be followed by `reset'"); +} + +static void +bdm_reset_command (args, from_tty) + char *args; + int from_tty; +{ + int status, pktlen; + + if (!ocd_desc) + error ("Not connected to OCD device."); + + ocd_do_command (OCD_RESET, &status, &pktlen); + dcache_flush (ocd_dcache); + registers_changed (); +} + +static void +bdm_restart_command (args, from_tty) + char *args; + int from_tty; +{ + int status, pktlen; + + if (!ocd_desc) + error ("Not connected to OCD device."); + + ocd_do_command (OCD_RESET_RUN, &status, &pktlen); + last_run_status = status; + clear_proceed_status (); + wait_for_inferior (); + normal_stop (); +} + +/* Temporary replacement for target_store_registers(). This prevents + generic_load from trying to set the PC. */ + +static void +noop_store_registers (regno) + int regno; +{ +} + +static void +bdm_update_flash_command (args, from_tty) + char *args; + int from_tty; +{ + int status, pktlen; + struct cleanup *old_chain; + void (*store_registers_tmp) PARAMS ((int)); + + if (!ocd_desc) + error ("Not connected to OCD device."); + + if (!args) + error ("Must specify file containing new OCD code."); + +/* old_chain = make_cleanup (flash_cleanup, 0);*/ + + ocd_do_command (OCD_ENTER_MON, &status, &pktlen); + + ocd_do_command (OCD_ERASE_FLASH, &status, &pktlen); + + write_mem_command = OCD_PROGRAM_FLASH; + store_registers_tmp = current_target.to_store_registers; + current_target.to_store_registers = noop_store_registers; + + generic_load (args, from_tty); + + current_target.to_store_registers = store_registers_tmp; + write_mem_command = OCD_WRITE_MEM; + + ocd_do_command (OCD_EXIT_MON, &status, &pktlen); + +/* discard_cleanups (old_chain);*/ +} + +static void +bdm_read_register_command (args, from_tty) + char *args; + int from_tty; +{ + /* XXX repeat should go on to the next register */ + + if (!ocd_desc) + error ("Not connected to OCD device."); + + if (!args) + error ("Must specify BDM register number."); + +} + +void +_initialize_remote_ocd () +{ + extern struct cmd_list_element *cmdlist; + static struct cmd_list_element *ocd_cmd_list = NULL; + + add_show_from_set (add_set_cmd ("remotetimeout", no_class, + var_integer, (char *)&remote_timeout, + "Set timeout value for remote read.\n", &setlist), + &showlist); + + add_prefix_cmd ("ocd", class_obscure, bdm_command, "", &ocd_cmd_list, "ocd ", + 0, &cmdlist); + + add_cmd ("reset", class_obscure, bdm_reset_command, "", &ocd_cmd_list); + add_cmd ("restart", class_obscure, bdm_restart_command, "", &ocd_cmd_list); + add_cmd ("update-flash", class_obscure, bdm_update_flash_command, "", &ocd_cmd_list); + /* add_cmd ("read-register", class_obscure, bdm_read_register_command, "", &ocd_cmd_list);*/ +} |