Skip to content

Commit

Permalink
[efi] Use the SNP protocol instance to match the SNP chainloading device
Browse files Browse the repository at this point in the history
Some systems will install a child of the SNP device and use this as
our loaded image's device handle, duplicating the installation of the
underlying SNP protocol onto the child device handle.  On such
systems, we want to end up driving the parent device (and
disconnecting any other drivers, such as MNP, which may be attached to
the parent device).

Fix by recording the SNP protocol instance at initialisation time, and
using this to match against device handles (rather than simply
comparing the handles themselves).

Reported-by: Jarrod Johnson <jarrod.b.johnson@gmail.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
  • Loading branch information
mcb30 committed Sep 4, 2014
1 parent 0cc2f42 commit 4c5b794
Showing 1 changed file with 89 additions and 8 deletions.
97 changes: 89 additions & 8 deletions src/drivers/net/efi/snponly.c
Expand Up @@ -20,38 +20,119 @@
FILE_LICENCE ( GPL2_OR_LATER );

#include <errno.h>
#include <ipxe/init.h>
#include <ipxe/efi/efi.h>
#include <ipxe/efi/efi_driver.h>
#include <ipxe/efi/Protocol/SimpleNetwork.h>
#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
*
* @v device EFI device handle
* @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,
};

0 comments on commit 4c5b794

Please sign in to comment.