* add file I/O support when debugging an embedded target via jtag
@ 2008-08-12 16:34 Bart Veer
2008-08-12 19:08 ` Eli Zaretskii
0 siblings, 1 reply; 16+ messages in thread
From: Bart Veer @ 2008-08-12 16:34 UTC (permalink / raw)
To: gdb-patches
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;
+}
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: add file I/O support when debugging an embedded target via jtag
2008-08-12 16:34 add file I/O support when debugging an embedded target via jtag Bart Veer
@ 2008-08-12 19:08 ` Eli Zaretskii
2008-08-31 11:42 ` Bart Veer
0 siblings, 1 reply; 16+ messages in thread
From: Eli Zaretskii @ 2008-08-12 19:08 UTC (permalink / raw)
To: Bart Veer; +Cc: gdb-patches
> Date: Tue, 12 Aug 2008 17:33:20 +0100
> From: Bart Veer <bartv@ecoscentric.com>
>
> +When set, if the target halts at a well-defined location GDB\n\
^
Need a comma here.
> +will treat that as a request for file I/O. The request will be\n\
^^
Please use two spaces after a period that ends a sentence.
> +@node Hardware Debug Support
> +@section Extra support for hardware debuggers
> +
> +@cindex hwdebug
This isn't a useful index entry for the Concept Index. The entries in
that index are supposed to be phrases, not command names. The latter
are introduced by @kindex. How about
@cindex hardware debuggers support
@kindex hwdebug
> +When @value{GDBN} interacts with a server or stub running on the
> +remote target the target-side application can perform a number of
^
Missing comma.
> +host-side file I/O operations, @xref{File-I/O Remote Protocol
@xref should not be in the middle of a sentence, because it produces
"See" with a capital S. Please either use "see @ref" or replace the
preceding comma with a period.
> +or BDM. If so then target-side code may be unable to generate the
^
Missing comma.
> +remote protocol messages directly. Instead it can send the request by
^
Missing comma
> Instead it can send the request by
> +triggering a breakpoint or processor exception at a well-known
> +location @code{_gdb_hwdebug_break}.
Sorry, I'm not sure I understand this sentence. Are you saying that
_gdb_hwdebug_break is a name of a location? If so, please say
something like
by triggering a breakpoint or processor exception at a well-known
location named @code{_gdb_hwdebug_break}.
> +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}. ^^^^^^^^^^^
Shouldn't this be "set hwdebug on"?
> +Exact usage depends on the application being debugged. Amongst other
> +things @kbd{set hwdebug} clears a flag @code{_gdb_hwdebug_disabled} on
^
Missing comma.
> +the target. If the application is loaded into RAM then there are no
^
Missing comma.
> +problems. However if it is programmed into flash and restarted from
^
Missing comma.
> +the reset vector inside the debug session then typically the
^
Missing comma.
> +target-side initialization code will reset the disabled flag to its
> +default state, undoing the effect of @kbd{set hwdebug}. Instead it
^
Missing comma.
> +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
^^^
Please don't use shorthand in the manual. Please use full words.
> +depends on the embedded OS or run-time being used. If the
> +functionality has not yet been ported then a reference implementation
^
Missing comma.
Otherwise, the doco part is okay with me, assuming that the code is
approved.
Thanks!
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: add file I/O support when debugging an embedded target via jtag
2008-08-12 19:08 ` Eli Zaretskii
@ 2008-08-31 11:42 ` Bart Veer
2008-08-31 14:36 ` Stan Shebs
2008-09-01 3:07 ` Eli Zaretskii
0 siblings, 2 replies; 16+ messages in thread
From: Bart Veer @ 2008-08-31 11:42 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: gdb-patches
>>>>> "Eli" == Eli Zaretskii <eliz@gnu.org> writes:
>> Date: Tue, 12 Aug 2008 17:33:20 +0100
>> From: Bart Veer <bartv@ecoscentric.com>
>>
>> +When set, if the target halts at a well-defined location GDB\n\
Eli> ^
Eli> Need a comma here.
<snip>
>> +depends on the embedded OS or run-time being used. If the
>> +functionality has not yet been ported then a reference implementation
Eli> ^
Eli> Missing comma.
Eli> Otherwise, the doco part is okay with me, assuming that the
Eli> code is approved.
Following on from
http://sourceware.org/ml/gdb-patches/2008-08/msg00315.html, I have not
heard anything about the code in the last two weeks. Do you know if
anybody is looking at it? Also, if there is a likelihood that the
patch will be accepted then I should probably get started on the
assignment paperwork.
In the meantime, please find below an updated version of the patch
which incorporates your documentation comments and which applies to
current CVS.
Thanks,
Bart Veer
----------------------------------------------------------------------------
Top-level ChangeLog entry:
2008-08-31 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-31 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.
Index: target.h
===================================================================
RCS file: /cvs/src/src/gdb/target.h,v
retrieving revision 1.130
diff -u -p -r1.130 target.h
--- target.h 19 Aug 2008 13:22:14 -0000 1.130
+++ target.h 31 Aug 2008 11:30:39 -0000
@@ -62,6 +62,7 @@ enum strata
file_stratum, /* Executable files, etc */
core_stratum, /* Core dump files */
process_stratum, /* Executing processes */
+ process_override_stratum, /* Tweak process settings */
thread_stratum /* Executing threads */
};
Index: remote-fileio.c
===================================================================
RCS file: /cvs/src/src/gdb/remote-fileio.c,v
retrieving revision 1.28
diff -u -p -r1.28 remote-fileio.c
--- remote-fileio.c 5 Mar 2008 17:21:10 -0000 1.28
+++ remote-fileio.c 31 Aug 2008 11:30:23 -0000
@@ -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 struct {
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 @@ remote_fileio_reply (int retcode, int er
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_return_success (int retcod
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 @@ remote_fileio_func_open (char *buf)
}
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 @@ remote_fileio_func_read (char *buf)
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 @@ remote_fileio_func_read (char *buf)
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 @@ remote_fileio_func_write (char *buf)
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 @@ remote_fileio_func_write (char *buf)
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 @@ static void
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 @@ remote_fileio_func_rename (char *buf)
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 @@ static void
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_func_unlink (char *buf)
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 @@ static void
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 @@ remote_fileio_func_stat (char *buf)
}
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_func_stat (char *buf)
{
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 @@ static void
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_func_fstat (char *buf)
{
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 @@ remote_fileio_func_gettimeofday (char *b
{
LONGEST lnum;
CORE_ADDR ptrval;
- int ret, retlength;
+ int ret;
struct timeval tv;
struct fio_timeval ftv;
@@ -1262,10 +1240,9 @@ remote_fileio_func_gettimeofday (char *b
{
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 @@ remote_fileio_func_system (char *buf)
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 @@ remote_fileio_reset (void)
}
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;
Index: remote-fileio.h
===================================================================
RCS file: /cvs/src/src/gdb/remote-fileio.h,v
retrieving revision 1.8
diff -u -p -r1.8 remote-fileio.h
--- remote-fileio.h 1 Jan 2008 22:53:12 -0000 1.8
+++ remote-fileio.h 31 Aug 2008 11:30:23 -0000
@@ -26,7 +26,7 @@ struct cmd_list_element;
/* 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);
Index: remote.c
===================================================================
RCS file: /cvs/src/src/gdb/remote.c,v
retrieving revision 1.308
diff -u -p -r1.308 remote.c
--- remote.c 27 Aug 2008 13:25:21 -0000 1.308
+++ remote.c 31 Aug 2008 11:30:32 -0000
@@ -3497,7 +3497,7 @@ remote_wait (ptid_t ptid, struct target_
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, ... */
{
Index: Makefile.in
===================================================================
RCS file: /cvs/src/src/gdb/Makefile.in,v
retrieving revision 1.1048
diff -u -p -r1.1048 Makefile.in
--- Makefile.in 20 Aug 2008 10:18:23 -0000 1.1048
+++ Makefile.in 31 Aug 2008 11:30:21 -0000
@@ -445,7 +445,8 @@ SER_HARDWIRE = @SER_HARDWIRE@
# 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 @@ SFILES = ada-exp.y ada-lang.c ada-typepr
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
Index: hwdebug-fileio.c
===================================================================
RCS file: hwdebug-fileio.c
diff -N hwdebug-fileio.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ hwdebug-fileio.c 31 Aug 2008 11:30:21 -0000
@@ -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);
+}
Index: doc/gdb.texinfo
===================================================================
RCS file: /cvs/src/src/gdb/doc/gdb.texinfo,v
retrieving revision 1.520
diff -u -p -r1.520 gdb.texinfo
--- doc/gdb.texinfo 26 Aug 2008 17:30:35 -0000 1.520
+++ doc/gdb.texinfo 31 Aug 2008 11:31:25 -0000
@@ -13341,6 +13341,7 @@ configuration of @value{GDBN}; use @code
* 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
@@ -14226,6 +14227,7 @@ but in general the stubs are likely to u
subroutines which @code{@value{NGCC}} generates as inline code.
+
@node Debug Session
@subsection Putting it All Together
@@ -14285,6 +14287,42 @@ Start @value{GDBN} on the host, and conn
@end enumerate
+@node Hardware Debug Support
+@section Extra support for hardware debuggers
+
+@cindex hardware debuggers support
+@kindex 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, see @ref{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 named @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 on} 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 hardware 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
@@ -27136,8 +27174,8 @@ is defined as follows:
@smallexample
struct timeval @{
- time_t tv_sec; /* second */
- long tv_usec; /* microsecond */
+ time_t tv_sec; /* second */
+ unsigned int tv_usec; /* microsecond */
@};
@end smallexample
Index: doc/hwdebug-example.c
===================================================================
RCS file: doc/hwdebug-example.c
diff -N doc/hwdebug-example.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ doc/hwdebug-example.c 31 Aug 2008 11:31:27 -0000
@@ -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;
+}
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: add file I/O support when debugging an embedded target via jtag
2008-08-31 11:42 ` Bart Veer
@ 2008-08-31 14:36 ` Stan Shebs
2008-08-31 15:39 ` Bart Veer
2008-09-01 3:07 ` Eli Zaretskii
1 sibling, 1 reply; 16+ messages in thread
From: Stan Shebs @ 2008-08-31 14:36 UTC (permalink / raw)
To: Bart Veer; +Cc: gdb-patches
Bart Veer wrote:
> Following on from
> http://sourceware.org/ml/gdb-patches/2008-08/msg00315.html, I have not
> heard anything about the code in the last two weeks. Do you know if
> anybody is looking at it? Also, if there is a likelihood that the
> patch will be accepted then I should probably get started on the
> assignment paperwork.
>
To be honest, I looked at it but didn't understand why all this stuff
seemed necessary. If this is not for the remote protocol, then what is
it for? A target supported by GDB, or something else? The addition of
another stratum raises alarm bells with me, because the stratums are not
as orthogonal as they should be, and I wouldn't be surprised if other
configurations were broken by this. Also, how will everybody
regression-test this (read: "make check") to know that other changes
haven't broken this code?
Stan
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: add file I/O support when debugging an embedded target via jtag
2008-08-31 14:36 ` Stan Shebs
@ 2008-08-31 15:39 ` Bart Veer
2008-09-23 10:15 ` Bart Veer
0 siblings, 1 reply; 16+ messages in thread
From: Bart Veer @ 2008-08-31 15:39 UTC (permalink / raw)
To: Stan Shebs; +Cc: gdb-patches
>>>>> "Stan" == Stan Shebs <stan@codesourcery.com> writes:
Stan> Bart Veer wrote:
>> Following on from
>> http://sourceware.org/ml/gdb-patches/2008-08/msg00315.html, I have not
>> heard anything about the code in the last two weeks. Do you know if
>> anybody is looking at it? Also, if there is a likelihood that the
>> patch will be accepted then I should probably get started on the
>> assignment paperwork.
Stan> To be honest, I looked at it but didn't understand why all
Stan> this stuff seemed necessary. If this is not for the remote
Stan> protocol, then what is it for? A target supported by GDB, or
Stan> something else?
The rationale was given in
http://sourceware.org/ml/gdb/2008-07/msg00045.html
The remote protocol file I/O extensions work fine when gdb interacts
with target-side stub code over a serial line or ethernet, since
target-side code can send the requests to gdb.
Now consider a typical setup involving jtag. gdb does not access the
jtag unit directly. Instead it uses the remote protocol to interact
with a server of some kind over TCP, and the server controls the jtag
unit. Examples of this include OpenOCD, m68k-elf-sprite,
nios2-gdb-server, ... Some are open source, others are proprietary.
Target-side code has no portable way of requesting that the jtag
server sends a file I/O request to gdb. Some of these servers may
implement their own file I/O mechanisms, but that does not give a
portable solution.
The purpose of the patch is to provide portable support for file I/O
independent from the jtag server being used. In fact it should also
work with targets that do not involve the remote protocol, e.g.
simulators, although I confess I have not tried that.
Stan> The addition of another stratum raises alarm bells with me,
Stan> because the stratums are not as orthogonal as they should
Stan> be, and I wouldn't be surprised if other configurations were
Stan> broken by this.
I did not see any alternative. The h/w debug file I/O code has to
sit at a higher stratum than process_stratum (e.g. remote_ops) since
it has to override that stratum's resume/wait/load operations. It
should also sit at a level lower than thread_stratum. The file I/O
support is independent from the specific embedded OS or run-time being
used, but thread support for each OS will not be quite so portable. I
did consider putting the code into remote.c and thus explicitly tying
it to the remote protocol and remote_ops, but that would make it
impossible to use the functionality with e.g. a simulator.
For existing configurations there really should not be any change in
behaviour since there won't be any target ops at
process_override_stratum. The numerical value associated with
thread_stratum will have incremented by one, but I would hope that
there is no code anywhere assuming that thread_stratum==4.
Stan> Also, how will everybody regression-test this (read: "make
Stan> check") to know that other changes haven't broken this code?
A fair point. I am not really familiar with the capabilities of the
gdb testsuite, but the obvious problem here is on the target-side. In
due course the h/w debug file I/O support may be integrated to a
greater or lesser extent into various embedded OS's, so you cannot
just take the example code in doc/hwdebug-example.c and turn it into a
testcase: you would have both the OS and the testcase trying to define
_gdb_hwdebug_breakpoint.
Bart
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: add file I/O support when debugging an embedded target via jtag
2008-08-31 11:42 ` Bart Veer
2008-08-31 14:36 ` Stan Shebs
@ 2008-09-01 3:07 ` Eli Zaretskii
1 sibling, 0 replies; 16+ messages in thread
From: Eli Zaretskii @ 2008-09-01 3:07 UTC (permalink / raw)
To: Bart Veer; +Cc: gdb-patches
> Date: Sun, 31 Aug 2008 12:41:40 +0100
> From: Bart Veer <bartv@ecoscentric.com>
> CC: gdb-patches@sourceware.org
>
> In the meantime, please find below an updated version of the patch
> which incorporates your documentation comments and which applies to
> current CVS.
Thanks, the documentation part is okay.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: add file I/O support when debugging an embedded target via jtag
2008-08-31 15:39 ` Bart Veer
@ 2008-09-23 10:15 ` Bart Veer
2008-09-23 12:03 ` Doug Evans
0 siblings, 1 reply; 16+ messages in thread
From: Bart Veer @ 2008-09-23 10:15 UTC (permalink / raw)
To: stan; +Cc: gdb-patches
>>> Following on from
>>> http://sourceware.org/ml/gdb-patches/2008-08/msg00315.html, I have not
>>> heard anything about the code in the last two weeks. Do you know if
>>> anybody is looking at it? Also, if there is a likelihood that the
>>> patch will be accepted then I should probably get started on the
>>> assignment paperwork.
Stan> To be honest, I looked at it but didn't understand why all
Stan> this stuff seemed necessary. If this is not for the remote
Stan> protocol, then what is it for? A target supported by GDB, or
Stan> something else?
Bart> The rationale was given in
Bart> http://sourceware.org/ml/gdb/2008-07/msg00045.html
<snip>
Just wondering if you had had a chance to take another look at this.
It has now been six weeks since the original posting.
Thanks,
Bart
--
Bart Veer eCos Configuration Architect
eCosCentric Limited The eCos experts http://www.ecoscentric.com/
Barnwell House, Barnwell Drive, Cambridge, UK. Tel: +44 1223 245571
Registered in England and Wales: Reg No 4422071.
** Visit us on stand 905 at the Embedded Systems Show 2008 **
** Oct 1-2, NEC, Birmingham, UK http://www.embedded.co.uk **
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: add file I/O support when debugging an embedded target via jtag
2008-09-23 10:15 ` Bart Veer
@ 2008-09-23 12:03 ` Doug Evans
2008-09-24 21:21 ` Bart Veer
0 siblings, 1 reply; 16+ messages in thread
From: Doug Evans @ 2008-09-23 12:03 UTC (permalink / raw)
To: Bart Veer; +Cc: stan, gdb-patches
On Tue, Sep 23, 2008 at 3:14 AM, Bart Veer <bartv@ecoscentric.com> wrote:
> >>> Following on from
> >>> http://sourceware.org/ml/gdb-patches/2008-08/msg00315.html, I have not
> >>> heard anything about the code in the last two weeks. Do you know if
> >>> anybody is looking at it? Also, if there is a likelihood that the
> >>> patch will be accepted then I should probably get started on the
> >>> assignment paperwork.
>
> Stan> To be honest, I looked at it but didn't understand why all
> Stan> this stuff seemed necessary. If this is not for the remote
> Stan> protocol, then what is it for? A target supported by GDB, or
> Stan> something else?
>
> Bart> The rationale was given in
> Bart> http://sourceware.org/ml/gdb/2008-07/msg00045.html
>
> <snip>
>
> Just wondering if you had had a chance to take another look at this.
> It has now been six weeks since the original posting.
Hi. fwiw, I've read the patch and past emails. fwiw, I love the idea but
wonder if it should be done differently. Adding a new stratum feels
wrong to me too. But maybe I'm missing something so let me first ask
a question. Basically all you need is a way to run some special code
when a particular breakpoint is hit, right? [There's some housekeeping
like needing to intercept program loads (IIRC), but basically the high
order bit is running special code when a particular breakpoint is hit,
right?] I'm just wondering if this can be done differently without being
as invasive on GDB's innards. My off-the-cuff thought is to see if this
is something that could be handled from Python.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: add file I/O support when debugging an embedded target via jtag
2008-09-23 12:03 ` Doug Evans
@ 2008-09-24 21:21 ` Bart Veer
2008-09-25 17:05 ` Doug Evans
0 siblings, 1 reply; 16+ messages in thread
From: Bart Veer @ 2008-09-24 21:21 UTC (permalink / raw)
To: Doug Evans; +Cc: stan, gdb-patches
>>>>> "Doug" == Doug Evans <dje@google.com> writes:
>> >>> Following on from
>> >>> http://sourceware.org/ml/gdb-patches/2008-08/msg00315.html,
>> >>> I have not heard anything about the code in the last two
>> >>> weeks. Do you know if anybody is looking at it? Also, if
>> >>> there is a likelihood that the patch will be accepted then
>> >>> I should probably get started on the assignment paperwork.
Stan> To be honest, I looked at it but didn't understand why all
Stan> this stuff seemed necessary. If this is not for the remote
Stan> protocol, then what is it for? A target supported by GDB, or
Stan> something else?
Bart> The rationale was given in
Bart> http://sourceware.org/ml/gdb/2008-07/msg00045.html
>>
>> <snip>
>>
>> Just wondering if you had had a chance to take another look at this.
>> It has now been six weeks since the original posting.
Doug> Hi. fwiw, I've read the patch and past emails. fwiw, I love
Doug> the idea but wonder if it should be done differently. Adding
Doug> a new stratum feels wrong to me too. But maybe I'm missing
Doug> something so let me first ask a question. Basically all you
Doug> need is a way to run some special code when a particular
Doug> breakpoint is hit, right? [There's some housekeeping like
Doug> needing to intercept program loads (IIRC), but basically the
Doug> high order bit is running special code when a particular
Doug> breakpoint is hit, right?] I'm just wondering if this can be
Doug> done differently without being as invasive on GDB's innards.
Doug> My off-the-cuff thought is to see if this is something that
Doug> could be handled from Python.
At a high-level you are correct: when a breakpoint is hit, call into
the existing file I/O code and then automatically resume the target.
However a usable implementation imposes some additional requirements.
There should be no visible output every time some file I/O is
requested, so no messages that the target has halted or that there has
been a thread switch. The overheads must be kept down as much as
possible, so no additional register fetching, no extra memory writes
to unset and reset breakpoints, etc. I don't think that is achievable
if you try to do the work at the command or scripting level.
I have not been following the Python work, but as far as I know it
does not allow scripts to operate at the target vector level. That
could be useful for some things, for example it should make it
possible to add thread support for different embedded OS'es by writing
a script instead of adding a new module to gdb. I suspect it will be
some time before anything like that is possible.
Bart
--
Bart Veer eCos Configuration Architect
eCosCentric Limited The eCos experts http://www.ecoscentric.com/
Barnwell House, Barnwell Drive, Cambridge, UK. Tel: +44 1223 245571
Registered in England and Wales: Reg No 4422071.
** Visit us on stand 905 at the Embedded Systems Show 2008 **
** Oct 1-2, NEC, Birmingham, UK http://www.embedded.co.uk **
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: add file I/O support when debugging an embedded target via jtag
2008-09-24 21:21 ` Bart Veer
@ 2008-09-25 17:05 ` Doug Evans
2008-09-25 20:27 ` Bart Veer
0 siblings, 1 reply; 16+ messages in thread
From: Doug Evans @ 2008-09-25 17:05 UTC (permalink / raw)
To: Bart Veer; +Cc: stan, gdb-patches
On Wed, Sep 24, 2008 at 2:20 PM, Bart Veer <bartv@ecoscentric.com> wrote:
>>>>>> "Doug" == Doug Evans <dje@google.com> writes:
>
> >> >>> Following on from
> >> >>> http://sourceware.org/ml/gdb-patches/2008-08/msg00315.html,
> >> >>> I have not heard anything about the code in the last two
> >> >>> weeks. Do you know if anybody is looking at it? Also, if
> >> >>> there is a likelihood that the patch will be accepted then
> >> >>> I should probably get started on the assignment paperwork.
>
> Stan> To be honest, I looked at it but didn't understand why all
> Stan> this stuff seemed necessary. If this is not for the remote
> Stan> protocol, then what is it for? A target supported by GDB, or
> Stan> something else?
>
> Bart> The rationale was given in
> Bart> http://sourceware.org/ml/gdb/2008-07/msg00045.html
> >>
> >> <snip>
> >>
> >> Just wondering if you had had a chance to take another look at this.
> >> It has now been six weeks since the original posting.
>
> Doug> Hi. fwiw, I've read the patch and past emails. fwiw, I love
> Doug> the idea but wonder if it should be done differently. Adding
> Doug> a new stratum feels wrong to me too. But maybe I'm missing
> Doug> something so let me first ask a question. Basically all you
> Doug> need is a way to run some special code when a particular
> Doug> breakpoint is hit, right? [There's some housekeeping like
> Doug> needing to intercept program loads (IIRC), but basically the
> Doug> high order bit is running special code when a particular
> Doug> breakpoint is hit, right?] I'm just wondering if this can be
> Doug> done differently without being as invasive on GDB's innards.
> Doug> My off-the-cuff thought is to see if this is something that
> Doug> could be handled from Python.
>
> At a high-level you are correct: when a breakpoint is hit, call into
> the existing file I/O code and then automatically resume the target.
> However a usable implementation imposes some additional requirements.
> There should be no visible output every time some file I/O is
> requested, so no messages that the target has halted or that there has
> been a thread switch. The overheads must be kept down as much as
> possible, so no additional register fetching, no extra memory writes
> to unset and reset breakpoints, etc. I don't think that is achievable
> if you try to do the work at the command or scripting level.
There are indeed issues to be resolved, but
I'd like to think these things are achievable.
We can provide lots more functionality this way, not just file i/o
for jtag targets (and such).
> I have not been following the Python work, but as far as I know it
> does not allow scripts to operate at the target vector level. That
> could be useful for some things, for example it should make it
> possible to add thread support for different embedded OS'es by writing
> a script instead of adding a new module to gdb. I suspect it will be
> some time before anything like that is possible.
I shouldn't be that hard to let Python work at this level, and AIUI
such things are not precluded.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: add file I/O support when debugging an embedded target via jtag
2008-09-25 17:05 ` Doug Evans
@ 2008-09-25 20:27 ` Bart Veer
2008-09-25 22:20 ` Daniel Jacobowitz
0 siblings, 1 reply; 16+ messages in thread
From: Bart Veer @ 2008-09-25 20:27 UTC (permalink / raw)
To: Doug Evans; +Cc: stan, gdb-patches
>>>>> "Doug" == Doug Evans <dje@google.com> writes:
>> >> >>> Following on from
>> >> >>> http://sourceware.org/ml/gdb-patches/2008-08/msg00315.html,
>> >> >>> I have not heard anything about the code in the last two
>> >> >>> weeks. Do you know if anybody is looking at it? Also, if
>> >> >>> there is a likelihood that the patch will be accepted
>> >> >>> then I should probably get started on the assignment
>> >> >>> paperwork.
<snip>
Doug> My off-the-cuff thought is to see if this is something that
Doug> could be handled from Python.
>>
>> At a high-level you are correct: when a breakpoint is hit, call
>> into the existing file I/O code and then automatically resume
>> the target. However a usable implementation imposes some
>> additional requirements. There should be no visible output
>> every time some file I/O is requested, so no messages that the
>> target has halted or that there has been a thread switch. The
>> overheads must be kept down as much as possible, so no
>> additional register fetching, no extra memory writes to unset
>> and reset breakpoints, etc. I don't think that is achievable if
>> you try to do the work at the command or scripting level.
Doug> There are indeed issues to be resolved, but I'd like to
Doug> think these things are achievable. We can provide lots more
Doug> functionality this way, not just file i/o for jtag targets
Doug> (and such).
>> I have not been following the Python work, but as far as I know
>> it does not allow scripts to operate at the target vector
>> level. That could be useful for some things, for example it
>> should make it possible to add thread support for different
>> embedded OS'es by writing a script instead of adding a new
>> module to gdb. I suspect it will be some time before anything
>> like that is possible.
Doug> I shouldn't be that hard to let Python work at this level,
Doug> and AIUI such things are not precluded.
Although I have no doubt that a Python interface at the target vector
level is possible, figuring out what such an interface should look
like requires a far greater understanding of the gdb internals than I
possess. I am pretty sure it would also involve much bigger changes to
the internals than a one-line addition to the stratum enum. I really
don't want to see the h/w debug I/O functionality to be delayed for a
long time, possibly years, until all the required infrastructure is in
place for a reimplementation in Python.
Bart
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: add file I/O support when debugging an embedded target via jtag
2008-09-25 20:27 ` Bart Veer
@ 2008-09-25 22:20 ` Daniel Jacobowitz
2008-10-01 18:39 ` Bart Veer
0 siblings, 1 reply; 16+ messages in thread
From: Daniel Jacobowitz @ 2008-09-25 22:20 UTC (permalink / raw)
To: Bart Veer; +Cc: Doug Evans, stan, gdb-patches
On Thu, Sep 25, 2008 at 09:26:30PM +0100, Bart Veer wrote:
> Although I have no doubt that a Python interface at the target vector
> level is possible, figuring out what such an interface should look
> like requires a far greater understanding of the gdb internals than I
> possess. I am pretty sure it would also involve much bigger changes to
> the internals than a one-line addition to the stratum enum. I really
> don't want to see the h/w debug I/O functionality to be delayed for a
> long time, possibly years, until all the required infrastructure is in
> place for a reimplementation in Python.
This is a reasonable argument. Unfortunately the other direction is
reasonable too: remember Stan's comment about adding new strata?
That's got maintenance cost for the rest of GDB, because the strata
aren't really meant to work this way. And it may get in the way of
future development, e.g. the work that Stan and Pedro are doing to
support multiple processes and multi-core systems.
FWIW, I find your reason for putting it above process_stratum
convincing but not your reason for putting it below thread_stratum.
I don't see why it has to be in the target vector at all.
--
Daniel Jacobowitz
CodeSourcery
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: add file I/O support when debugging an embedded target via jtag
2008-09-25 22:20 ` Daniel Jacobowitz
@ 2008-10-01 18:39 ` Bart Veer
2008-10-01 21:10 ` Daniel Jacobowitz
0 siblings, 1 reply; 16+ messages in thread
From: Bart Veer @ 2008-10-01 18:39 UTC (permalink / raw)
To: Daniel Jacobowitz; +Cc: gdb-patches
>>>>> "Daniel" == Daniel Jacobowitz <drow@false.org> writes:
Daniel> On Thu, Sep 25, 2008 at 09:26:30PM +0100, Bart Veer wrote:
>> Although I have no doubt that a Python interface at the target
>> vector level is possible, figuring out what such an interface
>> should look like requires a far greater understanding of the
>> gdb internals than I possess. I am pretty sure it would also
>> involve much bigger changes to the internals than a one-line
>> addition to the stratum enum. I really don't want to see the
>> h/w debug I/O functionality to be delayed for a long time,
>> possibly years, until all the required infrastructure is in
>> place for a reimplementation in Python.
Daniel> This is a reasonable argument. Unfortunately the other
Daniel> direction is reasonable too: remember Stan's comment about
Daniel> adding new strata? That's got maintenance cost for the
Daniel> rest of GDB, because the strata aren't really meant to
Daniel> work this way. And it may get in the way of future
Daniel> development, e.g. the work that Stan and Pedro are doing
Daniel> to support multiple processes and multi-core systems.
Adding a new stratum certainly appears to be controversial, although I
am not certain why. A grep -r stratum through the gdb sources
indicates that most files only set the to_stratum field to a known
value. As long as the order of the existing strata remains unchanged,
those files should not be affected. There are only six places, spread
over two files, where the to_stratum field is examined.
gdbtk/generic/gdbtk-cmds.c, gdb_entry_point(), has a > check against
dummy_stratum, to test for a fully-initialized target vector. If
Insight is started with no initial executable and a "set hwdebug"
command is issued before connecting to a target or specifying an
executable file, that test could trigger where previously it would
not. Except that "set hwdebug" will fail if there is no current
executable, without pushing hwdebug_ops, so actually the behaviour
is unchanged. And even if the test did trigger, all that would
happen is that gdb_entry_point() would call entry_point_address()
which would return 0, and the higher-level code in interface.tcl
copes with that.
target.c, push_stratum(). There is a >= test when deciding where to
insert a target. My patch preserves the order of the existing
strata. There is also an == test for replacing the current stratum.
For now nothing else uses process_override_stratum, so adding the
stratum won't change the behaviour.
target.c, pop_all_targets_above(), has a > test. Again this is of no
concern since the order of existing strata is preserved.
target.c, target_info() has a <= test against dummy_stratum. That
can only trigger for dummy_stratum, there is nothing lower. Adding
process_override_stratum won't affect it.
target.c, target_require_runnable(), has an == test against
thread_stratum. Possibly that should be changed to >
process_stratum. Otherwise if the hwdebug_ops are layered on top of
another target_ops which does implement to_create_inferior, the
runnable test will fail prematurely.
target.c, find_core_target() has an == test against core_stratum.
That won't be affected.
So, as far as I can tell, the only function in the current sources
that may be adversely affected by adding process_override_stratum is
target_require_runnable(), and the fix is straightforward.
Obviously if new code is written that makes assumptions about exactly
how a target vector gets constructed, that code may break after a "set
hwdebug". It is not clear why new code would want to make such
assumptions, but that is out of my control. A testcase would help a
bit, but there would be portability issues so I am not sure how useful
it would be, or how to go about writing one.
Daniel> FWIW, I find your reason for putting it above
Daniel> process_stratum convincing but not your reason for putting
Daniel> it below thread_stratum.
Consider an idealized setup for debugging an embedded OS. This could
have:
remote_ops @ process_stratum, talking to a gdb server that controls
a jtag unit. Typically that will not provide any of the file I/O
functionality associated with remote protocol 'F' packets. The gdb
server will also not have any knowledge of how the OS implements
multithreading so cannot provide any of the thread support.
hwdebug_ops @ process_override_stratum, providing I/O functionality
including a console for printf()'s.
eCos_ops @ thread_stratum, currently theoretical. This has detailed
knowledge of the internals of a specific OS so can implement e.g.
to_find_new_threads by peeking around the appropriate kernel data
structures.
This trio gives full debug capability using just a minimal gdb server.
Instead of remote_ops you could run on top of e.g. an architectural
simulator, without affecting hwdebug_ops or ecos_ops. For a different
embedded OS you could have e.g. FreeRTOS_ops instead of eCos_ops. That
other OS could reuse hwdebug_ops with no problems to get the file I/O
support. Or, if developing an embedded system without a multithreaded
OS, you could have nothing at thread_stratum and the file I/O support
would still be functional.
So, hwdebug_ops should not be part of the process_stratum. Merging it
into remote_ops would make the functionality inaccessible when e.g.
running on top of a simulator. Similarly hwdebug_ops should not be
part of the thread_stratum. You would end up duplicating the code in
ecos.c, freertos.c, etc.
With careful coding you could still have a module hwdebug-fileio.c
with no target_ops of its own, and depend on ecos.c, freertos.c, etc.
to merge the file I/O functionality into their thread stratum
target_ops. That seems messy, when gdb already has support for
multiple strata, and the no-OS case would be problematical.
There is still the question of whether hwdebug_ops should operate at a
stratum between process and thread, or at some stratum >
thread_stratum. Given the implementation in hwdebug-fileio.c it seems
obvious to me that the code should operate as closely as possible to
the process_stratum wait function. That avoids any overheads at the
thread stratum, e.g. detecting whether or not there has been a thread
context switch, every time the target halts for file I/O.
Daniel> I don't see why it has to be in the target vector at all.
The h/w debug file I/O code needs to take some action for every load,
resume and wait operation. For the wait, deprecated_target_wait_hook
provides an alternative but I assume that hook is going to disappear
at some point. There are no equivalent hooks for load and resume.
The existing support for multiple target_ops seems to provide exactly
the functionality that is required, and allows the h/w debug file I/O
functionality to be contained inside a single module with minimal
changes elsewhere. It does require adding a new stratum, but as far as
I can tell that is a harmless change.
The only obvious alternative I can see is to add observers to
target_wait()/load()/resume(), or to the various places where those
get invoked. That would require rather bigger changes to existing gdb
sources than the current patch. An observer would probably work fine
for load and resume, but wait is problematical. At first glance it
seems that observers are supposed to just observe, not change the flow
of control. A wait() observer could have to resume the target and
cause the target_wait() to restart, e.g. by setting a global flag.
This all seems messy compared with the current target_ops
implementation.
Bart
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: add file I/O support when debugging an embedded target via jtag
2008-10-01 18:39 ` Bart Veer
@ 2008-10-01 21:10 ` Daniel Jacobowitz
2008-10-09 17:11 ` Bart Veer
0 siblings, 1 reply; 16+ messages in thread
From: Daniel Jacobowitz @ 2008-10-01 21:10 UTC (permalink / raw)
To: Bart Veer; +Cc: gdb-patches
On Wed, Oct 01, 2008 at 07:38:22PM +0100, Bart Veer wrote:
> Adding a new stratum certainly appears to be controversial, although I
> am not certain why.
Because this is not about what in the current sources will break.
It's about the purpose of the target stack, which does not include
synthesizing operations like this.
> Daniel> I don't see why it has to be in the target vector at all.
>
> The h/w debug file I/O code needs to take some action for every load,
> resume and wait operation. For the wait, deprecated_target_wait_hook
> provides an alternative but I assume that hook is going to disappear
> at some point. There are no equivalent hooks for load and resume.
Those operations are where your patch acts, but I don't think they're
what you actually want. What you do after load doesn't have anything
to do with "load"; I assume that at load time you're setting the
breakpoint, but GDB already has several hooks to set internal
breakpoints. And it already has support for hitting an internal
breakpoint, hiding it from the user, and taking some action - this is
how SVR4 shared library support works.
--
Daniel Jacobowitz
CodeSourcery
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: add file I/O support when debugging an embedded target via jtag
2008-10-01 21:10 ` Daniel Jacobowitz
@ 2008-10-09 17:11 ` Bart Veer
2008-10-17 11:59 ` John Dallaway
0 siblings, 1 reply; 16+ messages in thread
From: Bart Veer @ 2008-10-09 17:11 UTC (permalink / raw)
To: Daniel Jacobowitz; +Cc: gdb-patches
>>>>> "Daniel" == Daniel Jacobowitz <drow@false.org> writes:
Daniel> On Wed, Oct 01, 2008 at 07:38:22PM +0100, Bart Veer wrote:
>> Adding a new stratum certainly appears to be controversial,
>> although I am not certain why.
Daniel> Because this is not about what in the current sources will
Daniel> break. It's about the purpose of the target stack, which
Daniel> does not include synthesizing operations like this.
I find this somewhat confusing. My understanding of the target stack
is that it allows target-related operations to be encapsulated in
independent modules. The various target_ops structures are combined
into a single current target vector, with a simple priority scheme in
the form of strata. According to my earlier investigation the
individual modules do not care whether there are five strata or 500,
as long as the existing priority ordering is preserved. I do not see
anything obvious in the source code or the gdb internals documentation
to contradict this. Further, I see code in bsd-uthread.c, aix-thread.c
and linux-thread-db.c which appears to work in much the same way as my
patch. For example bsd_uthread_wait() chains to another wait() lower
down the stack.
The current file I/O support in remote.c is handled entirely at the
target stack level, completely transparent to higher-level code like
wait_for_inferior(). It seems strange that the h/w debug file I/O now
needs to be implemented higher up.
Daniel>> I don't see why it has to be in the target vector at all.
>> The h/w debug file I/O code needs to take some action for every
>> load, resume and wait operation. For the wait,
>> deprecated_target_wait_hook provides an alternative but I
>> assume that hook is going to disappear at some point. There are
>> no equivalent hooks for load and resume.
Daniel> Those operations are where your patch acts, but I don't
Daniel> think they're what you actually want. What you do after
Daniel> load doesn't have anything to do with "load"; I assume
Daniel> that at load time you're setting the breakpoint, but GDB
Daniel> already has several hooks to set internal breakpoints. And
Daniel> it already has support for hitting an internal breakpoint,
Daniel> hiding it from the user, and taking some action - this is
Daniel> how SVR4 shared library support works.
That is not exactly how my patch works. The breakpoint is permanently
embedded in the target-side executable and never needs to be set by
gdb. That avoids using up one of the hardware breakpoint when
debugging an executable held in flash. However there is a target-side
flag in the .data section which determines whether or not the
application is running under gdb control. If instead the application
is running stand-alone then any file I/O operations will fail with
ENOSYS. The load operation must be detected because it will invalidate
the flag, and next subsequent resume operation must be intercepted so
that the flag can be set again.
Anyway, I have been trying to understand some of the higher-level gdb
code, to see how to get things working there. It looks like it would
take something like the following:
1) a new post-load notifier, invoked from load_command() after the
target_load() call.
2) a new resume notifier, invoked before the target gets resumed. I
am not totally sure where this should go, but proceed() is a likely
candidate.
3) extensions to breakpoint.c, along the lines of
create_solib_event_breakpoint() etc. This is where things get much
more complicated. hwdebug-fileio.o is part of REMOTE_OBS alongside
remote.o, remote-fileio.o, etc., and not part of the main sources.
Hence breakpoint.c cannot have any direct dependencies on the file I/O
support, and something more generic is needed. Something along the
lines of:
a) two new breakpoint types, bp_callback and bp_callback_no_insert.
should_be_inserted() can distinguish between these two types.
b) a new field in the breakpoint structure,
struct bpstat_what (*callback_fn)(something_or_other)
c) a new function to register a callback breakpoint
create_callback_breakpoint (bptype, CORE_ADDR, callback_fn);
plus of course a matching remove function.
d) when handle_inferior_event() calls bpstat_what(), that can detect
a callback breakpoint, invoke the callback function, and return
its bpstat_what value. handle_inferior_event() can then be made
to keep_going().
e) the h/w debug file I/O code can then register a no_insert
callback breakpoint at the appropriate address, and the callback
function would be responsible for performing the file I/O
operation.
That seems to be roughly how the SVR4 shared library support works.
No doubt there would be various details to sort out. I am also rather
concerned about handle_inferior_event(). There are almost 1000 lines
of code in that function before the call to bpstat_what(), and I have
very little understanding of what most of that code does or how it
might interfere with what I am trying to achieve. For example I don't
know whether gdb is going to try to remove all breakpoints and
reinsert them later, which would mess up performance for no good
reason. I do see some handling of thread events which are of no
interest if the target is going to be resumed immediately after the
file I/O operation has completed.
Now, my current patch changes one line apiece in target.h, remote.c,
and remote-fileio.h, plus a handful of simple changes in
remote-fileio.c. Everything else is done in a self-contained new
module, courtesy of the modularity within the current target vector
implementation. Doing things above the target vector level would seem
to involve changes to the gdb core that are much much bigger, and
would add yet more complexity to existing code. That looks like a bad
idea to me.
Bart
--
Bart Veer eCos Configuration Architect
eCosCentric Limited The eCos experts http://www.ecoscentric.com/
Barnwell House, Barnwell Drive, Cambridge, UK. Tel: +44 1223 245571
Registered in England and Wales: Reg No 4422071.
^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: add file I/O support when debugging an embedded target via jtag
2008-10-09 17:11 ` Bart Veer
@ 2008-10-17 11:59 ` John Dallaway
0 siblings, 0 replies; 16+ messages in thread
From: John Dallaway @ 2008-10-17 11:59 UTC (permalink / raw)
To: Daniel Jacobowitz, Doug Evans, Stan Shebs; +Cc: gdb-patches, Bart Veer
Daniel, Stan and Doug
Thank you for your comments on Bart Veer's original proposal and
subsequent patch for file I/O support when debugging via JTAG.
Proposal: http://sourceware.org/ml/gdb/2008-07/msg00045.html
Patch: http://sourceware.org/ml/gdb-patches/2008-08/msg00315.html
We now need to draw this to a conclusion and move on.
The principle objection to the patch is that it introduces a new target
ops stratum which might break other configurations or impede future
developments. During our initial investigation we came to the conclusion
that introducing a new stratum was the most elegant way to implement
this functionality in a generic manner. In his response to the original
proposal, Daniel wrote:
> It's sad that you need to use the target strata for this. Doing it
> directly in the remote target would work for all of the above except
> for "target bdm". But I suppose it's reasonable.
Is there anything we can now do to make the patch acceptable short of a
re-implemention to avoid introducing a new stratum?
John Dallaway
eCosCentric Limited
^ permalink raw reply [flat|nested] 16+ messages in thread
end of thread, other threads:[~2008-10-17 11:59 UTC | newest]
Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-08-12 16:34 add file I/O support when debugging an embedded target via jtag Bart Veer
2008-08-12 19:08 ` Eli Zaretskii
2008-08-31 11:42 ` Bart Veer
2008-08-31 14:36 ` Stan Shebs
2008-08-31 15:39 ` Bart Veer
2008-09-23 10:15 ` Bart Veer
2008-09-23 12:03 ` Doug Evans
2008-09-24 21:21 ` Bart Veer
2008-09-25 17:05 ` Doug Evans
2008-09-25 20:27 ` Bart Veer
2008-09-25 22:20 ` Daniel Jacobowitz
2008-10-01 18:39 ` Bart Veer
2008-10-01 21:10 ` Daniel Jacobowitz
2008-10-09 17:11 ` Bart Veer
2008-10-17 11:59 ` John Dallaway
2008-09-01 3:07 ` Eli Zaretskii
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox