iPXE
undinet.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017  * 02110-1301, USA.
00018  */
00019 
00020 FILE_LICENCE ( GPL2_OR_LATER );
00021 
00022 #include <string.h>
00023 #include <unistd.h>
00024 #include <byteswap.h>
00025 #include <pxe.h>
00026 #include <realmode.h>
00027 #include <pic8259.h>
00028 #include <biosint.h>
00029 #include <pnpbios.h>
00030 #include <basemem_packet.h>
00031 #include <ipxe/io.h>
00032 #include <ipxe/iobuf.h>
00033 #include <ipxe/netdevice.h>
00034 #include <ipxe/if_ether.h>
00035 #include <ipxe/ethernet.h>
00036 #include <ipxe/pci.h>
00037 #include <ipxe/profile.h>
00038 #include <undi.h>
00039 #include <undinet.h>
00040 
00041 /** @file
00042  *
00043  * UNDI network device driver
00044  *
00045  */
00046 
00047 /** An UNDI NIC */
00048 struct undi_nic {
00049         /** Device supports IRQs */
00050         int irq_supported;
00051         /** Assigned IRQ number */
00052         unsigned int irq;
00053         /** Currently processing ISR */
00054         int isr_processing;
00055         /** Bug workarounds */
00056         int hacks;
00057 };
00058 
00059 /* Disambiguate the various error causes */
00060 #define EINFO_EPXECALL                                                  \
00061         __einfo_uniqify ( EINFO_EPLATFORM, 0x01,                        \
00062                           "External PXE API error" )
00063 #define EPXECALL( status ) EPLATFORM ( EINFO_EPXECALL, status )
00064 
00065 /**
00066  * @defgroup undi_hacks UNDI workarounds
00067  * @{
00068  */
00069 
00070 /** Work around Etherboot 5.4 bugs */
00071 #define UNDI_HACK_EB54          0x0001
00072 
00073 /** @} */
00074 
00075 /** Maximum number of times to retry PXENV_UNDI_INITIALIZE */
00076 #define UNDI_INITIALIZE_RETRY_MAX 10
00077 
00078 /** Delay between retries of PXENV_UNDI_INITIALIZE */
00079 #define UNDI_INITIALIZE_RETRY_DELAY_MS 200
00080 
00081 /** Maximum number of received packets per poll */
00082 #define UNDI_RX_QUOTA 4
00083 
00084 /** Alignment of received frame payload */
00085 #define UNDI_RX_ALIGN 16
00086 
00087 static void undinet_close ( struct net_device *netdev );
00088 
00089 /**
00090  * UNDI parameter block
00091  *
00092  * Used as the parameter block for all UNDI API calls.  Resides in
00093  * base memory.
00094  */
00095 static union u_PXENV_ANY __bss16 ( undinet_params );
00096 #define undinet_params __use_data16 ( undinet_params )
00097 
00098 /**
00099  * UNDI entry point
00100  *
00101  * Used as the indirection vector for all UNDI API calls.  Resides in
00102  * base memory.
00103  */
00104 SEGOFF16_t __bss16 ( undinet_entry_point );
00105 #define undinet_entry_point __use_data16 ( undinet_entry_point )
00106 
00107 /** IRQ profiler */
00108 static struct profiler undinet_irq_profiler __profiler =
00109         { .name = "undinet.irq" };
00110 
00111 /** Receive profiler */
00112 static struct profiler undinet_rx_profiler __profiler =
00113         { .name = "undinet.rx" };
00114 
00115 /** A PXE API call breakdown profiler */
00116 struct undinet_profiler {
00117         /** Total time spent performing REAL_CALL() */
00118         struct profiler total;
00119         /** Time spent transitioning to real mode */
00120         struct profiler p2r;
00121         /** Time spent in external code */
00122         struct profiler ext;
00123         /** Time spent transitioning back to protected mode */
00124         struct profiler r2p;
00125 };
00126 
00127 /** PXENV_UNDI_TRANSMIT profiler */
00128 static struct undinet_profiler undinet_tx_profiler __profiler = {
00129         { .name = "undinet.tx" },
00130         { .name = "undinet.tx_p2r" },
00131         { .name = "undinet.tx_ext" },
00132         { .name = "undinet.tx_r2p" },
00133 };
00134 
00135 /** PXENV_UNDI_ISR profiler
00136  *
00137  * Note that this profiler will not see calls to
00138  * PXENV_UNDI_ISR_IN_START, which are handled by the UNDI ISR and do
00139  * not go via undinet_call().
00140  */
00141 static struct undinet_profiler undinet_isr_profiler __profiler = {
00142         { .name = "undinet.isr" },
00143         { .name = "undinet.isr_p2r" },
00144         { .name = "undinet.isr_ext" },
00145         { .name = "undinet.isr_r2p" },
00146 };
00147 
00148 /** PXE unknown API call profiler
00149  *
00150  * This profiler can be used to measure the overhead of a dummy PXE
00151  * API call.
00152  */
00153 static struct undinet_profiler undinet_unknown_profiler __profiler = {
00154         { .name = "undinet.unknown" },
00155         { .name = "undinet.unknown_p2r" },
00156         { .name = "undinet.unknown_ext" },
00157         { .name = "undinet.unknown_r2p" },
00158 };
00159 
00160 /** Miscellaneous PXE API call profiler */
00161 static struct undinet_profiler undinet_misc_profiler __profiler = {
00162         { .name = "undinet.misc" },
00163         { .name = "undinet.misc_p2r" },
00164         { .name = "undinet.misc_ext" },
00165         { .name = "undinet.misc_r2p" },
00166 };
00167 
00168 /*****************************************************************************
00169  *
00170  * UNDI API call
00171  *
00172  *****************************************************************************
00173  */
00174 
00175 /**
00176  * Name PXE API call
00177  *
00178  * @v function          API call number
00179  * @ret name            API call name
00180  */
00181 static inline __attribute__ (( always_inline )) const char *
00182 undinet_function_name ( unsigned int function ) {
00183         switch ( function ) {
00184         case PXENV_START_UNDI:
00185                 return "PXENV_START_UNDI";
00186         case PXENV_STOP_UNDI:
00187                 return "PXENV_STOP_UNDI";
00188         case PXENV_UNDI_STARTUP:
00189                 return "PXENV_UNDI_STARTUP";
00190         case PXENV_UNDI_CLEANUP:
00191                 return "PXENV_UNDI_CLEANUP";
00192         case PXENV_UNDI_INITIALIZE:
00193                 return "PXENV_UNDI_INITIALIZE";
00194         case PXENV_UNDI_RESET_ADAPTER:
00195                 return "PXENV_UNDI_RESET_ADAPTER";
00196         case PXENV_UNDI_SHUTDOWN:
00197                 return "PXENV_UNDI_SHUTDOWN";
00198         case PXENV_UNDI_OPEN:
00199                 return "PXENV_UNDI_OPEN";
00200         case PXENV_UNDI_CLOSE:
00201                 return "PXENV_UNDI_CLOSE";
00202         case PXENV_UNDI_TRANSMIT:
00203                 return "PXENV_UNDI_TRANSMIT";
00204         case PXENV_UNDI_SET_MCAST_ADDRESS:
00205                 return "PXENV_UNDI_SET_MCAST_ADDRESS";
00206         case PXENV_UNDI_SET_STATION_ADDRESS:
00207                 return "PXENV_UNDI_SET_STATION_ADDRESS";
00208         case PXENV_UNDI_SET_PACKET_FILTER:
00209                 return "PXENV_UNDI_SET_PACKET_FILTER";
00210         case PXENV_UNDI_GET_INFORMATION:
00211                 return "PXENV_UNDI_GET_INFORMATION";
00212         case PXENV_UNDI_GET_STATISTICS:
00213                 return "PXENV_UNDI_GET_STATISTICS";
00214         case PXENV_UNDI_CLEAR_STATISTICS:
00215                 return "PXENV_UNDI_CLEAR_STATISTICS";
00216         case PXENV_UNDI_INITIATE_DIAGS:
00217                 return "PXENV_UNDI_INITIATE_DIAGS";
00218         case PXENV_UNDI_FORCE_INTERRUPT:
00219                 return "PXENV_UNDI_FORCE_INTERRUPT";
00220         case PXENV_UNDI_GET_MCAST_ADDRESS:
00221                 return "PXENV_UNDI_GET_MCAST_ADDRESS";
00222         case PXENV_UNDI_GET_NIC_TYPE:
00223                 return "PXENV_UNDI_GET_NIC_TYPE";
00224         case PXENV_UNDI_GET_IFACE_INFO:
00225                 return "PXENV_UNDI_GET_IFACE_INFO";
00226         /*
00227          * Duplicate case value; this is a bug in the PXE specification.
00228          *
00229          *      case PXENV_UNDI_GET_STATE:
00230          *              return "PXENV_UNDI_GET_STATE";
00231          */
00232         case PXENV_UNDI_ISR:
00233                 return "PXENV_UNDI_ISR";
00234         case PXENV_GET_CACHED_INFO:
00235                 return "PXENV_GET_CACHED_INFO";
00236         default:
00237                 return "UNKNOWN API CALL";
00238         }
00239 }
00240 
00241 /**
00242  * Determine applicable profiler pair (for debugging)
00243  *
00244  * @v function          API call number
00245  * @ret profiler        Profiler
00246  */
00247 static struct undinet_profiler * undinet_profiler ( unsigned int function ) {
00248 
00249         /* Determine applicable profiler */
00250         switch ( function ) {
00251         case PXENV_UNDI_TRANSMIT:
00252                 return &undinet_tx_profiler;
00253         case PXENV_UNDI_ISR:
00254                 return &undinet_isr_profiler;
00255         case PXENV_UNKNOWN:
00256                 return &undinet_unknown_profiler;
00257         default:
00258                 return &undinet_misc_profiler;
00259         }
00260 }
00261 
00262 /**
00263  * Issue UNDI API call
00264  *
00265  * @v undinic           UNDI NIC
00266  * @v function          API call number
00267  * @v params            PXE parameter block
00268  * @v params_len        Length of PXE parameter block
00269  * @ret rc              Return status code
00270  */
00271 static int undinet_call ( struct undi_nic *undinic, unsigned int function,
00272                           void *params, size_t params_len ) {
00273         struct undinet_profiler *profiler = undinet_profiler ( function );
00274         PXENV_EXIT_t exit;
00275         uint32_t before;
00276         uint32_t started;
00277         uint32_t stopped;
00278         uint32_t after;
00279         int discard_D;
00280         int rc;
00281 
00282         /* Copy parameter block and entry point */
00283         assert ( params_len <= sizeof ( undinet_params ) );
00284         memcpy ( &undinet_params, params, params_len );
00285 
00286         /* Call real-mode entry point.  This calling convention will
00287          * work with both the !PXE and the PXENV+ entry points.
00288          */
00289         profile_start ( &profiler->total );
00290         __asm__ __volatile__ ( REAL_CODE ( "pushl %%ebp\n\t" /* gcc bug */
00291                                            "rdtsc\n\t"
00292                                            "pushl %%eax\n\t"
00293                                            "pushw %%es\n\t"
00294                                            "pushw %%di\n\t"
00295                                            "pushw %%bx\n\t"
00296                                            "lcall *undinet_entry_point\n\t"
00297                                            "movw %%ax, %%bx\n\t"
00298                                            "rdtsc\n\t"
00299                                            "addw $6, %%sp\n\t"
00300                                            "popl %%edx\n\t"
00301                                            "popl %%ebp\n\t" /* gcc bug */ )
00302                                : "=a" ( stopped ), "=d" ( started ),
00303                                  "=b" ( exit ), "=D" ( discard_D )
00304                                : "b" ( function ),
00305                                  "D" ( __from_data16 ( &undinet_params ) )
00306                                : "ecx", "esi" );
00307         profile_stop ( &profiler->total );
00308         before = profile_started ( &profiler->total );
00309         after = profile_stopped ( &profiler->total );
00310         profile_start_at ( &profiler->p2r, before );
00311         profile_stop_at ( &profiler->p2r, started );
00312         profile_start_at ( &profiler->ext, started );
00313         profile_stop_at ( &profiler->ext, stopped );
00314         profile_start_at ( &profiler->r2p, stopped );
00315         profile_stop_at ( &profiler->r2p, after );
00316 
00317         /* Determine return status code based on PXENV_EXIT and
00318          * PXENV_STATUS
00319          */
00320         rc = ( ( exit == PXENV_EXIT_SUCCESS ) ?
00321                0 : -EPXECALL ( undinet_params.Status ) );
00322 
00323         /* If anything goes wrong, print as much debug information as
00324          * it's possible to give.
00325          */
00326         if ( rc != 0 ) {
00327                 SEGOFF16_t rm_params = {
00328                         .segment = rm_ds,
00329                         .offset = __from_data16 ( &undinet_params ),
00330                 };
00331 
00332                 DBGC ( undinic, "UNDINIC %p %s failed: %s\n", undinic,
00333                        undinet_function_name ( function ), strerror ( rc ) );
00334                 DBGC ( undinic, "UNDINIC %p parameters at %04x:%04x length "
00335                        "%#02zx, entry point at %04x:%04x\n", undinic,
00336                        rm_params.segment, rm_params.offset, params_len,
00337                        undinet_entry_point.segment,
00338                        undinet_entry_point.offset );
00339                 DBGC ( undinic, "UNDINIC %p parameters provided:\n", undinic );
00340                 DBGC_HDA ( undinic, rm_params, params, params_len );
00341                 DBGC ( undinic, "UNDINIC %p parameters returned:\n", undinic );
00342                 DBGC_HDA ( undinic, rm_params, &undinet_params, params_len );
00343         }
00344 
00345         /* Copy parameter block back */
00346         memcpy ( params, &undinet_params, params_len );
00347 
00348         return rc;
00349 }
00350 
00351 /*****************************************************************************
00352  *
00353  * UNDI interrupt service routine
00354  *
00355  *****************************************************************************
00356  */
00357 
00358 /**
00359  * UNDI interrupt service routine
00360  *
00361  * The UNDI ISR increments a counter (@c trigger_count) and exits.
00362  */
00363 extern void undiisr ( void );
00364 
00365 /** IRQ number */
00366 uint8_t __data16 ( undiisr_irq );
00367 #define undiisr_irq __use_data16 ( undiisr_irq )
00368 
00369 /** IRQ chain vector */
00370 struct segoff __data16 ( undiisr_next_handler );
00371 #define undiisr_next_handler __use_data16 ( undiisr_next_handler )
00372 
00373 /** IRQ trigger count */
00374 volatile uint8_t __data16 ( undiisr_trigger_count ) = 0;
00375 #define undiisr_trigger_count __use_data16 ( undiisr_trigger_count )
00376 
00377 /** Last observed trigger count */
00378 static unsigned int last_trigger_count = 0;
00379 
00380 /**
00381  * Hook UNDI interrupt service routine
00382  *
00383  * @v irq               IRQ number
00384  */
00385 static void undinet_hook_isr ( unsigned int irq ) {
00386 
00387         assert ( irq <= IRQ_MAX );
00388         assert ( undiisr_irq == 0 );
00389 
00390         undiisr_irq = irq;
00391         hook_bios_interrupt ( IRQ_INT ( irq ), ( ( intptr_t ) undiisr ),
00392                               &undiisr_next_handler );
00393 }
00394 
00395 /**
00396  * Unhook UNDI interrupt service routine
00397  *
00398  * @v irq               IRQ number
00399  */
00400 static void undinet_unhook_isr ( unsigned int irq ) {
00401 
00402         assert ( irq <= IRQ_MAX );
00403 
00404         unhook_bios_interrupt ( IRQ_INT ( irq ), ( ( intptr_t ) undiisr ),
00405                                 &undiisr_next_handler );
00406         undiisr_irq = 0;
00407 }
00408 
00409 /**
00410  * Test to see if UNDI ISR has been triggered
00411  *
00412  * @ret triggered       ISR has been triggered since last check
00413  */
00414 static int undinet_isr_triggered ( void ) {
00415         unsigned int this_trigger_count;
00416 
00417         /* Read trigger_count.  Do this only once; it is volatile */
00418         this_trigger_count = undiisr_trigger_count;
00419 
00420         if ( this_trigger_count == last_trigger_count ) {
00421                 /* Not triggered */
00422                 return 0;
00423         } else {
00424                 /* Triggered */
00425                 last_trigger_count = this_trigger_count;
00426                 return 1;
00427         }
00428 }
00429 
00430 /*****************************************************************************
00431  *
00432  * UNDI network device interface
00433  *
00434  *****************************************************************************
00435  */
00436 
00437 /** UNDI transmit buffer descriptor */
00438 static struct s_PXENV_UNDI_TBD __data16 ( undinet_tbd );
00439 #define undinet_tbd __use_data16 ( undinet_tbd )
00440 
00441 /** UNDI transmit destination address */
00442 static uint8_t __data16_array ( undinet_destaddr, [ETH_ALEN] );
00443 #define undinet_destaddr __use_data16 ( undinet_destaddr )
00444 
00445 /**
00446  * Transmit packet
00447  *
00448  * @v netdev            Network device
00449  * @v iobuf             I/O buffer
00450  * @ret rc              Return status code
00451  */
00452 static int undinet_transmit ( struct net_device *netdev,
00453                               struct io_buffer *iobuf ) {
00454         struct undi_nic *undinic = netdev->priv;
00455         struct s_PXENV_UNDI_TRANSMIT undi_transmit;
00456         const void *ll_dest;
00457         const void *ll_source;
00458         uint16_t net_proto;
00459         unsigned int flags;
00460         uint8_t protocol;
00461         size_t len;
00462         int rc;
00463 
00464         /* Technically, we ought to make sure that the previous
00465          * transmission has completed before we re-use the buffer.
00466          * However, many PXE stacks (including at least some Intel PXE
00467          * stacks and Etherboot 5.4) fail to generate TX completions.
00468          * In practice this won't be a problem, since our TX datapath
00469          * has a very low packet volume and we can get away with
00470          * assuming that a TX will be complete by the time we want to
00471          * transmit the next packet.
00472          */
00473 
00474         /* Some PXE stacks are unable to cope with P_UNKNOWN, and will
00475          * always try to prepend a link-layer header.  Work around
00476          * these stacks by stripping the existing link-layer header
00477          * and allowing the PXE stack to (re)construct the link-layer
00478          * header itself.
00479          */
00480         if ( ( rc = eth_pull ( netdev, iobuf, &ll_dest, &ll_source,
00481                                &net_proto, &flags ) ) != 0 ) {
00482                 DBGC ( undinic, "UNDINIC %p could not strip Ethernet header: "
00483                        "%s\n", undinic, strerror ( rc ) );
00484                 return rc;
00485         }
00486         memcpy ( undinet_destaddr, ll_dest, sizeof ( undinet_destaddr ) );
00487         switch ( net_proto ) {
00488         case htons ( ETH_P_IP ) :
00489                 protocol = P_IP;
00490                 break;
00491         case htons ( ETH_P_ARP ) :
00492                 protocol = P_ARP;
00493                 break;
00494         case htons ( ETH_P_RARP ) :
00495                 protocol = P_RARP;
00496                 break;
00497         default:
00498                 /* Unknown protocol; restore the original link-layer header */
00499                 iob_push ( iobuf, sizeof ( struct ethhdr ) );
00500                 protocol = P_UNKNOWN;
00501                 break;
00502         }
00503 
00504         /* Copy packet to UNDI I/O buffer */
00505         len = iob_len ( iobuf );
00506         if ( len > sizeof ( basemem_packet ) )
00507                 len = sizeof ( basemem_packet );
00508         memcpy ( &basemem_packet, iobuf->data, len );
00509 
00510         /* Create PXENV_UNDI_TRANSMIT data structure */
00511         memset ( &undi_transmit, 0, sizeof ( undi_transmit ) );
00512         undi_transmit.Protocol = protocol;
00513         undi_transmit.XmitFlag = ( ( flags & LL_BROADCAST ) ?
00514                                    XMT_BROADCAST : XMT_DESTADDR );
00515         undi_transmit.DestAddr.segment = rm_ds;
00516         undi_transmit.DestAddr.offset = __from_data16 ( &undinet_destaddr );
00517         undi_transmit.TBD.segment = rm_ds;
00518         undi_transmit.TBD.offset = __from_data16 ( &undinet_tbd );
00519 
00520         /* Create PXENV_UNDI_TBD data structure */
00521         undinet_tbd.ImmedLength = len;
00522         undinet_tbd.Xmit.segment = rm_ds;
00523         undinet_tbd.Xmit.offset = __from_data16 ( basemem_packet );
00524 
00525         /* Issue PXE API call */
00526         if ( ( rc = undinet_call ( undinic, PXENV_UNDI_TRANSMIT, &undi_transmit,
00527                                    sizeof ( undi_transmit ) ) ) != 0 )
00528                 goto done;
00529 
00530         /* Free I/O buffer */
00531         netdev_tx_complete ( netdev, iobuf );
00532  done:
00533         return rc;
00534 }
00535 
00536 /** 
00537  * Poll for received packets
00538  *
00539  * @v netdev            Network device
00540  *
00541  * Fun, fun, fun.  UNDI drivers don't use polling; they use
00542  * interrupts.  We therefore cheat and pretend that an interrupt has
00543  * occurred every time undinet_poll() is called.  This isn't too much
00544  * of a hack; PCI devices share IRQs and so the first thing that a
00545  * proper ISR should do is call PXENV_UNDI_ISR to determine whether or
00546  * not the UNDI NIC generated the interrupt; there is no harm done by
00547  * spurious calls to PXENV_UNDI_ISR.  Similarly, we wouldn't be
00548  * handling them any more rapidly than the usual rate of
00549  * undinet_poll() being called even if we did implement a full ISR.
00550  * So it should work.  Ha!
00551  *
00552  * Addendum (21/10/03).  Some cards don't play nicely with this trick,
00553  * so instead of doing it the easy way we have to go to all the hassle
00554  * of installing a genuine interrupt service routine and dealing with
00555  * the wonderful 8259 Programmable Interrupt Controller.  Joy.
00556  *
00557  * Addendum (10/07/07).  When doing things such as iSCSI boot, in
00558  * which we have to co-operate with a running OS, we can't get away
00559  * with the "ISR-just-increments-a-counter-and-returns" trick at all,
00560  * because it involves tying up the PIC for far too long, and other
00561  * interrupt-dependent components (e.g. local disks) start breaking.
00562  * We therefore implement a "proper" ISR which calls PXENV_UNDI_ISR
00563  * from within interrupt context in order to deassert the device
00564  * interrupt, and sends EOI if applicable.
00565  */
00566 static void undinet_poll ( struct net_device *netdev ) {
00567         struct undi_nic *undinic = netdev->priv;
00568         struct s_PXENV_UNDI_ISR undi_isr;
00569         struct io_buffer *iobuf = NULL;
00570         unsigned int quota = UNDI_RX_QUOTA;
00571         size_t len;
00572         size_t reserve_len;
00573         size_t frag_len;
00574         size_t max_frag_len;
00575         int rc;
00576 
00577         if ( ! undinic->isr_processing ) {
00578                 /* Allow interrupt to occur.  Do this even if
00579                  * interrupts are not known to be supported, since
00580                  * some cards erroneously report that they do not
00581                  * support interrupts.
00582                  */
00583                 if ( ! undinet_isr_triggered() ) {
00584                         /* Allow interrupt to occur */
00585                         profile_start ( &undinet_irq_profiler );
00586                         __asm__ __volatile__ ( "sti\n\t"
00587                                                "nop\n\t"
00588                                                "nop\n\t"
00589                                                "cli\n\t" );
00590                         profile_stop ( &undinet_irq_profiler );
00591 
00592                         /* If interrupts are known to be supported,
00593                          * then do nothing on this poll; wait for the
00594                          * interrupt to be triggered.
00595                          */
00596                         if ( undinic->irq_supported )
00597                                 return;
00598                 }
00599 
00600                 /* Start ISR processing */
00601                 undinic->isr_processing = 1;
00602                 undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_PROCESS;
00603         } else {
00604                 /* Continue ISR processing */
00605                 undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
00606         }
00607 
00608         /* Run through the ISR loop */
00609         while ( quota ) {
00610                 if ( ( rc = undinet_call ( undinic, PXENV_UNDI_ISR, &undi_isr,
00611                                            sizeof ( undi_isr ) ) ) != 0 ) {
00612                         netdev_rx_err ( netdev, NULL, rc );
00613                         break;
00614                 }
00615                 switch ( undi_isr.FuncFlag ) {
00616                 case PXENV_UNDI_ISR_OUT_TRANSMIT:
00617                         /* We don't care about transmit completions */
00618                         break;
00619                 case PXENV_UNDI_ISR_OUT_RECEIVE:
00620                         /* Packet fragment received */
00621                         profile_start ( &undinet_rx_profiler );
00622                         len = undi_isr.FrameLength;
00623                         frag_len = undi_isr.BufferLength;
00624                         reserve_len = ( -undi_isr.FrameHeaderLength &
00625                                         ( UNDI_RX_ALIGN - 1 ) );
00626                         if ( ( len == 0 ) || ( len < frag_len ) ) {
00627                                 /* Don't laugh.  VMWare does it. */
00628                                 DBGC ( undinic, "UNDINIC %p reported insane "
00629                                        "fragment (%zd of %zd bytes)\n",
00630                                        undinic, frag_len, len );
00631                                 netdev_rx_err ( netdev, NULL, -EINVAL );
00632                                 break;
00633                         }
00634                         if ( ! iobuf ) {
00635                                 iobuf = alloc_iob ( reserve_len + len );
00636                                 if ( ! iobuf ) {
00637                                         DBGC ( undinic, "UNDINIC %p could not "
00638                                                "allocate %zd bytes for RX "
00639                                                "buffer\n", undinic, len );
00640                                         /* Fragment will be dropped */
00641                                         netdev_rx_err ( netdev, NULL, -ENOMEM );
00642                                         goto done;
00643                                 }
00644                                 iob_reserve ( iobuf, reserve_len );
00645                         }
00646                         max_frag_len = iob_tailroom ( iobuf );
00647                         if ( frag_len > max_frag_len ) {
00648                                 DBGC ( undinic, "UNDINIC %p fragment too big "
00649                                        "(%zd+%zd does not fit into %zd)\n",
00650                                        undinic, iob_len ( iobuf ), frag_len,
00651                                        ( iob_len ( iobuf ) + max_frag_len ) );
00652                                 frag_len = max_frag_len;
00653                         }
00654                         copy_from_real ( iob_put ( iobuf, frag_len ),
00655                                          undi_isr.Frame.segment,
00656                                          undi_isr.Frame.offset, frag_len );
00657                         if ( iob_len ( iobuf ) == len ) {
00658                                 /* Whole packet received; deliver it */
00659                                 netdev_rx ( netdev, iob_disown ( iobuf ) );
00660                                 quota--;
00661                                 /* Etherboot 5.4 fails to return all packets
00662                                  * under mild load; pretend it retriggered.
00663                                  */
00664                                 if ( undinic->hacks & UNDI_HACK_EB54 )
00665                                         --last_trigger_count;
00666                         }
00667                         profile_stop ( &undinet_rx_profiler );
00668                         break;
00669                 case PXENV_UNDI_ISR_OUT_DONE:
00670                         /* Processing complete */
00671                         undinic->isr_processing = 0;
00672                         goto done;
00673                 default:
00674                         /* Should never happen.  VMWare does it routinely. */
00675                         DBGC ( undinic, "UNDINIC %p ISR returned invalid "
00676                                "FuncFlag %04x\n", undinic, undi_isr.FuncFlag );
00677                         undinic->isr_processing = 0;
00678                         goto done;
00679                 }
00680                 undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
00681         }
00682 
00683  done:
00684         if ( iobuf ) {
00685                 DBGC ( undinic, "UNDINIC %p returned incomplete packet "
00686                        "(%zd of %zd)\n", undinic, iob_len ( iobuf ),
00687                        ( iob_len ( iobuf ) + iob_tailroom ( iobuf ) ) );
00688                 netdev_rx_err ( netdev, iobuf, -EINVAL );
00689         }
00690 }
00691 
00692 /**
00693  * Open NIC
00694  *
00695  * @v netdev            Net device
00696  * @ret rc              Return status code
00697  */
00698 static int undinet_open ( struct net_device *netdev ) {
00699         struct undi_nic *undinic = netdev->priv;
00700         struct s_PXENV_UNDI_SET_STATION_ADDRESS undi_set_address;
00701         struct s_PXENV_UNDI_OPEN undi_open;
00702         int rc;
00703 
00704         /* Hook interrupt service routine and enable interrupt if applicable */
00705         if ( undinic->irq ) {
00706                 undinet_hook_isr ( undinic->irq );
00707                 enable_irq ( undinic->irq );
00708                 send_eoi ( undinic->irq );
00709         }
00710 
00711         /* Set station address.  Required for some PXE stacks; will
00712          * spuriously fail on others.  Ignore failures.  We only ever
00713          * use it to set the MAC address to the card's permanent value
00714          * anyway.
00715          */
00716         memcpy ( undi_set_address.StationAddress, netdev->ll_addr,
00717                  sizeof ( undi_set_address.StationAddress ) );
00718         undinet_call ( undinic, PXENV_UNDI_SET_STATION_ADDRESS,
00719                        &undi_set_address, sizeof ( undi_set_address ) );
00720 
00721         /* Open NIC.  We ask for promiscuous operation, since it's the
00722          * only way to ask for all multicast addresses.  On any
00723          * switched network, it shouldn't really make a difference to
00724          * performance.
00725          */
00726         memset ( &undi_open, 0, sizeof ( undi_open ) );
00727         undi_open.PktFilter = ( FLTR_DIRECTED | FLTR_BRDCST | FLTR_PRMSCS );
00728         if ( ( rc = undinet_call ( undinic, PXENV_UNDI_OPEN, &undi_open,
00729                                    sizeof ( undi_open ) ) ) != 0 )
00730                 goto err;
00731 
00732         DBGC ( undinic, "UNDINIC %p opened\n", undinic );
00733         return 0;
00734 
00735  err:
00736         undinet_close ( netdev );
00737         return rc;
00738 }
00739 
00740 /**
00741  * Close NIC
00742  *
00743  * @v netdev            Net device
00744  */
00745 static void undinet_close ( struct net_device *netdev ) {
00746         struct undi_nic *undinic = netdev->priv;
00747         struct s_PXENV_UNDI_ISR undi_isr;
00748         struct s_PXENV_UNDI_CLOSE undi_close;
00749         int rc;
00750 
00751         /* Ensure ISR has exited cleanly */
00752         while ( undinic->isr_processing ) {
00753                 undi_isr.FuncFlag = PXENV_UNDI_ISR_IN_GET_NEXT;
00754                 if ( ( rc = undinet_call ( undinic, PXENV_UNDI_ISR, &undi_isr,
00755                                            sizeof ( undi_isr ) ) ) != 0 )
00756                         break;
00757                 switch ( undi_isr.FuncFlag ) {
00758                 case PXENV_UNDI_ISR_OUT_TRANSMIT:
00759                 case PXENV_UNDI_ISR_OUT_RECEIVE:
00760                         /* Continue draining */
00761                         break;
00762                 default:
00763                         /* Stop processing */
00764                         undinic->isr_processing = 0;
00765                         break;
00766                 }
00767         }
00768 
00769         /* Close NIC */
00770         undinet_call ( undinic, PXENV_UNDI_CLOSE, &undi_close,
00771                        sizeof ( undi_close ) );
00772 
00773         /* Disable interrupt and unhook ISR if applicable */
00774         if ( undinic->irq ) {
00775                 disable_irq ( undinic->irq );
00776                 undinet_unhook_isr ( undinic->irq );
00777         }
00778 
00779         DBGC ( undinic, "UNDINIC %p closed\n", undinic );
00780 }
00781 
00782 /**
00783  * Enable/disable interrupts
00784  *
00785  * @v netdev            Net device
00786  * @v enable            Interrupts should be enabled
00787  */
00788 static void undinet_irq ( struct net_device *netdev, int enable ) {
00789         struct undi_nic *undinic = netdev->priv;
00790 
00791         /* Cannot support interrupts yet */
00792         DBGC ( undinic, "UNDINIC %p cannot %s interrupts\n",
00793                undinic, ( enable ? "enable" : "disable" ) );
00794 }
00795 
00796 /** UNDI network device operations */
00797 static struct net_device_operations undinet_operations = {
00798         .open           = undinet_open,
00799         .close          = undinet_close,
00800         .transmit       = undinet_transmit,
00801         .poll           = undinet_poll,
00802         .irq            = undinet_irq,
00803 };
00804 
00805 /** A device with broken support for generating interrupts */
00806 struct undinet_irq_broken {
00807         /** PCI vendor ID */
00808         uint16_t pci_vendor;
00809         /** PCI device ID */
00810         uint16_t pci_device;
00811         /** PCI subsystem vendor ID */
00812         uint16_t pci_subsys_vendor;
00813         /** PCI subsystem ID */
00814         uint16_t pci_subsys;
00815 };
00816 
00817 /**
00818  * List of devices with broken support for generating interrupts
00819  *
00820  * Some PXE stacks are known to claim that IRQs are supported, but
00821  * then never generate interrupts.  No satisfactory solution has been
00822  * found to this problem; the workaround is to add the PCI vendor and
00823  * device IDs to this list.  This is something of a hack, since it
00824  * will generate false positives for identical devices with a working
00825  * PXE stack (e.g. those that have been reflashed with iPXE), but it's
00826  * an improvement on the current situation.
00827  */
00828 static const struct undinet_irq_broken undinet_irq_broken_list[] = {
00829         /* HP XX70x laptops */
00830         { 0x8086, 0x1502, PCI_ANY_ID, PCI_ANY_ID },
00831         { 0x8086, 0x1503, PCI_ANY_ID, PCI_ANY_ID },
00832         /* HP 745 G3 laptop */
00833         { 0x14e4, 0x1687, PCI_ANY_ID, PCI_ANY_ID },
00834 };
00835 
00836 /**
00837  * Check for devices with broken support for generating interrupts
00838  *
00839  * @v desc              Device description
00840  * @ret irq_is_broken   Interrupt support is broken; no interrupts are generated
00841  */
00842 static int undinet_irq_is_broken ( struct device_description *desc ) {
00843         const struct undinet_irq_broken *broken;
00844         struct pci_device pci;
00845         uint16_t subsys_vendor;
00846         uint16_t subsys;
00847         unsigned int i;
00848 
00849         /* Ignore non-PCI devices */
00850         if ( desc->bus_type != BUS_TYPE_PCI )
00851                 return 0;
00852 
00853         /* Read subsystem IDs */
00854         pci_init ( &pci, desc->location );
00855         pci_read_config_word ( &pci, PCI_SUBSYSTEM_VENDOR_ID, &subsys_vendor );
00856         pci_read_config_word ( &pci, PCI_SUBSYSTEM_ID, &subsys );
00857 
00858         /* Check for a match against the broken device list */
00859         for ( i = 0 ; i < ( sizeof ( undinet_irq_broken_list ) /
00860                             sizeof ( undinet_irq_broken_list[0] ) ) ; i++ ) {
00861                 broken = &undinet_irq_broken_list[i];
00862                 if ( ( broken->pci_vendor == desc->vendor ) &&
00863                      ( broken->pci_device == desc->device ) &&
00864                      ( ( broken->pci_subsys_vendor == subsys_vendor ) ||
00865                        ( broken->pci_subsys_vendor == PCI_ANY_ID ) ) &&
00866                      ( ( broken->pci_subsys == subsys ) ||
00867                        ( broken->pci_subsys == PCI_ANY_ID ) ) ) {
00868                         return 1;
00869                 }
00870         }
00871         return 0;
00872 }
00873 
00874 /**
00875  * Probe UNDI device
00876  *
00877  * @v undi              UNDI device
00878  * @v dev               Underlying generic device
00879  * @ret rc              Return status code
00880  */
00881 int undinet_probe ( struct undi_device *undi, struct device *dev ) {
00882         struct net_device *netdev;
00883         struct undi_nic *undinic;
00884         struct s_PXENV_START_UNDI start_undi;
00885         struct s_PXENV_UNDI_STARTUP undi_startup;
00886         struct s_PXENV_UNDI_INITIALIZE undi_init;
00887         struct s_PXENV_UNDI_GET_INFORMATION undi_info;
00888         struct s_PXENV_UNDI_GET_IFACE_INFO undi_iface;
00889         struct s_PXENV_UNDI_SHUTDOWN undi_shutdown;
00890         struct s_PXENV_UNDI_CLEANUP undi_cleanup;
00891         struct s_PXENV_STOP_UNDI stop_undi;
00892         unsigned int retry;
00893         int rc;
00894 
00895         /* Allocate net device */
00896         netdev = alloc_etherdev ( sizeof ( *undinic ) );
00897         if ( ! netdev )
00898                 return -ENOMEM;
00899         netdev_init ( netdev, &undinet_operations );
00900         undinic = netdev->priv;
00901         undi_set_drvdata ( undi, netdev );
00902         netdev->dev = dev;
00903         memset ( undinic, 0, sizeof ( *undinic ) );
00904         undinet_entry_point = undi->entry;
00905         DBGC ( undinic, "UNDINIC %p using UNDI %p\n", undinic, undi );
00906 
00907         /* Hook in UNDI stack */
00908         if ( ! ( undi->flags & UNDI_FL_STARTED ) ) {
00909                 memset ( &start_undi, 0, sizeof ( start_undi ) );
00910                 start_undi.AX = undi->pci_busdevfn;
00911                 start_undi.BX = undi->isapnp_csn;
00912                 start_undi.DX = undi->isapnp_read_port;
00913                 start_undi.ES = BIOS_SEG;
00914                 start_undi.DI = find_pnp_bios();
00915                 if ( ( rc = undinet_call ( undinic, PXENV_START_UNDI,
00916                                            &start_undi,
00917                                            sizeof ( start_undi ) ) ) != 0 )
00918                         goto err_start_undi;
00919         }
00920         undi->flags |= UNDI_FL_STARTED;
00921 
00922         /* Bring up UNDI stack */
00923         if ( ! ( undi->flags & UNDI_FL_INITIALIZED ) ) {
00924                 memset ( &undi_startup, 0, sizeof ( undi_startup ) );
00925                 if ( ( rc = undinet_call ( undinic, PXENV_UNDI_STARTUP,
00926                                            &undi_startup,
00927                                            sizeof ( undi_startup ) ) ) != 0 )
00928                         goto err_undi_startup;
00929                 /* On some PXE stacks, PXENV_UNDI_INITIALIZE may fail
00930                  * due to a transient condition (e.g. media test
00931                  * failing because the link has only just come out of
00932                  * reset).  We may therefore need to retry this call
00933                  * several times.
00934                  */
00935                 for ( retry = 0 ; ; ) {
00936                         memset ( &undi_init, 0, sizeof ( undi_init ) );
00937                         if ( ( rc = undinet_call ( undinic,
00938                                                    PXENV_UNDI_INITIALIZE,
00939                                                    &undi_init,
00940                                                    sizeof ( undi_init ) ) ) ==0)
00941                                 break;
00942                         if ( ++retry > UNDI_INITIALIZE_RETRY_MAX )
00943                                 goto err_undi_initialize;
00944                         DBGC ( undinic, "UNDINIC %p retrying "
00945                                "PXENV_UNDI_INITIALIZE (retry %d)\n",
00946                                undinic, retry );
00947                         /* Delay to allow link to settle if necessary */
00948                         mdelay ( UNDI_INITIALIZE_RETRY_DELAY_MS );
00949                 }
00950         }
00951         undi->flags |= UNDI_FL_INITIALIZED;
00952 
00953         /* Get device information */
00954         memset ( &undi_info, 0, sizeof ( undi_info ) );
00955         if ( ( rc = undinet_call ( undinic, PXENV_UNDI_GET_INFORMATION,
00956                                    &undi_info, sizeof ( undi_info ) ) ) != 0 )
00957                 goto err_undi_get_information;
00958         memcpy ( netdev->hw_addr, undi_info.PermNodeAddress, ETH_ALEN );
00959         memcpy ( netdev->ll_addr, undi_info.CurrentNodeAddress, ETH_ALEN );
00960         undinic->irq = undi_info.IntNumber;
00961         if ( undinic->irq > IRQ_MAX ) {
00962                 DBGC ( undinic, "UNDINIC %p ignoring invalid IRQ %d\n",
00963                        undinic, undinic->irq );
00964                 undinic->irq = 0;
00965         }
00966         DBGC ( undinic, "UNDINIC %p has MAC address %s and IRQ %d\n",
00967                undinic, eth_ntoa ( netdev->hw_addr ), undinic->irq );
00968 
00969         /* Get interface information */
00970         memset ( &undi_iface, 0, sizeof ( undi_iface ) );
00971         if ( ( rc = undinet_call ( undinic, PXENV_UNDI_GET_IFACE_INFO,
00972                                    &undi_iface, sizeof ( undi_iface ) ) ) != 0 )
00973                 goto err_undi_get_iface_info;
00974         DBGC ( undinic, "UNDINIC %p has type %s, speed %d, flags %08x\n",
00975                undinic, undi_iface.IfaceType, undi_iface.LinkSpeed,
00976                undi_iface.ServiceFlags );
00977         if ( ( undi_iface.ServiceFlags & SUPPORTED_IRQ ) &&
00978              ( undinic->irq != 0 ) ) {
00979                 undinic->irq_supported = 1;
00980         }
00981         DBGC ( undinic, "UNDINIC %p using %s mode\n", undinic,
00982                ( undinic->irq_supported ? "interrupt" : "polling" ) );
00983         if ( strncmp ( ( ( char * ) undi_iface.IfaceType ), "Etherboot",
00984                        sizeof ( undi_iface.IfaceType ) ) == 0 ) {
00985                 DBGC ( undinic, "UNDINIC %p Etherboot 5.4 workaround enabled\n",
00986                        undinic );
00987                 undinic->hacks |= UNDI_HACK_EB54;
00988         }
00989         if ( undinet_irq_is_broken ( &dev->desc ) ) {
00990                 DBGC ( undinic, "UNDINIC %p forcing polling mode due to "
00991                        "broken interrupts\n", undinic );
00992                 undinic->irq_supported = 0;
00993         }
00994 
00995         /* Register network device */
00996         if ( ( rc = register_netdev ( netdev ) ) != 0 )
00997                 goto err_register;
00998 
00999         /* Mark as link up; we don't handle link state */
01000         netdev_link_up ( netdev );
01001 
01002         DBGC ( undinic, "UNDINIC %p added\n", undinic );
01003         return 0;
01004 
01005  err_register:
01006  err_undi_get_iface_info:
01007  err_undi_get_information:
01008  err_undi_initialize:
01009         /* Shut down UNDI stack */
01010         memset ( &undi_shutdown, 0, sizeof ( undi_shutdown ) );
01011         undinet_call ( undinic, PXENV_UNDI_SHUTDOWN, &undi_shutdown,
01012                        sizeof ( undi_shutdown ) );
01013         memset ( &undi_cleanup, 0, sizeof ( undi_cleanup ) );
01014         undinet_call ( undinic, PXENV_UNDI_CLEANUP, &undi_cleanup,
01015                        sizeof ( undi_cleanup ) );
01016         undi->flags &= ~UNDI_FL_INITIALIZED;
01017  err_undi_startup:
01018         /* Unhook UNDI stack */
01019         memset ( &stop_undi, 0, sizeof ( stop_undi ) );
01020         undinet_call ( undinic, PXENV_STOP_UNDI, &stop_undi,
01021                        sizeof ( stop_undi ) );
01022         undi->flags &= ~UNDI_FL_STARTED;
01023  err_start_undi:
01024         netdev_nullify ( netdev );
01025         netdev_put ( netdev );
01026         undi_set_drvdata ( undi, NULL );
01027         return rc;
01028 }
01029 
01030 /**
01031  * Remove UNDI device
01032  *
01033  * @v undi              UNDI device
01034  */
01035 void undinet_remove ( struct undi_device *undi ) {
01036         struct net_device *netdev = undi_get_drvdata ( undi );
01037         struct undi_nic *undinic = netdev->priv;
01038         struct s_PXENV_UNDI_SHUTDOWN undi_shutdown;
01039         struct s_PXENV_UNDI_CLEANUP undi_cleanup;
01040         struct s_PXENV_STOP_UNDI stop_undi;
01041 
01042         /* Unregister net device */
01043         unregister_netdev ( netdev );
01044 
01045         /* If we are preparing for an OS boot, or if we cannot exit
01046          * via the PXE stack, then shut down the PXE stack.
01047          */
01048         if ( ! ( undi->flags & UNDI_FL_KEEP_ALL ) ) {
01049 
01050                 /* Shut down UNDI stack */
01051                 memset ( &undi_shutdown, 0, sizeof ( undi_shutdown ) );
01052                 undinet_call ( undinic, PXENV_UNDI_SHUTDOWN,
01053                                &undi_shutdown, sizeof ( undi_shutdown ) );
01054                 memset ( &undi_cleanup, 0, sizeof ( undi_cleanup ) );
01055                 undinet_call ( undinic, PXENV_UNDI_CLEANUP,
01056                                &undi_cleanup, sizeof ( undi_cleanup ) );
01057                 undi->flags &= ~UNDI_FL_INITIALIZED;
01058 
01059                 /* Unhook UNDI stack */
01060                 memset ( &stop_undi, 0, sizeof ( stop_undi ) );
01061                 undinet_call ( undinic, PXENV_STOP_UNDI, &stop_undi,
01062                                sizeof ( stop_undi ) );
01063                 undi->flags &= ~UNDI_FL_STARTED;
01064         }
01065 
01066         /* Clear entry point */
01067         memset ( &undinet_entry_point, 0, sizeof ( undinet_entry_point ) );
01068 
01069         /* Free network device */
01070         netdev_nullify ( netdev );
01071         netdev_put ( netdev );
01072 
01073         DBGC ( undinic, "UNDINIC %p removed\n", undinic );
01074 }