david/ipxe
david
/
ipxe
Archived
1
0
Fork 0

[efi] Disable SNP devices when running iPXE as the application

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>
This commit is contained in:
Michael Brown 2014-03-14 14:16:05 +00:00
parent f618178e60
commit f473b9c3f6
4 changed files with 138 additions and 9 deletions

View 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 );

View 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.

View 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 */

View 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 );
}