diff options
Diffstat (limited to 'src/interface/efi')
-rw-r--r-- | src/interface/efi/efi_debug.c | 98 | ||||
-rw-r--r-- | src/interface/efi/efi_file.c | 116 | ||||
-rw-r--r-- | src/interface/efi/efi_guid.c | 87 | ||||
-rw-r--r-- | src/interface/efi/efi_settings.c | 236 | ||||
-rw-r--r-- | src/interface/efi/efi_shim.c | 402 | ||||
-rw-r--r-- | src/interface/efi/efi_snp.c | 9 | ||||
-rw-r--r-- | src/interface/efi/efi_strings.c | 44 | ||||
-rw-r--r-- | src/interface/efi/efi_veto.c | 160 |
8 files changed, 1062 insertions, 90 deletions
diff --git a/src/interface/efi/efi_debug.c b/src/interface/efi/efi_debug.c index 967bb618..1372776f 100644 --- a/src/interface/efi/efi_debug.c +++ b/src/interface/efi/efi_debug.c @@ -47,12 +47,30 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); static EFI_DEVICE_PATH_TO_TEXT_PROTOCOL *efidpt; EFI_REQUEST_PROTOCOL ( EFI_DEVICE_PATH_TO_TEXT_PROTOCOL, &efidpt ); -/** Iscsi4Dxe module GUID */ +/** HttpBootDxe module GUID */ +static EFI_GUID efi_http_boot_dxe_guid = { + 0xecebcb00, 0xd9c8, 0x11e4, + { 0xaf, 0x3d, 0x8c, 0xdc, 0xd4, 0x26, 0xc9, 0x73 } +}; + +/** IScsiDxe module GUID */ +static EFI_GUID efi_iscsi_dxe_guid = { + 0x86cddf93, 0x4872, 0x4597, + { 0x8a, 0xf9, 0xa3, 0x5a, 0xe4, 0xd3, 0x72, 0x5f } +}; + +/** Old IScsi4Dxe module GUID */ static EFI_GUID efi_iscsi4_dxe_guid = { 0x4579b72d, 0x7ec4, 0x4dd4, { 0x84, 0x86, 0x08, 0x3c, 0x86, 0xb1, 0x82, 0xa7 } }; +/** UefiPxeBcDxe module GUID */ +static EFI_GUID efi_uefi_pxe_bc_dxe_guid = { + 0xb95e9fda, 0x26de, 0x48d2, + { 0x88, 0x07, 0x1f, 0x91, 0x07, 0xac, 0x5e, 0x3a } +}; + /** VlanConfigDxe module GUID */ static EFI_GUID efi_vlan_config_dxe_guid = { 0xe4f61863, 0xfe2c, 0x4b56, @@ -99,20 +117,48 @@ static struct efi_well_known_guid efi_well_known_guids[] = { "Dhcp4" }, { &efi_dhcp4_service_binding_protocol_guid, "Dhcp4Sb" }, + { &efi_dhcp6_protocol_guid, + "Dhcp6" }, + { &efi_dhcp6_service_binding_protocol_guid, + "Dhcp6Sb" }, { &efi_disk_io_protocol_guid, "DiskIo" }, + { &efi_dns4_protocol_guid, + "Dns4" }, + { &efi_dns4_service_binding_protocol_guid, + "Dns4Sb" }, + { &efi_dns6_protocol_guid, + "Dns6" }, + { &efi_dns6_service_binding_protocol_guid, + "Dns6Sb" }, { &efi_graphics_output_protocol_guid, "GraphicsOutput" }, { &efi_hii_config_access_protocol_guid, "HiiConfigAccess" }, { &efi_hii_font_protocol_guid, "HiiFont" }, + { &efi_http_boot_dxe_guid, + "HttpBootDxe" }, + { &efi_http_protocol_guid, + "Http" }, + { &efi_http_service_binding_protocol_guid, + "HttpSb" }, { &efi_ip4_protocol_guid, "Ip4" }, { &efi_ip4_config_protocol_guid, "Ip4Config" }, + { &efi_ip4_config2_protocol_guid, + "Ip4Config2" }, { &efi_ip4_service_binding_protocol_guid, "Ip4Sb" }, + { &efi_ip6_protocol_guid, + "Ip6" }, + { &efi_ip6_config_protocol_guid, + "Ip6Config" }, + { &efi_ip6_service_binding_protocol_guid, + "Ip6Sb" }, + { &efi_iscsi_dxe_guid, + "IScsiDxe" }, { &efi_iscsi4_dxe_guid, "IScsi4Dxe" }, { &efi_load_file_protocol_guid, @@ -131,6 +177,10 @@ static struct efi_well_known_guid efi_well_known_guids[] = { "Mtftp4" }, { &efi_mtftp4_service_binding_protocol_guid, "Mtftp4Sb" }, + { &efi_mtftp6_protocol_guid, + "Mtftp6" }, + { &efi_mtftp6_service_binding_protocol_guid, + "Mtftp6Sb" }, { &efi_nii_protocol_guid, "Nii" }, { &efi_nii31_protocol_guid, @@ -143,6 +193,8 @@ static struct efi_well_known_guid efi_well_known_guids[] = { "PxeBaseCode" }, { &efi_serial_io_protocol_guid, "SerialIo" }, + { &efi_shim_lock_protocol_guid, + "ShimLock" }, { &efi_simple_file_system_protocol_guid, "SimpleFileSystem" }, { &efi_simple_network_protocol_guid, @@ -161,12 +213,22 @@ static struct efi_well_known_guid efi_well_known_guids[] = { "Tcp4" }, { &efi_tcp4_service_binding_protocol_guid, "Tcp4Sb" }, + { &efi_tcp6_protocol_guid, + "Tcp6" }, + { &efi_tcp6_service_binding_protocol_guid, + "Tcp6Sb" }, { &efi_tree_protocol_guid, "TrEE" }, { &efi_udp4_protocol_guid, "Udp4" }, { &efi_udp4_service_binding_protocol_guid, "Udp4Sb" }, + { &efi_udp6_protocol_guid, + "Udp6" }, + { &efi_udp6_service_binding_protocol_guid, + "Udp6Sb" }, + { &efi_uefi_pxe_bc_dxe_guid, + "UefiPxeBcDxe" }, { &efi_uga_draw_protocol_guid, "UgaDraw" }, { &efi_unicode_collation_protocol_guid, @@ -325,6 +387,34 @@ void dbg_efi_openers ( EFI_HANDLE handle, EFI_GUID *protocol ) { } /** + * Print protocol information on a given handle + * + * @v handle EFI handle + * @v protocol Protocol GUID + */ +void dbg_efi_protocol ( EFI_HANDLE handle, EFI_GUID *protocol ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + VOID *interface; + EFI_STATUS efirc; + int rc; + + /* Get protocol instance */ + if ( ( efirc = bs->HandleProtocol ( handle, protocol, + &interface ) ) != 0 ) { + rc = -EEFI ( efirc ); + printf ( "HANDLE %s could not identify %s: %s\n", + efi_handle_name ( handle ), + efi_guid_ntoa ( protocol ), strerror ( rc ) ); + return; + } + printf ( "HANDLE %s %s at %p\n", efi_handle_name ( handle ), + efi_guid_ntoa ( protocol ), interface ); + + /* Dump list of openers */ + dbg_efi_openers ( handle, protocol ); +} + +/** * Print list of protocol handlers attached to a handle * * @v handle EFI handle @@ -332,7 +422,6 @@ void dbg_efi_openers ( EFI_HANDLE handle, EFI_GUID *protocol ) { void dbg_efi_protocols ( EFI_HANDLE handle ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; EFI_GUID **protocols; - EFI_GUID *protocol; UINTN count; unsigned int i; EFI_STATUS efirc; @@ -355,10 +444,7 @@ void dbg_efi_protocols ( EFI_HANDLE handle ) { /* Dump list of protocols */ for ( i = 0 ; i < count ; i++ ) { - protocol = protocols[i]; - printf ( "HANDLE %s %s supported\n", efi_handle_name ( handle ), - efi_guid_ntoa ( protocol ) ); - dbg_efi_openers ( handle, protocol ); + dbg_efi_protocol ( handle, protocols[i] ); } /* Free list */ diff --git a/src/interface/efi/efi_file.c b/src/interface/efi/efi_file.c index b232591d..2ae3a0cb 100644 --- a/src/interface/efi/efi_file.c +++ b/src/interface/efi/efi_file.c @@ -250,6 +250,10 @@ static size_t efi_file_read_initrd ( struct efi_file_reader *reader ) { len = 0; for_each_image ( image ) { + /* Skip hidden images */ + if ( image->flags & IMAGE_HIDDEN ) + continue; + /* Pad to alignment boundary */ pad_len = ( ( -reader->pos ) & ( INITRD_ALIGN - 1 ) ); if ( pad_len ) { @@ -291,10 +295,12 @@ static size_t efi_file_read_initrd ( struct efi_file_reader *reader ) { * Open fixed file * * @v file EFI file + * @v wname Filename * @v new New EFI file * @ret efirc EFI status code */ static EFI_STATUS efi_file_open_fixed ( struct efi_file *file, + const wchar_t *wname, EFI_FILE_PROTOCOL **new ) { /* Increment reference count */ @@ -303,7 +309,8 @@ static EFI_STATUS efi_file_open_fixed ( struct efi_file *file, /* Return opened file */ *new = &file->file; - DBGC ( file, "EFIFILE %s opened\n", efi_file_name ( file ) ); + DBGC ( file, "EFIFILE %s opened via %ls\n", + efi_file_name ( file ), wname ); return 0; } @@ -321,6 +328,36 @@ static void efi_file_image ( struct efi_file *file, struct image *image ) { } /** + * Open image-backed file + * + * @v image Image + * @v wname Filename + * @v new New EFI file + * @ret efirc EFI status code + */ +static EFI_STATUS efi_file_open_image ( struct image *image, + const wchar_t *wname, + EFI_FILE_PROTOCOL **new ) { + struct efi_file *file; + + /* Allocate and initialise file */ + file = zalloc ( sizeof ( *file ) ); + if ( ! file ) + return EFI_OUT_OF_RESOURCES; + ref_init ( &file->refcnt, efi_file_free ); + memcpy ( &file->file, &efi_file_root.file, sizeof ( file->file ) ); + memcpy ( &file->load, &efi_file_root.load, sizeof ( file->load ) ); + efi_file_image ( file, image_get ( image ) ); + + /* Return opened file */ + *new = &file->file; + + DBGC ( file, "EFIFILE %s opened via %ls\n", + efi_file_name ( file ), wname ); + return 0; +} + +/** * Open file * * @v this EFI file @@ -335,9 +372,9 @@ efi_file_open ( EFI_FILE_PROTOCOL *this, EFI_FILE_PROTOCOL **new, CHAR16 *wname, UINT64 mode, UINT64 attributes __unused ) { struct efi_file *file = container_of ( this, struct efi_file, file ); char buf[ wcslen ( wname ) + 1 /* NUL */ ]; - struct efi_file *new_file; struct image *image; char *name; + char *sep; /* Convert name to ASCII */ snprintf ( buf, sizeof ( buf ), "%ls", wname ); @@ -351,7 +388,7 @@ efi_file_open ( EFI_FILE_PROTOCOL *this, EFI_FILE_PROTOCOL **new, /* Allow root directory itself to be opened */ if ( ( name[0] == '\0' ) || ( name[0] == '.' ) ) - return efi_file_open_fixed ( &efi_file_root, new ); + return efi_file_open_fixed ( &efi_file_root, wname, new ); /* Fail unless opening from the root */ if ( file != &efi_file_root ) { @@ -367,31 +404,28 @@ efi_file_open ( EFI_FILE_PROTOCOL *this, EFI_FILE_PROTOCOL **new, return EFI_WRITE_PROTECTED; } - /* Allow magic initrd to be opened */ - if ( strcasecmp ( name, efi_file_initrd.file.name ) == 0 ) - return efi_file_open_fixed ( &efi_file_initrd.file, new ); + /* Allow registered images to be opened */ + if ( ( image = efi_file_find ( name ) ) != NULL ) + return efi_file_open_image ( image, wname, new ); - /* Identify image */ - image = efi_file_find ( name ); - if ( ! image ) { - DBGC ( file, "EFIFILE %s does not exist\n", name ); - return EFI_NOT_FOUND; + /* Allow magic initrd to be opened */ + if ( strcasecmp ( name, efi_file_initrd.file.name ) == 0 ) { + return efi_file_open_fixed ( &efi_file_initrd.file, wname, + new ); } - /* Allocate and initialise file */ - new_file = zalloc ( sizeof ( *new_file ) ); - if ( ! new_file ) - return EFI_OUT_OF_RESOURCES; - ref_init ( &file->refcnt, efi_file_free ); - memcpy ( &new_file->file, &efi_file_root.file, - sizeof ( new_file->file ) ); - memcpy ( &new_file->load, &efi_file_root.load, - sizeof ( new_file->load ) ); - efi_file_image ( new_file, image_get ( image ) ); - *new = &new_file->file; - DBGC ( new_file, "EFIFILE %s opened\n", efi_file_name ( new_file ) ); + /* Allow currently selected image to be opened as "grub*.efi", + * to work around buggy versions of the UEFI shim. + */ + if ( ( strncasecmp ( name, "grub", 4 ) == 0 ) && + ( ( sep = strrchr ( name, '.' ) ) != NULL ) && + ( strcasecmp ( sep, ".efi" ) == 0 ) && + ( ( image = find_image_tag ( &selected_image ) ) != NULL ) ) { + return efi_file_open_image ( image, wname, new ); + } - return 0; + DBGC ( file, "EFIFILE %ls does not exist\n", wname ); + return EFI_NOT_FOUND; } /** @@ -505,13 +539,21 @@ static EFI_STATUS efi_file_read_dir ( struct efi_file *file, UINTN *len, /* Construct directory entries for image-backed files */ index = file->pos; for_each_image ( image ) { - if ( index-- == 0 ) { - efi_file_image ( &entry, image ); - efirc = efi_file_info ( &entry, len, data ); - if ( efirc == 0 ) - file->pos++; - return efirc; - } + + /* Skip hidden images */ + if ( image->flags & IMAGE_HIDDEN ) + continue; + + /* Skip preceding images */ + if ( index-- ) + continue; + + /* Construct directory entry */ + efi_file_image ( &entry, image ); + efirc = efi_file_info ( &entry, len, data ); + if ( efirc == 0 ) + file->pos++; + return efirc; } /* No more entries */ @@ -821,7 +863,7 @@ efi_file_open_volume ( EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *filesystem __unused, EFI_FILE_PROTOCOL **file ) { DBGC ( &efi_file_root, "EFIFILE open volume\n" ); - return efi_file_open_fixed ( &efi_file_root, file ); + return efi_file_open_fixed ( &efi_file_root, L"<volume>", file ); } /** EFI simple file system protocol */ @@ -1074,6 +1116,7 @@ int efi_file_install ( EFI_HANDLE handle ) { EFI_DISK_IO_PROTOCOL *diskio; void *interface; } diskio; + struct image *image; EFI_STATUS efirc; int rc; @@ -1137,9 +1180,12 @@ int efi_file_install ( EFI_HANDLE handle ) { goto err_initrd_claim; /* Install Linux initrd fixed device path file if non-empty */ - if ( have_images() && - ( ( rc = efi_file_path_install ( &efi_file_initrd ) ) != 0 ) ) { - goto err_initrd_install; + for_each_image ( image ) { + if ( image->flags & IMAGE_HIDDEN ) + continue; + if ( ( rc = efi_file_path_install ( &efi_file_initrd ) ) != 0 ) + goto err_initrd_install; + break; } return 0; diff --git a/src/interface/efi/efi_guid.c b/src/interface/efi/efi_guid.c index 663585dc..f841448f 100644 --- a/src/interface/efi/efi_guid.c +++ b/src/interface/efi/efi_guid.c @@ -37,23 +37,32 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/efi/Protocol/DevicePath.h> #include <ipxe/efi/Protocol/DevicePathToText.h> #include <ipxe/efi/Protocol/Dhcp4.h> +#include <ipxe/efi/Protocol/Dhcp6.h> #include <ipxe/efi/Protocol/DiskIo.h> +#include <ipxe/efi/Protocol/Dns4.h> +#include <ipxe/efi/Protocol/Dns6.h> #include <ipxe/efi/Protocol/DriverBinding.h> #include <ipxe/efi/Protocol/GraphicsOutput.h> #include <ipxe/efi/Protocol/HiiConfigAccess.h> #include <ipxe/efi/Protocol/HiiFont.h> +#include <ipxe/efi/Protocol/Http.h> #include <ipxe/efi/Protocol/Ip4.h> #include <ipxe/efi/Protocol/Ip4Config.h> +#include <ipxe/efi/Protocol/Ip4Config2.h> +#include <ipxe/efi/Protocol/Ip6.h> +#include <ipxe/efi/Protocol/Ip6Config.h> #include <ipxe/efi/Protocol/LoadFile.h> #include <ipxe/efi/Protocol/LoadFile2.h> #include <ipxe/efi/Protocol/LoadedImage.h> #include <ipxe/efi/Protocol/ManagedNetwork.h> #include <ipxe/efi/Protocol/Mtftp4.h> +#include <ipxe/efi/Protocol/Mtftp6.h> #include <ipxe/efi/Protocol/NetworkInterfaceIdentifier.h> #include <ipxe/efi/Protocol/PciIo.h> #include <ipxe/efi/Protocol/PciRootBridgeIo.h> #include <ipxe/efi/Protocol/PxeBaseCode.h> #include <ipxe/efi/Protocol/SerialIo.h> +#include <ipxe/efi/Protocol/ShimLock.h> #include <ipxe/efi/Protocol/SimpleFileSystem.h> #include <ipxe/efi/Protocol/SimpleNetwork.h> #include <ipxe/efi/Protocol/SimplePointer.h> @@ -62,7 +71,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/efi/Protocol/SimpleTextOut.h> #include <ipxe/efi/Protocol/TcgService.h> #include <ipxe/efi/Protocol/Tcp4.h> +#include <ipxe/efi/Protocol/Tcp6.h> #include <ipxe/efi/Protocol/Udp4.h> +#include <ipxe/efi/Protocol/Udp6.h> #include <ipxe/efi/Protocol/UgaDraw.h> #include <ipxe/efi/Protocol/UnicodeCollation.h> #include <ipxe/efi/Protocol/UsbHostController.h> @@ -139,10 +150,34 @@ EFI_GUID efi_dhcp4_protocol_guid EFI_GUID efi_dhcp4_service_binding_protocol_guid = EFI_DHCP4_SERVICE_BINDING_PROTOCOL_GUID; +/** DHCPv6 protocol GUID */ +EFI_GUID efi_dhcp6_protocol_guid + = EFI_DHCP6_PROTOCOL_GUID; + +/** DHCPv6 service binding protocol GUID */ +EFI_GUID efi_dhcp6_service_binding_protocol_guid + = EFI_DHCP6_SERVICE_BINDING_PROTOCOL_GUID; + /** Disk I/O protocol GUID */ EFI_GUID efi_disk_io_protocol_guid = EFI_DISK_IO_PROTOCOL_GUID; +/** DNSv4 protocol GUID */ +EFI_GUID efi_dns4_protocol_guid + = EFI_DNS4_PROTOCOL_GUID; + +/** DNSv4 service binding protocol GUID */ +EFI_GUID efi_dns4_service_binding_protocol_guid + = EFI_DNS4_SERVICE_BINDING_PROTOCOL_GUID; + +/** DNSv6 protocol GUID */ +EFI_GUID efi_dns6_protocol_guid + = EFI_DNS6_PROTOCOL_GUID; + +/** DNSv6 service binding protocol GUID */ +EFI_GUID efi_dns6_service_binding_protocol_guid + = EFI_DNS6_SERVICE_BINDING_PROTOCOL_GUID; + /** Driver binding protocol GUID */ EFI_GUID efi_driver_binding_protocol_guid = EFI_DRIVER_BINDING_PROTOCOL_GUID; @@ -159,6 +194,14 @@ EFI_GUID efi_hii_config_access_protocol_guid EFI_GUID efi_hii_font_protocol_guid = EFI_HII_FONT_PROTOCOL_GUID; +/** HTTP protocol GUID */ +EFI_GUID efi_http_protocol_guid + = EFI_HTTP_PROTOCOL_GUID; + +/** HTTP service binding protocol GUID */ +EFI_GUID efi_http_service_binding_protocol_guid + = EFI_HTTP_SERVICE_BINDING_PROTOCOL_GUID; + /** IPv4 protocol GUID */ EFI_GUID efi_ip4_protocol_guid = EFI_IP4_PROTOCOL_GUID; @@ -167,10 +210,26 @@ EFI_GUID efi_ip4_protocol_guid EFI_GUID efi_ip4_config_protocol_guid = EFI_IP4_CONFIG_PROTOCOL_GUID; +/** IPv4 configuration 2 protocol GUID */ +EFI_GUID efi_ip4_config2_protocol_guid + = EFI_IP4_CONFIG2_PROTOCOL_GUID; + /** IPv4 service binding protocol GUID */ EFI_GUID efi_ip4_service_binding_protocol_guid = EFI_IP4_SERVICE_BINDING_PROTOCOL_GUID; +/** IPv6 protocol GUID */ +EFI_GUID efi_ip6_protocol_guid + = EFI_IP6_PROTOCOL_GUID; + +/** IPv6 configuration protocol GUID */ +EFI_GUID efi_ip6_config_protocol_guid + = EFI_IP6_CONFIG_PROTOCOL_GUID; + +/** IPv6 service binding protocol GUID */ +EFI_GUID efi_ip6_service_binding_protocol_guid + = EFI_IP6_SERVICE_BINDING_PROTOCOL_GUID; + /** Load file protocol GUID */ EFI_GUID efi_load_file_protocol_guid = EFI_LOAD_FILE_PROTOCOL_GUID; @@ -203,6 +262,14 @@ EFI_GUID efi_mtftp4_protocol_guid EFI_GUID efi_mtftp4_service_binding_protocol_guid = EFI_MTFTP4_SERVICE_BINDING_PROTOCOL_GUID; +/** MTFTPv6 protocol GUID */ +EFI_GUID efi_mtftp6_protocol_guid + = EFI_MTFTP6_PROTOCOL_GUID; + +/** MTFTPv6 service binding protocol GUID */ +EFI_GUID efi_mtftp6_service_binding_protocol_guid + = EFI_MTFTP6_SERVICE_BINDING_PROTOCOL_GUID; + /** Network interface identifier protocol GUID (old version) */ EFI_GUID efi_nii_protocol_guid = EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL_GUID; @@ -227,6 +294,10 @@ EFI_GUID efi_pxe_base_code_protocol_guid EFI_GUID efi_serial_io_protocol_guid = EFI_SERIAL_IO_PROTOCOL_GUID; +/** Shim lock protocol GUID */ +EFI_GUID efi_shim_lock_protocol_guid + = EFI_SHIM_LOCK_PROTOCOL_GUID; + /** Simple file system protocol GUID */ EFI_GUID efi_simple_file_system_protocol_guid = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID; @@ -263,6 +334,14 @@ EFI_GUID efi_tcp4_protocol_guid EFI_GUID efi_tcp4_service_binding_protocol_guid = EFI_TCP4_SERVICE_BINDING_PROTOCOL_GUID; +/** TCPv6 protocol GUID */ +EFI_GUID efi_tcp6_protocol_guid + = EFI_TCP6_PROTOCOL_GUID; + +/** TCPv6 service binding protocol GUID */ +EFI_GUID efi_tcp6_service_binding_protocol_guid + = EFI_TCP6_SERVICE_BINDING_PROTOCOL_GUID; + /** TrEE protocol GUID */ EFI_GUID efi_tree_protocol_guid = EFI_TREE_PROTOCOL_GUID; @@ -275,6 +354,14 @@ EFI_GUID efi_udp4_protocol_guid EFI_GUID efi_udp4_service_binding_protocol_guid = EFI_UDP4_SERVICE_BINDING_PROTOCOL_GUID; +/** UDPv6 protocol GUID */ +EFI_GUID efi_udp6_protocol_guid + = EFI_UDP6_PROTOCOL_GUID; + +/** UDPv6 service binding protocol GUID */ +EFI_GUID efi_udp6_service_binding_protocol_guid + = EFI_UDP6_SERVICE_BINDING_PROTOCOL_GUID; + /** UGA draw protocol GUID */ EFI_GUID efi_uga_draw_protocol_guid = EFI_UGA_DRAW_PROTOCOL_GUID; diff --git a/src/interface/efi/efi_settings.c b/src/interface/efi/efi_settings.c new file mode 100644 index 00000000..cde0ff8d --- /dev/null +++ b/src/interface/efi/efi_settings.c @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2023 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 ); + +/** + * @file + * + * EFI variable settings + * + */ + +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <ipxe/settings.h> +#include <ipxe/init.h> +#include <ipxe/efi/efi.h> +#include <ipxe/efi/efi_strings.h> + +/** EFI variable settings scope */ +static const struct settings_scope efivars_scope; + +/** EFI variable settings */ +static struct settings efivars; + +/** + * Check applicability of EFI variable setting + * + * @v settings Settings block + * @v setting Setting + * @ret applies Setting applies within this settings block + */ +static int efivars_applies ( struct settings *settings __unused, + const struct setting *setting ) { + + return ( setting->scope == &efivars_scope ); +} + +/** + * Find first matching EFI variable name + * + * @v wname Name + * @v guid GUID to fill in + * @ret rc Return status code + */ +static int efivars_find ( const CHAR16 *wname, EFI_GUID *guid ) { + EFI_RUNTIME_SERVICES *rs = efi_systab->RuntimeServices; + size_t wname_len = ( ( wcslen ( wname ) + 1 ) * sizeof ( wname[0] ) ); + CHAR16 *buf; + CHAR16 *tmp; + UINTN size; + EFI_STATUS efirc; + int rc; + + /* Allocate single wNUL for first call to GetNextVariableName() */ + size = sizeof ( buf[0] ); + buf = zalloc ( size ); + if ( ! buf ) + return -ENOMEM; + + /* Iterate over all veriables */ + while ( 1 ) { + + /* Get next variable name */ + efirc = rs->GetNextVariableName ( &size, buf, guid ); + if ( efirc == EFI_BUFFER_TOO_SMALL ) { + tmp = realloc ( buf, size ); + if ( ! tmp ) { + rc = -ENOMEM; + break; + } + buf = tmp; + efirc = rs->GetNextVariableName ( &size, buf, guid ); + } + if ( efirc == EFI_NOT_FOUND ) { + rc = -ENOENT; + break; + } + if ( efirc != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( &efivars, "EFIVARS %s:%ls could not fetch next " + "variable name: %s\n", + efi_guid_ntoa ( guid ), buf, strerror ( rc ) ); + break; + } + DBGC2 ( &efivars, "EFIVARS %s:%ls exists\n", + efi_guid_ntoa ( guid ), buf ); + + /* Check for matching variable name */ + if ( memcmp ( wname, buf, wname_len ) == 0 ) { + rc = 0; + break; + } + } + + /* Free temporary buffer */ + free ( buf ); + + return rc; +} + +/** + * Fetch value of EFI variable setting + * + * @v settings Settings block + * @v setting Setting to fetch + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ +static int efivars_fetch ( struct settings *settings __unused, + struct setting *setting, void *data, size_t len ) { + EFI_RUNTIME_SERVICES *rs = efi_systab->RuntimeServices; + size_t name_len = strlen ( setting->name ); + CHAR16 wname[ name_len + 1 /* wNUL */ ]; + EFI_GUID guid; + UINT32 attrs; + UINTN size; + void *buf; + EFI_STATUS efirc; + int rc; + + /* Convert name to UCS-2 */ + efi_snprintf ( wname, sizeof ( wname ), "%s", setting->name ); + + /* Find variable GUID */ + if ( ( rc = efivars_find ( wname, &guid ) ) != 0 ) + goto err_find; + + /* Get variable length */ + size = 0; + if ( ( efirc = rs->GetVariable ( wname, &guid, &attrs, &size, + NULL ) != EFI_BUFFER_TOO_SMALL ) ) { + rc = -EEFI ( efirc ); + DBGC ( &efivars, "EFIVARS %s:%ls could not get size: %s\n", + efi_guid_ntoa ( &guid ), wname, strerror ( rc ) ); + goto err_len; + } + + /* Allocate temporary buffer, since GetVariable() is not + * guaranteed to return partial data for an underlength + * buffer. + */ + buf = malloc ( size ); + if ( ! buf ) { + rc = -ENOMEM; + goto err_alloc; + } + + /* Get variable value */ + if ( ( efirc = rs->GetVariable ( wname, &guid, &attrs, &size, + buf ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( &efivars, "EFIVARS %s:%ls could not get %zd bytes: " + "%s\n", efi_guid_ntoa ( &guid ), wname, + ( ( size_t ) size ), strerror ( rc ) ); + goto err_get; + } + DBGC ( &efivars, "EFIVARS %s:%ls:\n", efi_guid_ntoa ( &guid ), wname ); + DBGC_HDA ( &efivars, 0, buf, size ); + + /* Return setting value */ + if ( len > size ) + len = size; + memcpy ( data, buf, len ); + if ( ! setting->type ) + setting->type = &setting_type_hex; + + /* Free temporary buffer */ + free ( buf ); + + return size; + + err_get: + free ( buf ); + err_alloc: + err_len: + err_find: + return rc; +} + +/** EFI variable settings operations */ +static struct settings_operations efivars_operations = { + .applies = efivars_applies, + .fetch = efivars_fetch, +}; + +/** EFI variable settings */ +static struct settings efivars = { + .refcnt = NULL, + .siblings = LIST_HEAD_INIT ( efivars.siblings ), + .children = LIST_HEAD_INIT ( efivars.children ), + .op = &efivars_operations, + .default_scope = &efivars_scope, +}; + +/** + * Initialise EFI variable settings + * + */ +static void efivars_init ( void ) { + int rc; + + /* Register settings block */ + if ( ( rc = register_settings ( &efivars, NULL, "efi" ) ) != 0 ) { + DBGC ( &efivars, "EFIVARS could not register: %s\n", + strerror ( rc ) ); + return; + } +} + +/** EFI variable settings initialiser */ +struct init_fn efivars_init_fn __init_fn ( INIT_NORMAL ) = { + .initialise = efivars_init, +}; diff --git a/src/interface/efi/efi_shim.c b/src/interface/efi/efi_shim.c new file mode 100644 index 00000000..a46d79d0 --- /dev/null +++ b/src/interface/efi/efi_shim.c @@ -0,0 +1,402 @@ +/* + * Copyright (C) 2022 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 <stdlib.h> +#include <errno.h> +#include <ipxe/image.h> +#include <ipxe/efi/efi.h> +#include <ipxe/efi/efi_strings.h> +#include <ipxe/efi/efi_shim.h> +#include <ipxe/efi/Protocol/PxeBaseCode.h> +#include <ipxe/efi/Protocol/ShimLock.h> + +/** @file + * + * UEFI shim special handling + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** + * Require use of a third party loader binary + * + * The UEFI shim is gradually becoming less capable of directly + * executing a Linux kernel image, due to an ever increasing list of + * assumptions that it will only ever be used in conjunction with a + * second stage loader binary such as GRUB. + * + * For example: shim will erroneously complain if the image that it + * loads and executes does not in turn call in to the "shim lock + * protocol" to verify a separate newly loaded binary before calling + * ExitBootServices(), even if no such separate binary is used or + * required. + * + * Experience shows that there is unfortunately no point in trying to + * get a fix for this upstreamed into shim. We therefore default to + * reducing the Secure Boot attack surface by removing, where + * possible, this spurious requirement for the use of an additional + * second stage loader. + * + * This option may be used to require the use of an additional second + * stage loader binary, in case this behaviour is ever desirable. + */ +int efi_shim_require_loader = 0; + +/** + * Allow use of PXE base code protocol + * + * We provide shim with access to all of the relevant downloaded files + * via our EFI_SIMPLE_FILE_SYSTEM_PROTOCOL interface. However, shim + * will instead try to redownload the files via TFTP since it prefers + * to use the EFI_PXE_BASE_CODE_PROTOCOL installed on the same handle. + * + * Experience shows that there is unfortunately no point in trying to + * get a fix for this upstreamed into shim. We therefore default to + * working around this undesirable behaviour by stopping the PXE base + * code protocol before invoking shim. + * + * This option may be used to allow shim to use the PXE base code + * protocol, in case this behaviour is ever desirable. + */ +int efi_shim_allow_pxe = 0; + +/** + * Allow SBAT variable access + * + * The UEFI shim implements a fairly nicely designed revocation + * mechanism designed around the concept of security generations. + * Unfortunately nobody in the shim community has thus far added the + * relevant metadata to the Linux kernel, with the result that current + * versions of shim are incapable of booting current versions of the + * Linux kernel. + * + * Experience shows that there is unfortunately no point in trying to + * get a fix for this upstreamed into shim. We therefore default to + * working around this undesirable behaviour by patching data read + * from the "SbatLevel" variable used to hold SBAT configuration. + * + * This option may be used to allow shim unpatched access to the + * "SbatLevel" variable, in case this behaviour is ever desirable. + */ +int efi_shim_allow_sbat = 0; + +/** UEFI shim image */ +struct image_tag efi_shim __image_tag = { + .name = "SHIM", +}; + +/** Original GetMemoryMap() function */ +static EFI_GET_MEMORY_MAP efi_shim_orig_get_memory_map; + +/** Original ExitBootServices() function */ +static EFI_EXIT_BOOT_SERVICES efi_shim_orig_exit_boot_services; + +/** Original SetVariable() function */ +static EFI_SET_VARIABLE efi_shim_orig_set_variable; + +/** Original GetVariable() function */ +static EFI_GET_VARIABLE efi_shim_orig_get_variable; + +/** Verify read from SbatLevel variable */ +static int efi_shim_sbatlevel_verify; + +/** + * Check if variable is SbatLevel + * + * @v name Variable name + * @v guid Variable namespace GUID + * @ret is_sbatlevel Variable is SbatLevel + */ +static int efi_shim_is_sbatlevel ( const CHAR16 *name, const EFI_GUID *guid ) { + static CHAR16 sbatlevel[] = L"SbatLevel"; + EFI_GUID *shimlock = &efi_shim_lock_protocol_guid; + + return ( ( memcmp ( name, sbatlevel, sizeof ( sbatlevel ) ) == 0 ) && + ( memcmp ( guid, shimlock, sizeof ( *shimlock ) ) == 0 ) ); +} + +/** + * Unlock UEFI shim + * + */ +static void efi_shim_unlock ( void ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + uint8_t empty[0]; + union { + EFI_SHIM_LOCK_PROTOCOL *lock; + void *interface; + } u; + EFI_STATUS efirc; + + /* Locate shim lock protocol */ + if ( ( efirc = bs->LocateProtocol ( &efi_shim_lock_protocol_guid, + NULL, &u.interface ) ) == 0 ) { + u.lock->Verify ( empty, sizeof ( empty ) ); + DBGC ( &efi_shim, "SHIM unlocked via %p\n", u.lock ); + } +} + +/** + * Wrap GetMemoryMap() + * + * @v len Memory map size + * @v map Memory map + * @v key Memory map key + * @v desclen Descriptor size + * @v descver Descriptor version + * @ret efirc EFI status code + */ +static EFIAPI EFI_STATUS efi_shim_get_memory_map ( UINTN *len, + EFI_MEMORY_DESCRIPTOR *map, + UINTN *key, UINTN *desclen, + UINT32 *descver ) { + + /* Unlock shim */ + if ( ! efi_shim_require_loader ) + efi_shim_unlock(); + + /* Hand off to original GetMemoryMap() */ + return efi_shim_orig_get_memory_map ( len, map, key, desclen, + descver ); +} + +/** + * Wrap ExitBootServices() + * + * @v handle Image handle + * @v key Memory map key + * @ret efirc EFI status code + */ +static EFIAPI EFI_STATUS efi_shim_exit_boot_services ( EFI_HANDLE handle, + UINTN key ) { + EFI_RUNTIME_SERVICES *rs = efi_systab->RuntimeServices; + + /* Restore original runtime services functions */ + rs->SetVariable = efi_shim_orig_set_variable; + rs->GetVariable = efi_shim_orig_get_variable; + + /* Hand off to original ExitBootServices() */ + return efi_shim_orig_exit_boot_services ( handle, key ); +} + +/** + * Wrap SetVariable() + * + * @v name Variable name + * @v guid Variable namespace GUID + * @v attrs Attributes + * @v len Buffer size + * @v data Data buffer + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI +efi_shim_set_variable ( CHAR16 *name, EFI_GUID *guid, UINT32 attrs, + UINTN len, VOID *data ) { + EFI_STATUS efirc; + + /* Call original SetVariable() */ + efirc = efi_shim_orig_set_variable ( name, guid, attrs, len, data ); + + /* Allow verification of SbatLevel variable content */ + if ( efi_shim_is_sbatlevel ( name, guid ) && ( efirc == 0 ) ) { + DBGC ( &efi_shim, "SHIM detected write to %ls:\n", name ); + DBGC_HDA ( &efi_shim, 0, data, len ); + efi_shim_sbatlevel_verify = 1; + } + + return efirc; +} + +/** + * Wrap GetVariable() + * + * @v name Variable name + * @v guid Variable namespace GUID + * @v attrs Attributes to fill in + * @v len Buffer size + * @v data Data buffer + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI +efi_shim_get_variable ( CHAR16 *name, EFI_GUID *guid, UINT32 *attrs, + UINTN *len, VOID *data ) { + char *value = data; + EFI_STATUS efirc; + + /* Call original GetVariable() */ + efirc = efi_shim_orig_get_variable ( name, guid, attrs, len, data ); + + /* Patch SbatLevel variable if applicable */ + if ( efi_shim_is_sbatlevel ( name, guid ) && data && ( efirc == 0 ) ) { + if ( efi_shim_allow_sbat ) { + DBGC ( &efi_shim, "SHIM allowing read from %ls:\n", + name ); + } else if ( efi_shim_sbatlevel_verify ) { + DBGC ( &efi_shim, "SHIM allowing one read from %ls:\n", + name ); + efi_shim_sbatlevel_verify = 0; + } else { + DBGC ( &efi_shim, "SHIM patching read from %ls:\n", + name ); + value[0] = '\0'; + } + DBGC_HDA ( &efi_shim, 0, data, *len ); + } + + return efirc; +} + +/** + * Inhibit use of PXE base code + * + * @v handle EFI handle + * @ret rc Return status code + */ +static int efi_shim_inhibit_pxe ( EFI_HANDLE handle ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + union { + EFI_PXE_BASE_CODE_PROTOCOL *pxe; + void *interface; + } u; + EFI_STATUS efirc; + int rc; + + /* Locate PXE base code */ + if ( ( efirc = bs->OpenProtocol ( handle, + &efi_pxe_base_code_protocol_guid, + &u.interface, efi_image_handle, NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){ + rc = -EEFI ( efirc ); + DBGC ( &efi_shim, "SHIM could not open PXE base code: %s\n", + strerror ( rc ) ); + goto err_no_base; + } + + /* Stop PXE base code */ + if ( ( efirc = u.pxe->Stop ( u.pxe ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( &efi_shim, "SHIM could not stop PXE base code: %s\n", + strerror ( rc ) ); + goto err_stop; + } + + /* Success */ + rc = 0; + DBGC ( &efi_shim, "SHIM stopped PXE base code\n" ); + + err_stop: + bs->CloseProtocol ( handle, &efi_pxe_base_code_protocol_guid, + efi_image_handle, NULL ); + err_no_base: + return rc; +} + +/** + * Update command line + * + * @v shim Shim image + * @v cmdline Command line to update + * @ret rc Return status code + */ +static int efi_shim_cmdline ( struct image *shim, wchar_t **cmdline ) { + wchar_t *shimcmdline; + int len; + int rc; + + /* Construct new command line */ + len = ( shim->cmdline ? + efi_asprintf ( &shimcmdline, "%s %s", shim->name, + shim->cmdline ) : + efi_asprintf ( &shimcmdline, "%s %ls", shim->name, + *cmdline ) ); + if ( len < 0 ) { + rc = len; + DBGC ( &efi_shim, "SHIM could not construct command line: " + "%s\n", strerror ( rc ) ); + return rc; + } + + /* Replace command line */ + free ( *cmdline ); + *cmdline = shimcmdline; + + return 0; +} + +/** + * Install UEFI shim special handling + * + * @v shim Shim image + * @v handle EFI device handle + * @v cmdline Command line to update + * @ret rc Return status code + */ +int efi_shim_install ( struct image *shim, EFI_HANDLE handle, + wchar_t **cmdline ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_RUNTIME_SERVICES *rs = efi_systab->RuntimeServices; + int rc; + + /* Stop PXE base code */ + if ( ( ! efi_shim_allow_pxe ) && + ( ( rc = efi_shim_inhibit_pxe ( handle ) ) != 0 ) ) { + return rc; + } + + /* Update command line */ + if ( ( rc = efi_shim_cmdline ( shim, cmdline ) ) != 0 ) + return rc; + + /* Record original boot and runtime services functions */ + efi_shim_orig_get_memory_map = bs->GetMemoryMap; + efi_shim_orig_exit_boot_services = bs->ExitBootServices; + efi_shim_orig_set_variable = rs->SetVariable; + efi_shim_orig_get_variable = rs->GetVariable; + + /* Wrap relevant boot and runtime services functions */ + bs->GetMemoryMap = efi_shim_get_memory_map; + bs->ExitBootServices = efi_shim_exit_boot_services; + rs->SetVariable = efi_shim_set_variable; + rs->GetVariable = efi_shim_get_variable; + + return 0; +} + +/** + * Uninstall UEFI shim special handling + * + */ +void efi_shim_uninstall ( void ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_RUNTIME_SERVICES *rs = efi_systab->RuntimeServices; + + /* Restore original boot and runtime services functions */ + bs->GetMemoryMap = efi_shim_orig_get_memory_map; + bs->ExitBootServices = efi_shim_orig_exit_boot_services; + rs->SetVariable = efi_shim_orig_set_variable; + rs->GetVariable = efi_shim_orig_get_variable; +} diff --git a/src/interface/efi/efi_snp.c b/src/interface/efi/efi_snp.c index c4f7d4ea..8443be99 100644 --- a/src/interface/efi/efi_snp.c +++ b/src/interface/efi/efi_snp.c @@ -1777,9 +1777,10 @@ static struct efi_snp_device * efi_snp_demux ( struct net_device *netdev ) { * Create SNP device * * @v netdev Network device + * @v priv Private data * @ret rc Return status code */ -static int efi_snp_probe ( struct net_device *netdev ) { +static int efi_snp_probe ( struct net_device *netdev, void *priv __unused ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_device *efidev; struct efi_snp_device *snpdev; @@ -2017,8 +2018,9 @@ static int efi_snp_probe ( struct net_device *netdev ) { * Handle SNP device or link state change * * @v netdev Network device + * @v priv Private data */ -static void efi_snp_notify ( struct net_device *netdev ) { +static void efi_snp_notify ( struct net_device *netdev, void *priv __unused ) { struct efi_snp_device *snpdev; /* Locate SNP device */ @@ -2042,8 +2044,9 @@ static void efi_snp_notify ( struct net_device *netdev ) { * Destroy SNP device * * @v netdev Network device + * @v priv Private data */ -static void efi_snp_remove ( struct net_device *netdev ) { +static void efi_snp_remove ( struct net_device *netdev, void *priv __unused ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_snp_device *snpdev; int leak = efi_shutdown_in_progress; diff --git a/src/interface/efi/efi_strings.c b/src/interface/efi/efi_strings.c index aa3afc64..765b23ca 100644 --- a/src/interface/efi/efi_strings.c +++ b/src/interface/efi/efi_strings.c @@ -25,6 +25,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stddef.h> #include <stdarg.h> +#include <stdlib.h> +#include <errno.h> #include <ipxe/vsprintf.h> #include <ipxe/efi/efi_strings.h> @@ -150,3 +152,45 @@ int efi_ssnprintf ( wchar_t *wbuf, ssize_t swsize, const char *fmt, ... ) { va_end ( args ); return len; } + +/** + * Write a formatted string to newly allocated memory + * + * @v wstrp Pointer to hold allocated string + * @v fmt Format string + * @v args Arguments corresponding to the format string + * @ret len Length of formatted string (in wide characters) + */ +int efi_vasprintf ( wchar_t **wstrp, const char *fmt, va_list args ) { + size_t len; + va_list args_tmp; + + /* Calculate length needed for string */ + va_copy ( args_tmp, args ); + len = ( efi_vsnprintf ( NULL, 0, fmt, args_tmp ) + 1 ); + va_end ( args_tmp ); + + /* Allocate and fill string */ + *wstrp = malloc ( len * sizeof ( **wstrp ) ); + if ( ! *wstrp ) + return -ENOMEM; + return efi_vsnprintf ( *wstrp, len, fmt, args ); +} + +/** + * Write a formatted string to newly allocated memory + * + * @v wstrp Pointer to hold allocated string + * @v fmt Format string + * @v ... Arguments corresponding to the format string + * @ret len Length of formatted string (in wide characters) + */ +int efi_asprintf ( wchar_t **wstrp, const char *fmt, ... ) { + va_list args; + int len; + + va_start ( args, fmt ); + len = efi_vasprintf ( wstrp, fmt, args ); + va_end ( args ); + return len; +} diff --git a/src/interface/efi/efi_veto.c b/src/interface/efi/efi_veto.c index b616539d..a3b60d65 100644 --- a/src/interface/efi/efi_veto.c +++ b/src/interface/efi/efi_veto.c @@ -37,8 +37,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ -/** A driver veto */ -struct efi_veto { +/** A driver veto candidate */ +struct efi_veto_candidate { /** Veto name (for debugging) */ const char *name; /** @@ -57,22 +57,38 @@ struct efi_veto { const char *manufacturer, const CHAR16 *name ); }; +/** A driver veto */ +struct efi_veto { + /** Driver binding handle */ + EFI_HANDLE driver; + /** Driving binding protocol */ + EFI_DRIVER_BINDING_PROTOCOL *binding; + /** Image handle */ + EFI_HANDLE image; + /** Loaded image protocol */ + EFI_LOADED_IMAGE_PROTOCOL *loaded; +}; + /** * Unload an EFI driver * - * @v driver Driver binding handle + * @v veto Driver veto * @ret rc Return status code */ -static int efi_veto_unload ( EFI_HANDLE driver ) { +static int efi_veto_unload ( struct efi_veto *veto ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_HANDLE driver = veto->driver; + EFI_HANDLE image = veto->image; EFI_STATUS efirc; int rc; /* Unload the driver */ - if ( ( efirc = bs->UnloadImage ( driver ) ) != 0 ) { + if ( ( efirc = bs->UnloadImage ( image ) ) != 0 ) { rc = -EEFI ( efirc ); - DBGC ( driver, "EFIVETO %s could not unload: %s\n", - efi_handle_name ( driver ), strerror ( rc ) ); + DBGC ( driver, "EFIVETO %s could not unload", + efi_handle_name ( driver ) ); + DBGC ( driver, " %s: %s\n", efi_handle_name ( image ), + strerror ( rc ) ); return rc; } @@ -82,11 +98,12 @@ static int efi_veto_unload ( EFI_HANDLE driver ) { /** * Disconnect an EFI driver from all handles * - * @v driver Driver binding handle + * @v veto Driver veto * @ret rc Return status code */ -static int efi_veto_disconnect ( EFI_HANDLE driver ) { +static int efi_veto_disconnect ( struct efi_veto *veto ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_HANDLE driver = veto->driver; EFI_HANDLE *handles; EFI_HANDLE handle; UINTN count; @@ -131,11 +148,12 @@ static int efi_veto_disconnect ( EFI_HANDLE driver ) { /** * Uninstall an EFI driver binding protocol * - * @v driver Driver binding handle + * @v veto Driver veto * @ret rc Return status code */ -static int efi_veto_uninstall ( EFI_HANDLE driver ) { +static int efi_veto_uninstall ( struct efi_veto *veto ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_HANDLE driver = veto->driver; union { EFI_DRIVER_BINDING_PROTOCOL *binding; void *interface; @@ -178,14 +196,16 @@ static int efi_veto_uninstall ( EFI_HANDLE driver ) { /** * Close protocol on handle potentially opened by an EFI driver * - * @v driver Driver binding handle + * @v veto Driver veto * @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, +static int efi_veto_close_protocol ( struct efi_veto *veto, EFI_HANDLE handle, EFI_GUID *protocol ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_HANDLE driver = veto->driver; + EFI_HANDLE image = veto->image; EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *openers; EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *opener; EFI_HANDLE controller; @@ -207,9 +227,11 @@ static int efi_veto_close_protocol ( EFI_HANDLE driver, EFI_HANDLE handle, /* Close anything opened by this driver */ for ( i = 0 ; i < count ; i++ ) { - opener = &openers[i]; - if ( opener->AgentHandle != driver ) + opener = &openers[ count - i - 1 ]; + if ( ( opener->AgentHandle != driver ) && + ( opener->AgentHandle != image ) ) { continue; + } controller = opener->ControllerHandle; DBGC_EFI_OPENER ( driver, handle, protocol, opener ); if ( ( efirc = bs->CloseProtocol ( handle, protocol, driver, @@ -235,13 +257,15 @@ static int efi_veto_close_protocol ( EFI_HANDLE driver, EFI_HANDLE handle, /** * Close handle potentially opened by an EFI driver * - * @v driver Driver binding handle + * @v veto Driver veto * @v handle Potentially opened handle * @ret rc Return status code */ -static int efi_veto_close_handle ( EFI_HANDLE driver, EFI_HANDLE handle ) { +static int efi_veto_close_handle ( struct efi_veto *veto, EFI_HANDLE handle ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_HANDLE driver = veto->driver; EFI_GUID **protocols; + EFI_GUID *protocol; UINTN count; unsigned int i; EFI_STATUS efirc; @@ -260,8 +284,9 @@ static int efi_veto_close_handle ( EFI_HANDLE driver, EFI_HANDLE handle ) { /* Close each protocol */ for ( i = 0 ; i < count ; i++ ) { - if ( ( rc = efi_veto_close_protocol ( driver, handle, - protocols[i] ) ) != 0 ) + protocol = protocols[ count - i - 1]; + if ( ( rc = efi_veto_close_protocol ( veto, handle, + protocol ) ) != 0 ) goto err_close; } @@ -277,12 +302,14 @@ static int efi_veto_close_handle ( EFI_HANDLE driver, EFI_HANDLE handle ) { /** * Close all remaining handles opened by an EFI driver * - * @v driver Driver binding handle + * @v veto Driver veto * @ret rc Return status code */ -static int efi_veto_close ( EFI_HANDLE driver ) { +static int efi_veto_close ( struct efi_veto *veto ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_HANDLE driver = veto->driver; EFI_HANDLE *handles; + EFI_HANDLE handle; UINTN count; unsigned int i; EFI_STATUS efirc; @@ -299,8 +326,8 @@ static int efi_veto_close ( EFI_HANDLE driver ) { /* Close each handle */ for ( i = 0 ; i < count ; i++ ) { - if ( ( rc = efi_veto_close_handle ( driver, - handles[i] ) ) != 0 ) + handle = handles[ count - i - 1 ]; + if ( ( rc = efi_veto_close_handle ( veto, handle ) ) != 0 ) goto err_close; } @@ -318,22 +345,23 @@ static int efi_veto_close ( EFI_HANDLE driver ) { /** * Terminate an EFI driver with extreme prejudice * - * @v driver Driver binding handle + * @v veto Driver veto * @ret rc Return status code */ -static int efi_veto_destroy ( EFI_HANDLE driver ) { +static int efi_veto_destroy ( struct efi_veto *veto ) { + EFI_HANDLE driver = veto->driver; int rc; /* Disconnect driver from all handles */ - if ( ( rc = efi_veto_disconnect ( driver ) ) != 0 ) + if ( ( rc = efi_veto_disconnect ( veto ) ) != 0 ) return rc; /* Uninstall driver binding protocol */ - if ( ( rc = efi_veto_uninstall ( driver ) ) != 0 ) + if ( ( rc = efi_veto_uninstall ( veto ) ) != 0 ) return rc; /* Close any remaining opened handles */ - if ( ( rc = efi_veto_close ( driver ) ) != 0 ) + if ( ( rc = efi_veto_close ( veto ) ) != 0 ) return rc; DBGC ( driver, "EFIVETO %s forcibly removed\n", @@ -344,18 +372,18 @@ static int efi_veto_destroy ( EFI_HANDLE driver ) { /** * Veto an EFI driver * - * @v driver Driver binding handle + * @v veto Driver veto * @ret rc Return status code */ -static int efi_veto_driver ( EFI_HANDLE driver ) { +static int efi_veto_driver ( struct efi_veto *veto ) { int rc; /* Try gracefully unloading the driver */ - if ( ( rc = efi_veto_unload ( driver ) ) == 0 ) + if ( ( rc = efi_veto_unload ( veto ) ) == 0 ) return 0; /* If that fails, use a hammer */ - if ( ( rc = efi_veto_destroy ( driver ) ) == 0 ) + if ( ( rc = efi_veto_destroy ( veto ) ) == 0 ) return 0; return rc; @@ -435,8 +463,39 @@ efi_veto_hp_xhci ( EFI_DRIVER_BINDING_PROTOCOL *binding __unused, return 0; } +/** + * Veto VMware UefiPxeBcDxe 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_vmware_uefipxebc ( 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 uefipxebc[] = L"UEFI PXE Base Code Driver"; + static const char *vmware = "VMware, Inc."; + + /* Check manufacturer and driver name */ + if ( ! manufacturer ) + return 0; + if ( ! name ) + return 0; + if ( strcmp ( manufacturer, vmware ) != 0 ) + return 0; + if ( memcmp ( name, uefipxebc, sizeof ( uefipxebc ) ) != 0 ) + return 0; + + return 1; +} + /** Driver vetoes */ -static struct efi_veto efi_vetoes[] = { +static struct efi_veto_candidate efi_vetoes[] = { { .name = "Ip4Config", .veto = efi_veto_ip4config, @@ -445,6 +504,10 @@ static struct efi_veto efi_vetoes[] = { .name = "HP Xhci", .veto = efi_veto_hp_xhci, }, + { + .name = "VMware UefiPxeBc", + .veto = efi_veto_vmware_uefipxebc, + }, }; /** @@ -452,11 +515,11 @@ static struct efi_veto efi_vetoes[] = { * * @v driver Driver binding handle * @v manufacturer Manufacturer name, if present - * @ret veto Driver veto, or NULL + * @ret veto Driver veto to fill in * @ret rc Return status code */ static int efi_veto_find ( EFI_HANDLE driver, const char *manufacturer, - struct efi_veto **veto ) { + struct efi_veto *veto ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; union { EFI_DRIVER_BINDING_PROTOCOL *binding; @@ -480,7 +543,7 @@ static int efi_veto_find ( EFI_HANDLE driver, const char *manufacturer, efi_handle_name ( driver ) ); /* Mark as not vetoed */ - *veto = NULL; + memset ( veto, 0, sizeof ( *veto ) ); /* Open driver binding protocol */ if ( ( efirc = bs->OpenProtocol ( @@ -532,7 +595,13 @@ static int efi_veto_find ( EFI_HANDLE driver, const char *manufacturer, sizeof ( efi_vetoes[0] ) ) ; i++ ) { if ( efi_vetoes[i].veto ( binding.binding, loaded.loaded, wtf.wtf, manufacturer, name ) ) { - *veto = &efi_vetoes[i]; + DBGC ( driver, "EFIVETO %s is vetoed (%s)\n", + efi_handle_name ( driver ), + efi_vetoes[i].name ); + veto->driver = driver; + veto->binding = binding.binding; + veto->image = image; + veto->loaded = loaded.loaded; break; } } @@ -560,10 +629,10 @@ static int efi_veto_find ( EFI_HANDLE driver, const char *manufacturer, */ void efi_veto ( void ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - struct efi_veto *veto; + struct efi_veto veto; EFI_HANDLE *drivers; EFI_HANDLE driver; - UINTN num_drivers; + UINTN count; unsigned int i; char *manufacturer; EFI_STATUS efirc; @@ -572,7 +641,7 @@ void efi_veto ( void ) { /* Locate all driver binding protocol handles */ if ( ( efirc = bs->LocateHandleBuffer ( ByProtocol, &efi_driver_binding_protocol_guid, - NULL, &num_drivers, &drivers ) ) != 0 ) { + NULL, &count, &drivers ) ) != 0 ) { rc = -EEFI ( efirc ); DBGC ( &efi_vetoes, "EFIVETO could not list all drivers: " "%s\n", strerror ( rc ) ); @@ -582,10 +651,11 @@ void efi_veto ( void ) { /* Get manufacturer name */ fetch_string_setting_copy ( NULL, &manufacturer_setting, &manufacturer ); + DBGC ( &efi_vetoes, "EFIVETO manufacturer is \"%s\"\n", manufacturer ); /* Unload any vetoed drivers */ - for ( i = 0 ; i < num_drivers ; i++ ) { - driver = drivers[i]; + for ( i = 0 ; i < count ; i++ ) { + driver = drivers[ count - i - 1 ]; if ( ( rc = efi_veto_find ( driver, manufacturer, &veto ) ) != 0 ) { DBGC ( driver, "EFIVETO %s could not determine " @@ -593,11 +663,9 @@ void efi_veto ( void ) { efi_handle_name ( driver ), strerror ( rc ) ); continue; } - if ( ! veto ) + if ( ! veto.driver ) continue; - DBGC ( driver, "EFIVETO %s is vetoed (%s)\n", - efi_handle_name ( driver ), veto->name ); - if ( ( rc = efi_veto_driver ( driver ) ) != 0 ) { + if ( ( rc = efi_veto_driver ( &veto ) ) != 0 ) { DBGC ( driver, "EFIVETO %s could not veto: %s\n", efi_handle_name ( driver ), strerror ( rc ) ); } |