#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 "exceptions.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"
#include "inlining.h"
static int ocd_read_bytes (CORE_ADDR memaddr, char *myaddr, int len);
static int ocd_start_remote (void *dummy);
static int readchar (int timeout);
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;
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"), s);
}
int
ocd_thread_alive (ptid_t th)
{
return 1;
}
void
ocd_close (int quitting)
{
if (ocd_desc)
serial_close (ocd_desc);
ocd_desc = NULL;
}
static int
ocd_start_remote (void *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++;
serial_send_break (ocd_desc);
speed = 80;
buf[0] = OCD_INIT;
buf[1] = speed >> 8;
buf[2] = speed & 0xff;
buf[3] = target_type;
ocd_put_packet (buf, 4);
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 (!(status & OCD_FLAG_BDM))
ocd_stop ();
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);
immediate_quit--;
flush_cached_frames ();
registers_changed ();
stop_pc = read_pc ();
if (stop_pc != inlined_function_call_stack_pc ())
inlined_function_update_call_stack (stop_pc);
print_stack_frame (get_selected_frame (NULL), 0, SRC_AND_LOC);
clear_inlined_subroutine_print_frames ();
buf[0] = OCD_LOG_FILE;
buf[1] = 3;
ocd_put_packet (buf, 2);
p = ocd_get_packet (buf[0], &pktlen, remote_timeout);
buf[0] = OCD_LOG_FILE;
buf[1] = 2;
ocd_put_packet (buf, 2);
p = ocd_get_packet (buf[0], &pktlen, remote_timeout);
return 1;
}
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);
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);
inferior_ptid = pid_to_ptid (42000);
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."));
}
}
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");
}
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;
static void
ocd_interrupt (int signo)
{
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) ();
static void
ocd_interrupt_twice (int signo)
{
signal (signo, ofunc);
interrupt_query ();
signal (signo, ocd_interrupt);
}
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 ();
deprecated_throw_reason (RETURN_QUIT);
}
target_terminal_inferior ();
}
static int kill_kludge;
int
ocd_wait (void)
{
unsigned char *p;
int error_code;
int pktlen;
char buf[1];
ocd_interrupt_flag = 0;
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;
}
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;
}
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)
{
}
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;
buf[6] = 0;
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)
{
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;
}
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;
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)
{
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;
}
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");
}
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;
}
}
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_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"));
}
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);
packet_ptr = packet;
checksum = 0;
ch = readchar (timeout);
if (ch < 0)
error (_("ocd_get_packet (readchar): %d"), ch);
*packet_ptr++ = ch;
checksum += ch;
ch = readchar (timeout);
if (ch < 0)
error (_("ocd_get_packet (readchar): %d"), ch);
*packet_ptr++ = ch;
checksum += ch;
ch = readchar (timeout);
if (ch < 0)
error (_("ocd_get_packet (readchar): %d"), ch);
*packet_ptr++ = ch;
checksum += ch;
switch (ch)
{
case 0x7:
len = 8;
break;
case 0x11:
case 0x15:
len = 5;
break;
default:
len = 0;
break;
case 0x0:
switch (packet[0])
{
case OCD_AYT:
case OCD_SET_BAUD_RATE:
case OCD_INIT:
case OCD_SET_SPEED:
case OCD_SET_FUNC_CODE:
case OCD_SET_CTL_FLAGS:
case OCD_SET_BUF_ADDR:
case OCD_RUN:
case OCD_RUN_ADDR:
case OCD_STOP:
case OCD_RESET_RUN:
case OCD_RESET:
case OCD_STEP:
case OCD_WRITE_REGS:
case OCD_WRITE_MEM:
case OCD_FILL_MEM:
case OCD_MOVE_MEM:
case OCD_WRITE_INT_MEM:
case OCD_JUMP:
case OCD_ERASE_FLASH:
case OCD_PROGRAM_FLASH:
case OCD_EXIT_MON:
case OCD_ENTER_MON:
case OCD_LOG_FILE:
case OCD_SET_CONNECTION:
len = 0;
break;
case OCD_GET_VERSION:
len = 10;
break;
case OCD_GET_STATUS_MASK:
len = 1;
break;
case OCD_GET_CTRS:
case OCD_READ_REGS:
case OCD_READ_MEM:
case OCD_READ_INT_MEM:
len = 257;
break;
default:
error (_("ocd_get_packet: unknown packet type 0x%x."), ch);
}
}
if (len == 257)
{
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)
{
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;
return packet;
}
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);
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;
ocd_put_packet (logbuf, 2);
ocd_get_packet (logbuf[0], &logpktlen, remote_timeout);
logbuf[0] = OCD_LOG_FILE;
logbuf[1] = 2;
ocd_put_packet (logbuf, 2);
ocd_get_packet (logbuf[0], &logpktlen, remote_timeout);
return p + 3;
}
void
ocd_kill (void)
{
if (kill_kludge)
{
kill_kludge = 0;
target_mourn_inferior ();
return;
}
target_mourn_inferior ();
}
void
ocd_mourn (void)
{
unpush_target (current_ops);
generic_mourn_inferior ();
}
void
ocd_create_inferior (char *exec_file, char *args, char **env, int from_tty)
{
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;
clear_symtab_users ();
}
#define BDM_BREAKPOINT {0x0,0x0,0x0,0x0}
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 ();
}
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."));
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);
}
extern initialize_file_ftype _initialize_remote_ocd;
void
_initialize_remote_ocd (void)
{
extern struct cmd_list_element *cmdlist;
static struct cmd_list_element *ocd_cmd_list = NULL;
add_setshow_integer_cmd ("remotetimeout", no_class, &remote_timeout, _("\
Set timeout value for remote read."), _("\
Show timeout value for remote read."), NULL,
NULL,
NULL,
&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);
}