915 lines
23 KiB
C
915 lines
23 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* taken from gdb/remote.c
|
|
*
|
|
* I am only interested in the write to memory stuff - everything else
|
|
* has been ripped out
|
|
*
|
|
* all the copyright notices etc have been left in
|
|
*/
|
|
|
|
/* enough so that it will compile */
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
/*nicked from gcc..*/
|
|
|
|
#ifndef alloca
|
|
#ifdef __GNUC__
|
|
#define alloca __builtin_alloca
|
|
#else /* not GNU C. */
|
|
#if (!defined (__STDC__) && defined (sparc)) || defined (__sparc__) || defined (__sparc) || defined (__sgi)
|
|
#include <alloca.h>
|
|
#else /* not sparc */
|
|
#if defined (MSDOS) && !defined (__TURBOC__)
|
|
#include <malloc.h>
|
|
#else /* not MSDOS, or __TURBOC__ */
|
|
#if defined(_AIX)
|
|
#include <malloc.h>
|
|
#pragma alloca
|
|
#else /* not MSDOS, __TURBOC__, or _AIX */
|
|
#ifdef __hpux
|
|
#endif /* __hpux */
|
|
#endif /* not _AIX */
|
|
#endif /* not MSDOS, or __TURBOC__ */
|
|
#endif /* not sparc. */
|
|
#endif /* not GNU C. */
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
void* alloca(size_t);
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
#endif /* alloca not defined. */
|
|
|
|
|
|
#include "serial.h"
|
|
#include "error.h"
|
|
#include "remote.h"
|
|
#define REGISTER_BYTES 0
|
|
#define fprintf_unfiltered fprintf
|
|
#define fprintf_filtered fprintf
|
|
#define fputs_unfiltered fputs
|
|
#define fputs_filtered fputs
|
|
#define fputc_unfiltered fputc
|
|
#define fputc_filtered fputc
|
|
#define printf_unfiltered printf
|
|
#define printf_filtered printf
|
|
#define puts_unfiltered puts
|
|
#define puts_filtered puts
|
|
#define putchar_unfiltered putchar
|
|
#define putchar_filtered putchar
|
|
#define fputstr_unfiltered(a,b,c) fputs((a), (c))
|
|
#define gdb_stdlog stderr
|
|
#define SERIAL_READCHAR(fd,timo) serialreadchar((fd), (timo))
|
|
#define SERIAL_WRITE(fd, addr, len) serialwrite((fd), (addr), (len))
|
|
#define error Error
|
|
#define perror_with_name Perror
|
|
#define gdb_flush fflush
|
|
#define max(a,b) (((a)>(b))?(a):(b))
|
|
#define min(a,b) (((a)<(b))?(a):(b))
|
|
#define target_mourn_inferior() {}
|
|
#define ULONGEST unsigned long
|
|
#define CORE_ADDR unsigned long
|
|
|
|
static int putpkt (char *);
|
|
static int putpkt_binary(char *, int);
|
|
static void getpkt (char *, int);
|
|
|
|
static int remote_debug = 0, remote_register_buf_size = 0, watchdog = 0;
|
|
|
|
int remote_desc = -1, remote_timeout = 10;
|
|
|
|
static void
|
|
fputstrn_unfiltered(char *s, int n, int x, FILE *fp)
|
|
{
|
|
while (n-- > 0)
|
|
fputc(*s++, fp);
|
|
}
|
|
|
|
void
|
|
remote_reset(void)
|
|
{
|
|
SERIAL_WRITE(remote_desc, "+", 1);
|
|
}
|
|
|
|
void
|
|
remote_continue(void)
|
|
{
|
|
putpkt("c");
|
|
}
|
|
|
|
/* Remote target communications for serial-line targets in custom GDB protocol
|
|
Copyright 1988, 91, 92, 93, 94, 95, 96, 97, 98, 1999
|
|
Free Software Foundation, Inc.
|
|
|
|
This file is part of GDB.
|
|
*/
|
|
/* *INDENT-OFF* */
|
|
/* Remote communication protocol.
|
|
|
|
A debug packet whose contents are <data>
|
|
is encapsulated for transmission in the form:
|
|
|
|
$ <data> # CSUM1 CSUM2
|
|
|
|
<data> must be ASCII alphanumeric and cannot include characters
|
|
'$' or '#'. If <data> starts with two characters followed by
|
|
':', then the existing stubs interpret this as a sequence number.
|
|
|
|
CSUM1 and CSUM2 are ascii hex representation of an 8-bit
|
|
checksum of <data>, the most significant nibble is sent first.
|
|
the hex digits 0-9,a-f are used.
|
|
|
|
Receiver responds with:
|
|
|
|
+ - if CSUM is correct and ready for next packet
|
|
- - if CSUM is incorrect
|
|
|
|
<data> is as follows:
|
|
Most values are encoded in ascii hex digits. Signal numbers are according
|
|
to the numbering in target.h.
|
|
|
|
Request Packet
|
|
|
|
set thread Hct... Set thread for subsequent operations.
|
|
c = 'c' for thread used in step and
|
|
continue; t... can be -1 for all
|
|
threads.
|
|
c = 'g' for thread used in other
|
|
operations. If zero, pick a thread,
|
|
any thread.
|
|
reply OK for success
|
|
ENN for an error.
|
|
|
|
read registers g
|
|
reply XX....X Each byte of register data
|
|
is described by two hex digits.
|
|
Registers are in the internal order
|
|
for GDB, and the bytes in a register
|
|
are in the same order the machine uses.
|
|
or ENN for an error.
|
|
|
|
write regs GXX..XX Each byte of register data
|
|
is described by two hex digits.
|
|
reply OK for success
|
|
ENN for an error
|
|
|
|
write reg Pn...=r... Write register n... with value r...,
|
|
which contains two hex digits for each
|
|
byte in the register (target byte
|
|
order).
|
|
reply OK for success
|
|
ENN for an error
|
|
(not supported by all stubs).
|
|
|
|
read mem mAA..AA,LLLL AA..AA is address, LLLL is length.
|
|
reply XX..XX XX..XX is mem contents
|
|
Can be fewer bytes than requested
|
|
if able to read only part of the data.
|
|
or ENN NN is errno
|
|
|
|
write mem MAA..AA,LLLL:XX..XX
|
|
AA..AA is address,
|
|
LLLL is number of bytes,
|
|
XX..XX is data
|
|
reply OK for success
|
|
ENN for an error (this includes the case
|
|
where only part of the data was
|
|
written).
|
|
|
|
write mem XAA..AA,LLLL:XX..XX
|
|
(binary) AA..AA is address,
|
|
LLLL is number of bytes,
|
|
XX..XX is binary data
|
|
reply OK for success
|
|
ENN for an error
|
|
|
|
continue cAA..AA AA..AA is address to resume
|
|
If AA..AA is omitted,
|
|
resume at same address.
|
|
|
|
step sAA..AA AA..AA is address to resume
|
|
If AA..AA is omitted,
|
|
resume at same address.
|
|
|
|
continue with Csig;AA..AA Continue with signal sig (hex signal
|
|
signal number). If ;AA..AA is omitted,
|
|
resume at same address.
|
|
|
|
step with Ssig;AA..AA Like 'C' but step not continue.
|
|
signal
|
|
|
|
last signal ? Reply the current reason for stopping.
|
|
This is the same reply as is generated
|
|
for step or cont : SAA where AA is the
|
|
signal number.
|
|
|
|
detach D Reply OK.
|
|
|
|
There is no immediate reply to step or cont.
|
|
The reply comes when the machine stops.
|
|
It is SAA AA is the signal number.
|
|
|
|
or... TAAn...:r...;n...:r...;n...:r...;
|
|
AA = signal number
|
|
n... = register number (hex)
|
|
r... = register contents
|
|
n... = `thread'
|
|
r... = thread process ID. This is
|
|
a hex integer.
|
|
n... = other string not starting
|
|
with valid hex digit.
|
|
gdb should ignore this n,r pair
|
|
and go on to the next. This way
|
|
we can extend the protocol.
|
|
or... WAA The process exited, and AA is
|
|
the exit status. This is only
|
|
applicable for certains sorts of
|
|
targets.
|
|
or... XAA The process terminated with signal
|
|
AA.
|
|
or (obsolete) NAA;tttttttt;dddddddd;bbbbbbbb
|
|
AA = signal number
|
|
tttttttt = address of symbol "_start"
|
|
dddddddd = base of data section
|
|
bbbbbbbb = base of bss section.
|
|
Note: only used by Cisco Systems
|
|
targets. The difference between this
|
|
reply and the "qOffsets" query is that
|
|
the 'N' packet may arrive spontaneously
|
|
whereas the 'qOffsets' is a query
|
|
initiated by the host debugger.
|
|
or... OXX..XX XX..XX is hex encoding of ASCII data. This
|
|
can happen at any time while the
|
|
program is running and the debugger
|
|
should continue to wait for
|
|
'W', 'T', etc.
|
|
|
|
thread alive TXX Find out if the thread XX is alive.
|
|
reply OK thread is still alive
|
|
ENN thread is dead
|
|
|
|
remote restart RXX Restart the remote server
|
|
|
|
extended ops ! Use the extended remote protocol.
|
|
Sticky -- only needs to be set once.
|
|
|
|
kill request k
|
|
|
|
toggle debug d toggle debug flag (see 386 & 68k stubs)
|
|
reset r reset -- see sparc stub.
|
|
reserved <other> On other requests, the stub should
|
|
ignore the request and send an empty
|
|
response ($#<checksum>). This way
|
|
we can extend the protocol and GDB
|
|
can tell whether the stub it is
|
|
talking to uses the old or the new.
|
|
search tAA:PP,MM Search backwards starting at address
|
|
AA for a match with pattern PP and
|
|
mask MM. PP and MM are 4 bytes.
|
|
Not supported by all stubs.
|
|
|
|
general query qXXXX Request info about XXXX.
|
|
general set QXXXX=yyyy Set value of XXXX to yyyy.
|
|
query sect offs qOffsets Get section offsets. Reply is
|
|
Text=xxx;Data=yyy;Bss=zzz
|
|
|
|
Responses can be run-length encoded to save space. A '*' means that
|
|
the next character is an ASCII encoding giving a repeat count which
|
|
stands for that many repititions of the character preceding the '*'.
|
|
The encoding is n+29, yielding a printable character where n >=3
|
|
(which is where rle starts to win). Don't use an n > 126.
|
|
|
|
So
|
|
"0* " means the same as "0000". */
|
|
/* *INDENT-ON* */
|
|
|
|
/* This variable (available to the user via "set remotebinarydownload")
|
|
dictates whether downloads are sent in binary (via the 'X' packet).
|
|
We assume that the stub can, and attempt to do it. This will be cleared if
|
|
the stub does not understand it. This switch is still needed, though
|
|
in cases when the packet is supported in the stub, but the connection
|
|
does not allow it (i.e., 7-bit serial connection only). */
|
|
static int remote_binary_download = 1;
|
|
|
|
/* Have we already checked whether binary downloads work? */
|
|
static int remote_binary_checked;
|
|
|
|
/* Maximum number of bytes to read/write at once. The value here
|
|
is chosen to fill up a packet (the headers account for the 32). */
|
|
#define MAXBUFBYTES(N) (((N)-32)/2)
|
|
|
|
/* Having this larger than 400 causes us to be incompatible with m68k-stub.c
|
|
and i386-stub.c. Normally, no one would notice because it only matters
|
|
for writing large chunks of memory (e.g. in downloads). Also, this needs
|
|
to be more than 400 if required to hold the registers (see below, where
|
|
we round it up based on REGISTER_BYTES). */
|
|
/* Round up PBUFSIZ to hold all the registers, at least. */
|
|
#define PBUFSIZ ((REGISTER_BYTES > MAXBUFBYTES (400)) \
|
|
? (REGISTER_BYTES * 2 + 32) \
|
|
: 400)
|
|
|
|
|
|
/* This variable sets the number of bytes to be written to the target
|
|
in a single packet. Normally PBUFSIZ is satisfactory, but some
|
|
targets need smaller values (perhaps because the receiving end
|
|
is slow). */
|
|
|
|
static int remote_write_size = 0x7fffffff;
|
|
|
|
/* This variable sets the number of bits in an address that are to be
|
|
sent in a memory ("M" or "m") packet. Normally, after stripping
|
|
leading zeros, the entire address would be sent. This variable
|
|
restricts the address to REMOTE_ADDRESS_SIZE bits. HISTORY: The
|
|
initial implementation of remote.c restricted the address sent in
|
|
memory packets to ``host::sizeof long'' bytes - (typically 32
|
|
bits). Consequently, for 64 bit targets, the upper 32 bits of an
|
|
address was never sent. Since fixing this bug may cause a break in
|
|
some remote targets this variable is principly provided to
|
|
facilitate backward compatibility. */
|
|
|
|
static int remote_address_size;
|
|
|
|
/* Convert hex digit A to a number. */
|
|
|
|
static int
|
|
fromhex (int a)
|
|
{
|
|
if (a >= '0' && a <= '9')
|
|
return a - '0';
|
|
else if (a >= 'a' && a <= 'f')
|
|
return a - 'a' + 10;
|
|
else if (a >= 'A' && a <= 'F')
|
|
return a - 'A' + 10;
|
|
else {
|
|
error ("Reply contains invalid hex digit %d", a);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* Convert number NIB to a hex digit. */
|
|
|
|
static int
|
|
tohex (int nib)
|
|
{
|
|
if (nib < 10)
|
|
return '0' + nib;
|
|
else
|
|
return 'a' + nib - 10;
|
|
}
|
|
|
|
/* Return the number of hex digits in num. */
|
|
|
|
static int
|
|
hexnumlen (ULONGEST num)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; num != 0; i++)
|
|
num >>= 4;
|
|
|
|
return max (i, 1);
|
|
}
|
|
|
|
/* Set BUF to the hex digits representing NUM. */
|
|
|
|
static int
|
|
hexnumstr (char *buf, ULONGEST num)
|
|
{
|
|
int i;
|
|
int len = hexnumlen (num);
|
|
|
|
buf[len] = '\0';
|
|
|
|
for (i = len - 1; i >= 0; i--)
|
|
{
|
|
buf[i] = "0123456789abcdef"[(num & 0xf)];
|
|
num >>= 4;
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
/* Mask all but the least significant REMOTE_ADDRESS_SIZE bits. */
|
|
|
|
static CORE_ADDR
|
|
remote_address_masked (CORE_ADDR addr)
|
|
{
|
|
if (remote_address_size > 0
|
|
&& remote_address_size < (sizeof (ULONGEST) * 8))
|
|
{
|
|
/* Only create a mask when that mask can safely be constructed
|
|
in a ULONGEST variable. */
|
|
ULONGEST mask = 1;
|
|
mask = (mask << remote_address_size) - 1;
|
|
addr &= mask;
|
|
}
|
|
return addr;
|
|
}
|
|
|
|
/* Determine whether the remote target supports binary downloading.
|
|
This is accomplished by sending a no-op memory write of zero length
|
|
to the target at the specified address. It does not suffice to send
|
|
the whole packet, since many stubs strip the eighth bit and subsequently
|
|
compute a wrong checksum, which causes real havoc with remote_write_bytes.
|
|
|
|
NOTE: This can still lose if the serial line is not eight-bit clean. In
|
|
cases like this, the user should clear "remotebinarydownload". */
|
|
static void
|
|
check_binary_download (CORE_ADDR addr)
|
|
{
|
|
if (remote_binary_download && !remote_binary_checked)
|
|
{
|
|
char *buf = alloca (PBUFSIZ);
|
|
char *p;
|
|
remote_binary_checked = 1;
|
|
|
|
p = buf;
|
|
*p++ = 'X';
|
|
p += hexnumstr (p, (ULONGEST) addr);
|
|
*p++ = ',';
|
|
p += hexnumstr (p, (ULONGEST) 0);
|
|
*p++ = ':';
|
|
*p = '\0';
|
|
|
|
putpkt_binary (buf, (int) (p - buf));
|
|
getpkt (buf, 0);
|
|
|
|
if (buf[0] == '\0')
|
|
remote_binary_download = 0;
|
|
}
|
|
|
|
if (remote_debug)
|
|
{
|
|
if (remote_binary_download)
|
|
fprintf_unfiltered (gdb_stdlog,
|
|
"binary downloading suppported by target\n");
|
|
else
|
|
fprintf_unfiltered (gdb_stdlog,
|
|
"binary downloading NOT suppported by target\n");
|
|
}
|
|
}
|
|
|
|
/* 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. */
|
|
|
|
int
|
|
remote_write_bytes (memaddr, myaddr, len)
|
|
CORE_ADDR memaddr;
|
|
char *myaddr;
|
|
int len;
|
|
{
|
|
unsigned char *buf = alloca (PBUFSIZ);
|
|
int max_buf_size; /* Max size of packet output buffer */
|
|
int origlen;
|
|
extern int verbose;
|
|
|
|
/* Verify that the target can support a binary download */
|
|
check_binary_download (memaddr);
|
|
|
|
/* Chop the transfer down if necessary */
|
|
|
|
max_buf_size = min (remote_write_size, PBUFSIZ);
|
|
if (remote_register_buf_size != 0)
|
|
max_buf_size = min (max_buf_size, remote_register_buf_size);
|
|
|
|
/* Subtract header overhead from max payload size - $M<memaddr>,<len>:#nn */
|
|
max_buf_size -= 2 + hexnumlen (memaddr + len - 1) + 1 + hexnumlen (len) + 4;
|
|
|
|
origlen = len;
|
|
while (len > 0)
|
|
{
|
|
unsigned char *p, *plen;
|
|
int todo;
|
|
int i;
|
|
|
|
/* construct "M"<memaddr>","<len>":" */
|
|
/* sprintf (buf, "M%lx,%x:", (unsigned long) memaddr, todo); */
|
|
memaddr = remote_address_masked (memaddr);
|
|
p = buf;
|
|
if (remote_binary_download)
|
|
{
|
|
*p++ = 'X';
|
|
todo = min (len, max_buf_size);
|
|
}
|
|
else
|
|
{
|
|
*p++ = 'M';
|
|
todo = min (len, max_buf_size / 2); /* num bytes that will fit */
|
|
}
|
|
|
|
p += hexnumstr ((char *)p, (ULONGEST) memaddr);
|
|
*p++ = ',';
|
|
|
|
plen = p; /* remember where len field goes */
|
|
p += hexnumstr ((char *)p, (ULONGEST) todo);
|
|
*p++ = ':';
|
|
*p = '\0';
|
|
|
|
/* We send target system values byte by byte, in increasing byte
|
|
addresses, each byte encoded as two hex characters (or one
|
|
binary character). */
|
|
if (remote_binary_download)
|
|
{
|
|
int escaped = 0;
|
|
for (i = 0;
|
|
(i < todo) && (i + escaped) < (max_buf_size - 2);
|
|
i++)
|
|
{
|
|
switch (myaddr[i] & 0xff)
|
|
{
|
|
case '$':
|
|
case '#':
|
|
case 0x7d:
|
|
/* These must be escaped */
|
|
escaped++;
|
|
*p++ = 0x7d;
|
|
*p++ = (myaddr[i] & 0xff) ^ 0x20;
|
|
break;
|
|
default:
|
|
*p++ = myaddr[i] & 0xff;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i < todo)
|
|
{
|
|
/* Escape chars have filled up the buffer prematurely,
|
|
and we have actually sent fewer bytes than planned.
|
|
Fix-up the length field of the packet. */
|
|
|
|
/* FIXME: will fail if new len is a shorter string than
|
|
old len. */
|
|
|
|
plen += hexnumstr ((char *)plen, (ULONGEST) i);
|
|
*plen++ = ':';
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < todo; i++)
|
|
{
|
|
*p++ = tohex ((myaddr[i] >> 4) & 0xf);
|
|
*p++ = tohex (myaddr[i] & 0xf);
|
|
}
|
|
*p = '\0';
|
|
}
|
|
|
|
putpkt_binary ((char *)buf, (int) (p - buf));
|
|
getpkt ((char *)buf, 0);
|
|
|
|
if (buf[0] == 'E')
|
|
{
|
|
/* There is no correspondance between what the remote protocol uses
|
|
for errors and errno codes. We would like a cleaner way of
|
|
representing errors (big enough to include errno codes, bfd_error
|
|
codes, and others). But for now just return EIO. */
|
|
errno = EIO;
|
|
return 0;
|
|
}
|
|
|
|
/* Increment by i, not by todo, in case escape chars
|
|
caused us to send fewer bytes than we'd planned. */
|
|
myaddr += i;
|
|
memaddr += i;
|
|
len -= i;
|
|
|
|
if (verbose)
|
|
putc('.', stderr);
|
|
}
|
|
return origlen;
|
|
}
|
|
|
|
/* 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 end, masking it down to 7 bits. */
|
|
|
|
static int
|
|
readchar (int timeout)
|
|
{
|
|
int ch;
|
|
|
|
ch = SERIAL_READCHAR (remote_desc, timeout);
|
|
|
|
switch (ch)
|
|
{
|
|
case SERIAL_EOF:
|
|
error ("Remote connection closed");
|
|
case SERIAL_ERROR:
|
|
perror_with_name ("Remote communication error");
|
|
case SERIAL_TIMEOUT:
|
|
return ch;
|
|
default:
|
|
return ch & 0x7f;
|
|
}
|
|
}
|
|
|
|
static int
|
|
putpkt (buf)
|
|
char *buf;
|
|
{
|
|
return putpkt_binary (buf, strlen (buf));
|
|
}
|
|
|
|
/* Send a packet to the remote machine, with error checking. The data
|
|
of the packet is in BUF. The string in BUF can be at most PBUFSIZ - 5
|
|
to account for the $, # and checksum, and for a possible /0 if we are
|
|
debugging (remote_debug) and want to print the sent packet as a string */
|
|
|
|
static int
|
|
putpkt_binary (buf, cnt)
|
|
char *buf;
|
|
int cnt;
|
|
{
|
|
int i;
|
|
unsigned char csum = 0;
|
|
char *buf2 = alloca (PBUFSIZ);
|
|
char *junkbuf = alloca (PBUFSIZ);
|
|
|
|
int ch;
|
|
int tcount = 0;
|
|
char *p;
|
|
|
|
/* Copy the packet into buffer BUF2, encapsulating it
|
|
and giving it a checksum. */
|
|
|
|
if (cnt > BUFSIZ - 5) /* Prosanity check */
|
|
abort ();
|
|
|
|
p = buf2;
|
|
*p++ = '$';
|
|
|
|
for (i = 0; i < cnt; i++)
|
|
{
|
|
csum += buf[i];
|
|
*p++ = buf[i];
|
|
}
|
|
*p++ = '#';
|
|
*p++ = tohex ((csum >> 4) & 0xf);
|
|
*p++ = tohex (csum & 0xf);
|
|
|
|
/* Send it over and over until we get a positive ack. */
|
|
|
|
while (1)
|
|
{
|
|
int started_error_output = 0;
|
|
|
|
if (remote_debug)
|
|
{
|
|
*p = '\0';
|
|
fprintf_unfiltered (gdb_stdlog, "Sending packet: ");
|
|
fputstrn_unfiltered (buf2, p - buf2, 0, gdb_stdlog);
|
|
fprintf_unfiltered (gdb_stdlog, "...");
|
|
gdb_flush (gdb_stdlog);
|
|
}
|
|
if (SERIAL_WRITE (remote_desc, buf2, p - buf2))
|
|
perror_with_name ("putpkt: write failed");
|
|
|
|
/* read until either a timeout occurs (-2) or '+' is read */
|
|
while (1)
|
|
{
|
|
ch = readchar (remote_timeout);
|
|
|
|
if (remote_debug)
|
|
{
|
|
switch (ch)
|
|
{
|
|
case '+':
|
|
case SERIAL_TIMEOUT:
|
|
case '$':
|
|
if (started_error_output)
|
|
{
|
|
putchar_unfiltered ('\n');
|
|
started_error_output = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
switch (ch)
|
|
{
|
|
case '+':
|
|
if (remote_debug)
|
|
fprintf_unfiltered (gdb_stdlog, "Ack\n");
|
|
return 1;
|
|
case SERIAL_TIMEOUT:
|
|
tcount++;
|
|
if (tcount > 3)
|
|
return 0;
|
|
break; /* Retransmit buffer */
|
|
case '$':
|
|
{
|
|
/* It's probably an old response, and we're out of sync.
|
|
Just gobble up the packet and ignore it. */
|
|
getpkt (junkbuf, 0);
|
|
continue; /* Now, go look for + */
|
|
}
|
|
default:
|
|
if (remote_debug)
|
|
{
|
|
if (!started_error_output)
|
|
{
|
|
started_error_output = 1;
|
|
fprintf_unfiltered (gdb_stdlog, "putpkt: Junk: ");
|
|
}
|
|
fputc_unfiltered (ch & 0177, gdb_stdlog);
|
|
}
|
|
continue;
|
|
}
|
|
break; /* Here to retransmit */
|
|
}
|
|
|
|
#if 0
|
|
/* This is wrong. If doing a long backtrace, the user should be
|
|
able to get out next time we call QUIT, without anything as
|
|
violent as interrupt_query. If we want to provide a way out of
|
|
here without getting to the next QUIT, it should be based on
|
|
hitting ^C twice as in remote_wait. */
|
|
if (quit_flag)
|
|
{
|
|
quit_flag = 0;
|
|
interrupt_query ();
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/* Come here after finding the start of the frame. Collect the rest
|
|
into BUF, verifying the checksum, length, and handling run-length
|
|
compression. Returns 0 on any error, 1 on success. */
|
|
|
|
static int
|
|
read_frame (char *buf)
|
|
{
|
|
unsigned char csum;
|
|
char *bp;
|
|
int c;
|
|
|
|
csum = 0;
|
|
bp = buf;
|
|
|
|
while (1)
|
|
{
|
|
c = readchar (remote_timeout);
|
|
|
|
switch (c)
|
|
{
|
|
case SERIAL_TIMEOUT:
|
|
if (remote_debug)
|
|
fputs_filtered ("Timeout in mid-packet, retrying\n", gdb_stdlog);
|
|
return 0;
|
|
case '$':
|
|
if (remote_debug)
|
|
fputs_filtered ("Saw new packet start in middle of old one\n",
|
|
gdb_stdlog);
|
|
return 0; /* Start a new packet, count retries */
|
|
case '#':
|
|
{
|
|
unsigned char pktcsum;
|
|
|
|
*bp = '\000';
|
|
|
|
pktcsum = fromhex (readchar (remote_timeout)) << 4;
|
|
pktcsum |= fromhex (readchar (remote_timeout));
|
|
|
|
if (csum == pktcsum)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
if (remote_debug)
|
|
{
|
|
fprintf_filtered (gdb_stdlog,
|
|
"Bad checksum, sentsum=0x%x, csum=0x%x, buf=",
|
|
pktcsum, csum);
|
|
fputs_filtered (buf, gdb_stdlog);
|
|
fputs_filtered ("\n", gdb_stdlog);
|
|
}
|
|
return 0;
|
|
}
|
|
case '*': /* Run length encoding */
|
|
csum += c;
|
|
c = readchar (remote_timeout);
|
|
csum += c;
|
|
c = c - ' ' + 3; /* Compute repeat count */
|
|
|
|
if (c > 0 && c < 255 && bp + c - 1 < buf + PBUFSIZ - 1)
|
|
{
|
|
memset (bp, *(bp - 1), c);
|
|
bp += c;
|
|
continue;
|
|
}
|
|
|
|
*bp = '\0';
|
|
printf_filtered ("Repeat count %d too large for buffer: ", c);
|
|
puts_filtered (buf);
|
|
puts_filtered ("\n");
|
|
return 0;
|
|
default:
|
|
if (bp < buf + PBUFSIZ - 1)
|
|
{
|
|
*bp++ = c;
|
|
csum += c;
|
|
continue;
|
|
}
|
|
|
|
*bp = '\0';
|
|
puts_filtered ("Remote packet too long: ");
|
|
puts_filtered (buf);
|
|
puts_filtered ("\n");
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Read a packet from the remote machine, with error checking, and
|
|
store it in BUF. BUF is expected to be of size PBUFSIZ. If
|
|
FOREVER, wait forever rather than timing out; this is used while
|
|
the target is executing user code. */
|
|
|
|
static void
|
|
getpkt (buf, forever)
|
|
char *buf;
|
|
int forever;
|
|
{
|
|
int c;
|
|
int tries;
|
|
int timeout;
|
|
int val;
|
|
|
|
strcpy (buf, "timeout");
|
|
|
|
if (forever)
|
|
{
|
|
timeout = watchdog > 0 ? watchdog : -1;
|
|
}
|
|
|
|
else
|
|
timeout = remote_timeout;
|
|
|
|
#define MAX_TRIES 3
|
|
|
|
for (tries = 1; tries <= MAX_TRIES; tries++)
|
|
{
|
|
/* This can loop forever if the remote side sends us characters
|
|
continuously, but if it pauses, we'll get a zero from readchar
|
|
because of timeout. Then we'll count that as a retry. */
|
|
|
|
/* Note that we will only wait forever prior to the start of a packet.
|
|
After that, we expect characters to arrive at a brisk pace. They
|
|
should show up within remote_timeout intervals. */
|
|
|
|
do
|
|
{
|
|
c = readchar (timeout);
|
|
|
|
if (c == SERIAL_TIMEOUT)
|
|
{
|
|
if (forever) /* Watchdog went off. Kill the target. */
|
|
{
|
|
target_mourn_inferior ();
|
|
error ("Watchdog has expired. Target detached.\n");
|
|
}
|
|
if (remote_debug)
|
|
fputs_filtered ("Timed out.\n", gdb_stdlog);
|
|
goto retry;
|
|
}
|
|
}
|
|
while (c != '$');
|
|
|
|
/* We've found the start of a packet, now collect the data. */
|
|
|
|
val = read_frame (buf);
|
|
|
|
if (val == 1)
|
|
{
|
|
if (remote_debug)
|
|
{
|
|
fprintf_unfiltered (gdb_stdlog, "Packet received: ");
|
|
fputstr_unfiltered (buf, 0, gdb_stdlog);
|
|
fprintf_unfiltered (gdb_stdlog, "\n");
|
|
}
|
|
SERIAL_WRITE (remote_desc, "+", 1);
|
|
return;
|
|
}
|
|
|
|
/* Try the whole thing again. */
|
|
retry:
|
|
SERIAL_WRITE (remote_desc, "-", 1);
|
|
}
|
|
|
|
/* We have tried hard enough, and just can't receive the packet. Give up. */
|
|
|
|
printf_unfiltered ("Ignoring packet error, continuing...\n");
|
|
SERIAL_WRITE (remote_desc, "+", 1);
|
|
}
|