Browse Source

[GDB] Remote debugging over UDP

This commit implements GDB over UDP.  Using UDP is more complex than
serial and has required some restructuring.

The GDB stub is now built using one or both of GDBSERIAL and GDBUDP
config.h options.

To enter the debugger, execute the gPXE shell command:
gdbstub <transport> [<options>...]

Where <transport> is "serial" or "udp".  For "udp", the name of a
configured network device is required:
gdbstub udp net0

The GDB stub listens on UDP port 43770 by default.
tags/v0.9.4
Stefan Hajnoczi 16 years ago
parent
commit
6e670b5f38

+ 3
- 1
src/config.h View File

@@ -163,7 +163,9 @@
163 163
 #undef	BUILD_ID		/* Include a custom build ID string,
164 164
 				 * e.g "test-foo" */
165 165
 #undef	NULL_TRAP		/* Attempt to catch NULL function calls */
166
-#undef	GDBSTUB			/* Remote GDB debugging */
166
+#undef	GDBSERIAL		/* Remote GDB debugging over serial */
167
+#undef	GDBUDP			/* Remote GDB debugging over UDP
168
+				 * (both may be set) */
167 169
 
168 170
 /* @END general.h */
169 171
 

+ 8
- 1
src/core/config.c View File

@@ -195,6 +195,13 @@ REQUIRE_OBJECT ( sanboot_cmd );
195 195
 #ifdef NULL_TRAP
196 196
 REQUIRE_OBJECT ( nulltrap );
197 197
 #endif
198
-#ifdef GDBSTUB
198
+#ifdef GDBSERIAL
199 199
 REQUIRE_OBJECT ( gdbidt );
200
+REQUIRE_OBJECT ( gdbserial );
201
+REQUIRE_OBJECT ( gdbstub_cmd );
202
+#endif
203
+#ifdef GDBUDP
204
+REQUIRE_OBJECT ( gdbidt );
205
+REQUIRE_OBJECT ( gdbudp );
206
+REQUIRE_OBJECT ( gdbstub_cmd );
200 207
 #endif

+ 41
- 0
src/core/gdbserial.c View File

@@ -0,0 +1,41 @@
1
+/*
2
+ * Copyright (C) 2008 Stefan Hajnoczi <stefanha@gmail.com>.
3
+ *
4
+ * This program is free software; you can redistribute it and/or
5
+ * modify it under the terms of the GNU General Public License as
6
+ * published by the Free Software Foundation; either version 2 of the
7
+ * License, or any later version.
8
+ *
9
+ * This program is distributed in the hope that it will be useful, but
10
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
+ * General Public License for more details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License
15
+ * along with this program; if not, write to the Free Software
16
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
+ */
18
+
19
+#include <assert.h>
20
+#include <gpxe/serial.h>
21
+#include <gpxe/gdbstub.h>
22
+
23
+struct gdb_transport serial_gdb_transport __gdb_transport;
24
+
25
+static size_t gdbserial_recv ( char *buf, size_t len ) {
26
+	assert ( len > 0 );
27
+	buf [ 0 ] = serial_getc();
28
+	return 1;
29
+}
30
+
31
+static void gdbserial_send ( const char *buf, size_t len ) {
32
+	while ( len-- > 0 ) {
33
+		serial_putc ( *buf++ );
34
+	}
35
+}
36
+
37
+struct gdb_transport serial_gdb_transport __gdb_transport = {
38
+	.name = "serial",
39
+	.recv = gdbserial_recv,
40
+	.send = gdbserial_send,
41
+};

+ 47
- 42
src/core/gdbstub.c View File

@@ -24,22 +24,24 @@
24 24
  */
25 25
 
26 26
 #include <stdlib.h>
27
-#include <stddef.h>
28 27
 #include <stdio.h>
28
+#include <string.h>
29 29
 #include <ctype.h>
30 30
 #include <byteswap.h>
31
-#include <gpxe/process.h>
32
-#include <gpxe/serial.h>
31
+#include <gpxe/gdbstub.h>
33 32
 #include "gdbmach.h"
34 33
 
35 34
 enum {
36
-	POSIX_EINVAL = 0x1c /* used to report bad arguments to GDB */
35
+	POSIX_EINVAL = 0x1c,  /* used to report bad arguments to GDB */
36
+	SIZEOF_PAYLOAD = 256, /* buffer size of GDB payload data */
37 37
 };
38 38
 
39 39
 struct gdbstub {
40
+	struct gdb_transport *trans;
41
+	int exit_handler; /* leave interrupt handler */
42
+
40 43
 	int signo;
41 44
 	gdbreg_t *regs;
42
-	int exit_handler; /* leave interrupt handler */
43 45
 
44 46
 	void ( * parse ) ( struct gdbstub *stub, char ch );
45 47
 	uint8_t cksum1;
@@ -47,10 +49,15 @@ struct gdbstub {
47 49
 	/* Buffer for payload data when parsing a packet.  Once the
48 50
 	 * packet has been received, this buffer is used to hold
49 51
 	 * the reply payload. */
50
-	char payload [ 256 ];
51
-	int len;
52
+	char buf [ SIZEOF_PAYLOAD + 4 ]; /* $...PAYLOAD...#XX */
53
+	char *payload;                   /* start of payload */
54
+	int len;                         /* length of payload */
52 55
 };
53 56
 
57
+/* Transports */
58
+static struct gdb_transport gdb_transport_start[0] __table_start ( struct gdb_transport, gdb_transports );
59
+static struct gdb_transport gdb_transport_end[0] __table_end ( struct gdb_transport, gdb_transports );
60
+
54 61
 /* Packet parser states */
55 62
 static void gdbstub_state_new ( struct gdbstub *stub, char ch );
56 63
 static void gdbstub_state_data ( struct gdbstub *stub, char ch );
@@ -132,29 +139,13 @@ static uint8_t gdbstub_cksum ( char *data, int len ) {
132 139
 	return cksum;
133 140
 }
134 141
 
135
-static int gdbstub_getchar ( struct gdbstub *stub ) {
136
-	if ( stub->exit_handler ) {
137
-		return -1;
138
-	}
139
-	return serial_getc();
140
-}
141
-
142
-static void gdbstub_putchar ( struct gdbstub * stub __unused, char ch ) {
143
-	serial_putc ( ch );
144
-}
145
-
146 142
 static void gdbstub_tx_packet ( struct gdbstub *stub ) {
147 143
 	uint8_t cksum = gdbstub_cksum ( stub->payload, stub->len );
148
-	int i;
149
-
150
-	gdbstub_putchar ( stub, '$' );
151
-	for ( i = 0; i < stub->len; i++ ) {
152
-		gdbstub_putchar ( stub, stub->payload [ i ] );
153
-	}
154
-	gdbstub_putchar ( stub, '#' );
155
-	gdbstub_putchar ( stub, gdbstub_to_hex_digit ( cksum >> 4 ) );
156
-	gdbstub_putchar ( stub, gdbstub_to_hex_digit ( cksum ) );
157
-
144
+	stub->buf [ 0 ] = '$';
145
+	stub->buf [ stub->len + 1 ] = '#';
146
+	stub->buf [ stub->len + 2 ] = gdbstub_to_hex_digit ( cksum >> 4 );
147
+	stub->buf [ stub->len + 3 ] = gdbstub_to_hex_digit ( cksum );
148
+	stub->trans->send ( stub->buf, stub->len + 4 );
158 149
 	stub->parse = gdbstub_state_wait_ack;
159 150
 }
160 151
 
@@ -229,7 +220,7 @@ static void gdbstub_read_mem ( struct gdbstub *stub ) {
229 220
 		gdbstub_send_errno ( stub, POSIX_EINVAL );
230 221
 		return;
231 222
 	}
232
-	args [ 1 ] = ( args [ 1 ] < sizeof stub->payload / 2 ) ? args [ 1 ] : sizeof stub->payload / 2;
223
+	args [ 1 ] = ( args [ 1 ] < SIZEOF_PAYLOAD / 2 ) ? args [ 1 ] : SIZEOF_PAYLOAD / 2;
233 224
 	gdbstub_to_hex_buf ( stub->payload, ( char * ) args [ 0 ], args [ 1 ] );
234 225
 	stub->len = args [ 1 ] * 2;
235 226
 	gdbstub_tx_packet ( stub );
@@ -306,7 +297,7 @@ static void gdbstub_state_data ( struct gdbstub *stub, char ch ) {
306 297
 		stub->len = 0; /* retry new packet */
307 298
 	} else {
308 299
 		/* If the length exceeds our buffer, let the checksum fail */
309
-		if ( stub->len < ( int ) sizeof stub->payload ) {
300
+		if ( stub->len < SIZEOF_PAYLOAD ) {
310 301
 			stub->payload [ stub->len++ ] = ch;
311 302
 		}
312 303
 	}
@@ -325,12 +316,12 @@ static void gdbstub_state_cksum2 ( struct gdbstub *stub, char ch ) {
325 316
 	their_cksum = stub->cksum1 + gdbstub_from_hex_digit ( ch );
326 317
 	our_cksum = gdbstub_cksum ( stub->payload, stub->len );
327 318
 	if ( their_cksum == our_cksum ) {
328
-		gdbstub_putchar ( stub, '+' );
319
+		stub->trans->send ( "+", 1 );
329 320
 		if ( stub->len > 0 ) {
330 321
 			gdbstub_rx_packet ( stub );
331 322
 		}
332 323
 	} else {
333
-		gdbstub_putchar ( stub, '-' );
324
+		stub->trans->send ( "-", 1 );
334 325
 	}
335 326
 }
336 327
 
@@ -351,23 +342,37 @@ static struct gdbstub stub = {
351 342
 };
352 343
 
353 344
 __cdecl void gdbstub_handler ( int signo, gdbreg_t *regs ) {
354
-	int ch;
345
+	char packet [ SIZEOF_PAYLOAD + 4 ];
346
+	size_t len, i;
347
+
348
+	/* A transport must be set up */
349
+	if ( !stub.trans ) {
350
+		return;
351
+	}
352
+
355 353
 	stub.signo = signo;
356 354
 	stub.regs = regs;
357 355
 	stub.exit_handler = 0;
358 356
 	gdbstub_report_signal ( &stub );
359
-	while ( ( ch = gdbstub_getchar( &stub ) ) != -1 ) {
360
-		gdbstub_parse ( &stub, ch );
357
+	while ( !stub.exit_handler && ( len = stub.trans->recv ( packet, sizeof ( packet ) ) ) > 0 ) {
358
+		for ( i = 0; i < len; i++ ) {
359
+			gdbstub_parse ( &stub, packet [ i ] );
360
+		}
361 361
 	}
362 362
 }
363 363
 
364
-/* Activity monitor to detect packets from GDB when we are not active */
365
-static void gdbstub_activity_step ( struct process *process __unused ) {
366
-	if ( serial_ischar() ) {
367
-		gdbmach_breakpoint();
364
+struct gdb_transport *find_gdb_transport ( const char *name ) {
365
+	struct gdb_transport *trans;
366
+	for ( trans = gdb_transport_start; trans < gdb_transport_end; trans++ ) {
367
+		if ( strcmp ( trans->name, name ) == 0 ) {
368
+			return trans;
369
+		}
368 370
 	}
371
+	return NULL;
369 372
 }
370 373
 
371
-struct process gdbstub_activity_process __permanent_process = {
372
-	.step = gdbstub_activity_step,
373
-};
374
+void gdbstub_start ( struct gdb_transport *trans ) {
375
+	stub.trans = trans;
376
+	stub.payload = &stub.buf [ 1 ];
377
+	gdbmach_breakpoint();
378
+}

+ 228
- 0
src/core/gdbudp.c View File

@@ -0,0 +1,228 @@
1
+/*
2
+ * Copyright (C) 2008 Stefan Hajnoczi <stefanha@gmail.com>.
3
+ *
4
+ * This program is free software; you can redistribute it and/or
5
+ * modify it under the terms of the GNU General Public License as
6
+ * published by the Free Software Foundation; either version 2 of the
7
+ * License, or any later version.
8
+ *
9
+ * This program is distributed in the hope that it will be useful, but
10
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
+ * General Public License for more details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License
15
+ * along with this program; if not, write to the Free Software
16
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
+ */
18
+
19
+#include <stdio.h>
20
+#include <string.h>
21
+#include <byteswap.h>
22
+#include <gpxe/iobuf.h>
23
+#include <gpxe/in.h>
24
+#include <gpxe/if_arp.h>
25
+#include <gpxe/if_ether.h>
26
+#include <gpxe/ip.h>
27
+#include <gpxe/udp.h>
28
+#include <gpxe/netdevice.h>
29
+#include <gpxe/gdbstub.h>
30
+
31
+struct gdb_transport udp_gdb_transport __gdb_transport;
32
+
33
+static struct net_device *netdev;
34
+static uint8_t dest_eth[ETH_ALEN];
35
+static uint8_t source_eth[ETH_ALEN];
36
+static struct sockaddr_in dest_addr;
37
+static struct sockaddr_in source_addr;
38
+
39
+static void gdbudp_ensure_netdev_open ( struct net_device *netdev ) {
40
+	if ( ( netdev->state & NETDEV_OPEN) == 0 ) {
41
+		netdev_open ( netdev );
42
+	}
43
+	/* TODO forcing the netdev to be open is useful when
44
+	 * gPXE closes the netdev between breakpoints.  Should
45
+	 * we restore the state of the netdev, i.e. closed,
46
+	 * before leaving the interrupt handler? */
47
+}
48
+
49
+static size_t gdbudp_recv ( char *buf, size_t len ) {
50
+	struct io_buffer *iob;
51
+	struct ethhdr *ethhdr;
52
+	struct arphdr *arphdr;
53
+	struct iphdr *iphdr;
54
+	struct udp_header *udphdr;
55
+	size_t payload_len;
56
+
57
+	assert ( netdev );
58
+	gdbudp_ensure_netdev_open ( netdev );
59
+
60
+	for ( ; ; ) {
61
+		while ( ( iob = netdev_rx_dequeue ( netdev ) ) != NULL ) {
62
+			if ( iob_len ( iob ) > sizeof ( *ethhdr ) + sizeof ( *iphdr ) + sizeof ( *udphdr ) + len ) {
63
+				goto bad_packet;
64
+			}
65
+
66
+			/* Ethernet header */
67
+			ethhdr = iob->data;
68
+			iob_pull ( iob, sizeof ( *ethhdr ) );
69
+			if ( ethhdr->h_protocol == htons ( ETH_P_ARP ) ) {
70
+				/* Handle ARP requests so the client can connect to us */
71
+				arphdr = iob->data;
72
+				if ( arphdr->ar_hrd != htons ( ARPHRD_ETHER ) ||
73
+						arphdr->ar_pro != htons ( ETH_P_IP ) ||
74
+						arphdr->ar_hln != ETH_ALEN ||
75
+						arphdr->ar_pln != sizeof ( struct in_addr ) ||
76
+						arphdr->ar_op != htons ( ARPOP_REQUEST ) ||
77
+						memcmp ( arp_target_pa ( arphdr ), &source_addr.sin_addr.s_addr, sizeof ( struct in_addr ) ) ) {
78
+					goto bad_packet;
79
+				}
80
+
81
+				/* Generate an ARP reply */
82
+				arphdr->ar_op = htons ( ARPOP_REPLY );
83
+				memswap ( arp_sender_pa ( arphdr ), arp_target_pa ( arphdr ), sizeof ( struct in_addr ) );
84
+				memcpy ( arp_target_ha ( arphdr ), arp_sender_ha ( arphdr ), ETH_ALEN );
85
+				memcpy ( arp_sender_ha ( arphdr ), source_eth, ETH_ALEN );
86
+
87
+				/* Fix up ethernet header */
88
+				ethhdr = iob_push ( iob, sizeof ( *ethhdr ) );
89
+				memswap ( ethhdr->h_source, ethhdr->h_dest, ETH_ALEN );
90
+
91
+				netdev_tx ( netdev, iob );
92
+				continue; /* no need to free iob */
93
+			}
94
+			if ( ethhdr->h_protocol != htons ( ETH_P_IP ) ) {
95
+				goto bad_packet;
96
+			}
97
+
98
+			/* IP header */
99
+			iphdr = iob->data;
100
+			iob_pull ( iob, sizeof ( *iphdr ) );
101
+			if ( iphdr->protocol != IP_UDP || iphdr->dest.s_addr != source_addr.sin_addr.s_addr ) {
102
+				goto bad_packet;
103
+			}
104
+
105
+			/* UDP header */
106
+			udphdr = iob->data;
107
+			if ( udphdr->dest != source_addr.sin_port ) {
108
+				goto bad_packet;
109
+			}
110
+
111
+			/* Learn the remote connection details */
112
+			memcpy ( dest_eth, ethhdr->h_source, ETH_ALEN );
113
+			dest_addr.sin_addr.s_addr = iphdr->src.s_addr;
114
+			dest_addr.sin_port = udphdr->src;
115
+
116
+			/* Payload */
117
+			payload_len = ntohs ( udphdr->len );
118
+			if ( payload_len < sizeof ( *udphdr ) ||
119
+					payload_len > iob_len ( iob ) ) {
120
+				goto bad_packet;
121
+			}
122
+			payload_len -= sizeof ( *udphdr );
123
+			iob_pull ( iob, sizeof ( *udphdr ) );
124
+			memcpy ( buf, iob->data, payload_len );
125
+
126
+			free_iob ( iob );
127
+			return payload_len;
128
+
129
+bad_packet:
130
+			free_iob ( iob );
131
+		}
132
+		netdev_poll ( netdev );
133
+	}
134
+}
135
+
136
+static void gdbudp_send ( const char *buf, size_t len ) {
137
+	struct io_buffer *iob;
138
+	struct ethhdr *ethhdr;
139
+	struct iphdr *iphdr;
140
+	struct udp_header *udphdr;
141
+
142
+	/* Check that we are connected */
143
+	if ( dest_addr.sin_port == 0 ) {
144
+		return;
145
+	}
146
+
147
+	assert ( netdev );
148
+	gdbudp_ensure_netdev_open ( netdev );
149
+
150
+	iob = alloc_iob ( sizeof ( *ethhdr ) + sizeof ( *iphdr ) + sizeof ( *udphdr ) + len );
151
+	if ( !iob ) {
152
+		return;
153
+	}
154
+
155
+	/* Payload */
156
+	iob_reserve ( iob, sizeof ( *ethhdr ) + sizeof ( *iphdr ) + sizeof ( *udphdr ) );
157
+	memcpy ( iob_put ( iob, len ), buf, len );
158
+
159
+	/* UDP header */
160
+	udphdr = iob_push ( iob, sizeof ( *udphdr ) );
161
+	udphdr->src = source_addr.sin_port;
162
+	udphdr->dest = dest_addr.sin_port;
163
+	udphdr->len = htons ( iob_len ( iob ) );
164
+	udphdr->chksum = 0; /* optional and we are not using it */
165
+
166
+	/* IP header */
167
+	iphdr = iob_push ( iob, sizeof ( *iphdr ) );
168
+	memset ( iphdr, 0, sizeof ( *iphdr ) );
169
+	iphdr->verhdrlen = ( IP_VER | ( sizeof ( *iphdr ) / 4 ) );
170
+	iphdr->service = IP_TOS;
171
+	iphdr->len = htons ( iob_len ( iob ) );	
172
+	iphdr->ttl = IP_TTL;
173
+	iphdr->protocol = IP_UDP;
174
+	iphdr->dest.s_addr = dest_addr.sin_addr.s_addr;
175
+	iphdr->src.s_addr = source_addr.sin_addr.s_addr;
176
+	iphdr->chksum = tcpip_chksum ( iphdr, sizeof ( *iphdr ) );
177
+
178
+	/* Ethernet header */
179
+	ethhdr = iob_push ( iob, sizeof ( *ethhdr ) );
180
+	memcpy ( ethhdr->h_dest, dest_eth, ETH_ALEN );
181
+	memcpy ( ethhdr->h_source, source_eth, ETH_ALEN );
182
+	ethhdr->h_protocol = htons ( ETH_P_IP );
183
+
184
+	netdev_tx ( netdev, iob );
185
+}
186
+
187
+static int gdbudp_init ( int argc, char **argv ) {
188
+	struct settings *settings;
189
+
190
+	if ( argc != 1 ) {
191
+		printf ( "udp: missing <interface> argument\n" );
192
+		return 1;
193
+	}
194
+
195
+	netdev = find_netdev ( argv[0] );
196
+	if ( !netdev ) {
197
+		printf ( "%s: no such interface\n", argv[0] );
198
+		return 1;
199
+	}
200
+
201
+	if ( !netdev_link_ok ( netdev ) ) {
202
+		printf ( "%s: link not up\n", argv[0] );
203
+		return 1;
204
+	}
205
+
206
+	/* Load network settings from device.  We keep the MAC address,
207
+	 * IP address, and UDP port.  The MAC and IP could be fetched
208
+	 * from the network device each time they are used in rx/tx.
209
+	 * Storing a separate copy makes it possible to use different
210
+	 * MAC/IP settings than the network stack. */
211
+	memcpy ( source_eth, netdev->ll_addr, ETH_ALEN );
212
+	source_addr.sin_port = htons ( 43770 ); /* TODO default port */
213
+	settings = netdev_settings ( netdev );
214
+	fetch_ipv4_setting ( settings, &ip_setting, &source_addr.sin_addr );
215
+	if ( source_addr.sin_addr.s_addr == 0 ) {
216
+		printf ( "%s: no IP address configured\n", argv[0] );
217
+		return 1;
218
+	}
219
+
220
+	return 0;
221
+}
222
+
223
+struct gdb_transport udp_gdb_transport __gdb_transport = {
224
+	.name = "udp",
225
+	.init = gdbudp_init,
226
+	.send = gdbudp_send,
227
+	.recv = gdbudp_recv,
228
+};

+ 105
- 0
src/hci/commands/gdbstub_cmd.c View File

@@ -0,0 +1,105 @@
1
+/*
2
+ * Copyright (C) 2008 Stefan Hajnoczi <stefanha@gmail.com>.
3
+ *
4
+ * This program is free software; you can redistribute it and/or
5
+ * modify it under the terms of the GNU General Public License as
6
+ * published by the Free Software Foundation; either version 2 of the
7
+ * License, or any later version.
8
+ *
9
+ * This program is distributed in the hope that it will be useful, but
10
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
+ * General Public License for more details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License
15
+ * along with this program; if not, write to the Free Software
16
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
+ */
18
+
19
+#include <stdio.h>
20
+#include <getopt.h>
21
+#include <gpxe/command.h>
22
+#include <gpxe/gdbstub.h>
23
+
24
+/** @file
25
+ *
26
+ * GDB stub command
27
+ *
28
+ */
29
+
30
+/**
31
+ * "gdbstub" command syntax message
32
+ *
33
+ * @v argv		Argument list
34
+ */
35
+static void gdbstub_syntax ( char **argv ) {
36
+	printf ( "Usage:\n"
37
+		 "  %s <transport> [<options>...]\n"
38
+		 "\n"
39
+		 "Start remote debugging using one of the following transports:\n"
40
+		 "  serial           use serial port (if compiled in)\n"
41
+		 "  udp <interface>  use UDP over network interface (if compiled in)\n",
42
+		 argv[0] );
43
+}
44
+
45
+/**
46
+ * The "gdbstub" command
47
+ *
48
+ * @v argc		Argument count
49
+ * @v argv		Argument list
50
+ * @ret rc		Exit code
51
+ */
52
+static int gdbstub_exec ( int argc, char **argv ) {
53
+	static struct option longopts[] = {
54
+		{ "help", 0, NULL, 'h' },
55
+		{ NULL, 0, NULL, 0 },
56
+	};
57
+	const char *trans_name;
58
+	struct gdb_transport *trans;
59
+	int c;
60
+
61
+	/* Parse options */
62
+	while ( ( c = getopt_long ( argc, argv, "h", longopts, NULL ) ) >= 0 ){
63
+		switch ( c ) {
64
+		case 'h':
65
+			/* Display help text */
66
+		default:
67
+			/* Unrecognised/invalid option */
68
+			gdbstub_syntax ( argv );
69
+			return 1;
70
+		}
71
+	}
72
+
73
+	/* At least one argument */
74
+	if ( optind == argc ) {
75
+		gdbstub_syntax ( argv );
76
+		return 1;
77
+	}
78
+
79
+	trans_name = argv[optind++];
80
+
81
+	/* Initialise transport */
82
+	trans = find_gdb_transport ( trans_name );
83
+	if ( !trans ) {
84
+		printf ( "%s: no such transport (is it compiled in?)\n", trans_name );
85
+		return 1;
86
+	}
87
+
88
+	if ( trans->init ) {
89
+		if ( trans->init ( argc - optind, &argv[optind] ) != 0 ) {
90
+			return 1;
91
+		}
92
+	}
93
+
94
+	/* Enter GDB stub */
95
+	gdbstub_start ( trans );
96
+	return 0;
97
+}
98
+
99
+/** GDB stub commands */
100
+struct command gdbstub_commands[] __command = {
101
+	{
102
+		.name = "gdbstub",
103
+		.exec = gdbstub_exec,
104
+	},
105
+};

+ 64
- 0
src/include/gpxe/gdbstub.h View File

@@ -0,0 +1,64 @@
1
+#ifndef _GPXE_GDBSTUB_H
2
+#define _GPXE_GDBSTUB_H
3
+
4
+/** @file
5
+ *
6
+ * GDB remote debugging
7
+ *
8
+ */
9
+
10
+#include <stdint.h>
11
+#include <gpxe/tables.h>
12
+
13
+/**
14
+ * A transport mechanism for the GDB protocol
15
+ *
16
+ */
17
+struct gdb_transport {
18
+	/** Transport name */
19
+	const char *name;
20
+	/**
21
+	 * Set up the transport given a list of arguments
22
+	 *
23
+	 * @v argc Number of arguments
24
+	 * @v argv Argument list
25
+	 * @ret Return status code
26
+	 *
27
+	 * Note that arguments start at argv[0].
28
+	 */
29
+	int ( * init ) ( int argc, char **argv );
30
+	/**
31
+	 * Perform a blocking read
32
+	 *
33
+	 * @v buf Buffer
34
+	 * @v len Size of buffer
35
+	 * @ret Number of bytes read into buffer
36
+	 */
37
+	size_t ( * recv ) ( char *buf, size_t len );
38
+	/**
39
+	 * Write, may block
40
+	 *
41
+	 * @v buf Buffer
42
+	 * @v len Size of buffer
43
+	 */
44
+	void ( * send ) ( const char *buf, size_t len );
45
+};
46
+
47
+#define __gdb_transport __table ( struct gdb_transport, gdb_transports, 01 )
48
+
49
+/**
50
+ * Look up GDB transport by name
51
+ *
52
+ * @v name Name of transport
53
+ * @ret GDB transport or NULL
54
+ */
55
+extern struct gdb_transport *find_gdb_transport ( const char *name );
56
+
57
+/**
58
+ * Break into the debugger using the given transport
59
+ *
60
+ * @v trans GDB transport
61
+ */
62
+extern void gdbstub_start ( struct gdb_transport *trans );
63
+
64
+#endif /* _GPXE_GDBSTUB_H */

+ 5
- 6
src/tests/gdbstub_test.gdb View File

@@ -3,16 +3,16 @@
3 3
 # Run:
4 4
 #   make bin/gpxe.hd.tmp
5 5
 #   make
6
-#   tests/gdbstub_test.gdb
6
+#   gdb
7
+#   (gdb) target remote :TCPPORT
8
+#   OR
9
+#   (gdb) target remote udp:IP:UDPPORT
10
+#   (gdb) source tests/gdbstub_test.gdb
7 11
 
8 12
 define gpxe_load_symbols
9 13
 	file bin/gpxe.hd.tmp
10 14
 end
11 15
 
12
-define gpxe_connect
13
-	target remote localhost:4444
14
-end
15
-
16 16
 define gpxe_assert
17 17
 	if $arg0 != $arg1
18 18
 		echo FAIL $arg2\n
@@ -78,7 +78,6 @@ define gpxe_test_step
78 78
 end
79 79
 
80 80
 gpxe_load_symbols
81
-gpxe_connect
82 81
 gpxe_start_tests
83 82
 gpxe_test_regs_read
84 83
 gpxe_test_regs_write

Loading…
Cancel
Save