| /* Target communications support for Macraigor Systems' On-Chip Debugging |
| |
| Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002 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 "gdbcmd.h" |
| #include "objfiles.h" |
| #include "gdb-stabs.h" |
| #include <sys/types.h> |
| #include <signal.h> |
| #include "serial.h" |
| #include "ocd.h" |
| #include "regcache.h" |
| |
| /* Prototypes for local functions */ |
| |
| static int ocd_read_bytes (CORE_ADDR memaddr, char *myaddr, int len); |
| |
| static int ocd_start_remote (PTR dummy); |
| |
| static int readchar (int timeout); |
| |
| static void reset_packet (void); |
| |
| static void output_packet (void); |
| |
| static int get_quoted_char (int timeout); |
| |
| static void put_quoted_char (int c); |
| |
| static void ocd_interrupt (int signo); |
| |
| static void ocd_interrupt_twice (int signo); |
| |
| static void interrupt_query (void); |
| |
| static unsigned char *ocd_do_command (int cmd, int *statusp, int *lenp); |
| |
| static void ocd_put_packet (unsigned char *packet, int pktlen); |
| |
| static unsigned char *ocd_get_packet (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 struct serial *ocd_desc = NULL; |
| |
| void |
| ocd_error (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 (ptid_t th) |
| { |
| return 1; |
| } |
| |
| /* Clean up connection to a remote debugger. */ |
| |
| /* ARGSUSED */ |
| void |
| ocd_close (int quitting) |
| { |
| if (ocd_desc) |
| serial_close (ocd_desc); |
| ocd_desc = NULL; |
| } |
| |
| /* Stub for catch_errors. */ |
| |
| static int |
| ocd_start_remote (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++; /* 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--; |
| |
| /* 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 ()); |
| 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. */ |
| |
| void |
| ocd_open (char *name, int from_tty, enum ocd_target_type target_type, |
| struct target_ops *ops) |
| { |
| unsigned char buf[10], *p; |
| 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_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_ptid = pid_to_ptid (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 (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 (ptid_t ptid, int step, enum target_signal siggnal) |
| { |
| int pktlen; |
| |
| if (step) |
| ocd_do_command (OCD_STEP, &last_run_status, &pktlen); |
| else |
| ocd_do_command (OCD_RUN, &last_run_status, &pktlen); |
| } |
| |
| void |
| ocd_stop (void) |
| { |
| 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 (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 (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 (void) |
| { |
| target_terminal_ours (); |
| |
| if (query ("Interrupted while waiting for the program.\n\ |
| Give up (and stop debugging it)? ")) |
| { |
| target_mourn_inferior (); |
| throw_exception (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 (void) |
| { |
| 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 (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 (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 (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 (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 (void) |
| { |
| } |
| |
| /* 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 (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 (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. TARGET |
| is ignored. */ |
| |
| /* ARGSUSED */ |
| int |
| ocd_xfer_memory (CORE_ADDR memaddr, char *myaddr, int len, int should_write, |
| struct mem_attrib *attrib, struct target_ops *target) |
| { |
| int res; |
| |
| if (should_write) |
| res = ocd_write_bytes (memaddr, myaddr, len); |
| else |
| res = ocd_read_bytes (memaddr, myaddr, len); |
| |
| return res; |
| } |
| |
| void |
| ocd_files_info (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 (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 (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 (void) |
| { |
| pktp = pkt; |
| } |
| |
| static void |
| output_packet (void) |
| { |
| 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 (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 (unsigned char *buf, int len) |
| { |
| unsigned char checksum; |
| unsigned char c; |
| |
| if (len == 0 || len > 256) |
| internal_error (__FILE__, __LINE__, "failed internal consistency check"); /* 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 (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 (unsigned char cmd, int *lenp, int timeout) |
| { |
| 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 (int cmd, int *lenp, int timeout) |
| { |
| int ch; |
| int len; |
| 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 (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 (void) |
| { |
| /* 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 (void) |
| { |
| 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 (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 (char *args, int from_tty) |
| { |
| generic_load (args, from_tty); |
| |
| inferior_ptid = null_ptid; |
| |
| /* 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 (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 (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 (char *args, int from_tty) |
| { |
| error ("bdm command must be followed by `reset'"); |
| } |
| |
| static void |
| bdm_reset_command (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_invalidate (target_dcache); |
| registers_changed (); |
| } |
| |
| static void |
| bdm_restart_command (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 (int regno) |
| { |
| } |
| |
| static void |
| bdm_update_flash_command (char *args, int from_tty) |
| { |
| int status, pktlen; |
| struct cleanup *old_chain; |
| void (*store_registers_tmp) (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 (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 (void) |
| { |
| 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); */ |
| } |