summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/config.h4
-rw-r--r--src/core/config.c9
-rw-r--r--src/core/gdbserial.c41
-rw-r--r--src/core/gdbstub.c89
-rw-r--r--src/core/gdbudp.c228
-rw-r--r--src/hci/commands/gdbstub_cmd.c105
-rw-r--r--src/include/gpxe/gdbstub.h64
-rwxr-xr-xsrc/tests/gdbstub_test.gdb11
8 files changed, 501 insertions, 50 deletions
diff --git a/src/config.h b/src/config.h
index e695b015..f43da040 100644
--- a/src/config.h
+++ b/src/config.h
@@ -163,7 +163,9 @@
#undef BUILD_ID /* Include a custom build ID string,
* e.g "test-foo" */
#undef NULL_TRAP /* Attempt to catch NULL function calls */
-#undef GDBSTUB /* Remote GDB debugging */
+#undef GDBSERIAL /* Remote GDB debugging over serial */
+#undef GDBUDP /* Remote GDB debugging over UDP
+ * (both may be set) */
/* @END general.h */
diff --git a/src/core/config.c b/src/core/config.c
index 018f084a..42026827 100644
--- a/src/core/config.c
+++ b/src/core/config.c
@@ -195,6 +195,13 @@ REQUIRE_OBJECT ( sanboot_cmd );
#ifdef NULL_TRAP
REQUIRE_OBJECT ( nulltrap );
#endif
-#ifdef GDBSTUB
+#ifdef GDBSERIAL
REQUIRE_OBJECT ( gdbidt );
+REQUIRE_OBJECT ( gdbserial );
+REQUIRE_OBJECT ( gdbstub_cmd );
+#endif
+#ifdef GDBUDP
+REQUIRE_OBJECT ( gdbidt );
+REQUIRE_OBJECT ( gdbudp );
+REQUIRE_OBJECT ( gdbstub_cmd );
#endif
diff --git a/src/core/gdbserial.c b/src/core/gdbserial.c
new file mode 100644
index 00000000..2fecd5f6
--- /dev/null
+++ b/src/core/gdbserial.c
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2008 Stefan Hajnoczi <stefanha@gmail.com>.
+ *
+ * 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 2 of the
+ * License, or 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <assert.h>
+#include <gpxe/serial.h>
+#include <gpxe/gdbstub.h>
+
+struct gdb_transport serial_gdb_transport __gdb_transport;
+
+static size_t gdbserial_recv ( char *buf, size_t len ) {
+ assert ( len > 0 );
+ buf [ 0 ] = serial_getc();
+ return 1;
+}
+
+static void gdbserial_send ( const char *buf, size_t len ) {
+ while ( len-- > 0 ) {
+ serial_putc ( *buf++ );
+ }
+}
+
+struct gdb_transport serial_gdb_transport __gdb_transport = {
+ .name = "serial",
+ .recv = gdbserial_recv,
+ .send = gdbserial_send,
+};
diff --git a/src/core/gdbstub.c b/src/core/gdbstub.c
index df50dc81..c331b85b 100644
--- a/src/core/gdbstub.c
+++ b/src/core/gdbstub.c
@@ -24,22 +24,24 @@
*/
#include <stdlib.h>
-#include <stddef.h>
#include <stdio.h>
+#include <string.h>
#include <ctype.h>
#include <byteswap.h>
-#include <gpxe/process.h>
-#include <gpxe/serial.h>
+#include <gpxe/gdbstub.h>
#include "gdbmach.h"
enum {
- POSIX_EINVAL = 0x1c /* used to report bad arguments to GDB */
+ POSIX_EINVAL = 0x1c, /* used to report bad arguments to GDB */
+ SIZEOF_PAYLOAD = 256, /* buffer size of GDB payload data */
};
struct gdbstub {
+ struct gdb_transport *trans;
+ int exit_handler; /* leave interrupt handler */
+
int signo;
gdbreg_t *regs;
- int exit_handler; /* leave interrupt handler */
void ( * parse ) ( struct gdbstub *stub, char ch );
uint8_t cksum1;
@@ -47,10 +49,15 @@ struct gdbstub {
/* Buffer for payload data when parsing a packet. Once the
* packet has been received, this buffer is used to hold
* the reply payload. */
- char payload [ 256 ];
- int len;
+ char buf [ SIZEOF_PAYLOAD + 4 ]; /* $...PAYLOAD...#XX */
+ char *payload; /* start of payload */
+ int len; /* length of payload */
};
+/* Transports */
+static struct gdb_transport gdb_transport_start[0] __table_start ( struct gdb_transport, gdb_transports );
+static struct gdb_transport gdb_transport_end[0] __table_end ( struct gdb_transport, gdb_transports );
+
/* Packet parser states */
static void gdbstub_state_new ( struct gdbstub *stub, char ch );
static void gdbstub_state_data ( struct gdbstub *stub, char ch );
@@ -132,29 +139,13 @@ static uint8_t gdbstub_cksum ( char *data, int len ) {
return cksum;
}
-static int gdbstub_getchar ( struct gdbstub *stub ) {
- if ( stub->exit_handler ) {
- return -1;
- }
- return serial_getc();
-}
-
-static void gdbstub_putchar ( struct gdbstub * stub __unused, char ch ) {
- serial_putc ( ch );
-}
-
static void gdbstub_tx_packet ( struct gdbstub *stub ) {
uint8_t cksum = gdbstub_cksum ( stub->payload, stub->len );
- int i;
-
- gdbstub_putchar ( stub, '$' );
- for ( i = 0; i < stub->len; i++ ) {
- gdbstub_putchar ( stub, stub->payload [ i ] );
- }
- gdbstub_putchar ( stub, '#' );
- gdbstub_putchar ( stub, gdbstub_to_hex_digit ( cksum >> 4 ) );
- gdbstub_putchar ( stub, gdbstub_to_hex_digit ( cksum ) );
-
+ stub->buf [ 0 ] = '$';
+ stub->buf [ stub->len + 1 ] = '#';
+ stub->buf [ stub->len + 2 ] = gdbstub_to_hex_digit ( cksum >> 4 );
+ stub->buf [ stub->len + 3 ] = gdbstub_to_hex_digit ( cksum );
+ stub->trans->send ( stub->buf, stub->len + 4 );
stub->parse = gdbstub_state_wait_ack;
}
@@ -229,7 +220,7 @@ static void gdbstub_read_mem ( struct gdbstub *stub ) {
gdbstub_send_errno ( stub, POSIX_EINVAL );
return;
}
- args [ 1 ] = ( args [ 1 ] < sizeof stub->payload / 2 ) ? args [ 1 ] : sizeof stub->payload / 2;
+ args [ 1 ] = ( args [ 1 ] < SIZEOF_PAYLOAD / 2 ) ? args [ 1 ] : SIZEOF_PAYLOAD / 2;
gdbstub_to_hex_buf ( stub->payload, ( char * ) args [ 0 ], args [ 1 ] );
stub->len = args [ 1 ] * 2;
gdbstub_tx_packet ( stub );
@@ -306,7 +297,7 @@ static void gdbstub_state_data ( struct gdbstub *stub, char ch ) {
stub->len = 0; /* retry new packet */
} else {
/* If the length exceeds our buffer, let the checksum fail */
- if ( stub->len < ( int ) sizeof stub->payload ) {
+ if ( stub->len < SIZEOF_PAYLOAD ) {
stub->payload [ stub->len++ ] = ch;
}
}
@@ -325,12 +316,12 @@ static void gdbstub_state_cksum2 ( struct gdbstub *stub, char ch ) {
their_cksum = stub->cksum1 + gdbstub_from_hex_digit ( ch );
our_cksum = gdbstub_cksum ( stub->payload, stub->len );
if ( their_cksum == our_cksum ) {
- gdbstub_putchar ( stub, '+' );
+ stub->trans->send ( "+", 1 );
if ( stub->len > 0 ) {
gdbstub_rx_packet ( stub );
}
} else {
- gdbstub_putchar ( stub, '-' );
+ stub->trans->send ( "-", 1 );
}
}
@@ -351,23 +342,37 @@ static struct gdbstub stub = {
};
__cdecl void gdbstub_handler ( int signo, gdbreg_t *regs ) {
- int ch;
+ char packet [ SIZEOF_PAYLOAD + 4 ];
+ size_t len, i;
+
+ /* A transport must be set up */
+ if ( !stub.trans ) {
+ return;
+ }
+
stub.signo = signo;
stub.regs = regs;
stub.exit_handler = 0;
gdbstub_report_signal ( &stub );
- while ( ( ch = gdbstub_getchar( &stub ) ) != -1 ) {
- gdbstub_parse ( &stub, ch );
+ while ( !stub.exit_handler && ( len = stub.trans->recv ( packet, sizeof ( packet ) ) ) > 0 ) {
+ for ( i = 0; i < len; i++ ) {
+ gdbstub_parse ( &stub, packet [ i ] );
+ }
}
}
-/* Activity monitor to detect packets from GDB when we are not active */
-static void gdbstub_activity_step ( struct process *process __unused ) {
- if ( serial_ischar() ) {
- gdbmach_breakpoint();
+struct gdb_transport *find_gdb_transport ( const char *name ) {
+ struct gdb_transport *trans;
+ for ( trans = gdb_transport_start; trans < gdb_transport_end; trans++ ) {
+ if ( strcmp ( trans->name, name ) == 0 ) {
+ return trans;
+ }
}
+ return NULL;
}
-struct process gdbstub_activity_process __permanent_process = {
- .step = gdbstub_activity_step,
-};
+void gdbstub_start ( struct gdb_transport *trans ) {
+ stub.trans = trans;
+ stub.payload = &stub.buf [ 1 ];
+ gdbmach_breakpoint();
+}
diff --git a/src/core/gdbudp.c b/src/core/gdbudp.c
new file mode 100644
index 00000000..fb381644
--- /dev/null
+++ b/src/core/gdbudp.c
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2008 Stefan Hajnoczi <stefanha@gmail.com>.
+ *
+ * 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 2 of the
+ * License, or 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <byteswap.h>
+#include <gpxe/iobuf.h>
+#include <gpxe/in.h>
+#include <gpxe/if_arp.h>
+#include <gpxe/if_ether.h>
+#include <gpxe/ip.h>
+#include <gpxe/udp.h>
+#include <gpxe/netdevice.h>
+#include <gpxe/gdbstub.h>
+
+struct gdb_transport udp_gdb_transport __gdb_transport;
+
+static struct net_device *netdev;
+static uint8_t dest_eth[ETH_ALEN];
+static uint8_t source_eth[ETH_ALEN];
+static struct sockaddr_in dest_addr;
+static struct sockaddr_in source_addr;
+
+static void gdbudp_ensure_netdev_open ( struct net_device *netdev ) {
+ if ( ( netdev->state & NETDEV_OPEN) == 0 ) {
+ netdev_open ( netdev );
+ }
+ /* TODO forcing the netdev to be open is useful when
+ * gPXE closes the netdev between breakpoints. Should
+ * we restore the state of the netdev, i.e. closed,
+ * before leaving the interrupt handler? */
+}
+
+static size_t gdbudp_recv ( char *buf, size_t len ) {
+ struct io_buffer *iob;
+ struct ethhdr *ethhdr;
+ struct arphdr *arphdr;
+ struct iphdr *iphdr;
+ struct udp_header *udphdr;
+ size_t payload_len;
+
+ assert ( netdev );
+ gdbudp_ensure_netdev_open ( netdev );
+
+ for ( ; ; ) {
+ while ( ( iob = netdev_rx_dequeue ( netdev ) ) != NULL ) {
+ if ( iob_len ( iob ) > sizeof ( *ethhdr ) + sizeof ( *iphdr ) + sizeof ( *udphdr ) + len ) {
+ goto bad_packet;
+ }
+
+ /* Ethernet header */
+ ethhdr = iob->data;
+ iob_pull ( iob, sizeof ( *ethhdr ) );
+ if ( ethhdr->h_protocol == htons ( ETH_P_ARP ) ) {
+ /* Handle ARP requests so the client can connect to us */
+ arphdr = iob->data;
+ if ( arphdr->ar_hrd != htons ( ARPHRD_ETHER ) ||
+ arphdr->ar_pro != htons ( ETH_P_IP ) ||
+ arphdr->ar_hln != ETH_ALEN ||
+ arphdr->ar_pln != sizeof ( struct in_addr ) ||
+ arphdr->ar_op != htons ( ARPOP_REQUEST ) ||
+ memcmp ( arp_target_pa ( arphdr ), &source_addr.sin_addr.s_addr, sizeof ( struct in_addr ) ) ) {
+ goto bad_packet;
+ }
+
+ /* Generate an ARP reply */
+ arphdr->ar_op = htons ( ARPOP_REPLY );
+ memswap ( arp_sender_pa ( arphdr ), arp_target_pa ( arphdr ), sizeof ( struct in_addr ) );
+ memcpy ( arp_target_ha ( arphdr ), arp_sender_ha ( arphdr ), ETH_ALEN );
+ memcpy ( arp_sender_ha ( arphdr ), source_eth, ETH_ALEN );
+
+ /* Fix up ethernet header */
+ ethhdr = iob_push ( iob, sizeof ( *ethhdr ) );
+ memswap ( ethhdr->h_source, ethhdr->h_dest, ETH_ALEN );
+
+ netdev_tx ( netdev, iob );
+ continue; /* no need to free iob */
+ }
+ if ( ethhdr->h_protocol != htons ( ETH_P_IP ) ) {
+ goto bad_packet;
+ }
+
+ /* IP header */
+ iphdr = iob->data;
+ iob_pull ( iob, sizeof ( *iphdr ) );
+ if ( iphdr->protocol != IP_UDP || iphdr->dest.s_addr != source_addr.sin_addr.s_addr ) {
+ goto bad_packet;
+ }
+
+ /* UDP header */
+ udphdr = iob->data;
+ if ( udphdr->dest != source_addr.sin_port ) {
+ goto bad_packet;
+ }
+
+ /* Learn the remote connection details */
+ memcpy ( dest_eth, ethhdr->h_source, ETH_ALEN );
+ dest_addr.sin_addr.s_addr = iphdr->src.s_addr;
+ dest_addr.sin_port = udphdr->src;
+
+ /* Payload */
+ payload_len = ntohs ( udphdr->len );
+ if ( payload_len < sizeof ( *udphdr ) ||
+ payload_len > iob_len ( iob ) ) {
+ goto bad_packet;
+ }
+ payload_len -= sizeof ( *udphdr );
+ iob_pull ( iob, sizeof ( *udphdr ) );
+ memcpy ( buf, iob->data, payload_len );
+
+ free_iob ( iob );
+ return payload_len;
+
+bad_packet:
+ free_iob ( iob );
+ }
+ netdev_poll ( netdev );
+ }
+}
+
+static void gdbudp_send ( const char *buf, size_t len ) {
+ struct io_buffer *iob;
+ struct ethhdr *ethhdr;
+ struct iphdr *iphdr;
+ struct udp_header *udphdr;
+
+ /* Check that we are connected */
+ if ( dest_addr.sin_port == 0 ) {
+ return;
+ }
+
+ assert ( netdev );
+ gdbudp_ensure_netdev_open ( netdev );
+
+ iob = alloc_iob ( sizeof ( *ethhdr ) + sizeof ( *iphdr ) + sizeof ( *udphdr ) + len );
+ if ( !iob ) {
+ return;
+ }
+
+ /* Payload */
+ iob_reserve ( iob, sizeof ( *ethhdr ) + sizeof ( *iphdr ) + sizeof ( *udphdr ) );
+ memcpy ( iob_put ( iob, len ), buf, len );
+
+ /* UDP header */
+ udphdr = iob_push ( iob, sizeof ( *udphdr ) );
+ udphdr->src = source_addr.sin_port;
+ udphdr->dest = dest_addr.sin_port;
+ udphdr->len = htons ( iob_len ( iob ) );
+ udphdr->chksum = 0; /* optional and we are not using it */
+
+ /* IP header */
+ iphdr = iob_push ( iob, sizeof ( *iphdr ) );
+ memset ( iphdr, 0, sizeof ( *iphdr ) );
+ iphdr->verhdrlen = ( IP_VER | ( sizeof ( *iphdr ) / 4 ) );
+ iphdr->service = IP_TOS;
+ iphdr->len = htons ( iob_len ( iob ) );
+ iphdr->ttl = IP_TTL;
+ iphdr->protocol = IP_UDP;
+ iphdr->dest.s_addr = dest_addr.sin_addr.s_addr;
+ iphdr->src.s_addr = source_addr.sin_addr.s_addr;
+ iphdr->chksum = tcpip_chksum ( iphdr, sizeof ( *iphdr ) );
+
+ /* Ethernet header */
+ ethhdr = iob_push ( iob, sizeof ( *ethhdr ) );
+ memcpy ( ethhdr->h_dest, dest_eth, ETH_ALEN );
+ memcpy ( ethhdr->h_source, source_eth, ETH_ALEN );
+ ethhdr->h_protocol = htons ( ETH_P_IP );
+
+ netdev_tx ( netdev, iob );
+}
+
+static int gdbudp_init ( int argc, char **argv ) {
+ struct settings *settings;
+
+ if ( argc != 1 ) {
+ printf ( "udp: missing <interface> argument\n" );
+ return 1;
+ }
+
+ netdev = find_netdev ( argv[0] );
+ if ( !netdev ) {
+ printf ( "%s: no such interface\n", argv[0] );
+ return 1;
+ }
+
+ if ( !netdev_link_ok ( netdev ) ) {
+ printf ( "%s: link not up\n", argv[0] );
+ return 1;
+ }
+
+ /* Load network settings from device. We keep the MAC address,
+ * IP address, and UDP port. The MAC and IP could be fetched
+ * from the network device each time they are used in rx/tx.
+ * Storing a separate copy makes it possible to use different
+ * MAC/IP settings than the network stack. */
+ memcpy ( source_eth, netdev->ll_addr, ETH_ALEN );
+ source_addr.sin_port = htons ( 43770 ); /* TODO default port */
+ settings = netdev_settings ( netdev );
+ fetch_ipv4_setting ( settings, &ip_setting, &source_addr.sin_addr );
+ if ( source_addr.sin_addr.s_addr == 0 ) {
+ printf ( "%s: no IP address configured\n", argv[0] );
+ return 1;
+ }
+
+ return 0;
+}
+
+struct gdb_transport udp_gdb_transport __gdb_transport = {
+ .name = "udp",
+ .init = gdbudp_init,
+ .send = gdbudp_send,
+ .recv = gdbudp_recv,
+};
diff --git a/src/hci/commands/gdbstub_cmd.c b/src/hci/commands/gdbstub_cmd.c
new file mode 100644
index 00000000..74167525
--- /dev/null
+++ b/src/hci/commands/gdbstub_cmd.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2008 Stefan Hajnoczi <stefanha@gmail.com>.
+ *
+ * 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 2 of the
+ * License, or 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <getopt.h>
+#include <gpxe/command.h>
+#include <gpxe/gdbstub.h>
+
+/** @file
+ *
+ * GDB stub command
+ *
+ */
+
+/**
+ * "gdbstub" command syntax message
+ *
+ * @v argv Argument list
+ */
+static void gdbstub_syntax ( char **argv ) {
+ printf ( "Usage:\n"
+ " %s <transport> [<options>...]\n"
+ "\n"
+ "Start remote debugging using one of the following transports:\n"
+ " serial use serial port (if compiled in)\n"
+ " udp <interface> use UDP over network interface (if compiled in)\n",
+ argv[0] );
+}
+
+/**
+ * The "gdbstub" command
+ *
+ * @v argc Argument count
+ * @v argv Argument list
+ * @ret rc Exit code
+ */
+static int gdbstub_exec ( int argc, char **argv ) {
+ static struct option longopts[] = {
+ { "help", 0, NULL, 'h' },
+ { NULL, 0, NULL, 0 },
+ };
+ const char *trans_name;
+ struct gdb_transport *trans;
+ int c;
+
+ /* Parse options */
+ while ( ( c = getopt_long ( argc, argv, "h", longopts, NULL ) ) >= 0 ){
+ switch ( c ) {
+ case 'h':
+ /* Display help text */
+ default:
+ /* Unrecognised/invalid option */
+ gdbstub_syntax ( argv );
+ return 1;
+ }
+ }
+
+ /* At least one argument */
+ if ( optind == argc ) {
+ gdbstub_syntax ( argv );
+ return 1;
+ }
+
+ trans_name = argv[optind++];
+
+ /* Initialise transport */
+ trans = find_gdb_transport ( trans_name );
+ if ( !trans ) {
+ printf ( "%s: no such transport (is it compiled in?)\n", trans_name );
+ return 1;
+ }
+
+ if ( trans->init ) {
+ if ( trans->init ( argc - optind, &argv[optind] ) != 0 ) {
+ return 1;
+ }
+ }
+
+ /* Enter GDB stub */
+ gdbstub_start ( trans );
+ return 0;
+}
+
+/** GDB stub commands */
+struct command gdbstub_commands[] __command = {
+ {
+ .name = "gdbstub",
+ .exec = gdbstub_exec,
+ },
+};
diff --git a/src/include/gpxe/gdbstub.h b/src/include/gpxe/gdbstub.h
new file mode 100644
index 00000000..adc7e382
--- /dev/null
+++ b/src/include/gpxe/gdbstub.h
@@ -0,0 +1,64 @@
+#ifndef _GPXE_GDBSTUB_H
+#define _GPXE_GDBSTUB_H
+
+/** @file
+ *
+ * GDB remote debugging
+ *
+ */
+
+#include <stdint.h>
+#include <gpxe/tables.h>
+
+/**
+ * A transport mechanism for the GDB protocol
+ *
+ */
+struct gdb_transport {
+ /** Transport name */
+ const char *name;
+ /**
+ * Set up the transport given a list of arguments
+ *
+ * @v argc Number of arguments
+ * @v argv Argument list
+ * @ret Return status code
+ *
+ * Note that arguments start at argv[0].
+ */
+ int ( * init ) ( int argc, char **argv );
+ /**
+ * Perform a blocking read
+ *
+ * @v buf Buffer
+ * @v len Size of buffer
+ * @ret Number of bytes read into buffer
+ */
+ size_t ( * recv ) ( char *buf, size_t len );
+ /**
+ * Write, may block
+ *
+ * @v buf Buffer
+ * @v len Size of buffer
+ */
+ void ( * send ) ( const char *buf, size_t len );
+};
+
+#define __gdb_transport __table ( struct gdb_transport, gdb_transports, 01 )
+
+/**
+ * Look up GDB transport by name
+ *
+ * @v name Name of transport
+ * @ret GDB transport or NULL
+ */
+extern struct gdb_transport *find_gdb_transport ( const char *name );
+
+/**
+ * Break into the debugger using the given transport
+ *
+ * @v trans GDB transport
+ */
+extern void gdbstub_start ( struct gdb_transport *trans );
+
+#endif /* _GPXE_GDBSTUB_H */
diff --git a/src/tests/gdbstub_test.gdb b/src/tests/gdbstub_test.gdb
index c0c59644..c86d4f2a 100755
--- a/src/tests/gdbstub_test.gdb
+++ b/src/tests/gdbstub_test.gdb
@@ -3,16 +3,16 @@
# Run:
# make bin/gpxe.hd.tmp
# make
-# tests/gdbstub_test.gdb
+# gdb
+# (gdb) target remote :TCPPORT
+# OR
+# (gdb) target remote udp:IP:UDPPORT
+# (gdb) source tests/gdbstub_test.gdb
define gpxe_load_symbols
file bin/gpxe.hd.tmp
end
-define gpxe_connect
- target remote localhost:4444
-end
-
define gpxe_assert
if $arg0 != $arg1
echo FAIL $arg2\n
@@ -78,7 +78,6 @@ define gpxe_test_step
end
gpxe_load_symbols
-gpxe_connect
gpxe_start_tests
gpxe_test_regs_read
gpxe_test_regs_write