+ for (i = 32; i >= 0; i--)
+ if (mask == 0)
+ break;
+ else
+ mask >>= 1;
+
+ mask = (unsigned long) 0xffffffff >> i;
+
+ return mask;
+}
+
+
+/* Insert a hardware breakpoint. This works only on LSI targets, which
+ implement ordinary breakpoints using hardware facilities. */
+
+int
+remote_mips_insert_hw_breakpoint (addr, contents_cache)
+ CORE_ADDR addr;
+ char *contents_cache;
+{
+ if (strcmp (target_shortname, "lsi") == 0)
+ return mips_insert_breakpoint (addr, contents_cache);
+ else
+ return -1;
+}
+
+
+/* Remove a hardware breakpoint. This works only on LSI targets, which
+ implement ordinary breakpoints using hardware facilities. */
+
+int
+remote_mips_remove_hw_breakpoint (addr, contents_cache)
+ CORE_ADDR addr;
+ char *contents_cache;
+{
+ if (strcmp (target_shortname, "lsi") == 0)
+ return mips_remove_breakpoint (addr, contents_cache);
+ else
+ return -1;
+}
+
+/* Set a data watchpoint. ADDR and LEN should be obvious. TYPE is 0
+ for a write watchpoint, 1 for a read watchpoint, or 2 for a read/write
+ watchpoint. */
+
+int
+remote_mips_set_watchpoint (addr, len, type)
+ CORE_ADDR addr;
+ int len;
+ int type;
+{
+ if (set_breakpoint (addr, len, type))
+ return -1;
+
+ return 0;
+}
+
+int
+remote_mips_remove_watchpoint (addr, len, type)
+ CORE_ADDR addr;
+ int len;
+ int type;
+{
+ if (clear_breakpoint (addr, len, type))
+ return -1;
+
+ return 0;
+}
+
+int
+remote_mips_stopped_by_watchpoint (void)
+{
+ return hit_watchpoint;
+}
+
+
+/* Insert a breakpoint. */
+
+static int
+set_breakpoint (addr, len, type)
+ CORE_ADDR addr;
+ int len;
+ enum break_type type;
+{
+ return common_breakpoint (1, addr, len, type);
+}
+
+
+/* Clear a breakpoint. */
+
+static int
+clear_breakpoint (addr, len, type)
+ CORE_ADDR addr;
+ int len;
+ enum break_type type;
+{
+ return common_breakpoint (0, addr, len, type);
+}
+
+
+/* Check the error code from the return packet for an LSI breakpoint
+ command. If there's no error, just return 0. If it's a warning,
+ print the warning text and return 0. If it's an error, print
+ the error text and return 1. <ADDR> is the address of the breakpoint
+ that was being set. <RERRFLG> is the error code returned by PMON.
+ This is a helper function for common_breakpoint. */
+
+static int
+check_lsi_error (addr, rerrflg)
+ CORE_ADDR addr;
+ int rerrflg;
+{
+ struct lsi_error *err;
+ char *saddr = paddr_nz (addr); /* printable address string */
+
+ if (rerrflg == 0) /* no error */
+ return 0;
+
+ /* Warnings can be ORed together, so check them all. */
+ if (rerrflg & W_WARN)
+ {
+ if (monitor_warnings)
+ {
+ int found = 0;
+ for (err = lsi_warning_table; err->code != 0; err++)
+ {
+ if ((err->code & rerrflg) == err->code)
+ {
+ found = 1;
+ fprintf_unfiltered (gdb_stderr,
+ "common_breakpoint (0x%s): Warning: %s\n",
+ saddr,
+ err->string);
+ }
+ }
+ if (!found)
+ fprintf_unfiltered (gdb_stderr,
+ "common_breakpoint (0x%s): Unknown warning: 0x%x\n",
+ saddr,
+ rerrflg);
+ }
+ return 0;
+ }
+
+ /* Errors are unique, i.e. can't be ORed together. */
+ for (err = lsi_error_table; err->code != 0; err++)
+ {
+ if ((err->code & rerrflg) == err->code)
+ {
+ fprintf_unfiltered (gdb_stderr,
+ "common_breakpoint (0x%s): Error: %s\n",
+ saddr,
+ err->string);
+ return 1;
+ }
+ }
+ fprintf_unfiltered (gdb_stderr,
+ "common_breakpoint (0x%s): Unknown error: 0x%x\n",
+ saddr,
+ rerrflg);
+ return 1;
+}
+
+
+/* This routine sends a breakpoint command to the remote target.
+
+ <SET> is 1 if setting a breakpoint, or 0 if clearing a breakpoint.
+ <ADDR> is the address of the breakpoint.
+ <LEN> the length of the region to break on.
+ <TYPE> is the type of breakpoint:
+ 0 = write (BREAK_WRITE)
+ 1 = read (BREAK_READ)
+ 2 = read/write (BREAK_ACCESS)
+ 3 = instruction fetch (BREAK_FETCH)
+
+ Return 0 if successful; otherwise 1. */
+
+static int
+common_breakpoint (set, addr, len, type)
+ int set;
+ CORE_ADDR addr;
+ int len;
+ enum break_type type;
+{
+ char buf[DATA_MAXLEN + 1];
+ char cmd, rcmd;
+ int rpid, rerrflg, rresponse, rlen;
+ int nfields;
+
+ addr = ADDR_BITS_REMOVE (addr);
+
+ if (mips_monitor == MON_LSI)
+ {
+ if (set == 0) /* clear breakpoint */
+ {
+ /* The LSI PMON "clear breakpoint" has this form:
+ <pid> 'b' <bptn> 0x0
+ reply:
+ <pid> 'b' 0x0 <code>
+
+ <bptn> is a breakpoint number returned by an earlier 'B' command.
+ Possible return codes: OK, E_BPT. */
+
+ int i;
+
+ /* Search for the breakpoint in the table. */
+ for (i = 0; i < MAX_LSI_BREAKPOINTS; i++)
+ if (lsi_breakpoints[i].type == type
+ && lsi_breakpoints[i].addr == addr
+ && lsi_breakpoints[i].len == len)
+ break;
+
+ /* Clear the table entry and tell PMON to clear the breakpoint. */
+ if (i == MAX_LSI_BREAKPOINTS)
+ {
+ warning ("common_breakpoint: Attempt to clear bogus breakpoint at %s\n",
+ paddr_nz (addr));
+ return 1;
+ }
+
+ lsi_breakpoints[i].type = BREAK_UNUSED;
+ sprintf (buf, "0x0 b 0x%x 0x0", i);
+ mips_send_packet (buf, 1);
+
+ rlen = mips_receive_packet (buf, 1, mips_receive_wait);
+ buf[rlen] = '\0';
+
+ nfields = sscanf (buf, "0x%x b 0x0 0x%x", &rpid, &rerrflg);
+ if (nfields != 2)
+ mips_error ("common_breakpoint: Bad response from remote board: %s", buf);
+
+ return (check_lsi_error (addr, rerrflg));
+ }
+ else
+ /* set a breakpoint */
+ {
+ /* The LSI PMON "set breakpoint" command has this form:
+ <pid> 'B' <addr> 0x0
+ reply:
+ <pid> 'B' <bptn> <code>
+
+ The "set data breakpoint" command has this form:
+
+ <pid> 'A' <addr1> <type> [<addr2> [<value>]]
+
+ where: type= "0x1" = read
+ "0x2" = write
+ "0x3" = access (read or write)
+
+ The reply returns two values:
+ bptn - a breakpoint number, which is a small integer with
+ possible values of zero through 255.
+ code - an error return code, a value of zero indicates a
+ succesful completion, other values indicate various
+ errors and warnings.
+
+ Possible return codes: OK, W_QAL, E_QAL, E_OUT, E_NON.
+
+ */
+
+ if (type == BREAK_FETCH) /* instruction breakpoint */
+ {
+ cmd = 'B';
+ sprintf (buf, "0x0 B 0x%s 0x0", paddr_nz (addr));
+ }
+ else
+ /* watchpoint */
+ {
+ cmd = 'A';
+ sprintf (buf, "0x0 A 0x%s 0x%x 0x%s", paddr_nz (addr),
+ type == BREAK_READ ? 1 : (type == BREAK_WRITE ? 2 : 3),
+ paddr_nz (addr + len - 1));
+ }
+ mips_send_packet (buf, 1);
+
+ rlen = mips_receive_packet (buf, 1, mips_receive_wait);
+ buf[rlen] = '\0';
+
+ nfields = sscanf (buf, "0x%x %c 0x%x 0x%x",
+ &rpid, &rcmd, &rresponse, &rerrflg);
+ if (nfields != 4 || rcmd != cmd || rresponse > 255)
+ mips_error ("common_breakpoint: Bad response from remote board: %s", buf);
+
+ if (rerrflg != 0)
+ if (check_lsi_error (addr, rerrflg))
+ return 1;
+
+ /* rresponse contains PMON's breakpoint number. Record the
+ information for this breakpoint so we can clear it later. */
+ lsi_breakpoints[rresponse].type = type;
+ lsi_breakpoints[rresponse].addr = addr;
+ lsi_breakpoints[rresponse].len = len;
+
+ return 0;
+ }
+ }
+ else
+ {
+ /* On non-LSI targets, the breakpoint command has this form:
+ 0x0 <CMD> <ADDR> <MASK> <FLAGS>
+ <MASK> is a don't care mask for addresses.
+ <FLAGS> is any combination of `r', `w', or `f' for read/write/fetch.
+ */
+ unsigned long mask;
+
+ mask = calculate_mask (addr, len);
+ addr &= ~mask;
+
+ if (set) /* set a breakpoint */
+ {
+ char *flags;
+ switch (type)
+ {
+ case BREAK_WRITE: /* write */
+ flags = "w";
+ break;
+ case BREAK_READ: /* read */
+ flags = "r";
+ break;
+ case BREAK_ACCESS: /* read/write */
+ flags = "rw";
+ break;
+ case BREAK_FETCH: /* fetch */
+ flags = "f";
+ break;
+ default:
+ abort ();
+ }
+
+ cmd = 'B';
+ sprintf (buf, "0x0 B 0x%s 0x%s %s", paddr_nz (addr),
+ paddr_nz (mask), flags);
+ }
+ else
+ {
+ cmd = 'b';
+ sprintf (buf, "0x0 b 0x%s", paddr_nz (addr));
+ }
+
+ mips_send_packet (buf, 1);
+
+ rlen = mips_receive_packet (buf, 1, mips_receive_wait);
+ buf[rlen] = '\0';
+
+ nfields = sscanf (buf, "0x%x %c 0x%x 0x%x",
+ &rpid, &rcmd, &rerrflg, &rresponse);
+
+ if (nfields != 4 || rcmd != cmd)
+ mips_error ("common_breakpoint: Bad response from remote board: %s",
+ buf);
+
+ if (rerrflg != 0)
+ {
+ /* Ddb returns "0x0 b 0x16 0x0\000", whereas
+ Cogent returns "0x0 b 0xffffffff 0x16\000": */
+ if (mips_monitor == MON_DDB)
+ rresponse = rerrflg;
+ if (rresponse != 22) /* invalid argument */
+ fprintf_unfiltered (gdb_stderr,
+ "common_breakpoint (0x%s): Got error: 0x%x\n",
+ paddr_nz (addr), rresponse);
+ return 1;
+ }
+ }
+ return 0;
+}
+\f
+static void
+send_srec (srec, len, addr)
+ char *srec;
+ int len;
+ CORE_ADDR addr;
+{
+ while (1)
+ {
+ int ch;
+
+ SERIAL_WRITE (mips_desc, srec, len);
+
+ ch = mips_readchar (2);
+
+ switch (ch)
+ {
+ case SERIAL_TIMEOUT:
+ error ("Timeout during download.");
+ break;
+ case 0x6: /* ACK */
+ return;
+ case 0x15: /* NACK */
+ fprintf_unfiltered (gdb_stderr, "Download got a NACK at byte %s! Retrying.\n", paddr_u (addr));
+ continue;
+ default:
+ error ("Download got unexpected ack char: 0x%x, retrying.\n", ch);
+ }
+ }
+}
+
+/* Download a binary file by converting it to S records. */
+
+static void
+mips_load_srec (args)
+ char *args;
+{
+ bfd *abfd;
+ asection *s;
+ char *buffer, srec[1024];
+ unsigned int i;
+ unsigned int srec_frame = 200;
+ int reclen;
+ static int hashmark = 1;
+
+ buffer = alloca (srec_frame * 2 + 256);
+
+ abfd = bfd_openr (args, 0);
+ if (!abfd)
+ {
+ printf_filtered ("Unable to open file %s\n", args);
+ return;
+ }
+
+ if (bfd_check_format (abfd, bfd_object) == 0)
+ {
+ printf_filtered ("File is not an object file\n");
+ return;
+ }
+
+/* This actually causes a download in the IDT binary format: */
+ mips_send_command (LOAD_CMD, 0);
+
+ for (s = abfd->sections; s; s = s->next)
+ {
+ if (s->flags & SEC_LOAD)
+ {
+ unsigned int numbytes;
+
+ /* FIXME! vma too small????? */
+ printf_filtered ("%s\t: 0x%4lx .. 0x%4lx ", s->name,
+ (long) s->vma,
+ (long) (s->vma + s->_raw_size));
+ gdb_flush (gdb_stdout);
+
+ for (i = 0; i < s->_raw_size; i += numbytes)
+ {
+ numbytes = min (srec_frame, s->_raw_size - i);
+
+ bfd_get_section_contents (abfd, s, buffer, i, numbytes);
+
+ reclen = mips_make_srec (srec, '3', s->vma + i, buffer, numbytes);
+ send_srec (srec, reclen, s->vma + i);
+
+ if (hashmark)
+ {
+ putchar_unfiltered ('#');
+ gdb_flush (gdb_stdout);
+ }
+
+ } /* Per-packet (or S-record) loop */
+
+ putchar_unfiltered ('\n');
+ } /* Loadable sections */
+ }
+ if (hashmark)
+ putchar_unfiltered ('\n');
+
+ /* Write a type 7 terminator record. no data for a type 7, and there
+ is no data, so len is 0. */
+
+ reclen = mips_make_srec (srec, '7', abfd->start_address, NULL, 0);
+
+ send_srec (srec, reclen, abfd->start_address);
+
+ SERIAL_FLUSH_INPUT (mips_desc);
+}
+
+/*
+ * mips_make_srec -- make an srecord. This writes each line, one at a
+ * time, each with it's own header and trailer line.
+ * An srecord looks like this:
+ *
+ * byte count-+ address
+ * start ---+ | | data +- checksum
+ * | | | |
+ * S01000006F6B692D746573742E73726563E4
+ * S315000448600000000000000000FC00005900000000E9
+ * S31A0004000023C1400037DE00F023604000377B009020825000348D
+ * S30B0004485A0000000000004E
+ * S70500040000F6
+ *
+ * S<type><length><address><data><checksum>
+ *
+ * Where
+ * - length
+ * is the number of bytes following upto the checksum. Note that
+ * this is not the number of chars following, since it takes two
+ * chars to represent a byte.
+ * - type
+ * is one of:
+ * 0) header record
+ * 1) two byte address data record
+ * 2) three byte address data record
+ * 3) four byte address data record
+ * 7) four byte address termination record
+ * 8) three byte address termination record
+ * 9) two byte address termination record
+ *
+ * - address
+ * is the start address of the data following, or in the case of
+ * a termination record, the start address of the image
+ * - data
+ * is the data.
+ * - checksum
+ * is the sum of all the raw byte data in the record, from the length
+ * upwards, modulo 256 and subtracted from 255.
+ *
+ * This routine returns the length of the S-record.
+ *
+ */
+
+static int
+mips_make_srec (buf, type, memaddr, myaddr, len)
+ char *buf;
+ int type;
+ CORE_ADDR memaddr;
+ unsigned char *myaddr;
+ int len;
+{
+ unsigned char checksum;
+ int i;
+
+ /* Create the header for the srec. addr_size is the number of bytes in the address,
+ and 1 is the number of bytes in the count. */
+
+ /* FIXME!! bigger buf required for 64-bit! */
+ buf[0] = 'S';
+ buf[1] = type;
+ buf[2] = len + 4 + 1; /* len + 4 byte address + 1 byte checksum */
+ /* This assumes S3 style downloads (4byte addresses). There should
+ probably be a check, or the code changed to make it more
+ explicit. */
+ buf[3] = memaddr >> 24;
+ buf[4] = memaddr >> 16;
+ buf[5] = memaddr >> 8;
+ buf[6] = memaddr;
+ memcpy (&buf[7], myaddr, len);
+
+ /* Note that the checksum is calculated on the raw data, not the
+ hexified data. It includes the length, address and the data
+ portions of the packet. */
+ checksum = 0;
+ buf += 2; /* Point at length byte */
+ for (i = 0; i < len + 4 + 1; i++)
+ checksum += *buf++;
+
+ *buf = ~checksum;
+
+ return len + 8;
+}
+
+/* The following manifest controls whether we enable the simple flow
+ control support provided by the monitor. If enabled the code will
+ wait for an affirmative ACK between transmitting packets. */
+#define DOETXACK (1)
+
+/* The PMON fast-download uses an encoded packet format constructed of
+ 3byte data packets (encoded as 4 printable ASCII characters), and
+ escape sequences (preceded by a '/'):
+
+ 'K' clear checksum
+ 'C' compare checksum (12bit value, not included in checksum calculation)
+ 'S' define symbol name (for addr) terminated with "," and padded to 4char boundary
+ 'Z' zero fill multiple of 3bytes
+ 'B' byte (12bit encoded value, of 8bit data)
+ 'A' address (36bit encoded value)
+ 'E' define entry as original address, and exit load
+
+ The packets are processed in 4 character chunks, so the escape
+ sequences that do not have any data (or variable length data)
+ should be padded to a 4 character boundary. The decoder will give
+ an error if the complete message block size is not a multiple of
+ 4bytes (size of record).
+
+ The encoding of numbers is done in 6bit fields. The 6bit value is
+ used to index into this string to get the specific character
+ encoding for the value: */
+static char encoding[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789,.";
+
+/* Convert the number of bits required into an encoded number, 6bits
+ at a time (range 0..63). Keep a checksum if required (passed
+ pointer non-NULL). The function returns the number of encoded
+ characters written into the buffer. */
+static int
+pmon_makeb64 (v, p, n, chksum)
+ unsigned long v;
+ char *p;
+ int n;
+ int *chksum;
+{
+ int count = (n / 6);
+
+ if ((n % 12) != 0)
+ {
+ fprintf_unfiltered (gdb_stderr,
+ "Fast encoding bitcount must be a multiple of 12bits: %dbit%s\n", n, (n == 1) ? "" : "s");
+ return (0);
+ }
+ if (n > 36)
+ {
+ fprintf_unfiltered (gdb_stderr,
+ "Fast encoding cannot process more than 36bits at the moment: %dbits\n", n);
+ return (0);
+ }
+
+ /* Deal with the checksum: */
+ if (chksum != NULL)
+ {
+ switch (n)
+ {
+ case 36:
+ *chksum += ((v >> 24) & 0xFFF);
+ case 24:
+ *chksum += ((v >> 12) & 0xFFF);
+ case 12:
+ *chksum += ((v >> 0) & 0xFFF);
+ }
+ }
+
+ do
+ {
+ n -= 6;
+ *p++ = encoding[(v >> n) & 0x3F];
+ }
+ while (n > 0);
+
+ return (count);
+}
+
+/* Shorthand function (that could be in-lined) to output the zero-fill
+ escape sequence into the data stream. */
+static int
+pmon_zeroset (recsize, buff, amount, chksum)
+ int recsize;
+ char **buff;
+ int *amount;
+ unsigned int *chksum;
+{
+ int count;
+
+ sprintf (*buff, "/Z");
+ count = pmon_makeb64 (*amount, (*buff + 2), 12, chksum);
+ *buff += (count + 2);
+ *amount = 0;
+ return (recsize + count + 2);
+}
+
+static int
+pmon_checkset (recsize, buff, value)
+ int recsize;
+ char **buff;
+ int *value;
+{
+ int count;
+
+ /* Add the checksum (without updating the value): */
+ sprintf (*buff, "/C");
+ count = pmon_makeb64 (*value, (*buff + 2), 12, NULL);
+ *buff += (count + 2);
+ sprintf (*buff, "\n");
+ *buff += 2; /* include zero terminator */
+ /* Forcing a checksum validation clears the sum: */
+ *value = 0;
+ return (recsize + count + 3);
+}
+
+/* Amount of padding we leave after at the end of the output buffer,
+ for the checksum and line termination characters: */
+#define CHECKSIZE (4 + 4 + 4 + 2)
+/* zero-fill, checksum, transfer end and line termination space. */
+
+/* The amount of binary data loaded from the object file in a single
+ operation: */
+#define BINCHUNK (1024)
+
+/* Maximum line of data accepted by the monitor: */
+#define MAXRECSIZE (550)
+/* NOTE: This constant depends on the monitor being used. This value
+ is for PMON 5.x on the Cogent Vr4300 board. */
+
+static void
+pmon_make_fastrec (outbuf, inbuf, inptr, inamount, recsize, csum, zerofill)
+ char **outbuf;
+ unsigned char *inbuf;
+ int *inptr;
+ int inamount;
+ int *recsize;
+ unsigned int *csum;
+ unsigned int *zerofill;
+{
+ int count = 0;
+ char *p = *outbuf;
+
+ /* This is a simple check to ensure that our data will fit within
+ the maximum allowable record size. Each record output is 4bytes
+ in length. We must allow space for a pending zero fill command,
+ the record, and a checksum record. */
+ while ((*recsize < (MAXRECSIZE - CHECKSIZE)) && ((inamount - *inptr) > 0))
+ {
+ /* Process the binary data: */
+ if ((inamount - *inptr) < 3)
+ {
+ if (*zerofill != 0)
+ *recsize = pmon_zeroset (*recsize, &p, zerofill, csum);
+ sprintf (p, "/B");
+ count = pmon_makeb64 (inbuf[*inptr], &p[2], 12, csum);
+ p += (2 + count);
+ *recsize += (2 + count);
+ (*inptr)++;
+ }
+ else
+ {
+ unsigned int value = ((inbuf[*inptr + 0] << 16) | (inbuf[*inptr + 1] << 8) | inbuf[*inptr + 2]);
+ /* Simple check for zero data. TODO: A better check would be
+ to check the last, and then the middle byte for being zero
+ (if the first byte is not). We could then check for
+ following runs of zeros, and if above a certain size it is
+ worth the 4 or 8 character hit of the byte insertions used
+ to pad to the start of the zeroes. NOTE: This also depends
+ on the alignment at the end of the zero run. */
+ if (value == 0x00000000)
+ {
+ (*zerofill)++;
+ if (*zerofill == 0xFFF) /* 12bit counter */
+ *recsize = pmon_zeroset (*recsize, &p, zerofill, csum);
+ }
+ else
+ {
+ if (*zerofill != 0)
+ *recsize = pmon_zeroset (*recsize, &p, zerofill, csum);
+ count = pmon_makeb64 (value, p, 24, csum);
+ p += count;
+ *recsize += count;
+ }
+ *inptr += 3;
+ }
+ }
+
+ *outbuf = p;
+ return;
+}
+
+static int
+pmon_check_ack (mesg)
+ char *mesg;
+{
+#if defined(DOETXACK)
+ int c;
+
+ if (!tftp_in_use)
+ {
+ c = SERIAL_READCHAR (udp_in_use ? udp_desc : mips_desc, 2);
+ if ((c == SERIAL_TIMEOUT) || (c != 0x06))
+ {
+ fprintf_unfiltered (gdb_stderr,
+ "Failed to receive valid ACK for %s\n", mesg);
+ return (-1); /* terminate the download */
+ }
+ }
+#endif /* DOETXACK */
+ return (0);
+}
+
+/* pmon_download - Send a sequence of characters to the PMON download port,
+ which is either a serial port or a UDP socket. */
+
+static void
+pmon_start_download ()
+{
+ if (tftp_in_use)
+ {
+ /* Create the temporary download file. */
+ if ((tftp_file = fopen (tftp_localname, "w")) == NULL)
+ perror_with_name (tftp_localname);
+ }
+ else
+ {
+ mips_send_command (udp_in_use ? LOAD_CMD_UDP : LOAD_CMD, 0);
+ mips_expect ("Downloading from ");
+ mips_expect (udp_in_use ? "udp" : "tty0");
+ mips_expect (", ^C to abort\r\n");
+ }
+}
+
+static int
+mips_expect_download (char *string)
+{
+ if (!mips_expect (string))
+ {
+ fprintf_unfiltered (gdb_stderr, "Load did not complete successfully.\n");
+ if (tftp_in_use)
+ remove (tftp_localname); /* Remove temporary file */
+ return 0;
+ }
+ else
+ return 1;
+}
+
+static void
+pmon_end_download (final, bintotal)
+ int final;
+ int bintotal;
+{
+ char hexnumber[9]; /* includes '\0' space */
+
+ if (tftp_in_use)
+ {
+ static char *load_cmd_prefix = "load -b -s ";
+ char *cmd;
+ struct stat stbuf;
+
+ /* Close off the temporary file containing the load data. */
+ fclose (tftp_file);
+ tftp_file = NULL;
+
+ /* Make the temporary file readable by the world. */
+ if (stat (tftp_localname, &stbuf) == 0)
+ chmod (tftp_localname, stbuf.st_mode | S_IROTH);
+
+ /* Must reinitialize the board to prevent PMON from crashing. */
+ mips_send_command ("initEther\r", -1);
+
+ /* Send the load command. */
+ cmd = xmalloc (strlen (load_cmd_prefix) + strlen (tftp_name) + 2);
+ strcpy (cmd, load_cmd_prefix);
+ strcat (cmd, tftp_name);
+ strcat (cmd, "\r");
+ mips_send_command (cmd, 0);
+ free (cmd);
+ if (!mips_expect_download ("Downloading from "))
+ return;
+ if (!mips_expect_download (tftp_name))
+ return;
+ if (!mips_expect_download (", ^C to abort\r\n"))
+ return;
+ }
+
+ /* Wait for the stuff that PMON prints after the load has completed.
+ The timeout value for use in the tftp case (15 seconds) was picked
+ arbitrarily but might be too small for really large downloads. FIXME. */
+ if (mips_monitor == MON_LSI)
+ {
+ pmon_check_ack ("termination");
+ mips_expect_timeout ("Entry address is ", tftp_in_use ? 15 : 2);
+ }
+ else
+ mips_expect_timeout ("Entry Address = ", tftp_in_use ? 15 : 2);
+
+ sprintf (hexnumber, "%x", final);
+ mips_expect (hexnumber);
+ mips_expect ("\r\n");
+ if (mips_monitor != MON_LSI)
+ pmon_check_ack ("termination");
+ mips_expect ("\r\ntotal = 0x");
+ sprintf (hexnumber, "%x", bintotal);
+ mips_expect (hexnumber);
+ if (!mips_expect_download (" bytes\r\n"))
+ return;
+
+ if (tftp_in_use)
+ remove (tftp_localname); /* Remove temporary file */
+}
+
+static void
+pmon_download (buffer, length)
+ char *buffer;
+ int length;
+{
+ if (tftp_in_use)
+ fwrite (buffer, 1, length, tftp_file);
+ else
+ SERIAL_WRITE (udp_in_use ? udp_desc : mips_desc, buffer, length);
+}
+
+static void
+pmon_load_fast (file)
+ char *file;
+{
+ bfd *abfd;
+ asection *s;
+ unsigned char *binbuf;
+ char *buffer;
+ int reclen;
+ unsigned int csum = 0;
+ int hashmark = !tftp_in_use;
+ int bintotal = 0;
+ int final = 0;
+ int finished = 0;
+
+ buffer = (char *) xmalloc (MAXRECSIZE + 1);
+ binbuf = (unsigned char *) xmalloc (BINCHUNK);
+
+ abfd = bfd_openr (file, 0);
+ if (!abfd)
+ {
+ printf_filtered ("Unable to open file %s\n", file);
+ return;
+ }
+
+ if (bfd_check_format (abfd, bfd_object) == 0)
+ {
+ printf_filtered ("File is not an object file\n");
+ return;
+ }
+
+ /* Setup the required download state: */
+ mips_send_command ("set dlproto etxack\r", -1);
+ mips_send_command ("set dlecho off\r", -1);
+ /* NOTE: We get a "cannot set variable" message if the variable is
+ already defined to have the argument we give. The code doesn't
+ care, since it just scans to the next prompt anyway. */
+ /* Start the download: */
+ pmon_start_download ();
+
+ /* Zero the checksum */
+ sprintf (buffer, "/Kxx\n");
+ reclen = strlen (buffer);
+ pmon_download (buffer, reclen);
+ finished = pmon_check_ack ("/Kxx");
+
+ for (s = abfd->sections; s && !finished; s = s->next)
+ if (s->flags & SEC_LOAD) /* only deal with loadable sections */
+ {
+ bintotal += s->_raw_size;
+ final = (s->vma + s->_raw_size);
+
+ printf_filtered ("%s\t: 0x%4x .. 0x%4x ", s->name, (unsigned int) s->vma,
+ (unsigned int) (s->vma + s->_raw_size));
+ gdb_flush (gdb_stdout);
+
+ /* Output the starting address */
+ sprintf (buffer, "/A");
+ reclen = pmon_makeb64 (s->vma, &buffer[2], 36, &csum);
+ buffer[2 + reclen] = '\n';
+ buffer[3 + reclen] = '\0';
+ reclen += 3; /* for the initial escape code and carriage return */
+ pmon_download (buffer, reclen);
+ finished = pmon_check_ack ("/A");
+
+ if (!finished)
+ {
+ unsigned int binamount;
+ unsigned int zerofill = 0;
+ char *bp = buffer;
+ unsigned int i;
+
+ reclen = 0;
+
+ for (i = 0; ((i < s->_raw_size) && !finished); i += binamount)
+ {
+ int binptr = 0;
+
+ binamount = min (BINCHUNK, s->_raw_size - i);
+
+ bfd_get_section_contents (abfd, s, binbuf, i, binamount);
+
+ /* This keeps a rolling checksum, until we decide to output
+ the line: */
+ for (; ((binamount - binptr) > 0);)
+ {
+ pmon_make_fastrec (&bp, binbuf, &binptr, binamount, &reclen, &csum, &zerofill);
+ if (reclen >= (MAXRECSIZE - CHECKSIZE))
+ {
+ reclen = pmon_checkset (reclen, &bp, &csum);
+ pmon_download (buffer, reclen);
+ finished = pmon_check_ack ("data record");
+ if (finished)
+ {
+ zerofill = 0; /* do not transmit pending zerofills */
+ break;
+ }
+
+ if (hashmark)
+ {
+ putchar_unfiltered ('#');
+ gdb_flush (gdb_stdout);
+ }
+
+ bp = buffer;
+ reclen = 0; /* buffer processed */
+ }
+ }
+ }
+
+ /* Ensure no out-standing zerofill requests: */
+ if (zerofill != 0)
+ reclen = pmon_zeroset (reclen, &bp, &zerofill, &csum);
+
+ /* and then flush the line: */
+ if (reclen > 0)
+ {
+ reclen = pmon_checkset (reclen, &bp, &csum);
+ /* Currently pmon_checkset outputs the line terminator by
+ default, so we write out the buffer so far: */
+ pmon_download (buffer, reclen);
+ finished = pmon_check_ack ("record remnant");
+ }
+ }
+
+ putchar_unfiltered ('\n');
+ }
+
+ /* Terminate the transfer. We know that we have an empty output
+ buffer at this point. */
+ sprintf (buffer, "/E/E\n"); /* include dummy padding characters */
+ reclen = strlen (buffer);
+ pmon_download (buffer, reclen);
+
+ if (finished)
+ { /* Ignore the termination message: */
+ SERIAL_FLUSH_INPUT (udp_in_use ? udp_desc : mips_desc);
+ }
+ else
+ { /* Deal with termination message: */
+ pmon_end_download (final, bintotal);
+ }
+
+ return;
+}
+
+/* mips_load -- download a file. */
+
+static void
+mips_load (file, from_tty)
+ char *file;
+ int from_tty;
+{
+ /* Get the board out of remote debugging mode. */
+ if (mips_exit_debug ())
+ error ("mips_load: Couldn't get into monitor mode.");
+
+ if (mips_monitor != MON_IDT)
+ pmon_load_fast (file);
+ else
+ mips_load_srec (file);
+
+ mips_initialize ();
+
+ /* Finally, make the PC point at the start address */
+ if (mips_monitor != MON_IDT)
+ {
+ /* Work around problem where PMON monitor updates the PC after a load
+ to a different value than GDB thinks it has. The following ensures
+ that the write_pc() WILL update the PC value: */
+ register_valid[PC_REGNUM] = 0;
+ }
+ if (exec_bfd)
+ write_pc (bfd_get_start_address (exec_bfd));
+
+ inferior_pid = 0; /* No process now */
+
+/* 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 ();
+}
+
+
+/* Pass the command argument as a packet to PMON verbatim. */
+
+static void
+pmon_command (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ char buf[DATA_MAXLEN + 1];
+ int rlen;
+
+ sprintf (buf, "0x0 %s", args);
+ mips_send_packet (buf, 1);
+ printf_filtered ("Send packet: %s\n", buf);
+
+ rlen = mips_receive_packet (buf, 1, mips_receive_wait);
+ buf[rlen] = '\0';
+ printf_filtered ("Received packet: %s\n", buf);
+}
+\f
+void
+_initialize_remote_mips ()
+{
+ /* Initialize the fields in mips_ops that are common to all four targets. */
+ mips_ops.to_longname = "Remote MIPS debugging over serial line";
+ mips_ops.to_close = mips_close;
+ mips_ops.to_detach = mips_detach;
+ mips_ops.to_resume = mips_resume;
+ mips_ops.to_fetch_registers = mips_fetch_registers;
+ mips_ops.to_store_registers = mips_store_registers;
+ mips_ops.to_prepare_to_store = mips_prepare_to_store;
+ mips_ops.to_xfer_memory = mips_xfer_memory;
+ mips_ops.to_files_info = mips_files_info;
+ mips_ops.to_insert_breakpoint = mips_insert_breakpoint;
+ mips_ops.to_remove_breakpoint = mips_remove_breakpoint;
+ mips_ops.to_kill = mips_kill;
+ mips_ops.to_load = mips_load;
+ mips_ops.to_create_inferior = mips_create_inferior;
+ mips_ops.to_mourn_inferior = mips_mourn_inferior;
+ mips_ops.to_stratum = process_stratum;
+ mips_ops.to_has_all_memory = 1;
+ mips_ops.to_has_memory = 1;
+ mips_ops.to_has_stack = 1;
+ mips_ops.to_has_registers = 1;
+ mips_ops.to_has_execution = 1;
+ mips_ops.to_magic = OPS_MAGIC;
+
+ /* Copy the common fields to all four target vectors. */
+ pmon_ops = ddb_ops = lsi_ops = mips_ops;
+
+ /* Initialize target-specific fields in the target vectors. */
+ mips_ops.to_shortname = "mips";
+ mips_ops.to_doc = "\
+Debug a board using the MIPS remote debugging protocol over a serial line.\n\
+The argument is the device it is connected to or, if it contains a colon,\n\
+HOST:PORT to access a board over a network";
+ mips_ops.to_open = mips_open;
+ mips_ops.to_wait = mips_wait;
+
+ pmon_ops.to_shortname = "pmon";
+ pmon_ops.to_doc = "\
+Debug a board using the PMON MIPS remote debugging protocol over a serial\n\
+line. The argument is the device it is connected to or, if it contains a\n\
+colon, HOST:PORT to access a board over a network";
+ pmon_ops.to_open = pmon_open;
+ pmon_ops.to_wait = mips_wait;
+
+ ddb_ops.to_shortname = "ddb";
+ ddb_ops.to_doc = "\
+Debug a board using the PMON MIPS remote debugging protocol over a serial\n\
+line. The first argument is the device it is connected to or, if it contains\n\
+a colon, HOST:PORT to access a board over a network. The optional second\n\
+parameter is the temporary file in the form HOST:FILENAME to be used for\n\
+TFTP downloads to the board. The optional third parameter is the local name\n\
+of the TFTP temporary file, if it differs from the filename seen by the board.";
+ ddb_ops.to_open = ddb_open;
+ ddb_ops.to_wait = mips_wait;
+
+ lsi_ops.to_shortname = "lsi";
+ lsi_ops.to_doc = pmon_ops.to_doc;
+ lsi_ops.to_open = lsi_open;
+ lsi_ops.to_wait = mips_wait;
+
+ /* Add the targets. */
+ add_target (&mips_ops);
+ add_target (&pmon_ops);
+ add_target (&ddb_ops);
+ add_target (&lsi_ops);
+
+ add_show_from_set (
+ add_set_cmd ("timeout", no_class, var_zinteger,
+ (char *) &mips_receive_wait,
+ "Set timeout in seconds for remote MIPS serial I/O.",
+ &setlist),
+ &showlist);
+
+ add_show_from_set (
+ add_set_cmd ("retransmit-timeout", no_class, var_zinteger,
+ (char *) &mips_retransmit_wait,
+ "Set retransmit timeout in seconds for remote MIPS serial I/O.\n\
+This is the number of seconds to wait for an acknowledgement to a packet\n\
+before resending the packet.", &setlist),
+ &showlist);
+
+ add_show_from_set (
+ add_set_cmd ("syn-garbage-limit", no_class, var_zinteger,
+ (char *) &mips_syn_garbage,
+ "Set the maximum number of characters to ignore when scanning for a SYN.\n\
+This is the maximum number of characters GDB will ignore when trying to\n\
+synchronize with the remote system. A value of -1 means that there is no limit\n\
+(Note that these characters are printed out even though they are ignored.)",
+ &setlist),
+ &showlist);
+
+ add_show_from_set
+ (add_set_cmd ("monitor-prompt", class_obscure, var_string,
+ (char *) &mips_monitor_prompt,
+ "Set the prompt that GDB expects from the monitor.",
+ &setlist),
+ &showlist);
+
+ add_show_from_set (
+ add_set_cmd ("monitor-warnings", class_obscure, var_zinteger,
+ (char *) &monitor_warnings,
+ "Set printing of monitor warnings.\n"
+ "When enabled, monitor warnings about hardware breakpoints "
+ "will be displayed.",
+ &setlist),
+ &showlist);
+
+ add_com ("pmon <command>", class_obscure, pmon_command,
+ "Send a packet to PMON (must be in debug mode).");