summaryrefslogtreecommitdiffstats
path: root/src/interface/efi
diff options
context:
space:
mode:
authorSimon Rettberg2021-03-08 15:55:33 +0100
committerSimon Rettberg2021-03-08 15:55:33 +0100
commit6707d9218c8e6e760e53068d5f41ceb31fac6ea0 (patch)
tree88e8fc84ede2a0ed2c1cec3a6109beb9fb53abf5 /src/interface/efi
parentMerge branch 'master' into openslx (diff)
parent[linux] Do not assume that stat() works on sysfs files (diff)
downloadipxe-6707d9218c8e6e760e53068d5f41ceb31fac6ea0.tar.gz
ipxe-6707d9218c8e6e760e53068d5f41ceb31fac6ea0.tar.xz
ipxe-6707d9218c8e6e760e53068d5f41ceb31fac6ea0.zip
Merge branch 'master' into openslx
Diffstat (limited to 'src/interface/efi')
-rw-r--r--src/interface/efi/efi_acpi.c1
-rw-r--r--src/interface/efi/efi_autoboot.c26
-rw-r--r--src/interface/efi/efi_autoexec.c206
-rw-r--r--src/interface/efi/efi_blacklist.c237
-rw-r--r--src/interface/efi/efi_block.c147
-rw-r--r--src/interface/efi/efi_bofm.c15
-rw-r--r--src/interface/efi/efi_cachedhcp.c94
-rw-r--r--src/interface/efi/efi_console.c77
-rw-r--r--src/interface/efi/efi_debug.c47
-rw-r--r--src/interface/efi/efi_driver.c23
-rw-r--r--src/interface/efi/efi_entropy.c4
-rw-r--r--src/interface/efi/efi_init.c38
-rw-r--r--src/interface/efi/efi_local.c11
-rw-r--r--src/interface/efi/efi_null.c672
-rw-r--r--src/interface/efi/efi_path.c506
-rw-r--r--src/interface/efi/efi_pci.c525
-rw-r--r--src/interface/efi/efi_pxe.c40
-rw-r--r--src/interface/efi/efi_smbios.c44
-rw-r--r--src/interface/efi/efi_snp.c178
-rw-r--r--src/interface/efi/efi_snp_hii.c83
-rw-r--r--src/interface/efi/efi_timer.c15
-rw-r--r--src/interface/efi/efi_usb.c319
-rw-r--r--src/interface/efi/efi_utils.c38
-rw-r--r--src/interface/efi/efi_veto.c609
-rw-r--r--src/interface/efi/efi_wrap.c675
-rw-r--r--src/interface/efi/efidrvprefix.c8
-rw-r--r--src/interface/efi/efiprefix.c34
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();