These changes are the raw update to qemu-2.6.
[kvmfornfv.git] / qemu / roms / ipxe / src / arch / i386 / interface / pxe / pxe_undi.c
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., 51 Franklin Street, Fifth Floor, Boston, MA
23  * 02110-1301, USA.
24  *
25  * You can also choose to distribute this program under the terms of
26  * the Unmodified Binary Distribution Licence (as given in the file
27  * COPYING.UBDL), provided that you have satisfied its requirements.
28  */
29
30 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
31
32 #include <stdint.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <byteswap.h>
36 #include <basemem_packet.h>
37 #include <ipxe/netdevice.h>
38 #include <ipxe/iobuf.h>
39 #include <ipxe/device.h>
40 #include <ipxe/pci.h>
41 #include <ipxe/if_ether.h>
42 #include <ipxe/ip.h>
43 #include <ipxe/arp.h>
44 #include <ipxe/rarp.h>
45 #include <ipxe/profile.h>
46 #include "pxe.h"
47
48 /**
49  * Count of outstanding transmitted packets
50  *
51  * This is incremented each time PXENV_UNDI_TRANSMIT is called, and
52  * decremented each time that PXENV_UNDI_ISR is called with the TX
53  * queue empty, stopping when the count reaches zero.  This allows us
54  * to provide a pessimistic approximation of TX completion events to
55  * the PXE NBP simply by monitoring the netdev's TX queue.
56  */
57 static int undi_tx_count = 0;
58
59 struct net_device *pxe_netdev = NULL;
60
61 /** Transmit profiler */
62 static struct profiler undi_tx_profiler __profiler = { .name = "undi.tx" };
63
64 /**
65  * Set network device as current PXE network device
66  *
67  * @v netdev            Network device, or NULL
68  */
69 void pxe_set_netdev ( struct net_device *netdev ) {
70
71         if ( pxe_netdev ) {
72                 netdev_rx_unfreeze ( pxe_netdev );
73                 netdev_put ( pxe_netdev );
74         }
75
76         pxe_netdev = NULL;
77
78         if ( netdev )
79                 pxe_netdev = netdev_get ( netdev );
80 }
81
82 /**
83  * Open PXE network device
84  *
85  * @ret rc              Return status code
86  */
87 static int pxe_netdev_open ( void ) {
88         int rc;
89
90         assert ( pxe_netdev != NULL );
91
92         if ( ( rc = netdev_open ( pxe_netdev ) ) != 0 )
93                 return rc;
94
95         netdev_rx_freeze ( pxe_netdev );
96         netdev_irq ( pxe_netdev, 1 );
97
98         return 0;
99 }
100
101 /**
102  * Close PXE network device
103  *
104  */
105 static void pxe_netdev_close ( void ) {
106
107         assert ( pxe_netdev != NULL );
108         netdev_rx_unfreeze ( pxe_netdev );
109         netdev_irq ( pxe_netdev, 0 );
110         netdev_close ( pxe_netdev );
111         undi_tx_count = 0;
112 }
113
114 /**
115  * Dump multicast address list
116  *
117  * @v mcast             PXE multicast address list
118  */
119 static void pxe_dump_mcast_list ( struct s_PXENV_UNDI_MCAST_ADDRESS *mcast ) {
120         struct ll_protocol *ll_protocol = pxe_netdev->ll_protocol;
121         unsigned int i;
122
123         for ( i = 0 ; i < mcast->MCastAddrCount ; i++ ) {
124                 DBGC ( &pxe_netdev, " %s",
125                        ll_protocol->ntoa ( mcast->McastAddr[i] ) );
126         }
127 }
128
129 /* PXENV_UNDI_STARTUP
130  *
131  * Status: working
132  */
133 static PXENV_EXIT_t
134 pxenv_undi_startup ( struct s_PXENV_UNDI_STARTUP *undi_startup ) {
135         DBGC ( &pxe_netdev, "PXENV_UNDI_STARTUP\n" );
136
137         /* Sanity check */
138         if ( ! pxe_netdev ) {
139                 DBGC ( &pxe_netdev, "PXENV_UNDI_STARTUP called with no "
140                        "network device\n" );
141                 undi_startup->Status = PXENV_STATUS_UNDI_INVALID_STATE;
142                 return PXENV_EXIT_FAILURE;
143         }
144
145         undi_startup->Status = PXENV_STATUS_SUCCESS;
146         return PXENV_EXIT_SUCCESS;
147 }
148
149 /* PXENV_UNDI_CLEANUP
150  *
151  * Status: working
152  */
153 static PXENV_EXIT_t
154 pxenv_undi_cleanup ( struct s_PXENV_UNDI_CLEANUP *undi_cleanup ) {
155         DBGC ( &pxe_netdev, "PXENV_UNDI_CLEANUP\n" );
156
157         /* Sanity check */
158         if ( ! pxe_netdev ) {
159                 DBGC ( &pxe_netdev, "PXENV_UNDI_CLEANUP called with no "
160                        "network device\n" );
161                 undi_cleanup->Status = PXENV_STATUS_UNDI_INVALID_STATE;
162                 return PXENV_EXIT_FAILURE;
163         }
164
165         /* Close network device */
166         pxe_netdev_close();
167
168         undi_cleanup->Status = PXENV_STATUS_SUCCESS;
169         return PXENV_EXIT_SUCCESS;
170 }
171
172 /* PXENV_UNDI_INITIALIZE
173  *
174  * Status: working
175  */
176 static PXENV_EXIT_t
177 pxenv_undi_initialize ( struct s_PXENV_UNDI_INITIALIZE *undi_initialize ) {
178         DBGC ( &pxe_netdev, "PXENV_UNDI_INITIALIZE protocolini %08x\n",
179                undi_initialize->ProtocolIni );
180
181         /* Sanity check */
182         if ( ! pxe_netdev ) {
183                 DBGC ( &pxe_netdev, "PXENV_UNDI_INITIALIZE called with no "
184                        "network device\n" );
185                 undi_initialize->Status = PXENV_STATUS_UNDI_INVALID_STATE;
186                 return PXENV_EXIT_FAILURE;
187         }
188
189         undi_initialize->Status = PXENV_STATUS_SUCCESS;
190         return PXENV_EXIT_SUCCESS;
191 }
192
193 /* PXENV_UNDI_RESET_ADAPTER
194  *
195  * Status: working
196  */
197 static PXENV_EXIT_t
198 pxenv_undi_reset_adapter ( struct s_PXENV_UNDI_RESET *undi_reset_adapter ) {
199         int rc;
200
201         DBGC ( &pxe_netdev, "PXENV_UNDI_RESET_ADAPTER" );
202         pxe_dump_mcast_list ( &undi_reset_adapter->R_Mcast_Buf );
203         DBGC ( &pxe_netdev, "\n" );
204
205         /* Sanity check */
206         if ( ! pxe_netdev ) {
207                 DBGC ( &pxe_netdev, "PXENV_UNDI_RESET_ADAPTER called with no "
208                        "network device\n" );
209                 undi_reset_adapter->Status = PXENV_STATUS_UNDI_INVALID_STATE;
210                 return PXENV_EXIT_FAILURE;
211         }
212
213         /* Close and reopen network device */
214         pxe_netdev_close();
215         if ( ( rc = pxe_netdev_open() ) != 0 ) {
216                 DBGC ( &pxe_netdev, "PXENV_UNDI_RESET_ADAPTER could not "
217                        "reopen %s: %s\n", pxe_netdev->name, strerror ( rc ) );
218                 undi_reset_adapter->Status = PXENV_STATUS ( rc );
219                 return PXENV_EXIT_FAILURE;
220         }
221
222         undi_reset_adapter->Status = PXENV_STATUS_SUCCESS;
223         return PXENV_EXIT_SUCCESS;
224 }
225
226 /* PXENV_UNDI_SHUTDOWN
227  *
228  * Status: working
229  */
230 static PXENV_EXIT_t
231 pxenv_undi_shutdown ( struct s_PXENV_UNDI_SHUTDOWN *undi_shutdown ) {
232         DBGC ( &pxe_netdev, "PXENV_UNDI_SHUTDOWN\n" );
233
234         /* Sanity check */
235         if ( ! pxe_netdev ) {
236                 DBGC ( &pxe_netdev, "PXENV_UNDI_SHUTDOWN called with no "
237                        "network device\n" );
238                 undi_shutdown->Status = PXENV_STATUS_UNDI_INVALID_STATE;
239                 return PXENV_EXIT_FAILURE;
240         }
241
242         /* Close network device */
243         pxe_netdev_close();
244
245         undi_shutdown->Status = PXENV_STATUS_SUCCESS;
246         return PXENV_EXIT_SUCCESS;
247 }
248
249 /* PXENV_UNDI_OPEN
250  *
251  * Status: working
252  */
253 static PXENV_EXIT_t pxenv_undi_open ( struct s_PXENV_UNDI_OPEN *undi_open ) {
254         int rc;
255
256         DBGC ( &pxe_netdev, "PXENV_UNDI_OPEN flag %04x filter %04x",
257                undi_open->OpenFlag, undi_open->PktFilter );
258         pxe_dump_mcast_list ( &undi_open->R_Mcast_Buf );
259         DBGC ( &pxe_netdev, "\n" );
260
261         /* Sanity check */
262         if ( ! pxe_netdev ) {
263                 DBGC ( &pxe_netdev, "PXENV_UNDI_OPEN called with no "
264                        "network device\n" );
265                 undi_open->Status = PXENV_STATUS_UNDI_INVALID_STATE;
266                 return PXENV_EXIT_FAILURE;
267         }
268
269         /* Open network device */
270         if ( ( rc = pxe_netdev_open() ) != 0 ) {
271                 DBGC ( &pxe_netdev, "PXENV_UNDI_OPEN could not open %s: %s\n",
272                        pxe_netdev->name, strerror ( rc ) );
273                 undi_open->Status = PXENV_STATUS ( rc );
274                 return PXENV_EXIT_FAILURE;
275         }
276
277         undi_open->Status = PXENV_STATUS_SUCCESS;
278         return PXENV_EXIT_SUCCESS;
279 }
280
281 /* PXENV_UNDI_CLOSE
282  *
283  * Status: working
284  */
285 static PXENV_EXIT_t pxenv_undi_close ( struct s_PXENV_UNDI_CLOSE *undi_close ) {
286         DBGC ( &pxe_netdev, "PXENV_UNDI_CLOSE\n" );
287
288         /* Sanity check */
289         if ( ! pxe_netdev ) {
290                 DBGC ( &pxe_netdev, "PXENV_UNDI_CLOSE called with no "
291                        "network device\n" );
292                 undi_close->Status = PXENV_STATUS_UNDI_INVALID_STATE;
293                 return PXENV_EXIT_FAILURE;
294         }
295
296         /* Close network device */
297         pxe_netdev_close();
298
299         undi_close->Status = PXENV_STATUS_SUCCESS;
300         return PXENV_EXIT_SUCCESS;
301 }
302
303 /* PXENV_UNDI_TRANSMIT
304  *
305  * Status: working
306  */
307 static PXENV_EXIT_t
308 pxenv_undi_transmit ( struct s_PXENV_UNDI_TRANSMIT *undi_transmit ) {
309         struct s_PXENV_UNDI_TBD tbd;
310         struct DataBlk *datablk;
311         struct io_buffer *iobuf;
312         struct net_protocol *net_protocol;
313         struct ll_protocol *ll_protocol;
314         char destaddr[MAX_LL_ADDR_LEN];
315         const void *ll_dest;
316         size_t len;
317         unsigned int i;
318         int rc;
319
320         /* Start profiling */
321         profile_start ( &undi_tx_profiler );
322
323         /* Sanity check */
324         if ( ! pxe_netdev ) {
325                 DBGC ( &pxe_netdev, "PXENV_UNDI_TRANSMIT called with no "
326                        "network device\n" );
327                 undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_STATE;
328                 return PXENV_EXIT_FAILURE;
329         }
330
331         DBGC2 ( &pxe_netdev, "PXENV_UNDI_TRANSMIT" );
332
333         /* Forcibly enable interrupts and freeze receive queue
334          * processing at this point, to work around callers that never
335          * call PXENV_UNDI_OPEN before attempting to use the UNDI API.
336          */
337         if ( ! netdev_rx_frozen ( pxe_netdev ) ) {
338                 netdev_rx_freeze ( pxe_netdev );
339                 netdev_irq ( pxe_netdev, 1 );
340         }
341
342         /* Identify network-layer protocol */
343         switch ( undi_transmit->Protocol ) {
344         case P_IP:      net_protocol = &ipv4_protocol;  break;
345         case P_ARP:     net_protocol = &arp_protocol;   break;
346         case P_RARP:    net_protocol = &rarp_protocol;  break;
347         case P_UNKNOWN:
348                 net_protocol = NULL;
349                 break;
350         default:
351                 DBGC2 ( &pxe_netdev, " %02x invalid protocol\n",
352                         undi_transmit->Protocol );
353                 undi_transmit->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
354                 return PXENV_EXIT_FAILURE;
355         }
356         DBGC2 ( &pxe_netdev, " %s",
357                 ( net_protocol ? net_protocol->name : "RAW" ) );
358
359         /* Calculate total packet length */
360         copy_from_real ( &tbd, undi_transmit->TBD.segment,
361                          undi_transmit->TBD.offset, sizeof ( tbd ) );
362         len = tbd.ImmedLength;
363         DBGC2 ( &pxe_netdev, " %04x:%04x+%x", tbd.Xmit.segment, tbd.Xmit.offset,
364                 tbd.ImmedLength );
365         for ( i = 0 ; i < tbd.DataBlkCount ; i++ ) {
366                 datablk = &tbd.DataBlock[i];
367                 len += datablk->TDDataLen;
368                 DBGC2 ( &pxe_netdev, " %04x:%04x+%x",
369                         datablk->TDDataPtr.segment, datablk->TDDataPtr.offset,
370                         datablk->TDDataLen );
371         }
372
373         /* Allocate and fill I/O buffer */
374         iobuf = alloc_iob ( MAX_LL_HEADER_LEN +
375                             ( ( len > IOB_ZLEN ) ? len : IOB_ZLEN ) );
376         if ( ! iobuf ) {
377                 DBGC2 ( &pxe_netdev, " could not allocate iobuf\n" );
378                 undi_transmit->Status = PXENV_STATUS_OUT_OF_RESOURCES;
379                 return PXENV_EXIT_FAILURE;
380         }
381         iob_reserve ( iobuf, MAX_LL_HEADER_LEN );
382         copy_from_real ( iob_put ( iobuf, tbd.ImmedLength ), tbd.Xmit.segment,
383                          tbd.Xmit.offset, tbd.ImmedLength );
384         for ( i = 0 ; i < tbd.DataBlkCount ; i++ ) {
385                 datablk = &tbd.DataBlock[i];
386                 copy_from_real ( iob_put ( iobuf, datablk->TDDataLen ),
387                                  datablk->TDDataPtr.segment,
388                                  datablk->TDDataPtr.offset,
389                                  datablk->TDDataLen );
390         }
391
392         /* Add link-layer header, if required to do so */
393         if ( net_protocol != NULL ) {
394
395                 /* Calculate destination address */
396                 ll_protocol = pxe_netdev->ll_protocol;
397                 if ( undi_transmit->XmitFlag == XMT_DESTADDR ) {
398                         copy_from_real ( destaddr,
399                                          undi_transmit->DestAddr.segment,
400                                          undi_transmit->DestAddr.offset,
401                                          ll_protocol->ll_addr_len );
402                         ll_dest = destaddr;
403                         DBGC2 ( &pxe_netdev, " DEST %s",
404                                 ll_protocol->ntoa ( ll_dest ) );
405                 } else {
406                         ll_dest = pxe_netdev->ll_broadcast;
407                         DBGC2 ( &pxe_netdev, " BCAST" );
408                 }
409
410                 /* Add link-layer header */
411                 if ( ( rc = ll_protocol->push ( pxe_netdev, iobuf, ll_dest,
412                                                 pxe_netdev->ll_addr,
413                                                 net_protocol->net_proto ))!=0){
414                         DBGC2 ( &pxe_netdev, " could not add link-layer "
415                                 "header: %s\n", strerror ( rc ) );
416                         free_iob ( iobuf );
417                         undi_transmit->Status = PXENV_STATUS ( rc );
418                         return PXENV_EXIT_FAILURE;
419                 }
420         }
421
422         /* Flag transmission as in-progress.  Do this before starting
423          * to transmit the packet, because the ISR may trigger before
424          * we return from netdev_tx().
425          */
426         undi_tx_count++;
427
428         /* Transmit packet */
429         DBGC2 ( &pxe_netdev, "\n" );
430         if ( ( rc = netdev_tx ( pxe_netdev, iobuf ) ) != 0 ) {
431                 DBGC2 ( &pxe_netdev, "PXENV_UNDI_TRANSMIT could not transmit: "
432                         "%s\n", strerror ( rc ) );
433                 undi_tx_count--;
434                 undi_transmit->Status = PXENV_STATUS ( rc );
435                 return PXENV_EXIT_FAILURE;
436         }
437
438         profile_stop ( &undi_tx_profiler );
439         undi_transmit->Status = PXENV_STATUS_SUCCESS;
440         return PXENV_EXIT_SUCCESS;
441 }
442
443 /* PXENV_UNDI_SET_MCAST_ADDRESS
444  *
445  * Status: working (for NICs that support receive-all-multicast)
446  */
447 static PXENV_EXIT_t
448 pxenv_undi_set_mcast_address ( struct s_PXENV_UNDI_SET_MCAST_ADDRESS
449                                *undi_set_mcast_address ) {
450         DBGC ( &pxe_netdev, "PXENV_UNDI_SET_MCAST_ADDRESS" );
451         pxe_dump_mcast_list ( &undi_set_mcast_address->R_Mcast_Buf );
452         DBGC ( &pxe_netdev, "\n" );
453
454         /* Sanity check */
455         if ( ! pxe_netdev ) {
456                 DBGC ( &pxe_netdev, "PXENV_UNDI_SET_MCAST_ADDRESS called with "
457                        "no network device\n" );
458                 undi_set_mcast_address->Status =
459                         PXENV_STATUS_UNDI_INVALID_STATE;
460                 return PXENV_EXIT_FAILURE;
461         }
462
463         undi_set_mcast_address->Status = PXENV_STATUS_SUCCESS;
464         return PXENV_EXIT_SUCCESS;
465 }
466
467 /* PXENV_UNDI_SET_STATION_ADDRESS
468  *
469  * Status: working
470  */
471 static PXENV_EXIT_t
472 pxenv_undi_set_station_address ( struct s_PXENV_UNDI_SET_STATION_ADDRESS
473                                  *undi_set_station_address ) {
474         struct ll_protocol *ll_protocol;
475
476         /* Sanity check */
477         if ( ! pxe_netdev ) {
478                 DBGC ( &pxe_netdev, "PXENV_UNDI_SET_STATION_ADDRESS called "
479                        "with no network device\n" );
480                 undi_set_station_address->Status =
481                         PXENV_STATUS_UNDI_INVALID_STATE;
482                 return PXENV_EXIT_FAILURE;
483         }
484
485         ll_protocol = pxe_netdev->ll_protocol;
486         DBGC ( &pxe_netdev, "PXENV_UNDI_SET_STATION_ADDRESS %s",
487                ll_protocol->ntoa ( undi_set_station_address->StationAddress ) );
488
489         /* If adapter is open, the change will have no effect; return
490          * an error
491          */
492         if ( netdev_is_open ( pxe_netdev ) ) {
493                 DBGC ( &pxe_netdev, " failed: netdev is open\n" );
494                 undi_set_station_address->Status =
495                         PXENV_STATUS_UNDI_INVALID_STATE;
496                 return PXENV_EXIT_FAILURE;
497         }
498
499         /* Update MAC address */
500         memcpy ( pxe_netdev->ll_addr,
501                  &undi_set_station_address->StationAddress,
502                  ll_protocol->ll_addr_len );
503
504         DBGC ( &pxe_netdev, "\n" );
505         undi_set_station_address->Status = PXENV_STATUS_SUCCESS;
506         return PXENV_EXIT_SUCCESS;
507 }
508
509 /* PXENV_UNDI_SET_PACKET_FILTER
510  *
511  * Status: won't implement (would require driver API changes for no
512  * real benefit)
513  */
514 static PXENV_EXIT_t
515 pxenv_undi_set_packet_filter ( struct s_PXENV_UNDI_SET_PACKET_FILTER
516                                *undi_set_packet_filter ) {
517
518         DBGC ( &pxe_netdev, "PXENV_UNDI_SET_PACKET_FILTER %02x\n",
519                undi_set_packet_filter->filter );
520
521         /* Sanity check */
522         if ( ! pxe_netdev ) {
523                 DBGC ( &pxe_netdev, "PXENV_UNDI_SET_PACKET_FILTER called with "
524                        "no network device\n" );
525                 undi_set_packet_filter->Status =
526                         PXENV_STATUS_UNDI_INVALID_STATE;
527                 return PXENV_EXIT_FAILURE;
528         }
529
530         /* Pretend that we succeeded, otherwise the 3Com DOS UNDI
531          * driver refuses to load.  (We ignore the filter value in the
532          * PXENV_UNDI_OPEN call anyway.)
533          */
534         undi_set_packet_filter->Status = PXENV_STATUS_SUCCESS;
535
536         return PXENV_EXIT_SUCCESS;
537 }
538
539 /* PXENV_UNDI_GET_INFORMATION
540  *
541  * Status: working
542  */
543 static PXENV_EXIT_t
544 pxenv_undi_get_information ( struct s_PXENV_UNDI_GET_INFORMATION
545                              *undi_get_information ) {
546         struct device *dev;
547         struct ll_protocol *ll_protocol;
548
549         /* Sanity check */
550         if ( ! pxe_netdev ) {
551                 DBGC ( &pxe_netdev, "PXENV_UNDI_GET_INFORMATION called with no "
552                        "network device\n" );
553                 undi_get_information->Status = PXENV_STATUS_UNDI_INVALID_STATE;
554                 return PXENV_EXIT_FAILURE;
555         }
556
557         DBGC ( &pxe_netdev, "PXENV_UNDI_GET_INFORMATION" );
558
559         /* Fill in information */
560         dev = pxe_netdev->dev;
561         ll_protocol = pxe_netdev->ll_protocol;
562         undi_get_information->BaseIo = dev->desc.ioaddr;
563         undi_get_information->IntNumber =
564                 ( netdev_irq_supported ( pxe_netdev ) ? dev->desc.irq : 0 );
565         /* Cheat: assume all cards can cope with this */
566         undi_get_information->MaxTranUnit = ETH_MAX_MTU;
567         undi_get_information->HwType = ntohs ( ll_protocol->ll_proto );
568         undi_get_information->HwAddrLen = ll_protocol->ll_addr_len;
569         assert ( ll_protocol->ll_addr_len <=
570                  sizeof ( undi_get_information->CurrentNodeAddress ) );
571         memcpy ( &undi_get_information->CurrentNodeAddress,
572                  pxe_netdev->ll_addr,
573                  sizeof ( undi_get_information->CurrentNodeAddress ) );
574         ll_protocol->init_addr ( pxe_netdev->hw_addr,
575                                  &undi_get_information->PermNodeAddress );
576         undi_get_information->ROMAddress = 0;
577                 /* nic.rom_info->rom_segment; */
578         /* We only provide the ability to receive or transmit a single
579          * packet at a time.  This is a bootloader, not an OS.
580          */
581         undi_get_information->RxBufCt = 1;
582         undi_get_information->TxBufCt = 1;
583
584         DBGC ( &pxe_netdev, " io %04x irq %d mtu %d %s %s\n",
585                undi_get_information->BaseIo, undi_get_information->IntNumber,
586                undi_get_information->MaxTranUnit, ll_protocol->name,
587                ll_protocol->ntoa ( &undi_get_information->CurrentNodeAddress ));
588         undi_get_information->Status = PXENV_STATUS_SUCCESS;
589         return PXENV_EXIT_SUCCESS;
590 }
591
592 /* PXENV_UNDI_GET_STATISTICS
593  *
594  * Status: working
595  */
596 static PXENV_EXIT_t
597 pxenv_undi_get_statistics ( struct s_PXENV_UNDI_GET_STATISTICS
598                             *undi_get_statistics ) {
599
600         /* Sanity check */
601         if ( ! pxe_netdev ) {
602                 DBGC ( &pxe_netdev, "PXENV_UNDI_GET_STATISTICS called with no "
603                        "network device\n" );
604                 undi_get_statistics->Status = PXENV_STATUS_UNDI_INVALID_STATE;
605                 return PXENV_EXIT_FAILURE;
606         }
607
608         DBGC ( &pxe_netdev, "PXENV_UNDI_GET_STATISTICS" );
609
610         /* Report statistics */
611         undi_get_statistics->XmtGoodFrames = pxe_netdev->tx_stats.good;
612         undi_get_statistics->RcvGoodFrames = pxe_netdev->rx_stats.good;
613         undi_get_statistics->RcvCRCErrors = pxe_netdev->rx_stats.bad;
614         undi_get_statistics->RcvResourceErrors = pxe_netdev->rx_stats.bad;
615         DBGC ( &pxe_netdev, " txok %d rxok %d rxcrc %d rxrsrc %d\n",
616                undi_get_statistics->XmtGoodFrames,
617                undi_get_statistics->RcvGoodFrames,
618                undi_get_statistics->RcvCRCErrors,
619                undi_get_statistics->RcvResourceErrors );
620
621         undi_get_statistics->Status = PXENV_STATUS_SUCCESS;
622         return PXENV_EXIT_SUCCESS;
623 }
624
625 /* PXENV_UNDI_CLEAR_STATISTICS
626  *
627  * Status: working
628  */
629 static PXENV_EXIT_t
630 pxenv_undi_clear_statistics ( struct s_PXENV_UNDI_CLEAR_STATISTICS
631                               *undi_clear_statistics ) {
632         DBGC ( &pxe_netdev, "PXENV_UNDI_CLEAR_STATISTICS\n" );
633
634         /* Sanity check */
635         if ( ! pxe_netdev ) {
636                 DBGC ( &pxe_netdev, "PXENV_UNDI_CLEAR_STATISTICS called with "
637                        "no network device\n" );
638                 undi_clear_statistics->Status = PXENV_STATUS_UNDI_INVALID_STATE;
639                 return PXENV_EXIT_FAILURE;
640         }
641
642         /* Clear statistics */
643         memset ( &pxe_netdev->tx_stats, 0, sizeof ( pxe_netdev->tx_stats ) );
644         memset ( &pxe_netdev->rx_stats, 0, sizeof ( pxe_netdev->rx_stats ) );
645
646         undi_clear_statistics->Status = PXENV_STATUS_SUCCESS;
647         return PXENV_EXIT_SUCCESS;
648 }
649
650 /* PXENV_UNDI_INITIATE_DIAGS
651  *
652  * Status: won't implement (would require driver API changes for no
653  * real benefit)
654  */
655 static PXENV_EXIT_t
656 pxenv_undi_initiate_diags ( struct s_PXENV_UNDI_INITIATE_DIAGS
657                             *undi_initiate_diags ) {
658         DBGC ( &pxe_netdev, "PXENV_UNDI_INITIATE_DIAGS failed: unsupported\n" );
659
660         /* Sanity check */
661         if ( ! pxe_netdev ) {
662                 DBGC ( &pxe_netdev, "PXENV_UNDI_INITIATE_DIAGS called with no "
663                        "network device\n" );
664                 undi_initiate_diags->Status = PXENV_STATUS_UNDI_INVALID_STATE;
665                 return PXENV_EXIT_FAILURE;
666         }
667
668         undi_initiate_diags->Status = PXENV_STATUS_UNSUPPORTED;
669         return PXENV_EXIT_FAILURE;
670 }
671
672 /* PXENV_UNDI_FORCE_INTERRUPT
673  *
674  * Status: won't implement (would require driver API changes for no
675  * perceptible benefit)
676  */
677 static PXENV_EXIT_t
678 pxenv_undi_force_interrupt ( struct s_PXENV_UNDI_FORCE_INTERRUPT
679                              *undi_force_interrupt ) {
680         DBGC ( &pxe_netdev,
681                "PXENV_UNDI_FORCE_INTERRUPT failed: unsupported\n" );
682
683         /* Sanity check */
684         if ( ! pxe_netdev ) {
685                 DBGC ( &pxe_netdev, "PXENV_UNDI_FORCE_INTERRUPT called with no "
686                        "network device\n" );
687                 undi_force_interrupt->Status = PXENV_STATUS_UNDI_INVALID_STATE;
688                 return PXENV_EXIT_FAILURE;
689         }
690
691         undi_force_interrupt->Status = PXENV_STATUS_UNSUPPORTED;
692         return PXENV_EXIT_FAILURE;
693 }
694
695 /* PXENV_UNDI_GET_MCAST_ADDRESS
696  *
697  * Status: working
698  */
699 static PXENV_EXIT_t
700 pxenv_undi_get_mcast_address ( struct s_PXENV_UNDI_GET_MCAST_ADDRESS
701                                *undi_get_mcast_address ) {
702         struct ll_protocol *ll_protocol;
703         struct in_addr ip = { .s_addr = undi_get_mcast_address->InetAddr };
704         int rc;
705
706         /* Sanity check */
707         if ( ! pxe_netdev ) {
708                 DBGC ( &pxe_netdev, "PXENV_UNDI_GET_MCAST_ADDRESS called with "
709                        "no network device\n" );
710                 undi_get_mcast_address->Status =
711                         PXENV_STATUS_UNDI_INVALID_STATE;
712                 return PXENV_EXIT_FAILURE;
713         }
714
715         DBGC ( &pxe_netdev, "PXENV_UNDI_GET_MCAST_ADDRESS %s",
716                inet_ntoa ( ip ) );
717
718         /* Hash address using the network device's link-layer protocol */
719         ll_protocol = pxe_netdev->ll_protocol;
720         if ( ( rc = ll_protocol->mc_hash ( AF_INET, &ip,
721                                       undi_get_mcast_address->MediaAddr ))!=0){
722                 DBGC ( &pxe_netdev, " failed: %s\n", strerror ( rc ) );
723                 undi_get_mcast_address->Status = PXENV_STATUS ( rc );
724                 return PXENV_EXIT_FAILURE;
725         }
726         DBGC ( &pxe_netdev, "=>%s\n",
727                ll_protocol->ntoa ( undi_get_mcast_address->MediaAddr ) );
728
729         undi_get_mcast_address->Status = PXENV_STATUS_SUCCESS;
730         return PXENV_EXIT_SUCCESS;
731 }
732
733 /* PXENV_UNDI_GET_NIC_TYPE
734  *
735  * Status: working
736  */
737 static PXENV_EXIT_t pxenv_undi_get_nic_type ( struct s_PXENV_UNDI_GET_NIC_TYPE
738                                               *undi_get_nic_type ) {
739         struct device *dev;
740
741         /* Sanity check */
742         if ( ! pxe_netdev ) {
743                 DBGC ( &pxe_netdev, "PXENV_UNDI_GET_NIC_TYPE called with "
744                        "no network device\n" );
745                 undi_get_nic_type->Status = PXENV_STATUS_UNDI_INVALID_STATE;
746                 return PXENV_EXIT_FAILURE;
747         }
748
749         DBGC ( &pxe_netdev, "PXENV_UNDI_GET_NIC_TYPE" );
750
751         /* Fill in information */
752         memset ( &undi_get_nic_type->info, 0,
753                  sizeof ( undi_get_nic_type->info ) );
754         dev = pxe_netdev->dev;
755         switch ( dev->desc.bus_type ) {
756         case BUS_TYPE_PCI: {
757                 struct pci_nic_info *info = &undi_get_nic_type->info.pci;
758
759                 undi_get_nic_type->NicType = PCI_NIC;
760                 info->Vendor_ID = dev->desc.vendor;
761                 info->Dev_ID = dev->desc.device;
762                 info->Base_Class = PCI_BASE_CLASS ( dev->desc.class );
763                 info->Sub_Class = PCI_SUB_CLASS ( dev->desc.class );
764                 info->Prog_Intf = PCI_PROG_INTF ( dev->desc.class );
765                 info->BusDevFunc = dev->desc.location;
766                 /* Earlier versions of the PXE specification do not
767                  * have the SubVendor_ID and SubDevice_ID fields.  It
768                  * is possible that some NBPs will not provide space
769                  * for them, and so we must not fill them in.
770                  */
771                 DBGC ( &pxe_netdev, " PCI %02x:%02x.%x %04x:%04x "
772                        "('%04x:%04x') %02x%02x%02x rev %02x\n",
773                        PCI_BUS ( info->BusDevFunc ),
774                        PCI_SLOT ( info->BusDevFunc ),
775                        PCI_FUNC ( info->BusDevFunc ), info->Vendor_ID,
776                        info->Dev_ID, info->SubVendor_ID, info->SubDevice_ID,
777                        info->Base_Class, info->Sub_Class, info->Prog_Intf,
778                        info->Rev );
779                 break; }
780         case BUS_TYPE_ISAPNP: {
781                 struct pnp_nic_info *info = &undi_get_nic_type->info.pnp;
782
783                 undi_get_nic_type->NicType = PnP_NIC;
784                 info->EISA_Dev_ID = ( ( dev->desc.vendor << 16 ) |
785                                       dev->desc.device );
786                 info->CardSelNum = dev->desc.location;
787                 /* Cheat: remaining fields are probably unnecessary,
788                  * and would require adding extra code to isapnp.c.
789                  */
790                 DBGC ( &pxe_netdev, " ISAPnP CSN %04x %08x %02x%02x%02x\n",
791                        info->CardSelNum, info->EISA_Dev_ID,
792                        info->Base_Class, info->Sub_Class, info->Prog_Intf );
793                 break; }
794         default:
795                 DBGC ( &pxe_netdev, " failed: unknown bus type\n" );
796                 undi_get_nic_type->Status = PXENV_STATUS_FAILURE;
797                 return PXENV_EXIT_FAILURE;
798         }
799
800         undi_get_nic_type->Status = PXENV_STATUS_SUCCESS;
801         return PXENV_EXIT_SUCCESS;
802 }
803
804 /* PXENV_UNDI_GET_IFACE_INFO
805  *
806  * Status: working
807  */
808 static PXENV_EXIT_t
809 pxenv_undi_get_iface_info ( struct s_PXENV_UNDI_GET_IFACE_INFO
810                             *undi_get_iface_info ) {
811
812         /* Sanity check */
813         if ( ! pxe_netdev ) {
814                 DBGC ( &pxe_netdev, "PXENV_UNDI_GET_IFACE_INFO called with "
815                        "no network device\n" );
816                 undi_get_iface_info->Status = PXENV_STATUS_UNDI_INVALID_STATE;
817                 return PXENV_EXIT_FAILURE;
818         }
819
820         DBGC ( &pxe_netdev, "PXENV_UNDI_GET_IFACE_INFO" );
821
822         /* Just hand back some info, doesn't really matter what it is.
823          * Most PXE stacks seem to take this approach.
824          */
825         snprintf ( ( char * ) undi_get_iface_info->IfaceType,
826                    sizeof ( undi_get_iface_info->IfaceType ), "DIX+802.3" );
827         undi_get_iface_info->LinkSpeed = 10000000; /* 10 Mbps */
828         undi_get_iface_info->ServiceFlags =
829                 ( SUPPORTED_BROADCAST | SUPPORTED_MULTICAST |
830                   SUPPORTED_SET_STATION_ADDRESS | SUPPORTED_RESET |
831                   SUPPORTED_OPEN_CLOSE );
832         if ( netdev_irq_supported ( pxe_netdev ) )
833                 undi_get_iface_info->ServiceFlags |= SUPPORTED_IRQ;
834         memset ( undi_get_iface_info->Reserved, 0,
835                  sizeof(undi_get_iface_info->Reserved) );
836
837         DBGC ( &pxe_netdev, " %s %dbps flags %08x\n",
838                undi_get_iface_info->IfaceType, undi_get_iface_info->LinkSpeed,
839                undi_get_iface_info->ServiceFlags );
840         undi_get_iface_info->Status = PXENV_STATUS_SUCCESS;
841         return PXENV_EXIT_SUCCESS;
842 }
843
844 /* PXENV_UNDI_GET_STATE
845  *
846  * Status: impossible due to opcode collision
847  */
848
849 /* PXENV_UNDI_ISR
850  *
851  * Status: working
852  */
853 static PXENV_EXIT_t pxenv_undi_isr ( struct s_PXENV_UNDI_ISR *undi_isr ) {
854         struct io_buffer *iobuf;
855         size_t len;
856         struct ll_protocol *ll_protocol;
857         const void *ll_dest;
858         const void *ll_source;
859         uint16_t net_proto;
860         unsigned int flags;
861         size_t ll_hlen;
862         struct net_protocol *net_protocol;
863         unsigned int prottype;
864         int rc;
865
866         /* Use a different debug colour, since UNDI ISR messages are
867          * likely to be interspersed amongst other UNDI messages.
868          */
869
870         /* Sanity check */
871         if ( ! pxe_netdev ) {
872                 DBGC ( &pxenv_undi_isr, "PXENV_UNDI_ISR called with "
873                        "no network device\n" );
874                 undi_isr->Status = PXENV_STATUS_UNDI_INVALID_STATE;
875                 return PXENV_EXIT_FAILURE;
876         }
877
878         DBGC2 ( &pxenv_undi_isr, "PXENV_UNDI_ISR" );
879
880         /* Just in case some idiot actually looks at these fields when
881          * we weren't meant to fill them in...
882          */
883         undi_isr->BufferLength = 0;
884         undi_isr->FrameLength = 0;
885         undi_isr->FrameHeaderLength = 0;
886         undi_isr->ProtType = 0;
887         undi_isr->PktType = 0;
888
889         switch ( undi_isr->FuncFlag ) {
890         case PXENV_UNDI_ISR_IN_START :
891                 DBGC2 ( &pxenv_undi_isr, " START" );
892
893                 /* Call poll().  This should acknowledge the device
894                  * interrupt and queue up any received packet.
895                  */
896                 net_poll();
897
898                 /* A 100% accurate determination of "OURS" vs "NOT
899                  * OURS" is difficult to achieve without invasive and
900                  * unpleasant changes to the driver model.  We settle
901                  * for always returning "OURS" if interrupts are
902                  * currently enabled.
903                  *
904                  * Returning "NOT OURS" when interrupts are disabled
905                  * allows us to avoid a potential interrupt storm when
906                  * we are on a shared interrupt line; if we were to
907                  * always return "OURS" then the other device's ISR
908                  * may never be called.
909                  */
910                 if ( netdev_irq_enabled ( pxe_netdev ) ) {
911                         DBGC2 ( &pxenv_undi_isr, " OURS" );
912                         undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_OURS;
913                 } else {
914                         DBGC2 ( &pxenv_undi_isr, " NOT OURS" );
915                         undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_NOT_OURS;
916                 }
917
918                 /* Disable interrupts */
919                 netdev_irq ( pxe_netdev, 0 );
920
921                 break;
922         case PXENV_UNDI_ISR_IN_PROCESS :
923         case PXENV_UNDI_ISR_IN_GET_NEXT :
924                 DBGC2 ( &pxenv_undi_isr, " %s",
925                         ( ( undi_isr->FuncFlag == PXENV_UNDI_ISR_IN_PROCESS ) ?
926                           "PROCESS" : "GET_NEXT" ) );
927
928                 /* Some dumb NBPs (e.g. emBoot's winBoot/i) never call
929                  * PXENV_UNDI_ISR with FuncFlag=PXENV_UNDI_ISR_START;
930                  * they just sit in a tight polling loop merrily
931                  * violating the PXE spec with repeated calls to
932                  * PXENV_UNDI_ISR_IN_PROCESS.  Force extra polls to
933                  * cope with these out-of-spec clients.
934                  */
935                 net_poll();
936
937                 /* If we have not yet marked a TX as complete, and the
938                  * netdev TX queue is empty, report the TX completion.
939                  */
940                 if ( undi_tx_count && list_empty ( &pxe_netdev->tx_queue ) ) {
941                         DBGC2 ( &pxenv_undi_isr, " TXC" );
942                         undi_tx_count--;
943                         undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_TRANSMIT;
944                         break;
945                 }
946
947                 /* Remove first packet from netdev RX queue */
948                 iobuf = netdev_rx_dequeue ( pxe_netdev );
949                 if ( ! iobuf ) {
950                         DBGC2 ( &pxenv_undi_isr, " DONE" );
951                         /* No more packets remaining */
952                         undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
953                         /* Re-enable interrupts */
954                         netdev_irq ( pxe_netdev, 1 );
955                         break;
956                 }
957
958                 /* Copy packet to base memory buffer */
959                 len = iob_len ( iobuf );
960                 DBGC2 ( &pxenv_undi_isr, " RX" );
961                 if ( len > sizeof ( basemem_packet ) ) {
962                         /* Should never happen */
963                         DBGC2 ( &pxenv_undi_isr, " overlength (%zx)", len );
964                         len = sizeof ( basemem_packet );
965                 }
966                 memcpy ( basemem_packet, iobuf->data, len );
967
968                 /* Strip link-layer header */
969                 ll_protocol = pxe_netdev->ll_protocol;
970                 if ( ( rc = ll_protocol->pull ( pxe_netdev, iobuf, &ll_dest,
971                                                 &ll_source, &net_proto,
972                                                 &flags ) ) != 0 ) {
973                         /* Assume unknown net_proto and no ll_source */
974                         net_proto = 0;
975                         ll_source = NULL;
976                 }
977                 ll_hlen = ( len - iob_len ( iobuf ) );
978
979                 /* Determine network-layer protocol */
980                 switch ( net_proto ) {
981                 case htons ( ETH_P_IP ):
982                         net_protocol = &ipv4_protocol;
983                         prottype = P_IP;
984                         break;
985                 case htons ( ETH_P_ARP ):
986                         net_protocol = &arp_protocol;
987                         prottype = P_ARP;
988                         break;
989                 case htons ( ETH_P_RARP ):
990                         net_protocol = &rarp_protocol;
991                         prottype = P_RARP;
992                         break;
993                 default:
994                         net_protocol = NULL;
995                         prottype = P_UNKNOWN;
996                         break;
997                 }
998
999                 /* Fill in UNDI_ISR structure */
1000                 undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_RECEIVE;
1001                 undi_isr->BufferLength = len;
1002                 undi_isr->FrameLength = len;
1003                 undi_isr->FrameHeaderLength = ll_hlen;
1004                 undi_isr->Frame.segment = rm_ds;
1005                 undi_isr->Frame.offset = __from_data16 ( basemem_packet );
1006                 undi_isr->ProtType = prottype;
1007                 if ( flags & LL_BROADCAST ) {
1008                         undi_isr->PktType = P_BROADCAST;
1009                 } else if ( flags & LL_MULTICAST ) {
1010                         undi_isr->PktType = P_MULTICAST;
1011                 } else {
1012                         undi_isr->PktType = P_DIRECTED;
1013                 }
1014                 DBGC2 ( &pxenv_undi_isr, " %04x:%04x+%x(%x) %s hlen %d",
1015                         undi_isr->Frame.segment, undi_isr->Frame.offset,
1016                         undi_isr->BufferLength, undi_isr->FrameLength,
1017                         ( net_protocol ? net_protocol->name : "RAW" ),
1018                         undi_isr->FrameHeaderLength );
1019
1020                 /* Free packet */
1021                 free_iob ( iobuf );
1022                 break;
1023         default :
1024                 DBGC2 ( &pxenv_undi_isr, " INVALID(%04x)\n",
1025                         undi_isr->FuncFlag );
1026
1027                 /* Should never happen */
1028                 undi_isr->FuncFlag = PXENV_UNDI_ISR_OUT_DONE;
1029                 undi_isr->Status = PXENV_STATUS_UNDI_INVALID_PARAMETER;
1030                 return PXENV_EXIT_FAILURE;
1031         }
1032
1033         DBGC2 ( &pxenv_undi_isr, "\n" );
1034         undi_isr->Status = PXENV_STATUS_SUCCESS;
1035         return PXENV_EXIT_SUCCESS;
1036 }
1037
1038 /** PXE UNDI API */
1039 struct pxe_api_call pxe_undi_api[] __pxe_api_call = {
1040         PXE_API_CALL ( PXENV_UNDI_STARTUP, pxenv_undi_startup,
1041                        struct s_PXENV_UNDI_STARTUP ),
1042         PXE_API_CALL ( PXENV_UNDI_CLEANUP, pxenv_undi_cleanup,
1043                        struct s_PXENV_UNDI_CLEANUP ),
1044         PXE_API_CALL ( PXENV_UNDI_INITIALIZE, pxenv_undi_initialize,
1045                        struct s_PXENV_UNDI_INITIALIZE ),
1046         PXE_API_CALL ( PXENV_UNDI_RESET_ADAPTER, pxenv_undi_reset_adapter,
1047                        struct s_PXENV_UNDI_RESET ),
1048         PXE_API_CALL ( PXENV_UNDI_SHUTDOWN, pxenv_undi_shutdown,
1049                        struct s_PXENV_UNDI_SHUTDOWN ),
1050         PXE_API_CALL ( PXENV_UNDI_OPEN, pxenv_undi_open,
1051                        struct s_PXENV_UNDI_OPEN ),
1052         PXE_API_CALL ( PXENV_UNDI_CLOSE, pxenv_undi_close,
1053                        struct s_PXENV_UNDI_CLOSE ),
1054         PXE_API_CALL ( PXENV_UNDI_TRANSMIT, pxenv_undi_transmit,
1055                        struct s_PXENV_UNDI_TRANSMIT ),
1056         PXE_API_CALL ( PXENV_UNDI_SET_MCAST_ADDRESS,
1057                        pxenv_undi_set_mcast_address,
1058                        struct s_PXENV_UNDI_SET_MCAST_ADDRESS ),
1059         PXE_API_CALL ( PXENV_UNDI_SET_STATION_ADDRESS,
1060                        pxenv_undi_set_station_address,
1061                        struct s_PXENV_UNDI_SET_STATION_ADDRESS ),
1062         PXE_API_CALL ( PXENV_UNDI_SET_PACKET_FILTER,
1063                        pxenv_undi_set_packet_filter,
1064                        struct s_PXENV_UNDI_SET_PACKET_FILTER ),
1065         PXE_API_CALL ( PXENV_UNDI_GET_INFORMATION, pxenv_undi_get_information,
1066                        struct s_PXENV_UNDI_GET_INFORMATION ),
1067         PXE_API_CALL ( PXENV_UNDI_GET_STATISTICS, pxenv_undi_get_statistics,
1068                        struct s_PXENV_UNDI_GET_STATISTICS ),
1069         PXE_API_CALL ( PXENV_UNDI_CLEAR_STATISTICS, pxenv_undi_clear_statistics,
1070                        struct s_PXENV_UNDI_CLEAR_STATISTICS ),
1071         PXE_API_CALL ( PXENV_UNDI_INITIATE_DIAGS, pxenv_undi_initiate_diags,
1072                        struct s_PXENV_UNDI_INITIATE_DIAGS ),
1073         PXE_API_CALL ( PXENV_UNDI_FORCE_INTERRUPT, pxenv_undi_force_interrupt,
1074                        struct s_PXENV_UNDI_FORCE_INTERRUPT ),
1075         PXE_API_CALL ( PXENV_UNDI_GET_MCAST_ADDRESS,
1076                        pxenv_undi_get_mcast_address,
1077                        struct s_PXENV_UNDI_GET_MCAST_ADDRESS ),
1078         PXE_API_CALL ( PXENV_UNDI_GET_NIC_TYPE, pxenv_undi_get_nic_type,
1079                        struct s_PXENV_UNDI_GET_NIC_TYPE ),
1080         PXE_API_CALL ( PXENV_UNDI_GET_IFACE_INFO, pxenv_undi_get_iface_info,
1081                        struct s_PXENV_UNDI_GET_IFACE_INFO ),
1082         PXE_API_CALL ( PXENV_UNDI_ISR, pxenv_undi_isr,
1083                        struct s_PXENV_UNDI_ISR ),
1084 };