This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
add file I/O support when debugging an embedded target via jtag
- From: Bart Veer <bartv at ecoscentric dot com>
- To: gdb-patches at sourceware dot org
- Date: Tue, 12 Aug 2008 17:33:20 +0100
- Subject: add file I/O support when debugging an embedded target via jtag
This patch extends the remote protocol file I/O support so that it can
be used in conjunction with hardware debuggers which typically do not
support this. It was previously discussed in
http://sourceware.org/ml/gdb/2008-07/msg00045.html
The patch consists of the following bits:
1) add a new stratum to target.h to allow for a target vector between
the process and thread levels.
2) update remote-fileio.c so that it no longer assumes it is called
directly from remote.c. This involves passing a putpkt() function
to remote_fileio_request() instead of automatically using
remote.c's putpkt(). It also involves using target_read_memory()
and target_write_memory() instead of remote_read_bytes() and
remote_write_bytes().
3) remote-fileio.h needs an updated prototype for
remote_fileio_request() and remote.c needs to pass a putpkt()
function.
4) a new module hwdebug-fileio.c to implement the gdb side.
5) Makefile.in update to add hwdebug-fileio.c
6) documentation update to gdb.texinfo to describe the hwdebug
support.
7) an example implementation for an embedded target, showing
developers of embedded OS's and run-time systems how to incorporate
the functionality.
Top-level ChangeLog entry:
2008-08-12 Bart Veer <bartv@ecoscentric.co.uk>
* target.h: add new stratum process_override
* remote-fileio.h, remote.c: add putpkt argument to
remote_fileio_request()
* remote-fileio.c: the putpkt function is supplied by the
caller. Use target_read_memory() and target_write_memory()
instead of assuming the remote protocol.
* hwdebug-fileio.c: new module supporting file I/O operations
when the remote protocol server does not provide the necessary
support.
* Makefile.in: add hwdebug-fileio.c
doc ChangeLog entry:
2008-08-12 Bart Veer <bartv@ecoscentric.co.uk>
* gdb.texinfo: document h/w debug file I/O support.
* hwdebug-example.c: example target-side code for the h/w
debug file I/O support.
Bart
----------------------------------------------------------------------------
diff -ruN --exclude=CVS gdb/target.h gdb-hwdebug/target.h
--- gdb/target.h 2008-07-21 16:03:07.000000000 +0100
+++ gdb-hwdebug/target.h 2008-07-21 16:37:09.000000000 +0100
@@ -62,6 +62,7 @@
file_stratum, /* Executable files, etc */
core_stratum, /* Core dump files */
process_stratum, /* Executing processes */
+ process_override_stratum, /* Tweak process settings */
thread_stratum /* Executing threads */
};
diff -ruN --exclude=CVS gdb/remote-fileio.c gdb-hwdebug/remote-fileio.c
--- gdb/remote-fileio.c 2008-03-08 10:23:14.000000000 +0000
+++ gdb-hwdebug/remote-fileio.c 2008-07-30 17:40:40.000000000 +0100
@@ -29,6 +29,7 @@
#include "exceptions.h"
#include "remote-fileio.h"
#include "event-loop.h"
+#include "target.h"
#include <fcntl.h>
#include <sys/time.h>
@@ -48,6 +49,8 @@
static int remote_fio_system_call_allowed = 0;
+static int (*target_putpkt_fn) (char*);
+
static struct async_signal_handler *sigint_fileio_token;
static int
@@ -549,7 +552,7 @@
strcat (buf, ",C");
}
remote_fileio_sig_set (remote_fileio_ctrl_c_signal_handler);
- putpkt (buf);
+ (*target_putpkt_fn) (buf);
}
static void
@@ -577,29 +580,11 @@
remote_fileio_reply (retcode, 0);
}
-/* Wrapper function for remote_write_bytes() which has the disadvantage to
- write only one packet, regardless of the requested number of bytes to
- transfer. This wrapper calls remote_write_bytes() as often as needed. */
-static int
-remote_fileio_write_bytes (CORE_ADDR memaddr, gdb_byte *myaddr, int len)
-{
- int ret = 0, written;
-
- while (len > 0 && (written = remote_write_bytes (memaddr, myaddr, len)) > 0)
- {
- len -= written;
- memaddr += written;
- myaddr += written;
- ret += written;
- }
- return ret;
-}
-
static void
remote_fileio_func_open (char *buf)
{
CORE_ADDR ptrval;
- int length, retlength;
+ int length;
long num;
int flags, fd;
mode_t mode;
@@ -627,10 +612,9 @@
}
mode = remote_fileio_mode_to_host (num, 1);
- /* Request pathname using 'm' packet */
+ /* Request pathname */
pathname = alloca (length);
- retlength = remote_read_bytes (ptrval, (gdb_byte *) pathname, length);
- if (retlength != length)
+ if (target_read_memory (ptrval, (gdb_byte *) pathname, length) )
{
remote_fileio_ioerror ();
return;
@@ -698,7 +682,7 @@
long target_fd, num;
LONGEST lnum;
CORE_ADDR ptrval;
- int fd, ret, retlength;
+ int fd, ret;
gdb_byte *buffer;
size_t length;
off_t old_offset, new_offset;
@@ -797,9 +781,11 @@
if (ret > 0)
{
- retlength = remote_fileio_write_bytes (ptrval, buffer, ret);
- if (retlength != ret)
- ret = -1; /* errno has been set to EIO in remote_fileio_write_bytes() */
+ if ( target_write_memory (ptrval, buffer, ret) )
+ {
+ errno = EIO;
+ ret = -1;
+ }
}
if (ret < 0)
@@ -816,7 +802,7 @@
long target_fd, num;
LONGEST lnum;
CORE_ADDR ptrval;
- int fd, ret, retlength;
+ int fd, ret;
gdb_byte *buffer;
size_t length;
@@ -848,8 +834,7 @@
length = (size_t) num;
buffer = (gdb_byte *) xmalloc (length);
- retlength = remote_read_bytes (ptrval, buffer, length);
- if (retlength != length)
+ if ( target_read_memory (ptrval, buffer, length))
{
xfree (buffer);
remote_fileio_ioerror ();
@@ -942,7 +927,7 @@
remote_fileio_func_rename (char *buf)
{
CORE_ADDR old_ptr, new_ptr;
- int old_len, new_len, retlength;
+ int old_len, new_len;
char *oldpath, *newpath;
int ret, of, nf;
struct stat ost, nst;
@@ -961,19 +946,17 @@
return;
}
- /* Request oldpath using 'm' packet */
+ /* Request oldpath */
oldpath = alloca (old_len);
- retlength = remote_read_bytes (old_ptr, (gdb_byte *) oldpath, old_len);
- if (retlength != old_len)
+ if (target_read_memory (old_ptr, (gdb_byte*) oldpath, old_len))
{
remote_fileio_ioerror ();
return;
}
- /* Request newpath using 'm' packet */
+ /* Request newpath */
newpath = alloca (new_len);
- retlength = remote_read_bytes (new_ptr, (gdb_byte *) newpath, new_len);
- if (retlength != new_len)
+ if (target_read_memory (new_ptr, (gdb_byte *) newpath, new_len))
{
remote_fileio_ioerror ();
return;
@@ -1036,7 +1019,7 @@
remote_fileio_func_unlink (char *buf)
{
CORE_ADDR ptrval;
- int length, retlength;
+ int length;
char *pathname;
int ret;
struct stat st;
@@ -1047,10 +1030,9 @@
remote_fileio_ioerror ();
return;
}
- /* Request pathname using 'm' packet */
+ /* Request pathname */
pathname = alloca (length);
- retlength = remote_read_bytes (ptrval, (gdb_byte *) pathname, length);
- if (retlength != length)
+ if (target_read_memory (ptrval, (gdb_byte *) pathname, length))
{
remote_fileio_ioerror ();
return;
@@ -1077,7 +1059,7 @@
remote_fileio_func_stat (char *buf)
{
CORE_ADDR statptr, nameptr;
- int ret, namelength, retlength;
+ int ret, namelength;
char *pathname;
LONGEST lnum;
struct stat st;
@@ -1098,10 +1080,9 @@
}
statptr = (CORE_ADDR) lnum;
- /* Request pathname using 'm' packet */
+ /* Request pathname */
pathname = alloca (namelength);
- retlength = remote_read_bytes (nameptr, (gdb_byte *) pathname, namelength);
- if (retlength != namelength)
+ if (target_read_memory (nameptr, (gdb_byte *) pathname, namelength))
{
remote_fileio_ioerror ();
return;
@@ -1125,12 +1106,10 @@
{
remote_fileio_to_fio_stat (&st, &fst);
remote_fileio_to_fio_uint (0, fst.fst_dev);
-
- retlength = remote_fileio_write_bytes (statptr,
- (gdb_byte *) &fst, sizeof fst);
- if (retlength != sizeof fst)
+
+ if (target_write_memory (statptr, (gdb_byte *) &fst, sizeof fst) )
{
- remote_fileio_return_errno (-1);
+ remote_fileio_ioerror ();
return;
}
}
@@ -1141,7 +1120,7 @@
remote_fileio_func_fstat (char *buf)
{
CORE_ADDR ptrval;
- int fd, ret, retlength;
+ int fd, ret;
long target_fd;
LONGEST lnum;
struct stat st;
@@ -1210,10 +1189,9 @@
{
remote_fileio_to_fio_stat (&st, &fst);
- retlength = remote_fileio_write_bytes (ptrval, (gdb_byte *) &fst, sizeof fst);
- if (retlength != sizeof fst)
+ if (target_write_memory (ptrval, (gdb_byte *) &fst, sizeof fst))
{
- remote_fileio_return_errno (-1);
+ remote_fileio_ioerror ();
return;
}
}
@@ -1225,7 +1203,7 @@
{
LONGEST lnum;
CORE_ADDR ptrval;
- int ret, retlength;
+ int ret;
struct timeval tv;
struct fio_timeval ftv;
@@ -1262,10 +1240,9 @@
{
remote_fileio_to_fio_timeval (&tv, &ftv);
- retlength = remote_fileio_write_bytes (ptrval, (gdb_byte *) &ftv, sizeof ftv);
- if (retlength != sizeof ftv)
+ if (target_write_memory (ptrval, (gdb_byte *) &ftv, sizeof ftv))
{
- remote_fileio_return_errno (-1);
+ remote_fileio_ioerror ();
return;
}
}
@@ -1306,10 +1283,9 @@
if (length)
{
- /* Request commandline using 'm' packet */
+ /* Request commandline */
cmdline = alloca (length);
- retlength = remote_read_bytes (ptrval, (gdb_byte *) cmdline, length);
- if (retlength != length)
+ if (target_read_memory (ptrval, (gdb_byte *) cmdline, length))
{
remote_fileio_ioerror ();
return;
@@ -1405,10 +1381,12 @@
}
void
-remote_fileio_request (char *buf)
+remote_fileio_request (char *buf, int (*putpkt_fn)(char*))
{
int ex;
+ target_putpkt_fn = putpkt_fn;
+
remote_fileio_sig_init ();
remote_fio_ctrl_c_flag = 0;
diff -ruN --exclude=CVS gdb/remote-fileio.h gdb-hwdebug/remote-fileio.h
--- gdb/remote-fileio.h 2008-03-08 10:23:14.000000000 +0000
+++ gdb-hwdebug/remote-fileio.h 2008-07-04 21:37:57.000000000 +0100
@@ -26,7 +26,7 @@
/* Unified interface to remote fileio, called in remote.c from
remote_wait () and remote_async_wait () */
-extern void remote_fileio_request (char *buf);
+extern void remote_fileio_request (char *buf, int (*putpkt_fn)(char*));
/* Cleanup any remote fileio state. */
extern void remote_fileio_reset (void);
diff -ruN --exclude=CVS gdb/remote.c gdb-hwdebug/remote.c
--- gdb/remote.c 2008-08-12 16:19:32.000000000 +0100
+++ gdb-hwdebug/remote.c 2008-08-12 16:22:42.000000000 +0100
@@ -3523,7 +3523,7 @@
status->value.sig = TARGET_SIGNAL_0;
goto got_status;
case 'F': /* File-I/O request. */
- remote_fileio_request (buf);
+ remote_fileio_request (buf, &putpkt);
continue;
case 'T': /* Status with PC, SP, FP, ... */
{
diff -ruN --exclude=CVS gdb/hwdebug-fileio.c gdb-hwdebug/hwdebug-fileio.c
--- gdb/hwdebug-fileio.c 1970-01-01 01:00:00.000000000 +0100
+++ gdb-hwdebug/hwdebug-fileio.c 2008-08-02 22:56:47.000000000 +0100
@@ -0,0 +1,620 @@
+/* File I/O support for when not using the remote protocol.
+
+ Copyright (C) 2008 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 3 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, see <http://www.gnu.org/licenses/>. */
+
+#include "defs.h"
+#include "gdbarch.h"
+#include "target.h"
+#include "command.h"
+#include "gdbtypes.h"
+#include "remote-fileio.h"
+#include "exceptions.h"
+#include "symtab.h"
+#include "symfile.h"
+#include "value.h"
+#include "inferior.h"
+#include "regcache.h"
+#include "observer.h"
+#include "gdbcmd.h"
+
+static int hwdebug_unpack_int32 (const gdb_byte *, int);
+static int hwdebug_unpack_int64 (const gdb_byte *, int);
+static int hwdebug_unpack_voidptr (const gdb_byte *, int);
+static int hwdebug_unpack_charptr (const gdb_byte *, int);
+static int hwdebug_handle_request (void);
+static int hwdebug_putpkt (char *);
+static int hwdebug_update_target_data (void);
+
+static void hwdebug_push_target_vec (void);
+static ptid_t hwdebug_wait (ptid_t, struct target_waitstatus *);
+static void hwdebug_resume (ptid_t, int, enum target_signal);
+static void hwdebug_load (char *, int);
+static LONGEST hwdebug_xfer_partial (struct target_ops *, enum target_object,
+ const char *, gdb_byte *,
+ const gdb_byte *, ULONGEST, LONGEST);
+static void hwdebug_set (char *, int, struct cmd_list_element *);
+static void hwdebug_executable_changed (void);
+
+void _initialize_hwdebug_fileio (void);
+
+/* ----------------------------------------------------------------------------
+ This module's functionality is disabled by default. It must be
+ explicitly enabled with a set hwdebug. */
+static int hwdebug_enabled = 0;
+
+/* A set of h/w debug-specific target operations, overriding certain
+ remote protocol or equivalent operations. */
+static struct target_ops hwdebug_ops;
+
+/* Operations that are currently pending, for example clearing the
+ target-side disabled flag on the next continue. */
+static int update_needed__gdb_hwdebug_disabled = 0;
+
+// Has remote-fileio.c requested a ctrl-C?
+static int ctrlc_pending = 0;
+
+/* Space for local copies of the target-side request and reply.
+ Sizes and padding may depend on the architecture. */
+static gdb_byte *request_copy = NULL;
+static gdb_byte *reply_copy = NULL;
+
+/* Space for the remote protocol string generated from request_copy.
+ The largest request is Frename,ptr/len,ptr/len. Allow for 64-bit
+ pointers, 32-bit integers, and a \0 ->
+ (1 + 6 + 1 + 16 + 1 + 8 + 1 + 16 + 1 + 8 + 1) == 60
+ The unpacking process may consume one extra byte. */
+#define REQUEST_MAX 61
+static char request_str[REQUEST_MAX];
+
+// Does the target executable provide all required functionality?
+static int target_has_hwdebug_support = 0;
+
+/* Various target-side addresses, types, etc. that are needed. These
+ are calculated only when h/w debugging is enabled or when the
+ executable is changed, to keep file I/O turnaround as quick as
+ possible. */
+static CORE_ADDR _gdb_hwdebug_disabled;
+static CORE_ADDR _gdb_hwdebug_breakpoint;
+static int has_gdb_hwdebug_continue = 0;
+static CORE_ADDR _gdb_hwdebug_continue;
+static CORE_ADDR _gdb_hwdebug_request;
+static int sizeof_request;
+static int offset_request_op;
+static CORE_ADDR _gdb_hwdebug_reply;
+static int sizeof_reply;
+static int offset_reply_result;
+static int offset_reply_errcode;
+
+/* Details of the supported commands and arguments. The array must be kept
+ in the same order as the target-side command numbering. The argument names
+ correspond to the target_side _gdb_hwdebug_request.data fields. */
+static struct
+{
+ const char *name;
+ struct
+ {
+ const char *field;
+ int (*unpack_fn) (const gdb_byte *, int);
+ int offset;
+ } args[5];
+} known_requests[] =
+{
+ {
+ "open",
+ {
+ { "d_open.path", &hwdebug_unpack_charptr, 0},
+ { "d_open.pathlen", &hwdebug_unpack_int32, 0},
+ { "d_open.flags", &hwdebug_unpack_int32, 0},
+ { "d_open.mode", &hwdebug_unpack_int32, 0},
+ { NULL}
+ },
+ },
+ {
+ "close",
+ {
+ { "d_close.fd", &hwdebug_unpack_int32, 0},
+ { NULL}
+ },
+ },
+ {
+ "read",
+ {
+ { "d_read.fd", &hwdebug_unpack_int32, 0},
+ { "d_read.buf", &hwdebug_unpack_voidptr, 0},
+ { "d_read.count", &hwdebug_unpack_int32, 0},
+ { NULL}
+ },
+ },
+ {
+ "write",
+ {
+ { "d_write.fd", &hwdebug_unpack_int32, 0},
+ { "d_write.buf", &hwdebug_unpack_voidptr, 0},
+ { "d_write.count", &hwdebug_unpack_int32, 0},
+ { NULL}
+ },
+ },
+ {
+ "lseek",
+ {
+ { "d_lseek.fd", &hwdebug_unpack_int32, 0},
+ { "d_lseek.offset", &hwdebug_unpack_int64, 0},
+ { "d_lseek.flag", &hwdebug_unpack_int32, 0},
+ { NULL}
+ },
+ },
+ {
+ "rename",
+ {
+ { "d_rename.oldpath", &hwdebug_unpack_charptr, 0},
+ { "d_rename.oldpathlen", &hwdebug_unpack_int32, 0},
+ { "d_rename.newpath", &hwdebug_unpack_charptr, 0},
+ { "d_rename.newpathlen", &hwdebug_unpack_int32, 0},
+ { NULL}
+ },
+ },
+ {
+ "unlink",
+ {
+ { "d_unlink.path", &hwdebug_unpack_charptr, 0},
+ { "d_unlink.pathlen", &hwdebug_unpack_int32, 0},
+ { NULL}
+ },
+ },
+ {
+ "stat",
+ {
+ { "d_stat.path", &hwdebug_unpack_charptr, 0},
+ { "d_stat.pathlen", &hwdebug_unpack_int32, 0},
+ { "d_stat.buf", &hwdebug_unpack_voidptr, 0},
+ { NULL}
+ },
+ },
+ {
+ "fstat",
+ {
+ { "d_fstat.fd", &hwdebug_unpack_int32, 0},
+ { "d_fstat.buf", &hwdebug_unpack_voidptr, 0},
+ { NULL}
+ },
+ },
+ {
+ "gettimeofday",
+ {
+ { "d_gettimeofday.tv", &hwdebug_unpack_voidptr, 0},
+ { "d_gettimeofday.tz", &hwdebug_unpack_voidptr, 0},
+ { NULL}
+ },
+ },
+ {
+ "isatty",
+ {
+ { "d_isatty.fd", &hwdebug_unpack_int32, 0},
+ { NULL}
+ },
+ },
+ {
+ "system",
+ {
+ { "d_system.command", &hwdebug_unpack_charptr, 0},
+ { "d_system.commandlen", &hwdebug_unpack_int32, 0},
+ { NULL}
+ },
+ }
+};
+
+#define NUMBER_REQUESTS (sizeof(known_requests) / sizeof(known_requests[0]))
+
+
+// ----------------------------------------------------------------------------
+/* These unpack routines interpret a field in the local copy of the
+ _gdb_hwdebug_request structure and update the current remote protocol
+ request string at offset IDX. */
+
+static int
+hwdebug_unpack_int32 (const gdb_byte * src, int idx)
+{
+ ULONGEST val = extract_unsigned_integer (src, 4);
+ char *str = phex_nz (val, 4);
+ return xsnprintf (request_str + idx, REQUEST_MAX - idx, "%s,", str);
+}
+
+static int
+hwdebug_unpack_int64 (const gdb_byte * src, int idx)
+{
+ ULONGEST val = extract_unsigned_integer (src, 8);
+ char *str = phex_nz (val, 8);
+ return xsnprintf (request_str + idx, REQUEST_MAX - idx, "%s,", str);
+}
+
+static int
+hwdebug_unpack_charptr (const gdb_byte * src, int idx)
+{
+ CORE_ADDR addr = extract_typed_address (src, builtin_type_void_data_ptr);
+ char *str = paddr_nz (addr);
+ return xsnprintf (request_str + idx, REQUEST_MAX - idx, "%s/", str);
+}
+
+static int
+hwdebug_unpack_voidptr (const gdb_byte * src, int idx)
+{
+ CORE_ADDR addr = extract_typed_address (src, builtin_type_void_data_ptr);
+ char *str = paddr_nz (addr);
+ return xsnprintf (request_str + idx, REQUEST_MAX - idx, "%s,", str);
+}
+
+/* There is a file I/O request encoded in struct _gdb_hwdebug_request
+ on the target. To avoid unnecessary traffic the whole structure is
+ transferred in one go. */
+
+static int
+hwdebug_handle_request (void)
+{
+ LONGEST tmp;
+ int op, i, idx;
+ int (*unpack_fn) (const gdb_byte *, int);
+
+ target_read_memory (_gdb_hwdebug_request, request_copy, sizeof_request);
+ tmp = extract_unsigned_integer (request_copy + offset_request_op, 4);
+ op = longest_to_int (tmp);
+
+ if ((op < 0) || (op >= NUMBER_REQUESTS))
+ {
+ warning (_("target request for unknown file I/O operation %d"), op);
+ return 0;
+ }
+ idx = xsnprintf (request_str, REQUEST_MAX, "F%s,", known_requests[op].name);
+ for (i = 0; known_requests[op].args[i].field; i++)
+ {
+ unpack_fn = known_requests[op].args[i].unpack_fn;
+ idx += (*unpack_fn) (request_copy + known_requests[op].args[i].offset,
+ idx);
+ }
+ // The last unpack will have left a trailing ','
+ request_str[--idx] = '\0';
+
+ // Now call into remote-fileio.c to perform the I/O operation.
+ remote_fileio_request (request_str, &hwdebug_putpkt);
+
+ // And make the calling code return control to the target.
+ return 1;
+}
+
+/* Send a response back to the target. The reply packet has the form
+ F<code>,<errno>,<ctrl-C>, with only code required. */
+
+static int
+hwdebug_putpkt (char *buf_arg)
+{
+ const char *buf = (const char *) buf_arg;
+ LONGEST result = 0;
+ LONGEST errcode = 0;
+
+ result = strtoulst (buf + 1, &buf, 16);
+ if (*buf == ',')
+ {
+ errcode = strtoulst (buf + 1, &buf, 16);
+ if ((buf[0] == ',') && (buf[1] == 'C'))
+ ctrlc_pending = 1;
+ }
+ store_signed_integer (reply_copy + offset_reply_result, 8, result);
+ store_signed_integer (reply_copy + offset_reply_errcode, 4, errcode);
+ target_write_memory (_gdb_hwdebug_reply, reply_copy, sizeof_reply);
+ return 1;
+}
+
+/* Update all the symbols etc. needed to allow subsequent file I/O requests
+ to be handled as efficiently as possible. */
+
+static int
+hwdebug_update_target_data (void)
+{
+ volatile struct gdb_exception e;
+ struct minimal_symbol *minsym;
+ int i, j;
+
+ /* Assume failure, the flag is set only when all the way through extracting
+ the required fields. */
+ target_has_hwdebug_support = 0;
+
+ minsym = lookup_minimal_symbol ("_gdb_hwdebug_disabled", NULL, NULL);
+ if (!minsym)
+ {
+ warning (_("missing target-side symbol _gdb_hwdebug_disabled"));
+ return 0;
+ }
+ _gdb_hwdebug_disabled = SYMBOL_VALUE_ADDRESS (minsym);
+
+ minsym = lookup_minimal_symbol ("_gdb_hwdebug_breakpoint", NULL, NULL);
+ if (!minsym)
+ {
+ warning (_("missing target-side symbol _gdb_hwdebug_breakpoint"));
+ return 0;
+ }
+ _gdb_hwdebug_breakpoint = SYMBOL_VALUE_ADDRESS (minsym);
+
+ // _gdb_hwdebug_continue is optional.
+ has_gdb_hwdebug_continue = 0;
+ minsym = lookup_minimal_symbol ("_gdb_hwdebug_continue", NULL, NULL);
+ if (minsym)
+ {
+ _gdb_hwdebug_continue = SYMBOL_VALUE_ADDRESS (minsym);
+ has_gdb_hwdebug_continue = 1;
+ }
+
+ minsym = lookup_minimal_symbol ("_gdb_hwdebug_request", NULL, NULL);
+ if (!minsym)
+ {
+ warning (_("missing target-side symbol _gdb_hwdebug_request"));
+ return 0;
+ }
+ _gdb_hwdebug_request = SYMBOL_VALUE_ADDRESS (minsym);
+
+ minsym = lookup_minimal_symbol ("_gdb_hwdebug_reply", NULL, NULL);
+ if (!minsym)
+ {
+ warning (_("missing target-side symbol _gdb_hwdebug_reply"));
+ return 0;
+ }
+ _gdb_hwdebug_reply = SYMBOL_VALUE_ADDRESS (minsym);
+
+ TRY_CATCH (e, RETURN_MASK_ERROR)
+ {
+ sizeof_request =
+ longest_to_int (parse_and_eval_long ("sizeof(_gdb_hwdebug_request)"));
+ offset_request_op =
+ longest_to_int (parse_and_eval_long
+ ("(char*)&_gdb_hwdebug_request.op - (char*)&_gdb_hwdebug_request"));
+ sizeof_reply =
+ longest_to_int (parse_and_eval_long ("sizeof(_gdb_hwdebug_reply)"));
+ offset_reply_result =
+ longest_to_int (parse_and_eval_long
+ ("(char*)&_gdb_hwdebug_reply.result - (char*)&_gdb_hwdebug_reply"));
+ offset_reply_errcode =
+ longest_to_int (parse_and_eval_long
+ ("(char*)&_gdb_hwdebug_reply.errcode - (char*)&_gdb_hwdebug_reply"));
+
+ // Make sure we have enough space for copies of the target-side structures.
+ if (request_copy)
+ xfree (request_copy);
+ request_copy = XCALLOC (sizeof_request, gdb_byte);
+ if (reply_copy)
+ xfree (reply_copy);
+ reply_copy = XCALLOC (sizeof_reply, gdb_byte);
+
+ // Now to calculate all the argument offsets.
+ for (i = 0; i < NUMBER_REQUESTS; i++)
+ {
+ for (j = 0; known_requests[i].args[j].field; j++)
+ {
+ LONGEST offset;
+ char expr[128];
+ xsnprintf (expr, 128,
+ "(char*)&_gdb_hwdebug_request.data.%s - (char*)&_gdb_hwdebug_request",
+ known_requests[i].args[j].field);
+ offset = parse_and_eval_long (expr);
+ known_requests[i].args[j].offset = longest_to_int (offset);
+ }
+ }
+ }
+ if (RETURN_ERROR == e.reason)
+ {
+ warning (_("target-side h/w data structure mismatch, %s"), e.message);
+ return 0;
+ }
+
+ // All the required info is present and has been extracted.
+ target_has_hwdebug_support = 1;
+ return 1;
+}
+
+/* Target Operations.
+
+ When suitably compiled the target-side includes a h/w breakpoint
+ instruction @ _gdb_hwdebug_breakpoint. This gets executed whenever
+ the target-side performs a file I/O operation, for example when a
+ buffer full of diagnostic output has been collected. The breakpoint
+ only gets executed when the global flag _gdb_hwdebug_disabled is clear,
+ which happens here in the resume code. This allows the application
+ to run either stand-alone or under gdb control.
+
+ Three target vector operations need to be intercepted for this to
+ work. The resume operation clears the target-side _gdb_hwdebug_disabled
+ flag if necessary, then chains to the original resume. The wait
+ operation repeatedly chains to the original wait. If it detects
+ the target-side code has halted @ _gdb_hwdebug_breakpoint then it
+ takes the appropriate action requested by the target and automatically
+ resumes the target. Otherwise it passes the wait result back to
+ higher-level code. The load operation is intercepted to cope with
+ the same executable being loaded multiple times in one debug session,
+ overwriting the _gdb_hwdebug_disabled flag. */
+
+static void
+hwdebug_push_target_vec (void)
+{
+ static int pushed = 0;
+ if (pushed)
+ return;
+ pushed = 1;
+
+ memset (&hwdebug_ops, 0, sizeof (struct target_ops));
+ hwdebug_ops.to_shortname = "hwdebug I/O";
+ hwdebug_ops.to_longname = "hwdebug I/O extensions";
+ hwdebug_ops.to_doc =
+ "Enable hardware debug I/O extensions on an existing target \n\
+connection.";
+ hwdebug_ops.to_resume = &hwdebug_resume;
+ hwdebug_ops.to_wait = &hwdebug_wait;
+ hwdebug_ops.to_load = &hwdebug_load;
+ // This operation does not get inherited automatically so we
+ // need a chaining dummy.
+ hwdebug_ops.to_xfer_partial = &hwdebug_xfer_partial;
+ hwdebug_ops.to_stratum = process_override_stratum;
+ hwdebug_ops.to_magic = OPS_MAGIC;
+ push_target (&hwdebug_ops);
+
+}
+
+static int hwdebug_last_step;
+static int hwdebug_last_sig;
+
+static void
+hwdebug_resume (ptid_t ptid, int step, enum target_signal sig)
+{
+ struct target_ops *orig_ops;
+
+ if (update_needed__gdb_hwdebug_disabled && target_has_hwdebug_support)
+ {
+ gdb_byte newval[4];
+ store_signed_integer (newval, 4, hwdebug_enabled ? 0 : 1);
+ target_write_memory (_gdb_hwdebug_disabled, newval, 4);
+ update_needed__gdb_hwdebug_disabled = 0;
+ }
+
+ /* Remember the args so that wait() below can automatically resume
+ the target with the same options. That may or may not be the
+ right thing to do for signals, but those are not really supported
+ anyway. */
+ hwdebug_last_step = step;
+ hwdebug_last_sig = sig;
+ // Chain to the original resume operation.
+ orig_ops = find_target_beneath (&hwdebug_ops);
+ (orig_ops->to_resume) (ptid, step, sig);
+}
+
+static ptid_t
+hwdebug_wait (ptid_t ptid, struct target_waitstatus *status)
+{
+ ptid_t result;
+ struct target_ops *orig_ops = find_target_beneath (&hwdebug_ops);
+ CORE_ADDR pc;
+
+ while (1)
+ {
+ ctrlc_pending = 0;
+ result = (*orig_ops->to_wait) (ptid, status);
+ if (!hwdebug_enabled || !target_has_hwdebug_support)
+ break;
+
+ /* If the target has stopped @ _gdb_hwdebug_breakpoint then it is
+ making a file I/O request. For anything else just return
+ control to the user. */
+ pc = read_pc ();
+ if (pc != _gdb_hwdebug_breakpoint)
+ break;
+
+ /* Issuing the hardware breakpoint may have had side effects like
+ pushing some state on to the stack. Also on some architectures
+ the current PC may still be set to the breakpoint instruction
+ instead of the next one. If the target provides a label
+ _gdb_hwdebug_continue then that code will take whatever clean-up
+ action is needed. */
+ if (has_gdb_hwdebug_continue)
+ write_pc (_gdb_hwdebug_continue);
+
+ /* For a faulty request the called code should have issued a warning,
+ and it is safer to transfer control to the user rather than let
+ the target continue in an indeterminate state. */
+ if (!hwdebug_handle_request ())
+ break;
+
+ /* If remote-fileio.c has requested a ctrl-C via the reply putpkt(),
+ the target is still halted and should be in a state where it
+ can resume safely with the appropriate error code etc. So just
+ transfer control to the user here, no action is needed on the
+ target-side. */
+ if (ctrlc_pending)
+ break;
+
+ // As per wait_for_inferior ()
+ overlay_cache_invalid = 1;
+ registers_changed ();
+
+ // Automatically resume the target and wait again.
+ hwdebug_resume (ptid, hwdebug_last_step, hwdebug_last_sig);
+ }
+
+ /* The target has halted for reasons other than the h/w debug support code,
+ e.g. an ordinary breakpoint. Return to higher-level gdb code. */
+ return result;
+}
+
+static void
+hwdebug_load (char *name, int from_tty)
+{
+ /* Loading will have invalidated the target-side _gdb_hwdebug_disabled
+ flag, so reset it during the next resume. */
+ struct target_ops *orig_ops = find_target_beneath (&hwdebug_ops);
+ update_needed__gdb_hwdebug_disabled = 1;
+ (orig_ops->to_load) (name, from_tty);
+}
+
+// xfer_partial is always needed in a target_ops but can just chain.
+
+static LONGEST
+hwdebug_xfer_partial (struct target_ops *ops, enum target_object object,
+ const char *annex, gdb_byte * readbuf,
+ const gdb_byte * writebuf, ULONGEST offset, LONGEST len)
+{
+ gdb_assert (ops->beneath->to_xfer_partial);
+ return ops->beneath->to_xfer_partial (ops->beneath, object, annex,
+ readbuf, writebuf, offset, len);
+}
+
+static void
+hwdebug_set (char *args, int from_tty, struct cmd_list_element *c)
+{
+ if (hwdebug_enabled)
+ {
+ /* We may or may not already have symbol table info etc.,
+ which may or may not correspond to the current executable.
+ Make sure that the info is accurate. */
+ if (!hwdebug_update_target_data ())
+ {
+ warning (_("cannot enable h/w debug, missing target-side support"));
+ hwdebug_enabled = 0;
+ }
+ else
+ {
+ hwdebug_push_target_vec ();
+ /* During the next continue, update the target-side
+ _gdb_hwdebug_disabled flag to activate the h/w debug
+ functionality. */
+ update_needed__gdb_hwdebug_disabled = 1;
+ }
+ }
+ else
+ {
+ /* If hwdebug is disabled then we don't care about the accuracy
+ of symbol table info. The target vector entries must be kept
+ in place so that target-side _gdb_hwdebug_disabled gets
+ set again during the next resume. */
+ update_needed__gdb_hwdebug_disabled = 1;
+ }
+}
+
+void
+_initialize_hwdebug_fileio (void)
+{
+ add_setshow_boolean_cmd ("hwdebug", class_obscure, &hwdebug_enabled, _("\
+Set use of h/w debug file I/O support."), _("\
+Show use of h/w debug file I/O support."), _("\
+When set, if the target halts at a well-defined location GDB\n\
+will treat that as a request for file I/O. The request will be\n\
+handled and the target will be resumed automatically."), &hwdebug_set, NULL, &setlist, &showlist);
+}
diff -ruN --exclude=CVS gdb/Makefile.in gdb-hwdebug/Makefile.in
--- gdb/Makefile.in 2008-08-12 16:19:32.000000000 +0100
+++ gdb-hwdebug/Makefile.in 2008-08-11 21:10:17.000000000 +0100
@@ -445,7 +445,8 @@
# The `remote' debugging target is supported for most architectures,
# but not all (e.g. 960)
-REMOTE_OBS = remote.o dcache.o tracepoint.o ax-general.o ax-gdb.o remote-fileio.o
+REMOTE_OBS = remote.o dcache.o tracepoint.o ax-general.o ax-gdb.o \
+ remote-fileio.o hwdebug-fileio.o
# This is remote-sim.o if a simulator is to be linked in.
SIM_OBS = @SIM_OBS@
@@ -655,7 +656,8 @@
user-regs.c \
valarith.c valops.c valprint.c value.c varobj.c vec.c \
wrapper.c \
- xml-tdesc.c xml-support.c
+ xml-tdesc.c xml-support.c \
+ hwdebug-fileio.c
LINTFILES = $(SFILES) $(YYFILES) $(CONFIG_SRCS) init.c
diff -ruN --exclude=CVS gdb/doc/gdb.texinfo gdb-hwdebug/doc/gdb.texinfo
--- gdb/doc/gdb.texinfo 2008-08-12 16:21:11.000000000 +0100
+++ gdb-hwdebug/doc/gdb.texinfo 2008-08-12 16:22:47.000000000 +0100
@@ -13318,6 +13318,7 @@
* Server:: Using the gdbserver program
* Remote Configuration:: Remote configuration
* Remote Stub:: Implementing a remote stub
+* Hardware Debug Support:: Extra support for hardware debuggers
@end menu
@node Connecting
@@ -14203,6 +14204,7 @@
subroutines which @code{@value{NGCC}} generates as inline code.
+
@node Debug Session
@subsection Putting it All Together
@@ -14262,6 +14264,41 @@
@end enumerate
+@node Hardware Debug Support
+@section Extra support for hardware debuggers
+
+@cindex hwdebug
+When @value{GDBN} interacts with a server or stub running on the
+remote target the target-side application can perform a number of
+host-side file I/O operations, @xref{File-I/O Remote Protocol
+Extension}. Remote debugging can also involve a separate server that
+controls the target via a hardware debug mechanism, for example jtag
+or BDM. If so then target-side code may be unable to generate the
+remote protocol messages directly. Instead it can send the request by
+triggering a breakpoint or processor exception at a well-known
+location @code{_gdb_hwdebug_break}. This functionality is disabled by
+default, allowing the application to run stand-alone as well as inside
+a debug session. It can be enabled by a @kbd{set hwdebug} command, or
+disabled by @kbd{set hwdebug off}.
+
+Exact usage depends on the application being debugged. Amongst other
+things @kbd{set hwdebug} clears a flag @code{_gdb_hwdebug_disabled} on
+the target. If the application is loaded into RAM then there are no
+problems. However if it is programmed into flash and restarted from
+the reset vector inside the debug session then typically the
+target-side initialization code will reset the disabled flag to its
+default state, undoing the effect of @kbd{set hwdebug}. Instead it
+will be necessary to set a hardware breakpoint at a suitably early
+point in the application startup and invoke @kbd{set hwdebug} when
+that breakpoint is hit.
+
+The target-side API for accessing the h/w debug file I/O functionality
+depends on the embedded OS or run-time being used. If the
+functionality has not yet been ported then a reference implementation
+can be found in @file{doc/hwdebug-example.c} in the @value{GDBN}
+sources.
+
+
@node Configurations
@chapter Configuration-Specific Information
diff -ruN --exclude=CVS gdb/doc/hwdebug-example.c gdb-hwdebug/doc/hwdebug-example.c
--- gdb/doc/hwdebug-example.c 1970-01-01 01:00:00.000000000 +0100
+++ gdb-hwdebug/doc/hwdebug-example.c 2008-08-04 16:31:54.000000000 +0100
@@ -0,0 +1,815 @@
+/* Example target-side code for hwdebug-fileio.c
+
+ Copyright (C) 2008 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 3 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, see <http://www.gnu.org/licenses/>. */
+
+/* ----------------------------------------------------------------------------
+ This section contains everything that is OS or target-specific: data
+ types, locking, and how to invoke a hardware breakpoint. */
+#include <cyg/infra/cyg_type.h>
+#include <cyg/hal/hal_intr.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+typedef cyg_uint32 uint32_t;
+typedef cyg_int32 int32_t;
+typedef cyg_uint64 uint64_t;
+typedef cyg_int64 int64_t;
+
+/* Locking is kept simple - just disable interrupts for the duration
+ of the I/O operation. The target is going to be blocked for a
+ large number of cycles anyway while the I/O operation takes place. */
+#define HAL_GDB_HWDEBUG_LOCK() \
+ int __gdb_hwdebug_lock_ints_state; \
+ HAL_DISABLE_INTERRUPTS(__gdb_hwdebug_lock_ints_state)
+
+#define HAL_GDB_HWDEBUG_UNLOCK() \
+ HAL_RESTORE_INTERRUPTS(__gdb_hwdebug_lock_ints_state)
+
+/* This macro is used to get the attention of the hardware debug unit.
+ * Depending on the architecture and possibly the specific processor
+ * variant this may involve a custom breakpoint instruction, an
+ * illegal instruction trap, or some other kind of processor
+ * exception. A likely candidate is whatever instruction gdb uses for
+ * a software breakpoint. This macro uses inline assembler. On some
+ * architectures a function call may be necessary instead.
+ *
+ * At least one label should be associated with the hardware
+ * breakpoint: _gdb_hwdebug_breakpoint. This label should correspond
+ * to the address that will be reported to gdb. Typically it will be
+ * either the address of the breakpoint instruction or, as on M68K,
+ * that of the next instruction.
+ *
+ * If _gdb_hwdebug_breakpoint corresponds to the breakpoint instruction
+ * itself then the target cannot just be resumed: it would just execute
+ * the breakpoint again, triggering another file I/O operation, ad
+ * infinitum. To avoid this HAL_HWBREAKPOINT() can define a label
+ * _gdb_hwdebug_continue. gdb will detect the presence of that label
+ * and set the program counter to its address before resuming the target.
+ * On some architectures executing the breakpoint instruction may have
+ * side effects like pushing extra state on to the stack. If so then
+ * the code @ _gdb_hwdebug_continue can undo those.
+ *
+ * The M68K breakpoint instruction has no side effects and the address
+ * reported to gdb is that of the next instruction, so there is no need
+ * to provide a _gdb_hwdebug_continue label. It is provided here merely
+ * as an example. */
+
+#if defined(CYGPKG_HAL_M68K_MCF52xx)
+# define HAL_HWBREAKPOINT() \
+ CYG_MACRO_START \
+ __asm__ volatile ( \
+ " halt\n" \
+ ".globl _gdb_hwdebug_breakpoint\n" \
+ ".type _gdb_hwdebug_breakpoint, function\n" \
+ "_gdb_hwdebug_breakpoint:\n" \
+ ".globl _gdb_hwdebug_continue\n" \
+ ".type _gdb_hwdebug_continue, function\n" \
+ "_gdb_hwdebug_continue:\n" \
+ : : : "memory" \
+ ); \
+ CYG_MACRO_END
+
+#else
+# error The H/W debug file I/O support has not yet been ported to this architecture.
+#endif
+
+/* ----------------------------------------------------------------------------
+ This section contains definitions which typically would be exported in
+ a header file. The exact names will depend on the conventions used by
+ the embedded OS or run-time. The definitions can be used for either a
+ h/w debug implementation or for an application running on top of gdb stubs. */
+
+#define HAL_GDB_FILEIO_STDIN 0
+#define HAL_GDB_FILEIO_STDOUT 1
+#define HAL_GDB_FILEIO_STDERR 2
+
+#define HAL_GDB_FILEIO_O_RDONLY 0x0000
+#define HAL_GDB_FILEIO_O_WRONLY 0x0001
+#define HAL_GDB_FILEIO_O_RDWR 0x0002
+#define HAL_GDB_FILEIO_O_APPEND 0x0008
+#define HAL_GDB_FILEIO_O_CREAT 0x0200
+#define HAL_GDB_FILEIO_O_TRUNC 0x0400
+#define HAL_GDB_FILEIO_O_EXCL 0x0800
+
+#define HAL_GDB_FILEIO_S_IFREG 0100000
+#define HAL_GDB_FILEIO_S_IFDIR 040000
+#define HAL_GDB_FILEIO_S_IRUSR 0400
+#define HAL_GDB_FILEIO_S_IWUSR 0200
+#define HAL_GDB_FILEIO_S_IXUSR 0100
+#define HAL_GDB_FILEIO_S_IRGRP 040
+#define HAL_GDB_FILEIO_S_IWGRP 020
+#define HAL_GDB_FILEIO_S_IXGRP 010
+#define HAL_GDB_FILEIO_S_IROTH 04
+#define HAL_GDB_FILEIO_S_IWOTH 02
+#define HAL_GDB_FILEIO_S_IXOTH 01
+
+#define HAL_GDB_FILEIO_ENOERR 0
+#define HAL_GDB_FILEIO_EPERM 1
+#define HAL_GDB_FILEIO_ENOENT 2
+#define HAL_GDB_FILEIO_EINTR 4
+#define HAL_GDB_FILEIO_EBADF 9
+#define HAL_GDB_FILEIO_EACCES 13
+#define HAL_GDB_FILEIO_EFAULT 14
+#define HAL_GDB_FILEIO_EBUSY 16
+#define HAL_GDB_FILEIO_EEXIST 17
+#define HAL_GDB_FILEIO_ENODEV 19
+#define HAL_GDB_FILEIO_ENOTDIR 20
+#define HAL_GDB_FILEIO_EISDIR 21
+#define HAL_GDB_FILEIO_EINVAL 22
+#define HAL_GDB_FILEIO_ENFILE 23
+#define HAL_GDB_FILEIO_EMFILE 24
+#define HAL_GDB_FILEIO_EFBIG 27
+#define HAL_GDB_FILEIO_ENOSPC 28
+#define HAL_GDB_FILEIO_ESPIPE 29
+#define HAL_GDB_FILEIO_EROFS 30
+#define HAL_GDB_FILEIO_ENAMETOOLONG 91
+#define HAL_GDB_FILEIO_EUNKNOWN 9999
+
+#define HAL_GDB_FILEIO_ENOSYS 38
+
+#define HAL_GDB_FILEIO_SEEK_SET 0
+#define HAL_GDB_FILEIO_SEEK_CUR 1
+#define HAL_GDB_FILEIO_SEEK_END 2
+
+struct hal_gdb_fileio_stat
+{
+ uint32_t st_dev;
+ uint32_t st_ino;
+ uint32_t st_mode;
+ uint32_t st_nlink;
+ uint32_t st_uid;
+ uint32_t st_gid;
+ uint32_t st_rdev;
+ uint64_t st_size;
+ uint64_t st_blksize;
+ uint64_t st_blocks;
+ uint32_t st_atime;
+ uint32_t st_mtime;
+ uint32_t st_ctime;
+};
+
+struct hal_gdb_fileio_timeval
+{
+ uint32_t tv_sec;
+ // The protocol makes this a uint64_t, but 32 bits are plenty.
+ uint32_t tv_usec;
+};
+
+/* ----------------------------------------------------------------------------
+ This is an example implementation of the h/w debug file I/O functionality.
+ The matching gdb code expects to find variables _gdb_hwdebug_disabled,
+ _gdb_hwdebug_request and _gdb_hwdebug_reply, with the types given here.
+ That includes the types and names of all the structure fields. gdb also
+ expects to find a label _gdb_hwdebug_break and optionally
+ _gdb_hwdebug_continue, as described earlier.
+
+ As long as these conditions are satisfied the code can be rewritten as
+ appropriate for the embedded OS or run-time, for example to match the
+ conventions for naming, error handling, and so on. */
+
+#define HAL_GDB_FILEIO_OPEN 0
+#define HAL_GDB_FILEIO_CLOSE 1
+#define HAL_GDB_FILEIO_READ 2
+#define HAL_GDB_FILEIO_WRITE 3
+#define HAL_GDB_FILEIO_LSEEK 4
+#define HAL_GDB_FILEIO_RENAME 5
+#define HAL_GDB_FILEIO_UNLINK 6
+#define HAL_GDB_FILEIO_STAT 7
+#define HAL_GDB_FILEIO_FSTAT 8
+#define HAL_GDB_FILEIO_GETTIMEOFDAY 9
+#define HAL_GDB_FILEIO_ISATTY 10
+#define HAL_GDB_FILEIO_SYSTEM 11
+
+static volatile uint32_t _gdb_hwdebug_disabled = 1;
+
+static struct
+{
+ uint32_t op;
+ union
+ {
+ struct
+ {
+ const char *path;
+ uint32_t pathlen;
+ int32_t flags;
+ int32_t mode;
+ } d_open;
+ struct
+ {
+ int32_t fd;
+ } d_close;
+ struct
+ {
+ int32_t fd;
+ void *buf;
+ uint32_t count;
+ } d_read;
+ struct
+ {
+ int32_t fd;
+ const void *buf;
+ uint32_t count;
+ } d_write;
+ struct
+ {
+ int32_t fd;
+ int64_t offset;
+ int32_t flag;
+ } d_lseek;
+ struct
+ {
+ const char *oldpath;
+ uint32_t oldpathlen;
+ const char *newpath;
+ uint32_t newpathlen;
+ } d_rename;
+ struct
+ {
+ const char *path;
+ uint32_t pathlen;
+ } d_unlink;
+ struct
+ {
+ const char *path;
+ uint32_t pathlen;
+ void *buf;
+ } d_stat;
+ struct
+ {
+ int32_t fd;
+ void *buf;
+ } d_fstat;
+ struct
+ {
+ void *tv;
+ void *tz;
+ } d_gettimeofday;
+ struct
+ {
+ int32_t fd;
+ } d_isatty;
+ struct
+ {
+ const char *command;
+ uint32_t commandlen;
+ } d_system;
+ } data;
+} _gdb_hwdebug_request;
+
+/* This definition of a reply allows for a result field of up to 64 bits,
+ e.g. an lseek64() offset, and the error code. This is not really needed
+ with current I/O operations. A single 32-bit field, -ve for errors,
+ 0 or +ve for success, would work for everything except seeks within
+ a file >= 2GB. However it leaves room for future expansion of the
+ protocol. */
+static struct
+{
+ int64_t result;
+ int32_t errcode;
+} _gdb_hwdebug_reply;
+
+
+/* The convention adopted here is that a return code >= 0 indicates
+ success and < 0 indicates failure, with the absolute value of the
+ return code giving the error number. For most operations this will be
+ fine, for example an embedded target is unlikely to attempt to read
+ 2GB of data in one go. However it does limit lseek() operations to
+ files smaller than 2GB. Note that, if desired, some of the calling
+ functions can ignore the return code and instead examine
+ _gdb_hwdebug_reply directly before releasing the lock. */
+
+static int
+_gdb_hwdebug_call (void)
+{
+ int result;
+ if (_gdb_hwdebug_disabled)
+ return -HAL_GDB_FILEIO_ENOSYS;
+ HAL_HWBREAKPOINT ();
+ result = (int) _gdb_hwdebug_reply.result;
+ if (result < 0)
+ result = -_gdb_hwdebug_reply.errcode;
+ return result;
+}
+
+/* The actual I/O operations. */
+
+int
+hal_gdb_fileio_open (const char *path, int flags, int mode)
+{
+ int result;
+
+ HAL_GDB_HWDEBUG_LOCK ();
+ _gdb_hwdebug_request.op = HAL_GDB_FILEIO_OPEN;
+ _gdb_hwdebug_request.data.d_open.path = path;
+ _gdb_hwdebug_request.data.d_open.pathlen = strlen (path) + 1;
+ _gdb_hwdebug_request.data.d_open.flags = flags;
+ _gdb_hwdebug_request.data.d_open.mode = mode;
+ result = _gdb_hwdebug_call ();
+ HAL_GDB_HWDEBUG_UNLOCK ();
+ return result;
+}
+
+int
+hal_gdb_fileio_close (int fd)
+{
+ int result;
+ HAL_GDB_HWDEBUG_LOCK ();
+ _gdb_hwdebug_request.op = HAL_GDB_FILEIO_CLOSE;
+ _gdb_hwdebug_request.data.d_close.fd = fd;
+ result = _gdb_hwdebug_call ();
+ HAL_GDB_HWDEBUG_UNLOCK ();
+ return result;
+}
+
+int
+hal_gdb_fileio_read (int fd, void *buf, int count)
+{
+ int result;
+ HAL_GDB_HWDEBUG_LOCK ();
+ _gdb_hwdebug_request.op = HAL_GDB_FILEIO_READ;
+ _gdb_hwdebug_request.data.d_read.fd = fd;
+ _gdb_hwdebug_request.data.d_read.buf = buf;
+ _gdb_hwdebug_request.data.d_read.count = count;
+ result = _gdb_hwdebug_call ();
+ HAL_GDB_HWDEBUG_UNLOCK ();
+ return result;
+}
+
+int
+hal_gdb_fileio_write (int fd, const void *buf, int count)
+{
+ int result;
+ HAL_GDB_HWDEBUG_LOCK ();
+ _gdb_hwdebug_request.op = HAL_GDB_FILEIO_WRITE;
+ _gdb_hwdebug_request.data.d_write.fd = fd;
+ _gdb_hwdebug_request.data.d_write.buf = buf;
+ _gdb_hwdebug_request.data.d_write.count = count;
+ result = _gdb_hwdebug_call ();
+ HAL_GDB_HWDEBUG_UNLOCK ();
+ return result;
+}
+
+/* The request is defined in terms of a 64-bit offset, but
+ the reply from gdb only contains a uint32_t offset.
+ That means a file size limit of 4GB. The convention used
+ in this code further limits file sizes to 2GB, but that
+ could be changed by e.g. extracting the full 32-bit
+ return code from _gdb_hwdebug_reply before releasing the
+ lock. */
+int32_t
+hal_gdb_fileio_lseek (int fd, int32_t offset, int flag)
+{
+ int32_t result;
+
+ HAL_GDB_HWDEBUG_LOCK ();
+ _gdb_hwdebug_request.op = HAL_GDB_FILEIO_LSEEK;
+ _gdb_hwdebug_request.data.d_lseek.fd = fd;
+ _gdb_hwdebug_request.data.d_lseek.offset = (int64_t) offset;
+ _gdb_hwdebug_request.data.d_lseek.flag = flag;
+ result = _gdb_hwdebug_call ();
+ HAL_GDB_HWDEBUG_UNLOCK ();
+ return result;
+}
+
+int
+hal_gdb_fileio_rename (const char *oldpath, const char *newpath)
+{
+ int result;
+ HAL_GDB_HWDEBUG_LOCK ();
+ _gdb_hwdebug_request.op = HAL_GDB_FILEIO_RENAME;
+ _gdb_hwdebug_request.data.d_rename.oldpath = oldpath;
+ _gdb_hwdebug_request.data.d_rename.oldpathlen = strlen (oldpath) + 1;
+ _gdb_hwdebug_request.data.d_rename.newpath = newpath;
+ _gdb_hwdebug_request.data.d_rename.newpathlen = strlen (newpath) + 1;
+ result = _gdb_hwdebug_call ();
+ HAL_GDB_HWDEBUG_UNLOCK ();
+ return result;
+}
+
+int
+hal_gdb_fileio_unlink (const char *path)
+{
+ int result;
+ HAL_GDB_HWDEBUG_LOCK ();
+ _gdb_hwdebug_request.op = HAL_GDB_FILEIO_UNLINK;
+ _gdb_hwdebug_request.data.d_unlink.path = path;
+ _gdb_hwdebug_request.data.d_unlink.pathlen = strlen (path) + 1;
+ result = _gdb_hwdebug_call ();
+ HAL_GDB_HWDEBUG_UNLOCK ();
+ return result;
+}
+
+/* The host-side packs the stat data into a 64-byte buffer with all
+ integers and long longs in big-endian format. This may not map
+ exactly onto the hal_gdb_fileio_stat structure, e.g. there may
+ be padding between the st_rdev and st_size fields. For now
+ always do an explicit unpack. It should be possible to optimize
+ the code on some big-endian targets. */
+
+static uint32_t
+hal_gdb_fileio_unpack32 (unsigned char *buf)
+{
+ uint32_t result;
+ result = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | (buf[3] << 0);
+ return result;
+}
+
+static uint64_t
+hal_gdb_fileio_unpack64 (unsigned char *buf)
+{
+ uint64_t result;
+ result = ((uint64_t) buf[0] << 56) | ((uint64_t) buf[1] << 48) |
+ ((uint64_t) buf[2] << 40) | ((uint64_t) buf[3] << 32) |
+ ((uint64_t) buf[4] << 24) | ((uint64_t) buf[5] << 16) |
+ ((uint64_t) buf[6] << 8) | ((uint64_t) buf[7] << 0);
+ return result;
+}
+
+static void
+hal_gdb_fileio_unpack_stat (unsigned char *buf,
+ struct hal_gdb_fileio_stat *stat)
+{
+ stat->st_dev = hal_gdb_fileio_unpack32 (buf + 0);
+ stat->st_ino = hal_gdb_fileio_unpack32 (buf + 4);
+ stat->st_mode = hal_gdb_fileio_unpack32 (buf + 8);
+ stat->st_nlink = hal_gdb_fileio_unpack32 (buf + 12);
+ stat->st_uid = hal_gdb_fileio_unpack32 (buf + 16);
+ stat->st_gid = hal_gdb_fileio_unpack32 (buf + 20);
+ stat->st_rdev = hal_gdb_fileio_unpack32 (buf + 24);
+ stat->st_size = hal_gdb_fileio_unpack64 (buf + 28);
+ stat->st_blksize = hal_gdb_fileio_unpack64 (buf + 36);
+ stat->st_blocks = hal_gdb_fileio_unpack64 (buf + 44);
+ stat->st_atime = hal_gdb_fileio_unpack32 (buf + 52);
+ stat->st_mtime = hal_gdb_fileio_unpack32 (buf + 56);
+ stat->st_ctime = hal_gdb_fileio_unpack32 (buf + 60);
+}
+
+int
+hal_gdb_fileio_stat (const char *path, struct hal_gdb_fileio_stat *stat)
+{
+ int result;
+ // There is a choice here between 64 bytes of extra stack space or
+ // 64 bytes of static data protected within the lock.
+ unsigned char buf[64];
+ HAL_GDB_HWDEBUG_LOCK ();
+ _gdb_hwdebug_request.op = HAL_GDB_FILEIO_STAT;
+ _gdb_hwdebug_request.data.d_stat.path = path;
+ _gdb_hwdebug_request.data.d_stat.pathlen = strlen (path) + 1;
+ _gdb_hwdebug_request.data.d_stat.buf = buf;
+ result = _gdb_hwdebug_call ();
+ HAL_GDB_HWDEBUG_UNLOCK ();
+ if (HAL_GDB_FILEIO_ENOERR == result)
+ {
+ hal_gdb_fileio_unpack_stat (buf, stat);
+ }
+ return result;
+}
+
+int
+hal_gdb_fileio_fstat (int fd, struct hal_gdb_fileio_stat * stat)
+{
+ int result;
+ unsigned char buf[64];
+ HAL_GDB_HWDEBUG_LOCK ();
+ _gdb_hwdebug_request.op = HAL_GDB_FILEIO_FSTAT;
+ _gdb_hwdebug_request.data.d_fstat.fd = fd;
+ _gdb_hwdebug_request.data.d_fstat.buf = buf;
+ result = _gdb_hwdebug_call ();
+ HAL_GDB_HWDEBUG_UNLOCK ();
+ if (HAL_GDB_FILEIO_ENOERR == result)
+ {
+ hal_gdb_fileio_unpack_stat (buf, stat);
+ }
+ return result;
+}
+
+int
+hal_gdb_fileio_gettimeofday (struct hal_gdb_fileio_timeval * tv, void *tz)
+{
+ unsigned char buf[12];
+ int result;
+ HAL_GDB_HWDEBUG_LOCK ();
+ _gdb_hwdebug_request.op = HAL_GDB_FILEIO_GETTIMEOFDAY;
+ _gdb_hwdebug_request.data.d_gettimeofday.tv = buf;
+ _gdb_hwdebug_request.data.d_gettimeofday.tz = tz;
+ result = _gdb_hwdebug_call ();
+ HAL_GDB_HWDEBUG_UNLOCK ();
+ if (HAL_GDB_FILEIO_ENOERR == result)
+ {
+ tv->tv_sec = hal_gdb_fileio_unpack32 (buf);
+ tv->tv_usec = hal_gdb_fileio_unpack64 (&(buf[4]));
+ }
+ return result;
+}
+
+int
+hal_gdb_fileio_isatty (int fd)
+{
+ int result;
+ HAL_GDB_HWDEBUG_LOCK ();
+ _gdb_hwdebug_request.op = HAL_GDB_FILEIO_ISATTY;
+ _gdb_hwdebug_request.data.d_isatty.fd = fd;
+ result = _gdb_hwdebug_call ();
+ HAL_GDB_HWDEBUG_UNLOCK ();
+ return result;
+}
+
+int
+hal_gdb_fileio_system (const char *command)
+{
+ int result;
+ HAL_GDB_HWDEBUG_LOCK ();
+ _gdb_hwdebug_request.op = HAL_GDB_FILEIO_SYSTEM;
+ _gdb_hwdebug_request.data.d_system.command = command;
+ _gdb_hwdebug_request.data.d_system.commandlen = strlen (command) + 1;
+ result = _gdb_hwdebug_call ();
+ HAL_GDB_HWDEBUG_UNLOCK ();
+ return result;
+}
+
+/* ----------------------------------------------------------------------------
+ Basic test code. */
+
+#define TEST1 "/tmp/gdb_fio.tst"
+#define TEST2 "/tmp/gdb_fio2.tst"
+#define BUFSIZE 512
+static unsigned char testbuf1[BUFSIZE];
+static unsigned char testbuf2[BUFSIZE];
+
+int
+main (int argc, char **argv)
+{
+ struct hal_gdb_fileio_timeval tv;
+ struct hal_gdb_fileio_stat stat;
+ char *msg = "console message: Hello world.\n";
+ int result;
+ int i;
+ int fd;
+
+ printf ("Testing GDB file I/O support.\n");
+ result = hal_gdb_fileio_write (HAL_GDB_FILEIO_STDOUT, msg, strlen (msg));
+ if (result != strlen (msg))
+ {
+ fprintf (stderr, "FAIL: write to stdout unsuccessful.\n");
+ exit (EXIT_FAILURE);
+ }
+
+ result = hal_gdb_fileio_gettimeofday (&tv, NULL);
+ if (-HAL_GDB_FILEIO_ENOSYS == result)
+ {
+ fprintf (stderr, "FAIL: file I/O support is disabled.\n");
+ exit (EXIT_FAILURE);
+ }
+ // Show that time is monotonically increasing.
+ for (i = 0; i < 10; i++)
+ {
+ result = hal_gdb_fileio_gettimeofday (&tv, NULL);
+ if (-HAL_GDB_FILEIO_ENOERR != result)
+ {
+ fprintf (stderr,
+ "FAIL: unexpected result %d from gettimeofday().\n",
+ result);
+ exit (EXIT_FAILURE);
+ }
+ printf ("Read time: tv_sec %d, tv_usec %d\n", tv.tv_sec, tv.tv_usec);
+ }
+
+ // Look for a file that probably exists.
+ result = hal_gdb_fileio_stat ("/etc/motd", &stat);
+ if (-HAL_GDB_FILEIO_ENOENT == result)
+ {
+ printf ("WARNING: tried to stat \"/etc/motd\": no such file.\n");
+ }
+ else if (HAL_GDB_FILEIO_ENOERR != result)
+ {
+ fprintf (stderr,
+ "FAIL: unexpected result %d calling stat(\"/etc/motd\", );\n",
+ result);
+ exit (EXIT_FAILURE);
+ }
+ else
+ {
+ printf ("stat on /etc/motd\n");
+ printf (" st_dev %#x, st_ino %#x, st_mode %#x, st_nlink %#x\n",
+ (int) stat.st_dev, (int) stat.st_ino,
+ (int) stat.st_mode, (int) stat.st_nlink);
+ printf (" st_uid %#x, st_gid %#x, st_rdev %#x\n",
+ (int) stat.st_uid, (int) stat.st_gid, (int) stat.st_rdev);
+ printf (" st_size %lld, st_blksize %lld, st_blocks %lld\n",
+ (long long) stat.st_size, (long long) stat.st_blksize,
+ (long long) stat.st_blocks);
+ printf (" st_atime %#x, st_mtime %#x, st_ctime %#x\n",
+ (int) stat.st_atime, (int) stat.st_mtime, (int) stat.st_ctime);
+
+ fd = hal_gdb_fileio_open ("/etc/motd", -HAL_GDB_FILEIO_O_RDONLY, 0);
+ if (fd < 0)
+ {
+ printf ("WARNING: unable to open(\"/etc/motd\", );\n");
+ }
+ else
+ {
+ result = hal_gdb_fileio_fstat (fd, &stat);
+ if (result != HAL_GDB_FILEIO_ENOERR)
+ {
+ fprintf (stderr,
+ "FAIL: unexpected result %d calling fstat() for \"/etc/motd\".\n",
+ result);
+ exit (EXIT_FAILURE);
+ }
+ else
+ {
+ printf ("fstat on /etc/motd\n");
+ printf (" st_dev %#x, st_ino %#x, st_mode %#x, st_nlink %#x\n",
+ (int) stat.st_dev, (int) stat.st_ino,
+ (int) stat.st_mode, (int) stat.st_nlink);
+ printf (" st_uid %#x, st_gid %#x, st_rdev %#x\n",
+ (int) stat.st_uid, (int) stat.st_gid,
+ (int) stat.st_rdev);
+ printf (" st_size %lld, st_blksize %lld, st_blocks %lld\n",
+ (long long) stat.st_size, (long long) stat.st_blksize,
+ (long long) stat.st_blocks);
+ printf (" st_atime %#x, st_mtime %#x, st_ctime %#x\n",
+ (int) stat.st_atime, (int) stat.st_mtime,
+ (int) stat.st_ctime);
+ }
+ result = hal_gdb_fileio_close (fd);
+ if (result != HAL_GDB_FILEIO_ENOERR)
+ {
+ fprintf (stderr,
+ "FAIL: unexpected result %d closing \"/etc/motd\".\n",
+ result);
+ exit (EXIT_FAILURE);
+ }
+ }
+ }
+
+ result = hal_gdb_fileio_stat (TEST1, &stat);
+ if (result != -HAL_GDB_FILEIO_ENOENT)
+ {
+ fprintf (stderr, "Cannot proceed: file " TEST1 " already exists.\n");
+ exit (EXIT_FAILURE);
+ }
+ result = hal_gdb_fileio_stat (TEST2, &stat);
+ if (result != -HAL_GDB_FILEIO_ENOENT)
+ {
+ fprintf (stderr, "Cannot proceed: file " TEST2 " already exists.\n");
+ exit (EXIT_FAILURE);
+ }
+ fd = hal_gdb_fileio_open (TEST1,
+ HAL_GDB_FILEIO_O_WRONLY | HAL_GDB_FILEIO_O_CREAT,
+ HAL_GDB_FILEIO_S_IRUSR | HAL_GDB_FILEIO_S_IWUSR);
+ if (fd < 0)
+ {
+ fprintf (stderr,
+ "FAIL: unexpected error %d creating new file " TEST1 ".\n",
+ fd);
+ exit (EXIT_FAILURE);
+ }
+ result = hal_gdb_fileio_isatty (fd);
+ if (result < 0)
+ {
+ fprintf (stderr,
+ "FAIL: unexpected error %d calling isatty() on " TEST1 ".\n",
+ result);
+ }
+ else if (result)
+ {
+ fprintf (stderr,
+ "FAIL: isatty() failed, claims file " TEST1 " is a tty.\n");
+ }
+ result = hal_gdb_fileio_isatty (1);
+ if (result < 0)
+ {
+ fprintf (stderr,
+ "FAIL: unexpected error %d calling isatty() on stdout.\n",
+ result);
+ }
+ else if (!result)
+ {
+ fprintf (stderr,
+ "FAIL: isatty() failed, claims stdout is not a tty.\n");
+ }
+
+ for (i = 0; i < BUFSIZE; i++)
+ {
+ testbuf1[i] = (unsigned char) (i & 0x00FF);
+ }
+ result = hal_gdb_fileio_write (fd, testbuf1, BUFSIZE);
+ if (BUFSIZE != result)
+ {
+ fprintf (stderr,
+ "FAIL: tried to write %d bytes to " TEST1 ", result %d.\n",
+ BUFSIZE, result);
+ exit (EXIT_FAILURE);
+ }
+ result = hal_gdb_fileio_close (fd);
+ if (HAL_GDB_FILEIO_ENOERR != result)
+ {
+ fprintf (stderr, "FAIL: unexpected error %d calling close().\n",
+ result);
+ exit (EXIT_FAILURE);
+ }
+ result = hal_gdb_fileio_stat (TEST1, &stat);
+ if (result < 0)
+ {
+ fprintf (stderr, "FAIL: stat(\"" TEST1 "\") failed with %d.\n", result);
+ exit (EXIT_FAILURE);
+ }
+ if (BUFSIZE != stat.st_size)
+ {
+ fprintf (stderr, "FAIL: " TEST1 " should be %d bytes, not %lld.\n",
+ BUFSIZE, stat.st_size);
+ exit (EXIT_FAILURE);
+ }
+
+ fd = hal_gdb_fileio_open (TEST1, HAL_GDB_FILEIO_O_RDONLY, 0);
+ if (fd < 0)
+ {
+ fprintf (stderr,
+ "FAIL: failed to open " TEST1 " read-only, result %d.\n", fd);
+ exit (EXIT_FAILURE);
+ }
+ result = hal_gdb_fileio_read (fd, testbuf2, BUFSIZE);
+ if (BUFSIZE != result)
+ {
+ fprintf (stderr,
+ "FAIL: tried to read %d bytes from " TEST1 ", result %d.\n",
+ BUFSIZE, result);
+ exit (EXIT_FAILURE);
+ }
+ for (i = 0; i < BUFSIZE; i++)
+ {
+ if (testbuf1[i] != testbuf2[i])
+ {
+ fprintf (stderr,
+ "FAIL: discrepancy @ offset %d in " TEST1
+ ", expected %#x, got %#x.\n", i, testbuf1[i], testbuf2[i]);
+ exit (EXIT_FAILURE);
+ }
+ }
+ result = hal_gdb_fileio_lseek (fd, BUFSIZE / 2, HAL_GDB_FILEIO_SEEK_SET);
+ if (result < 0)
+ {
+ fprintf (stderr, "FAIL: unexpected result %d from lseek.\n", result);
+ exit (EXIT_FAILURE);
+ }
+ else if (result != (BUFSIZE / 2))
+ {
+ fprintf (stderr,
+ "FAIL: unexpected position %d from lseek, should be %d.\n",
+ result, BUFSIZE / 2);
+ exit (EXIT_FAILURE);
+ }
+ hal_gdb_fileio_close (fd);
+
+ result = hal_gdb_fileio_rename (TEST1, TEST2);
+ if (result < 0)
+ {
+ fprintf (stderr,
+ "FAIL: rename from " TEST1 " to " TEST2 " failed with %d.\n",
+ result);
+ exit (EXIT_FAILURE);
+ }
+ result = hal_gdb_fileio_unlink (TEST2);
+ if (result < 0)
+ {
+ fprintf (stderr, "FAIL: unlink for " TEST2 " failed with %d.\n",
+ result);
+ exit (EXIT_FAILURE);
+ }
+ printf ("I/O operations successful.\n");
+
+ // This may or may not succeed, depending on the system-call-allowed flag
+ result = hal_gdb_fileio_system ("cat /etc/motd");
+ if (result == -HAL_GDB_FILEIO_EPERM)
+ {
+ printf
+ ("system() call failed, gdb's remote system-call-allowed flag is not set.\n");
+ }
+ else if (result < 0)
+ {
+ fprintf (stderr, "system() call failed with unexpected result %d.\n",
+ result);
+ }
+ else
+ {
+ printf ("system() call succeeded.\n");
+ }
+
+ return EXIT_SUCCESS;
+}