]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[efi] Disable SNP devices when running iPXE as the application
authorMichael Brown <mcb30@ipxe.org>
Fri, 14 Mar 2014 14:16:05 +0000 (14:16 +0000)
committerMichael Brown <mcb30@ipxe.org>
Fri, 14 Mar 2014 17:09:51 +0000 (17:09 +0000)
Some UEFI builds will set up a timer to continuously poll any SNP
devices.  This can drain packets from the network device's receive
queue before iPXE gets a chance to process them.

Use netdev_rx_[un]freeze() to explicitly indicate when we expect our
network devices to be driven via the external SNP API (as we do with
the UNDI API on the standard BIOS build), and disable the SNP API
except when receive queue processing is frozen.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/arch/x86/prefix/efidrvprefix.c
src/image/efi_image.c
src/include/ipxe/efi/efi_snp.h
src/interface/efi/efi_snp.c

index 8a31df56d45fecd5182fdd3085c80f7b1d5f34c8..280e33535fd7d450ef33b8e43cfeaaae36815676 100644 (file)
@@ -22,6 +22,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
 #include <stdlib.h>
 #include <ipxe/init.h>
 #include <ipxe/efi/efi.h>
+#include <ipxe/efi/efi_snp.h>
 
 /**
  * EFI entry point
@@ -42,7 +43,8 @@ EFI_STATUS EFIAPI _efidrv_start ( EFI_HANDLE image_handle,
        initialise();
        startup();
 
+       /* Release network devices for use via SNP */
+       efi_snp_release();
+
        return 0;
 }
-
-REQUIRE_OBJECT ( efi_snp );
index 1c1ee0c3c5e81e974ac382479cf29ce7b5f7576b..5de915b0a576e12dfcf50fb414cb7a5886933091 100644 (file)
@@ -219,6 +219,9 @@ static int efi_image_exec ( struct image *image ) {
        loaded.image->LoadOptionsSize =
                ( ( wcslen ( cmdline ) + 1 /* NUL */ ) * sizeof ( wchar_t ) );
 
+       /* Release network devices for use via SNP */
+       efi_snp_release();
+
        /* Start the image */
        if ( ( efirc = bs->StartImage ( handle, NULL, NULL ) ) != 0 ) {
                rc = -EEFI_START ( efirc );
@@ -231,6 +234,7 @@ static int efi_image_exec ( struct image *image ) {
        rc = 0;
 
  err_start_image:
+       efi_snp_claim();
  err_open_protocol:
        /* Unload the image.  We can't leave it loaded, because we
         * have no "unload" operation.
index 3791645975d3a4e0595ebe8d61df61ce35d91a37..e1b866eb8dbe861da2fbdcb178a54bcf0af0aa25 100644 (file)
@@ -32,6 +32,8 @@ struct efi_snp_device {
        EFI_SIMPLE_NETWORK_PROTOCOL snp;
        /** The SNP "mode" (parameters) */
        EFI_SIMPLE_NETWORK_MODE mode;
+       /** Started flag */
+       int started;
        /** Outstanding TX packet count (via "interrupt status")
         *
         * Used in order to generate TX completions.
@@ -75,5 +77,7 @@ struct efi_snp_device {
 extern int efi_snp_hii_install ( struct efi_snp_device *snpdev );
 extern void efi_snp_hii_uninstall ( struct efi_snp_device *snpdev );
 extern struct efi_snp_device * last_opened_snpdev ( void );
+extern void efi_snp_claim ( void );
+extern void efi_snp_release ( void );
 
 #endif /* _IPXE_EFI_SNP_H */
index adeb38de3f1d5ce69ae42d767b106025559f2bf4..9c541552bef75ff96ed5cc44e2722501e480923e 100644 (file)
@@ -69,6 +69,35 @@ static EFI_GUID efi_load_file_protocol_guid
 /** List of SNP devices */
 static LIST_HEAD ( efi_snp_devices );
 
+/**
+ * Set EFI SNP mode state
+ *
+ * @v snp              SNP interface
+ */
+static void efi_snp_set_state ( struct efi_snp_device *snpdev ) {
+       struct net_device *netdev = snpdev->netdev;
+       EFI_SIMPLE_NETWORK_MODE *mode = &snpdev->mode;
+
+       /* Calculate state */
+       if ( ! snpdev->started ) {
+               /* Start() method not called; report as Stopped */
+               mode->State = EfiSimpleNetworkStopped;
+       } else if ( ! netdev_is_open ( netdev ) ) {
+               /* Network device not opened; report as Started */
+               mode->State = EfiSimpleNetworkStarted;
+       } else if ( ! netdev_rx_frozen ( netdev ) ) {
+               /* Network device opened but claimed for use by iPXE; report
+                * as Started to inhibit receive polling.
+                */
+               mode->State = EfiSimpleNetworkStarted;
+       } else {
+               /* Network device opened and available for use via SNP; report
+                * as Initialized.
+                */
+               mode->State = EfiSimpleNetworkInitialized;
+       }
+}
+
 /**
  * Set EFI SNP mode based on iPXE net device parameters
  *
@@ -134,7 +163,12 @@ efi_snp_start ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ) {
 
        DBGC2 ( snpdev, "SNPDEV %p START\n", snpdev );
 
-       snpdev->mode.State = EfiSimpleNetworkStarted;
+       /* Fail if net device is currently claimed for use by iPXE */
+       if ( ! netdev_rx_frozen ( snpdev->netdev ) )
+               return EFI_NOT_READY;
+
+       snpdev->started = 1;
+       efi_snp_set_state ( snpdev );
        return 0;
 }
 
@@ -151,7 +185,12 @@ efi_snp_stop ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ) {
 
        DBGC2 ( snpdev, "SNPDEV %p STOP\n", snpdev );
 
-       snpdev->mode.State = EfiSimpleNetworkStopped;
+       /* Fail if net device is currently claimed for use by iPXE */
+       if ( ! netdev_rx_frozen ( snpdev->netdev ) )
+               return EFI_NOT_READY;
+
+       snpdev->started = 0;
+       efi_snp_set_state ( snpdev );
        return 0;
 }
 
@@ -174,13 +213,17 @@ efi_snp_initialize ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
                snpdev, ( ( unsigned long ) extra_rx_bufsize ),
                ( ( unsigned long ) extra_tx_bufsize ) );
 
+       /* Fail if net device is currently claimed for use by iPXE */
+       if ( ! netdev_rx_frozen ( snpdev->netdev ) )
+               return EFI_NOT_READY;
+
        if ( ( rc = netdev_open ( snpdev->netdev ) ) != 0 ) {
                DBGC ( snpdev, "SNPDEV %p could not open %s: %s\n",
                       snpdev, snpdev->netdev->name, strerror ( rc ) );
                return EFIRC ( rc );
        }
+       efi_snp_set_state ( snpdev );
 
-       snpdev->mode.State = EfiSimpleNetworkInitialized;
        return 0;
 }
 
@@ -200,16 +243,20 @@ efi_snp_reset ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN ext_verify ) {
        DBGC2 ( snpdev, "SNPDEV %p RESET (%s extended verification)\n",
                snpdev, ( ext_verify ? "with" : "without" ) );
 
+       /* Fail if net device is currently claimed for use by iPXE */
+       if ( ! netdev_rx_frozen ( snpdev->netdev ) )
+               return EFI_NOT_READY;
+
        netdev_close ( snpdev->netdev );
-       snpdev->mode.State = EfiSimpleNetworkStarted;
+       efi_snp_set_state ( snpdev );
 
        if ( ( rc = netdev_open ( snpdev->netdev ) ) != 0 ) {
                DBGC ( snpdev, "SNPDEV %p could not reopen %s: %s\n",
                       snpdev, snpdev->netdev->name, strerror ( rc ) );
                return EFIRC ( rc );
        }
+       efi_snp_set_state ( snpdev );
 
-       snpdev->mode.State = EfiSimpleNetworkInitialized;
        return 0;
 }
 
@@ -226,8 +273,13 @@ efi_snp_shutdown ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ) {
 
        DBGC2 ( snpdev, "SNPDEV %p SHUTDOWN\n", snpdev );
 
+       /* Fail if net device is currently claimed for use by iPXE */
+       if ( ! netdev_rx_frozen ( snpdev->netdev ) )
+               return EFI_NOT_READY;
+
        netdev_close ( snpdev->netdev );
-       snpdev->mode.State = EfiSimpleNetworkStarted;
+       efi_snp_set_state ( snpdev );
+
        return 0;
 }
 
@@ -258,6 +310,10 @@ efi_snp_receive_filters ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, UINT32 enable,
                            snpdev->netdev->ll_protocol->ll_addr_len );
        }
 
+       /* Fail if net device is currently claimed for use by iPXE */
+       if ( ! netdev_rx_frozen ( snpdev->netdev ) )
+               return EFI_NOT_READY;
+
        /* Lie through our teeth, otherwise MNP refuses to accept us */
        return 0;
 }
@@ -280,6 +336,10 @@ efi_snp_station_address ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN reset,
        DBGC2 ( snpdev, "SNPDEV %p STATION_ADDRESS %s\n", snpdev,
                ( reset ? "reset" : ll_protocol->ntoa ( new ) ) );
 
+       /* Fail if net device is currently claimed for use by iPXE */
+       if ( ! netdev_rx_frozen ( snpdev->netdev ) )
+               return EFI_NOT_READY;
+
        /* Set the MAC address */
        if ( reset )
                new = &snpdev->mode.PermanentAddress;
@@ -313,6 +373,10 @@ efi_snp_statistics ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN reset,
        DBGC2 ( snpdev, "SNPDEV %p STATISTICS%s", snpdev,
                ( reset ? " reset" : "" ) );
 
+       /* Fail if net device is currently claimed for use by iPXE */
+       if ( ! netdev_rx_frozen ( snpdev->netdev ) )
+               return EFI_NOT_READY;
+
        /* Gather statistics */
        memset ( &stats_buf, 0, sizeof ( stats_buf ) );
        stats_buf.TxGoodFrames = snpdev->netdev->tx_stats.good;
@@ -361,6 +425,10 @@ efi_snp_mcast_ip_to_mac ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN ipv6,
                   inet_ntoa ( *( ( struct in_addr * ) ip ) ) );
        DBGC2 ( snpdev, "SNPDEV %p MCAST_IP_TO_MAC %s\n", snpdev, ip_str );
 
+       /* Fail if net device is currently claimed for use by iPXE */
+       if ( ! netdev_rx_frozen ( snpdev->netdev ) )
+               return EFI_NOT_READY;
+
        /* Try to hash the address */
        if ( ( rc = ll_protocol->mc_hash ( ( ipv6 ? AF_INET6 : AF_INET ),
                                           ip, mac ) ) != 0 ) {
@@ -394,6 +462,10 @@ efi_snp_nvdata ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN read,
        if ( ! read )
                DBGC2_HDA ( snpdev, offset, data, len );
 
+       /* Fail if net device is currently claimed for use by iPXE */
+       if ( ! netdev_rx_frozen ( snpdev->netdev ) )
+               return EFI_NOT_READY;
+
        return EFI_UNSUPPORTED;
 }
 
@@ -413,6 +485,10 @@ efi_snp_get_status ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
 
        DBGC2 ( snpdev, "SNPDEV %p GET_STATUS", snpdev );
 
+       /* Fail if net device is currently claimed for use by iPXE */
+       if ( ! netdev_rx_frozen ( snpdev->netdev ) )
+               return EFI_NOT_READY;
+
        /* Poll the network device */
        efi_snp_poll ( snpdev );
 
@@ -508,6 +584,10 @@ efi_snp_transmit ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
        }
        DBGC2 ( snpdev, "\n" );
 
+       /* Fail if net device is currently claimed for use by iPXE */
+       if ( ! netdev_rx_frozen ( snpdev->netdev ) )
+               return EFI_NOT_READY;
+
        /* Sanity checks */
        if ( ll_header_len ) {
                if ( ll_header_len != ll_protocol->ll_header_len ) {
@@ -616,6 +696,10 @@ efi_snp_receive ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
        DBGC2 ( snpdev, "SNPDEV %p RECEIVE %p(+%lx)", snpdev, data,
                ( ( unsigned long ) *len ) );
 
+       /* Fail if net device is currently claimed for use by iPXE */
+       if ( ! netdev_rx_frozen ( snpdev->netdev ) )
+               return EFI_NOT_READY;
+
        /* Poll the network device */
        efi_snp_poll ( snpdev );
 
@@ -655,7 +739,7 @@ efi_snp_receive ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
 
  out_bad_ll_header:
        free_iob ( iobuf );
-out_no_packet:
+ out_no_packet:
        return EFIRC ( rc );
 }
 
@@ -676,6 +760,10 @@ static VOID EFIAPI efi_snp_wait_for_packet ( EFI_EVENT event,
        if ( ! netdev_is_open ( snpdev->netdev ) )
                return;
 
+       /* Do nothing if net device is currently claimed for use by iPXE */
+       if ( ! netdev_rx_frozen ( snpdev->netdev ) )
+               return;
+
        /* Poll the network device */
        efi_snp_poll ( snpdev );
 
@@ -785,9 +873,15 @@ efi_snp_load_file ( EFI_LOAD_FILE_PROTOCOL *load_file,
                return EFI_UNSUPPORTED;
        }
 
+       /* Claim network devices for use by iPXE */
+       efi_snp_claim();
+
        /* Boot from network device */
        ipxe ( netdev );
 
+       /* Release network devices for use via SNP */
+       efi_snp_release();
+
        /* Assume boot process was aborted */
        return EFI_ABORTED;
 }
@@ -1011,6 +1105,9 @@ static void efi_snp_notify ( struct net_device *netdev ) {
                ( netdev_link_ok ( netdev ) ? TRUE : FALSE );
        DBGC ( snpdev, "SNPDEV %p link is %s\n", snpdev,
               ( snpdev->mode.MediaPresent ? "up" : "down" ) );
+
+       /* Update mode state */
+       efi_snp_set_state ( snpdev );
 }
 
 /**
@@ -1069,3 +1166,25 @@ struct efi_snp_device * last_opened_snpdev ( void ) {
 
        return efi_snp_demux ( netdev );
 }
+
+/**
+ * Claim network devices for use by iPXE
+ *
+ */
+void efi_snp_claim ( void ) {
+       struct net_device *netdev;
+
+       for_each_netdev ( netdev )
+               netdev_rx_unfreeze ( netdev );
+}
+
+/**
+ * Release network devices for use via SNP
+ *
+ */
+void efi_snp_release ( void ) {
+       struct net_device *netdev;
+
+       for_each_netdev ( netdev )
+               netdev_rx_freeze ( netdev );
+}