Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / ipxe / src / arch / i386 / drivers / net / undinet.c
1 /*
2  * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  */
19
20 FILE_LICENCE ( GPL2_OR_LATER );
21
22 #include <string.h>
23 #include <unistd.h>
24 #include <byteswap.h>
25 #include <pxe.h>
26 #include <realmode.h>
27 #include <pic8259.h>
28 #include <biosint.h>
29 #include <pnpbios.h>
30 #include <basemem_packet.h>
31 #include <ipxe/io.h>
32 #include <ipxe/iobuf.h>
33 #include <ipxe/netdevice.h>
34 #include <ipxe/if_ether.h>
35 #include <ipxe/ethernet.h>
36 #include <ipxe/profile.h>
37 #include <undi.h>
38 #include <undinet.h>
39 #include <pxeparent.h>
40
41 /** @file
42  *
43  * UNDI network device driver
44  *
45  */
46
47 /** An UNDI NIC */
48 struct undi_nic {
49         /** Device supports IRQs */
50         int irq_supported;
51         /** Assigned IRQ number */
52         unsigned int irq;
53         /** Currently processing ISR */
54         int isr_processing;
55         /** Bug workarounds */
56         int hacks;
57 };
58
59 /**
60  * @defgroup undi_hacks UNDI workarounds
61  * @{
62  */
63
64 /** Work around Etherboot 5.4 bugs */
65 #define UNDI_HACK_EB54          0x0001
66
67 /** @} */
68
69 /** Maximum number of times to retry PXENV_UNDI_INITIALIZE */
70 #define UNDI_INITIALIZE_RETRY_MAX 10
71
72 /** Delay between retries of PXENV_UNDI_INITIALIZE */
73 #define UNDI_INITIALIZE_RETRY_DELAY_MS 200
74
75 /** Maximum number of received packets per poll */
76 #define UNDI_RX_QUOTA 4
77
78 /** Alignment of received frame payload */
79 #define UNDI_RX_ALIGN 16
80
81 static void undinet_close ( struct net_device *netdev );
82
83 /** Address of UNDI entry point */
84 static SEGOFF16_t undinet_entry;
85
86 /** Transmit profiler */
87 static struct profiler undinet_tx_profiler __profiler =
88         { .name = "undinet.tx" };
89
90 /** Transmit call profiler */
91 static struct profiler undinet_tx_call_profiler __profiler =
92         { .name = "undinet.tx_call" };
93
94 /** IRQ profiler */
95 static struct profiler undinet_irq_profiler __profiler =
96         { .name = "undinet.irq" };
97
98 /** ISR call profiler */
99 static struct profiler undinet_isr_call_profiler __profiler =
100         { .name = "undinet.isr_call" };
101
102 /** Receive profiler */
103 static struct profiler undinet_rx_profiler __profiler =
104         { .name = "undinet.rx" };
105
106 /*****************************************************************************
107  *
108  * UNDI interrupt service routine
109  *
110  *****************************************************************************
111  */
112
113 /**
114  * UNDI interrupt service routine
115  *
116  * The UNDI ISR increments a counter (@c trigger_count) and exits.
117  */
118 extern void undiisr ( void );
119
120 /** IRQ number */
121 uint8_t __data16 ( undiisr_irq );
122 #define undiisr_irq __use_data16 ( undiisr_irq )
123
124 /** IRQ chain vector */
125 struct segoff __data16 ( undiisr_next_handler );
126 #define undiisr_next_handler __use_data16 ( undiisr_next_handler )
127
128 /** IRQ trigger count */
129 volatile uint8_t __data16 ( undiisr_trigger_count ) = 0;
130 #define undiisr_trigger_count __use_data16 ( undiisr_trigger_count )
131
132 /** Last observed trigger count */
133 static unsigned int last_trigger_count = 0;
134
135 /**
136  * Hook UNDI interrupt service routine
137  *
138  * @v irq               IRQ number
139  */
140 static void undinet_hook_isr ( unsigned int irq ) {
141
142         assert ( irq <= IRQ_MAX );
143         assert ( undiisr_irq == 0 );
144
145         undiisr_irq = irq;
146         hook_bios_interrupt ( IRQ_INT ( irq ),
147                               ( ( unsigned int ) undiisr ),
148                               &undiisr_next_handler );
149 }
150
151 /**
152  * Unhook UNDI interrupt service routine
153  *
154  * @v irq               IRQ number
155  */
156 static void undinet_unhook_isr ( unsigned int irq ) {
157
158         assert ( irq <= IRQ_MAX );
159
160         unhook_bios_interrupt ( IRQ_INT ( irq ),
161                                 ( ( unsigned int ) undiisr ),
162                                 &undiisr_next_handler );
163         undiisr_irq = 0;
164 }
165
166 /**
167  * Test to see if UNDI ISR has been triggered
168  *
169  * @ret triggered       ISR has been triggered since last check
170  */
171 static int undinet_isr_triggered ( void ) {
172         unsigned int this_trigger_count;
173
174         /* Read trigger_count.  Do this only once; it is volatile */
175         this_trigger_count = undiisr_trigger_count;
176
177         if ( this_trigger_count == last_trigger_count ) {
178                 /* Not triggered */
179                 return 0;
180         } else {
181                 /* Triggered */
182                 last_trigger_count = this_trigger_count;
183                 return 1;
184         }
185 }
186
187 /*****************************************************************************
188  *
189  * UNDI network device interface
190  *
191  *****************************************************************************
192  */
193
194 /** UNDI transmit buffer descriptor */
195 static struct s_PXENV_UNDI_TBD __data16 ( undinet_tbd );
196 #define undinet_tbd __use_data16 ( undinet_tbd )
197
198 /** UNDI transmit destination address */
199 static uint8_t __data16_array ( undinet_destaddr, [ETH_ALEN] );
200 #define undinet_destaddr __use_data16 ( undinet_destaddr )
201
202 /**
203  * Transmit packet
204  *
205  * @v netdev            Network device
206  * @v iobuf             I/O buffer
207  * @ret rc              Return status code
208  */
209 static int undinet_transmit ( struct net_device *netdev,
210                               struct io_buffer *iobuf ) {
211         struct undi_nic *undinic = netdev->priv;
212         struct s_PXENV_UNDI_TRANSMIT undi_transmit;
213         const void *ll_dest;
214         const void *ll_source;
215         uint16_t net_proto;
216         unsigned int flags;
217         uint8_t protocol;
218         size_t len;
219         int rc;
220
221         /* Start profiling */
222         profile_start ( &undinet_tx_profiler );
223
224         /* Technically, we ought to make sure that the previous
225          * transmission has completed before we re-use the buffer.
226          * However, many PXE stacks (including at least some Intel PXE
227          * stacks and Etherboot 5.4) fail to generate TX completions.
228          * In practice this won't be a problem, since our TX datapath
229          * has a very low packet volume and we can get away with
230          * assuming that a TX will be complete by the time we want to
231          * transmit the next packet.
232          */
233
234         /* Some PXE stacks are unable to cope with P_UNKNOWN, and will
235          * always try to prepend a link-layer header.  Work around
236          * these stacks by stripping the existing link-layer header
237          * and allowing the PXE stack to (re)construct the link-layer
238          * header itself.
239          */
240         if ( ( rc = eth_pull ( netdev, iobuf, &ll_dest, &ll_source,
241                                &net_proto, &flags ) ) != 0 ) {
242                 DBGC ( undinic, "UNDINIC %p could not strip Ethernet header: "
243                        "%s\n", undinic, strerror ( rc ) );
244                 return rc;
245         }
246         memcpy ( undinet_destaddr, ll_dest, sizeof ( undinet_destaddr ) );
247         switch ( net_proto ) {
248         case htons ( ETH_P_IP ) :
249                 protocol = P_IP;
250                 break;
251         case htons ( ETH_P_ARP ) :
252                 protocol = P_ARP;
253                 break;
254         case htons ( ETH_P_RARP ) :
255                 protocol = P_RARP;
256                 break;
257         default:
258                 /* Unknown protocol; restore the original link-layer header */
259                 iob_push ( iobuf, sizeof ( struct ethhdr ) );
260                 protocol = P_UNKNOWN;
261                 break;
262         }
263
264         /* Copy packet to UNDI I/O buffer */
265         len = iob_len ( iobuf );
266         if ( len > sizeof ( basemem_packet ) )
267                 len = sizeof ( basemem_packet );
268         memcpy ( &basemem_packet, iobuf->data, len );
269
270         /* Create PXENV_UNDI_TRANSMIT data structure */
271         memset ( &undi_transmit, 0, sizeof ( undi_transmit ) );
272         undi_transmit.Protocol = protocol;
273         undi_transmit.XmitFlag = ( ( flags & LL_BROADCAST ) ?
274                                    XMT_BROADCAST : XMT_DESTADDR );
275         undi_transmit.DestAddr.segment = rm_ds;
276         undi_transmit.DestAddr.offset = __from_data16 ( &undinet_destaddr );
277         undi_transmit.TBD.segment = rm_ds;
278         undi_transmit.TBD.offset = __from_data16 ( &undinet_tbd );
279
280         /* Create PXENV_UNDI_TBD data structure */
281         undinet_tbd.ImmedLength = len;
282         undinet_tbd.Xmit.segment = rm_ds;
283         undinet_tbd.Xmit.offset = __from_data16 ( basemem_packet );
284
285         /* Issue PXE API call */
286         profile_start ( &undinet_tx_call_profiler );
287         if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_TRANSMIT,
288                                      &undi_transmit,
289                                      sizeof ( undi_transmit ) ) ) != 0 )
290                 goto done;
291         profile_stop ( &undinet_tx_call_profiler );
292
293         /* Free I/O buffer */
294         netdev_tx_complete ( netdev, iobuf );
295         profile_stop ( &undinet_tx_profiler );
296  done:
297         return rc;
298 }
299
300 /** 
301  * Poll for received packets
302  *
303  * @v netdev            Network device
304  *
305  * Fun, fun, fun.  UNDI drivers don't use polling; they use
306  * interrupts.  We therefore cheat and pretend that an interrupt has
307  * occurred every time undinet_poll() is called.  This isn't too much
308  * of a hack; PCI devices share IRQs and so the first thing that a
309  * proper ISR should do is call PXENV_UNDI_ISR to determine whether or
310  * not the UNDI NIC generated the interrupt; there is no harm done by
311  * spurious calls to PXENV_UNDI_ISR.  Similarly, we wouldn't be
312  * handling them any more rapidly than the usual rate of
313  * undinet_poll() being called even if we did implement a full ISR.
314  * So it should work.  Ha!
315  *
316  * Addendum (21/10/03).  Some cards don't play nicely with this trick,
317  * so instead of doing it the easy way we have to go to all the hassle
318  * of installing a genuine interrupt service routine and dealing with
319  * the wonderful 8259 Programmable Interrupt Controller.  Joy.
320  *
321  * Addendum (10/07/07).  When doing things such as iSCSI boot, in
322  * which we have to co-operate with a running OS, we can't get away
323  * with the "ISR-just-increments-a-counter-and-returns" trick at all,
324  * because it involves tying up the PIC for far too long, and other
325  * interrupt-dependent components (e.g. local disks) start breaking.
326  * We therefore implement a "proper" ISR which calls PXENV_UNDI_ISR
327  * from within interrupt context in order to deassert the device
328  * interrupt, and sends EOI if applicable.
329  */
330 static void undinet_poll ( struct net_device *netdev ) {
331         struct undi_nic *undinic = netdev->priv;
332         struct s_PXENV_UNDI_ISR undi_isr;
333         struct io_buffer *iobuf = NULL;
334         unsigned int quota = UNDI_RX_QUOTA;
335         size_t len;
336         size_t reserve_len;
337         size_t frag_len;
338         size_t max_frag_len;
339         int rc;
340
341         if ( ! undinic->isr_processing ) {
342                 /* Allow interrupt to occur.  Do this even if
343                  * interrupts are not known to be supported, since
344                  * some cards erroneously report that they do not
345                  * support interrupts.
346                  */
347                 if ( ! undinet_isr_triggered() ) {
348                         /* Allow interrupt to occur */
349                         profile_start ( &undinet_irq_profiler );
350                         __asm__ __volatile__ ( "sti\n\t"
351                                                "nop\n\t"
352                                                "nop\n\t"
353                                                "cli\n\t" );
354                         profile_stop ( &undinet_irq_profiler );
355
356                         /* If interrupts are known to be supported,
357                          * then do nothing on this poll; wait for the
358                          * interrupt to be triggered.
359                          */
360                         if ( undinic->irq_supported )
361                                 return;
362                 }
363
364                 /* Start ISR processing */
365                 undinic->isr_processing = 1;
366                 undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_PROCESS;
367         } else {
368                 /* Continue ISR processing */
369                 undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
370         }
371
372         /* Run through the ISR loop */
373         while ( quota ) {
374                 profile_start ( &undinet_isr_call_profiler );
375                 if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_ISR,
376                                              &undi_isr,
377                                              sizeof ( undi_isr ) ) ) != 0 ) {
378                         netdev_rx_err ( netdev, NULL, rc );
379                         break;
380                 }
381                 profile_stop ( &undinet_isr_call_profiler );
382                 switch ( undi_isr.FuncFlag ) {
383                 case PXENV_UNDI_ISR_OUT_TRANSMIT:
384                         /* We don't care about transmit completions */
385                         break;
386                 case PXENV_UNDI_ISR_OUT_RECEIVE:
387                         /* Packet fragment received */
388                         profile_start ( &undinet_rx_profiler );
389                         len = undi_isr.FrameLength;
390                         frag_len = undi_isr.BufferLength;
391                         reserve_len = ( -undi_isr.FrameHeaderLength &
392                                         ( UNDI_RX_ALIGN - 1 ) );
393                         if ( ( len == 0 ) || ( len < frag_len ) ) {
394                                 /* Don't laugh.  VMWare does it. */
395                                 DBGC ( undinic, "UNDINIC %p reported insane "
396                                        "fragment (%zd of %zd bytes)\n",
397                                        undinic, frag_len, len );
398                                 netdev_rx_err ( netdev, NULL, -EINVAL );
399                                 break;
400                         }
401                         if ( ! iobuf ) {
402                                 iobuf = alloc_iob ( reserve_len + len );
403                                 if ( ! iobuf ) {
404                                         DBGC ( undinic, "UNDINIC %p could not "
405                                                "allocate %zd bytes for RX "
406                                                "buffer\n", undinic, len );
407                                         /* Fragment will be dropped */
408                                         netdev_rx_err ( netdev, NULL, -ENOMEM );
409                                         goto done;
410                                 }
411                                 iob_reserve ( iobuf, reserve_len );
412                         }
413                         max_frag_len = iob_tailroom ( iobuf );
414                         if ( frag_len > max_frag_len ) {
415                                 DBGC ( undinic, "UNDINIC %p fragment too big "
416                                        "(%zd+%zd does not fit into %zd)\n",
417                                        undinic, iob_len ( iobuf ), frag_len,
418                                        ( iob_len ( iobuf ) + max_frag_len ) );
419                                 frag_len = max_frag_len;
420                         }
421                         copy_from_real ( iob_put ( iobuf, frag_len ),
422                                          undi_isr.Frame.segment,
423                                          undi_isr.Frame.offset, frag_len );
424                         if ( iob_len ( iobuf ) == len ) {
425                                 /* Whole packet received; deliver it */
426                                 netdev_rx ( netdev, iob_disown ( iobuf ) );
427                                 quota--;
428                                 /* Etherboot 5.4 fails to return all packets
429                                  * under mild load; pretend it retriggered.
430                                  */
431                                 if ( undinic->hacks & UNDI_HACK_EB54 )
432                                         --last_trigger_count;
433                         }
434                         profile_stop ( &undinet_rx_profiler );
435                         break;
436                 case PXENV_UNDI_ISR_OUT_DONE:
437                         /* Processing complete */
438                         undinic->isr_processing = 0;
439                         goto done;
440                 default:
441                         /* Should never happen.  VMWare does it routinely. */
442                         DBGC ( undinic, "UNDINIC %p ISR returned invalid "
443                                "FuncFlag %04x\n", undinic, undi_isr.FuncFlag );
444                         undinic->isr_processing = 0;
445                         goto done;
446                 }
447                 undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
448         }
449
450  done:
451         if ( iobuf ) {
452                 DBGC ( undinic, "UNDINIC %p returned incomplete packet "
453                        "(%zd of %zd)\n", undinic, iob_len ( iobuf ),
454                        ( iob_len ( iobuf ) + iob_tailroom ( iobuf ) ) );
455                 netdev_rx_err ( netdev, iobuf, -EINVAL );
456         }
457 }
458
459 /**
460  * Open NIC
461  *
462  * @v netdev            Net device
463  * @ret rc              Return status code
464  */
465 static int undinet_open ( struct net_device *netdev ) {
466         struct undi_nic *undinic = netdev->priv;
467         struct s_PXENV_UNDI_SET_STATION_ADDRESS undi_set_address;
468         struct s_PXENV_UNDI_OPEN undi_open;
469         int rc;
470
471         /* Hook interrupt service routine and enable interrupt if applicable */
472         if ( undinic->irq ) {
473                 undinet_hook_isr ( undinic->irq );
474                 enable_irq ( undinic->irq );
475                 send_eoi ( undinic->irq );
476         }
477
478         /* Set station address.  Required for some PXE stacks; will
479          * spuriously fail on others.  Ignore failures.  We only ever
480          * use it to set the MAC address to the card's permanent value
481          * anyway.
482          */
483         memcpy ( undi_set_address.StationAddress, netdev->ll_addr,
484                  sizeof ( undi_set_address.StationAddress ) );
485         pxeparent_call ( undinet_entry, PXENV_UNDI_SET_STATION_ADDRESS,
486                          &undi_set_address, sizeof ( undi_set_address ) );
487
488         /* Open NIC.  We ask for promiscuous operation, since it's the
489          * only way to ask for all multicast addresses.  On any
490          * switched network, it shouldn't really make a difference to
491          * performance.
492          */
493         memset ( &undi_open, 0, sizeof ( undi_open ) );
494         undi_open.PktFilter = ( FLTR_DIRECTED | FLTR_BRDCST | FLTR_PRMSCS );
495         if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_OPEN,
496                                      &undi_open, sizeof ( undi_open ) ) ) != 0 )
497                 goto err;
498
499         DBGC ( undinic, "UNDINIC %p opened\n", undinic );
500         return 0;
501
502  err:
503         undinet_close ( netdev );
504         return rc;
505 }
506
507 /**
508  * Close NIC
509  *
510  * @v netdev            Net device
511  */
512 static void undinet_close ( struct net_device *netdev ) {
513         struct undi_nic *undinic = netdev->priv;
514         struct s_PXENV_UNDI_ISR undi_isr;
515         struct s_PXENV_UNDI_CLOSE undi_close;
516         int rc;
517
518         /* Ensure ISR has exited cleanly */
519         while ( undinic->isr_processing ) {
520                 undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
521                 if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_ISR,
522                                              &undi_isr,
523                                              sizeof ( undi_isr ) ) ) != 0 )
524                         break;
525                 switch ( undi_isr.FuncFlag ) {
526                 case PXENV_UNDI_ISR_OUT_TRANSMIT:
527                 case PXENV_UNDI_ISR_OUT_RECEIVE:
528                         /* Continue draining */
529                         break;
530                 default:
531                         /* Stop processing */
532                         undinic->isr_processing = 0;
533                         break;
534                 }
535         }
536
537         /* Close NIC */
538         pxeparent_call ( undinet_entry, PXENV_UNDI_CLOSE,
539                          &undi_close, sizeof ( undi_close ) );
540
541         /* Disable interrupt and unhook ISR if applicable */
542         if ( undinic->irq ) {
543                 disable_irq ( undinic->irq );
544                 undinet_unhook_isr ( undinic->irq );
545         }
546
547         DBGC ( undinic, "UNDINIC %p closed\n", undinic );
548 }
549
550 /**
551  * Enable/disable interrupts
552  *
553  * @v netdev            Net device
554  * @v enable            Interrupts should be enabled
555  */
556 static void undinet_irq ( struct net_device *netdev, int enable ) {
557         struct undi_nic *undinic = netdev->priv;
558
559         /* Cannot support interrupts yet */
560         DBGC ( undinic, "UNDINIC %p cannot %s interrupts\n",
561                undinic, ( enable ? "enable" : "disable" ) );
562 }
563
564 /** UNDI network device operations */
565 static struct net_device_operations undinet_operations = {
566         .open           = undinet_open,
567         .close          = undinet_close,
568         .transmit       = undinet_transmit,
569         .poll           = undinet_poll,
570         .irq            = undinet_irq,
571 };
572
573 /** A device with broken support for generating interrupts */
574 struct undinet_irq_broken {
575         /** PCI vendor ID */
576         uint16_t pci_vendor;
577         /** PCI device ID */
578         uint16_t pci_device;
579 };
580
581 /**
582  * List of devices with broken support for generating interrupts
583  *
584  * Some PXE stacks are known to claim that IRQs are supported, but
585  * then never generate interrupts.  No satisfactory solution has been
586  * found to this problem; the workaround is to add the PCI vendor and
587  * device IDs to this list.  This is something of a hack, since it
588  * will generate false positives for identical devices with a working
589  * PXE stack (e.g. those that have been reflashed with iPXE), but it's
590  * an improvement on the current situation.
591  */
592 static const struct undinet_irq_broken undinet_irq_broken_list[] = {
593         /* HP XX70x laptops */
594         { .pci_vendor = 0x8086, .pci_device = 0x1502 },
595         { .pci_vendor = 0x8086, .pci_device = 0x1503 },
596 };
597
598 /**
599  * Check for devices with broken support for generating interrupts
600  *
601  * @v undi              UNDI device
602  * @ret irq_is_broken   Interrupt support is broken; no interrupts are generated
603  */
604 static int undinet_irq_is_broken ( struct undi_device *undi ) {
605         const struct undinet_irq_broken *broken;
606         unsigned int i;
607
608         for ( i = 0 ; i < ( sizeof ( undinet_irq_broken_list ) /
609                             sizeof ( undinet_irq_broken_list[0] ) ) ; i++ ) {
610                 broken = &undinet_irq_broken_list[i];
611                 if ( ( undi->dev.desc.bus_type == BUS_TYPE_PCI ) &&
612                      ( undi->dev.desc.vendor == broken->pci_vendor ) &&
613                      ( undi->dev.desc.device == broken->pci_device ) ) {
614                         return 1;
615                 }
616         }
617         return 0;
618 }
619
620 /**
621  * Probe UNDI device
622  *
623  * @v undi              UNDI device
624  * @ret rc              Return status code
625  */
626 int undinet_probe ( struct undi_device *undi ) {
627         struct net_device *netdev;
628         struct undi_nic *undinic;
629         struct s_PXENV_START_UNDI start_undi;
630         struct s_PXENV_UNDI_STARTUP undi_startup;
631         struct s_PXENV_UNDI_INITIALIZE undi_init;
632         struct s_PXENV_UNDI_GET_INFORMATION undi_info;
633         struct s_PXENV_UNDI_GET_IFACE_INFO undi_iface;
634         struct s_PXENV_UNDI_SHUTDOWN undi_shutdown;
635         struct s_PXENV_UNDI_CLEANUP undi_cleanup;
636         struct s_PXENV_STOP_UNDI stop_undi;
637         unsigned int retry;
638         int rc;
639
640         /* Allocate net device */
641         netdev = alloc_etherdev ( sizeof ( *undinic ) );
642         if ( ! netdev )
643                 return -ENOMEM;
644         netdev_init ( netdev, &undinet_operations );
645         undinic = netdev->priv;
646         undi_set_drvdata ( undi, netdev );
647         netdev->dev = &undi->dev;
648         memset ( undinic, 0, sizeof ( *undinic ) );
649         undinet_entry = undi->entry;
650         DBGC ( undinic, "UNDINIC %p using UNDI %p\n", undinic, undi );
651
652         /* Hook in UNDI stack */
653         if ( ! ( undi->flags & UNDI_FL_STARTED ) ) {
654                 memset ( &start_undi, 0, sizeof ( start_undi ) );
655                 start_undi.AX = undi->pci_busdevfn;
656                 start_undi.BX = undi->isapnp_csn;
657                 start_undi.DX = undi->isapnp_read_port;
658                 start_undi.ES = BIOS_SEG;
659                 start_undi.DI = find_pnp_bios();
660                 if ( ( rc = pxeparent_call ( undinet_entry, PXENV_START_UNDI,
661                                              &start_undi,
662                                              sizeof ( start_undi ) ) ) != 0 )
663                         goto err_start_undi;
664         }
665         undi->flags |= UNDI_FL_STARTED;
666
667         /* Bring up UNDI stack */
668         if ( ! ( undi->flags & UNDI_FL_INITIALIZED ) ) {
669                 memset ( &undi_startup, 0, sizeof ( undi_startup ) );
670                 if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_STARTUP,
671                                              &undi_startup,
672                                              sizeof ( undi_startup ) ) ) != 0 )
673                         goto err_undi_startup;
674                 /* On some PXE stacks, PXENV_UNDI_INITIALIZE may fail
675                  * due to a transient condition (e.g. media test
676                  * failing because the link has only just come out of
677                  * reset).  We may therefore need to retry this call
678                  * several times.
679                  */
680                 for ( retry = 0 ; ; ) {
681                         memset ( &undi_init, 0, sizeof ( undi_init ) );
682                         if ( ( rc = pxeparent_call ( undinet_entry,
683                                                      PXENV_UNDI_INITIALIZE,
684                                                      &undi_init,
685                                                      sizeof ( undi_init ))) ==0)
686                                 break;
687                         if ( ++retry > UNDI_INITIALIZE_RETRY_MAX )
688                                 goto err_undi_initialize;
689                         DBGC ( undinic, "UNDINIC %p retrying "
690                                "PXENV_UNDI_INITIALIZE (retry %d)\n",
691                                undinic, retry );
692                         /* Delay to allow link to settle if necessary */
693                         mdelay ( UNDI_INITIALIZE_RETRY_DELAY_MS );
694                 }
695         }
696         undi->flags |= UNDI_FL_INITIALIZED;
697
698         /* Get device information */
699         memset ( &undi_info, 0, sizeof ( undi_info ) );
700         if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_GET_INFORMATION,
701                                      &undi_info, sizeof ( undi_info ) ) ) != 0 )
702                 goto err_undi_get_information;
703         memcpy ( netdev->hw_addr, undi_info.PermNodeAddress, ETH_ALEN );
704         memcpy ( netdev->ll_addr, undi_info.CurrentNodeAddress, ETH_ALEN );
705         undinic->irq = undi_info.IntNumber;
706         if ( undinic->irq > IRQ_MAX ) {
707                 DBGC ( undinic, "UNDINIC %p has invalid IRQ %d\n",
708                        undinic, undinic->irq );
709                 rc = -EINVAL;
710                 goto err_bad_irq;
711         }
712         DBGC ( undinic, "UNDINIC %p has MAC address %s and IRQ %d\n",
713                undinic, eth_ntoa ( netdev->hw_addr ), undinic->irq );
714
715         /* Get interface information */
716         memset ( &undi_iface, 0, sizeof ( undi_iface ) );
717         if ( ( rc = pxeparent_call ( undinet_entry, PXENV_UNDI_GET_IFACE_INFO,
718                                      &undi_iface,
719                                      sizeof ( undi_iface ) ) ) != 0 )
720                 goto err_undi_get_iface_info;
721         DBGC ( undinic, "UNDINIC %p has type %s, speed %d, flags %08x\n",
722                undinic, undi_iface.IfaceType, undi_iface.LinkSpeed,
723                undi_iface.ServiceFlags );
724         if ( ( undi_iface.ServiceFlags & SUPPORTED_IRQ ) &&
725              ( undinic->irq != 0 ) ) {
726                 undinic->irq_supported = 1;
727         }
728         DBGC ( undinic, "UNDINIC %p using %s mode\n", undinic,
729                ( undinic->irq_supported ? "interrupt" : "polling" ) );
730         if ( strncmp ( ( ( char * ) undi_iface.IfaceType ), "Etherboot",
731                        sizeof ( undi_iface.IfaceType ) ) == 0 ) {
732                 DBGC ( undinic, "UNDINIC %p Etherboot 5.4 workaround enabled\n",
733                        undinic );
734                 undinic->hacks |= UNDI_HACK_EB54;
735         }
736         if ( undinet_irq_is_broken ( undi ) ) {
737                 DBGC ( undinic, "UNDINIC %p forcing polling mode due to "
738                        "broken interrupts\n", undinic );
739                 undinic->irq_supported = 0;
740         }
741
742         /* Register network device */
743         if ( ( rc = register_netdev ( netdev ) ) != 0 )
744                 goto err_register;
745
746         /* Mark as link up; we don't handle link state */
747         netdev_link_up ( netdev );
748
749         DBGC ( undinic, "UNDINIC %p added\n", undinic );
750         return 0;
751
752  err_register:
753  err_undi_get_iface_info:
754  err_bad_irq:
755  err_undi_get_information:
756  err_undi_initialize:
757         /* Shut down UNDI stack */
758         memset ( &undi_shutdown, 0, sizeof ( undi_shutdown ) );
759         pxeparent_call ( undinet_entry, PXENV_UNDI_SHUTDOWN, &undi_shutdown,
760                          sizeof ( undi_shutdown ) );
761         memset ( &undi_cleanup, 0, sizeof ( undi_cleanup ) );
762         pxeparent_call ( undinet_entry, PXENV_UNDI_CLEANUP, &undi_cleanup,
763                          sizeof ( undi_cleanup ) );
764         undi->flags &= ~UNDI_FL_INITIALIZED;
765  err_undi_startup:
766         /* Unhook UNDI stack */
767         memset ( &stop_undi, 0, sizeof ( stop_undi ) );
768         pxeparent_call ( undinet_entry, PXENV_STOP_UNDI, &stop_undi,
769                          sizeof ( stop_undi ) );
770         undi->flags &= ~UNDI_FL_STARTED;
771  err_start_undi:
772         netdev_nullify ( netdev );
773         netdev_put ( netdev );
774         undi_set_drvdata ( undi, NULL );
775         return rc;
776 }
777
778 /**
779  * Remove UNDI device
780  *
781  * @v undi              UNDI device
782  */
783 void undinet_remove ( struct undi_device *undi ) {
784         struct net_device *netdev = undi_get_drvdata ( undi );
785         struct undi_nic *undinic = netdev->priv;
786         struct s_PXENV_UNDI_SHUTDOWN undi_shutdown;
787         struct s_PXENV_UNDI_CLEANUP undi_cleanup;
788         struct s_PXENV_STOP_UNDI stop_undi;
789
790         /* Unregister net device */
791         unregister_netdev ( netdev );
792
793         /* If we are preparing for an OS boot, or if we cannot exit
794          * via the PXE stack, then shut down the PXE stack.
795          */
796         if ( ! ( undi->flags & UNDI_FL_KEEP_ALL ) ) {
797
798                 /* Shut down UNDI stack */
799                 memset ( &undi_shutdown, 0, sizeof ( undi_shutdown ) );
800                 pxeparent_call ( undinet_entry, PXENV_UNDI_SHUTDOWN,
801                                  &undi_shutdown, sizeof ( undi_shutdown ) );
802                 memset ( &undi_cleanup, 0, sizeof ( undi_cleanup ) );
803                 pxeparent_call ( undinet_entry, PXENV_UNDI_CLEANUP,
804                                  &undi_cleanup, sizeof ( undi_cleanup ) );
805                 undi->flags &= ~UNDI_FL_INITIALIZED;
806
807                 /* Unhook UNDI stack */
808                 memset ( &stop_undi, 0, sizeof ( stop_undi ) );
809                 pxeparent_call ( undinet_entry, PXENV_STOP_UNDI, &stop_undi,
810                                  sizeof ( stop_undi ) );
811                 undi->flags &= ~UNDI_FL_STARTED;
812         }
813
814         /* Clear entry point */
815         memset ( &undinet_entry, 0, sizeof ( undinet_entry ) );
816
817         /* Free network device */
818         netdev_nullify ( netdev );
819         netdev_put ( netdev );
820
821         DBGC ( undinic, "UNDINIC %p removed\n", undinic );
822 }