This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

add file I/O support when debugging an embedded target via jtag


This patch extends the remote protocol file I/O support so that it can
be used in conjunction with hardware debuggers which typically do not
support this. It was previously discussed in
http://sourceware.org/ml/gdb/2008-07/msg00045.html

The patch consists of the following bits:

1) add a new stratum to target.h to allow for a target vector between
   the process and thread levels.

2) update remote-fileio.c so that it no longer assumes it is called
   directly from remote.c. This involves passing a putpkt() function
   to remote_fileio_request() instead of automatically using
   remote.c's putpkt(). It also involves using target_read_memory()
   and target_write_memory() instead of remote_read_bytes() and
   remote_write_bytes().

3) remote-fileio.h needs an updated prototype for
   remote_fileio_request() and remote.c needs to pass a putpkt()
   function.

4) a new module hwdebug-fileio.c to implement the gdb side.

5) Makefile.in update to add hwdebug-fileio.c

6) documentation update to gdb.texinfo to describe the hwdebug
   support. 

7) an example implementation for an embedded target, showing
   developers of embedded OS's and run-time systems how to incorporate
   the functionality.

Top-level ChangeLog entry:

2008-08-12  Bart Veer  <bartv@ecoscentric.co.uk>

	* target.h: add new stratum process_override
	* remote-fileio.h, remote.c: add putpkt argument to
	remote_fileio_request()
	* remote-fileio.c: the putpkt function is supplied by the
	caller. Use target_read_memory() and target_write_memory()
	instead of assuming the remote protocol.
	* hwdebug-fileio.c: new module supporting file I/O operations
	when the remote protocol server does not provide the necessary
	support.
	* Makefile.in: add hwdebug-fileio.c


doc ChangeLog entry:

2008-08-12  Bart Veer  <bartv@ecoscentric.co.uk>

	* gdb.texinfo: document h/w debug file I/O support.
	* hwdebug-example.c: example target-side code for the h/w
	debug file I/O support.

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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]