summaryrefslogtreecommitdiffstats
path: root/src/drivers/net/efi/snponly.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/drivers/net/efi/snponly.c')
-rw-r--r--src/drivers/net/efi/snponly.c136
1 files changed, 95 insertions, 41 deletions
diff --git a/src/drivers/net/efi/snponly.c b/src/drivers/net/efi/snponly.c
index 674e0a05..2ae63fc0 100644
--- a/src/drivers/net/efi/snponly.c
+++ b/src/drivers/net/efi/snponly.c
@@ -29,6 +29,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/efi/efi.h>
#include <ipxe/efi/efi_driver.h>
#include <ipxe/efi/efi_utils.h>
+#include <ipxe/efi/mnpnet.h>
#include <ipxe/efi/Protocol/SimpleNetwork.h>
#include <ipxe/efi/Protocol/NetworkInterfaceIdentifier.h>
#include "snpnet.h"
@@ -45,14 +46,23 @@ struct chained_protocol {
/** Protocol GUID */
EFI_GUID *protocol;
/**
- * Protocol instance installed on the loaded image's device handle
+ * Target device handle
+ *
+ * This is the uppermost handle on which the same protocol
+ * instance is installed as we find on the loaded image's
+ * device handle.
*
* We match against the protocol instance (rather than simply
* matching against the device handle itself) because some
* systems load us via a child of the underlying device, with
* a duplicate protocol installed on the child handle.
+ *
+ * We record the handle rather than the protocol instance
+ * pointer since the calls to DisconnectController() and
+ * ConnectController() may end up uninstalling and
+ * reinstalling the protocol instance.
*/
- void *interface;
+ EFI_HANDLE device;
};
/** Chainloaded SNP protocol */
@@ -65,50 +75,74 @@ static struct chained_protocol chained_nii = {
.protocol = &efi_nii31_protocol_guid,
};
+/** Chainloaded MNP protocol */
+static struct chained_protocol chained_mnp = {
+ .protocol = &efi_managed_network_service_binding_protocol_guid,
+};
+
/**
- * Locate chainloaded protocol instance
+ * Locate chainloaded protocol
*
* @v chained Chainloaded protocol
- * @ret rc Return status code
*/
-static int chained_locate ( struct chained_protocol *chained ) {
+static void chained_locate ( struct chained_protocol *chained ) {
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
EFI_HANDLE device = efi_loaded_image->DeviceHandle;
- EFI_HANDLE parent;
+ EFI_HANDLE handle;
+ void *match = NULL;
+ void *interface;
+ unsigned int skip;
EFI_STATUS efirc;
int rc;
- /* Locate handle supporting this protocol */
- if ( ( rc = efi_locate_device ( device, chained->protocol,
- &parent, 0 ) ) != 0 ) {
- DBGC ( device, "CHAINED %s does not support %s: %s\n",
- efi_handle_name ( device ),
- efi_guid_ntoa ( chained->protocol ), strerror ( rc ) );
- goto err_locate_device;
- }
- DBGC ( device, "CHAINED %s found %s on ", efi_handle_name ( device ),
- efi_guid_ntoa ( chained->protocol ) );
- DBGC ( device, "%s\n", efi_handle_name ( parent ) );
+ /* Identify target device handle */
+ for ( skip = 0 ; ; skip++ ) {
- /* Get protocol instance */
- if ( ( efirc = bs->OpenProtocol ( parent, chained->protocol,
- &chained->interface, efi_image_handle,
- device,
- EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){
- rc = -EEFI ( efirc );
- DBGC ( device, "CHAINED %s could not open %s on ",
+ /* Locate handle supporting this protocol */
+ if ( ( rc = efi_locate_device ( device, chained->protocol,
+ &handle, skip ) ) != 0 ) {
+ if ( skip == 0 ) {
+ DBGC ( device, "CHAINED %s does not support "
+ "%s: %s\n", efi_handle_name ( device ),
+ efi_guid_ntoa ( chained->protocol ),
+ strerror ( rc ) );
+ }
+ break;
+ }
+
+ /* Get protocol instance */
+ if ( ( efirc = bs->OpenProtocol (
+ handle, chained->protocol, &interface,
+ efi_image_handle, handle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL )) != 0){
+ rc = -EEFI ( efirc );
+ DBGC ( device, "CHAINED %s could not open %s on ",
+ efi_handle_name ( device ),
+ efi_guid_ntoa ( chained->protocol ) );
+ DBGC ( device, "%s: %s\n",
+ 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 ) ) {
+ DBGC ( device, "CHAINED %s found non-matching %s on ",
+ efi_handle_name ( device ),
+ efi_guid_ntoa ( chained->protocol ) );
+ DBGC ( device, "%s\n", efi_handle_name ( handle ) );
+ break;
+ }
+
+ /* Record this handle */
+ chained->device = handle;
+ match = interface;
+ DBGC ( device, "CHAINED %s found %s on ",
efi_handle_name ( device ),
efi_guid_ntoa ( chained->protocol ) );
- DBGC ( device, "%s: %s\n",
- efi_handle_name ( parent ), strerror ( rc ) );
- goto err_open_protocol;
+ DBGC ( device, "%s\n", efi_handle_name ( chained->device ) );
}
-
- err_locate_device:
- bs->CloseProtocol ( parent, chained->protocol, efi_image_handle,
- device );
- err_open_protocol:
- return rc;
}
/**
@@ -121,8 +155,8 @@ static int chained_locate ( struct chained_protocol *chained ) {
static int chained_supported ( EFI_HANDLE device,
struct chained_protocol *chained ) {
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
- EFI_STATUS efirc;
void *interface;
+ EFI_STATUS efirc;
int rc;
/* Get protocol */
@@ -136,19 +170,19 @@ static int chained_supported ( EFI_HANDLE device,
goto err_open_protocol;
}
- /* Test for a match against the chainloading device */
- if ( interface != chained->interface ) {
- DBGC ( device, "CHAINED %s %p is not the chainloaded %s\n",
- efi_handle_name ( device ), interface,
- efi_guid_ntoa ( chained->protocol ) );
+ /* Ignore non-matching handles */
+ if ( device != chained->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;
}
/* Success */
rc = 0;
- DBGC ( device, "CHAINED %s %p is the chainloaded %s\n",
- efi_handle_name ( device ), interface,
+ DBGC ( device, "CHAINED %s is the chainloaded %s\n",
+ efi_handle_name ( device ),
efi_guid_ntoa ( chained->protocol ) );
err_no_match:
@@ -180,6 +214,17 @@ static int niionly_supported ( EFI_HANDLE device ) {
return chained_supported ( device, &chained_nii );
}
+/**
+ * Check to see if driver supports a device
+ *
+ * @v device EFI device handle
+ * @ret rc Return status code
+ */
+static int mnponly_supported ( EFI_HANDLE device ) {
+
+ return chained_supported ( device, &chained_mnp );
+}
+
/** EFI SNP chainloading-device-only driver */
struct efi_driver snponly_driver __efi_driver ( EFI_DRIVER_NORMAL ) = {
.name = "SNPONLY",
@@ -196,6 +241,14 @@ struct efi_driver niionly_driver __efi_driver ( EFI_DRIVER_NORMAL ) = {
.stop = nii_stop,
};
+/** EFI MNP chainloading-device-only driver */
+struct efi_driver mnponly_driver __efi_driver ( EFI_DRIVER_NORMAL ) = {
+ .name = "MNPONLY",
+ .supported = mnponly_supported,
+ .start = mnpnet_start,
+ .stop = mnpnet_stop,
+};
+
/**
* Initialise EFI chainloaded-device-only driver
*
@@ -204,6 +257,7 @@ static void chained_init ( void ) {
chained_locate ( &chained_snp );
chained_locate ( &chained_nii );
+ chained_locate ( &chained_mnp );
}
/** EFI chainloaded-device-only initialisation function */