From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 6878 invoked by alias); 12 Aug 2008 16:34:36 -0000 Received: (qmail 6851 invoked by uid 22791); 12 Aug 2008 16:34:28 -0000 X-Spam-Check-By: sourceware.org Received: from snape.ecoscentric.com (HELO snape.ecoscentric.com) (212.13.207.199) by sourceware.org (qpsmtpd/0.31) with ESMTP; Tue, 12 Aug 2008 16:33:30 +0000 Received: from localhost (snape.ecoscentric.com [127.0.0.1]) by snape.ecoscentric.com (Postfix) with ESMTP id 8C9BCC54168 for ; Tue, 12 Aug 2008 17:33:27 +0100 (BST) Received: from snape.ecoscentric.com ([127.0.0.1]) by localhost (snape.ecoscentric.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id xzchSk3evGlU; Tue, 12 Aug 2008 17:33:22 +0100 (BST) Received: from delenn.bartv.net (unknown [212.13.207.199]) by snape.ecoscentric.com (Postfix) with ESMTP id 5A0F8C540DC for ; Tue, 12 Aug 2008 17:33:21 +0100 (BST) Date: Tue, 12 Aug 2008 16:34:00 -0000 Message-Id: From: Bart Veer To: gdb-patches@sourceware.org Subject: add file I/O support when debugging an embedded target via jtag Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2008-08/txt/msg00315.txt.bz2 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 * 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 * 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 #include @@ -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 . */ + +#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,,, 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 . */ + +/* ---------------------------------------------------------------------------- + This section contains everything that is OS or target-specific: data + types, locking, and how to invoke a hardware breakpoint. */ +#include +#include +#include +#include +#include +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; +}