diff options
author | Michael Brown <mcb30@ipxe.org> | 2014-08-05 20:49:42 +0100 |
---|---|---|
committer | Michael Brown <mcb30@ipxe.org> | 2014-08-05 23:10:33 +0100 |
commit | 7b3cc18462425ab816348103f1bfa2546d248d37 (patch) | |
tree | ed3c38e2d663c51e74d81f7638b2ead8afd4aaaf /src/interface/efi/efi_snp.c | |
parent | 3b42ed477f21ee3f5851dc2993ea670e9edc10f7 (diff) | |
download | ipxe-7b3cc18462425ab816348103f1bfa2546d248d37.tar.gz |
[efi] Open device path protocol only at point of use
Some EFI 1.10 systems (observed on an Apple iMac) do not allow us to
open the device path protocol with an attribute of
EFI_OPEN_PROTOCOL_BY_DRIVER and so we cannot maintain a safe,
long-lived pointer to the device path. Work around this by instead
opening the device path protocol with an attribute of
EFI_OPEN_PROTOCOL_GET_PROTOCOL whenever we need to use it.
Debugged-by: Curtis Larsen <larsen@dixie.edu>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/interface/efi/efi_snp.c')
-rw-r--r-- | src/interface/efi/efi_snp.c | 55 |
1 files changed, 43 insertions, 12 deletions
diff --git a/src/interface/efi/efi_snp.c b/src/interface/efi/efi_snp.c index b5eeab212..2eec29cab 100644 --- a/src/interface/efi/efi_snp.c +++ b/src/interface/efi/efi_snp.c @@ -909,6 +909,10 @@ static int efi_snp_probe ( struct net_device *netdev ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_device *efidev; struct efi_snp_device *snpdev; + union { + EFI_DEVICE_PATH_PROTOCOL *path; + void *interface; + } path; EFI_DEVICE_PATH_PROTOCOL *path_end; MAC_ADDR_DEVICE_PATH *macpath; size_t path_prefix_len = 0; @@ -923,14 +927,8 @@ static int efi_snp_probe ( struct net_device *netdev ) { goto err_no_efidev; } - /* Calculate device path prefix length */ - path_end = efi_devpath_end ( efidev->path ); - path_prefix_len = ( ( ( void * ) path_end ) - - ( ( void * ) efidev->path ) ); - /* Allocate the SNP device */ - snpdev = zalloc ( sizeof ( *snpdev ) + path_prefix_len + - sizeof ( *macpath ) ); + snpdev = zalloc ( sizeof ( *snpdev ) ); if ( ! snpdev ) { rc = -ENOMEM; goto err_alloc_snp; @@ -993,9 +991,32 @@ static int efi_snp_probe ( struct net_device *netdev ) { sizeof ( snpdev->name[0] ) ), "%s", netdev->name ); + /* Get the parent device path */ + if ( ( efirc = bs->OpenProtocol ( efidev->device, + &efi_device_path_protocol_guid, + &path.interface, efi_image_handle, + efidev->device, + EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){ + rc = -EEFI ( efirc ); + DBGC ( snpdev, "SNPDEV %p cannot get %p %s device path: %s\n", + snpdev, efidev->device, + efi_handle_name ( efidev->device ), strerror ( rc ) ); + goto err_open_device_path; + } + + /* Allocate the new device path */ + path_end = efi_devpath_end ( path.path ); + path_prefix_len = ( ( ( void * ) path_end ) - ( ( void * ) path.path )); + snpdev->path = zalloc ( path_prefix_len + sizeof ( *macpath ) + + sizeof ( *path_end ) ); + if ( ! snpdev->path ) { + rc = -ENOMEM; + goto err_alloc_device_path; + } + /* Populate the device path */ - memcpy ( &snpdev->path, efidev->path, path_prefix_len ); - macpath = ( ( ( void * ) &snpdev->path ) + path_prefix_len ); + memcpy ( snpdev->path, path.path, path_prefix_len ); + macpath = ( ( ( void * ) snpdev->path ) + path_prefix_len ); path_end = ( ( void * ) ( macpath + 1 ) ); memset ( macpath, 0, sizeof ( *macpath ) ); macpath->Header.Type = MESSAGING_DEVICE_PATH; @@ -1013,7 +1034,7 @@ static int efi_snp_probe ( struct net_device *netdev ) { if ( ( efirc = bs->InstallMultipleProtocolInterfaces ( &snpdev->handle, &efi_simple_network_protocol_guid, &snpdev->snp, - &efi_device_path_protocol_guid, &snpdev->path, + &efi_device_path_protocol_guid, snpdev->path, &efi_nii_protocol_guid, &snpdev->nii, &efi_nii31_protocol_guid, &snpdev->nii, &efi_component_name2_protocol_guid, &snpdev->name2, @@ -1046,6 +1067,10 @@ static int efi_snp_probe ( struct net_device *netdev ) { /* Add to list of SNP devices */ list_add ( &snpdev->list, &efi_snp_devices ); + /* Close device path */ + bs->CloseProtocol ( efidev->device, &efi_device_path_protocol_guid, + efi_image_handle, efidev->device ); + DBGC ( snpdev, "SNPDEV %p installed for %s as device %p %s\n", snpdev, netdev->name, snpdev->handle, efi_handle_name ( snpdev->handle ) ); @@ -1058,13 +1083,18 @@ static int efi_snp_probe ( struct net_device *netdev ) { bs->UninstallMultipleProtocolInterfaces ( snpdev->handle, &efi_simple_network_protocol_guid, &snpdev->snp, - &efi_device_path_protocol_guid, &snpdev->path, + &efi_device_path_protocol_guid, snpdev->path, &efi_nii_protocol_guid, &snpdev->nii, &efi_nii31_protocol_guid, &snpdev->nii, &efi_component_name2_protocol_guid, &snpdev->name2, &efi_load_file_protocol_guid, &snpdev->load_file, NULL ); err_install_protocol_interface: + free ( snpdev->path ); + err_alloc_device_path: + bs->CloseProtocol ( efidev->device, &efi_device_path_protocol_guid, + efi_image_handle, efidev->device ); + err_open_device_path: bs->CloseEvent ( snpdev->snp.WaitForPacket ); err_create_event: err_ll_addr_len: @@ -1124,12 +1154,13 @@ static void efi_snp_remove ( struct net_device *netdev ) { bs->UninstallMultipleProtocolInterfaces ( snpdev->handle, &efi_simple_network_protocol_guid, &snpdev->snp, - &efi_device_path_protocol_guid, &snpdev->path, + &efi_device_path_protocol_guid, snpdev->path, &efi_nii_protocol_guid, &snpdev->nii, &efi_nii31_protocol_guid, &snpdev->nii, &efi_component_name2_protocol_guid, &snpdev->name2, &efi_load_file_protocol_guid, &snpdev->load_file, NULL ); + free ( snpdev->path ); bs->CloseEvent ( snpdev->snp.WaitForPacket ); netdev_put ( snpdev->netdev ); free ( snpdev ); |