|
@@ -27,6 +27,17 @@
|
27
|
27
|
#include <gpxe/udp.h>
|
28
|
28
|
#include <gpxe/netdevice.h>
|
29
|
29
|
#include <gpxe/gdbstub.h>
|
|
30
|
+#include <bios.h>
|
|
31
|
+
|
|
32
|
+/** @file
|
|
33
|
+ *
|
|
34
|
+ * GDB over UDP transport
|
|
35
|
+ *
|
|
36
|
+ */
|
|
37
|
+
|
|
38
|
+enum {
|
|
39
|
+ DEFAULT_PORT = 43770, /* UDP listen port */
|
|
40
|
+};
|
30
|
41
|
|
31
|
42
|
struct gdb_transport udp_gdb_transport __gdb_transport;
|
32
|
43
|
|
|
@@ -37,13 +48,11 @@ static struct sockaddr_in dest_addr;
|
37
|
48
|
static struct sockaddr_in source_addr;
|
38
|
49
|
|
39
|
50
|
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? */
|
|
51
|
+ /* The device may have been closed between breakpoints */
|
|
52
|
+ assert ( netdev );
|
|
53
|
+ netdev_open ( netdev );
|
|
54
|
+
|
|
55
|
+ /* Strictly speaking, we may need to close the device when leaving the interrupt handler */
|
47
|
56
|
}
|
48
|
57
|
|
49
|
58
|
static size_t gdbudp_recv ( char *buf, size_t len ) {
|
|
@@ -54,27 +63,28 @@ static size_t gdbudp_recv ( char *buf, size_t len ) {
|
54
|
63
|
struct udp_header *udphdr;
|
55
|
64
|
size_t payload_len;
|
56
|
65
|
|
57
|
|
- assert ( netdev );
|
58
|
66
|
gdbudp_ensure_netdev_open ( netdev );
|
59
|
67
|
|
60
|
68
|
for ( ; ; ) {
|
|
69
|
+ netdev_poll ( netdev );
|
61
|
70
|
while ( ( iob = netdev_rx_dequeue ( netdev ) ) != NULL ) {
|
62
|
|
- if ( iob_len ( iob ) > sizeof ( *ethhdr ) + sizeof ( *iphdr ) + sizeof ( *udphdr ) + len ) {
|
|
71
|
+ /* Ethernet header */
|
|
72
|
+ if ( iob_len ( iob ) < sizeof ( *ethhdr ) ) {
|
63
|
73
|
goto bad_packet;
|
64
|
74
|
}
|
65
|
|
-
|
66
|
|
- /* Ethernet header */
|
67
|
75
|
ethhdr = iob->data;
|
68
|
76
|
iob_pull ( iob, sizeof ( *ethhdr ) );
|
|
77
|
+
|
|
78
|
+ /* Handle ARP requests so the client can find our MAC */
|
69
|
79
|
if ( ethhdr->h_protocol == htons ( ETH_P_ARP ) ) {
|
70
|
|
- /* Handle ARP requests so the client can connect to us */
|
71
|
80
|
arphdr = iob->data;
|
72
|
|
- if ( arphdr->ar_hrd != htons ( ARPHRD_ETHER ) ||
|
|
81
|
+ if ( iob_len ( iob ) < sizeof ( *arphdr ) + 2 * ( ETH_ALEN + sizeof ( struct in_addr ) ) ||
|
|
82
|
+ arphdr->ar_hrd != htons ( ARPHRD_ETHER ) ||
|
73
|
83
|
arphdr->ar_pro != htons ( ETH_P_IP ) ||
|
74
|
84
|
arphdr->ar_hln != ETH_ALEN ||
|
75
|
85
|
arphdr->ar_pln != sizeof ( struct in_addr ) ||
|
76
|
86
|
arphdr->ar_op != htons ( ARPOP_REQUEST ) ||
|
77
|
|
- memcmp ( arp_target_pa ( arphdr ), &source_addr.sin_addr.s_addr, sizeof ( struct in_addr ) ) ) {
|
|
87
|
+ * ( uint32_t * ) arp_target_pa ( arphdr ) != source_addr.sin_addr.s_addr ) {
|
78
|
88
|
goto bad_packet;
|
79
|
89
|
}
|
80
|
90
|
|
|
@@ -91,11 +101,15 @@ static size_t gdbudp_recv ( char *buf, size_t len ) {
|
91
|
101
|
netdev_tx ( netdev, iob );
|
92
|
102
|
continue; /* no need to free iob */
|
93
|
103
|
}
|
|
104
|
+
|
94
|
105
|
if ( ethhdr->h_protocol != htons ( ETH_P_IP ) ) {
|
95
|
106
|
goto bad_packet;
|
96
|
107
|
}
|
97
|
108
|
|
98
|
109
|
/* IP header */
|
|
110
|
+ if ( iob_len ( iob ) < sizeof ( *iphdr ) ) {
|
|
111
|
+ goto bad_packet;
|
|
112
|
+ }
|
99
|
113
|
iphdr = iob->data;
|
100
|
114
|
iob_pull ( iob, sizeof ( *iphdr ) );
|
101
|
115
|
if ( iphdr->protocol != IP_UDP || iphdr->dest.s_addr != source_addr.sin_addr.s_addr ) {
|
|
@@ -103,6 +117,9 @@ static size_t gdbudp_recv ( char *buf, size_t len ) {
|
103
|
117
|
}
|
104
|
118
|
|
105
|
119
|
/* UDP header */
|
|
120
|
+ if ( iob_len ( iob ) < sizeof ( *udphdr ) ) {
|
|
121
|
+ goto bad_packet;
|
|
122
|
+ }
|
106
|
123
|
udphdr = iob->data;
|
107
|
124
|
if ( udphdr->dest != source_addr.sin_port ) {
|
108
|
125
|
goto bad_packet;
|
|
@@ -115,12 +132,14 @@ static size_t gdbudp_recv ( char *buf, size_t len ) {
|
115
|
132
|
|
116
|
133
|
/* Payload */
|
117
|
134
|
payload_len = ntohs ( udphdr->len );
|
118
|
|
- if ( payload_len < sizeof ( *udphdr ) ||
|
119
|
|
- payload_len > iob_len ( iob ) ) {
|
|
135
|
+ if ( payload_len < sizeof ( *udphdr ) || payload_len > iob_len ( iob ) ) {
|
120
|
136
|
goto bad_packet;
|
121
|
137
|
}
|
122
|
138
|
payload_len -= sizeof ( *udphdr );
|
123
|
139
|
iob_pull ( iob, sizeof ( *udphdr ) );
|
|
140
|
+ if ( payload_len > len ) {
|
|
141
|
+ goto bad_packet;
|
|
142
|
+ }
|
124
|
143
|
memcpy ( buf, iob->data, payload_len );
|
125
|
144
|
|
126
|
145
|
free_iob ( iob );
|
|
@@ -129,7 +148,7 @@ static size_t gdbudp_recv ( char *buf, size_t len ) {
|
129
|
148
|
bad_packet:
|
130
|
149
|
free_iob ( iob );
|
131
|
150
|
}
|
132
|
|
- netdev_poll ( netdev );
|
|
151
|
+ cpu_nap();
|
133
|
152
|
}
|
134
|
153
|
}
|
135
|
154
|
|
|
@@ -144,7 +163,6 @@ static void gdbudp_send ( const char *buf, size_t len ) {
|
144
|
163
|
return;
|
145
|
164
|
}
|
146
|
165
|
|
147
|
|
- assert ( netdev );
|
148
|
166
|
gdbudp_ensure_netdev_open ( netdev );
|
149
|
167
|
|
150
|
168
|
iob = alloc_iob ( sizeof ( *ethhdr ) + sizeof ( *iphdr ) + sizeof ( *udphdr ) + len );
|
|
@@ -192,14 +210,22 @@ static int gdbudp_init ( int argc, char **argv ) {
|
192
|
210
|
return 1;
|
193
|
211
|
}
|
194
|
212
|
|
|
213
|
+ /* Release old network device */
|
|
214
|
+ netdev_put ( netdev );
|
|
215
|
+
|
195
|
216
|
netdev = find_netdev ( argv[0] );
|
196
|
217
|
if ( !netdev ) {
|
197
|
218
|
printf ( "%s: no such interface\n", argv[0] );
|
198
|
219
|
return 1;
|
199
|
220
|
}
|
200
|
221
|
|
|
222
|
+ /* Hold network device */
|
|
223
|
+ netdev_get ( netdev );
|
|
224
|
+
|
201
|
225
|
if ( !netdev_link_ok ( netdev ) ) {
|
202
|
226
|
printf ( "%s: link not up\n", argv[0] );
|
|
227
|
+ netdev_put ( netdev );
|
|
228
|
+ netdev = NULL;
|
203
|
229
|
return 1;
|
204
|
230
|
}
|
205
|
231
|
|
|
@@ -209,11 +235,13 @@ static int gdbudp_init ( int argc, char **argv ) {
|
209
|
235
|
* Storing a separate copy makes it possible to use different
|
210
|
236
|
* MAC/IP settings than the network stack. */
|
211
|
237
|
memcpy ( source_eth, netdev->ll_addr, ETH_ALEN );
|
212
|
|
- source_addr.sin_port = htons ( 43770 ); /* TODO default port */
|
|
238
|
+ source_addr.sin_port = htons ( DEFAULT_PORT );
|
213
|
239
|
settings = netdev_settings ( netdev );
|
214
|
240
|
fetch_ipv4_setting ( settings, &ip_setting, &source_addr.sin_addr );
|
215
|
241
|
if ( source_addr.sin_addr.s_addr == 0 ) {
|
216
|
242
|
printf ( "%s: no IP address configured\n", argv[0] );
|
|
243
|
+ netdev_put ( netdev );
|
|
244
|
+ netdev = NULL;
|
217
|
245
|
return 1;
|
218
|
246
|
}
|
219
|
247
|
|