• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /** @file
2  *
3  * PXE UNDI API
4  *
5  */
6 
7 /*
8  * Copyright (C) 2004 Michael Brown <mbrown@fensystems.co.uk>.
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License as
12  * published by the Free Software Foundation; either version 2 of the
13  * License, or any later version.
14  *
15  * This program is distributed in the hope that it will be useful, but
16  * WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23  */
24 
25 FILE_LICENCE ( GPL2_OR_LATER );
26 
27 #include <stdint.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <byteswap.h>
31 #include <basemem_packet.h>
32 #include <gpxe/netdevice.h>
33 #include <gpxe/iobuf.h>
34 #include <gpxe/device.h>
35 #include <gpxe/pci.h>
36 #include <gpxe/if_ether.h>
37 #include <gpxe/ip.h>
38 #include <gpxe/arp.h>
39 #include <gpxe/rarp.h>
40 #include "pxe.h"
41 
42 /**
43  * Count of outstanding transmitted packets
44  *
45  * This is incremented each time PXENV_UNDI_TRANSMIT is called, and
46  * decremented each time that PXENV_UNDI_ISR is called with the TX
47  * queue empty, stopping when the count reaches zero.  This allows us
48  * to provide a pessimistic approximation of TX completion events to
49  * the PXE NBP simply by monitoring the netdev's TX queue.
50  */
51 static int undi_tx_count = 0;
52 
53 struct net_device *pxe_netdev = NULL;
54 
55 /**
56  * Set network device as current PXE network device
57  *
58  * @v netdev		Network device, or NULL
59  */
pxe_set_netdev(struct net_device * netdev)60 void pxe_set_netdev ( struct net_device *netdev ) {
61 	if ( pxe_netdev )
62 		netdev_put ( pxe_netdev );
63 	pxe_netdev = NULL;
64 	if ( netdev )
65 		pxe_netdev = netdev_get ( netdev );
66 }
67 
68 /**
69  * Open PXE network device
70  *
71  * @ret rc		Return status code
72  */
pxe_netdev_open(void)73 static int pxe_netdev_open ( void ) {
74 	int rc;
75 
76 	if ( ( rc = netdev_open ( pxe_netdev ) ) != 0 )
77 		return rc;
78 
79 	netdev_irq ( pxe_netdev, 1 );
80 	return 0;
81 }
82 
83 /**
84  * Close PXE network device
85  *
86  */
pxe_netdev_close(void)87 static void pxe_netdev_close ( void ) {
88 	netdev_irq ( pxe_netdev, 0 );
89 	netdev_close ( pxe_netdev );
90 	undi_tx_count = 0;
91 }
92 
93 /**
94  * Dump multicast address list
95  *
96  * @v mcast		PXE multicast address list
97  */
pxe_dump_mcast_list(struct s_PXENV_UNDI_MCAST_ADDRESS * mcast)98 static void pxe_dump_mcast_list ( struct s_PXENV_UNDI_MCAST_ADDRESS *mcast ) {
99 	struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol;
100 	unsigned int i;
101 
102 	for ( i = 0 ; i < mcast->MCastAddrCount ; i++ ) {
103 		DBG ( " %s", ll_protocol->ntoa ( mcast->McastAddr[i] ) );
104 	}
105 }
106 
107 /* PXENV_UNDI_STARTUP
108  *
109  * Status: working
110  */
pxenv_undi_startup(struct s_PXENV_UNDI_STARTUP * undi_startup)111 PXENV_EXIT_t pxenv_undi_startup ( struct s_PXENV_UNDI_STARTUP *undi_startup ) {
112 	DBG ( "PXENV_UNDI_STARTUP\n" );
113 
114 	undi_startup->Status = PXENV_STATUS_SUCCESS;
115 	return PXENV_EXIT_SUCCESS;
116 }
117 
118 /* PXENV_UNDI_CLEANUP
119  *
120  * Status: working
121  */
pxenv_undi_cleanup(struct s_PXENV_UNDI_CLEANUP * undi_cleanup)122 PXENV_EXIT_t pxenv_undi_cleanup ( struct s_PXENV_UNDI_CLEANUP *undi_cleanup ) {
123 	DBG ( "PXENV_UNDI_CLEANUP\n" );
124 
125 	pxe_netdev_close();
126 
127 	undi_cleanup->Status = PXENV_STATUS_SUCCESS;
128 	return PXENV_EXIT_SUCCESS;
129 }
130 
131 /* PXENV_UNDI_INITIALIZE
132  *
133  * Status: working
134  */
pxenv_undi_initialize(struct s_PXENV_UNDI_INITIALIZE * undi_initialize)135 PXENV_EXIT_t pxenv_undi_initialize ( struct s_PXENV_UNDI_INITIALIZE
136 				     *undi_initialize ) {
137 	DBG ( "PXENV_UNDI_INITIALIZE protocolini %08x\n",
138 	      undi_initialize->ProtocolIni );
139 
140 	undi_initialize->Status = PXENV_STATUS_SUCCESS;
141 	return PXENV_EXIT_SUCCESS;
142 }
143 
144 /* PXENV_UNDI_RESET_ADAPTER
145  *
146  * Status: working
147  */
pxenv_undi_reset_adapter(struct s_PXENV_UNDI_RESET * undi_reset_adapter)148 PXENV_EXIT_t pxenv_undi_reset_adapter ( struct s_PXENV_UNDI_RESET
149 					*undi_reset_adapter ) {
150 	int rc;
151 
152 	DBG ( "PXENV_UNDI_RESET_ADAPTER" );
153 	pxe_dump_mcast_list ( &undi_reset_adapter->R_Mcast_Buf );
154 	DBG ( "\n" );
155 
156 	pxe_netdev_close();
157 	if ( ( rc = pxe_netdev_open() ) != 0 ) {
158 		DBG ( "PXENV_UNDI_RESET_ADAPTER could not reopen %s: %s\n",
159 		      pxe_netdev->name, strerror ( rc ) );
160 		undi_reset_adapter->Status = PXENV_STATUS ( rc );
161 		return PXENV_EXIT_FAILURE;
162 	}
163 
164 	undi_reset_adapter->Status = PXENV_STATUS_SUCCESS;
165 	return PXENV_EXIT_SUCCESS;
166 }
167 
168 /* PXENV_UNDI_SHUTDOWN
169  *
170  * Status: working
171  */
pxenv_undi_shutdown(struct s_PXENV_UNDI_SHUTDOWN * undi_shutdown)172 PXENV_EXIT_t pxenv_undi_shutdown ( struct s_PXENV_UNDI_SHUTDOWN
173 				   *undi_shutdown ) {
174 	DBG ( "PXENV_UNDI_SHUTDOWN\n" );
175 
176 	pxe_netdev_close();
177 
178 	undi_shutdown->Status = PXENV_STATUS_SUCCESS;
179 	return PXENV_EXIT_SUCCESS;
180 }
181 
182 /* PXENV_UNDI_OPEN
183  *
184  * Status: working
185  */
pxenv_undi_open(struct s_PXENV_UNDI_OPEN * undi_open)186 PXENV_EXIT_t pxenv_undi_open ( struct s_PXENV_UNDI_OPEN *undi_open ) {
187 	int rc;
188 
189 	DBG ( "PXENV_UNDI_OPEN flag %04x filter %04x",
190 	      undi_open->OpenFlag, undi_open->PktFilter );
191 	pxe_dump_mcast_list ( &undi_open->R_Mcast_Buf );
192 	DBG ( "\n" );
193 
194 	if ( ( rc = pxe_netdev_open() ) != 0 ) {
195 		DBG ( "PXENV_UNDI_OPEN could not open %s: %s\n",
196 		      pxe_netdev->name, strerror ( rc ) );
197 		undi_open->Status = PXENV_STATUS ( rc );
198 		return PXENV_EXIT_FAILURE;
199 	}
200 
201 	undi_open->Status = PXENV_STATUS_SUCCESS;
202 	return PXENV_EXIT_SUCCESS;
203 }
204 
205 /* PXENV_UNDI_CLOSE
206  *
207  * Status: working
208  */
pxenv_undi_close(struct s_PXENV_UNDI_CLOSE * undi_close)209 PXENV_EXIT_t pxenv_undi_close ( struct s_PXENV_UNDI_CLOSE *undi_close ) {
210 	DBG ( "PXENV_UNDI_CLOSE\n" );
211 
212 	pxe_netdev_close();
213 
214 	undi_close->Status = PXENV_STATUS_SUCCESS;
215 	return PXENV_EXIT_SUCCESS;
216 }
217 
218 /* PXENV_UNDI_TRANSMIT
219  *
220  * Status: working
221  */
pxenv_undi_transmit(struct s_PXENV_UNDI_TRANSMIT * undi_transmit)222 PXENV_EXIT_t pxenv_undi_transmit ( struct s_PXENV_UNDI_TRANSMIT
223 				   *undi_transmit ) {
224 	struct s_PXENV_UNDI_TBD tbd;
225 	struct DataBlk *datablk;
226 	struct io_buffer *iobuf;
227 	struct net_protocol *net_protocol;
228 	struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol;
229 	char destaddr[MAX_LL_ADDR_LEN];
230 	const void *ll_dest;
231 	size_t ll_hlen = ll_protocol->ll_header_len;
232 	size_t len;
233 	unsigned int i;
234 	int rc;
235 
236 	DBG2 ( "PXENV_UNDI_TRANSMIT" );
237 
238 	/* Forcibly enable interrupts at this point, to work around
239 	 * callers that never call PXENV_UNDI_OPEN before attempting
240 	 * to use the UNDI API.
241 	 */
242 	netdev_irq ( pxe_netdev, 1 );
243 
244 	/* Identify network-layer protocol */
245 	switch ( undi_transmit->Protocol ) {
246 	case P_IP:	net_protocol = &ipv4_protocol;	break;
247 	case P_ARP:	net_protocol = &arp_protocol;	break;
248 	case P_RARP:	net_protocol = &rarp_protocol;	break;
249 	case P_UNKNOWN:
250 		net_protocol = NULL;
251 		ll_hlen = 0;
252 		break;
253 	default:
254 		DBG2 ( " %02x invalid protocol\n", undi_transmit->Protocol );
255 		undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
256 		return PXENV_EXIT_FAILURE;
257 	}
258 	DBG2 ( " %s", ( net_protocol ? net_protocol->name : "RAW" ) );
259 
260 	/* Calculate total packet length */
261 	copy_from_real ( &tbd, undi_transmit->TBD.segment,
262 			 undi_transmit->TBD.offset, sizeof ( tbd ) );
263 	len = tbd.ImmedLength;
264 	DBG2 ( " %04x:%04x+%x", tbd.Xmit.segment, tbd.Xmit.offset,
265 	       tbd.ImmedLength );
266 	for ( i = 0 ; i < tbd.DataBlkCount ; i++ ) {
267 		datablk = &tbd.DataBlock[i];
268 		len += datablk->TDDataLen;
269 		DBG2 ( " %04x:%04x+%x", datablk->TDDataPtr.segment,
270 		       datablk->TDDataPtr.offset, datablk->TDDataLen );
271 	}
272 
273 	/* Allocate and fill I/O buffer */
274 	iobuf = alloc_iob ( ll_hlen + len );
275 	if ( ! iobuf ) {
276 		DBG2 ( " could not allocate iobuf\n" );
277 		undi_transmit->Status = PXENV_STATUS_OUT_OF_RESOURCES;
278 		return PXENV_EXIT_FAILURE;
279 	}
280 	iob_reserve ( iobuf, ll_hlen );
281 	copy_from_real ( iob_put ( iobuf, tbd.ImmedLength ), tbd.Xmit.segment,
282 			 tbd.Xmit.offset, tbd.ImmedLength );
283 	for ( i = 0 ; i < tbd.DataBlkCount ; i++ ) {
284 		datablk = &tbd.DataBlock[i];
285 		copy_from_real ( iob_put ( iobuf, datablk->TDDataLen ),
286 				 datablk->TDDataPtr.segment,
287 				 datablk->TDDataPtr.offset,
288 				 datablk->TDDataLen );
289 	}
290 
291 	/* Add link-layer header, if required to do so */
292 	if ( net_protocol != NULL ) {
293 
294 		/* Calculate destination address */
295 		if ( undi_transmit->XmitFlag == XMT_DESTADDR ) {
296 			copy_from_real ( destaddr,
297 					 undi_transmit->DestAddr.segment,
298 					 undi_transmit->DestAddr.offset,
299 					 ll_protocol->ll_addr_len );
300 			ll_dest = destaddr;
301 			DBG2 ( " DEST %s", ll_protocol->ntoa ( ll_dest ) );
302 		} else {
303 			ll_dest = pxe_netdev->ll_broadcast;
304 			DBG2 ( " BCAST" );
305 		}
306 
307 		/* Add link-layer header */
308 		if ( ( rc = ll_protocol->push ( pxe_netdev, iobuf, ll_dest,
309 						pxe_netdev->ll_addr,
310 						net_protocol->net_proto ))!=0){
311 			DBG2 ( " could not add link-layer header: %s\n",
312 			       strerror ( rc ) );
313 			free_iob ( iobuf );
314 			undi_transmit->Status = PXENV_STATUS ( rc );
315 			return PXENV_EXIT_FAILURE;
316 		}
317 	}
318 
319 	/* Flag transmission as in-progress.  Do this before starting
320 	 * to transmit the packet, because the ISR may trigger before
321 	 * we return from netdev_tx().
322 	 */
323 	undi_tx_count++;
324 
325 	/* Transmit packet */
326 	DBG2 ( "\n" );
327 	if ( ( rc = netdev_tx ( pxe_netdev, iobuf ) ) != 0 ) {
328 		DBG2 ( "PXENV_UNDI_TRANSMIT could not transmit: %s\n",
329 		       strerror ( rc ) );
330 		undi_tx_count--;
331 		undi_transmit->Status = PXENV_STATUS ( rc );
332 		return PXENV_EXIT_FAILURE;
333 	}
334 
335 	undi_transmit->Status = PXENV_STATUS_SUCCESS;
336 	return PXENV_EXIT_SUCCESS;
337 }
338 
339 /* PXENV_UNDI_SET_MCAST_ADDRESS
340  *
341  * Status: working (for NICs that support receive-all-multicast)
342  */
343 PXENV_EXIT_t
pxenv_undi_set_mcast_address(struct s_PXENV_UNDI_SET_MCAST_ADDRESS * undi_set_mcast_address)344 pxenv_undi_set_mcast_address ( struct s_PXENV_UNDI_SET_MCAST_ADDRESS
345 			       *undi_set_mcast_address ) {
346 	DBG ( "PXENV_UNDI_SET_MCAST_ADDRESS" );
347 	pxe_dump_mcast_list ( &undi_set_mcast_address->R_Mcast_Buf );
348 	DBG ( "\n" );
349 
350 	undi_set_mcast_address->Status = PXENV_STATUS_SUCCESS;
351 	return PXENV_EXIT_SUCCESS;
352 }
353 
354 /* PXENV_UNDI_SET_STATION_ADDRESS
355  *
356  * Status: working
357  */
358 PXENV_EXIT_t
pxenv_undi_set_station_address(struct s_PXENV_UNDI_SET_STATION_ADDRESS * undi_set_station_address)359 pxenv_undi_set_station_address ( struct s_PXENV_UNDI_SET_STATION_ADDRESS
360 				 *undi_set_station_address ) {
361 	struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol;
362 
363 	DBG ( "PXENV_UNDI_SET_STATION_ADDRESS %s",
364 	      ll_protocol->ntoa ( undi_set_station_address->StationAddress ) );
365 
366 	/* If adapter is open, the change will have no effect; return
367 	 * an error
368 	 */
369 	if ( pxe_netdev->state & NETDEV_OPEN ) {
370 		DBG ( " failed: netdev is open\n" );
371 		undi_set_station_address->Status =
372 			PXENV_STATUS_UNDI_INVALID_STATE;
373 		return PXENV_EXIT_FAILURE;
374 	}
375 
376 	/* Update MAC address */
377 	memcpy ( pxe_netdev->ll_addr,
378 		 &undi_set_station_address->StationAddress,
379 		 ll_protocol->ll_addr_len );
380 
381 	DBG ( "\n" );
382 	undi_set_station_address->Status = PXENV_STATUS_SUCCESS;
383 	return PXENV_EXIT_SUCCESS;
384 }
385 
386 /* PXENV_UNDI_SET_PACKET_FILTER
387  *
388  * Status: won't implement (would require driver API changes for no
389  * real benefit)
390  */
391 PXENV_EXIT_t
pxenv_undi_set_packet_filter(struct s_PXENV_UNDI_SET_PACKET_FILTER * undi_set_packet_filter)392 pxenv_undi_set_packet_filter ( struct s_PXENV_UNDI_SET_PACKET_FILTER
393 			       *undi_set_packet_filter ) {
394 
395 	DBG ( "PXENV_UNDI_SET_PACKET_FILTER %02x\n",
396 	      undi_set_packet_filter->filter );
397 
398 	/* Pretend that we succeeded, otherwise the 3Com DOS UNDI
399 	 * driver refuses to load.  (We ignore the filter value in the
400 	 * PXENV_UNDI_OPEN call anyway.)
401 	 */
402 	undi_set_packet_filter->Status = PXENV_STATUS_SUCCESS;
403 
404 	return PXENV_EXIT_SUCCESS;
405 }
406 
407 /* PXENV_UNDI_GET_INFORMATION
408  *
409  * Status: working
410  */
pxenv_undi_get_information(struct s_PXENV_UNDI_GET_INFORMATION * undi_get_information)411 PXENV_EXIT_t pxenv_undi_get_information ( struct s_PXENV_UNDI_GET_INFORMATION
412 					  *undi_get_information ) {
413 	struct device *dev = pxe_netdev->dev;
414 	struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol;
415 	size_t ll_addr_len = ll_protocol->ll_addr_len;
416 
417 	DBG ( "PXENV_UNDI_GET_INFORMATION" );
418 
419 	undi_get_information->BaseIo = dev->desc.ioaddr;
420 	undi_get_information->IntNumber = dev->desc.irq;
421 	/* Cheat: assume all cards can cope with this */
422 	undi_get_information->MaxTranUnit = ETH_MAX_MTU;
423 	undi_get_information->HwType = ntohs ( ll_protocol->ll_proto );
424 	undi_get_information->HwAddrLen = ll_addr_len;
425 	assert ( ll_addr_len <=
426 		 sizeof ( undi_get_information->CurrentNodeAddress ) );
427 	memcpy ( &undi_get_information->CurrentNodeAddress,
428 		 pxe_netdev->ll_addr,
429 		 sizeof ( undi_get_information->CurrentNodeAddress ) );
430 	ll_protocol->init_addr ( pxe_netdev->hw_addr,
431 				 &undi_get_information->PermNodeAddress );
432 	undi_get_information->ROMAddress = 0;
433 		/* nic.rom_info->rom_segment; */
434 	/* We only provide the ability to receive or transmit a single
435 	 * packet at a time.  This is a bootloader, not an OS.
436 	 */
437 	undi_get_information->RxBufCt = 1;
438 	undi_get_information->TxBufCt = 1;
439 
440 	DBG ( " io %04x irq %d mtu %d %s %s\n",
441 	      undi_get_information->BaseIo, undi_get_information->IntNumber,
442 	      undi_get_information->MaxTranUnit, ll_protocol->name,
443 	      ll_protocol->ntoa ( &undi_get_information->CurrentNodeAddress ));
444 	undi_get_information->Status = PXENV_STATUS_SUCCESS;
445 	return PXENV_EXIT_SUCCESS;
446 }
447 
448 /* PXENV_UNDI_GET_STATISTICS
449  *
450  * Status: working
451  */
pxenv_undi_get_statistics(struct s_PXENV_UNDI_GET_STATISTICS * undi_get_statistics)452 PXENV_EXIT_t pxenv_undi_get_statistics ( struct s_PXENV_UNDI_GET_STATISTICS
453 					 *undi_get_statistics ) {
454 	DBG ( "PXENV_UNDI_GET_STATISTICS" );
455 
456 	undi_get_statistics->XmtGoodFrames = pxe_netdev->tx_stats.good;
457 	undi_get_statistics->RcvGoodFrames = pxe_netdev->rx_stats.good;
458 	undi_get_statistics->RcvCRCErrors = pxe_netdev->rx_stats.bad;
459 	undi_get_statistics->RcvResourceErrors = pxe_netdev->rx_stats.bad;
460 
461 	DBG ( " txok %d rxok %d rxcrc %d rxrsrc %d\n",
462 	      undi_get_statistics->XmtGoodFrames,
463 	      undi_get_statistics->RcvGoodFrames,
464 	      undi_get_statistics->RcvCRCErrors,
465 	      undi_get_statistics->RcvResourceErrors );
466 	undi_get_statistics->Status = PXENV_STATUS_SUCCESS;
467 	return PXENV_EXIT_SUCCESS;
468 }
469 
470 /* PXENV_UNDI_CLEAR_STATISTICS
471  *
472  * Status: working
473  */
pxenv_undi_clear_statistics(struct s_PXENV_UNDI_CLEAR_STATISTICS * undi_clear_statistics)474 PXENV_EXIT_t pxenv_undi_clear_statistics ( struct s_PXENV_UNDI_CLEAR_STATISTICS
475 					   *undi_clear_statistics ) {
476 	DBG ( "PXENV_UNDI_CLEAR_STATISTICS\n" );
477 
478 	memset ( &pxe_netdev->tx_stats, 0, sizeof ( pxe_netdev->tx_stats ) );
479 	memset ( &pxe_netdev->rx_stats, 0, sizeof ( pxe_netdev->rx_stats ) );
480 
481 	undi_clear_statistics->Status = PXENV_STATUS_SUCCESS;
482 	return PXENV_EXIT_SUCCESS;
483 }
484 
485 /* PXENV_UNDI_INITIATE_DIAGS
486  *
487  * Status: won't implement (would require driver API changes for no
488  * real benefit)
489  */
pxenv_undi_initiate_diags(struct s_PXENV_UNDI_INITIATE_DIAGS * undi_initiate_diags)490 PXENV_EXIT_t pxenv_undi_initiate_diags ( struct s_PXENV_UNDI_INITIATE_DIAGS
491 					 *undi_initiate_diags ) {
492 	DBG ( "PXENV_UNDI_INITIATE_DIAGS failed: unsupported\n" );
493 
494 	undi_initiate_diags->Status = PXENV_STATUS_UNSUPPORTED;
495 	return PXENV_EXIT_FAILURE;
496 }
497 
498 /* PXENV_UNDI_FORCE_INTERRUPT
499  *
500  * Status: won't implement (would require driver API changes for no
501  * perceptible benefit)
502  */
pxenv_undi_force_interrupt(struct s_PXENV_UNDI_FORCE_INTERRUPT * undi_force_interrupt)503 PXENV_EXIT_t pxenv_undi_force_interrupt ( struct s_PXENV_UNDI_FORCE_INTERRUPT
504 					  *undi_force_interrupt ) {
505 	DBG ( "PXENV_UNDI_FORCE_INTERRUPT failed: unsupported\n" );
506 
507 	undi_force_interrupt->Status = PXENV_STATUS_UNSUPPORTED;
508 	return PXENV_EXIT_FAILURE;
509 }
510 
511 /* PXENV_UNDI_GET_MCAST_ADDRESS
512  *
513  * Status: working
514  */
515 PXENV_EXIT_t
pxenv_undi_get_mcast_address(struct s_PXENV_UNDI_GET_MCAST_ADDRESS * undi_get_mcast_address)516 pxenv_undi_get_mcast_address ( struct s_PXENV_UNDI_GET_MCAST_ADDRESS
517 			       *undi_get_mcast_address ) {
518 	struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol;
519 	struct in_addr ip = { .s_addr = undi_get_mcast_address->InetAddr };
520 	int rc;
521 
522 	DBG ( "PXENV_UNDI_GET_MCAST_ADDRESS %s", inet_ntoa ( ip ) );
523 
524 	if ( ( rc = ll_protocol->mc_hash ( AF_INET, &ip,
525 				      undi_get_mcast_address->MediaAddr ))!=0){
526 		DBG ( " failed: %s\n", strerror ( rc ) );
527 		undi_get_mcast_address->Status = PXENV_STATUS ( rc );
528 		return PXENV_EXIT_FAILURE;
529 	}
530 	DBG ( "=>%s\n",
531 	      ll_protocol->ntoa ( undi_get_mcast_address->MediaAddr ) );
532 
533 	undi_get_mcast_address->Status = PXENV_STATUS_SUCCESS;
534 	return PXENV_EXIT_SUCCESS;
535 }
536 
537 /* PXENV_UNDI_GET_NIC_TYPE
538  *
539  * Status: working
540  */
pxenv_undi_get_nic_type(struct s_PXENV_UNDI_GET_NIC_TYPE * undi_get_nic_type)541 PXENV_EXIT_t pxenv_undi_get_nic_type ( struct s_PXENV_UNDI_GET_NIC_TYPE
542 				       *undi_get_nic_type ) {
543 	struct device *dev = pxe_netdev->dev;
544 
545 	DBG ( "PXENV_UNDI_GET_NIC_TYPE" );
546 
547 	memset ( &undi_get_nic_type->info, 0,
548 		 sizeof ( undi_get_nic_type->info ) );
549 
550 	switch ( dev->desc.bus_type ) {
551 	case BUS_TYPE_PCI: {
552 		struct pci_nic_info *info = &undi_get_nic_type->info.pci;
553 
554 		undi_get_nic_type->NicType = PCI_NIC;
555 		info->Vendor_ID = dev->desc.vendor;
556 		info->Dev_ID = dev->desc.device;
557 		info->Base_Class = PCI_BASE_CLASS ( dev->desc.class );
558 		info->Sub_Class = PCI_SUB_CLASS ( dev->desc.class );
559 		info->Prog_Intf = PCI_PROG_INTF ( dev->desc.class );
560 		info->BusDevFunc = dev->desc.location;
561 		/* Cheat: remaining fields are probably unnecessary,
562 		 * and would require adding extra code to pci.c.
563 		 */
564 		undi_get_nic_type->info.pci.SubVendor_ID = 0xffff;
565 		undi_get_nic_type->info.pci.SubDevice_ID = 0xffff;
566 		DBG ( " PCI %02x:%02x.%x %04x:%04x (%04x:%04x) %02x%02x%02x "
567 		      "rev %02x\n", PCI_BUS ( info->BusDevFunc ),
568 		      PCI_SLOT ( info->BusDevFunc ),
569 		      PCI_FUNC ( info->BusDevFunc ), info->Vendor_ID,
570 		      info->Dev_ID, info->SubVendor_ID, info->SubDevice_ID,
571 		      info->Base_Class, info->Sub_Class, info->Prog_Intf,
572 		      info->Rev );
573 		break; }
574 	case BUS_TYPE_ISAPNP: {
575 		struct pnp_nic_info *info = &undi_get_nic_type->info.pnp;
576 
577 		undi_get_nic_type->NicType = PnP_NIC;
578 		info->EISA_Dev_ID = ( ( dev->desc.vendor << 16 ) |
579 				      dev->desc.device );
580 		info->CardSelNum = dev->desc.location;
581 		/* Cheat: remaining fields are probably unnecessary,
582 		 * and would require adding extra code to isapnp.c.
583 		 */
584 		DBG ( " ISAPnP CSN %04x %08x %02x%02x%02x\n",
585 		      info->CardSelNum, info->EISA_Dev_ID,
586 		      info->Base_Class, info->Sub_Class, info->Prog_Intf );
587 		break; }
588 	default:
589 		DBG ( " failed: unknown bus type\n" );
590 		undi_get_nic_type->Status = PXENV_STATUS_FAILURE;
591 		return PXENV_EXIT_FAILURE;
592 	}
593 
594 	undi_get_nic_type->Status = PXENV_STATUS_SUCCESS;
595 	return PXENV_EXIT_SUCCESS;
596 }
597 
598 /* PXENV_UNDI_GET_IFACE_INFO
599  *
600  * Status: working
601  */
pxenv_undi_get_iface_info(struct s_PXENV_UNDI_GET_IFACE_INFO * undi_get_iface_info)602 PXENV_EXIT_t pxenv_undi_get_iface_info ( struct s_PXENV_UNDI_GET_IFACE_INFO
603 					 *undi_get_iface_info ) {
604 	DBG ( "PXENV_UNDI_GET_IFACE_INFO" );
605 
606 	/* Just hand back some info, doesn't really matter what it is.
607 	 * Most PXE stacks seem to take this approach.
608 	 */
609 	snprintf ( ( char * ) undi_get_iface_info->IfaceType,
610 		   sizeof ( undi_get_iface_info->IfaceType ), "DIX+802.3" );
611 	undi_get_iface_info->LinkSpeed = 10000000; /* 10 Mbps */
612 	undi_get_iface_info->ServiceFlags =
613 		( SUPPORTED_BROADCAST | SUPPORTED_MULTICAST |
614 		  SUPPORTED_SET_STATION_ADDRESS | SUPPORTED_RESET |
615 		  SUPPORTED_OPEN_CLOSE | SUPPORTED_IRQ );
616 	memset ( undi_get_iface_info->Reserved, 0,
617 		 sizeof(undi_get_iface_info->Reserved) );
618 
619 	DBG ( " %s %dbps flags %08x\n", undi_get_iface_info->IfaceType,
620 	      undi_get_iface_info->LinkSpeed,
621 	      undi_get_iface_info->ServiceFlags );
622 	undi_get_iface_info->Status = PXENV_STATUS_SUCCESS;
623 	return PXENV_EXIT_SUCCESS;
624 }
625 
626 /* PXENV_UNDI_GET_STATE
627  *
628  * Status: impossible
629  */
pxenv_undi_get_state(struct s_PXENV_UNDI_GET_STATE * undi_get_state)630 PXENV_EXIT_t pxenv_undi_get_state ( struct s_PXENV_UNDI_GET_STATE
631 				    *undi_get_state ) {
632 	DBG ( "PXENV_UNDI_GET_STATE failed: unsupported\n" );
633 
634 	undi_get_state->Status = PXENV_STATUS_UNSUPPORTED;
635 	return PXENV_EXIT_FAILURE;
636 };
637 
638 /* PXENV_UNDI_ISR
639  *
640  * Status: working
641  */
pxenv_undi_isr(struct s_PXENV_UNDI_ISR * undi_isr)642 PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) {
643 	struct io_buffer *iobuf;
644 	size_t len;
645 	struct ll_protocol *ll_protocol;
646 	const void *ll_dest;
647 	const void *ll_source;
648 	uint16_t net_proto;
649 	size_t ll_hlen;
650 	struct net_protocol *net_protocol;
651 	unsigned int prottype;
652 	int rc;
653 
654 	/* Use coloured debug, since UNDI ISR messages are likely to
655 	 * be interspersed amongst other UNDI messages.
656 	 */
657 	DBGC2 ( &pxenv_undi_isr, "PXENV_UNDI_ISR" );
658 
659 	/* Just in case some idiot actually looks at these fields when
660 	 * we weren't meant to fill them in...
661 	 */
662 	undi_isr->BufferLength = 0;
663 	undi_isr->FrameLength = 0;
664 	undi_isr->FrameHeaderLength = 0;
665 	undi_isr->ProtType = 0;
666 	undi_isr->PktType = 0;
667 
668 	switch ( undi_isr->FuncFlag ) {
669 	case PXENV_UNDI_ISR_IN_START :
670 		DBGC2 ( &pxenv_undi_isr, " START" );
671 
672 		/* Call poll().  This should acknowledge the device
673 		 * interrupt and queue up any received packet.
674 		 */
675 		netdev_poll ( pxe_netdev );
676 
677 		/* Disable interrupts to avoid interrupt storm */
678 		netdev_irq ( pxe_netdev, 0 );
679 
680 		/* Always say it was ours for the sake of simplicity */
681 		DBGC2 ( &pxenv_undi_isr, " OURS" );
682 		undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_OURS;
683 		break;
684 	case PXENV_UNDI_ISR_IN_PROCESS :
685 	case PXENV_UNDI_ISR_IN_GET_NEXT :
686 		DBGC2 ( &pxenv_undi_isr, " %s",
687 			( ( undi_isr->FuncFlag == PXENV_UNDI_ISR_IN_PROCESS ) ?
688 			  "PROCESS" : "GET_NEXT" ) );
689 
690 		/* Some dumb NBPs (e.g. emBoot's winBoot/i) never call
691 		 * PXENV_UNDI_ISR with FuncFlag=PXENV_UNDI_ISR_START;
692 		 * they just sit in a tight polling loop merrily
693 		 * violating the PXE spec with repeated calls to
694 		 * PXENV_UNDI_ISR_IN_PROCESS.  Force extra polls to
695 		 * cope with these out-of-spec clients.
696 		 */
697 		netdev_poll ( pxe_netdev );
698 
699 		/* If we have not yet marked a TX as complete, and the
700 		 * netdev TX queue is empty, report the TX completion.
701 		 */
702 		if ( undi_tx_count && list_empty ( &pxe_netdev->tx_queue ) ) {
703 			DBGC2 ( &pxenv_undi_isr, " TXC" );
704 			undi_tx_count--;
705 			undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_TRANSMIT;
706 			break;
707 		}
708 
709 		/* Remove first packet from netdev RX queue */
710 		iobuf = netdev_rx_dequeue ( pxe_netdev );
711 		if ( ! iobuf ) {
712 			DBGC2 ( &pxenv_undi_isr, " DONE" );
713 			/* No more packets remaining */
714 			undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
715 			/* Re-enable interrupts */
716 			netdev_irq ( pxe_netdev, 1 );
717 			break;
718 		}
719 
720 		/* Copy packet to base memory buffer */
721 		len = iob_len ( iobuf );
722 		DBGC2 ( &pxenv_undi_isr, " RX" );
723 		if ( len > sizeof ( basemem_packet ) ) {
724 			/* Should never happen */
725 			DBGC2 ( &pxenv_undi_isr, " overlength (%zx)", len );
726 			len = sizeof ( basemem_packet );
727 		}
728 		memcpy ( basemem_packet, iobuf->data, len );
729 
730 		/* Strip link-layer header */
731 		ll_protocol = pxe_netdev->ll_protocol;
732 		if ( ( rc = ll_protocol->pull ( pxe_netdev, iobuf, &ll_dest,
733 						&ll_source, &net_proto )) !=0){
734 			/* Assume unknown net_proto and no ll_source */
735 			net_proto = 0;
736 			ll_source = NULL;
737 		}
738 		ll_hlen = ( len - iob_len ( iobuf ) );
739 
740 		/* Determine network-layer protocol */
741 		switch ( net_proto ) {
742 		case htons ( ETH_P_IP ):
743 			net_protocol = &ipv4_protocol;
744 			prottype = P_IP;
745 			break;
746 		case htons ( ETH_P_ARP ):
747 			net_protocol = &arp_protocol;
748 			prottype = P_ARP;
749 			break;
750 		case htons ( ETH_P_RARP ):
751 			net_protocol = &rarp_protocol;
752 			prottype = P_RARP;
753 			break;
754 		default:
755 			net_protocol = NULL;
756 			prottype = P_UNKNOWN;
757 			break;
758 		}
759 
760 		/* Fill in UNDI_ISR structure */
761 		undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_RECEIVE;
762 		undi_isr->BufferLength = len;
763 		undi_isr->FrameLength = len;
764 		undi_isr->FrameHeaderLength = ll_hlen;
765 		undi_isr->Frame.segment = rm_ds;
766 		undi_isr->Frame.offset = __from_data16 ( basemem_packet );
767 		undi_isr->ProtType = prottype;
768 		undi_isr->PktType = XMT_DESTADDR;
769 		DBGC2 ( &pxenv_undi_isr, " %04x:%04x+%x(%x) %s hlen %d",
770 			undi_isr->Frame.segment, undi_isr->Frame.offset,
771 			undi_isr->BufferLength, undi_isr->FrameLength,
772 			( net_protocol ? net_protocol->name : "RAW" ),
773 			undi_isr->FrameHeaderLength );
774 
775 		/* Free packet */
776 		free_iob ( iobuf );
777 		break;
778 	default :
779 		DBGC2 ( &pxenv_undi_isr, " INVALID(%04x)\n",
780 			undi_isr->FuncFlag );
781 
782 		/* Should never happen */
783 		undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
784 		undi_isr->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
785 		return PXENV_EXIT_FAILURE;
786 	}
787 
788 	DBGC2 ( &pxenv_undi_isr, "\n" );
789 	undi_isr->Status = PXENV_STATUS_SUCCESS;
790 	return PXENV_EXIT_SUCCESS;
791 }
792