diff options
| author | Simon Rettberg | 2021-03-08 15:55:33 +0100 |
|---|---|---|
| committer | Simon Rettberg | 2021-03-08 15:55:33 +0100 |
| commit | 6707d9218c8e6e760e53068d5f41ceb31fac6ea0 (patch) | |
| tree | 88e8fc84ede2a0ed2c1cec3a6109beb9fb53abf5 /src/interface/efi | |
| parent | Merge branch 'master' into openslx (diff) | |
| parent | [linux] Do not assume that stat() works on sysfs files (diff) | |
| download | ipxe-6707d9218c8e6e760e53068d5f41ceb31fac6ea0.tar.gz ipxe-6707d9218c8e6e760e53068d5f41ceb31fac6ea0.tar.xz ipxe-6707d9218c8e6e760e53068d5f41ceb31fac6ea0.zip | |
Merge branch 'master' into openslx
Diffstat (limited to 'src/interface/efi')
27 files changed, 3914 insertions, 758 deletions
diff --git a/src/interface/efi/efi_acpi.c b/src/interface/efi/efi_acpi.c index a347eaf3a..07a225632 100644 --- a/src/interface/efi/efi_acpi.c +++ b/src/interface/efi/efi_acpi.c @@ -54,3 +54,4 @@ static userptr_t efi_find_rsdt ( void ) { } PROVIDE_ACPI ( efi, acpi_find_rsdt, efi_find_rsdt ); +PROVIDE_ACPI_INLINE ( efi, acpi_find ); diff --git a/src/interface/efi/efi_autoboot.c b/src/interface/efi/efi_autoboot.c index a9e807e23..08d67f761 100644 --- a/src/interface/efi/efi_autoboot.c +++ b/src/interface/efi/efi_autoboot.c @@ -23,6 +23,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +#include <string.h> +#include <errno.h> #include <ipxe/efi/efi.h> #include <ipxe/efi/efi_autoboot.h> #include <ipxe/efi/Protocol/SimpleNetwork.h> @@ -37,8 +39,10 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * Identify autoboot device * + * @v device Device handle + * @ret rc Return status code */ -void efi_set_autoboot ( void ) { +int efi_set_autoboot_ll_addr ( EFI_HANDLE device ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; union { EFI_SIMPLE_NETWORK_PROTOCOL *snp; @@ -46,26 +50,30 @@ void efi_set_autoboot ( void ) { } snp; EFI_SIMPLE_NETWORK_MODE *mode; EFI_STATUS efirc; + int rc; /* Look for an SNP instance on the image's device handle */ - if ( ( efirc = bs->OpenProtocol ( efi_loaded_image->DeviceHandle, + if ( ( efirc = bs->OpenProtocol ( device, &efi_simple_network_protocol_guid, &snp.interface, efi_image_handle, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){ - DBGC ( efi_loaded_image, "EFI found no autoboot device\n" ); - return; + rc = -EEFI ( efirc ); + DBGC ( device, "EFI %s has no SNP instance: %s\n", + efi_handle_name ( device ), strerror ( rc ) ); + return rc; } /* Record autoboot device */ mode = snp.snp->Mode; set_autoboot_ll_addr ( &mode->CurrentAddress, mode->HwAddressSize ); - DBGC ( efi_loaded_image, "EFI found autoboot link-layer address:\n" ); - DBGC_HDA ( efi_loaded_image, 0, &mode->CurrentAddress, - mode->HwAddressSize ); + DBGC ( device, "EFI %s found autoboot link-layer address:\n", + efi_handle_name ( device ) ); + DBGC_HDA ( device, 0, &mode->CurrentAddress, mode->HwAddressSize ); /* Close protocol */ - bs->CloseProtocol ( efi_loaded_image->DeviceHandle, - &efi_simple_network_protocol_guid, + bs->CloseProtocol ( device, &efi_simple_network_protocol_guid, efi_image_handle, NULL ); + + return 0; } diff --git a/src/interface/efi/efi_autoexec.c b/src/interface/efi/efi_autoexec.c new file mode 100644 index 000000000..88eb379bb --- /dev/null +++ b/src/interface/efi/efi_autoexec.c @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2021 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <string.h> +#include <errno.h> +#include <ipxe/image.h> +#include <ipxe/init.h> +#include <ipxe/efi/efi.h> +#include <ipxe/efi/efi_autoexec.h> +#include <ipxe/efi/Protocol/SimpleFileSystem.h> +#include <ipxe/efi/Guid/FileInfo.h> + +/** @file + * + * EFI autoexec script + * + */ + +/** Autoexec script filename */ +#define AUTOEXEC_FILENAME L"autoexec.ipxe" + +/** Autoexec script image name */ +#define AUTOEXEC_NAME "autoexec.ipxe" + +/** Autoexec script (if any) */ +static void *efi_autoexec; + +/** Autoexec script length */ +static size_t efi_autoexec_len; + +/** + * Load autoexec script + * + * @v device Device handle + * @ret rc Return status code + */ +int efi_autoexec_load ( EFI_HANDLE device ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + static wchar_t name[] = AUTOEXEC_FILENAME; + union { + void *interface; + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *fs; + } u; + struct { + EFI_FILE_INFO info; + CHAR16 name[ sizeof ( name ) / sizeof ( name[0] ) ]; + } info; + EFI_FILE_PROTOCOL *root; + EFI_FILE_PROTOCOL *file; + UINTN size; + VOID *data; + EFI_STATUS efirc; + int rc; + + /* Sanity check */ + assert ( efi_autoexec == NULL ); + assert ( efi_autoexec_len == 0 ); + + /* Open simple file system protocol */ + if ( ( efirc = bs->OpenProtocol ( device, + &efi_simple_file_system_protocol_guid, + &u.interface, efi_image_handle, + device, + EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){ + rc = -EEFI ( efirc ); + DBGC ( device, "EFI %s has no filesystem instance: %s\n", + efi_handle_name ( device ), strerror ( rc ) ); + goto err_filesystem; + } + + /* Open root directory */ + if ( ( efirc = u.fs->OpenVolume ( u.fs, &root ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( device, "EFI %s could not open volume: %s\n", + efi_handle_name ( device ), strerror ( rc ) ); + goto err_volume; + } + + /* Open autoexec script */ + if ( ( efirc = root->Open ( root, &file, name, + EFI_FILE_MODE_READ, 0 ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( device, "EFI %s has no %ls: %s\n", + efi_handle_name ( device ), name, strerror ( rc ) ); + goto err_open; + } + + /* Get file information */ + size = sizeof ( info ); + if ( ( efirc = file->GetInfo ( file, &efi_file_info_id, &size, + &info ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( device, "EFI %s could not get %ls info: %s\n", + efi_handle_name ( device ), name, strerror ( rc ) ); + goto err_getinfo; + } + size = info.info.FileSize; + + /* Ignore zero-length files */ + if ( ! size ) { + rc = -EINVAL; + DBGC ( device, "EFI %s has zero-length %ls\n", + efi_handle_name ( device ), name ); + goto err_empty; + } + + /* Allocate temporary copy */ + if ( ( efirc = bs->AllocatePool ( EfiBootServicesData, size, + &data ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( device, "EFI %s could not allocate %ls: %s\n", + efi_handle_name ( device ), name, strerror ( rc ) ); + goto err_alloc; + } + + /* Read file */ + if ( ( efirc = file->Read ( file, &size, data ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( device, "EFI %s could not read %ls: %s\n", + efi_handle_name ( device ), name, strerror ( rc ) ); + goto err_read; + } + + /* Record autoexec script */ + efi_autoexec = data; + efi_autoexec_len = size; + data = NULL; + DBGC ( device, "EFI %s found %ls\n", + efi_handle_name ( device ), name ); + + /* Success */ + rc = 0; + + err_read: + if ( data ) + bs->FreePool ( data ); + err_alloc: + err_empty: + err_getinfo: + file->Close ( file ); + err_open: + root->Close ( root ); + err_volume: + bs->CloseProtocol ( device, &efi_simple_file_system_protocol_guid, + efi_image_handle, device ); + err_filesystem: + return rc; +} + +/** + * Register autoexec script + * + */ +static void efi_autoexec_startup ( void ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_HANDLE device = efi_loaded_image->DeviceHandle; + const char *name = AUTOEXEC_NAME; + struct image *image; + + /* Do nothing if we have no autoexec script */ + if ( ! efi_autoexec ) + return; + + /* Create autoexec image */ + image = image_memory ( name, virt_to_user ( efi_autoexec ), + efi_autoexec_len ); + if ( ! image ) { + DBGC ( device, "EFI %s could not create %s\n", + efi_handle_name ( device ), name ); + return; + } + DBGC ( device, "EFI %s registered %s\n", + efi_handle_name ( device ), name ); + + /* Free temporary copy */ + bs->FreePool ( efi_autoexec ); + efi_autoexec = NULL; +} + +/** Autoexec script startup function */ +struct startup_fn efi_autoexec_startup_fn __startup_fn ( STARTUP_NORMAL ) = { + .name = "efi_autoexec", + .startup = efi_autoexec_startup, +}; diff --git a/src/interface/efi/efi_blacklist.c b/src/interface/efi/efi_blacklist.c deleted file mode 100644 index 292b28e8c..000000000 --- a/src/interface/efi/efi_blacklist.c +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Copyright (C) 2019 Michael Brown <mbrown@fensystems.co.uk>. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - */ - -FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); - -#include <stddef.h> -#include <string.h> -#include <errno.h> -#include <ipxe/settings.h> -#include <ipxe/efi/efi.h> -#include <ipxe/efi/Protocol/DriverBinding.h> -#include <ipxe/efi/Protocol/LoadedImage.h> -#include <ipxe/efi/Protocol/ComponentName.h> -#include <ipxe/efi/efi_blacklist.h> - -/** @file - * - * EFI driver blacklist - * - */ - -/** A blacklisted driver */ -struct efi_blacklist { - /** Name */ - const char *name; - /** - * Check if driver is blacklisted - * - * @v binding Driver binding protocol - * @v loaded Loaded image protocol - * @v wtf Component name protocol, if present - * @ret blacklisted Driver is the blacklisted driver - */ - int ( * blacklist ) ( EFI_DRIVER_BINDING_PROTOCOL *binding, - EFI_LOADED_IMAGE_PROTOCOL *loaded, - EFI_COMPONENT_NAME_PROTOCOL *wtf ); -}; - -/** - * Blacklist Dell Ip4ConfigDxe driver - * - * @v binding Driver binding protocol - * @v loaded Loaded image protocol - * @v wtf Component name protocol, if present - * @ret blacklisted Driver is the blacklisted driver - */ -static int -efi_blacklist_dell_ip4config ( EFI_DRIVER_BINDING_PROTOCOL *binding __unused, - EFI_LOADED_IMAGE_PROTOCOL *loaded __unused, - EFI_COMPONENT_NAME_PROTOCOL *wtf ) { - static const CHAR16 ip4cfg[] = L"IP4 CONFIG Network Service Driver"; - static const char dell[] = "Dell Inc."; - char manufacturer[ sizeof ( dell ) ]; - CHAR16 *name; - - /* Check driver name */ - if ( ! wtf ) - return 0; - if ( wtf->GetDriverName ( wtf, "eng", &name ) != 0 ) - return 0; - if ( memcmp ( name, ip4cfg, sizeof ( ip4cfg ) ) != 0 ) - return 0; - - /* Check manufacturer */ - fetch_string_setting ( NULL, &manufacturer_setting, manufacturer, - sizeof ( manufacturer ) ); - if ( strcmp ( manufacturer, dell ) != 0 ) - return 0; - - return 1; -} - -/** Blacklisted drivers */ -static struct efi_blacklist efi_blacklists[] = { - { - .name = "Dell Ip4Config", - .blacklist = efi_blacklist_dell_ip4config, - }, -}; - -/** - * Find driver blacklisting, if any - * - * @v driver Driver binding handle - * @ret blacklist Driver blacklisting, or NULL - * @ret rc Return status code - */ -static int efi_blacklist ( EFI_HANDLE driver, - struct efi_blacklist **blacklist ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - union { - EFI_DRIVER_BINDING_PROTOCOL *binding; - void *interface; - } binding; - union { - EFI_LOADED_IMAGE_PROTOCOL *loaded; - void *interface; - } loaded; - union { - EFI_COMPONENT_NAME_PROTOCOL *wtf; - void *interface; - } wtf; - unsigned int i; - EFI_HANDLE image; - EFI_STATUS efirc; - int rc; - - DBGC2 ( &efi_blacklists, "EFIBL checking %s\n", - efi_handle_name ( driver ) ); - - /* Mark as not blacklisted */ - *blacklist = NULL; - - /* Open driver binding protocol */ - if ( ( efirc = bs->OpenProtocol ( - driver, &efi_driver_binding_protocol_guid, - &binding.interface, efi_image_handle, driver, - EFI_OPEN_PROTOCOL_GET_PROTOCOL ) ) != 0 ) { - rc = -EEFI ( efirc ); - DBGC ( driver, "EFIBL %s could not open driver binding " - "protocol: %s\n", efi_handle_name ( driver ), - strerror ( rc ) ); - goto err_binding; - } - image = binding.binding->ImageHandle; - - /* Open loaded image protocol */ - if ( ( efirc = bs->OpenProtocol ( - image, &efi_loaded_image_protocol_guid, - &loaded.interface, efi_image_handle, image, - EFI_OPEN_PROTOCOL_GET_PROTOCOL ) ) != 0 ) { - rc = -EEFI ( efirc ); - DBGC ( driver, "EFIBL %s could not open", - efi_handle_name ( driver ) ); - DBGC ( driver, " %s loaded image protocol: %s\n", - efi_handle_name ( image ), strerror ( rc ) ); - goto err_loaded; - } - - /* Open component name protocol, if present*/ - if ( ( efirc = bs->OpenProtocol ( - driver, &efi_component_name_protocol_guid, - &wtf.interface, efi_image_handle, driver, - EFI_OPEN_PROTOCOL_GET_PROTOCOL ) ) != 0 ) { - /* Ignore failure; is not required to be present */ - wtf.interface = NULL; - } - - /* Check blacklistings */ - for ( i = 0 ; i < ( sizeof ( efi_blacklists ) / - sizeof ( efi_blacklists[0] ) ) ; i++ ) { - if ( efi_blacklists[i].blacklist ( binding.binding, - loaded.loaded, wtf.wtf ) ) { - *blacklist = &efi_blacklists[i]; - break; - } - } - - /* Success */ - rc = 0; - - /* Close protocols */ - if ( wtf.wtf ) { - bs->CloseProtocol ( driver, &efi_component_name_protocol_guid, - efi_image_handle, driver ); - } - bs->CloseProtocol ( image, &efi_loaded_image_protocol_guid, - efi_image_handle, image ); - err_loaded: - bs->CloseProtocol ( driver, &efi_driver_binding_protocol_guid, - efi_image_handle, driver ); - err_binding: - return rc; -} - -/** - * Unload any blacklisted drivers - * - */ -void efi_unload_blacklist ( void ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - struct efi_blacklist *blacklist; - EFI_HANDLE *drivers; - EFI_HANDLE driver; - UINTN num_drivers; - unsigned int i; - EFI_STATUS efirc; - int rc; - - /* Locate all driver binding protocol handles */ - if ( ( efirc = bs->LocateHandleBuffer ( - ByProtocol, &efi_driver_binding_protocol_guid, - NULL, &num_drivers, &drivers ) ) != 0 ) { - rc = -EEFI ( efirc ); - DBGC ( &efi_blacklists, "EFIBL could not list all drivers: " - "%s\n", strerror ( rc ) ); - return; - } - - /* Unload any blacklisted drivers */ - for ( i = 0 ; i < num_drivers ; i++ ) { - driver = drivers[i]; - if ( ( rc = efi_blacklist ( driver, &blacklist ) ) != 0 ) { - DBGC ( driver, "EFIBL could not determine " - "blacklisting for %s: %s\n", - efi_handle_name ( driver ), strerror ( rc ) ); - continue; - } - if ( ! blacklist ) - continue; - DBGC ( driver, "EFIBL unloading %s (%s)\n", - efi_handle_name ( driver ), blacklist->name ); - if ( ( efirc = bs->UnloadImage ( driver ) ) != 0 ) { - DBGC ( driver, "EFIBL could not unload %s: %s\n", - efi_handle_name ( driver ), strerror ( rc ) ); - } - } - - /* Free handle list */ - bs->FreePool ( drivers ); -} diff --git a/src/interface/efi/efi_block.c b/src/interface/efi/efi_block.c index 64d1e1980..74cf7c0c0 100644 --- a/src/interface/efi/efi_block.c +++ b/src/interface/efi/efi_block.c @@ -54,7 +54,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/efi/efi_driver.h> #include <ipxe/efi/efi_strings.h> #include <ipxe/efi/efi_snp.h> -#include <ipxe/efi/efi_utils.h> +#include <ipxe/efi/efi_path.h> +#include <ipxe/efi/efi_null.h> #include <ipxe/efi/efi_block.h> /** ACPI table protocol protocol */ @@ -64,23 +65,6 @@ EFI_REQUEST_PROTOCOL ( EFI_ACPI_TABLE_PROTOCOL, &acpi ); /** Boot filename */ static wchar_t efi_block_boot_filename[] = EFI_REMOVABLE_MEDIA_FILE_NAME; -/** iPXE EFI block device vendor device path GUID */ -#define IPXE_BLOCK_DEVICE_PATH_GUID \ - { 0x8998b594, 0xf531, 0x4e87, \ - { 0x8b, 0xdf, 0x8f, 0x88, 0x54, 0x3e, 0x99, 0xd4 } } - -/** iPXE EFI block device vendor device path GUID */ -static EFI_GUID ipxe_block_device_path_guid - = IPXE_BLOCK_DEVICE_PATH_GUID; - -/** An iPXE EFI block device vendor device path */ -struct efi_block_vendor_path { - /** Generic vendor device path */ - VENDOR_DEVICE_PATH vendor; - /** Block device URI */ - CHAR16 uri[0]; -} __attribute__ (( packed )); - /** EFI SAN device private data */ struct efi_block_data { /** SAN device */ @@ -237,7 +221,7 @@ static void efi_block_connect ( struct san_device *sandev ) { /* Try to connect all possible drivers to this block device */ if ( ( efirc = bs->ConnectController ( block->handle, NULL, - NULL, 1 ) ) != 0 ) { + NULL, TRUE ) ) != 0 ) { rc = -EEFI ( efirc ); DBGC ( sandev, "EFIBLK %#02x could not connect drivers: %s\n", sandev->drive, strerror ( rc ) ); @@ -259,16 +243,9 @@ static void efi_block_connect ( struct san_device *sandev ) { static int efi_block_hook ( unsigned int drive, struct uri **uris, unsigned int count, unsigned int flags ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - EFI_DEVICE_PATH_PROTOCOL *end; - struct efi_block_vendor_path *vendor; - struct efi_snp_device *snpdev; struct san_device *sandev; struct efi_block_data *block; - size_t prefix_len; - size_t uri_len; - size_t vendor_len; - size_t len; - char *uri_buf; + int leak = 0; EFI_STATUS efirc; int rc; @@ -279,24 +256,8 @@ static int efi_block_hook ( unsigned int drive, struct uri **uris, goto err_no_uris; } - /* Find an appropriate parent device handle */ - snpdev = last_opened_snpdev(); - if ( ! snpdev ) { - DBG ( "EFIBLK could not identify SNP device\n" ); - rc = -ENODEV; - goto err_no_snpdev; - } - - /* Calculate length of private data */ - prefix_len = efi_devpath_len ( snpdev->path ); - uri_len = format_uri ( uris[0], NULL, 0 ); - vendor_len = ( sizeof ( *vendor ) + - ( ( uri_len + 1 /* NUL */ ) * sizeof ( wchar_t ) ) ); - len = ( sizeof ( *block ) + uri_len + 1 /* NUL */ + prefix_len + - vendor_len + sizeof ( *end ) ); - /* Allocate and initialise structure */ - sandev = alloc_sandev ( uris, count, len ); + sandev = alloc_sandev ( uris, count, sizeof ( *block ) ); if ( ! sandev ) { rc = -ENOMEM; goto err_alloc; @@ -311,26 +272,6 @@ static int efi_block_hook ( unsigned int drive, struct uri **uris, block->block_io.ReadBlocks = efi_block_io_read; block->block_io.WriteBlocks = efi_block_io_write; block->block_io.FlushBlocks = efi_block_io_flush; - uri_buf = ( ( ( void * ) block ) + sizeof ( *block ) ); - block->path = ( ( ( void * ) uri_buf ) + uri_len + 1 /* NUL */ ); - - /* Construct device path */ - memcpy ( block->path, snpdev->path, prefix_len ); - vendor = ( ( ( void * ) block->path ) + prefix_len ); - vendor->vendor.Header.Type = HARDWARE_DEVICE_PATH; - vendor->vendor.Header.SubType = HW_VENDOR_DP; - vendor->vendor.Header.Length[0] = ( vendor_len & 0xff ); - vendor->vendor.Header.Length[1] = ( vendor_len >> 8 ); - memcpy ( &vendor->vendor.Guid, &ipxe_block_device_path_guid, - sizeof ( vendor->vendor.Guid ) ); - format_uri ( uris[0], uri_buf, ( uri_len + 1 /* NUL */ ) ); - efi_snprintf ( vendor->uri, ( uri_len + 1 /* NUL */ ), "%s", uri_buf ); - end = ( ( ( void * ) vendor ) + vendor_len ); - end->Type = END_DEVICE_PATH_TYPE; - end->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE; - end->Length[0] = sizeof ( *end ); - DBGC ( sandev, "EFIBLK %#02x has device path %s\n", - drive, efi_devpath_text ( block->path ) ); /* Register SAN device */ if ( ( rc = register_sandev ( sandev, drive, flags ) ) != 0 ) { @@ -345,6 +286,22 @@ static int efi_block_hook ( unsigned int drive, struct uri **uris, block->media.LastBlock = ( ( sandev->capacity.blocks >> sandev->blksize_shift ) - 1 ); + /* Construct device path */ + if ( ! sandev->active ) { + rc = -ENODEV; + DBGC ( sandev, "EFIBLK %#02x not active after registration\n", + drive ); + goto err_active; + } + block->path = efi_describe ( &sandev->active->block ); + if ( ! block->path ) { + rc = -ENODEV; + DBGC ( sandev, "EFIBLK %#02x has no device path\n", drive ); + goto err_describe; + } + DBGC ( sandev, "EFIBLK %#02x has device path %s\n", + drive, efi_devpath_text ( block->path ) ); + /* Install protocols */ if ( ( efirc = bs->InstallMultipleProtocolInterfaces ( &block->handle, @@ -362,17 +319,33 @@ static int efi_block_hook ( unsigned int drive, struct uri **uris, return drive; - bs->UninstallMultipleProtocolInterfaces ( + if ( ( efirc = bs->UninstallMultipleProtocolInterfaces ( block->handle, &efi_block_io_protocol_guid, &block->block_io, - &efi_device_path_protocol_guid, block->path, NULL ); + &efi_device_path_protocol_guid, block->path, + NULL ) ) != 0 ) { + DBGC ( sandev, "EFIBLK %#02x could not uninstall protocols: " + "%s\n", sandev->drive, strerror ( -EEFI ( efirc ) ) ); + leak = 1; + } + efi_nullify_block ( &block->block_io ); err_install: + if ( ! leak ) { + free ( block->path ); + block->path = NULL; + } + err_describe: + err_active: unregister_sandev ( sandev ); err_register: - sandev_put ( sandev ); + if ( ! leak ) + sandev_put ( sandev ); err_alloc: - err_no_snpdev: err_no_uris: + if ( leak ) { + DBGC ( sandev, "EFIBLK %#02x nullified and leaked\n", + sandev->drive ); + } return rc; } @@ -385,6 +358,8 @@ static void efi_block_unhook ( unsigned int drive ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct san_device *sandev; struct efi_block_data *block; + int leak = efi_shutdown_in_progress; + EFI_STATUS efirc; /* Find SAN device */ sandev = sandev_find ( drive ); @@ -395,16 +370,36 @@ static void efi_block_unhook ( unsigned int drive ) { block = sandev->priv; /* Uninstall protocols */ - bs->UninstallMultipleProtocolInterfaces ( + if ( ( ! efi_shutdown_in_progress ) && + ( ( efirc = bs->UninstallMultipleProtocolInterfaces ( block->handle, &efi_block_io_protocol_guid, &block->block_io, - &efi_device_path_protocol_guid, block->path, NULL ); + &efi_device_path_protocol_guid, block->path, + NULL ) ) != 0 ) ) { + DBGC ( sandev, "EFIBLK %#02x could not uninstall protocols: " + "%s\n", sandev->drive, strerror ( -EEFI ( efirc ) ) ); + leak = 1; + } + efi_nullify_block ( &block->block_io ); + + /* Free device path */ + if ( ! leak ) { + free ( block->path ); + block->path = NULL; + } /* Unregister SAN device */ unregister_sandev ( sandev ); /* Drop reference to drive */ - sandev_put ( sandev ); + if ( ! leak ) + sandev_put ( sandev ); + + /* Report leakage, if applicable */ + if ( leak && ( ! efi_shutdown_in_progress ) ) { + DBGC ( sandev, "EFIBLK %#02x nullified and leaked\n", + sandev->drive ); + } } /** An installed ACPI table */ @@ -450,17 +445,17 @@ static int efi_block_install ( struct acpi_header *hdr ) { rc = -EEFI ( efirc ); DBGC ( acpi, "EFIBLK could not install %s: %s\n", acpi_name ( hdr->signature ), strerror ( rc ) ); - DBGC_HDA ( acpi, 0, hdr, len ); + DBGC2_HDA ( acpi, 0, hdr, len ); goto err_install; } /* Add to list of installed tables */ list_add_tail ( &installed->list, &efi_acpi_tables ); - DBGC ( acpi, "EFIBLK installed %s as ACPI table %#lx:\n", + DBGC ( acpi, "EFIBLK installed %s as ACPI table %#lx\n", acpi_name ( hdr->signature ), ( ( unsigned long ) installed->key ) ); - DBGC_HDA ( acpi, 0, hdr, len ); + DBGC2_HDA ( acpi, 0, hdr, len ); return 0; list_del ( &installed->list ); @@ -551,7 +546,7 @@ static int efi_block_boot_image ( struct san_device *sandev, EFI_HANDLE handle, } /* Check if this device is a child of our block device */ - prefix_len = efi_devpath_len ( block->path ); + prefix_len = efi_path_len ( block->path ); if ( memcmp ( path.path, block->path, prefix_len ) != 0 ) { /* Not a child device */ rc = -ENOTTY; @@ -561,7 +556,7 @@ static int efi_block_boot_image ( struct san_device *sandev, EFI_HANDLE handle, sandev->drive, efi_devpath_text ( path.path ) ); /* Construct device path for boot image */ - end = efi_devpath_end ( path.path ); + end = efi_path_end ( path.path ); prefix_len = ( ( ( void * ) end ) - ( ( void * ) path.path ) ); filepath_len = ( SIZE_OF_FILEPATH_DEVICE_PATH + ( filename ? diff --git a/src/interface/efi/efi_bofm.c b/src/interface/efi/efi_bofm.c index 00f6a1d5c..15f3837cc 100644 --- a/src/interface/efi/efi_bofm.c +++ b/src/interface/efi/efi_bofm.c @@ -164,7 +164,7 @@ static EFI_GUID bofm2_protocol_guid = */ static int efi_bofm_supported ( EFI_HANDLE device ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - struct pci_device pci; + struct efi_pci_device efipci; union { IBM_BOFM_DRIVER_CONFIGURATION_PROTOCOL *bofm1; void *interface; @@ -173,11 +173,11 @@ static int efi_bofm_supported ( EFI_HANDLE device ) { int rc; /* Get PCI device information */ - if ( ( rc = efipci_info ( device, &pci ) ) != 0 ) + if ( ( rc = efipci_info ( device, &efipci ) ) != 0 ) return rc; /* Look for a BOFM driver */ - if ( ( rc = bofm_find_driver ( &pci ) ) != 0 ) { + if ( ( rc = bofm_find_driver ( &efipci.pci ) ) != 0 ) { DBGCP ( device, "EFIBOFM %s has no driver\n", efi_handle_name ( device ) ); return rc; @@ -204,7 +204,7 @@ static int efi_bofm_supported ( EFI_HANDLE device ) { } DBGC ( device, "EFIBOFM %s has driver \"%s\"\n", - efi_handle_name ( device ), pci.id->name ); + efi_handle_name ( device ), efipci.pci.id->name ); return 0; } @@ -225,7 +225,7 @@ static int efi_bofm_start ( struct efi_device *efidev ) { IBM_BOFM_DRIVER_CONFIGURATION_PROTOCOL2 *bofm2; void *interface; } bofm2; - struct pci_device pci; + struct efi_pci_device efipci; IBM_BOFM_TABLE *bofmtab; IBM_BOFM_TABLE *bofmtab2; int bofmrc; @@ -234,7 +234,7 @@ static int efi_bofm_start ( struct efi_device *efidev ) { /* Open PCI device, if possible */ if ( ( rc = efipci_open ( device, EFI_OPEN_PROTOCOL_GET_PROTOCOL, - &pci ) ) != 0 ) + &efipci ) ) != 0 ) goto err_open; /* Locate BOFM protocol */ @@ -274,7 +274,8 @@ static int efi_bofm_start ( struct efi_device *efidev ) { efi_handle_name ( device ) ); DBGC2_HD ( device, bofmtab2, bofmtab2->Parameters.Length ); } - bofmrc = bofm ( virt_to_user ( bofmtab2 ? bofmtab2 : bofmtab ), &pci ); + bofmrc = bofm ( virt_to_user ( bofmtab2 ? bofmtab2 : bofmtab ), + &efipci.pci ); DBGC ( device, "EFIBOFM %s status %08x\n", efi_handle_name ( device ), bofmrc ); DBGC2 ( device, "EFIBOFM %s version 1 after processing:\n", diff --git a/src/interface/efi/efi_cachedhcp.c b/src/interface/efi/efi_cachedhcp.c new file mode 100644 index 000000000..14b531d09 --- /dev/null +++ b/src/interface/efi/efi_cachedhcp.c @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2021 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <string.h> +#include <errno.h> +#include <ipxe/cachedhcp.h> +#include <ipxe/efi/efi.h> +#include <ipxe/efi/efi_cachedhcp.h> +#include <ipxe/efi/Protocol/PxeBaseCode.h> + +/** @file + * + * EFI cached DHCP packet + * + */ + +/** + * Record cached DHCP packet + * + * @v device Device handle + * @ret rc Return status code + */ +int efi_cachedhcp_record ( EFI_HANDLE device ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + union { + EFI_PXE_BASE_CODE_PROTOCOL *pxe; + void *interface; + } pxe; + EFI_PXE_BASE_CODE_MODE *mode; + EFI_STATUS efirc; + int rc; + + /* Look for a PXE base code instance on the image's device handle */ + if ( ( efirc = bs->OpenProtocol ( device, + &efi_pxe_base_code_protocol_guid, + &pxe.interface, efi_image_handle, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){ + rc = -EEFI ( efirc ); + DBGC ( device, "EFI %s has no PXE base code instance: %s\n", + efi_handle_name ( device ), strerror ( rc ) ); + goto err_open; + } + + /* Do not attempt to cache IPv6 packets */ + mode = pxe.pxe->Mode; + if ( mode->UsingIpv6 ) { + rc = -ENOTSUP; + DBGC ( device, "EFI %s has IPv6 PXE base code\n", + efi_handle_name ( device ) ); + goto err_ipv6; + } + + /* Record DHCPACK, if present */ + if ( mode->DhcpAckReceived && + ( ( rc = cachedhcp_record ( virt_to_user ( &mode->DhcpAck ), + sizeof ( mode->DhcpAck ) ) ) != 0 ) ) { + DBGC ( device, "EFI %s could not record DHCPACK: %s\n", + efi_handle_name ( device ), strerror ( rc ) ); + goto err_record; + } + + /* Success */ + rc = 0; + + err_record: + err_ipv6: + bs->CloseProtocol ( device, &efi_pxe_base_code_protocol_guid, + efi_image_handle, NULL ); + err_open: + return rc; +} diff --git a/src/interface/efi/efi_console.c b/src/interface/efi/efi_console.c index 047baed47..98ebbf3ac 100644 --- a/src/interface/efi/efi_console.c +++ b/src/interface/efi/efi_console.c @@ -54,6 +54,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ATTR_DEFAULT ATTR_FCOL_WHITE +#define CTRL_MASK 0x1f + /* Set default console usage if applicable */ #if ! ( defined ( CONSOLE_EFI ) && CONSOLE_EXPLICIT ( CONSOLE_EFI ) ) #undef CONSOLE_EFI @@ -67,6 +69,9 @@ static unsigned int efi_attr = ATTR_DEFAULT; static EFI_CONSOLE_CONTROL_PROTOCOL *conctrl; EFI_REQUEST_PROTOCOL ( EFI_CONSOLE_CONTROL_PROTOCOL, &conctrl ); +/** Extended simple text input protocol, if present */ +static EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *efi_conin_ex; + /** * Handle ANSI CUP (cursor position) * @@ -278,8 +283,9 @@ static const char * scancode_to_ansi_seq ( unsigned int scancode ) { */ static int efi_getchar ( void ) { EFI_SIMPLE_TEXT_INPUT_PROTOCOL *conin = efi_systab->ConIn; + EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *conin_ex = efi_conin_ex; const char *ansi_seq; - EFI_INPUT_KEY key; + EFI_KEY_DATA key; EFI_STATUS efirc; int rc; @@ -288,20 +294,42 @@ static int efi_getchar ( void ) { return *(ansi_input++); /* Read key from real EFI console */ - if ( ( efirc = conin->ReadKeyStroke ( conin, &key ) ) != 0 ) { - rc = -EEFI ( efirc ); - DBG ( "EFI could not read keystroke: %s\n", strerror ( rc ) ); - return 0; + memset ( &key, 0, sizeof ( key ) ); + if ( conin_ex ) { + if ( ( efirc = conin_ex->ReadKeyStrokeEx ( conin_ex, + &key ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBG ( "EFI could not read extended keystroke: %s\n", + strerror ( rc ) ); + return 0; + } + } else { + if ( ( efirc = conin->ReadKeyStroke ( conin, + &key.Key ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBG ( "EFI could not read keystroke: %s\n", + strerror ( rc ) ); + return 0; + } + } + DBG2 ( "EFI read key stroke shift %08x toggle %02x unicode %04x " + "scancode %04x\n", key.KeyState.KeyShiftState, + key.KeyState.KeyToggleState, key.Key.UnicodeChar, + key.Key.ScanCode ); + + /* Translate Ctrl-<key> */ + if ( ( key.KeyState.KeyShiftState & EFI_SHIFT_STATE_VALID ) && + ( key.KeyState.KeyShiftState & ( EFI_LEFT_CONTROL_PRESSED | + EFI_RIGHT_CONTROL_PRESSED ) ) ) { + key.Key.UnicodeChar &= CTRL_MASK; } - DBG2 ( "EFI read key stroke with unicode %04x scancode %04x\n", - key.UnicodeChar, key.ScanCode ); /* If key has a Unicode representation, return it */ - if ( key.UnicodeChar ) - return key.UnicodeChar; + if ( key.Key.UnicodeChar ) + return key.Key.UnicodeChar; /* Otherwise, check for a special key that we know about */ - if ( ( ansi_seq = scancode_to_ansi_seq ( key.ScanCode ) ) ) { + if ( ( ansi_seq = scancode_to_ansi_seq ( key.Key.ScanCode ) ) ) { /* Start of escape sequence: return ESC (0x1b) */ ansi_input = ansi_seq; return 0x1b; @@ -319,6 +347,8 @@ static int efi_getchar ( void ) { static int efi_iskey ( void ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; EFI_SIMPLE_TEXT_INPUT_PROTOCOL *conin = efi_systab->ConIn; + EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *conin_ex = efi_conin_ex; + EFI_EVENT *event; EFI_STATUS efirc; /* If we are mid-sequence, we are always ready */ @@ -326,7 +356,8 @@ static int efi_iskey ( void ) { return 1; /* Check to see if the WaitForKey event has fired */ - if ( ( efirc = bs->CheckEvent ( conin->WaitForKey ) ) == 0 ) + event = ( conin_ex ? conin_ex->WaitForKeyEx : conin->WaitForKey ); + if ( ( efirc = bs->CheckEvent ( event ) ) == 0 ) return 1; return 0; @@ -345,7 +376,14 @@ struct console_driver efi_console __console_driver = { * */ static void efi_console_init ( void ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; EFI_CONSOLE_CONTROL_SCREEN_MODE mode; + union { + void *interface; + EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *wtf; + } u; + EFI_STATUS efirc; + int rc; /* On some older EFI 1.10 implementations, we must use the * (now obsolete) EFI_CONSOLE_CONTROL_PROTOCOL to switch the @@ -358,6 +396,23 @@ static void efi_console_init ( void ) { EfiConsoleControlScreenText ); } } + + /* Attempt to open the Simple Text Input Ex protocol on the + * console input handle. This is provably unsafe, but is + * apparently the expected behaviour for all UEFI + * applications. Don't ask. + */ + if ( ( efirc = bs->OpenProtocol ( efi_systab->ConsoleInHandle, + &efi_simple_text_input_ex_protocol_guid, + &u.interface, efi_image_handle, + efi_systab->ConsoleInHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL ) ) == 0 ) { + efi_conin_ex = u.wtf; + DBG ( "EFI using SimpleTextInputEx\n" ); + } else { + rc = -EEFI ( efirc ); + DBG ( "EFI has no SimpleTextInputEx: %s\n", strerror ( rc ) ); + } } /** diff --git a/src/interface/efi/efi_debug.c b/src/interface/efi/efi_debug.c index de9b1af55..967bb6182 100644 --- a/src/interface/efi/efi_debug.c +++ b/src/interface/efi/efi_debug.c @@ -37,7 +37,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/base16.h> #include <ipxe/vsprintf.h> #include <ipxe/efi/efi.h> -#include <ipxe/efi/efi_utils.h> +#include <ipxe/efi/efi_path.h> #include <ipxe/efi/Protocol/ComponentName.h> #include <ipxe/efi/Protocol/ComponentName2.h> #include <ipxe/efi/Protocol/DevicePathToText.h> @@ -189,7 +189,7 @@ static struct efi_well_known_guid efi_well_known_guids[] = { * @v guid GUID * @ret string Printable string */ -const __attribute__ (( pure )) char * efi_guid_ntoa ( EFI_GUID *guid ) { +const __attribute__ (( pure )) char * efi_guid_ntoa ( CONST EFI_GUID *guid ) { union { union uuid uuid; EFI_GUID guid; @@ -263,6 +263,28 @@ efi_open_attributes_name ( unsigned int attributes ) { } /** + * Print opened protocol information + * + * @v handle EFI handle + * @V protocol Protocol GUID + * @v opener Opened protocol information + */ +void dbg_efi_opener ( EFI_HANDLE handle, EFI_GUID *protocol, + EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *opener ) { + + printf ( "HANDLE %s %s opened %dx (%s)", efi_handle_name ( handle ), + efi_guid_ntoa ( protocol ), opener->OpenCount, + efi_open_attributes_name ( opener->Attributes ) ); + printf ( " by %s", efi_handle_name ( opener->AgentHandle ) ); + if ( opener->ControllerHandle == handle ) { + printf ( "\n" ); + } else { + printf ( " for %s\n", + efi_handle_name ( opener->ControllerHandle ) ); + } +} + +/** * Print list of openers of a given protocol on a given handle * * @v handle EFI handle @@ -271,7 +293,6 @@ efi_open_attributes_name ( unsigned int attributes ) { void dbg_efi_openers ( EFI_HANDLE handle, EFI_GUID *protocol ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *openers; - EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *opener; UINTN count; unsigned int i; EFI_STATUS efirc; @@ -296,20 +317,8 @@ void dbg_efi_openers ( EFI_HANDLE handle, EFI_GUID *protocol ) { } /* Dump list of openers */ - for ( i = 0 ; i < count ; i++ ) { - opener = &openers[i]; - printf ( "HANDLE %s %s opened %dx (%s)", - efi_handle_name ( handle ), - efi_guid_ntoa ( protocol ), opener->OpenCount, - efi_open_attributes_name ( opener->Attributes ) ); - printf ( " by %s", efi_handle_name ( opener->AgentHandle ) ); - if ( opener->ControllerHandle == handle ) { - printf ( "\n" ); - } else { - printf ( " for %s\n", - efi_handle_name ( opener->ControllerHandle ) ); - } - } + for ( i = 0 ; i < count ; i++ ) + dbg_efi_opener ( handle, protocol, &openers[i] ); /* Free list */ bs->FreePool ( openers ); @@ -365,7 +374,7 @@ void dbg_efi_protocols ( EFI_HANDLE handle ) { const __attribute__ (( pure )) char * efi_devpath_text ( EFI_DEVICE_PATH_PROTOCOL *path ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - static char text[256]; + static char text[512]; size_t len; CHAR16 *wtext; @@ -378,7 +387,7 @@ efi_devpath_text ( EFI_DEVICE_PATH_PROTOCOL *path ) { /* If we have no DevicePathToText protocol then use a raw hex string */ if ( ! efidpt ) { DBG ( "[No DevicePathToText]" ); - len = efi_devpath_len ( path ); + len = efi_path_len ( path ); base16_encode ( path, len, text, sizeof ( text ) ); return text; } diff --git a/src/interface/efi/efi_driver.c b/src/interface/efi/efi_driver.c index 760ee41a8..8e537d535 100644 --- a/src/interface/efi/efi_driver.c +++ b/src/interface/efi/efi_driver.c @@ -30,7 +30,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <ipxe/efi/Protocol/ComponentName2.h> #include <ipxe/efi/Protocol/DevicePath.h> #include <ipxe/efi/efi_strings.h> -#include <ipxe/efi/efi_utils.h> +#include <ipxe/efi/efi_path.h> #include <ipxe/efi/efi_driver.h> /** @file @@ -156,13 +156,13 @@ efi_driver_start ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_driver *efidrv; struct efi_device *efidev; + struct efi_saved_tpl tpl; union { EFI_DEVICE_PATH_PROTOCOL *path; void *interface; } path; EFI_DEVICE_PATH_PROTOCOL *path_end; size_t path_len; - EFI_TPL saved_tpl; EFI_STATUS efirc; int rc; @@ -181,7 +181,7 @@ efi_driver_start ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, } /* Raise TPL */ - saved_tpl = bs->RaiseTPL ( TPL_CALLBACK ); + efi_raise_tpl ( &tpl ); /* Do nothing if we are currently disconnecting drivers */ if ( efi_driver_disconnecting ) { @@ -202,7 +202,7 @@ efi_driver_start ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, efi_handle_name ( device ), strerror ( rc ) ); goto err_open_path; } - path_len = ( efi_devpath_len ( path.path ) + sizeof ( *path_end ) ); + path_len = ( efi_path_len ( path.path ) + sizeof ( *path_end ) ); /* Allocate and initialise structure */ efidev = zalloc ( sizeof ( *efidev ) + path_len ); @@ -236,7 +236,7 @@ efi_driver_start ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, DBGC ( device, "EFIDRV %s using driver \"%s\"\n", efi_handle_name ( device ), efidev->driver->name ); - bs->RestoreTPL ( saved_tpl ); + efi_restore_tpl ( &tpl ); return 0; } DBGC ( device, "EFIDRV %s could not start driver \"%s\": %s\n", @@ -254,7 +254,7 @@ efi_driver_start ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, } err_open_path: err_disconnecting: - bs->RestoreTPL ( saved_tpl ); + efi_restore_tpl ( &tpl ); err_already_started: return efirc; } @@ -273,10 +273,9 @@ static EFI_STATUS EFIAPI efi_driver_stop ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, EFI_HANDLE device, UINTN num_children, EFI_HANDLE *children ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_driver *efidrv; struct efi_device *efidev; - EFI_TPL saved_tpl; + struct efi_saved_tpl tpl; UINTN i; DBGC ( device, "EFIDRV %s DRIVER_STOP", efi_handle_name ( device ) ); @@ -295,7 +294,7 @@ efi_driver_stop ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, } /* Raise TPL */ - saved_tpl = bs->RaiseTPL ( TPL_CALLBACK ); + efi_raise_tpl ( &tpl ); /* Stop this device */ efidrv = efidev->driver; @@ -304,7 +303,7 @@ efi_driver_stop ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, list_del ( &efidev->dev.siblings ); free ( efidev ); - bs->RestoreTPL ( saved_tpl ); + efi_restore_tpl ( &tpl ); return 0; } @@ -470,7 +469,7 @@ static int efi_driver_connect ( EFI_HANDLE device ) { DBGC ( device, "EFIDRV %s connecting new drivers\n", efi_handle_name ( device ) ); if ( ( efirc = bs->ConnectController ( device, drivers, NULL, - FALSE ) ) != 0 ) { + TRUE ) ) != 0 ) { rc = -EEFI_CONNECT ( efirc ); DBGC ( device, "EFIDRV %s could not connect new drivers: " "%s\n", efi_handle_name ( device ), strerror ( rc ) ); @@ -520,7 +519,7 @@ static int efi_driver_reconnect ( EFI_HANDLE device ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; /* Reconnect any available driver */ - bs->ConnectController ( device, NULL, NULL, FALSE ); + bs->ConnectController ( device, NULL, NULL, TRUE ); return 0; } diff --git a/src/interface/efi/efi_entropy.c b/src/interface/efi/efi_entropy.c index f6e82e2c0..b7cb6a40c 100644 --- a/src/interface/efi/efi_entropy.c +++ b/src/interface/efi/efi_entropy.c @@ -84,8 +84,8 @@ static int efi_entropy_enable ( void ) { DBGC ( &tick, "ENTROPY %s RNG protocol\n", ( efirng ? "has" : "has no" ) ); - /* Drop to TPL_APPLICATION to allow timer tick event to take place */ - bs->RestoreTPL ( TPL_APPLICATION ); + /* Drop to external TPL to allow timer tick event to take place */ + bs->RestoreTPL ( efi_external_tpl ); /* Create timer tick event */ if ( ( efirc = bs->CreateEvent ( EVT_TIMER, TPL_NOTIFY, NULL, NULL, diff --git a/src/interface/efi/efi_init.c b/src/interface/efi/efi_init.c index 70212b184..b7cac16e5 100644 --- a/src/interface/efi/efi_init.c +++ b/src/interface/efi/efi_init.c @@ -26,7 +26,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <ipxe/rotate.h> #include <ipxe/efi/efi.h> #include <ipxe/efi/efi_driver.h> -#include <ipxe/efi/efi_utils.h> +#include <ipxe/efi/efi_path.h> #include <ipxe/efi/Protocol/LoadedImage.h> /** Image handle passed to entry point */ @@ -47,6 +47,9 @@ EFI_DEVICE_PATH_PROTOCOL *efi_loaded_image_path; */ EFI_SYSTEM_TABLE * _C2 ( PLATFORM, _systab ); +/** External task priority level */ +EFI_TPL efi_external_tpl = TPL_APPLICATION; + /** EFI shutdown is in progress */ int efi_shutdown_in_progress; @@ -252,7 +255,7 @@ EFI_STATUS efi_init ( EFI_HANDLE image_handle, * path, since the device handle itself may become invalidated * when we load our own drivers. */ - device_path_len = ( efi_devpath_len ( device_path ) + + device_path_len = ( efi_path_len ( device_path ) + sizeof ( EFI_DEVICE_PATH_PROTOCOL ) ); if ( ( efirc = bs->AllocatePool ( EfiBootServicesData, device_path_len, &device_path_copy ) ) != 0 ) { @@ -361,3 +364,34 @@ __attribute__ (( noreturn )) void __stack_chk_fail ( void ) { while ( 1 ) {} } + +/** + * Raise task priority level to TPL_CALLBACK + * + * @v tpl Saved TPL + */ +void efi_raise_tpl ( struct efi_saved_tpl *tpl ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + + /* Record current external TPL */ + tpl->previous = efi_external_tpl; + + /* Raise TPL and record previous TPL as new external TPL */ + tpl->current = bs->RaiseTPL ( TPL_CALLBACK ); + efi_external_tpl = tpl->current; +} + +/** + * Restore task priority level + * + * @v tpl Saved TPL + */ +void efi_restore_tpl ( struct efi_saved_tpl *tpl ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + + /* Restore external TPL */ + efi_external_tpl = tpl->previous; + + /* Restore TPL */ + bs->RestoreTPL ( tpl->current ); +} diff --git a/src/interface/efi/efi_local.c b/src/interface/efi/efi_local.c index 79ea822f3..4ebca5726 100644 --- a/src/interface/efi/efi_local.c +++ b/src/interface/efi/efi_local.c @@ -37,7 +37,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/process.h> #include <ipxe/efi/efi.h> #include <ipxe/efi/efi_strings.h> -#include <ipxe/efi/efi_utils.h> +#include <ipxe/efi/efi_path.h> #include <ipxe/efi/Protocol/SimpleFileSystem.h> #include <ipxe/efi/Guid/FileInfo.h> #include <ipxe/efi/Guid/FileSystemInfo.h> @@ -425,7 +425,7 @@ static int efi_local_open_resolved ( struct efi_local *local, static int efi_local_open_path ( struct efi_local *local, const char *path ) { FILEPATH_DEVICE_PATH *fp = container_of ( efi_loaded_image->FilePath, FILEPATH_DEVICE_PATH, Header); - size_t fp_len = ( fp ? efi_devpath_len ( &fp->Header ) : 0 ); + size_t fp_len = ( fp ? efi_path_len ( &fp->Header ) : 0 ); char base[ fp_len / 2 /* Cannot exceed this length */ ]; size_t remaining = sizeof ( base ); size_t len; @@ -548,8 +548,8 @@ static int efi_local_open ( struct interface *xfer, struct uri *uri ) { } ref_init ( &local->refcnt, NULL ); intf_init ( &local->xfer, &efi_local_xfer_desc, &local->refcnt ); - process_init ( &local->process, &efi_local_process_desc, - &local->refcnt ); + process_init_stopped ( &local->process, &efi_local_process_desc, + &local->refcnt ); /* Open specified volume */ if ( ( rc = efi_local_open_volume ( local, volume ) ) != 0 ) @@ -563,6 +563,9 @@ static int efi_local_open ( struct interface *xfer, struct uri *uri ) { if ( ( rc = efi_local_len ( local ) ) != 0 ) goto err_len; + /* Start download process */ + process_add ( &local->process ); + /* Attach to parent interface, mortalise self, and return */ intf_plug_plug ( &local->xfer, xfer ); ref_put ( &local->refcnt ); diff --git a/src/interface/efi/efi_null.c b/src/interface/efi/efi_null.c new file mode 100644 index 000000000..29ca5b9b6 --- /dev/null +++ b/src/interface/efi/efi_null.c @@ -0,0 +1,672 @@ +/* + * Copyright (C) 2020 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <string.h> +#include <ipxe/efi/efi.h> +#include <ipxe/efi/efi_null.h> + +/** @file + * + * EFI null interfaces + * + */ + +/****************************************************************************** + * + * Simple Network Protocol + * + ****************************************************************************** + */ + +static EFI_STATUS EFIAPI +efi_null_snp_start ( EFI_SIMPLE_NETWORK_PROTOCOL *snp __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI +efi_null_snp_stop ( EFI_SIMPLE_NETWORK_PROTOCOL *snp __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI +efi_null_snp_initialize ( EFI_SIMPLE_NETWORK_PROTOCOL *snp __unused, + UINTN extra_rx_bufsize __unused, + UINTN extra_tx_bufsize __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI +efi_null_snp_reset ( EFI_SIMPLE_NETWORK_PROTOCOL *snp __unused, + BOOLEAN ext_verify __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI +efi_null_snp_shutdown ( EFI_SIMPLE_NETWORK_PROTOCOL *snp __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI +efi_null_snp_receive_filters ( EFI_SIMPLE_NETWORK_PROTOCOL *snp __unused, + UINT32 enable __unused, + UINT32 disable __unused, + BOOLEAN mcast_reset __unused, + UINTN mcast_count __unused, + EFI_MAC_ADDRESS *mcast __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI +efi_null_snp_station_address ( EFI_SIMPLE_NETWORK_PROTOCOL *snp __unused, + BOOLEAN reset __unused, + EFI_MAC_ADDRESS *new __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI +efi_null_snp_statistics ( EFI_SIMPLE_NETWORK_PROTOCOL *snp __unused, + BOOLEAN reset __unused, UINTN *stats_len __unused, + EFI_NETWORK_STATISTICS *stats __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI +efi_null_snp_mcast_ip_to_mac ( EFI_SIMPLE_NETWORK_PROTOCOL *snp __unused, + BOOLEAN ipv6 __unused, + EFI_IP_ADDRESS *ip __unused, + EFI_MAC_ADDRESS *mac __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI +efi_null_snp_nvdata ( EFI_SIMPLE_NETWORK_PROTOCOL *snp __unused, + BOOLEAN read __unused, UINTN offset __unused, + UINTN len __unused, VOID *data __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI +efi_null_snp_get_status ( EFI_SIMPLE_NETWORK_PROTOCOL *snp __unused, + UINT32 *interrupts __unused, VOID **txbuf __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI +efi_null_snp_transmit ( EFI_SIMPLE_NETWORK_PROTOCOL *snp __unused, + UINTN ll_header_len __unused, UINTN len __unused, + VOID *data __unused, EFI_MAC_ADDRESS *ll_src __unused, + EFI_MAC_ADDRESS *ll_dest __unused, + UINT16 *net_proto __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI +efi_null_snp_receive ( EFI_SIMPLE_NETWORK_PROTOCOL *snp __unused, + UINTN *ll_header_len __unused, UINTN *len __unused, + VOID *data __unused, EFI_MAC_ADDRESS *ll_src __unused, + EFI_MAC_ADDRESS *ll_dest __unused, + UINT16 *net_proto __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_SIMPLE_NETWORK_PROTOCOL efi_null_snp = { + .Revision = EFI_SIMPLE_NETWORK_PROTOCOL_REVISION, + .Start = efi_null_snp_start, + .Stop = efi_null_snp_stop, + .Initialize = efi_null_snp_initialize, + .Reset = efi_null_snp_reset, + .Shutdown = efi_null_snp_shutdown, + .ReceiveFilters = efi_null_snp_receive_filters, + .StationAddress = efi_null_snp_station_address, + .Statistics = efi_null_snp_statistics, + .MCastIpToMac = efi_null_snp_mcast_ip_to_mac, + .NvData = efi_null_snp_nvdata, + .GetStatus = efi_null_snp_get_status, + .Transmit = efi_null_snp_transmit, + .Receive = efi_null_snp_receive, +}; + +/** + * Nullify SNP interface + * + * @v snp SNP interface + */ +void efi_nullify_snp ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ) { + + memcpy ( snp, &efi_null_snp, + offsetof ( typeof ( *snp ), WaitForPacket ) ); + snp->Mode->State = EfiSimpleNetworkStopped; +} + +/****************************************************************************** + * + * Network Interface Identification protocol + * + ****************************************************************************** + */ + +static EFIAPI VOID efi_null_undi_issue ( UINT64 cdb_phys ) { + PXE_CDB *cdb = ( ( void * ) ( intptr_t ) cdb_phys ); + + cdb->StatCode = PXE_STATCODE_UNSUPPORTED; + cdb->StatFlags = PXE_STATFLAGS_COMMAND_FAILED; +} + +static PXE_SW_UNDI efi_null_undi __attribute__ (( aligned ( 16 ) )) = { + .Signature = PXE_ROMID_SIGNATURE, + .Len = sizeof ( efi_null_undi ), + .Rev = PXE_ROMID_REV, + .MajorVer = PXE_ROMID_MAJORVER, + .MinorVer = PXE_ROMID_MINORVER, + .Implementation = PXE_ROMID_IMP_SW_VIRT_ADDR, +}; + +/** + * Nullify NII interface + * + * @v nii NII interface + */ +void efi_nullify_nii ( EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL *nii ) { + efi_null_undi.EntryPoint = ( ( intptr_t ) efi_null_undi_issue ); + nii->Id = ( ( intptr_t ) &efi_null_undi ); +} + +/****************************************************************************** + * + * Component name protocol + * + ****************************************************************************** + */ + +static EFI_STATUS EFIAPI +efi_null_get_driver_name ( EFI_COMPONENT_NAME2_PROTOCOL *name2 __unused, + CHAR8 *language __unused, + CHAR16 **driver_name __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI +efi_null_get_controller_name ( EFI_COMPONENT_NAME2_PROTOCOL *name2 __unused, + EFI_HANDLE device __unused, + EFI_HANDLE child __unused, + CHAR8 *language __unused, + CHAR16 **controller_name __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_COMPONENT_NAME2_PROTOCOL efi_null_name2 = { + .GetDriverName = efi_null_get_driver_name, + .GetControllerName = efi_null_get_controller_name, + .SupportedLanguages = "", +}; + +/** + * Nullify Component Name Protocol interface + * + * @v name2 Component name protocol + */ +void efi_nullify_name2 ( EFI_COMPONENT_NAME2_PROTOCOL *name2 ) { + + memcpy ( name2, &efi_null_name2, sizeof ( *name2 ) ); +} + +/****************************************************************************** + * + * Load file protocol + * + ****************************************************************************** + */ + +static EFI_STATUS EFIAPI +efi_null_load_file ( EFI_LOAD_FILE_PROTOCOL *load_file __unused, + EFI_DEVICE_PATH_PROTOCOL *path __unused, + BOOLEAN booting __unused, UINTN *len __unused, + VOID *data __unused ) { + return EFI_UNSUPPORTED; +} + +/** + * Nullify Load File Protocol interface + * + * @v load_file Load file protocol + */ +void efi_nullify_load_file ( EFI_LOAD_FILE_PROTOCOL *load_file ) { + load_file->LoadFile = efi_null_load_file; +} + +/****************************************************************************** + * + * HII configuration access protocol + * + ****************************************************************************** + */ + +static EFI_STATUS EFIAPI +efi_null_hii_extract ( const EFI_HII_CONFIG_ACCESS_PROTOCOL *hii __unused, + EFI_STRING request __unused, + EFI_STRING *progress __unused, + EFI_STRING *results __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI +efi_null_hii_route ( const EFI_HII_CONFIG_ACCESS_PROTOCOL *hii __unused, + EFI_STRING config __unused, + EFI_STRING *progress __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI +efi_null_hii_callback ( const EFI_HII_CONFIG_ACCESS_PROTOCOL *hii __unused, + EFI_BROWSER_ACTION action __unused, + EFI_QUESTION_ID question_id __unused, + UINT8 type __unused, EFI_IFR_TYPE_VALUE *value __unused, + EFI_BROWSER_ACTION_REQUEST *action_request __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_HII_CONFIG_ACCESS_PROTOCOL efi_null_hii = { + .ExtractConfig = efi_null_hii_extract, + .RouteConfig = efi_null_hii_route, + .Callback = efi_null_hii_callback, +}; + +/** + * Nullify HII configuration access protocol + * + * @v hii HII configuration access protocol + */ +void efi_nullify_hii ( EFI_HII_CONFIG_ACCESS_PROTOCOL *hii ) { + + memcpy ( hii, &efi_null_hii, sizeof ( *hii ) ); +} + +/****************************************************************************** + * + * Block I/O protocol + * + ****************************************************************************** + */ + +static EFI_STATUS EFIAPI +efi_null_block_reset ( EFI_BLOCK_IO_PROTOCOL *block __unused, + BOOLEAN verify __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI +efi_null_block_read ( EFI_BLOCK_IO_PROTOCOL *block __unused, + UINT32 media __unused, EFI_LBA lba __unused, + UINTN len __unused, VOID *data __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI +efi_null_block_write ( EFI_BLOCK_IO_PROTOCOL *block __unused, + UINT32 media __unused, EFI_LBA lba __unused, + UINTN len __unused, VOID *data __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI +efi_null_block_flush ( EFI_BLOCK_IO_PROTOCOL *block __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_BLOCK_IO_MEDIA efi_null_block_media; + +static EFI_BLOCK_IO_PROTOCOL efi_null_block = { + .Revision = EFI_BLOCK_IO_INTERFACE_REVISION, + .Media = &efi_null_block_media, + .Reset = efi_null_block_reset, + .ReadBlocks = efi_null_block_read, + .WriteBlocks = efi_null_block_write, + .FlushBlocks = efi_null_block_flush, +}; + +/** + * Nullify block I/O protocol + * + * @v block Block I/O protocol + */ +void efi_nullify_block ( EFI_BLOCK_IO_PROTOCOL *block ) { + + memcpy ( block, &efi_null_block, sizeof ( *block ) ); +} + +/****************************************************************************** + * + * PXE base code protocol + * + ****************************************************************************** + */ + +static EFI_STATUS EFIAPI +efi_null_pxe_start ( EFI_PXE_BASE_CODE_PROTOCOL *pxe __unused, + BOOLEAN use_ipv6 __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI +efi_null_pxe_stop ( EFI_PXE_BASE_CODE_PROTOCOL *pxe __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI +efi_null_pxe_dhcp ( EFI_PXE_BASE_CODE_PROTOCOL *pxe __unused, + BOOLEAN sort __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI +efi_null_pxe_discover ( EFI_PXE_BASE_CODE_PROTOCOL *pxe __unused, + UINT16 type __unused, UINT16 *layer __unused, + BOOLEAN bis __unused, + EFI_PXE_BASE_CODE_DISCOVER_INFO *info __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI +efi_null_pxe_mtftp ( EFI_PXE_BASE_CODE_PROTOCOL *pxe __unused, + EFI_PXE_BASE_CODE_TFTP_OPCODE opcode __unused, + VOID *data __unused, BOOLEAN overwrite __unused, + UINT64 *len __unused, UINTN *blksize __unused, + EFI_IP_ADDRESS *ip __unused, UINT8 *filename __unused, + EFI_PXE_BASE_CODE_MTFTP_INFO *info __unused, + BOOLEAN callback __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI +efi_null_pxe_udp_write ( EFI_PXE_BASE_CODE_PROTOCOL *pxe __unused, + UINT16 flags __unused, + EFI_IP_ADDRESS *dest_ip __unused, + EFI_PXE_BASE_CODE_UDP_PORT *dest_port __unused, + EFI_IP_ADDRESS *gateway __unused, + EFI_IP_ADDRESS *src_ip __unused, + EFI_PXE_BASE_CODE_UDP_PORT *src_port __unused, + UINTN *hdr_len __unused, VOID *hdr __unused, + UINTN *len __unused, VOID *data __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI +efi_null_pxe_udp_read ( EFI_PXE_BASE_CODE_PROTOCOL *pxe __unused, + UINT16 flags __unused, + EFI_IP_ADDRESS *dest_ip __unused, + EFI_PXE_BASE_CODE_UDP_PORT *dest_port __unused, + EFI_IP_ADDRESS *src_ip __unused, + EFI_PXE_BASE_CODE_UDP_PORT *src_port __unused, + UINTN *hdr_len __unused, VOID *hdr __unused, + UINTN *len __unused, VOID *data __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI +efi_null_pxe_set_ip_filter ( EFI_PXE_BASE_CODE_PROTOCOL *pxe __unused, + EFI_PXE_BASE_CODE_IP_FILTER *filter __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI +efi_null_pxe_arp ( EFI_PXE_BASE_CODE_PROTOCOL *pxe __unused, + EFI_IP_ADDRESS *ip __unused, + EFI_MAC_ADDRESS *mac __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI +efi_null_pxe_set_parameters ( EFI_PXE_BASE_CODE_PROTOCOL *pxe __unused, + BOOLEAN *autoarp __unused, + BOOLEAN *sendguid __unused, UINT8 *ttl __unused, + UINT8 *tos __unused, + BOOLEAN *callback __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI +efi_null_pxe_set_station_ip ( EFI_PXE_BASE_CODE_PROTOCOL *pxe __unused, + EFI_IP_ADDRESS *ip __unused, + EFI_IP_ADDRESS *netmask __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI +efi_null_pxe_set_packets ( EFI_PXE_BASE_CODE_PROTOCOL *pxe __unused, + BOOLEAN *dhcpdisc_ok __unused, + BOOLEAN *dhcpack_ok __unused, + BOOLEAN *proxyoffer_ok __unused, + BOOLEAN *pxebsdisc_ok __unused, + BOOLEAN *pxebsack_ok __unused, + BOOLEAN *pxebsbis_ok __unused, + EFI_PXE_BASE_CODE_PACKET *dhcpdisc __unused, + EFI_PXE_BASE_CODE_PACKET *dhcpack __unused, + EFI_PXE_BASE_CODE_PACKET *proxyoffer __unused, + EFI_PXE_BASE_CODE_PACKET *pxebsdisc __unused, + EFI_PXE_BASE_CODE_PACKET *pxebsack __unused, + EFI_PXE_BASE_CODE_PACKET *pxebsbis __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_PXE_BASE_CODE_PROTOCOL efi_null_pxe = { + .Revision = EFI_PXE_BASE_CODE_PROTOCOL_REVISION, + .Start = efi_null_pxe_start, + .Stop = efi_null_pxe_stop, + .Dhcp = efi_null_pxe_dhcp, + .Discover = efi_null_pxe_discover, + .Mtftp = efi_null_pxe_mtftp, + .UdpWrite = efi_null_pxe_udp_write, + .UdpRead = efi_null_pxe_udp_read, + .SetIpFilter = efi_null_pxe_set_ip_filter, + .Arp = efi_null_pxe_arp, + .SetParameters = efi_null_pxe_set_parameters, + .SetStationIp = efi_null_pxe_set_station_ip, + .SetPackets = efi_null_pxe_set_packets, +}; + +/** + * Nullify PXE base code protocol + * + * @v pxe PXE base code protocol + */ +void efi_nullify_pxe ( EFI_PXE_BASE_CODE_PROTOCOL *pxe ) { + + memcpy ( pxe, &efi_null_pxe, offsetof ( typeof ( *pxe ), Mode ) ); + pxe->Mode->Started = FALSE; +} + +/****************************************************************************** + * + * Apple Net Boot protocol + * + ****************************************************************************** + */ + +static EFI_STATUS EFIAPI +efi_null_apple_dhcp ( EFI_APPLE_NET_BOOT_PROTOCOL *apple __unused, + UINTN *len __unused, VOID *data __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI +efi_null_apple_bsdp ( EFI_APPLE_NET_BOOT_PROTOCOL *apple __unused, + UINTN *len __unused, VOID *data __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_APPLE_NET_BOOT_PROTOCOL efi_null_apple = { + .GetDhcpResponse = efi_null_apple_dhcp, + .GetBsdpResponse = efi_null_apple_bsdp, +}; + +/** + * Nullify Apple Net Boot protocol + * + * @v apple Apple Net Boot protocol + */ +void efi_nullify_apple ( EFI_APPLE_NET_BOOT_PROTOCOL *apple ) { + + memcpy ( apple, &efi_null_apple, sizeof ( *apple ) ); +} + +/****************************************************************************** + * + * USB I/O Protocol + * + ****************************************************************************** + */ + +static EFI_STATUS EFIAPI +efi_null_usb_control_transfer ( EFI_USB_IO_PROTOCOL *usbio __unused, + EFI_USB_DEVICE_REQUEST *packet __unused, + EFI_USB_DATA_DIRECTION direction __unused, + UINT32 timeout __unused, VOID *data __unused, + UINTN len __unused, UINT32 *status __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI +efi_null_usb_bulk_transfer ( EFI_USB_IO_PROTOCOL *usbio __unused, + UINT8 endpoint __unused, VOID *data __unused, + UINTN *len __unused, UINTN timeout __unused, + UINT32 *status __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI +efi_null_usb_sync_interrupt_transfer ( EFI_USB_IO_PROTOCOL *usbio __unused, + UINT8 endpoint __unused, + VOID *data __unused, + UINTN *len __unused, + UINTN timeout __unused, + UINT32 *status __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI +efi_null_usb_async_interrupt_transfer ( EFI_USB_IO_PROTOCOL *usbio __unused, + UINT8 endpoint __unused, + BOOLEAN start __unused, + UINTN interval __unused, + UINTN len __unused, + EFI_ASYNC_USB_TRANSFER_CALLBACK + callback __unused, + VOID *context __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI +efi_null_usb_isochronous_transfer ( EFI_USB_IO_PROTOCOL *usbio __unused, + UINT8 endpoint __unused, + VOID *data __unused, UINTN len __unused, + UINT32 *status __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI +efi_null_usb_async_isochronous_transfer ( EFI_USB_IO_PROTOCOL *usbio __unused, + UINT8 endpoint __unused, + VOID *data __unused, + UINTN len __unused, + EFI_ASYNC_USB_TRANSFER_CALLBACK + callback __unused, + VOID *context __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI +efi_null_usb_get_device_descriptor ( EFI_USB_IO_PROTOCOL *usbio __unused, + EFI_USB_DEVICE_DESCRIPTOR + *efidesc __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI +efi_null_usb_get_config_descriptor ( EFI_USB_IO_PROTOCOL *usbio __unused, + EFI_USB_CONFIG_DESCRIPTOR + *efidesc __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI +efi_null_usb_get_interface_descriptor ( EFI_USB_IO_PROTOCOL *usbio __unused, + EFI_USB_INTERFACE_DESCRIPTOR + *efidesc __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI +efi_null_usb_get_endpoint_descriptor ( EFI_USB_IO_PROTOCOL *usbio __unused, + UINT8 index __unused, + EFI_USB_ENDPOINT_DESCRIPTOR + *efidesc __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI +efi_null_usb_get_string_descriptor ( EFI_USB_IO_PROTOCOL *usbio __unused, + UINT16 language __unused, + UINT8 index __unused, + CHAR16 **string __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI +efi_null_usb_get_supported_languages ( EFI_USB_IO_PROTOCOL *usbio __unused, + UINT16 **languages __unused, + UINT16 *len __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI +efi_null_usb_port_reset ( EFI_USB_IO_PROTOCOL *usbio __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_USB_IO_PROTOCOL efi_null_usbio = { + .UsbControlTransfer = efi_null_usb_control_transfer, + .UsbBulkTransfer = efi_null_usb_bulk_transfer, + .UsbAsyncInterruptTransfer = efi_null_usb_async_interrupt_transfer, + .UsbSyncInterruptTransfer = efi_null_usb_sync_interrupt_transfer, + .UsbIsochronousTransfer = efi_null_usb_isochronous_transfer, + .UsbAsyncIsochronousTransfer = efi_null_usb_async_isochronous_transfer, + .UsbGetDeviceDescriptor = efi_null_usb_get_device_descriptor, + .UsbGetConfigDescriptor = efi_null_usb_get_config_descriptor, + .UsbGetInterfaceDescriptor = efi_null_usb_get_interface_descriptor, + .UsbGetEndpointDescriptor = efi_null_usb_get_endpoint_descriptor, + .UsbGetStringDescriptor = efi_null_usb_get_string_descriptor, + .UsbGetSupportedLanguages = efi_null_usb_get_supported_languages, + .UsbPortReset = efi_null_usb_port_reset, +}; + +/** + * Nullify USB I/O protocol + * + * @v usbio USB I/O protocol + */ +void efi_nullify_usbio ( EFI_USB_IO_PROTOCOL *usbio ) { + + memcpy ( usbio, &efi_null_usbio, sizeof ( *usbio ) ); +} diff --git a/src/interface/efi/efi_path.c b/src/interface/efi/efi_path.c new file mode 100644 index 000000000..bae0ac4b5 --- /dev/null +++ b/src/interface/efi/efi_path.c @@ -0,0 +1,506 @@ +/* + * Copyright (C) 2020 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <byteswap.h> +#include <ipxe/netdevice.h> +#include <ipxe/vlan.h> +#include <ipxe/tcpip.h> +#include <ipxe/uri.h> +#include <ipxe/iscsi.h> +#include <ipxe/aoe.h> +#include <ipxe/fcp.h> +#include <ipxe/ib_srp.h> +#include <ipxe/usb.h> +#include <ipxe/efi/efi.h> +#include <ipxe/efi/efi_driver.h> +#include <ipxe/efi/efi_path.h> + +/** @file + * + * EFI device paths + * + */ + +/** + * Find end of device path + * + * @v path Path to device + * @ret path_end End of device path + */ +EFI_DEVICE_PATH_PROTOCOL * efi_path_end ( EFI_DEVICE_PATH_PROTOCOL *path ) { + + while ( path->Type != END_DEVICE_PATH_TYPE ) { + path = ( ( ( void * ) path ) + + /* There's this amazing new-fangled thing known as + * a UINT16, but who wants to use one of those? */ + ( ( path->Length[1] << 8 ) | path->Length[0] ) ); + } + + return path; +} + +/** + * Find length of device path (excluding terminator) + * + * @v path Path to device + * @ret path_len Length of device path + */ +size_t efi_path_len ( EFI_DEVICE_PATH_PROTOCOL *path ) { + EFI_DEVICE_PATH_PROTOCOL *end = efi_path_end ( path ); + + return ( ( ( void * ) end ) - ( ( void * ) path ) ); +} + +/** + * Concatenate EFI device paths + * + * @v ... List of device paths (NULL terminated) + * @ret path Concatenated device path, or NULL on error + * + * The caller is responsible for eventually calling free() on the + * allocated device path. + */ +EFI_DEVICE_PATH_PROTOCOL * efi_paths ( EFI_DEVICE_PATH_PROTOCOL *first, ... ) { + EFI_DEVICE_PATH_PROTOCOL *path; + EFI_DEVICE_PATH_PROTOCOL *src; + EFI_DEVICE_PATH_PROTOCOL *dst; + EFI_DEVICE_PATH_PROTOCOL *end; + va_list args; + size_t len; + + /* Calculate device path length */ + va_start ( args, first ); + len = 0; + src = first; + while ( src ) { + len += efi_path_len ( src ); + src = va_arg ( args, EFI_DEVICE_PATH_PROTOCOL * ); + } + va_end ( args ); + + /* Allocate device path */ + path = zalloc ( len + sizeof ( *end ) ); + if ( ! path ) + return NULL; + + /* Populate device path */ + va_start ( args, first ); + dst = path; + src = first; + while ( src ) { + len = efi_path_len ( src ); + memcpy ( dst, src, len ); + dst = ( ( ( void * ) dst ) + len ); + src = va_arg ( args, EFI_DEVICE_PATH_PROTOCOL * ); + } + va_end ( args ); + end = dst; + end->Type = END_DEVICE_PATH_TYPE; + end->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE; + end->Length[0] = sizeof ( *end ); + + return path; +} + +/** + * Construct EFI device path for network device + * + * @v netdev Network device + * @ret path EFI device path, or NULL on error + * + * The caller is responsible for eventually calling free() on the + * allocated device path. + */ +EFI_DEVICE_PATH_PROTOCOL * efi_netdev_path ( struct net_device *netdev ) { + struct efi_device *efidev; + EFI_DEVICE_PATH_PROTOCOL *path; + MAC_ADDR_DEVICE_PATH *macpath; + VLAN_DEVICE_PATH *vlanpath; + EFI_DEVICE_PATH_PROTOCOL *end; + unsigned int tag; + size_t prefix_len; + size_t len; + + /* Find parent EFI device */ + efidev = efidev_parent ( netdev->dev ); + if ( ! efidev ) + return NULL; + + /* Calculate device path length */ + prefix_len = efi_path_len ( efidev->path ); + len = ( prefix_len + sizeof ( *macpath ) + sizeof ( *vlanpath ) + + sizeof ( *end ) ); + + /* Allocate device path */ + path = zalloc ( len ); + if ( ! path ) + return NULL; + + /* Construct device path */ + memcpy ( path, efidev->path, prefix_len ); + macpath = ( ( ( void * ) path ) + prefix_len ); + macpath->Header.Type = MESSAGING_DEVICE_PATH; + macpath->Header.SubType = MSG_MAC_ADDR_DP; + macpath->Header.Length[0] = sizeof ( *macpath ); + assert ( netdev->ll_protocol->ll_addr_len < + sizeof ( macpath->MacAddress ) ); + memcpy ( &macpath->MacAddress, netdev->ll_addr, + netdev->ll_protocol->ll_addr_len ); + macpath->IfType = ntohs ( netdev->ll_protocol->ll_proto ); + if ( ( tag = vlan_tag ( netdev ) ) ) { + vlanpath = ( ( ( void * ) macpath ) + sizeof ( *macpath ) ); + vlanpath->Header.Type = MESSAGING_DEVICE_PATH; + vlanpath->Header.SubType = MSG_VLAN_DP; + vlanpath->Header.Length[0] = sizeof ( *vlanpath ); + vlanpath->VlanId = tag; + end = ( ( ( void * ) vlanpath ) + sizeof ( *vlanpath ) ); + } else { + end = ( ( ( void * ) macpath ) + sizeof ( *macpath ) ); + } + end->Type = END_DEVICE_PATH_TYPE; + end->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE; + end->Length[0] = sizeof ( *end ); + + return path; +} + +/** + * Construct EFI device path for URI + * + * @v uri URI + * @ret path EFI device path, or NULL on error + * + * The caller is responsible for eventually calling free() on the + * allocated device path. + */ +EFI_DEVICE_PATH_PROTOCOL * efi_uri_path ( struct uri *uri ) { + EFI_DEVICE_PATH_PROTOCOL *path; + EFI_DEVICE_PATH_PROTOCOL *end; + URI_DEVICE_PATH *uripath; + size_t uri_len; + size_t uripath_len; + size_t len; + + /* Calculate device path length */ + uri_len = ( format_uri ( uri, NULL, 0 ) + 1 /* NUL */ ); + uripath_len = ( sizeof ( *uripath ) + uri_len ); + len = ( uripath_len + sizeof ( *end ) ); + + /* Allocate device path */ + path = zalloc ( len ); + if ( ! path ) + return NULL; + + /* Construct device path */ + uripath = ( ( void * ) path ); + uripath->Header.Type = MESSAGING_DEVICE_PATH; + uripath->Header.SubType = MSG_URI_DP; + uripath->Header.Length[0] = ( uripath_len & 0xff ); + uripath->Header.Length[1] = ( uripath_len >> 8 ); + format_uri ( uri, uripath->Uri, uri_len ); + end = ( ( ( void * ) path ) + uripath_len ); + end->Type = END_DEVICE_PATH_TYPE; + end->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE; + end->Length[0] = sizeof ( *end ); + + return path; +} + +/** + * Construct EFI device path for iSCSI device + * + * @v iscsi iSCSI session + * @ret path EFI device path, or NULL on error + */ +EFI_DEVICE_PATH_PROTOCOL * efi_iscsi_path ( struct iscsi_session *iscsi ) { + struct sockaddr_tcpip *st_target; + struct net_device *netdev; + EFI_DEVICE_PATH_PROTOCOL *netpath; + EFI_DEVICE_PATH_PROTOCOL *path; + EFI_DEVICE_PATH_PROTOCOL *end; + ISCSI_DEVICE_PATH *iscsipath; + char *name; + size_t prefix_len; + size_t name_len; + size_t iscsi_len; + size_t len; + + /* Get network device associated with target address */ + st_target = ( ( struct sockaddr_tcpip * ) &iscsi->target_sockaddr ); + netdev = tcpip_netdev ( st_target ); + if ( ! netdev ) + goto err_netdev; + + /* Get network device path */ + netpath = efi_netdev_path ( netdev ); + if ( ! netpath ) + goto err_netpath; + + /* Calculate device path length */ + prefix_len = efi_path_len ( netpath ); + name_len = ( strlen ( iscsi->target_iqn ) + 1 /* NUL */ ); + iscsi_len = ( sizeof ( *iscsipath ) + name_len ); + len = ( prefix_len + iscsi_len + sizeof ( *end ) ); + + /* Allocate device path */ + path = zalloc ( len ); + if ( ! path ) + goto err_alloc; + + /* Construct device path */ + memcpy ( path, netpath, prefix_len ); + iscsipath = ( ( ( void * ) path ) + prefix_len ); + iscsipath->Header.Type = MESSAGING_DEVICE_PATH; + iscsipath->Header.SubType = MSG_ISCSI_DP; + iscsipath->Header.Length[0] = iscsi_len; + iscsipath->LoginOption = ISCSI_LOGIN_OPTION_AUTHMETHOD_NON; + memcpy ( &iscsipath->Lun, &iscsi->lun, sizeof ( iscsipath->Lun ) ); + name = ( ( ( void * ) iscsipath ) + sizeof ( *iscsipath ) ); + memcpy ( name, iscsi->target_iqn, name_len ); + end = ( ( ( void * ) name ) + name_len ); + end->Type = END_DEVICE_PATH_TYPE; + end->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE; + end->Length[0] = sizeof ( *end ); + + /* Free temporary paths */ + free ( netpath ); + + return path; + + err_alloc: + free ( netpath ); + err_netpath: + err_netdev: + return NULL; +} + +/** + * Construct EFI device path for AoE device + * + * @v aoedev AoE device + * @ret path EFI device path, or NULL on error + */ +EFI_DEVICE_PATH_PROTOCOL * efi_aoe_path ( struct aoe_device *aoedev ) { + struct { + SATA_DEVICE_PATH sata; + EFI_DEVICE_PATH_PROTOCOL end; + } satapath; + EFI_DEVICE_PATH_PROTOCOL *netpath; + EFI_DEVICE_PATH_PROTOCOL *path; + + /* Get network device path */ + netpath = efi_netdev_path ( aoedev->netdev ); + if ( ! netpath ) + goto err_netdev; + + /* Construct SATA path */ + memset ( &satapath, 0, sizeof ( satapath ) ); + satapath.sata.Header.Type = MESSAGING_DEVICE_PATH; + satapath.sata.Header.SubType = MSG_SATA_DP; + satapath.sata.Header.Length[0] = sizeof ( satapath.sata ); + satapath.sata.HBAPortNumber = aoedev->major; + satapath.sata.PortMultiplierPortNumber = aoedev->minor; + satapath.end.Type = END_DEVICE_PATH_TYPE; + satapath.end.SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE; + satapath.end.Length[0] = sizeof ( satapath.end ); + + /* Construct overall device path */ + path = efi_paths ( netpath, &satapath, NULL ); + if ( ! path ) + goto err_paths; + + /* Free temporary paths */ + free ( netpath ); + + return path; + + err_paths: + free ( netpath ); + err_netdev: + return NULL; +} + +/** + * Construct EFI device path for Fibre Channel device + * + * @v desc FCP device description + * @ret path EFI device path, or NULL on error + */ +EFI_DEVICE_PATH_PROTOCOL * efi_fcp_path ( struct fcp_description *desc ) { + struct { + FIBRECHANNELEX_DEVICE_PATH fc; + EFI_DEVICE_PATH_PROTOCOL end; + } __attribute__ (( packed )) *path; + + /* Allocate device path */ + path = zalloc ( sizeof ( *path ) ); + if ( ! path ) + return NULL; + + /* Construct device path */ + path->fc.Header.Type = MESSAGING_DEVICE_PATH; + path->fc.Header.SubType = MSG_FIBRECHANNELEX_DP; + path->fc.Header.Length[0] = sizeof ( path->fc ); + memcpy ( path->fc.WWN, &desc->wwn, sizeof ( path->fc.WWN ) ); + memcpy ( path->fc.Lun, &desc->lun, sizeof ( path->fc.Lun ) ); + path->end.Type = END_DEVICE_PATH_TYPE; + path->end.SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE; + path->end.Length[0] = sizeof ( path->end ); + + return &path->fc.Header; +} + +/** + * Construct EFI device path for Infiniband SRP device + * + * @v ib_srp Infiniband SRP device + * @ret path EFI device path, or NULL on error + */ +EFI_DEVICE_PATH_PROTOCOL * efi_ib_srp_path ( struct ib_srp_device *ib_srp ) { + const struct ipxe_ib_sbft *sbft = &ib_srp->sbft; + union ib_srp_target_port_id *id = + container_of ( &sbft->srp.target, union ib_srp_target_port_id, + srp ); + struct efi_device *efidev; + EFI_DEVICE_PATH_PROTOCOL *path; + INFINIBAND_DEVICE_PATH *ibpath; + EFI_DEVICE_PATH_PROTOCOL *end; + size_t prefix_len; + size_t len; + + /* Find parent EFI device */ + efidev = efidev_parent ( ib_srp->ibdev->dev ); + if ( ! efidev ) + return NULL; + + /* Calculate device path length */ + prefix_len = efi_path_len ( efidev->path ); + len = ( prefix_len + sizeof ( *ibpath ) + sizeof ( *end ) ); + + /* Allocate device path */ + path = zalloc ( len ); + if ( ! path ) + return NULL; + + /* Construct device path */ + memcpy ( path, efidev->path, prefix_len ); + ibpath = ( ( ( void * ) path ) + prefix_len ); + ibpath->Header.Type = MESSAGING_DEVICE_PATH; + ibpath->Header.SubType = MSG_INFINIBAND_DP; + ibpath->Header.Length[0] = sizeof ( *ibpath ); + ibpath->ResourceFlags = INFINIBAND_RESOURCE_FLAG_STORAGE_PROTOCOL; + memcpy ( ibpath->PortGid, &sbft->ib.dgid, sizeof ( ibpath->PortGid ) ); + memcpy ( &ibpath->ServiceId, &sbft->ib.service_id, + sizeof ( ibpath->ServiceId ) ); + memcpy ( &ibpath->TargetPortId, &id->ib.ioc_guid, + sizeof ( ibpath->TargetPortId ) ); + memcpy ( &ibpath->DeviceId, &id->ib.id_ext, + sizeof ( ibpath->DeviceId ) ); + end = ( ( ( void * ) ibpath ) + sizeof ( *ibpath ) ); + end->Type = END_DEVICE_PATH_TYPE; + end->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE; + end->Length[0] = sizeof ( *end ); + + return path; +} + +/** + * Construct EFI device path for USB function + * + * @v func USB function + * @ret path EFI device path, or NULL on error + * + * The caller is responsible for eventually calling free() on the + * allocated device path. + */ +EFI_DEVICE_PATH_PROTOCOL * efi_usb_path ( struct usb_function *func ) { + struct usb_device *usb = func->usb; + struct efi_device *efidev; + EFI_DEVICE_PATH_PROTOCOL *path; + EFI_DEVICE_PATH_PROTOCOL *end; + USB_DEVICE_PATH *usbpath; + unsigned int count; + size_t prefix_len; + size_t len; + + /* Sanity check */ + assert ( func->desc.count >= 1 ); + + /* Find parent EFI device */ + efidev = efidev_parent ( &func->dev ); + if ( ! efidev ) + return NULL; + + /* Calculate device path length */ + count = ( usb_depth ( usb ) + 1 ); + prefix_len = efi_path_len ( efidev->path ); + len = ( prefix_len + ( count * sizeof ( *usbpath ) ) + + sizeof ( *end ) ); + + /* Allocate device path */ + path = zalloc ( len ); + if ( ! path ) + return NULL; + + /* Construct device path */ + memcpy ( path, efidev->path, prefix_len ); + end = ( ( ( void * ) path ) + len - sizeof ( *end ) ); + end->Type = END_DEVICE_PATH_TYPE; + end->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE; + end->Length[0] = sizeof ( *end ); + usbpath = ( ( ( void * ) end ) - sizeof ( *usbpath ) ); + usbpath->InterfaceNumber = func->interface[0]; + for ( ; usb ; usbpath--, usb = usb->port->hub->usb ) { + usbpath->Header.Type = MESSAGING_DEVICE_PATH; + usbpath->Header.SubType = MSG_USB_DP; + usbpath->Header.Length[0] = sizeof ( *usbpath ); + usbpath->ParentPortNumber = ( usb->port->address - 1 ); + } + + return path; +} + +/** + * Describe object as an EFI device path + * + * @v intf Interface + * @ret path EFI device path, or NULL + * + * The caller is responsible for eventually calling free() on the + * allocated device path. + */ +EFI_DEVICE_PATH_PROTOCOL * efi_describe ( struct interface *intf ) { + struct interface *dest; + efi_describe_TYPE ( void * ) *op = + intf_get_dest_op ( intf, efi_describe, &dest ); + void *object = intf_object ( dest ); + EFI_DEVICE_PATH_PROTOCOL *path; + + if ( op ) { + path = op ( object ); + } else { + path = NULL; + } + + intf_put ( dest ); + return path; +} diff --git a/src/interface/efi/efi_pci.c b/src/interface/efi/efi_pci.c index c1f451c99..4adee0fd8 100644 --- a/src/interface/efi/efi_pci.c +++ b/src/interface/efi/efi_pci.c @@ -26,6 +26,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdlib.h> #include <errno.h> #include <ipxe/pci.h> +#include <ipxe/acpi.h> #include <ipxe/efi/efi.h> #include <ipxe/efi/efi_pci.h> #include <ipxe/efi/efi_driver.h> @@ -62,15 +63,79 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ /** - * Locate EFI PCI root bridge I/O protocol + * Check for a matching PCI root bridge I/O protocol + * + * @v pci PCI device + * @v handle EFI PCI root bridge handle + * @v root EFI PCI root bridge I/O protocol + * @ret rc Return status code + */ +static int efipci_root_match ( struct pci_device *pci, EFI_HANDLE handle, + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *root ) { + union { + union acpi_resource *res; + void *raw; + } u; + unsigned int segment = PCI_SEG ( pci->busdevfn ); + unsigned int bus = PCI_BUS ( pci->busdevfn ); + unsigned int start; + unsigned int end; + unsigned int tag; + EFI_STATUS efirc; + int rc; + + /* Check segment number */ + if ( root->SegmentNumber != segment ) + return -ENOENT; + + /* Get ACPI resource descriptors */ + if ( ( efirc = root->Configuration ( root, &u.raw ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( pci, "EFIPCI " PCI_FMT " cannot get configuration for " + "%s: %s\n", PCI_ARGS ( pci ), + efi_handle_name ( handle ), strerror ( rc ) ); + return rc; + } + + /* Assume success if no bus number range descriptors are found */ + rc = 0; + + /* Parse resource descriptors */ + for ( ; ( ( tag = acpi_resource_tag ( u.res ) ) != ACPI_END_RESOURCE ) ; + u.res = acpi_resource_next ( u.res ) ) { + + /* Ignore anything other than a bus number range descriptor */ + if ( tag != ACPI_QWORD_ADDRESS_SPACE_RESOURCE ) + continue; + if ( u.res->qword.type != ACPI_ADDRESS_TYPE_BUS ) + continue; + + /* Check for a matching bus number */ + start = le64_to_cpu ( u.res->qword.min ); + end = ( start + le64_to_cpu ( u.res->qword.len ) ); + if ( ( bus >= start ) && ( bus < end ) ) + return 0; + + /* We have seen at least one non-matching range + * descriptor, so assume failure unless we find a + * subsequent match. + */ + rc = -ENOENT; + } + + return rc; +} + +/** + * Open EFI PCI root bridge I/O protocol * * @v pci PCI device * @ret handle EFI PCI root bridge handle * @ret root EFI PCI root bridge I/O protocol, or NULL if not found * @ret rc Return status code */ -static int efipci_root ( struct pci_device *pci, EFI_HANDLE *handle, - EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL **root ) { +static int efipci_root_open ( struct pci_device *pci, EFI_HANDLE *handle, + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL **root ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; EFI_HANDLE *handles; UINTN num_handles; @@ -105,7 +170,7 @@ static int efipci_root ( struct pci_device *pci, EFI_HANDLE *handle, strerror ( rc ) ); continue; } - if ( u.root->SegmentNumber == PCI_SEG ( pci->busdevfn ) ) { + if ( efipci_root_match ( pci, *handle, u.root ) == 0 ) { *root = u.root; bs->FreePool ( handles ); return 0; @@ -124,6 +189,19 @@ static int efipci_root ( struct pci_device *pci, EFI_HANDLE *handle, } /** + * Close EFI PCI root bridge I/O protocol + * + * @v handle EFI PCI root bridge handle + */ +static void efipci_root_close ( EFI_HANDLE handle ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + + /* Close protocol */ + bs->CloseProtocol ( handle, &efi_pci_root_bridge_io_protocol_guid, + efi_image_handle, handle ); +} + +/** * Calculate EFI PCI configuration space address * * @v pci PCI device @@ -149,14 +227,13 @@ static unsigned long efipci_address ( struct pci_device *pci, */ int efipci_read ( struct pci_device *pci, unsigned long location, void *value ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *root; EFI_HANDLE handle; EFI_STATUS efirc; int rc; - /* Identify root bridge */ - if ( ( rc = efipci_root ( pci, &handle, &root ) ) != 0 ) + /* Open root bridge */ + if ( ( rc = efipci_root_open ( pci, &handle, &root ) ) != 0 ) goto err_root; /* Read from configuration space */ @@ -171,8 +248,7 @@ int efipci_read ( struct pci_device *pci, unsigned long location, } err_read: - bs->CloseProtocol ( handle, &efi_pci_root_bridge_io_protocol_guid, - efi_image_handle, handle ); + efipci_root_close ( handle ); err_root: return rc; } @@ -187,14 +263,13 @@ int efipci_read ( struct pci_device *pci, unsigned long location, */ int efipci_write ( struct pci_device *pci, unsigned long location, unsigned long value ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *root; EFI_HANDLE handle; EFI_STATUS efirc; int rc; - /* Identify root bridge */ - if ( ( rc = efipci_root ( pci, &handle, &root ) ) != 0 ) + /* Open root bridge */ + if ( ( rc = efipci_root_open ( pci, &handle, &root ) ) != 0 ) goto err_root; /* Read from configuration space */ @@ -209,12 +284,81 @@ int efipci_write ( struct pci_device *pci, unsigned long location, } err_write: - bs->CloseProtocol ( handle, &efi_pci_root_bridge_io_protocol_guid, - efi_image_handle, handle ); + efipci_root_close ( handle ); err_root: return rc; } +/** + * Map PCI bus address as an I/O address + * + * @v bus_addr PCI bus address + * @v len Length of region + * @ret io_addr I/O address, or NULL on error + */ +void * efipci_ioremap ( struct pci_device *pci, unsigned long bus_addr, + size_t len ) { + union { + union acpi_resource *res; + void *raw; + } u; + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *root; + EFI_HANDLE handle; + unsigned int tag; + uint64_t offset; + uint64_t start; + uint64_t end; + EFI_STATUS efirc; + int rc; + + /* Open root bridge */ + if ( ( rc = efipci_root_open ( pci, &handle, &root ) ) != 0 ) + goto err_root; + + /* Get ACPI resource descriptors */ + if ( ( efirc = root->Configuration ( root, &u.raw ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( pci, "EFIPCI " PCI_FMT " cannot get configuration: " + "%s\n", PCI_ARGS ( pci ), strerror ( rc ) ); + goto err_config; + } + + /* Parse resource descriptors */ + for ( ; ( ( tag = acpi_resource_tag ( u.res ) ) != ACPI_END_RESOURCE ) ; + u.res = acpi_resource_next ( u.res ) ) { + + /* Ignore anything other than a memory range descriptor */ + if ( tag != ACPI_QWORD_ADDRESS_SPACE_RESOURCE ) + continue; + if ( u.res->qword.type != ACPI_ADDRESS_TYPE_MEM ) + continue; + + /* Ignore descriptors that do not cover this memory range */ + offset = le64_to_cpu ( u.res->qword.offset ); + start = ( offset + le64_to_cpu ( u.res->qword.min ) ); + end = ( start + le64_to_cpu ( u.res->qword.len ) ); + if ( ( bus_addr < start ) || ( ( bus_addr + len ) > end ) ) + continue; + + /* Use this address space descriptor */ + DBGC2 ( pci, "EFIPCI " PCI_FMT " %08lx+%zx -> ", + PCI_ARGS ( pci ), bus_addr, len ); + bus_addr -= offset; + DBGC2 ( pci, "%08lx\n", bus_addr ); + break; + } + if ( tag == ACPI_END_RESOURCE ) { + DBGC ( pci, "EFIPCI " PCI_FMT " %08lx+%zx is not within " + "root bridge address space\n", + PCI_ARGS ( pci ), bus_addr, len ); + } + + err_config: + efipci_root_close ( handle ); + err_root: + return ioremap ( bus_addr, len ); +} + PROVIDE_PCIAPI_INLINE ( efi, pci_num_bus ); PROVIDE_PCIAPI_INLINE ( efi, pci_read_config_byte ); PROVIDE_PCIAPI_INLINE ( efi, pci_read_config_word ); @@ -222,6 +366,282 @@ PROVIDE_PCIAPI_INLINE ( efi, pci_read_config_dword ); PROVIDE_PCIAPI_INLINE ( efi, pci_write_config_byte ); PROVIDE_PCIAPI_INLINE ( efi, pci_write_config_word ); PROVIDE_PCIAPI_INLINE ( efi, pci_write_config_dword ); +PROVIDE_PCIAPI ( efi, pci_ioremap, efipci_ioremap ); + +/****************************************************************************** + * + * EFI PCI DMA mappings + * + ****************************************************************************** + */ + +/** + * Map buffer for DMA + * + * @v dma DMA device + * @v map DMA mapping to fill in + * @v addr Buffer address + * @v len Length of buffer + * @v flags Mapping flags + * @ret rc Return status code + */ +static int efipci_dma_map ( struct dma_device *dma, struct dma_mapping *map, + physaddr_t addr, size_t len, int flags ) { + struct efi_pci_device *efipci = + container_of ( dma, struct efi_pci_device, pci.dma ); + struct pci_device *pci = &efipci->pci; + EFI_PCI_IO_PROTOCOL *pci_io = efipci->io; + EFI_PCI_IO_PROTOCOL_OPERATION op; + EFI_PHYSICAL_ADDRESS bus; + UINTN count; + VOID *mapping; + EFI_STATUS efirc; + int rc; + + /* Sanity check */ + assert ( map->dma == NULL ); + assert ( map->offset == 0 ); + assert ( map->token == NULL ); + + /* Determine operation */ + switch ( flags ) { + case DMA_TX: + op = EfiPciIoOperationBusMasterRead; + break; + case DMA_RX: + op = EfiPciIoOperationBusMasterWrite; + break; + default: + op = EfiPciIoOperationBusMasterCommonBuffer; + break; + } + + /* Map buffer (if non-zero length) */ + count = len; + if ( len ) { + if ( ( efirc = pci_io->Map ( pci_io, op, phys_to_virt ( addr ), + &count, &bus, &mapping ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( pci, "EFIPCI " PCI_FMT " cannot map %08lx+%zx: " + "%s\n", PCI_ARGS ( pci ), addr, len, + strerror ( rc ) ); + goto err_map; + } + } else { + bus = addr; + mapping = NULL; + } + + /* Check that full length was mapped. The UEFI specification + * allows for multiple mappings to be required, but even the + * EDK2 PCI device drivers will fail if a platform ever + * requires this. + */ + if ( count != len ) { + DBGC ( pci, "EFIPCI " PCI_FMT " attempted split mapping for " + "%08lx+%zx\n", PCI_ARGS ( pci ), addr, len ); + rc = -ENOTSUP; + goto err_len; + } + + /* Populate mapping */ + map->dma = dma; + map->offset = ( bus - addr ); + map->token = mapping; + + /* Increment mapping count (for debugging) */ + if ( DBG_LOG ) + dma->mapped++; + + return 0; + + err_len: + pci_io->Unmap ( pci_io, mapping ); + err_map: + return rc; +} + +/** + * Unmap buffer + * + * @v dma DMA device + * @v map DMA mapping + */ +static void efipci_dma_unmap ( struct dma_device *dma, + struct dma_mapping *map ) { + struct efi_pci_device *efipci = + container_of ( dma, struct efi_pci_device, pci.dma ); + EFI_PCI_IO_PROTOCOL *pci_io = efipci->io; + + /* Unmap buffer (if non-zero length) */ + if ( map->token ) + pci_io->Unmap ( pci_io, map->token ); + + /* Clear mapping */ + map->dma = NULL; + map->offset = 0; + map->token = NULL; + + /* Decrement mapping count (for debugging) */ + if ( DBG_LOG ) + dma->mapped--; +} + +/** + * Allocate and map DMA-coherent buffer + * + * @v dma DMA device + * @v map DMA mapping to fill in + * @v len Length of buffer + * @v align Physical alignment + * @ret addr Buffer address, or NULL on error + */ +static void * efipci_dma_alloc ( struct dma_device *dma, + struct dma_mapping *map, + size_t len, size_t align __unused ) { + struct efi_pci_device *efipci = + container_of ( dma, struct efi_pci_device, pci.dma ); + struct pci_device *pci = &efipci->pci; + EFI_PCI_IO_PROTOCOL *pci_io = efipci->io; + unsigned int pages; + VOID *addr; + EFI_STATUS efirc; + int rc; + + /* Calculate number of pages */ + pages = ( ( len + EFI_PAGE_SIZE - 1 ) / EFI_PAGE_SIZE ); + + /* Allocate (page-aligned) buffer */ + if ( ( efirc = pci_io->AllocateBuffer ( pci_io, AllocateAnyPages, + EfiBootServicesData, pages, + &addr, 0 ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( pci, "EFIPCI " PCI_FMT " could not allocate %zd bytes: " + "%s\n", PCI_ARGS ( pci ), len, strerror ( rc ) ); + goto err_alloc; + } + + /* Map buffer */ + if ( ( rc = efipci_dma_map ( dma, map, virt_to_phys ( addr ), + len, DMA_BI ) ) != 0 ) + goto err_map; + + /* Increment allocation count (for debugging) */ + if ( DBG_LOG ) + dma->allocated++; + + return addr; + + efipci_dma_unmap ( dma, map ); + err_map: + pci_io->FreeBuffer ( pci_io, pages, addr ); + err_alloc: + return NULL; +} + +/** + * Unmap and free DMA-coherent buffer + * + * @v dma DMA device + * @v map DMA mapping + * @v addr Buffer address + * @v len Length of buffer + */ +static void efipci_dma_free ( struct dma_device *dma, struct dma_mapping *map, + void *addr, size_t len ) { + struct efi_pci_device *efipci = + container_of ( dma, struct efi_pci_device, pci.dma ); + EFI_PCI_IO_PROTOCOL *pci_io = efipci->io; + unsigned int pages; + + /* Calculate number of pages */ + pages = ( ( len + EFI_PAGE_SIZE - 1 ) / EFI_PAGE_SIZE ); + + /* Unmap buffer */ + efipci_dma_unmap ( dma, map ); + + /* Free buffer */ + pci_io->FreeBuffer ( pci_io, pages, addr ); + + /* Decrement allocation count (for debugging) */ + if ( DBG_LOG ) + dma->allocated--; +} + +/** + * Allocate and map DMA-coherent buffer from external (user) memory + * + * @v dma DMA device + * @v map DMA mapping to fill in + * @v len Length of buffer + * @v align Physical alignment + * @ret addr Buffer address, or NULL on error + */ +static userptr_t efipci_dma_umalloc ( struct dma_device *dma, + struct dma_mapping *map, + size_t len, size_t align ) { + void *addr; + + addr = efipci_dma_alloc ( dma, map, len, align ); + return virt_to_user ( addr ); +} + +/** + * Unmap and free DMA-coherent buffer from external (user) memory + * + * @v dma DMA device + * @v map DMA mapping + * @v addr Buffer address + * @v len Length of buffer + */ +static void efipci_dma_ufree ( struct dma_device *dma, struct dma_mapping *map, + userptr_t addr, size_t len ) { + + efipci_dma_free ( dma, map, user_to_virt ( addr, 0 ), len ); +} + +/** + * Set addressable space mask + * + * @v dma DMA device + * @v mask Addressable space mask + */ +static void efipci_dma_set_mask ( struct dma_device *dma, physaddr_t mask ) { + struct efi_pci_device *efipci = + container_of ( dma, struct efi_pci_device, pci.dma ); + struct pci_device *pci = &efipci->pci; + EFI_PCI_IO_PROTOCOL *pci_io = efipci->io; + EFI_PCI_IO_PROTOCOL_ATTRIBUTE_OPERATION op; + UINT64 attrs; + int is64; + EFI_STATUS efirc; + int rc; + + /* Set dual address cycle attribute for 64-bit capable devices */ + is64 = ( ( ( ( uint64_t ) mask ) + 1 ) == 0 ); + op = ( is64 ? EfiPciIoAttributeOperationEnable : + EfiPciIoAttributeOperationDisable ); + attrs = EFI_PCI_IO_ATTRIBUTE_DUAL_ADDRESS_CYCLE; + if ( ( efirc = pci_io->Attributes ( pci_io, op, attrs, NULL ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( pci, "EFIPCI " PCI_FMT " could not %sable DAC: %s\n", + PCI_ARGS ( pci ), ( is64 ? "en" : "dis" ), + strerror ( rc ) ); + /* Ignore failure: errors will manifest in mapping attempts */ + return; + } +} + +/** EFI PCI DMA operations */ +static struct dma_operations efipci_dma_operations = { + .map = efipci_dma_map, + .unmap = efipci_dma_unmap, + .alloc = efipci_dma_alloc, + .free = efipci_dma_free, + .umalloc = efipci_dma_umalloc, + .ufree = efipci_dma_ufree, + .set_mask = efipci_dma_set_mask, +}; /****************************************************************************** * @@ -235,11 +655,11 @@ PROVIDE_PCIAPI_INLINE ( efi, pci_write_config_dword ); * * @v device EFI device handle * @v attributes Protocol opening attributes - * @v pci PCI device to fill in + * @v efipci EFI PCI device to fill in * @ret rc Return status code */ int efipci_open ( EFI_HANDLE device, UINT32 attributes, - struct pci_device *pci ) { + struct efi_pci_device *efipci ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; union { EFI_PCI_IO_PROTOCOL *pci_io; @@ -259,6 +679,7 @@ int efipci_open ( EFI_HANDLE device, UINT32 attributes, efi_handle_name ( device ), strerror ( rc ) ); goto err_open_protocol; } + efipci->io = pci_io.pci_io; /* Get PCI bus:dev.fn address */ if ( ( efirc = pci_io.pci_io->GetLocation ( pci_io.pci_io, &pci_segment, @@ -270,9 +691,10 @@ int efipci_open ( EFI_HANDLE device, UINT32 attributes, goto err_get_location; } busdevfn = PCI_BUSDEVFN ( pci_segment, pci_bus, pci_dev, pci_fn ); - pci_init ( pci, busdevfn ); + pci_init ( &efipci->pci, busdevfn ); + dma_init ( &efipci->pci.dma, &efipci_dma_operations ); DBGCP ( device, "EFIPCI " PCI_FMT " is %s\n", - PCI_ARGS ( pci ), efi_handle_name ( device ) ); + PCI_ARGS ( &efipci->pci ), efi_handle_name ( device ) ); /* Try to enable I/O cycles, memory cycles, and bus mastering. * Some platforms will 'helpfully' report errors if these bits @@ -291,10 +713,10 @@ int efipci_open ( EFI_HANDLE device, UINT32 attributes, EFI_PCI_IO_ATTRIBUTE_BUS_MASTER, NULL ); /* Populate PCI device */ - if ( ( rc = pci_read_config ( pci ) ) != 0 ) { + if ( ( rc = pci_read_config ( &efipci->pci ) ) != 0 ) { DBGC ( device, "EFIPCI " PCI_FMT " cannot read PCI " "configuration: %s\n", - PCI_ARGS ( pci ), strerror ( rc ) ); + PCI_ARGS ( &efipci->pci ), strerror ( rc ) ); goto err_pci_read_config; } @@ -324,15 +746,15 @@ void efipci_close ( EFI_HANDLE device ) { * Get EFI PCI device information * * @v device EFI device handle - * @v pci PCI device to fill in + * @v efipci EFI PCI device to fill in * @ret rc Return status code */ -int efipci_info ( EFI_HANDLE device, struct pci_device *pci ) { +int efipci_info ( EFI_HANDLE device, struct efi_pci_device *efipci ) { int rc; /* Open PCI device, if possible */ if ( ( rc = efipci_open ( device, EFI_OPEN_PROTOCOL_GET_PROTOCOL, - pci ) ) != 0 ) + efipci ) ) != 0 ) return rc; /* Close PCI device */ @@ -355,23 +777,24 @@ int efipci_info ( EFI_HANDLE device, struct pci_device *pci ) { * @ret rc Return status code */ static int efipci_supported ( EFI_HANDLE device ) { - struct pci_device pci; + struct efi_pci_device efipci; int rc; /* Get PCI device information */ - if ( ( rc = efipci_info ( device, &pci ) ) != 0 ) + if ( ( rc = efipci_info ( device, &efipci ) ) != 0 ) return rc; /* Look for a driver */ - if ( ( rc = pci_find_driver ( &pci ) ) != 0 ) { + if ( ( rc = pci_find_driver ( &efipci.pci ) ) != 0 ) { DBGC ( device, "EFIPCI " PCI_FMT " (%04x:%04x class %06x) " - "has no driver\n", PCI_ARGS ( &pci ), pci.vendor, - pci.device, pci.class ); + "has no driver\n", PCI_ARGS ( &efipci.pci ), + efipci.pci.vendor, efipci.pci.device, + efipci.pci.class ); return rc; } DBGC ( device, "EFIPCI " PCI_FMT " (%04x:%04x class %06x) has driver " - "\"%s\"\n", PCI_ARGS ( &pci ), pci.vendor, pci.device, - pci.class, pci.id->name ); + "\"%s\"\n", PCI_ARGS ( &efipci.pci ), efipci.pci.vendor, + efipci.pci.device, efipci.pci.class, efipci.pci.id->name ); return 0; } @@ -384,12 +807,12 @@ static int efipci_supported ( EFI_HANDLE device ) { */ static int efipci_start ( struct efi_device *efidev ) { EFI_HANDLE device = efidev->device; - struct pci_device *pci; + struct efi_pci_device *efipci; int rc; /* Allocate PCI device */ - pci = zalloc ( sizeof ( *pci ) ); - if ( ! pci ) { + efipci = zalloc ( sizeof ( *efipci ) ); + if ( ! efipci ) { rc = -ENOMEM; goto err_alloc; } @@ -397,7 +820,7 @@ static int efipci_start ( struct efi_device *efidev ) { /* Open PCI device */ if ( ( rc = efipci_open ( device, ( EFI_OPEN_PROTOCOL_BY_DRIVER | EFI_OPEN_PROTOCOL_EXCLUSIVE ), - pci ) ) != 0 ) { + efipci ) ) != 0 ) { DBGC ( device, "EFIPCI %s could not open PCI device: %s\n", efi_handle_name ( device ), strerror ( rc ) ); DBGC_EFI_OPENERS ( device, device, &efi_pci_io_protocol_guid ); @@ -405,36 +828,36 @@ static int efipci_start ( struct efi_device *efidev ) { } /* Find driver */ - if ( ( rc = pci_find_driver ( pci ) ) != 0 ) { + if ( ( rc = pci_find_driver ( &efipci->pci ) ) != 0 ) { DBGC ( device, "EFIPCI " PCI_FMT " has no driver\n", - PCI_ARGS ( pci ) ); + PCI_ARGS ( &efipci->pci ) ); goto err_find_driver; } /* Mark PCI device as a child of the EFI device */ - pci->dev.parent = &efidev->dev; - list_add ( &pci->dev.siblings, &efidev->dev.children ); + efipci->pci.dev.parent = &efidev->dev; + list_add ( &efipci->pci.dev.siblings, &efidev->dev.children ); /* Probe driver */ - if ( ( rc = pci_probe ( pci ) ) != 0 ) { + if ( ( rc = pci_probe ( &efipci->pci ) ) != 0 ) { DBGC ( device, "EFIPCI " PCI_FMT " could not probe driver " - "\"%s\": %s\n", PCI_ARGS ( pci ), pci->id->name, - strerror ( rc ) ); + "\"%s\": %s\n", PCI_ARGS ( &efipci->pci ), + efipci->pci.id->name, strerror ( rc ) ); goto err_probe; } DBGC ( device, "EFIPCI " PCI_FMT " using driver \"%s\"\n", - PCI_ARGS ( pci ), pci->id->name ); + PCI_ARGS ( &efipci->pci ), efipci->pci.id->name ); - efidev_set_drvdata ( efidev, pci ); + efidev_set_drvdata ( efidev, efipci ); return 0; - pci_remove ( pci ); + pci_remove ( &efipci->pci ); err_probe: - list_del ( &pci->dev.siblings ); + list_del ( &efipci->pci.dev.siblings ); err_find_driver: efipci_close ( device ); err_open: - free ( pci ); + free ( efipci ); err_alloc: return rc; } @@ -445,13 +868,15 @@ static int efipci_start ( struct efi_device *efidev ) { * @v efidev EFI device */ static void efipci_stop ( struct efi_device *efidev ) { - struct pci_device *pci = efidev_get_drvdata ( efidev ); + struct efi_pci_device *efipci = efidev_get_drvdata ( efidev ); EFI_HANDLE device = efidev->device; - pci_remove ( pci ); - list_del ( &pci->dev.siblings ); + pci_remove ( &efipci->pci ); + list_del ( &efipci->pci.dev.siblings ); + assert ( efipci->pci.dma.mapped == 0 ); + assert ( efipci->pci.dma.allocated == 0 ); efipci_close ( device ); - free ( pci ); + free ( efipci ); } /** EFI PCI driver */ diff --git a/src/interface/efi/efi_pxe.c b/src/interface/efi/efi_pxe.c index a1f81df59..15224a5e4 100644 --- a/src/interface/efi/efi_pxe.c +++ b/src/interface/efi/efi_pxe.c @@ -41,6 +41,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/efi/efi.h> #include <ipxe/efi/efi_snp.h> #include <ipxe/efi/efi_pxe.h> +#include <ipxe/efi/efi_null.h> #include <ipxe/efi/Protocol/PxeBaseCode.h> #include <ipxe/efi/Protocol/AppleNetBoot.h> #include <usr/ifmgmt.h> @@ -755,7 +756,8 @@ static EFI_STATUS EFIAPI efi_pxe_start ( EFI_PXE_BASE_CODE_PROTOCOL *base, sa_family_t family = ( use_ipv6 ? AF_INET6 : AF_INET ); int rc; - DBGC ( pxe, "PXE %s START %s\n", pxe->name, ( ipv6 ? "IPv6" : "IPv4" )); + DBGC ( pxe, "PXE %s START %s\n", + pxe->name, ( use_ipv6 ? "IPv6" : "IPv4" ) ); /* Initialise mode structure */ memset ( mode, 0, sizeof ( *mode ) ); @@ -1591,6 +1593,7 @@ int efi_pxe_install ( EFI_HANDLE handle, struct net_device *netdev ) { struct efi_pxe *pxe; struct in_addr ip; BOOLEAN use_ipv6; + int leak = 0; EFI_STATUS efirc; int rc; @@ -1643,14 +1646,23 @@ int efi_pxe_install ( EFI_HANDLE handle, struct net_device *netdev ) { pxe->name, efi_handle_name ( handle ) ); return 0; - bs->UninstallMultipleProtocolInterfaces ( + if ( ( efirc = bs->UninstallMultipleProtocolInterfaces ( handle, &efi_pxe_base_code_protocol_guid, &pxe->base, &efi_apple_net_boot_protocol_guid, &pxe->apple, - NULL ); + NULL ) ) != 0 ) { + DBGC ( pxe, "PXE %s could not uninstall: %s\n", + pxe->name, strerror ( -EEFI ( efirc ) ) ); + leak = 1; + } + efi_nullify_pxe ( &pxe->base ); + efi_nullify_apple ( &pxe->apple ); err_install_protocol: - ref_put ( &pxe->refcnt ); + if ( ! leak ) + ref_put ( &pxe->refcnt ); err_alloc: + if ( leak ) + DBGC ( pxe, "PXE %s nullified and leaked\n", pxe->name ); return rc; } @@ -1662,6 +1674,8 @@ int efi_pxe_install ( EFI_HANDLE handle, struct net_device *netdev ) { void efi_pxe_uninstall ( EFI_HANDLE handle ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_pxe *pxe; + int leak = efi_shutdown_in_progress; + EFI_STATUS efirc; /* Locate PXE base code */ pxe = efi_pxe_find ( handle ); @@ -1675,13 +1689,25 @@ void efi_pxe_uninstall ( EFI_HANDLE handle ) { efi_pxe_stop ( &pxe->base ); /* Uninstall PXE base code protocol */ - bs->UninstallMultipleProtocolInterfaces ( + if ( ( ! efi_shutdown_in_progress ) && + ( ( efirc = bs->UninstallMultipleProtocolInterfaces ( handle, &efi_pxe_base_code_protocol_guid, &pxe->base, &efi_apple_net_boot_protocol_guid, &pxe->apple, - NULL ); + NULL ) ) != 0 ) ) { + DBGC ( pxe, "PXE %s could not uninstall: %s\n", + pxe->name, strerror ( -EEFI ( efirc ) ) ); + leak = 1; + } + efi_nullify_pxe ( &pxe->base ); + efi_nullify_apple ( &pxe->apple ); /* Remove from list and drop list's reference */ list_del ( &pxe->list ); - ref_put ( &pxe->refcnt ); + if ( ! leak ) + ref_put ( &pxe->refcnt ); + + /* Report leakage, if applicable */ + if ( leak && ( ! efi_shutdown_in_progress ) ) + DBGC ( pxe, "PXE %s nullified and leaked\n", pxe->name ); } diff --git a/src/interface/efi/efi_smbios.c b/src/interface/efi/efi_smbios.c index 304f95a56..d7877b0aa 100644 --- a/src/interface/efi/efi_smbios.c +++ b/src/interface/efi/efi_smbios.c @@ -34,6 +34,10 @@ FILE_LICENCE ( GPL2_OR_LATER ); static struct smbios_entry *smbios_entry; EFI_USE_TABLE ( SMBIOS_TABLE, &smbios_entry, 0 ); +/** SMBIOS configuration table */ +static struct smbios3_entry *smbios3_entry; +EFI_USE_TABLE ( SMBIOS3_TABLE, &smbios3_entry, 0 ); + /** * Find SMBIOS * @@ -42,26 +46,34 @@ EFI_USE_TABLE ( SMBIOS_TABLE, &smbios_entry, 0 ); */ static int efi_find_smbios ( struct smbios *smbios ) { - if ( ! smbios_entry ) { - DBG ( "No SMBIOS table provided\n" ); - return -ENODEV; + /* Use 64-bit table if present */ + if ( smbios3_entry && ( smbios3_entry->signature == SMBIOS3_SIGNATURE ) ) { + smbios->address = phys_to_user ( smbios3_entry->smbios_address ); + smbios->len = smbios3_entry->smbios_len; + smbios->count = 0; + smbios->version = + SMBIOS_VERSION ( smbios3_entry->major, smbios3_entry->minor ); + DBG ( "Found 64-bit SMBIOS v%d.%d entry point at %p (%lx+%zx)\n", + smbios3_entry->major, smbios3_entry->minor, smbios3_entry, + user_to_phys ( smbios->address, 0 ), smbios->len ); + return 0; } - if ( smbios_entry->signature != SMBIOS_SIGNATURE ) { - DBG ( "Invalid SMBIOS signature\n" ); - return -ENODEV; + /* Otherwise, use 32-bit table if present */ + if ( smbios_entry && ( smbios_entry->signature == SMBIOS_SIGNATURE ) ) { + smbios->address = phys_to_user ( smbios_entry->smbios_address ); + smbios->len = smbios_entry->smbios_len; + smbios->count = smbios_entry->smbios_count; + smbios->version = + SMBIOS_VERSION ( smbios_entry->major, smbios_entry->minor ); + DBG ( "Found 32-bit SMBIOS v%d.%d entry point at %p (%lx+%zx)\n", + smbios_entry->major, smbios_entry->minor, smbios_entry, + user_to_phys ( smbios->address, 0 ), smbios->len ); + return 0; } - smbios->address = phys_to_user ( smbios_entry->smbios_address ); - smbios->len = smbios_entry->smbios_len; - smbios->count = smbios_entry->smbios_count; - smbios->version = - SMBIOS_VERSION ( smbios_entry->major, smbios_entry->minor ); - DBG ( "Found SMBIOS v%d.%d entry point at %p (%x+%zx)\n", - smbios_entry->major, smbios_entry->minor, smbios_entry, - smbios_entry->smbios_address, smbios->len ); - - return 0; + DBG ( "No SMBIOS table provided\n" ); + return -ENODEV; } PROVIDE_SMBIOS ( efi, find_smbios, efi_find_smbios ); diff --git a/src/interface/efi/efi_snp.c b/src/interface/efi/efi_snp.c index d648700f6..6649eb1b0 100644 --- a/src/interface/efi/efi_snp.c +++ b/src/interface/efi/efi_snp.c @@ -33,8 +33,10 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <ipxe/efi/efi.h> #include <ipxe/efi/efi_driver.h> #include <ipxe/efi/efi_strings.h> +#include <ipxe/efi/efi_path.h> #include <ipxe/efi/efi_utils.h> #include <ipxe/efi/efi_watchdog.h> +#include <ipxe/efi/efi_null.h> #include <ipxe/efi/efi_snp.h> #include <usr/autoboot.h> #include <config/general.h> @@ -46,7 +48,7 @@ static LIST_HEAD ( efi_snp_devices ); static int efi_snp_claimed; /** TPL prior to network devices being claimed */ -static EFI_TPL efi_snp_old_tpl; +static struct efi_saved_tpl efi_snp_saved_tpl; /* Downgrade user experience if configured to do so * @@ -190,9 +192,11 @@ efi_snp_start ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ) { DBGC ( snpdev, "SNPDEV %p START\n", snpdev ); - /* Fail if net device is currently claimed for use by iPXE */ - if ( efi_snp_claimed ) - return EFI_NOT_READY; + /* Allow start even if net device is currently claimed by iPXE */ + if ( efi_snp_claimed ) { + DBGC ( snpdev, "SNPDEV %p allowing start while claimed\n", + snpdev ); + } snpdev->started = 1; efi_snp_set_state ( snpdev ); @@ -233,24 +237,29 @@ efi_snp_stop ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ) { static EFI_STATUS EFIAPI efi_snp_initialize ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, UINTN extra_rx_bufsize, UINTN extra_tx_bufsize ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_snp_device *snpdev = container_of ( snp, struct efi_snp_device, snp ); - EFI_TPL saved_tpl; + struct efi_saved_tpl tpl; int rc; DBGC ( snpdev, "SNPDEV %p INITIALIZE (%ld extra RX, %ld extra TX)\n", snpdev, ( ( unsigned long ) extra_rx_bufsize ), ( ( unsigned long ) extra_tx_bufsize ) ); - /* Fail if net device is currently claimed for use by iPXE */ + /* Do nothing if net device is currently claimed for use by + * iPXE. Do not return an error, because this will cause + * MnpDxe et al to fail to install the relevant child handles + * and to leave behind a partially initialised device handle + * that can cause a later system crash. + */ if ( efi_snp_claimed ) { - rc = -EAGAIN; - goto err_claimed; + DBGC ( snpdev, "SNPDEV %p ignoring initialization while " + "claimed\n", snpdev ); + return 0; } /* Raise TPL */ - saved_tpl = bs->RaiseTPL ( TPL_CALLBACK ); + efi_raise_tpl ( &tpl ); /* Open network device */ if ( ( rc = netdev_open ( snpdev->netdev ) ) != 0 ) { @@ -261,8 +270,7 @@ efi_snp_initialize ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, efi_snp_set_state ( snpdev ); err_open: - bs->RestoreTPL ( saved_tpl ); - err_claimed: + efi_restore_tpl ( &tpl ); return EFIRC ( rc ); } @@ -275,10 +283,9 @@ efi_snp_initialize ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, */ static EFI_STATUS EFIAPI efi_snp_reset ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN ext_verify ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_snp_device *snpdev = container_of ( snp, struct efi_snp_device, snp ); - EFI_TPL saved_tpl; + struct efi_saved_tpl tpl; int rc; DBGC ( snpdev, "SNPDEV %p RESET (%s extended verification)\n", @@ -291,7 +298,7 @@ efi_snp_reset ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN ext_verify ) { } /* Raise TPL */ - saved_tpl = bs->RaiseTPL ( TPL_CALLBACK ); + efi_raise_tpl ( &tpl ); /* Close network device */ netdev_close ( snpdev->netdev ); @@ -307,7 +314,7 @@ efi_snp_reset ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN ext_verify ) { efi_snp_set_state ( snpdev ); err_open: - bs->RestoreTPL ( saved_tpl ); + efi_restore_tpl ( &tpl ); err_claimed: return EFIRC ( rc ); } @@ -320,10 +327,9 @@ efi_snp_reset ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN ext_verify ) { */ static EFI_STATUS EFIAPI efi_snp_shutdown ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_snp_device *snpdev = container_of ( snp, struct efi_snp_device, snp ); - EFI_TPL saved_tpl; + struct efi_saved_tpl tpl; DBGC ( snpdev, "SNPDEV %p SHUTDOWN\n", snpdev ); @@ -332,7 +338,7 @@ efi_snp_shutdown ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ) { return EFI_NOT_READY; /* Raise TPL */ - saved_tpl = bs->RaiseTPL ( TPL_CALLBACK ); + efi_raise_tpl ( &tpl ); /* Close network device */ netdev_close ( snpdev->netdev ); @@ -340,7 +346,7 @@ efi_snp_shutdown ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ) { efi_snp_flush ( snpdev ); /* Restore TPL */ - bs->RestoreTPL ( saved_tpl ); + efi_restore_tpl ( &tpl ); return 0; } @@ -544,10 +550,9 @@ efi_snp_nvdata ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN read, static EFI_STATUS EFIAPI efi_snp_get_status ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, UINT32 *interrupts, VOID **txbuf ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_snp_device *snpdev = container_of ( snp, struct efi_snp_device, snp ); - EFI_TPL saved_tpl; + struct efi_saved_tpl tpl; DBGC2 ( snpdev, "SNPDEV %p GET_STATUS", snpdev ); @@ -558,7 +563,7 @@ efi_snp_get_status ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, } /* Raise TPL */ - saved_tpl = bs->RaiseTPL ( TPL_CALLBACK ); + efi_raise_tpl ( &tpl ); /* Poll the network device */ efi_snp_poll ( snpdev ); @@ -583,7 +588,7 @@ efi_snp_get_status ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, } /* Restore TPL */ - bs->RestoreTPL ( saved_tpl ); + efi_restore_tpl ( &tpl ); DBGC2 ( snpdev, "\n" ); return 0; @@ -606,14 +611,13 @@ efi_snp_transmit ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, UINTN ll_header_len, UINTN len, VOID *data, EFI_MAC_ADDRESS *ll_src, EFI_MAC_ADDRESS *ll_dest, UINT16 *net_proto ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_snp_device *snpdev = container_of ( snp, struct efi_snp_device, snp ); struct ll_protocol *ll_protocol = snpdev->netdev->ll_protocol; + struct efi_saved_tpl tpl; struct io_buffer *iobuf; size_t payload_len; unsigned int tx_fill; - EFI_TPL saved_tpl; int rc; DBGC2 ( snpdev, "SNPDEV %p TRANSMIT %p+%lx", snpdev, data, @@ -640,7 +644,7 @@ efi_snp_transmit ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, } /* Raise TPL */ - saved_tpl = bs->RaiseTPL ( TPL_CALLBACK ); + efi_raise_tpl ( &tpl ); /* Sanity checks */ if ( ll_header_len ) { @@ -725,7 +729,7 @@ efi_snp_transmit ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, snpdev->interrupts |= EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT; /* Restore TPL */ - bs->RestoreTPL ( saved_tpl ); + efi_restore_tpl ( &tpl ); return 0; @@ -735,7 +739,7 @@ efi_snp_transmit ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, free_iob ( iobuf ); err_alloc_iob: err_sanity: - bs->RestoreTPL ( saved_tpl ); + efi_restore_tpl ( &tpl ); err_claimed: return EFIRC ( rc ); } @@ -757,17 +761,16 @@ efi_snp_receive ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, UINTN *ll_header_len, UINTN *len, VOID *data, EFI_MAC_ADDRESS *ll_src, EFI_MAC_ADDRESS *ll_dest, UINT16 *net_proto ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_snp_device *snpdev = container_of ( snp, struct efi_snp_device, snp ); struct ll_protocol *ll_protocol = snpdev->netdev->ll_protocol; + struct efi_saved_tpl tpl; struct io_buffer *iobuf; const void *iob_ll_dest; const void *iob_ll_src; uint16_t iob_net_proto; unsigned int iob_flags; size_t copy_len; - EFI_TPL saved_tpl; int rc; DBGC2 ( snpdev, "SNPDEV %p RECEIVE %p(+%lx)", snpdev, data, @@ -780,7 +783,7 @@ efi_snp_receive ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, } /* Raise TPL */ - saved_tpl = bs->RaiseTPL ( TPL_CALLBACK ); + efi_raise_tpl ( &tpl ); /* Poll the network device */ efi_snp_poll ( snpdev ); @@ -829,7 +832,7 @@ efi_snp_receive ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, out_bad_ll_header: free_iob ( iobuf ); out_no_packet: - bs->RestoreTPL ( saved_tpl ); + efi_restore_tpl ( &tpl ); err_claimed: return EFIRC ( rc ); } @@ -842,9 +845,8 @@ efi_snp_receive ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, */ static VOID EFIAPI efi_snp_wait_for_packet ( EFI_EVENT event __unused, VOID *context ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_snp_device *snpdev = context; - EFI_TPL saved_tpl; + struct efi_saved_tpl tpl; DBGCP ( snpdev, "SNPDEV %p WAIT_FOR_PACKET\n", snpdev ); @@ -857,13 +859,13 @@ static VOID EFIAPI efi_snp_wait_for_packet ( EFI_EVENT event __unused, return; /* Raise TPL */ - saved_tpl = bs->RaiseTPL ( TPL_CALLBACK ); + efi_raise_tpl ( &tpl ); /* Poll the network device */ efi_snp_poll ( snpdev ); /* Restore TPL */ - bs->RestoreTPL ( saved_tpl ); + efi_restore_tpl ( &tpl ); } /** SNP interface */ @@ -1623,13 +1625,9 @@ 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; - EFI_DEVICE_PATH_PROTOCOL *path_end; - MAC_ADDR_DEVICE_PATH *macpath; - VLAN_DEVICE_PATH *vlanpath; - size_t path_prefix_len = 0; unsigned int ifcnt; - unsigned int tag; void *interface; + int leak = 0; EFI_STATUS efirc; int rc; @@ -1713,41 +1711,13 @@ static int efi_snp_probe ( struct net_device *netdev ) { sizeof ( snpdev->name[0] ) ), "%s", netdev->name ); - /* Allocate the new device path */ - path_prefix_len = efi_devpath_len ( efidev->path ); - snpdev->path = zalloc ( path_prefix_len + sizeof ( *macpath ) + - sizeof ( *vlanpath ) + sizeof ( *path_end ) ); + /* Construct device path */ + snpdev->path = efi_netdev_path ( netdev ); if ( ! snpdev->path ) { rc = -ENOMEM; - goto err_alloc_device_path; + goto err_path; } - /* Populate the device path */ - memcpy ( snpdev->path, efidev->path, path_prefix_len ); - macpath = ( ( ( void * ) snpdev->path ) + path_prefix_len ); - memset ( macpath, 0, sizeof ( *macpath ) ); - macpath->Header.Type = MESSAGING_DEVICE_PATH; - macpath->Header.SubType = MSG_MAC_ADDR_DP; - macpath->Header.Length[0] = sizeof ( *macpath ); - memcpy ( &macpath->MacAddress, netdev->ll_addr, - netdev->ll_protocol->ll_addr_len ); - macpath->IfType = ntohs ( netdev->ll_protocol->ll_proto ); - if ( ( tag = vlan_tag ( netdev ) ) ) { - vlanpath = ( ( ( void * ) macpath ) + sizeof ( *macpath ) ); - memset ( vlanpath, 0, sizeof ( *vlanpath ) ); - vlanpath->Header.Type = MESSAGING_DEVICE_PATH; - vlanpath->Header.SubType = MSG_VLAN_DP; - vlanpath->Header.Length[0] = sizeof ( *vlanpath ); - vlanpath->VlanId = tag; - path_end = ( ( ( void * ) vlanpath ) + sizeof ( *vlanpath ) ); - } else { - path_end = ( ( ( void * ) macpath ) + sizeof ( *macpath ) ); - } - memset ( path_end, 0, sizeof ( *path_end ) ); - path_end->Type = END_DEVICE_PATH_TYPE; - path_end->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE; - path_end->Length[0] = sizeof ( *path_end ); - /* Install the SNP */ if ( ( efirc = bs->InstallMultipleProtocolInterfaces ( &snpdev->handle, @@ -1826,7 +1796,7 @@ static int efi_snp_probe ( struct net_device *netdev ) { list_del ( &snpdev->list ); if ( snpdev->package_list ) - efi_snp_hii_uninstall ( snpdev ); + leak |= efi_snp_hii_uninstall ( snpdev ); efi_child_del ( efidev->device, snpdev->handle ); err_efi_child_add: bs->CloseProtocol ( snpdev->handle, &efi_nii31_protocol_guid, @@ -1835,7 +1805,7 @@ static int efi_snp_probe ( struct net_device *netdev ) { bs->CloseProtocol ( snpdev->handle, &efi_nii_protocol_guid, efi_image_handle, snpdev->handle ); err_open_nii: - bs->UninstallMultipleProtocolInterfaces ( + if ( ( efirc = bs->UninstallMultipleProtocolInterfaces ( snpdev->handle, &efi_simple_network_protocol_guid, &snpdev->snp, &efi_device_path_protocol_guid, snpdev->path, @@ -1843,17 +1813,30 @@ static int efi_snp_probe ( struct net_device *netdev ) { &efi_nii31_protocol_guid, &snpdev->nii, &efi_component_name2_protocol_guid, &snpdev->name2, &efi_load_file_protocol_guid, &snpdev->load_file, - NULL ); + NULL ) ) != 0 ) { + DBGC ( snpdev, "SNPDEV %p could not uninstall: %s\n", + snpdev, strerror ( -EEFI ( efirc ) ) ); + leak = 1; + } + efi_nullify_snp ( &snpdev->snp ); + efi_nullify_nii ( &snpdev->nii ); + efi_nullify_name2 ( &snpdev->name2 ); + efi_nullify_load_file ( &snpdev->load_file ); err_install_protocol_interface: - free ( snpdev->path ); - err_alloc_device_path: + if ( ! leak ) + free ( snpdev->path ); + err_path: bs->CloseEvent ( snpdev->snp.WaitForPacket ); err_create_event: err_ll_addr_len: - netdev_put ( netdev ); - free ( snpdev ); + if ( ! leak ) { + netdev_put ( netdev ); + free ( snpdev ); + } err_alloc_snp: err_no_efidev: + if ( leak ) + DBGC ( snpdev, "SNPDEV %p nullified and leaked\n", snpdev ); return rc; } @@ -1890,6 +1873,8 @@ static void efi_snp_notify ( struct net_device *netdev ) { static void efi_snp_remove ( struct net_device *netdev ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_snp_device *snpdev; + int leak = efi_shutdown_in_progress; + EFI_STATUS efirc; /* Locate SNP device */ snpdev = efi_snp_demux ( netdev ); @@ -1901,13 +1886,14 @@ static void efi_snp_remove ( struct net_device *netdev ) { /* Uninstall the SNP */ list_del ( &snpdev->list ); if ( snpdev->package_list ) - efi_snp_hii_uninstall ( snpdev ); + leak |= efi_snp_hii_uninstall ( snpdev ); efi_child_del ( snpdev->efidev->device, snpdev->handle ); bs->CloseProtocol ( snpdev->handle, &efi_nii_protocol_guid, efi_image_handle, snpdev->handle ); bs->CloseProtocol ( snpdev->handle, &efi_nii31_protocol_guid, efi_image_handle, snpdev->handle ); - bs->UninstallMultipleProtocolInterfaces ( + if ( ( ! efi_shutdown_in_progress ) && + ( ( efirc = bs->UninstallMultipleProtocolInterfaces ( snpdev->handle, &efi_simple_network_protocol_guid, &snpdev->snp, &efi_device_path_protocol_guid, snpdev->path, @@ -1915,11 +1901,26 @@ static void efi_snp_remove ( struct net_device *netdev ) { &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 ); + NULL ) ) != 0 ) ) { + DBGC ( snpdev, "SNPDEV %p could not uninstall: %s\n", + snpdev, strerror ( -EEFI ( efirc ) ) ); + leak = 1; + } + efi_nullify_snp ( &snpdev->snp ); + efi_nullify_nii ( &snpdev->nii ); + efi_nullify_name2 ( &snpdev->name2 ); + efi_nullify_load_file ( &snpdev->load_file ); + if ( ! leak ) + free ( snpdev->path ); bs->CloseEvent ( snpdev->snp.WaitForPacket ); - netdev_put ( snpdev->netdev ); - free ( snpdev ); + if ( ! leak ) { + netdev_put ( snpdev->netdev ); + free ( snpdev ); + } + + /* Report leakage, if applicable */ + if ( leak && ( ! efi_shutdown_in_progress ) ) + DBGC ( snpdev, "SNPDEV %p nullified and leaked\n", snpdev ); } /** SNP driver */ @@ -1967,12 +1968,11 @@ struct efi_snp_device * last_opened_snpdev ( void ) { * @v delta Claim count change */ void efi_snp_add_claim ( int delta ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_snp_device *snpdev; /* Raise TPL if we are about to claim devices */ if ( ! efi_snp_claimed ) - efi_snp_old_tpl = bs->RaiseTPL ( TPL_CALLBACK ); + efi_raise_tpl ( &efi_snp_saved_tpl ); /* Claim SNP devices */ efi_snp_claimed += delta; @@ -1984,5 +1984,5 @@ void efi_snp_add_claim ( int delta ) { /* Restore TPL if we have released devices */ if ( ! efi_snp_claimed ) - bs->RestoreTPL ( efi_snp_old_tpl ); + efi_restore_tpl ( &efi_snp_saved_tpl ); } diff --git a/src/interface/efi/efi_snp_hii.c b/src/interface/efi/efi_snp_hii.c index 1e681a429..5d5f80cd7 100644 --- a/src/interface/efi/efi_snp_hii.c +++ b/src/interface/efi/efi_snp_hii.c @@ -63,7 +63,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/efi/efi_hii.h> #include <ipxe/efi/efi_snp.h> #include <ipxe/efi/efi_strings.h> +#include <ipxe/efi/efi_path.h> #include <ipxe/efi/efi_utils.h> +#include <ipxe/efi/efi_null.h> #include <config/branding.h> /** EFI platform setup formset GUID */ @@ -658,7 +660,8 @@ int efi_snp_hii_install ( struct efi_snp_device *snpdev ) { VENDOR_DEVICE_PATH *vendor_path; EFI_DEVICE_PATH_PROTOCOL *path_end; size_t path_prefix_len; - int efirc; + int leak = 0; + EFI_STATUS efirc; int rc; /* Do nothing if HII database protocol is not supported */ @@ -680,7 +683,7 @@ int efi_snp_hii_install ( struct efi_snp_device *snpdev ) { } /* Allocate the new device path */ - path_prefix_len = efi_devpath_len ( snpdev->path ); + path_prefix_len = efi_path_len ( snpdev->path ); snpdev->hii_child_path = zalloc ( path_prefix_len + sizeof ( *vendor_path ) + sizeof ( *path_end ) ); @@ -750,23 +753,37 @@ int efi_snp_hii_install ( struct efi_snp_device *snpdev ) { efi_child_del ( snpdev->handle, snpdev->hii_child_handle ); err_efi_child_add: - bs->UninstallMultipleProtocolInterfaces ( + if ( ( efirc = bs->UninstallMultipleProtocolInterfaces ( snpdev->hii_child_handle, &efi_hii_config_access_protocol_guid, &snpdev->hii, - NULL ); + NULL ) ) != 0 ) { + DBGC ( snpdev, "SNPDEV %p could not uninstall HII protocol: " + "%s\n", snpdev, strerror ( -EEFI ( efirc ) ) ); + leak = 1; + } + efi_nullify_hii ( &snpdev->hii ); err_install_protocol: - efihii->RemovePackageList ( efihii, snpdev->hii_handle ); + if ( ! leak ) + efihii->RemovePackageList ( efihii, snpdev->hii_handle ); err_new_package_list: - bs->UninstallMultipleProtocolInterfaces ( + if ( ( efirc = bs->UninstallMultipleProtocolInterfaces ( snpdev->hii_child_handle, &efi_device_path_protocol_guid, snpdev->hii_child_path, - NULL ); + NULL ) ) != 0 ) { + DBGC ( snpdev, "SNPDEV %p could not uninstall HII path: %s\n", + snpdev, strerror ( -EEFI ( efirc ) ) ); + leak = 1; + } err_hii_child_handle: - free ( snpdev->hii_child_path ); - snpdev->hii_child_path = NULL; + if ( ! leak ) { + free ( snpdev->hii_child_path ); + snpdev->hii_child_path = NULL; + } err_alloc_child_path: - free ( snpdev->package_list ); - snpdev->package_list = NULL; + if ( ! leak ) { + free ( snpdev->package_list ); + snpdev->package_list = NULL; + } err_build_package_list: err_no_hii: return rc; @@ -776,27 +793,49 @@ int efi_snp_hii_install ( struct efi_snp_device *snpdev ) { * Uninstall HII protocol and package for SNP device * * @v snpdev SNP device + * @ret leak Uninstallation failed: leak memory */ -void efi_snp_hii_uninstall ( struct efi_snp_device *snpdev ) { +int efi_snp_hii_uninstall ( struct efi_snp_device *snpdev ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + int leak = efi_shutdown_in_progress; + EFI_STATUS efirc; /* Do nothing if HII database protocol is not supported */ if ( ! efihii ) - return; + return 0; /* Uninstall protocols and remove package list */ efi_child_del ( snpdev->handle, snpdev->hii_child_handle ); - bs->UninstallMultipleProtocolInterfaces ( + if ( ( ! efi_shutdown_in_progress ) && + ( ( efirc = bs->UninstallMultipleProtocolInterfaces ( snpdev->hii_child_handle, &efi_hii_config_access_protocol_guid, &snpdev->hii, - NULL ); - efihii->RemovePackageList ( efihii, snpdev->hii_handle ); - bs->UninstallMultipleProtocolInterfaces ( + NULL ) ) != 0 ) ) { + DBGC ( snpdev, "SNPDEV %p could not uninstall HII protocol: " + "%s\n", snpdev, strerror ( -EEFI ( efirc ) ) ); + leak = 1; + } + efi_nullify_hii ( &snpdev->hii ); + if ( ! leak ) + efihii->RemovePackageList ( efihii, snpdev->hii_handle ); + if ( ( ! efi_shutdown_in_progress ) && + ( ( efirc = bs->UninstallMultipleProtocolInterfaces ( snpdev->hii_child_handle, &efi_device_path_protocol_guid, snpdev->hii_child_path, - NULL ); - free ( snpdev->hii_child_path ); - snpdev->hii_child_path = NULL; - free ( snpdev->package_list ); - snpdev->package_list = NULL; + NULL ) ) != 0 ) ) { + DBGC ( snpdev, "SNPDEV %p could not uninstall HII path: %s\n", + snpdev, strerror ( -EEFI ( efirc ) ) ); + leak = 1; + } + if ( ! leak ) { + free ( snpdev->hii_child_path ); + snpdev->hii_child_path = NULL; + free ( snpdev->package_list ); + snpdev->package_list = NULL; + } + + /* Report leakage, if applicable */ + if ( leak && ( ! efi_shutdown_in_progress ) ) + DBGC ( snpdev, "SNPDEV %p HII nullified and leaked\n", snpdev ); + return leak; } diff --git a/src/interface/efi/efi_timer.c b/src/interface/efi/efi_timer.c index 8f40cb81a..405cd3454 100644 --- a/src/interface/efi/efi_timer.c +++ b/src/interface/efi/efi_timer.c @@ -97,8 +97,17 @@ static unsigned long efi_currticks ( void ) { * gain us any substantive benefits (since even with such * calls we would still be suffering from the limitations of a * polling design), we instead choose to run at TPL_CALLBACK - * almost all of the time, dropping to TPL_APPLICATION to - * allow timer ticks to occur. + * almost all of the time, dropping to a lower TPL to allow + * timer ticks to occur. + * + * We record the external TPL at the point of entry into iPXE, + * and drop back only as far as this external TPL. This + * avoids the unexpected behaviour that may arise from having + * iPXE temporarily drop to TPL_APPLICATION in the middle of + * an entry point invoked at TPL_CALLBACK. The side effect is + * that iPXE's view of the system time is effectively frozen + * for the duration of any call made in to iPXE at + * TPL_CALLBACK or higher. * * * For added excitement, UEFI provides no clean way for device @@ -127,7 +136,7 @@ static unsigned long efi_currticks ( void ) { if ( efi_shutdown_in_progress ) { efi_jiffies++; } else { - bs->RestoreTPL ( TPL_APPLICATION ); + bs->RestoreTPL ( efi_external_tpl ); bs->RaiseTPL ( TPL_CALLBACK ); } diff --git a/src/interface/efi/efi_usb.c b/src/interface/efi/efi_usb.c index 6f49b369e..28dfc8680 100644 --- a/src/interface/efi/efi_usb.c +++ b/src/interface/efi/efi_usb.c @@ -30,8 +30,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <errno.h> #include <assert.h> #include <ipxe/efi/efi.h> -#include <ipxe/efi/efi_utils.h> +#include <ipxe/efi/efi_path.h> #include <ipxe/efi/efi_driver.h> +#include <ipxe/efi/efi_null.h> #include <ipxe/efi/efi_usb.h> #include <ipxe/usb.h> @@ -73,13 +74,14 @@ static const char * efi_usb_direction_name ( EFI_USB_DATA_DIRECTION direction ){ static VOID EFIAPI efi_usb_timer ( EFI_EVENT event __unused, VOID *context ) { struct efi_usb_endpoint *usbep = context; - struct usb_bus *bus = usbep->usbintf->usbdev->usb->port->hub->bus; + struct usb_function *func = usbep->usbintf->usbdev->func; /* Poll bus */ - usb_poll ( bus ); + usb_poll ( func->usb->port->hub->bus ); /* Refill endpoint */ - usb_refill ( &usbep->ep ); + if ( usbep->ep.open ) + usb_refill ( &usbep->ep ); } /** @@ -118,6 +120,21 @@ static int efi_usb_mtu ( struct efi_usb_interface *usbintf, } /** + * Check if endpoint is open + * + * @v usbintf EFI USB interface + * @v endpoint Endpoint address + * @ret is_open Endpoint is open + */ +static int efi_usb_is_open ( struct efi_usb_interface *usbintf, + unsigned int endpoint ) { + unsigned int index = USB_ENDPOINT_IDX ( endpoint ); + struct efi_usb_endpoint *usbep = usbintf->endpoint[index]; + + return ( usbep && usbep->ep.open ); +} + +/** * Open endpoint * * @v usbintf EFI USB interface @@ -139,6 +156,22 @@ static int efi_usb_open ( struct efi_usb_interface *usbintf, EFI_STATUS efirc; int rc; + /* Allocate structure, if needed. Once allocated, we leave + * the endpoint structure in place until the device is + * removed, to work around external UEFI code that closes the + * endpoint at illegal times. + */ + usbep = usbintf->endpoint[index]; + if ( ! usbep ) { + usbep = zalloc ( sizeof ( *usbep ) ); + if ( ! usbep ) { + rc = -ENOMEM; + goto err_alloc; + } + usbep->usbintf = usbintf; + usbintf->endpoint[index] = usbep; + } + /* Get endpoint MTU */ mtu = efi_usb_mtu ( usbintf, endpoint ); if ( mtu < 0 ) { @@ -147,13 +180,7 @@ static int efi_usb_open ( struct efi_usb_interface *usbintf, } /* Allocate and initialise structure */ - usbep = zalloc ( sizeof ( *usbep ) ); - if ( ! usbep ) { - rc = -ENOMEM; - goto err_alloc; - } - usbep->usbintf = usbintf; - usb_endpoint_init ( &usbep->ep, usbdev->usb, driver ); + usb_endpoint_init ( &usbep->ep, usbdev->func->usb, driver ); usb_endpoint_describe ( &usbep->ep, endpoint, attributes, mtu, 0, ( interval << 3 /* microframes */ ) ); @@ -164,9 +191,6 @@ static int efi_usb_open ( struct efi_usb_interface *usbintf, strerror ( rc ) ); goto err_open; } - - /* Record opened endpoint */ - usbintf->endpoint[index] = usbep; DBGC ( usbdev, "USBDEV %s %s opened\n", usbintf->name, usb_endpoint_name ( &usbep->ep ) ); @@ -185,12 +209,10 @@ static int efi_usb_open ( struct efi_usb_interface *usbintf, bs->CloseEvent ( usbep->event ); err_event: - usbintf->endpoint[index] = usbep; usb_endpoint_close ( &usbep->ep ); err_open: - free ( usbep ); - err_alloc: err_mtu: + err_alloc: return rc; } @@ -216,12 +238,6 @@ static void efi_usb_close ( struct efi_usb_endpoint *usbep ) { usb_endpoint_close ( &usbep->ep ); DBGC ( usbdev, "USBDEV %s %s closed\n", usbintf->name, usb_endpoint_name ( &usbep->ep ) ); - - /* Free endpoint */ - free ( usbep ); - - /* Record closed endpoint */ - usbintf->endpoint[index] = NULL; } /** @@ -236,12 +252,32 @@ static void efi_usb_close_all ( struct efi_usb_interface *usbintf ) { for ( i = 0 ; i < ( sizeof ( usbintf->endpoint ) / sizeof ( usbintf->endpoint[0] ) ) ; i++ ) { usbep = usbintf->endpoint[i]; - if ( usbep ) + if ( usbep && usbep->ep.open ) efi_usb_close ( usbep ); } } /** + * Free all endpoints + * + * @v usbintf EFI USB interface + */ +static void efi_usb_free_all ( struct efi_usb_interface *usbintf ) { + struct efi_usb_endpoint *usbep; + unsigned int i; + + for ( i = 0 ; i < ( sizeof ( usbintf->endpoint ) / + sizeof ( usbintf->endpoint[0] ) ) ; i++ ) { + usbep = usbintf->endpoint[i]; + if ( usbep ) { + assert ( ! usbep->ep.open ); + free ( usbep ); + usbintf->endpoint[i] = NULL; + } + } +} + +/** * Complete synchronous transfer * * @v ep USB endpoint @@ -286,7 +322,7 @@ static int efi_usb_sync_transfer ( struct efi_usb_interface *usbintf, int rc; /* Open endpoint, if applicable */ - if ( ( ! usbintf->endpoint[index] ) && + if ( ( ! efi_usb_is_open ( usbintf, endpoint ) ) && ( ( rc = efi_usb_open ( usbintf, endpoint, attributes, 0, &efi_usb_sync_driver ) ) != 0 ) ) { goto err_open; @@ -319,7 +355,7 @@ static int efi_usb_sync_transfer ( struct efi_usb_interface *usbintf, for ( i = 0 ; ( ( timeout == 0 ) || ( i < timeout ) ) ; i++ ) { /* Poll bus */ - usb_poll ( usbdev->usb->port->hub->bus ); + usb_poll ( usbdev->func->usb->port->hub->bus ); /* Check for completion */ if ( usbep->rc != -EINPROGRESS ) { @@ -377,15 +413,21 @@ static void efi_usb_async_complete ( struct usb_endpoint *ep, goto drop; /* Construct status */ - status = ( ( rc == 0 ) ? 0 : EFI_USB_ERR_STALL ); + status = ( ( rc == 0 ) ? 0 : EFI_USB_ERR_SYSTEM ); - /* Report completion */ - usbep->callback ( iobuf->data, iob_len ( iobuf ), usbep->context, - status ); + /* Report completion, if applicable */ + if ( usbep->callback ) { + usbep->callback ( iobuf->data, iob_len ( iobuf ), + usbep->context, status ); + } drop: - /* Recycle I/O buffer */ - usb_recycle ( &usbep->ep, iobuf ); + /* Recycle or free I/O buffer */ + if ( usbep->ep.open ) { + usb_recycle ( &usbep->ep, iobuf ); + } else { + free_iob ( iobuf ); + } } /** Asynchronous endpoint operations */ @@ -416,6 +458,10 @@ static int efi_usb_async_start ( struct efi_usb_interface *usbintf, EFI_STATUS efirc; int rc; + /* Close endpoint, if applicable */ + if ( efi_usb_is_open ( usbintf, endpoint ) ) + efi_usb_close ( usbintf->endpoint[index] ); + /* Open endpoint */ if ( ( rc = efi_usb_open ( usbintf, endpoint, USB_ENDPOINT_ATTR_INTERRUPT, interval, @@ -451,6 +497,8 @@ static int efi_usb_async_start ( struct efi_usb_interface *usbintf, bs->SetTimer ( usbep->event, TimerCancel, 0 ); err_timer: err_prefill: + usbep->callback = NULL; + usbep->context = NULL; efi_usb_close ( usbep ); err_open: return rc; @@ -469,15 +517,16 @@ static void efi_usb_async_stop ( struct efi_usb_interface *usbintf, unsigned int index = USB_ENDPOINT_IDX ( endpoint ); /* Do nothing if endpoint is already closed */ - usbep = usbintf->endpoint[index]; - if ( ! usbep ) + if ( ! efi_usb_is_open ( usbintf, endpoint ) ) return; + usbep = usbintf->endpoint[index]; /* Stop timer */ bs->SetTimer ( usbep->event, TimerCancel, 0 ); - /* Close endpoint */ - efi_usb_close ( usbep ); + /* Clear callback parameters */ + usbep->callback = NULL; + usbep->context = NULL; } /****************************************************************************** @@ -505,7 +554,6 @@ efi_usb_control_transfer ( EFI_USB_IO_PROTOCOL *usbio, EFI_USB_DATA_DIRECTION direction, UINT32 timeout, VOID *data, UINTN len, UINT32 *status ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_usb_interface *usbintf = container_of ( usbio, struct efi_usb_interface, usbio ); struct efi_usb_device *usbdev = usbintf->usbdev; @@ -513,7 +561,7 @@ efi_usb_control_transfer ( EFI_USB_IO_PROTOCOL *usbio, USB_REQUEST_TYPE ( packet->Request ) ); unsigned int value = le16_to_cpu ( packet->Value ); unsigned int index = le16_to_cpu ( packet->Index ); - EFI_TPL saved_tpl; + struct efi_saved_tpl tpl; int rc; DBGC2 ( usbdev, "USBDEV %s control %04x:%04x:%04x:%04x %s %dms " @@ -523,7 +571,7 @@ efi_usb_control_transfer ( EFI_USB_IO_PROTOCOL *usbio, ( ( size_t ) len ) ); /* Raise TPL */ - saved_tpl = bs->RaiseTPL ( TPL_CALLBACK ); + efi_raise_tpl ( &tpl ); /* Clear status */ *status = 0; @@ -548,14 +596,13 @@ efi_usb_control_transfer ( EFI_USB_IO_PROTOCOL *usbio, efi_usb_close_all ( usbintf ); /* Issue control transfer */ - if ( ( rc = usb_control ( usbdev->usb, request, value, index, + if ( ( rc = usb_control ( usbdev->func->usb, request, value, index, data, len ) ) != 0 ) { DBGC ( usbdev, "USBDEV %s control %04x:%04x:%04x:%04x %p+%zx " "failed: %s\n", usbintf->name, request, value, index, le16_to_cpu ( packet->Length ), data, ( ( size_t ) len ), strerror ( rc ) ); - /* Assume that any error represents a stall */ - *status = EFI_USB_ERR_STALL; + *status = EFI_USB_ERR_SYSTEM; goto err_control; } @@ -568,7 +615,7 @@ efi_usb_control_transfer ( EFI_USB_IO_PROTOCOL *usbio, err_control: err_change_config: - bs->RestoreTPL ( saved_tpl ); + efi_restore_tpl ( &tpl ); return EFIRC ( rc ); } @@ -586,12 +633,11 @@ efi_usb_control_transfer ( EFI_USB_IO_PROTOCOL *usbio, static EFI_STATUS EFIAPI efi_usb_bulk_transfer ( EFI_USB_IO_PROTOCOL *usbio, UINT8 endpoint, VOID *data, UINTN *len, UINTN timeout, UINT32 *status ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_usb_interface *usbintf = container_of ( usbio, struct efi_usb_interface, usbio ); struct efi_usb_device *usbdev = usbintf->usbdev; size_t actual = *len; - EFI_TPL saved_tpl; + struct efi_saved_tpl tpl; int rc; DBGC2 ( usbdev, "USBDEV %s bulk %s %p+%zx %dms\n", usbintf->name, @@ -599,7 +645,7 @@ efi_usb_bulk_transfer ( EFI_USB_IO_PROTOCOL *usbio, UINT8 endpoint, VOID *data, ( ( size_t ) *len ), ( ( unsigned int ) timeout ) ); /* Raise TPL */ - saved_tpl = bs->RaiseTPL ( TPL_CALLBACK ); + efi_raise_tpl ( &tpl ); /* Clear status */ *status = 0; @@ -614,7 +660,7 @@ efi_usb_bulk_transfer ( EFI_USB_IO_PROTOCOL *usbio, UINT8 endpoint, VOID *data, } err_transfer: - bs->RestoreTPL ( saved_tpl ); + efi_restore_tpl ( &tpl ); return EFIRC ( rc ); } @@ -633,12 +679,11 @@ static EFI_STATUS EFIAPI efi_usb_sync_interrupt_transfer ( EFI_USB_IO_PROTOCOL *usbio, UINT8 endpoint, VOID *data, UINTN *len, UINTN timeout, UINT32 *status ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_usb_interface *usbintf = container_of ( usbio, struct efi_usb_interface, usbio ); struct efi_usb_device *usbdev = usbintf->usbdev; size_t actual = *len; - EFI_TPL saved_tpl; + struct efi_saved_tpl tpl; int rc; DBGC2 ( usbdev, "USBDEV %s sync intr %s %p+%zx %dms\n", usbintf->name, @@ -646,7 +691,7 @@ efi_usb_sync_interrupt_transfer ( EFI_USB_IO_PROTOCOL *usbio, UINT8 endpoint, ( ( size_t ) *len ), ( ( unsigned int ) timeout ) ); /* Raise TPL */ - saved_tpl = bs->RaiseTPL ( TPL_CALLBACK ); + efi_raise_tpl ( &tpl ); /* Clear status */ *status = 0; @@ -661,7 +706,7 @@ efi_usb_sync_interrupt_transfer ( EFI_USB_IO_PROTOCOL *usbio, UINT8 endpoint, } err_transfer: - bs->RestoreTPL ( saved_tpl ); + efi_restore_tpl ( &tpl ); return EFIRC ( rc ); } @@ -682,11 +727,10 @@ efi_usb_async_interrupt_transfer ( EFI_USB_IO_PROTOCOL *usbio, UINT8 endpoint, BOOLEAN start, UINTN interval, UINTN len, EFI_ASYNC_USB_TRANSFER_CALLBACK callback, VOID *context ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_usb_interface *usbintf = container_of ( usbio, struct efi_usb_interface, usbio ); struct efi_usb_device *usbdev = usbintf->usbdev; - EFI_TPL saved_tpl; + struct efi_saved_tpl tpl; int rc; DBGC2 ( usbdev, "USBDEV %s async intr %s len %#zx int %d %p/%p\n", @@ -696,7 +740,7 @@ efi_usb_async_interrupt_transfer ( EFI_USB_IO_PROTOCOL *usbio, UINT8 endpoint, callback, context ); /* Raise TPL */ - saved_tpl = bs->RaiseTPL ( TPL_CALLBACK ); + efi_raise_tpl ( &tpl ); /* Start/stop transfer as applicable */ if ( start ) { @@ -718,7 +762,7 @@ efi_usb_async_interrupt_transfer ( EFI_USB_IO_PROTOCOL *usbio, UINT8 endpoint, } err_start: - bs->RestoreTPL ( saved_tpl ); + efi_restore_tpl ( &tpl ); return EFIRC ( rc ); } @@ -796,7 +840,7 @@ efi_usb_get_device_descriptor ( EFI_USB_IO_PROTOCOL *usbio, DBGC2 ( usbdev, "USBDEV %s get device descriptor\n", usbintf->name ); /* Copy cached device descriptor */ - memcpy ( efidesc, &usbdev->usb->device, sizeof ( *efidesc ) ); + memcpy ( efidesc, &usbdev->func->usb->device, sizeof ( *efidesc ) ); return 0; } @@ -914,9 +958,9 @@ efi_usb_get_string_descriptor ( EFI_USB_IO_PROTOCOL *usbio, UINT16 language, container_of ( usbio, struct efi_usb_interface, usbio ); struct efi_usb_device *usbdev = usbintf->usbdev; struct usb_descriptor_header header; + struct efi_saved_tpl tpl; VOID *buffer; size_t len; - EFI_TPL saved_tpl; EFI_STATUS efirc; int rc; @@ -924,11 +968,12 @@ efi_usb_get_string_descriptor ( EFI_USB_IO_PROTOCOL *usbio, UINT16 language, usbintf->name, language, index ); /* Raise TPL */ - saved_tpl = bs->RaiseTPL ( TPL_CALLBACK ); + efi_raise_tpl ( &tpl ); /* Read descriptor header */ - if ( ( rc = usb_get_descriptor ( usbdev->usb, 0, USB_STRING_DESCRIPTOR, - index, language, &header, + if ( ( rc = usb_get_descriptor ( usbdev->func->usb, 0, + USB_STRING_DESCRIPTOR, index, + language, &header, sizeof ( header ) ) ) != 0 ) { DBGC ( usbdev, "USBDEV %s could not get string %d:%d " "descriptor header: %s\n", usbintf->name, language, @@ -936,6 +981,12 @@ efi_usb_get_string_descriptor ( EFI_USB_IO_PROTOCOL *usbio, UINT16 language, goto err_get_header; } len = header.len; + if ( len < sizeof ( header ) ) { + DBGC ( usbdev, "USBDEV %s underlength string %d:%d\n", + usbintf->name, language, index ); + rc = -EINVAL; + goto err_len; + } /* Allocate buffer */ if ( ( efirc = bs->AllocatePool ( EfiBootServicesData, len, @@ -945,9 +996,9 @@ efi_usb_get_string_descriptor ( EFI_USB_IO_PROTOCOL *usbio, UINT16 language, } /* Read whole descriptor */ - if ( ( rc = usb_get_descriptor ( usbdev->usb, 0, USB_STRING_DESCRIPTOR, - index, language, buffer, - len ) ) != 0 ) { + if ( ( rc = usb_get_descriptor ( usbdev->func->usb, 0, + USB_STRING_DESCRIPTOR, index, + language, buffer, len ) ) != 0 ) { DBGC ( usbdev, "USBDEV %s could not get string %d:%d " "descriptor: %s\n", usbintf->name, language, index, strerror ( rc ) ); @@ -960,7 +1011,7 @@ efi_usb_get_string_descriptor ( EFI_USB_IO_PROTOCOL *usbio, UINT16 language, memset ( ( buffer + len - sizeof ( header ) ), 0, sizeof ( **string ) ); /* Restore TPL */ - bs->RestoreTPL ( saved_tpl ); + efi_restore_tpl ( &tpl ); /* Return allocated string */ *string = buffer; @@ -969,8 +1020,9 @@ efi_usb_get_string_descriptor ( EFI_USB_IO_PROTOCOL *usbio, UINT16 language, err_get_descriptor: bs->FreePool ( buffer ); err_alloc: + err_len: err_get_header: - bs->RestoreTPL ( saved_tpl ); + efi_restore_tpl ( &tpl ); return EFIRC ( rc ); } @@ -992,9 +1044,8 @@ efi_usb_get_supported_languages ( EFI_USB_IO_PROTOCOL *usbio, DBGC2 ( usbdev, "USBDEV %s get supported languages\n", usbintf->name ); /* Return cached supported languages */ - *languages = ( ( ( void * ) usbdev->languages ) + - sizeof ( *(usbdev->languages) ) ); - *len = usbdev->languages->len; + *languages = usbdev->lang; + *len = usbdev->lang_len; return 0; } @@ -1056,25 +1107,14 @@ static EFI_USB_IO_PROTOCOL efi_usb_io_protocol = { static int efi_usb_install ( struct efi_usb_device *usbdev, unsigned int interface ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - struct efi_device *efidev = usbdev->efidev; + struct usb_function *func = usbdev->func; struct efi_usb_interface *usbintf; - struct usb_device *usb; - EFI_DEVICE_PATH_PROTOCOL *path_end; - USB_DEVICE_PATH *usbpath; - unsigned int path_count; - size_t path_prefix_len; - size_t path_len; + int leak = 0; EFI_STATUS efirc; int rc; - /* Calculate device path length */ - path_count = ( usb_depth ( usbdev->usb ) + 1 ); - path_prefix_len = efi_devpath_len ( efidev->path ); - path_len = ( path_prefix_len + ( path_count * sizeof ( *usbpath ) ) + - sizeof ( *path_end ) ); - /* Allocate and initialise structure */ - usbintf = zalloc ( sizeof ( *usbintf ) + path_len ); + usbintf = zalloc ( sizeof ( *usbintf ) ); if ( ! usbintf ) { rc = -ENOMEM; goto err_alloc; @@ -1085,22 +1125,12 @@ static int efi_usb_install ( struct efi_usb_device *usbdev, usbintf->interface = interface; memcpy ( &usbintf->usbio, &efi_usb_io_protocol, sizeof ( usbintf->usbio ) ); - usbintf->path = ( ( ( void * ) usbintf ) + sizeof ( *usbintf ) ); /* Construct device path */ - memcpy ( usbintf->path, efidev->path, path_prefix_len ); - path_end = ( ( ( void * ) usbintf->path ) + path_len - - sizeof ( *path_end ) ); - path_end->Type = END_DEVICE_PATH_TYPE; - path_end->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE; - path_end->Length[0] = sizeof ( *path_end ); - usbpath = ( ( ( void * ) path_end ) - sizeof ( *usbpath ) ); - usbpath->InterfaceNumber = interface; - for ( usb = usbdev->usb ; usb ; usbpath--, usb = usb->port->hub->usb ) { - usbpath->Header.Type = MESSAGING_DEVICE_PATH; - usbpath->Header.SubType = MSG_USB_DP; - usbpath->Header.Length[0] = sizeof ( *usbpath ); - usbpath->ParentPortNumber = ( usb->port->address - 1 ); + usbintf->path = efi_usb_path ( func ); + if ( ! usbintf->path ) { + rc = -ENODEV; + goto err_path; } /* Add to list of interfaces */ @@ -1122,16 +1152,30 @@ static int efi_usb_install ( struct efi_usb_device *usbdev, usbintf->name, efi_handle_name ( usbintf->handle ) ); return 0; - efi_usb_close_all ( usbintf ); - bs->UninstallMultipleProtocolInterfaces ( + if ( ( efirc = bs->UninstallMultipleProtocolInterfaces ( usbintf->handle, &efi_usb_io_protocol_guid, &usbintf->usbio, &efi_device_path_protocol_guid, usbintf->path, - NULL ); + NULL ) ) != 0 ) { + DBGC ( usbdev, "USBDEV %s could not uninstall: %s\n", + usbintf->name, strerror ( -EEFI ( efirc ) ) ); + leak = 1; + } + efi_nullify_usbio ( &usbintf->usbio ); err_install_protocol: + efi_usb_close_all ( usbintf ); + efi_usb_free_all ( usbintf ); list_del ( &usbintf->list ); - free ( usbintf ); + if ( ! leak ) + free ( usbintf->path ); + err_path: + if ( ! leak ) + free ( usbintf ); err_alloc: + if ( leak ) { + DBGC ( usbdev, "USBDEV %s nullified and leaked\n", + usbintf->name ); + } return rc; } @@ -1142,22 +1186,53 @@ static int efi_usb_install ( struct efi_usb_device *usbdev, */ static void efi_usb_uninstall ( struct efi_usb_interface *usbintf ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + struct efi_usb_device *usbdev = usbintf->usbdev; + int leak = efi_shutdown_in_progress; + EFI_STATUS efirc; - /* Close all endpoints */ - efi_usb_close_all ( usbintf ); + DBGC ( usbdev, "USBDEV %s uninstalling %s\n", + usbintf->name, efi_handle_name ( usbintf->handle ) ); + + /* Disconnect controllers. This should not be necessary, but + * seems to be required on some platforms to avoid failures + * when uninstalling protocols. + */ + if ( ! efi_shutdown_in_progress ) + bs->DisconnectController ( usbintf->handle, NULL, NULL ); /* Uninstall protocols */ - bs->UninstallMultipleProtocolInterfaces ( + if ( ( ! efi_shutdown_in_progress ) && + ( ( efirc = bs->UninstallMultipleProtocolInterfaces ( usbintf->handle, &efi_usb_io_protocol_guid, &usbintf->usbio, &efi_device_path_protocol_guid, usbintf->path, - NULL ); + NULL ) ) != 0 ) ) { + DBGC ( usbdev, "USBDEV %s could not uninstall: %s\n", + usbintf->name, strerror ( -EEFI ( efirc ) ) ); + leak = 1; + } + efi_nullify_usbio ( &usbintf->usbio ); + + /* Close and free all endpoints */ + efi_usb_close_all ( usbintf ); + efi_usb_free_all ( usbintf ); /* Remove from list of interfaces */ list_del ( &usbintf->list ); + /* Free device path */ + if ( ! leak ) + free ( usbintf->path ); + /* Free interface */ - free ( usbintf ); + if ( ! leak ) + free ( usbintf ); + + /* Report leakage, if applicable */ + if ( leak && ( ! efi_shutdown_in_progress ) ) { + DBGC ( usbdev, "USBDEV %s nullified and leaked\n", + usbintf->name ); + } } /** @@ -1189,19 +1264,13 @@ static int efi_usb_probe ( struct usb_function *func, struct usb_device *usb = func->usb; struct efi_usb_device *usbdev; struct efi_usb_interface *usbintf; - struct efi_device *efidev; struct usb_descriptor_header header; + struct usb_descriptor_header *lang; size_t config_len; + size_t lang_len; unsigned int i; int rc; - /* Find parent EFI device */ - efidev = efidev_parent ( &func->dev ); - if ( ! efidev ) { - rc = -ENOTTY; - goto err_no_efidev; - } - /* Get configuration length */ config_len = le16_to_cpu ( config->len ); @@ -1211,27 +1280,30 @@ static int efi_usb_probe ( struct usb_function *func, /* Assume no strings are present */ header.len = 0; } + lang_len = ( ( header.len >= sizeof ( header ) ) ? + ( header.len - sizeof ( header ) ) : 0 ); /* Allocate and initialise structure */ - usbdev = zalloc ( sizeof ( *usbdev ) + config_len + header.len ); + usbdev = zalloc ( sizeof ( *usbdev ) + config_len + + sizeof ( *lang ) + lang_len ); if ( ! usbdev ) { rc = -ENOMEM; goto err_alloc; } usb_func_set_drvdata ( func, usbdev ); usbdev->name = func->name; - usbdev->usb = usb; - usbdev->efidev = efidev; + usbdev->func = func; usbdev->config = ( ( ( void * ) usbdev ) + sizeof ( *usbdev ) ); memcpy ( usbdev->config, config, config_len ); - usbdev->languages = ( ( ( void * ) usbdev->config ) + config_len ); + lang = ( ( ( void * ) usbdev->config ) + config_len ); + usbdev->lang = ( ( ( void * ) lang ) + sizeof ( *lang ) ); + usbdev->lang_len = lang_len; INIT_LIST_HEAD ( &usbdev->interfaces ); - /* Get supported languages descriptor */ - if ( header.len && - ( rc = usb_get_descriptor ( usb, 0, USB_STRING_DESCRIPTOR, 0, 0, - usbdev->languages, - header.len ) ) != 0 ) { + /* Get supported languages descriptor, if applicable */ + if ( lang_len && + ( ( rc = usb_get_descriptor ( usb, 0, USB_STRING_DESCRIPTOR, + 0, 0, lang, header.len ) ) != 0 ) ) { DBGC ( usbdev, "USBDEV %s could not get supported languages: " "%s\n", usbdev->name, strerror ( rc ) ); goto err_get_languages; @@ -1256,7 +1328,6 @@ static int efi_usb_probe ( struct usb_function *func, err_get_languages: free ( usbdev ); err_alloc: - err_no_efidev: return rc; } @@ -1286,7 +1357,7 @@ static struct usb_device_id efi_usb_ids[] = { }; /** USB I/O protocol driver */ -struct usb_driver usbio_driver __usb_driver = { +struct usb_driver usbio_driver __usb_fallback_driver = { .ids = efi_usb_ids, .id_count = ( sizeof ( efi_usb_ids ) / sizeof ( efi_usb_ids[0] ) ), .class = USB_CLASS_ID ( USB_ANY_ID, USB_ANY_ID, USB_ANY_ID ), diff --git a/src/interface/efi/efi_utils.c b/src/interface/efi/efi_utils.c index 4dc75414c..8e660e9d7 100644 --- a/src/interface/efi/efi_utils.c +++ b/src/interface/efi/efi_utils.c @@ -33,36 +33,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); */ /** - * Find end of device path - * - * @v path Path to device - * @ret path_end End of device path - */ -EFI_DEVICE_PATH_PROTOCOL * efi_devpath_end ( EFI_DEVICE_PATH_PROTOCOL *path ) { - - while ( path->Type != END_DEVICE_PATH_TYPE ) { - path = ( ( ( void * ) path ) + - /* There's this amazing new-fangled thing known as - * a UINT16, but who wants to use one of those? */ - ( ( path->Length[1] << 8 ) | path->Length[0] ) ); - } - - return path; -} - -/** - * Find length of device path (excluding terminator) - * - * @v path Path to device - * @ret path_len Length of device path - */ -size_t efi_devpath_len ( EFI_DEVICE_PATH_PROTOCOL *path ) { - EFI_DEVICE_PATH_PROTOCOL *end = efi_devpath_end ( path ); - - return ( ( ( void * ) end ) - ( ( void * ) path ) ); -} - -/** * Locate parent device supporting a given protocol * * @v device EFI device handle @@ -175,7 +145,7 @@ void efi_child_del ( EFI_HANDLE parent, EFI_HANDLE child ) { static int efi_pci_info ( EFI_HANDLE device, const char *prefix, struct device *dev ) { EFI_HANDLE pci_device; - struct pci_device pci; + struct efi_pci_device efipci; int rc; /* Find parent PCI device */ @@ -187,16 +157,16 @@ static int efi_pci_info ( EFI_HANDLE device, const char *prefix, } /* Get PCI device information */ - if ( ( rc = efipci_info ( pci_device, &pci ) ) != 0 ) { + if ( ( rc = efipci_info ( pci_device, &efipci ) ) != 0 ) { DBGC ( device, "EFIDEV %s could not get PCI information: %s\n", efi_handle_name ( device ), strerror ( rc ) ); return rc; } /* Populate device information */ - memcpy ( &dev->desc, &pci.dev.desc, sizeof ( dev->desc ) ); + memcpy ( &dev->desc, &efipci.pci.dev.desc, sizeof ( dev->desc ) ); snprintf ( dev->name, sizeof ( dev->name ), "%s-%s", - prefix, pci.dev.name ); + prefix, efipci.pci.dev.name ); return 0; } diff --git a/src/interface/efi/efi_veto.c b/src/interface/efi/efi_veto.c new file mode 100644 index 000000000..6ff7898e9 --- /dev/null +++ b/src/interface/efi/efi_veto.c @@ -0,0 +1,609 @@ +/* + * Copyright (C) 2019 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <ipxe/settings.h> +#include <ipxe/pci.h> +#include <ipxe/efi/efi.h> +#include <ipxe/efi/Protocol/DriverBinding.h> +#include <ipxe/efi/Protocol/LoadedImage.h> +#include <ipxe/efi/Protocol/ComponentName.h> +#include <ipxe/efi/efi_veto.h> + +/** @file + * + * EFI driver vetoes + * + */ + +/** A driver veto */ +struct efi_veto { + /** Veto name (for debugging) */ + const char *name; + /** + * Check if driver is vetoed + * + * @v binding Driver binding protocol + * @v loaded Loaded image protocol + * @v wtf Component name protocol, if present + * @v manufacturer Manufacturer name, if present + * @v name Driver name (in "eng" language), if present + * @ret vetoed Driver is to be vetoed + */ + int ( * veto ) ( EFI_DRIVER_BINDING_PROTOCOL *binding, + EFI_LOADED_IMAGE_PROTOCOL *loaded, + EFI_COMPONENT_NAME_PROTOCOL *wtf, + const char *manufacturer, const CHAR16 *name ); +}; + +/** + * Unload an EFI driver + * + * @v driver Driver binding handle + * @ret rc Return status code + */ +static int efi_veto_unload ( EFI_HANDLE driver ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_STATUS efirc; + int rc; + + /* Unload the driver */ + if ( ( efirc = bs->UnloadImage ( driver ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( driver, "EFIVETO %s could not unload: %s\n", + efi_handle_name ( driver ), strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Disconnect an EFI driver from all handles + * + * @v driver Driver binding handle + * @ret rc Return status code + */ +static int efi_veto_disconnect ( EFI_HANDLE driver ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_HANDLE *handles; + EFI_HANDLE handle; + UINTN count; + unsigned int i; + EFI_STATUS efirc; + int rc; + + /* Enumerate all handles */ + if ( ( efirc = bs->LocateHandleBuffer ( AllHandles, NULL, NULL, + &count, &handles ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( driver, "EFIVETO %s could not enumerate handles: %s\n", + efi_handle_name ( driver ), strerror ( rc ) ); + goto err_list; + } + + /* Disconnect driver from all handles, in reverse order */ + for ( i = 0 ; i < count ; i++ ) { + handle = handles[ count - i - 1 ]; + efirc = bs->DisconnectController ( handle, driver, NULL ); + if ( ( efirc != 0 ) && ( efirc != EFI_NOT_FOUND ) ) { + rc = -EEFI ( efirc ); + DBGC ( driver, "EFIVETO %s could not disconnect", + efi_handle_name ( driver ) ); + DBGC ( driver, " %s: %s\n", + efi_handle_name ( handle ), strerror ( rc ) ); + goto err_disconnect; + } + } + + /* Success */ + rc = 0; + DBGC2 ( driver, "EFIVETO %s disconnected all handles\n", + efi_handle_name ( driver ) ); + + err_disconnect: + bs->FreePool ( handles ); + err_list: + return rc; +} + +/** + * Uninstall an EFI driver binding protocol + * + * @v driver Driver binding handle + * @ret rc Return status code + */ +static int efi_veto_uninstall ( EFI_HANDLE driver ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + union { + EFI_DRIVER_BINDING_PROTOCOL *binding; + void *interface; + } binding; + EFI_STATUS efirc; + int rc; + + /* Open driver binding protocol */ + if ( ( efirc = bs->OpenProtocol ( + driver, &efi_driver_binding_protocol_guid, + &binding.interface, efi_image_handle, driver, + EFI_OPEN_PROTOCOL_GET_PROTOCOL ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( driver, "EFIVETO %s could not open driver binding " + "protocol: %s\n", efi_handle_name ( driver ), + strerror ( rc ) ); + return rc; + } + + /* Close driver binding protocol */ + bs->CloseProtocol ( driver, &efi_driver_binding_protocol_guid, + efi_image_handle, driver ); + + /* Uninstall driver binding protocol */ + if ( ( efirc = bs->UninstallMultipleProtocolInterfaces ( + driver, &efi_driver_binding_protocol_guid, + binding.binding, NULL ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( driver, "EFIVETO %s could not uninstall driver " + "binding protocol: %s\n", + efi_handle_name ( driver ), strerror ( rc ) ); + return rc; + } + + DBGC2 ( driver, "EFIVETO %s uninstalled driver binding protocol\n", + efi_handle_name ( driver ) ); + return 0; +} + +/** + * Close protocol on handle potentially opened by an EFI driver + * + * @v driver Driver binding handle + * @v handle Potentially opened handle + * @v protocol Opened protocol + * @ret rc Return status code + */ +static int efi_veto_close_protocol ( EFI_HANDLE driver, EFI_HANDLE handle, + EFI_GUID *protocol ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *openers; + EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *opener; + EFI_HANDLE controller; + UINTN count; + unsigned int i; + EFI_STATUS efirc; + int rc; + + /* Retrieve list of openers */ + if ( ( efirc = bs->OpenProtocolInformation ( handle, protocol, &openers, + &count ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( driver, "EFIVETO %s could not retrieve openers", + efi_handle_name ( driver ) ); + DBGC ( driver, " of %s %s: %s", efi_handle_name ( handle ), + efi_guid_ntoa ( protocol ), strerror ( rc ) ); + goto err_list; + } + + /* Close anything opened by this driver */ + for ( i = 0 ; i < count ; i++ ) { + opener = &openers[i]; + if ( opener->AgentHandle != driver ) + continue; + controller = opener->ControllerHandle; + DBGC_EFI_OPENER ( driver, handle, protocol, opener ); + if ( ( efirc = bs->CloseProtocol ( handle, protocol, driver, + controller ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( driver, "EFIVETO %s could not close stray open", + efi_handle_name ( driver ) ); + DBGC ( driver, " of %s: %s\n", + efi_handle_name ( handle ), strerror ( rc ) ); + goto err_close; + } + } + + /* Success */ + rc = 0; + + err_close: + bs->FreePool ( openers ); + err_list: + return rc; +} + +/** + * Close handle potentially opened by an EFI driver + * + * @v driver Driver binding handle + * @v handle Potentially opened handle + * @ret rc Return status code + */ +static int efi_veto_close_handle ( EFI_HANDLE driver, EFI_HANDLE handle ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_GUID **protocols; + UINTN count; + unsigned int i; + EFI_STATUS efirc; + int rc; + + /* Retrieve list of protocols */ + if ( ( efirc = bs->ProtocolsPerHandle ( handle, &protocols, + &count ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( driver, "EFIVETO %s could not retrieve protocols", + efi_handle_name ( driver ) ); + DBGC ( driver, " for %s: %s\n", + efi_handle_name ( handle ), strerror ( rc ) ); + goto err_list; + } + + /* Close each protocol */ + for ( i = 0 ; i < count ; i++ ) { + if ( ( rc = efi_veto_close_protocol ( driver, handle, + protocols[i] ) ) != 0 ) + goto err_close; + } + + /* Success */ + rc = 0; + + err_close: + bs->FreePool ( protocols ); + err_list: + return rc; +} + +/** + * Close all remaining handles opened by an EFI driver + * + * @v driver Driver binding handle + * @ret rc Return status code + */ +static int efi_veto_close ( EFI_HANDLE driver ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_HANDLE *handles; + UINTN count; + unsigned int i; + EFI_STATUS efirc; + int rc; + + /* Enumerate all handles */ + if ( ( efirc = bs->LocateHandleBuffer ( AllHandles, NULL, NULL, + &count, &handles ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( driver, "EFIVETO %s could not enumerate handles: %s\n", + efi_handle_name ( driver ), strerror ( rc ) ); + goto err_list; + } + + /* Close each handle */ + for ( i = 0 ; i < count ; i++ ) { + if ( ( rc = efi_veto_close_handle ( driver, + handles[i] ) ) != 0 ) + goto err_close; + } + + /* Success */ + rc = 0; + DBGC2 ( driver, "EFIVETO %s closed all remaining handles\n", + efi_handle_name ( driver ) ); + + err_close: + bs->FreePool ( handles ); + err_list: + return rc; +} + +/** + * Terminate an EFI driver with extreme prejudice + * + * @v driver Driver binding handle + * @ret rc Return status code + */ +static int efi_veto_destroy ( EFI_HANDLE driver ) { + int rc; + + /* Disconnect driver from all handles */ + if ( ( rc = efi_veto_disconnect ( driver ) ) != 0 ) + return rc; + + /* Uninstall driver binding protocol */ + if ( ( rc = efi_veto_uninstall ( driver ) ) != 0 ) + return rc; + + /* Close any remaining opened handles */ + if ( ( rc = efi_veto_close ( driver ) ) != 0 ) + return rc; + + DBGC ( driver, "EFIVETO %s forcibly removed\n", + efi_handle_name ( driver ) ); + return 0; +} + +/** + * Veto an EFI driver + * + * @v driver Driver binding handle + * @ret rc Return status code + */ +static int efi_veto_driver ( EFI_HANDLE driver ) { + int rc; + + /* Try gracefully unloading the driver */ + if ( ( rc = efi_veto_unload ( driver ) ) == 0 ) + return 0; + + /* If that fails, use a hammer */ + if ( ( rc = efi_veto_destroy ( driver ) ) == 0 ) + return 0; + + return rc; +} + +/** + * Veto Dell Ip4ConfigDxe driver + * + * @v binding Driver binding protocol + * @v loaded Loaded image protocol + * @v wtf Component name protocol, if present + * @v manufacturer Manufacturer name, if present + * @v name Driver name, if present + * @ret vetoed Driver is to be vetoed + */ +static int +efi_veto_dell_ip4config ( EFI_DRIVER_BINDING_PROTOCOL *binding __unused, + EFI_LOADED_IMAGE_PROTOCOL *loaded __unused, + EFI_COMPONENT_NAME_PROTOCOL *wtf __unused, + const char *manufacturer, const CHAR16 *name ) { + static const CHAR16 ip4cfg[] = L"IP4 CONFIG Network Service Driver"; + static const char *dell = "Dell Inc."; + + /* Check manufacturer and driver name */ + if ( ! manufacturer ) + return 0; + if ( ! name ) + return 0; + if ( strcmp ( manufacturer, dell ) != 0 ) + return 0; + if ( memcmp ( name, ip4cfg, sizeof ( ip4cfg ) ) != 0 ) + return 0; + + return 1; +} + +/** + * Veto HP XhciDxe driver + * + * @v binding Driver binding protocol + * @v loaded Loaded image protocol + * @v wtf Component name protocol, if present + * @v manufacturer Manufacturer name, if present + * @v name Driver name, if present + * @ret vetoed Driver is to be vetoed + */ +static int +efi_veto_hp_xhci ( EFI_DRIVER_BINDING_PROTOCOL *binding __unused, + EFI_LOADED_IMAGE_PROTOCOL *loaded __unused, + EFI_COMPONENT_NAME_PROTOCOL *wtf __unused, + const char *manufacturer, const CHAR16 *name ) { + static const CHAR16 xhci[] = L"Usb Xhci Driver"; + static const char *hp = "HP"; + struct pci_driver *driver; + + /* Check manufacturer and driver name */ + if ( ! manufacturer ) + return 0; + if ( ! name ) + return 0; + if ( strcmp ( manufacturer, hp ) != 0 ) + return 0; + if ( memcmp ( name, xhci, sizeof ( xhci ) ) != 0 ) + return 0; + + /* Veto driver only if we have our own xHCI driver */ + for_each_table_entry ( driver, PCI_DRIVERS ) { + if ( driver->class.class == + PCI_CLASS ( PCI_CLASS_SERIAL, PCI_CLASS_SERIAL_USB, + PCI_CLASS_SERIAL_USB_XHCI ) ) { + return 1; + } + } + + return 0; +} + +/** Driver vetoes */ +static struct efi_veto efi_vetoes[] = { + { + .name = "Dell Ip4Config", + .veto = efi_veto_dell_ip4config, + }, + { + .name = "HP Xhci", + .veto = efi_veto_hp_xhci, + }, +}; + +/** + * Find driver veto, if any + * + * @v driver Driver binding handle + * @v manufacturer Manufacturer name, if present + * @ret veto Driver veto, or NULL + * @ret rc Return status code + */ +static int efi_veto_find ( EFI_HANDLE driver, const char *manufacturer, + struct efi_veto **veto ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + union { + EFI_DRIVER_BINDING_PROTOCOL *binding; + void *interface; + } binding; + union { + EFI_LOADED_IMAGE_PROTOCOL *loaded; + void *interface; + } loaded; + union { + EFI_COMPONENT_NAME_PROTOCOL *wtf; + void *interface; + } wtf; + CHAR16 *name; + unsigned int i; + EFI_HANDLE image; + EFI_STATUS efirc; + int rc; + + DBGC2 ( &efi_vetoes, "EFIVETO checking %s\n", + efi_handle_name ( driver ) ); + + /* Mark as not vetoed */ + *veto = NULL; + + /* Open driver binding protocol */ + if ( ( efirc = bs->OpenProtocol ( + driver, &efi_driver_binding_protocol_guid, + &binding.interface, efi_image_handle, driver, + EFI_OPEN_PROTOCOL_GET_PROTOCOL ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( driver, "EFIVETO %s could not open driver binding " + "protocol: %s\n", efi_handle_name ( driver ), + strerror ( rc ) ); + goto err_binding; + } + image = binding.binding->ImageHandle; + + /* Open loaded image protocol */ + if ( ( efirc = bs->OpenProtocol ( + image, &efi_loaded_image_protocol_guid, + &loaded.interface, efi_image_handle, image, + EFI_OPEN_PROTOCOL_GET_PROTOCOL ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( driver, "EFIVETO %s could not open", + efi_handle_name ( driver ) ); + DBGC ( driver, " %s loaded image protocol: %s\n", + efi_handle_name ( image ), strerror ( rc ) ); + goto err_loaded; + } + + /* Open component name protocol, if present*/ + if ( ( efirc = bs->OpenProtocol ( + driver, &efi_component_name_protocol_guid, + &wtf.interface, efi_image_handle, driver, + EFI_OPEN_PROTOCOL_GET_PROTOCOL ) ) != 0 ) { + /* Ignore failure; is not required to be present */ + wtf.interface = NULL; + } + + /* Get driver name, if available */ + if ( wtf.wtf && + ( ( efirc = wtf.wtf->GetDriverName ( wtf.wtf, "eng", + &name ) == 0 ) ) ) { + /* Driver has a name */ + } else { + /* Ignore failure; name is not required to be present */ + name = NULL; + } + + /* Check vetoes */ + for ( i = 0 ; i < ( sizeof ( efi_vetoes ) / + sizeof ( efi_vetoes[0] ) ) ; i++ ) { + if ( efi_vetoes[i].veto ( binding.binding, loaded.loaded, + wtf.wtf, manufacturer, name ) ) { + *veto = &efi_vetoes[i]; + break; + } + } + + /* Success */ + rc = 0; + + /* Close protocols */ + if ( wtf.wtf ) { + bs->CloseProtocol ( driver, &efi_component_name_protocol_guid, + efi_image_handle, driver ); + } + bs->CloseProtocol ( image, &efi_loaded_image_protocol_guid, + efi_image_handle, image ); + err_loaded: + bs->CloseProtocol ( driver, &efi_driver_binding_protocol_guid, + efi_image_handle, driver ); + err_binding: + return rc; +} + +/** + * Remove any vetoed drivers + * + */ +void efi_veto ( void ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + struct efi_veto *veto; + EFI_HANDLE *drivers; + EFI_HANDLE driver; + UINTN num_drivers; + unsigned int i; + char *manufacturer; + EFI_STATUS efirc; + int rc; + + /* Locate all driver binding protocol handles */ + if ( ( efirc = bs->LocateHandleBuffer ( + ByProtocol, &efi_driver_binding_protocol_guid, + NULL, &num_drivers, &drivers ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( &efi_vetoes, "EFIVETO could not list all drivers: " + "%s\n", strerror ( rc ) ); + return; + } + + /* Get manufacturer name */ + fetch_string_setting_copy ( NULL, &manufacturer_setting, + &manufacturer ); + + /* Unload any vetoed drivers */ + for ( i = 0 ; i < num_drivers ; i++ ) { + driver = drivers[i]; + if ( ( rc = efi_veto_find ( driver, manufacturer, + &veto ) ) != 0 ) { + DBGC ( driver, "EFIVETO %s could not determine " + "vetoing: %s\n", + efi_handle_name ( driver ), strerror ( rc ) ); + continue; + } + if ( ! veto ) + continue; + DBGC ( driver, "EFIVETO %s is vetoed (%s)\n", + efi_handle_name ( driver ), veto->name ); + if ( ( rc = efi_veto_driver ( driver ) ) != 0 ) { + DBGC ( driver, "EFIVETO %s could not veto: %s\n", + efi_handle_name ( driver ), strerror ( rc ) ); + } + } + + /* Free manufacturer name */ + free ( manufacturer ); + + /* Free handle list */ + bs->FreePool ( drivers ); +} diff --git a/src/interface/efi/efi_wrap.c b/src/interface/efi/efi_wrap.c index c0c40eec6..5c02a7ee1 100644 --- a/src/interface/efi/efi_wrap.c +++ b/src/interface/efi/efi_wrap.c @@ -37,14 +37,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/efi/Protocol/LoadedImage.h> #include <ipxe/efi/efi_wrap.h> -/** EFI system table wrapper */ -static EFI_SYSTEM_TABLE efi_systab_wrapper; - -/** EFI boot services table wrapper */ -static EFI_BOOT_SERVICES efi_bs_wrapper; - /** Colour for debug messages */ -#define colour &efi_systab_wrapper +#define colour &efi_systab /** * Convert EFI status code to text @@ -112,6 +106,358 @@ static const char * efi_boolean ( BOOLEAN boolean ) { } /** + * Convert EFI TPL to text + * + * @v tpl Task priority level + * @ret text Task priority level as text + */ +static const char * efi_tpl ( EFI_TPL tpl ) { + static char buf[ 19 /* "0xXXXXXXXXXXXXXXXX" + NUL */ ]; + + switch ( tpl ) { + case TPL_APPLICATION: return "Application"; + case TPL_CALLBACK: return "Callback"; + case TPL_NOTIFY: return "Notify"; + case TPL_HIGH_LEVEL: return "HighLevel"; + default: + snprintf ( buf, sizeof ( buf ), "%#lx", + ( unsigned long ) tpl ); + return buf; + } +} + +/** + * Convert EFI allocation type to text + * + * @v type Allocation type + * @ret text Allocation type as text + */ +static const char * efi_allocate_type ( EFI_ALLOCATE_TYPE type ) { + static char buf[ 11 /* "0xXXXXXXXX" + NUL */ ]; + + switch ( type ) { + case AllocateAnyPages: return "AnyPages"; + case AllocateMaxAddress: return "MaxAddress"; + case AllocateAddress: return "Address"; + default: + snprintf ( buf, sizeof ( buf ), "%#x", type ); + return buf; + } +} + +/** + * Convert EFI memory type to text + * + * @v type Memory type + * @ret text Memory type as text + */ +static const char * efi_memory_type ( EFI_MEMORY_TYPE type ) { + static char buf[ 11 /* "0xXXXXXXXX" + NUL */ ]; + + switch ( type ) { + case EfiReservedMemoryType: return "Reserved"; + case EfiLoaderCode: return "LoaderCode"; + case EfiLoaderData: return "LoaderData"; + case EfiBootServicesCode: return "BootCode"; + case EfiBootServicesData: return "BootData"; + case EfiRuntimeServicesCode: return "RuntimeCode"; + case EfiRuntimeServicesData: return "RuntimeData"; + case EfiConventionalMemory: return "Conventional"; + case EfiUnusableMemory: return "Unusable"; + case EfiACPIReclaimMemory: return "ACPIReclaim"; + case EfiACPIMemoryNVS: return "ACPINVS"; + case EfiMemoryMappedIO: return "MMIO"; + case EfiMemoryMappedIOPortSpace:return "PIO"; + case EfiPalCode: return "PalCode"; + case EfiPersistentMemory: return "Persistent"; + default: + snprintf ( buf, sizeof ( buf ), "%#x", type ); + return buf; + } +} + +/** + * Convert EFI timer delay type to text + * + * @v type Timer delay type + * @ret text Timer delay type as text + */ +static const char * efi_timer_delay ( EFI_TIMER_DELAY type ) { + static char buf[ 11 /* "0xXXXXXXXX" + NUL */ ]; + + switch ( type ) { + case TimerCancel: return "Cancel"; + case TimerPeriodic: return "Periodic"; + case TimerRelative: return "Relative"; + default: + snprintf ( buf, sizeof ( buf ), "%#x", type ); + return buf; + } +} + +/** + * Wrap RaiseTPL() + * + */ +static EFI_TPL EFIAPI +efi_raise_tpl_wrapper ( EFI_TPL new_tpl ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + void *retaddr = __builtin_return_address ( 0 ); + EFI_TPL old_tpl; + + DBGCP ( colour, "RaiseTPL ( %s ) ", efi_tpl ( new_tpl ) ); + old_tpl = bs->RaiseTPL ( new_tpl ); + DBGCP ( colour, "= %s -> %p\n", efi_tpl ( old_tpl ), retaddr ); + return old_tpl; +} + +/** + * Wrap RestoreTPL() + * + */ +static VOID EFIAPI +efi_restore_tpl_wrapper ( EFI_TPL old_tpl ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + void *retaddr = __builtin_return_address ( 0 ); + + DBGCP ( colour, "RestoreTPL ( %s ) ", efi_tpl ( old_tpl ) ); + bs->RestoreTPL ( old_tpl ); + DBGCP ( colour, "-> %p\n", retaddr ); +} + +/** + * Wrap AllocatePages() + * + */ +static EFI_STATUS EFIAPI +efi_allocate_pages_wrapper ( EFI_ALLOCATE_TYPE type, + EFI_MEMORY_TYPE memory_type, UINTN pages, + EFI_PHYSICAL_ADDRESS *memory ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + void *retaddr = __builtin_return_address ( 0 ); + EFI_STATUS efirc; + + DBGC2 ( colour, "AllocatePages ( %s, %s, %#llx, %#llx ) ", + efi_allocate_type ( type ), efi_memory_type ( memory_type ), + ( ( unsigned long long ) pages ), + ( ( unsigned long long ) *memory ) ); + efirc = bs->AllocatePages ( type, memory_type, pages, memory ); + DBGC2 ( colour, "= %s ( %#llx ) -> %p\n", efi_status ( efirc ), + ( ( unsigned long long ) *memory ), retaddr ); + return efirc; +} + +/** + * Wrap FreePages() + * + */ +static EFI_STATUS EFIAPI +efi_free_pages_wrapper ( EFI_PHYSICAL_ADDRESS memory, UINTN pages ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + void *retaddr = __builtin_return_address ( 0 ); + EFI_STATUS efirc; + + DBGC2 ( colour, "FreePages ( %#llx, %#llx ) ", + ( ( unsigned long long ) memory ), + ( ( unsigned long long ) pages ) ); + efirc = bs->FreePages ( memory, pages ); + DBGC2 ( colour, "= %s -> %p\n", efi_status ( efirc ), retaddr ); + return efirc; +} + +/** + * Wrap GetMemoryMap() + * + */ +static EFI_STATUS EFIAPI +efi_get_memory_map_wrapper ( UINTN *memory_map_size, + EFI_MEMORY_DESCRIPTOR *memory_map, UINTN *map_key, + UINTN *descriptor_size, + UINT32 *descriptor_version ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + void *retaddr = __builtin_return_address ( 0 ); + EFI_MEMORY_DESCRIPTOR *desc; + size_t remaining; + EFI_STATUS efirc; + + DBGC ( colour, "GetMemoryMap ( %#llx, %p ) ", + ( ( unsigned long long ) *memory_map_size ), memory_map ); + efirc = bs->GetMemoryMap ( memory_map_size, memory_map, map_key, + descriptor_size, descriptor_version ); + DBGC ( colour, "= %s ( %#llx, %#llx, %#llx, v%d", + efi_status ( efirc ), + ( ( unsigned long long ) *memory_map_size ), + ( ( unsigned long long ) *map_key ), + ( ( unsigned long long ) *descriptor_size ), + *descriptor_version ); + if ( DBG_EXTRA && ( efirc == 0 ) ) { + DBGC2 ( colour, ",\n" ); + for ( desc = memory_map, remaining = *memory_map_size ; + remaining >= *descriptor_size ; + desc = ( ( ( void * ) desc ) + *descriptor_size ), + remaining -= *descriptor_size ) { + DBGC2 ( colour, "%#016llx+%#08llx %#016llx " + "%s\n", desc->PhysicalStart, + ( desc->NumberOfPages * EFI_PAGE_SIZE ), + desc->Attribute, + efi_memory_type ( desc->Type ) ); + } + } else { + DBGC ( colour, " " ); + } + DBGC ( colour, ") -> %p\n", retaddr ); + return efirc; +} + +/** + * Wrap AllocatePool() + * + */ +static EFI_STATUS EFIAPI +efi_allocate_pool_wrapper ( EFI_MEMORY_TYPE pool_type, UINTN size, + VOID **buffer ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + void *retaddr = __builtin_return_address ( 0 ); + EFI_STATUS efirc; + + DBGC2 ( colour, "AllocatePool ( %s, %#llx ) ", + efi_memory_type ( pool_type ), + ( ( unsigned long long ) size ) ); + efirc = bs->AllocatePool ( pool_type, size, buffer ); + DBGC2 ( colour, "= %s ( %p ) -> %p\n", + efi_status ( efirc ), *buffer, retaddr ); + return efirc; +} + +/** + * Wrap FreePool() + * + */ +static EFI_STATUS EFIAPI +efi_free_pool_wrapper ( VOID *buffer ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + void *retaddr = __builtin_return_address ( 0 ); + EFI_STATUS efirc; + + DBGC2 ( colour, "FreePool ( %p ) ", buffer ); + efirc = bs->FreePool ( buffer ); + DBGC2 ( colour, "= %s -> %p\n", efi_status ( efirc ), retaddr ); + return efirc; +} + +/** + * Wrap CreateEvent() + * + */ +static EFI_STATUS EFIAPI +efi_create_event_wrapper ( UINT32 type, EFI_TPL notify_tpl, + EFI_EVENT_NOTIFY notify_function, + VOID *notify_context, EFI_EVENT *event ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + void *retaddr = __builtin_return_address ( 0 ); + EFI_STATUS efirc; + + DBGC ( colour, "CreateEvent ( %#x, %s, %p, %p ) ", + type, efi_tpl ( notify_tpl ), notify_function, notify_context ); + efirc = bs->CreateEvent ( type, notify_tpl, notify_function, + notify_context, event ); + DBGC ( colour, "= %s ( %p ) -> %p\n", + efi_status ( efirc ), *event, retaddr ); + return efirc; +} + +/** + * Wrap SetTimer() + * + */ +static EFI_STATUS EFIAPI +efi_set_timer_wrapper ( EFI_EVENT event, EFI_TIMER_DELAY type, + UINT64 trigger_time ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + void *retaddr = __builtin_return_address ( 0 ); + EFI_STATUS efirc; + + DBGC ( colour, "SetTimer ( %p, %s, %ld.%07ld00s ) ", + event, efi_timer_delay ( type ), + ( ( unsigned long ) ( trigger_time / 10000000 ) ), + ( ( unsigned long ) ( trigger_time % 10000000 ) ) ); + efirc = bs->SetTimer ( event, type, trigger_time ); + DBGC ( colour, "= %s -> %p\n", efi_status ( efirc ), retaddr ); + return efirc; +} + +/** + * Wrap WaitForEvent() + * + */ +static EFI_STATUS EFIAPI +efi_wait_for_event_wrapper ( UINTN number_of_events, EFI_EVENT *event, + UINTN *index ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + void *retaddr = __builtin_return_address ( 0 ); + unsigned int i; + EFI_STATUS efirc; + + DBGC ( colour, "WaitForEvent (" ); + for ( i = 0 ; i < number_of_events ; i++ ) + DBGC ( colour, " %p", event[i] ); + DBGC ( colour, " ) " ); + efirc = bs->WaitForEvent ( number_of_events, event, index ); + DBGC ( colour, "= %s", efi_status ( efirc ) ); + if ( efirc == 0 ) + DBGC ( colour, " ( %p )", event[*index] ); + DBGC ( colour, " -> %p\n", retaddr ); + return efirc; +} + +/** + * Wrap SignalEvent() + * + */ +static EFI_STATUS EFIAPI +efi_signal_event_wrapper ( EFI_EVENT event ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + void *retaddr = __builtin_return_address ( 0 ); + EFI_STATUS efirc; + + DBGC2 ( colour, "SignalEvent ( %p ) ", event ); + efirc = bs->SignalEvent ( event ); + DBGC2 ( colour, "= %s -> %p\n", efi_status ( efirc ), retaddr ); + return efirc; +} + +/** + * Wrap CloseEvent() + * + */ +static EFI_STATUS EFIAPI +efi_close_event_wrapper ( EFI_EVENT event ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + void *retaddr = __builtin_return_address ( 0 ); + EFI_STATUS efirc; + + DBGC ( colour, "CloseEvent ( %p ) ", event ); + efirc = bs->SignalEvent ( event ); + DBGC ( colour, "= %s -> %p\n", efi_status ( efirc ), retaddr ); + return efirc; +} +/** + * Wrap CheckEvent() + * + */ +static EFI_STATUS EFIAPI +efi_check_event_wrapper ( EFI_EVENT event ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + void *retaddr = __builtin_return_address ( 0 ); + EFI_STATUS efirc; + + DBGCP ( colour, "CheckEvent ( %p ) ", event ); + efirc = bs->SignalEvent ( event ); + DBGCP ( colour, "= %s -> %p\n", efi_status ( efirc ), retaddr ); + return efirc; +} + +/** * Wrap InstallProtocolInterface() * */ @@ -195,6 +541,25 @@ efi_handle_protocol_wrapper ( EFI_HANDLE handle, EFI_GUID *protocol, } /** + * Wrap RegisterProtocolNotify() + * + */ +static EFI_STATUS EFIAPI +efi_register_protocol_notify_wrapper ( EFI_GUID *protocol, EFI_EVENT event, + VOID **registration ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + void *retaddr = __builtin_return_address ( 0 ); + EFI_STATUS efirc; + + DBGC ( colour, "RegisterProtocolNotify ( %s, %p ) ", + efi_guid_ntoa ( protocol ), event ); + efirc = bs->RegisterProtocolNotify ( protocol, event, registration ); + DBGC ( colour, "= %s ( %p ) -> %p\n", + efi_status ( efirc ), *registration, retaddr ); + return efirc; +} + +/** * Wrap LocateHandle() * */ @@ -249,6 +614,23 @@ efi_locate_device_path_wrapper ( EFI_GUID *protocol, } /** + * Wrap InstallConfigurationTable() + * + */ +static EFI_STATUS EFIAPI +efi_install_configuration_table_wrapper ( EFI_GUID *guid, VOID *table ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + void *retaddr = __builtin_return_address ( 0 ); + EFI_STATUS efirc; + + DBGC ( colour, "InstallConfigurationTable ( %s, %p ) ", + efi_guid_ntoa ( guid ), table ); + efirc = bs->InstallConfigurationTable ( guid, table ); + DBGC ( colour, "= %s -> %p\n", efi_status ( efirc ), retaddr ); + return efirc; +} + +/** * Wrap LoadImage() * */ @@ -362,6 +744,61 @@ efi_exit_boot_services_wrapper ( EFI_HANDLE image_handle, UINTN map_key ) { } /** + * Wrap GetNextMonotonicCount() + * + */ +static EFI_STATUS EFIAPI +efi_get_next_monotonic_count_wrapper ( UINT64 *count ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + void *retaddr = __builtin_return_address ( 0 ); + EFI_STATUS efirc; + + DBGCP ( colour, "GetNextMonotonicCount() " ); + efirc = bs->GetNextMonotonicCount ( count ); + DBGCP ( colour, "= %s ( %#llx ) -> %p\n", + efi_status ( efirc ), *count, retaddr ); + return efirc; +} + +/** + * Wrap Stall() + * + */ +static EFI_STATUS EFIAPI +efi_stall_wrapper ( UINTN microseconds ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + void *retaddr = __builtin_return_address ( 0 ); + EFI_STATUS efirc; + + DBGC2 ( colour, "Stall ( %ld.%06lds ) ", + ( ( unsigned long ) ( microseconds / 1000000 ) ), + ( ( unsigned long ) ( microseconds % 1000000 ) ) ); + efirc = bs->Stall ( microseconds ); + DBGC2 ( colour, "= %s -> %p\n", efi_status ( efirc ), retaddr ); + return efirc; +} + +/** + * Wrap SetWatchdogTimer() + * + */ +static EFI_STATUS EFIAPI +efi_set_watchdog_timer_wrapper ( UINTN timeout, UINT64 watchdog_code, + UINTN data_size, CHAR16 *watchdog_data ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + void *retaddr = __builtin_return_address ( 0 ); + EFI_STATUS efirc; + + DBGC ( colour, "SetWatchdogTimer ( %lds, %#llx, %#llx, %p ) ", + ( ( unsigned long ) timeout ), watchdog_code, + ( ( unsigned long long ) data_size ), watchdog_data ); + efirc = bs->SetWatchdogTimer ( timeout, watchdog_code, data_size, + watchdog_data ); + DBGC ( colour, "= %s -> %p\n", efi_status ( efirc ), retaddr ); + return efirc; +} + +/** * Wrap ConnectController() * */ @@ -463,6 +900,29 @@ efi_close_protocol_wrapper ( EFI_HANDLE handle, EFI_GUID *protocol, } /** + * Wrap OpenProtocolInformation() + * + */ +static EFI_STATUS EFIAPI +efi_open_protocol_information_wrapper ( EFI_HANDLE handle, EFI_GUID *protocol, + EFI_OPEN_PROTOCOL_INFORMATION_ENTRY + **entry_buffer, + UINTN *entry_count ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + void *retaddr = __builtin_return_address ( 0 ); + EFI_STATUS efirc; + + DBGC ( colour, "OpenProtocolInformation ( %s, %s ) ", + efi_handle_name ( handle ), efi_guid_ntoa ( protocol ) ); + efirc = bs->OpenProtocolInformation ( handle, protocol, entry_buffer, + entry_count ); + DBGC ( colour, "= %s ( %p, %#llx ) -> %p\n", + efi_status ( efirc ), *entry_buffer, + ( ( unsigned long long ) *entry_count ), retaddr ); + return efirc; +} + +/** * Wrap ProtocolsPerHandle() * */ @@ -542,29 +1002,157 @@ efi_locate_protocol_wrapper ( EFI_GUID *protocol, VOID *registration, return efirc; } +/** Maximum number of interfaces for wrapped ...MultipleProtocolInterfaces() */ +#define MAX_WRAP_MULTI 20 + /** - * Wrap the calls made by a loaded image + * Wrap InstallMultipleProtocolInterfaces() * - * @v handle Image handle */ - void efi_wrap ( EFI_HANDLE handle ) { +static EFI_STATUS EFIAPI +efi_install_multiple_protocol_interfaces_wrapper ( EFI_HANDLE *handle, ... ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - union { - EFI_LOADED_IMAGE_PROTOCOL *image; - void *intf; - } loaded; + void *retaddr = __builtin_return_address ( 0 ); + EFI_GUID *protocol[ MAX_WRAP_MULTI + 1 ]; + VOID *interface[MAX_WRAP_MULTI]; + VA_LIST ap; + unsigned int i; EFI_STATUS efirc; - int rc; - /* Do nothing unless debugging is enabled */ - if ( ! DBG_LOG ) - return; + DBGC ( colour, "InstallMultipleProtocolInterfaces ( %s", + efi_handle_name ( *handle ) ); + memset ( protocol, 0, sizeof ( protocol ) ); + memset ( interface, 0, sizeof ( interface ) ); + VA_START ( ap, handle ); + for ( i = 0 ; ( protocol[i] = VA_ARG ( ap, EFI_GUID * ) ) ; i++ ) { + if ( i == MAX_WRAP_MULTI ) { + VA_END ( ap ); + efirc = EFI_OUT_OF_RESOURCES; + DBGC ( colour, "<FATAL: too many arguments> ) = %s " + "-> %p\n", efi_status ( efirc ), retaddr ); + return efirc; + } + interface[i] = VA_ARG ( ap, VOID * ); + DBGC ( colour, ", %s, %p", + efi_guid_ntoa ( protocol[i] ), interface[i] ); + } + VA_END ( ap ); + DBGC ( colour, " ) " ); + efirc = bs->InstallMultipleProtocolInterfaces ( handle, + protocol[0], interface[0], protocol[1], interface[1], + protocol[2], interface[2], protocol[3], interface[3], + protocol[4], interface[4], protocol[5], interface[5], + protocol[6], interface[6], protocol[7], interface[7], + protocol[8], interface[8], protocol[9], interface[9], + protocol[10], interface[10], protocol[11], interface[11], + protocol[12], interface[12], protocol[13], interface[13], + protocol[14], interface[14], protocol[15], interface[15], + protocol[16], interface[16], protocol[17], interface[17], + protocol[18], interface[18], protocol[19], interface[19], + NULL ); + DBGC ( colour, "= %s ( %s ) -> %p\n", + efi_status ( efirc ), efi_handle_name ( *handle ), retaddr ); + return efirc; +} - /* Populate table wrappers */ - memcpy ( &efi_systab_wrapper, efi_systab, - sizeof ( efi_systab_wrapper ) ); +/** + * Wrap UninstallMultipleProtocolInterfaces() + * + */ +static EFI_STATUS EFIAPI +efi_uninstall_multiple_protocol_interfaces_wrapper ( EFI_HANDLE handle, ... ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + void *retaddr = __builtin_return_address ( 0 ); + EFI_GUID *protocol[ MAX_WRAP_MULTI + 1 ]; + VOID *interface[MAX_WRAP_MULTI]; + VA_LIST ap; + unsigned int i; + EFI_STATUS efirc; + + DBGC ( colour, "UninstallMultipleProtocolInterfaces ( %s", + efi_handle_name ( handle ) ); + memset ( protocol, 0, sizeof ( protocol ) ); + memset ( interface, 0, sizeof ( interface ) ); + VA_START ( ap, handle ); + for ( i = 0 ; ( protocol[i] = VA_ARG ( ap, EFI_GUID * ) ) ; i++ ) { + if ( i == MAX_WRAP_MULTI ) { + VA_END ( ap ); + efirc = EFI_OUT_OF_RESOURCES; + DBGC ( colour, "<FATAL: too many arguments> ) = %s " + "-> %p\n", efi_status ( efirc ), retaddr ); + return efirc; + } + interface[i] = VA_ARG ( ap, VOID * ); + DBGC ( colour, ", %s, %p", + efi_guid_ntoa ( protocol[i] ), interface[i] ); + } + VA_END ( ap ); + DBGC ( colour, " ) " ); + efirc = bs->UninstallMultipleProtocolInterfaces ( handle, + protocol[0], interface[0], protocol[1], interface[1], + protocol[2], interface[2], protocol[3], interface[3], + protocol[4], interface[4], protocol[5], interface[5], + protocol[6], interface[6], protocol[7], interface[7], + protocol[8], interface[8], protocol[9], interface[9], + protocol[10], interface[10], protocol[11], interface[11], + protocol[12], interface[12], protocol[13], interface[13], + protocol[14], interface[14], protocol[15], interface[15], + protocol[16], interface[16], protocol[17], interface[17], + protocol[18], interface[18], protocol[19], interface[19], + NULL ); + DBGC ( colour, "= %s -> %p\n", + efi_status ( efirc ), retaddr ); + return efirc; +} + +/** + * Wrap CreateEventEx() + * + */ +static EFI_STATUS EFIAPI +efi_create_event_ex_wrapper ( UINT32 type, EFI_TPL notify_tpl, + EFI_EVENT_NOTIFY notify_function, + CONST VOID *notify_context, + CONST EFI_GUID *event_group, EFI_EVENT *event ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + void *retaddr = __builtin_return_address ( 0 ); + EFI_STATUS efirc; + + DBGC ( colour, "CreateEventEx ( %#x, %s, %p, %p, %s ) ", + type, efi_tpl ( notify_tpl ), notify_function, notify_context, + efi_guid_ntoa ( event_group ) ); + efirc = bs->CreateEventEx ( type, notify_tpl, notify_function, + notify_context, event_group, event ); + DBGC ( colour, "= %s ( %p ) -> %p\n", + efi_status ( efirc ), *event, retaddr ); + return efirc; +} + +/** + * Build table wrappers + * + * @ret systab Wrapped system table + */ +EFI_SYSTEM_TABLE * efi_wrap_systab ( void ) { + static EFI_SYSTEM_TABLE efi_systab_wrapper; + static EFI_BOOT_SERVICES efi_bs_wrapper; + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + + /* Build boot services table wrapper */ memcpy ( &efi_bs_wrapper, bs, sizeof ( efi_bs_wrapper ) ); - efi_systab_wrapper.BootServices = &efi_bs_wrapper; + efi_bs_wrapper.RaiseTPL = efi_raise_tpl_wrapper; + efi_bs_wrapper.RestoreTPL = efi_restore_tpl_wrapper; + efi_bs_wrapper.AllocatePages = efi_allocate_pages_wrapper; + efi_bs_wrapper.FreePages = efi_free_pages_wrapper; + efi_bs_wrapper.GetMemoryMap = efi_get_memory_map_wrapper; + efi_bs_wrapper.AllocatePool = efi_allocate_pool_wrapper; + efi_bs_wrapper.FreePool = efi_free_pool_wrapper; + efi_bs_wrapper.CreateEvent = efi_create_event_wrapper; + efi_bs_wrapper.SetTimer = efi_set_timer_wrapper; + efi_bs_wrapper.WaitForEvent = efi_wait_for_event_wrapper; + efi_bs_wrapper.SignalEvent = efi_signal_event_wrapper; + efi_bs_wrapper.CloseEvent = efi_close_event_wrapper; + efi_bs_wrapper.CheckEvent = efi_check_event_wrapper; efi_bs_wrapper.InstallProtocolInterface = efi_install_protocol_interface_wrapper; efi_bs_wrapper.ReinstallProtocolInterface @@ -572,24 +1160,65 @@ efi_locate_protocol_wrapper ( EFI_GUID *protocol, VOID *registration, efi_bs_wrapper.UninstallProtocolInterface = efi_uninstall_protocol_interface_wrapper; efi_bs_wrapper.HandleProtocol = efi_handle_protocol_wrapper; + efi_bs_wrapper.RegisterProtocolNotify + = efi_register_protocol_notify_wrapper; efi_bs_wrapper.LocateHandle = efi_locate_handle_wrapper; efi_bs_wrapper.LocateDevicePath = efi_locate_device_path_wrapper; + efi_bs_wrapper.InstallConfigurationTable + = efi_install_configuration_table_wrapper; efi_bs_wrapper.LoadImage = efi_load_image_wrapper; efi_bs_wrapper.StartImage = efi_start_image_wrapper; efi_bs_wrapper.Exit = efi_exit_wrapper; efi_bs_wrapper.UnloadImage = efi_unload_image_wrapper; efi_bs_wrapper.ExitBootServices = efi_exit_boot_services_wrapper; + efi_bs_wrapper.GetNextMonotonicCount + = efi_get_next_monotonic_count_wrapper; + efi_bs_wrapper.Stall = efi_stall_wrapper; + efi_bs_wrapper.SetWatchdogTimer = efi_set_watchdog_timer_wrapper; efi_bs_wrapper.ConnectController = efi_connect_controller_wrapper; efi_bs_wrapper.DisconnectController = efi_disconnect_controller_wrapper; efi_bs_wrapper.OpenProtocol = efi_open_protocol_wrapper; efi_bs_wrapper.CloseProtocol = efi_close_protocol_wrapper; + efi_bs_wrapper.OpenProtocolInformation + = efi_open_protocol_information_wrapper; efi_bs_wrapper.ProtocolsPerHandle = efi_protocols_per_handle_wrapper; efi_bs_wrapper.LocateHandleBuffer = efi_locate_handle_buffer_wrapper; efi_bs_wrapper.LocateProtocol = efi_locate_protocol_wrapper; + efi_bs_wrapper.InstallMultipleProtocolInterfaces + = efi_install_multiple_protocol_interfaces_wrapper; + efi_bs_wrapper.UninstallMultipleProtocolInterfaces + = efi_uninstall_multiple_protocol_interfaces_wrapper; + efi_bs_wrapper.CreateEventEx = efi_create_event_ex_wrapper; + + /* Build system table wrapper */ + memcpy ( &efi_systab_wrapper, efi_systab, + sizeof ( efi_systab_wrapper ) ); + efi_systab_wrapper.BootServices = &efi_bs_wrapper; + + return &efi_systab_wrapper; +} + +/** + * Wrap the calls made by a loaded image + * + * @v handle Image handle + */ +void efi_wrap ( EFI_HANDLE handle ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + union { + EFI_LOADED_IMAGE_PROTOCOL *image; + void *intf; + } loaded; + EFI_STATUS efirc; + int rc; + + /* Do nothing unless debugging is enabled */ + if ( ! DBG_LOG ) + return; /* Open loaded image protocol */ if ( ( efirc = bs->OpenProtocol ( handle, @@ -603,7 +1232,7 @@ efi_locate_protocol_wrapper ( EFI_GUID *protocol, VOID *registration, } /* Provide system table wrapper to image */ - loaded.image->SystemTable = &efi_systab_wrapper; + loaded.image->SystemTable = efi_wrap_systab(); DBGC ( colour, "WRAP %s at base %p has protocols:\n", efi_handle_name ( handle ), loaded.image->ImageBase ); DBGC_EFI_PROTOCOLS ( colour, handle ); diff --git a/src/interface/efi/efidrvprefix.c b/src/interface/efi/efidrvprefix.c index ac7d94374..9ca54ff4f 100644 --- a/src/interface/efi/efidrvprefix.c +++ b/src/interface/efi/efidrvprefix.c @@ -34,8 +34,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); */ EFI_STATUS EFIAPI _efidrv_start ( EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *systab ) { - EFI_BOOT_SERVICES *bs; - EFI_TPL saved_tpl; + static struct efi_saved_tpl tpl; /* avoid triggering stack protector */ EFI_STATUS efirc; /* Initialise stack cookie */ @@ -46,15 +45,14 @@ EFI_STATUS EFIAPI _efidrv_start ( EFI_HANDLE image_handle, return efirc; /* Raise TPL */ - bs = efi_systab->BootServices; - saved_tpl = bs->RaiseTPL ( TPL_CALLBACK ); + efi_raise_tpl ( &tpl ); /* Initialise iPXE environment */ initialise(); startup(); /* Restore TPL */ - bs->RestoreTPL ( saved_tpl ); + efi_restore_tpl ( &tpl ); return 0; } diff --git a/src/interface/efi/efiprefix.c b/src/interface/efi/efiprefix.c index 2c5a5b31d..126c813d7 100644 --- a/src/interface/efi/efiprefix.c +++ b/src/interface/efi/efiprefix.c @@ -22,12 +22,15 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <stdlib.h> #include <errno.h> #include <ipxe/device.h> +#include <ipxe/init.h> #include <ipxe/efi/efi.h> #include <ipxe/efi/efi_driver.h> #include <ipxe/efi/efi_snp.h> #include <ipxe/efi/efi_autoboot.h> +#include <ipxe/efi/efi_autoexec.h> +#include <ipxe/efi/efi_cachedhcp.h> #include <ipxe/efi/efi_watchdog.h> -#include <ipxe/efi/efi_blacklist.h> +#include <ipxe/efi/efi_veto.h> /** * EFI entry point @@ -48,9 +51,6 @@ EFI_STATUS EFIAPI _efi_start ( EFI_HANDLE image_handle, if ( ( efirc = efi_init ( image_handle, systab ) ) != 0 ) goto err_init; - /* Record autoboot device (if any) */ - efi_set_autoboot(); - /* Claim SNP devices for use by iPXE */ efi_snp_claim(); @@ -73,14 +73,36 @@ EFI_STATUS EFIAPI _efi_start ( EFI_HANDLE image_handle, } /** + * Initialise EFI application + * + */ +static void efi_init_application ( void ) { + EFI_HANDLE device = efi_loaded_image->DeviceHandle; + + /* Identify autoboot device, if any */ + efi_set_autoboot_ll_addr ( device ); + + /* Store cached DHCP packet, if any */ + efi_cachedhcp_record ( device ); + + /* Load autoexec script, if any */ + efi_autoexec_load ( device ); +} + +/** EFI application initialisation function */ +struct init_fn efi_init_application_fn __init_fn ( INIT_NORMAL ) = { + .initialise = efi_init_application, +}; + +/** * Probe EFI root bus * * @v rootdev EFI root device */ static int efi_probe ( struct root_device *rootdev __unused ) { - /* Unloaded any blacklisted drivers */ - efi_unload_blacklist(); + /* Remove any vetoed drivers */ + efi_veto(); /* Connect our drivers */ return efi_driver_connect_all(); |
