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