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
 #undef	BUILD_ID		/* Include a custom build ID string,
163
 #undef	BUILD_ID		/* Include a custom build ID string,
164
 				 * e.g "test-foo" */
164
 				 * e.g "test-foo" */
165
 #undef	NULL_TRAP		/* Attempt to catch NULL function calls */
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
 /* @END general.h */
170
 /* @END general.h */
169
 
171
 

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

195
 #ifdef NULL_TRAP
195
 #ifdef NULL_TRAP
196
 REQUIRE_OBJECT ( nulltrap );
196
 REQUIRE_OBJECT ( nulltrap );
197
 #endif
197
 #endif
198
-#ifdef GDBSTUB
198
+#ifdef GDBSERIAL
199
 REQUIRE_OBJECT ( gdbidt );
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
 #endif
207
 #endif

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

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
  */
24
  */
25
 
25
 
26
 #include <stdlib.h>
26
 #include <stdlib.h>
27
-#include <stddef.h>
28
 #include <stdio.h>
27
 #include <stdio.h>
28
+#include <string.h>
29
 #include <ctype.h>
29
 #include <ctype.h>
30
 #include <byteswap.h>
30
 #include <byteswap.h>
31
-#include <gpxe/process.h>
32
-#include <gpxe/serial.h>
31
+#include <gpxe/gdbstub.h>
33
 #include "gdbmach.h"
32
 #include "gdbmach.h"
34
 
33
 
35
 enum {
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
 struct gdbstub {
39
 struct gdbstub {
40
+	struct gdb_transport *trans;
41
+	int exit_handler; /* leave interrupt handler */
42
+
40
 	int signo;
43
 	int signo;
41
 	gdbreg_t *regs;
44
 	gdbreg_t *regs;
42
-	int exit_handler; /* leave interrupt handler */
43
 
45
 
44
 	void ( * parse ) ( struct gdbstub *stub, char ch );
46
 	void ( * parse ) ( struct gdbstub *stub, char ch );
45
 	uint8_t cksum1;
47
 	uint8_t cksum1;
47
 	/* Buffer for payload data when parsing a packet.  Once the
49
 	/* Buffer for payload data when parsing a packet.  Once the
48
 	 * packet has been received, this buffer is used to hold
50
 	 * packet has been received, this buffer is used to hold
49
 	 * the reply payload. */
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
 /* Packet parser states */
61
 /* Packet parser states */
55
 static void gdbstub_state_new ( struct gdbstub *stub, char ch );
62
 static void gdbstub_state_new ( struct gdbstub *stub, char ch );
56
 static void gdbstub_state_data ( struct gdbstub *stub, char ch );
63
 static void gdbstub_state_data ( struct gdbstub *stub, char ch );
132
 	return cksum;
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
 static void gdbstub_tx_packet ( struct gdbstub *stub ) {
142
 static void gdbstub_tx_packet ( struct gdbstub *stub ) {
147
 	uint8_t cksum = gdbstub_cksum ( stub->payload, stub->len );
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
 	stub->parse = gdbstub_state_wait_ack;
149
 	stub->parse = gdbstub_state_wait_ack;
159
 }
150
 }
160
 
151
 
229
 		gdbstub_send_errno ( stub, POSIX_EINVAL );
220
 		gdbstub_send_errno ( stub, POSIX_EINVAL );
230
 		return;
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
 	gdbstub_to_hex_buf ( stub->payload, ( char * ) args [ 0 ], args [ 1 ] );
224
 	gdbstub_to_hex_buf ( stub->payload, ( char * ) args [ 0 ], args [ 1 ] );
234
 	stub->len = args [ 1 ] * 2;
225
 	stub->len = args [ 1 ] * 2;
235
 	gdbstub_tx_packet ( stub );
226
 	gdbstub_tx_packet ( stub );
306
 		stub->len = 0; /* retry new packet */
297
 		stub->len = 0; /* retry new packet */
307
 	} else {
298
 	} else {
308
 		/* If the length exceeds our buffer, let the checksum fail */
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
 			stub->payload [ stub->len++ ] = ch;
301
 			stub->payload [ stub->len++ ] = ch;
311
 		}
302
 		}
312
 	}
303
 	}
325
 	their_cksum = stub->cksum1 + gdbstub_from_hex_digit ( ch );
316
 	their_cksum = stub->cksum1 + gdbstub_from_hex_digit ( ch );
326
 	our_cksum = gdbstub_cksum ( stub->payload, stub->len );
317
 	our_cksum = gdbstub_cksum ( stub->payload, stub->len );
327
 	if ( their_cksum == our_cksum ) {
318
 	if ( their_cksum == our_cksum ) {
328
-		gdbstub_putchar ( stub, '+' );
319
+		stub->trans->send ( "+", 1 );
329
 		if ( stub->len > 0 ) {
320
 		if ( stub->len > 0 ) {
330
 			gdbstub_rx_packet ( stub );
321
 			gdbstub_rx_packet ( stub );
331
 		}
322
 		}
332
 	} else {
323
 	} else {
333
-		gdbstub_putchar ( stub, '-' );
324
+		stub->trans->send ( "-", 1 );
334
 	}
325
 	}
335
 }
326
 }
336
 
327
 
351
 };
342
 };
352
 
343
 
353
 __cdecl void gdbstub_handler ( int signo, gdbreg_t *regs ) {
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
 	stub.signo = signo;
353
 	stub.signo = signo;
356
 	stub.regs = regs;
354
 	stub.regs = regs;
357
 	stub.exit_handler = 0;
355
 	stub.exit_handler = 0;
358
 	gdbstub_report_signal ( &stub );
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

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

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

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
 # Run:
3
 # Run:
4
 #   make bin/gpxe.hd.tmp
4
 #   make bin/gpxe.hd.tmp
5
 #   make
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
 define gpxe_load_symbols
12
 define gpxe_load_symbols
9
 	file bin/gpxe.hd.tmp
13
 	file bin/gpxe.hd.tmp
10
 end
14
 end
11
 
15
 
12
-define gpxe_connect
13
-	target remote localhost:4444
14
-end
15
-
16
 define gpxe_assert
16
 define gpxe_assert
17
 	if $arg0 != $arg1
17
 	if $arg0 != $arg1
18
 		echo FAIL $arg2\n
18
 		echo FAIL $arg2\n
78
 end
78
 end
79
 
79
 
80
 gpxe_load_symbols
80
 gpxe_load_symbols
81
-gpxe_connect
82
 gpxe_start_tests
81
 gpxe_start_tests
83
 gpxe_test_regs_read
82
 gpxe_test_regs_read
84
 gpxe_test_regs_write
83
 gpxe_test_regs_write

Loading…
Cancel
Save