summaryrefslogtreecommitdiffstats
path: root/src/drivers/net/efi
diff options
context:
space:
mode:
Diffstat (limited to 'src/drivers/net/efi')
-rw-r--r--src/drivers/net/efi/mnp.c4
-rw-r--r--src/drivers/net/efi/mnpnet.c26
-rw-r--r--src/drivers/net/efi/nii.c67
-rw-r--r--src/drivers/net/efi/nii.h2
-rw-r--r--src/drivers/net/efi/snp.c16
-rw-r--r--src/drivers/net/efi/snpnet.c245
-rw-r--r--src/drivers/net/efi/snpnet.h5
-rw-r--r--src/drivers/net/efi/snponly.c57
8 files changed, 307 insertions, 115 deletions
diff --git a/src/drivers/net/efi/mnp.c b/src/drivers/net/efi/mnp.c
index 33218fb10..212c712df 100644
--- a/src/drivers/net/efi/mnp.c
+++ b/src/drivers/net/efi/mnp.c
@@ -44,11 +44,11 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
static int mnp_supported ( EFI_HANDLE device ) {
EFI_GUID *binding = &efi_managed_network_service_binding_protocol_guid;
- return snpnet_supported ( device, binding );
+ return snpnet_supported ( device, binding, 0 );
}
/** EFI MNP driver */
-struct efi_driver mnp_driver __efi_driver ( EFI_DRIVER_NORMAL ) = {
+struct efi_driver mnp_driver __efi_driver ( EFI_DRIVER_MNP ) = {
.name = "MNP",
.supported = mnp_supported,
.start = mnpnet_start,
diff --git a/src/drivers/net/efi/mnpnet.c b/src/drivers/net/efi/mnpnet.c
index eb4b129c7..fe0ebaadb 100644
--- a/src/drivers/net/efi/mnpnet.c
+++ b/src/drivers/net/efi/mnpnet.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
/** @file
*
@@ -367,14 +368,9 @@ static struct net_device_operations mnpnet_operations = {
* @ret rc Return status code
*/
int mnpnet_start ( struct efi_device *efidev ) {
- EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
EFI_HANDLE device = efidev->device;
EFI_GUID *binding = &efi_managed_network_service_binding_protocol_guid;
EFI_SIMPLE_NETWORK_MODE mode;
- union {
- EFI_MANAGED_NETWORK_PROTOCOL *mnp;
- void *interface;
- } u;
struct net_device *netdev;
struct mnp_nic *mnp;
EFI_STATUS efirc;
@@ -408,18 +404,13 @@ int mnpnet_start ( struct efi_device *efidev ) {
}
/* Open MNP protocol */
- if ( ( efirc = bs->OpenProtocol ( efidev->child,
- &efi_managed_network_protocol_guid,
- &u.interface, efi_image_handle,
- efidev->child,
- ( EFI_OPEN_PROTOCOL_BY_DRIVER |
- EFI_OPEN_PROTOCOL_EXCLUSIVE )))!=0){
- rc = -EEFI ( efirc );
+ if ( ( rc = efi_open_by_driver ( efidev->child,
+ &efi_managed_network_protocol_guid,
+ &mnp->mnp ) ) != 0 ) {
DBGC ( mnp, "MNP %s could not open MNP protocol: %s\n",
efi_handle_name ( device ), strerror ( rc ) );
goto err_open;
}
- mnp->mnp = u.mnp;
/* Get configuration */
efirc = mnp->mnp->GetModeData ( mnp->mnp, NULL, &mode );
@@ -464,8 +455,8 @@ int mnpnet_start ( struct efi_device *efidev ) {
err_ll_addr_len:
err_hw_addr_len:
err_mode:
- bs->CloseProtocol ( efidev->child, &efi_managed_network_protocol_guid,
- efi_image_handle, efidev->child );
+ efi_close_by_driver ( efidev->child,
+ &efi_managed_network_protocol_guid );
err_open:
efi_service_del ( device, binding, efidev->child );
err_service:
@@ -482,7 +473,6 @@ int mnpnet_start ( struct efi_device *efidev ) {
* @v efidev EFI device
*/
void mnpnet_stop ( struct efi_device *efidev ) {
- EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
EFI_GUID *binding = &efi_managed_network_service_binding_protocol_guid;
struct net_device *netdev = efidev_get_drvdata ( efidev );
struct mnp_nic *mnp = netdev->priv;
@@ -491,8 +481,8 @@ void mnpnet_stop ( struct efi_device *efidev ) {
unregister_netdev ( netdev );
/* Close MNP protocol */
- bs->CloseProtocol ( efidev->child, &efi_managed_network_protocol_guid,
- efi_image_handle, efidev->child );
+ efi_close_by_driver ( efidev->child,
+ &efi_managed_network_protocol_guid );
/* Remove MNP child (unless whole system shutdown is in progress) */
if ( ! efi_shutdown_in_progress )
diff --git a/src/drivers/net/efi/nii.c b/src/drivers/net/efi/nii.c
index 16e9e10df..d1adf3d44 100644
--- a/src/drivers/net/efi/nii.c
+++ b/src/drivers/net/efi/nii.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <string.h>
#include <strings.h>
@@ -177,7 +178,7 @@ struct nii_nic {
size_t mtu;
/** Hardware transmit/receive buffer */
- userptr_t buffer;
+ void *buffer;
/** Hardware transmit/receive buffer length */
size_t buffer_len;
@@ -210,10 +211,6 @@ static int nii_pci_open ( struct nii_nic *nii ) {
EFI_HANDLE device = nii->efidev->device;
EFI_HANDLE pci_device;
union {
- EFI_PCI_IO_PROTOCOL *pci_io;
- void *interface;
- } pci_io;
- union {
EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *acpi;
void *resource;
} desc;
@@ -230,17 +227,18 @@ static int nii_pci_open ( struct nii_nic *nii ) {
}
nii->pci_device = pci_device;
- /* Open PCI I/O protocol */
- if ( ( efirc = bs->OpenProtocol ( pci_device, &efi_pci_io_protocol_guid,
- &pci_io.interface, efi_image_handle,
- device,
- EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){
- rc = -EEFI ( efirc );
+ /* Open PCI I/O protocol
+ *
+ * We cannot open this safely as a by-driver open, since doing
+ * so would disconnect the underlying NII driver. We must
+ * therefore use an unsafe open.
+ */
+ if ( ( rc = efi_open_unsafe ( pci_device, &efi_pci_io_protocol_guid,
+ &nii->pci_io ) ) != 0 ) {
DBGC ( nii, "NII %s could not open PCI I/O protocol: %s\n",
nii->dev.name, strerror ( rc ) );
goto err_open;
}
- nii->pci_io = pci_io.pci_io;
/* Identify memory and I/O BARs */
nii->mem_bar = PCI_MAX_BAR;
@@ -280,8 +278,7 @@ static int nii_pci_open ( struct nii_nic *nii ) {
return 0;
err_get_bar_attributes:
- bs->CloseProtocol ( pci_device, &efi_pci_io_protocol_guid,
- efi_image_handle, device );
+ efi_close_unsafe ( pci_device, &efi_pci_io_protocol_guid );
err_open:
err_locate:
return rc;
@@ -294,7 +291,6 @@ static int nii_pci_open ( struct nii_nic *nii ) {
* @ret rc Return status code
*/
static void nii_pci_close ( struct nii_nic *nii ) {
- EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
struct nii_mapping *map;
struct nii_mapping *tmp;
@@ -308,8 +304,7 @@ static void nii_pci_close ( struct nii_nic *nii ) {
}
/* Close protocols */
- bs->CloseProtocol ( nii->pci_device, &efi_pci_io_protocol_guid,
- efi_image_handle, nii->efidev->device );
+ efi_close_unsafe ( nii->pci_device, &efi_pci_io_protocol_guid );
}
/**
@@ -1264,18 +1259,35 @@ static struct net_device_operations nii_operations = {
};
/**
+ * Exclude existing drivers
+ *
+ * @v device EFI device handle
+ * @ret rc Return status code
+ */
+int nii_exclude ( EFI_HANDLE device ) {
+ EFI_GUID *protocol = &efi_nii31_protocol_guid;
+ int rc;
+
+ /* Exclude existing NII protocol drivers */
+ if ( ( rc = efi_driver_exclude ( device, protocol ) ) != 0 ) {
+ DBGC ( device, "NII %s could not exclude drivers: %s\n",
+ efi_handle_name ( device ), strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
* Attach driver to device
*
* @v efidev EFI device
* @ret rc Return status code
*/
int nii_start ( struct efi_device *efidev ) {
- EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
EFI_HANDLE device = efidev->device;
struct net_device *netdev;
struct nii_nic *nii;
- void *interface;
- EFI_STATUS efirc;
int rc;
/* Allocate and initialise structure */
@@ -1300,17 +1312,13 @@ int nii_start ( struct efi_device *efidev ) {
netdev->dev = &nii->dev;
/* Open NII protocol */
- if ( ( efirc = bs->OpenProtocol ( device, &efi_nii31_protocol_guid,
- &interface, efi_image_handle, device,
- ( EFI_OPEN_PROTOCOL_BY_DRIVER |
- EFI_OPEN_PROTOCOL_EXCLUSIVE )))!=0){
- rc = -EEFI ( efirc );
+ if ( ( rc = efi_open_by_driver ( device, &efi_nii31_protocol_guid,
+ &nii->nii ) ) != 0 ) {
DBGC ( nii, "NII %s cannot open NII protocol: %s\n",
nii->dev.name, strerror ( rc ) );
DBGC_EFI_OPENERS ( device, device, &efi_nii31_protocol_guid );
goto err_open_protocol;
}
- nii->nii = interface;
/* Locate UNDI and entry point */
nii->undi = ( ( void * ) ( intptr_t ) nii->nii->Id );
@@ -1373,8 +1381,7 @@ int nii_start ( struct efi_device *efidev ) {
err_pci_open:
err_hw_undi:
err_no_undi:
- bs->CloseProtocol ( device, &efi_nii31_protocol_guid,
- efi_image_handle, device );
+ efi_close_by_driver ( device, &efi_nii31_protocol_guid );
err_open_protocol:
list_del ( &nii->dev.siblings );
netdev_nullify ( netdev );
@@ -1389,7 +1396,6 @@ int nii_start ( struct efi_device *efidev ) {
* @v efidev EFI device
*/
void nii_stop ( struct efi_device *efidev ) {
- EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
struct net_device *netdev = efidev_get_drvdata ( efidev );
struct nii_nic *nii = netdev->priv;
EFI_HANDLE device = efidev->device;
@@ -1404,8 +1410,7 @@ void nii_stop ( struct efi_device *efidev ) {
nii_pci_close ( nii );
/* Close NII protocol */
- bs->CloseProtocol ( device, &efi_nii31_protocol_guid,
- efi_image_handle, device );
+ efi_close_by_driver ( device, &efi_nii31_protocol_guid );
/* Free network device */
list_del ( &nii->dev.siblings );
diff --git a/src/drivers/net/efi/nii.h b/src/drivers/net/efi/nii.h
index c10be9db5..e0b07f0a5 100644
--- a/src/drivers/net/efi/nii.h
+++ b/src/drivers/net/efi/nii.h
@@ -8,9 +8,11 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
struct efi_device;
+extern int nii_exclude ( EFI_HANDLE device );
extern int nii_start ( struct efi_device *efidev );
extern void nii_stop ( struct efi_device *efidev );
diff --git a/src/drivers/net/efi/snp.c b/src/drivers/net/efi/snp.c
index cac8b38e2..854fa872d 100644
--- a/src/drivers/net/efi/snp.c
+++ b/src/drivers/net/efi/snp.c
@@ -41,8 +41,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
* @ret rc Return status code
*/
static int snp_supported ( EFI_HANDLE device ) {
+ EFI_GUID *protocol = &efi_simple_network_protocol_guid;
- return snpnet_supported ( device, &efi_simple_network_protocol_guid );
+ return snpnet_supported ( device, protocol, 1 );
}
/**
@@ -52,22 +53,29 @@ static int snp_supported ( EFI_HANDLE device ) {
* @ret rc Return status code
*/
static int nii_supported ( EFI_HANDLE device ) {
+ EFI_GUID *protocol = &efi_nii31_protocol_guid;
- return snpnet_supported ( device, &efi_nii31_protocol_guid );
+ return snpnet_supported ( device, protocol, 1 );
}
/** EFI SNP driver */
-struct efi_driver snp_driver __efi_driver ( EFI_DRIVER_NORMAL ) = {
+struct efi_driver snp_driver __efi_driver ( EFI_DRIVER_SNP ) = {
.name = "SNP",
.supported = snp_supported,
+ .exclude = snpnet_exclude,
.start = snpnet_start,
.stop = snpnet_stop,
};
/** EFI NII driver */
-struct efi_driver nii_driver __efi_driver ( EFI_DRIVER_NORMAL ) = {
+struct efi_driver nii_driver __efi_driver ( EFI_DRIVER_NII ) = {
.name = "NII",
.supported = nii_supported,
+ .exclude = nii_exclude,
.start = nii_start,
.stop = nii_stop,
};
+
+/** Drag in MNP driver */
+REQUIRING_SYMBOL ( snp_driver );
+REQUIRE_SYMBOL ( mnp_driver );
diff --git a/src/drivers/net/efi/snpnet.c b/src/drivers/net/efi/snpnet.c
index 6ce731d78..6046f0a1e 100644
--- a/src/drivers/net/efi/snpnet.c
+++ b/src/drivers/net/efi/snpnet.c
@@ -18,6 +18,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER );
+FILE_SECBOOT ( PERMITTED );
#include <stdlib.h>
#include <stdio.h>
@@ -86,6 +87,14 @@ struct snp_nic {
*/
#define SNP_RX_PAD 8
+/** An SNP interface patch to inhibit shutdown for insomniac devices */
+struct snp_insomniac_patch {
+ /** Original Shutdown() method */
+ EFI_SIMPLE_NETWORK_SHUTDOWN shutdown;
+ /** Original Stop() method */
+ EFI_SIMPLE_NETWORK_STOP stop;
+};
+
/**
* Format SNP MAC address (for debugging)
*
@@ -387,9 +396,10 @@ static int snpnet_open ( struct net_device *netdev ) {
/* Initialise NIC, retrying multiple times if link stays down */
for ( retry = 0 ; ; ) {
- /* Initialise NIC */
- if ( ( efirc = snp->snp->Initialize ( snp->snp,
- 0, 0 ) ) != 0 ) {
+ /* Initialise NIC, if not already initialised */
+ if ( ( mode->State != EfiSimpleNetworkInitialized ) &&
+ ( ( efirc = snp->snp->Initialize ( snp->snp,
+ 0, 0 ) ) != 0 ) ) {
rc = -EEFI ( efirc );
snpnet_dump_mode ( netdev );
DBGC ( snp, "SNP %s could not initialise: %s\n",
@@ -413,11 +423,13 @@ static int snpnet_open ( struct net_device *netdev ) {
/* Delay to allow time for link to establish */
mdelay ( SNP_INITIALIZE_RETRY_DELAY_MS );
- /* Shut down and retry; this is sometimes necessary in
- * order to persuade the underlying SNP driver to
- * actually update the link state.
+ /* Shut down and retry (unless device is insomniac);
+ * this is sometimes necessary in order to persuade
+ * the underlying SNP driver to actually update the
+ * link state.
*/
- if ( ( efirc = snp->snp->Shutdown ( snp->snp ) ) != 0 ) {
+ if ( ( ! netdev_insomniac ( netdev ) ) &&
+ ( ( efirc = snp->snp->Shutdown ( snp->snp ) ) != 0 ) ) {
rc = -EEFI ( efirc );
snpnet_dump_mode ( netdev );
DBGC ( snp, "SNP %s could not shut down: %s\n",
@@ -455,8 +467,11 @@ static void snpnet_close ( struct net_device *netdev ) {
EFI_STATUS efirc;
int rc;
- /* Shut down NIC (unless whole system shutdown is in progress) */
+ /* Shut down NIC (unless whole system shutdown is in progress,
+ * or device is insomniac).
+ */
if ( ( ! efi_shutdown_in_progress ) &&
+ ( ! netdev_insomniac ( netdev ) ) &&
( ( efirc = snp->snp->Shutdown ( snp->snp ) ) != 0 ) ) {
rc = -EEFI ( efirc );
DBGC ( snp, "SNP %s could not shut down: %s\n",
@@ -490,12 +505,12 @@ static struct net_device_operations snpnet_operations = {
*
* @v device EFI device handle
* @v protocol Protocol GUID
+ * @v inhibit_wifi Inhibit wireless devices
* @ret rc Return status code
*/
-int snpnet_supported ( EFI_HANDLE device, EFI_GUID *protocol ) {
- EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+int snpnet_supported ( EFI_HANDLE device, EFI_GUID *protocol,
+ int inhibit_wifi ) {
EFI_HANDLE parent;
- EFI_STATUS efirc;
int rc;
/* Check that this is not a device we are providing ourselves */
@@ -506,13 +521,11 @@ int snpnet_supported ( EFI_HANDLE device, EFI_GUID *protocol ) {
}
/* Test for presence of protocol */
- if ( ( efirc = bs->OpenProtocol ( device, protocol,
- NULL, efi_image_handle, device,
- EFI_OPEN_PROTOCOL_TEST_PROTOCOL))!=0){
+ if ( ( rc = efi_test ( device, protocol ) ) != 0 ) {
DBGCP ( device, "HANDLE %s is not a %s device\n",
efi_handle_name ( device ),
efi_guid_ntoa ( protocol ) );
- return -EEFI ( efirc );
+ return rc;
}
/* Check that there are no instances of this protocol further
@@ -526,35 +539,205 @@ int snpnet_supported ( EFI_HANDLE device, EFI_GUID *protocol ) {
DBGC2 ( device, "%s\n", efi_handle_name ( parent ) );
return -ENOTTY;
}
-
DBGC ( device, "HANDLE %s is a %s device\n",
efi_handle_name ( device ), efi_guid_ntoa ( protocol ) );
+
+ /* Check for wireless devices, if applicable */
+ if ( inhibit_wifi &&
+ ( ( efi_test ( device, &efi_wifi2_protocol_guid ) ) == 0 ) ) {
+ DBGC ( device, "HANDLE %s is wireless: assuming vendor %s "
+ "driver is too unreliable to use\n",
+ efi_handle_name ( device ),
+ efi_guid_ntoa ( protocol ) );
+ return -ENOTTY;
+ }
+
+ return 0;
+}
+
+/**
+ * Check if device must be insomniac
+ *
+ * @v device EFI device handle
+ * @v is_insomniac Device must be insomniac
+ */
+static int snpnet_is_insomniac ( EFI_HANDLE device ) {
+ int rc;
+
+ /* Check for wireless devices
+ *
+ * The UEFI model for wireless network configuration is
+ * somewhat underdefined. At the time of writing, the EDK2
+ * "UEFI WiFi Connection Manager" driver provides only one way
+ * to configure wireless network credentials, which is to
+ * enter them interactively via an HII form. Credentials are
+ * not stored (or exposed via any protocol interface), and so
+ * any temporary disconnection from the wireless network will
+ * inevitably leave the interface in an unusable state that
+ * cannot be recovered without user intervention.
+ *
+ * Experimentation shows that at least some wireless network
+ * drivers will disconnect from the wireless network when the
+ * SNP Shutdown() method is called, or if the device is not
+ * polled sufficiently frequently to maintain its association
+ * to the network. We therefore inhibit calls to Shutdown()
+ * and Stop() for any such SNP protocol interfaces, and mark
+ * our network device as insomniac so that it will be polled
+ * even when closed.
+ */
+ if ( ( rc = efi_test ( device, &efi_wifi2_protocol_guid ) ) == 0 ) {
+ DBGC ( device, "SNP %s is wireless: assuming insomniac\n",
+ efi_handle_name ( device ) );
+ return 1;
+ }
+
return 0;
}
/**
+ * Ignore shutdown attempt
+ *
+ * @v snp SNP interface
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI
+snpnet_do_nothing ( EFI_SIMPLE_NETWORK_PROTOCOL *snp __unused ) {
+
+ return 0;
+}
+
+/**
+ * Patch SNP protocol interface to prevent shutdown
+ *
+ * @v device EFI device handle
+ * @v patch Interface patch
+ * @ret rc Return status code
+ */
+static int snpnet_insomniac_patch ( EFI_HANDLE device,
+ struct snp_insomniac_patch *patch ) {
+ EFI_SIMPLE_NETWORK_PROTOCOL *interface;
+ int rc;
+
+ /* Open interface for ephemeral use */
+ if ( ( rc = efi_open ( device, &efi_simple_network_protocol_guid,
+ &interface ) ) != 0 ) {
+ DBGC ( device, "SNP %s cannot open SNP protocol for patching: "
+ "%s\n", efi_handle_name ( device ), strerror ( rc ) );
+ return rc;
+ }
+
+ /* Record original Shutdown() and Stop() methods */
+ patch->shutdown = interface->Shutdown;
+ patch->stop = interface->Stop;
+
+ /* Inhibit other UEFI drivers' calls to Shutdown() and Stop()
+ *
+ * This is necessary since disconnecting the MnpDxe driver
+ * will attempt to shut down the SNP device, which would leave
+ * us with an unusable device.
+ */
+ interface->Shutdown = snpnet_do_nothing;
+ interface->Stop = snpnet_do_nothing;
+ DBGC ( device, "SNP %s patched to inhibit shutdown\n",
+ efi_handle_name ( device ) );
+
+ return 0;
+}
+
+/**
+ * Restore patched SNP protocol interface
+ *
+ * @v device EFI device handle
+ * @v patch Interface patch to fill in
+ * @ret rc Return status code
+ */
+static int snpnet_insomniac_restore ( EFI_HANDLE device,
+ struct snp_insomniac_patch *patch ) {
+ EFI_SIMPLE_NETWORK_PROTOCOL *interface;
+ int rc;
+
+ /* Avoid returning uninitialised data on error */
+ memset ( patch, 0, sizeof ( *patch ) );
+
+ /* Open interface for ephemeral use */
+ if ( ( rc = efi_open ( device, &efi_simple_network_protocol_guid,
+ &interface ) ) != 0 ) {
+ DBGC ( device, "SNP %s cannot open patched SNP protocol: %s\n",
+ efi_handle_name ( device ), strerror ( rc ) );
+ return rc;
+ }
+
+ /* Restore original Shutdown() and Stop() methods, if possible */
+ if ( interface->Shutdown == snpnet_do_nothing )
+ interface->Shutdown = patch->shutdown;
+ if ( interface->Stop == snpnet_do_nothing )
+ interface->Stop = patch->stop;
+
+ /* Check that original methods were restored (by us or others) */
+ if ( ( interface->Shutdown != patch->shutdown ) ||
+ ( interface->Stop != patch->stop ) ) {
+ DBGC ( device, "SNP %s could not restore patched SNP "
+ "protocol\n", efi_handle_name ( device ) );
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+/**
+ * Exclude existing drivers
+ *
+ * @v device EFI device handle
+ * @ret rc Return status code
+ */
+int snpnet_exclude ( EFI_HANDLE device ) {
+ EFI_GUID *protocol = &efi_simple_network_protocol_guid;
+ struct snp_insomniac_patch patch;
+ int insomniac;
+ int rc;
+
+ /* Check if this is a device that must not ever be shut down */
+ insomniac = snpnet_is_insomniac ( device );
+
+ /* Inhibit calls to Shutdown() and Stop(), if applicable */
+ if ( insomniac &&
+ ( ( rc = snpnet_insomniac_patch ( device, &patch ) ) != 0 ) ) {
+ goto err_patch;
+ }
+
+ /* Exclude existing SNP drivers */
+ if ( ( rc = efi_driver_exclude ( device, protocol ) ) != 0 ) {
+ DBGC ( device, "SNP %s could not exclude drivers: %s\n",
+ efi_handle_name ( device ), strerror ( rc ) );
+ goto err_exclude;
+ }
+
+ err_exclude:
+ if ( insomniac )
+ snpnet_insomniac_restore ( device, &patch );
+ err_patch:
+ return rc;
+}
+
+/**
* Attach driver to device
*
* @v efidev EFI device
* @ret rc Return status code
*/
int snpnet_start ( struct efi_device *efidev ) {
- EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
EFI_HANDLE device = efidev->device;
+ EFI_SIMPLE_NETWORK_PROTOCOL *interface;
EFI_SIMPLE_NETWORK_MODE *mode;
struct net_device *netdev;
struct snp_nic *snp;
- void *interface;
EFI_STATUS efirc;
int rc;
/* Open SNP protocol */
- if ( ( efirc = bs->OpenProtocol ( device,
- &efi_simple_network_protocol_guid,
- &interface, efi_image_handle, device,
- ( EFI_OPEN_PROTOCOL_BY_DRIVER |
- EFI_OPEN_PROTOCOL_EXCLUSIVE )))!=0){
- rc = -EEFI ( efirc );
+ if ( ( rc = efi_open_by_driver ( device,
+ &efi_simple_network_protocol_guid,
+ &interface ) ) != 0 ) {
DBGC ( device, "SNP %s cannot open SNP protocol: %s\n",
efi_handle_name ( device ), strerror ( rc ) );
DBGC_EFI_OPENERS ( device, device,
@@ -583,7 +766,11 @@ int snpnet_start ( struct efi_device *efidev ) {
INIT_LIST_HEAD ( &snp->dev.children );
netdev->dev = &snp->dev;
- /* Bring to the Started state */
+ /* Check if device is insomniac */
+ if ( snpnet_is_insomniac ( device ) )
+ netdev->state |= NETDEV_INSOMNIAC;
+
+ /* Bring to the correct state for a closed interface */
if ( ( mode->State == EfiSimpleNetworkStopped ) &&
( ( efirc = snp->snp->Start ( snp->snp ) ) != 0 ) ) {
rc = -EEFI ( efirc );
@@ -592,6 +779,7 @@ int snpnet_start ( struct efi_device *efidev ) {
goto err_start;
}
if ( ( mode->State == EfiSimpleNetworkInitialized ) &&
+ ( ! netdev_insomniac ( netdev ) ) &&
( ( efirc = snp->snp->Shutdown ( snp->snp ) ) != 0 ) ) {
rc = -EEFI ( efirc );
DBGC ( device, "SNP %s could not shut down: %s\n",
@@ -644,8 +832,7 @@ int snpnet_start ( struct efi_device *efidev ) {
netdev_nullify ( netdev );
netdev_put ( netdev );
err_alloc:
- bs->CloseProtocol ( device, &efi_simple_network_protocol_guid,
- efi_image_handle, device );
+ efi_close_by_driver ( device, &efi_simple_network_protocol_guid );
err_open_protocol:
return rc;
}
@@ -656,7 +843,6 @@ int snpnet_start ( struct efi_device *efidev ) {
* @v efidev EFI device
*/
void snpnet_stop ( struct efi_device *efidev ) {
- EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
struct net_device *netdev = efidev_get_drvdata ( efidev );
struct snp_nic *snp = netdev->priv;
EFI_HANDLE device = efidev->device;
@@ -681,6 +867,5 @@ void snpnet_stop ( struct efi_device *efidev ) {
netdev_put ( netdev );
/* Close SNP protocol */
- bs->CloseProtocol ( device, &efi_simple_network_protocol_guid,
- efi_image_handle, device );
+ efi_close_by_driver ( device, &efi_simple_network_protocol_guid );
}
diff --git a/src/drivers/net/efi/snpnet.h b/src/drivers/net/efi/snpnet.h
index 4699c7892..a361a99c0 100644
--- a/src/drivers/net/efi/snpnet.h
+++ b/src/drivers/net/efi/snpnet.h
@@ -8,10 +8,13 @@
*/
FILE_LICENCE ( GPL2_OR_LATER );
+FILE_SECBOOT ( PERMITTED );
struct efi_device;
-extern int snpnet_supported ( EFI_HANDLE device, EFI_GUID *protocol );
+extern int snpnet_supported ( EFI_HANDLE device, EFI_GUID *protocol,
+ int inhibit_wifi );
+extern int snpnet_exclude ( EFI_HANDLE device );
extern int snpnet_start ( struct efi_device *efidev );
extern void snpnet_stop ( struct efi_device *efidev );
diff --git a/src/drivers/net/efi/snponly.c b/src/drivers/net/efi/snponly.c
index 2ae63fc06..b7231ce01 100644
--- a/src/drivers/net/efi/snponly.c
+++ b/src/drivers/net/efi/snponly.c
@@ -22,6 +22,7 @@
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
#include <string.h>
#include <errno.h>
@@ -63,16 +64,20 @@ struct chained_protocol {
* reinstalling the protocol instance.
*/
EFI_HANDLE device;
+ /** Assume wireless devices are unusable */
+ int inhibit_wifi;
};
/** Chainloaded SNP protocol */
static struct chained_protocol chained_snp = {
.protocol = &efi_simple_network_protocol_guid,
+ .inhibit_wifi = 1,
};
/** Chainloaded NII protocol */
static struct chained_protocol chained_nii = {
.protocol = &efi_nii31_protocol_guid,
+ .inhibit_wifi = 1,
};
/** Chainloaded MNP protocol */
@@ -86,13 +91,11 @@ static struct chained_protocol chained_mnp = {
* @v chained Chainloaded protocol
*/
static void chained_locate ( struct chained_protocol *chained ) {
- EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
EFI_HANDLE device = efi_loaded_image->DeviceHandle;
EFI_HANDLE handle;
void *match = NULL;
void *interface;
unsigned int skip;
- EFI_STATUS efirc;
int rc;
/* Identify target device handle */
@@ -111,11 +114,8 @@ static void chained_locate ( struct chained_protocol *chained ) {
}
/* Get protocol instance */
- if ( ( efirc = bs->OpenProtocol (
- handle, chained->protocol, &interface,
- efi_image_handle, handle,
- EFI_OPEN_PROTOCOL_GET_PROTOCOL )) != 0){
- rc = -EEFI ( efirc );
+ if ( ( rc = efi_open ( handle, chained->protocol,
+ &interface ) ) != 0 ) {
DBGC ( device, "CHAINED %s could not open %s on ",
efi_handle_name ( device ),
efi_guid_ntoa ( chained->protocol ) );
@@ -123,8 +123,6 @@ static void chained_locate ( struct chained_protocol *chained ) {
efi_handle_name ( handle ), strerror ( rc ) );
break;
}
- bs->CloseProtocol ( handle, chained->protocol,
- efi_image_handle, handle );
/* Stop if we reach a non-matching protocol instance */
if ( match && ( match != interface ) ) {
@@ -154,20 +152,16 @@ static void chained_locate ( struct chained_protocol *chained ) {
*/
static int chained_supported ( EFI_HANDLE device,
struct chained_protocol *chained ) {
- EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
void *interface;
- EFI_STATUS efirc;
int rc;
/* Get protocol */
- if ( ( efirc = bs->OpenProtocol ( device, chained->protocol, &interface,
- efi_image_handle, device,
- EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){
- rc = -EEFI ( efirc );
+ if ( ( rc = efi_open ( device, chained->protocol,
+ &interface ) ) != 0 ) {
DBGCP ( device, "CHAINED %s is not a %s device\n",
efi_handle_name ( device ),
efi_guid_ntoa ( chained->protocol ) );
- goto err_open_protocol;
+ return rc;
}
/* Ignore non-matching handles */
@@ -175,21 +169,23 @@ static int chained_supported ( EFI_HANDLE device,
DBGC2 ( device, "CHAINED %s is not the chainloaded %s\n",
efi_handle_name ( device ),
efi_guid_ntoa ( chained->protocol ) );
- rc = -ENOTTY;
- goto err_no_match;
+ return -ENOTTY;
}
-
- /* Success */
- rc = 0;
DBGC ( device, "CHAINED %s is the chainloaded %s\n",
efi_handle_name ( device ),
efi_guid_ntoa ( chained->protocol ) );
- err_no_match:
- bs->CloseProtocol ( device, chained->protocol, efi_image_handle,
- device );
- err_open_protocol:
- return rc;
+ /* Check for wireless devices, if applicable */
+ if ( chained->inhibit_wifi &&
+ ( ( efi_test ( device, &efi_wifi2_protocol_guid ) ) == 0 ) ) {
+ DBGC ( device, "CHAINED %s is wireless: assuming vendor %s "
+ "driver is too unreliable to use\n",
+ efi_handle_name ( device ),
+ efi_guid_ntoa ( chained->protocol ) );
+ return -ENOTTY;
+ }
+
+ return 0;
}
/**
@@ -226,23 +222,25 @@ static int mnponly_supported ( EFI_HANDLE device ) {
}
/** EFI SNP chainloading-device-only driver */
-struct efi_driver snponly_driver __efi_driver ( EFI_DRIVER_NORMAL ) = {
+struct efi_driver snponly_driver __efi_driver ( EFI_DRIVER_SNP ) = {
.name = "SNPONLY",
.supported = snponly_supported,
+ .exclude = snpnet_exclude,
.start = snpnet_start,
.stop = snpnet_stop,
};
/** EFI NII chainloading-device-only driver */
-struct efi_driver niionly_driver __efi_driver ( EFI_DRIVER_NORMAL ) = {
+struct efi_driver niionly_driver __efi_driver ( EFI_DRIVER_NII ) = {
.name = "NIIONLY",
.supported = niionly_supported,
+ .exclude = nii_exclude,
.start = nii_start,
.stop = nii_stop,
};
/** EFI MNP chainloading-device-only driver */
-struct efi_driver mnponly_driver __efi_driver ( EFI_DRIVER_NORMAL ) = {
+struct efi_driver mnponly_driver __efi_driver ( EFI_DRIVER_MNP ) = {
.name = "MNPONLY",
.supported = mnponly_supported,
.start = mnpnet_start,
@@ -262,5 +260,6 @@ static void chained_init ( void ) {
/** EFI chainloaded-device-only initialisation function */
struct init_fn chained_init_fn __init_fn ( INIT_LATE ) = {
+ .name = "chained",
.initialise = chained_init,
};