diff --git a/src/drivers/net/efi/snponly.c b/src/drivers/net/efi/snponly.c index d8dc86ad2..d454ed20b 100644 --- a/src/drivers/net/efi/snponly.c +++ b/src/drivers/net/efi/snponly.c @@ -20,16 +20,28 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include +#include #include #include +#include #include "snpnet.h" /** @file * - * SNP chainloaded-device-only driver + * SNP chainloading-device-only driver * */ +/** + * SNP protocol instance installed on the loaded image's device handle + * + * We match against the SNP protocol instance (rather than simply + * matching against the device handle itself) because some systems + * load us via a child of the SNP device, with a duplicate SNP + * protocol installed on the child handle. + */ +static EFI_SIMPLE_NETWORK_PROTOCOL *snponly; + /** * Check to see if driver supports a device * @@ -37,21 +49,90 @@ FILE_LICENCE ( GPL2_OR_LATER ); * @ret rc Return status code */ static int snponly_supported ( EFI_HANDLE device ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_STATUS efirc; + union { + EFI_SIMPLE_NETWORK_PROTOCOL *snp; + void *interface; + } snp; + int rc; - /* Check that this device is our loaded image's device */ - if ( device != efi_loaded_image->DeviceHandle ) - return -ENOTTY; + /* Get SNP protocol */ + if ( ( efirc = bs->OpenProtocol ( device, + &efi_simple_network_protocol_guid, + &snp.interface, efi_image_handle, + device, + EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){ + rc = -EEFI ( efirc ); + DBGCP ( device, "SNPONLY %p %s is not an SNP device\n", + device, efi_handle_name ( device ) ); + goto err_not_snp; + } - DBGC ( device, "SNP %p %s is the SNP chainloading device\n", - device, efi_handle_name ( device ) ); + /* Test for a match against the chainloading device */ + if ( snp.snp != snponly ) { + DBGC ( device, "SNPONLY %p %s SNP %p is not the " + "chainloading SNP\n", device, + efi_handle_name ( device ), snp.snp ); + rc = -ENOTTY; + goto err_not_snponly; + } - return 0; + /* Success */ + rc = 0; + DBGC ( device, "SNPONLY %p %s SNP %p is the chainloading SNP\n", + device, efi_handle_name ( device ), snp.snp ); + + err_not_snponly: + bs->CloseProtocol ( device, &efi_simple_network_protocol_guid, + efi_image_handle, device ); + err_not_snp: + return rc; } -/** EFI SNP driver */ +/** EFI chainloading-device-only driver */ struct efi_driver snponly_driver __efi_driver ( EFI_DRIVER_NORMAL ) = { .name = "SNPONLY", .supported = snponly_supported, .start = snpnet_start, .stop = snpnet_stop, }; + +/** + * Initialise EFI chainloading-device-only driver + * + */ +static void snponly_init ( void ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_HANDLE device = efi_loaded_image->DeviceHandle; + union { + EFI_SIMPLE_NETWORK_PROTOCOL *snp; + void *interface; + } snp; + EFI_STATUS efirc; + + /* Look for SNP protocol on the loaded image's device handle */ + if ( ( efirc = bs->OpenProtocol ( device, + &efi_simple_network_protocol_guid, + &snp.interface, efi_image_handle, + device, + EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){ + DBGC ( device, "SNPONLY %p %s is not an SNP device\n", + device, efi_handle_name ( device ) ); + goto err_open_protocol; + } + + /* Record SNP protocol instance */ + snponly = snp.snp; + DBGC ( device, "SNPONLY %p %s found chainloading SNP %p\n", + device, efi_handle_name ( device ), snponly ); + + err_open_protocol: + bs->CloseProtocol ( device, &efi_simple_network_protocol_guid, + efi_image_handle, device ); +} + +/** EFI chainloading-device-only initialisation function */ +struct init_fn snponly_init_fn __init_fn ( INIT_LATE ) = { + .initialise = snponly_init, +};