|  | @@ -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 |  
 |