diff options
Diffstat (limited to 'src/interface')
60 files changed, 3141 insertions, 1555 deletions
diff --git a/src/interface/bofm/bofm.c b/src/interface/bofm/bofm.c index 54039193a..b5438e07c 100644 --- a/src/interface/bofm/bofm.c +++ b/src/interface/bofm/bofm.c @@ -26,7 +26,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <string.h> #include <errno.h> -#include <ipxe/uaccess.h> #include <ipxe/list.h> #include <ipxe/ethernet.h> #include <ipxe/bofm.h> @@ -150,27 +149,25 @@ static void bofm_remove ( struct pci_device *pci ) { /** * Locate BOFM table section * - * @v bofmtab BOFM table - * @v len Length of BOFM table + * @v bofmhdr BOFM table header * @v magic Section magic - * @v bofmsec BOFM section header to fill in - * @ret offset Offset to section, or 0 if not found + * @ret bofmsec BOFM section header, or NULL if not found */ -static size_t bofm_locate_section ( userptr_t bofmtab, size_t len, - uint32_t magic, - struct bofm_section_header *bofmsec ) { - size_t offset = sizeof ( struct bofm_global_header ); - - while ( offset < len ) { - copy_from_user ( bofmsec, bofmtab, offset, - sizeof ( *bofmsec ) ); +static struct bofm_section_header * +bofm_locate_section ( struct bofm_global_header *bofmhdr, uint32_t magic ) { + struct bofm_section_header *bofmsec; + size_t offset; + + /* Scan for section */ + for ( offset = sizeof ( *bofmhdr ) ; offset < bofmhdr->length ; + offset += ( sizeof ( *bofmsec ) + bofmsec->length ) ) { + bofmsec = ( ( ( void * ) bofmhdr ) + offset ); if ( bofmsec->magic == magic ) - return offset; + return bofmsec; if ( bofmsec->magic == BOFM_DONE_MAGIC ) break; - offset += ( sizeof ( *bofmsec ) + bofmsec->length ); } - return 0; + return NULL; } /** @@ -235,32 +232,31 @@ static int bofm_en ( struct bofm_device *bofm, struct bofm_en *en ) { * @v pci PCI device * @ret bofmrc BOFM return status */ -int bofm ( userptr_t bofmtab, struct pci_device *pci ) { - struct bofm_global_header bofmhdr; - struct bofm_section_header bofmsec; - struct bofm_en en; +int bofm ( void *bofmtab, struct pci_device *pci ) { + struct bofm_global_header *bofmhdr; + struct bofm_section_header *bofmsec; + struct bofm_en *en; struct bofm_device *bofm; - size_t en_region_offset; - size_t en_offset; + size_t offset; int skip; int rc; int bofmrc; /* Read BOFM structure */ - copy_from_user ( &bofmhdr, bofmtab, 0, sizeof ( bofmhdr ) ); - if ( bofmhdr.magic != BOFM_IOAA_MAGIC ) { + bofmhdr = bofmtab; + if ( bofmhdr->magic != BOFM_IOAA_MAGIC ) { DBG ( "BOFM: invalid table signature " BOFM_MAGIC_FMT "\n", - BOFM_MAGIC_ARGS ( bofmhdr.magic ) ); + BOFM_MAGIC_ARGS ( bofmhdr->magic ) ); bofmrc = BOFM_ERR_INVALID_ACTION; goto err_bad_signature; } DBG ( "BOFM: " BOFM_MAGIC_FMT " (profile \"%s\")\n", - BOFM_MAGIC_ARGS ( bofmhdr.action ), bofmhdr.profile ); + BOFM_MAGIC_ARGS ( bofmhdr->action ), bofmhdr->profile ); /* Determine whether or not we should skip normal POST * initialisation. */ - switch ( bofmhdr.action ) { + switch ( bofmhdr->action ) { case BOFM_ACTION_UPDT: case BOFM_ACTION_DFLT: case BOFM_ACTION_HVST: @@ -272,7 +268,7 @@ int bofm ( userptr_t bofmtab, struct pci_device *pci ) { break; default: DBG ( "BOFM: invalid action " BOFM_MAGIC_FMT "\n", - BOFM_MAGIC_ARGS ( bofmhdr.action ) ); + BOFM_MAGIC_ARGS ( bofmhdr->action ) ); bofmrc = BOFM_ERR_INVALID_ACTION; goto err_bad_action; } @@ -291,46 +287,44 @@ int bofm ( userptr_t bofmtab, struct pci_device *pci ) { } /* Locate EN section, if present */ - en_region_offset = bofm_locate_section ( bofmtab, bofmhdr.length, - BOFM_EN_MAGIC, &bofmsec ); - if ( ! en_region_offset ) { + bofmsec = bofm_locate_section ( bofmhdr, BOFM_EN_MAGIC ); + if ( ! bofmsec ) { DBG ( "BOFM: No EN section found\n" ); bofmrc = ( BOFM_SUCCESS | skip ); goto err_no_en_section; } /* Iterate through EN entries */ - for ( en_offset = ( en_region_offset + sizeof ( bofmsec ) ) ; - en_offset < ( en_region_offset + sizeof ( bofmsec ) + - bofmsec.length ) ; en_offset += sizeof ( en ) ) { - copy_from_user ( &en, bofmtab, en_offset, sizeof ( en ) ); + for ( offset = sizeof ( *bofmsec ) ; offset < bofmsec->length ; + offset += sizeof ( *en ) ) { + en = ( ( ( void * ) bofmsec ) + offset ); DBG2 ( "BOFM: EN entry found:\n" ); - DBG2_HDA ( en_offset, &en, sizeof ( en ) ); - if ( ( en.options & BOFM_EN_MAP_MASK ) != BOFM_EN_MAP_PFA ) { + DBG2_HDA ( offset, en, sizeof ( *en ) ); + if ( ( en->options & BOFM_EN_MAP_MASK ) != BOFM_EN_MAP_PFA ) { DBG ( "BOFM: slot %d port %d has no PCI mapping\n", - en.slot, ( en.port + 1 ) ); + en->slot, ( en->port + 1 ) ); continue; } DBG ( "BOFM: slot %d port %d%s is " PCI_FMT " mport %d\n", - en.slot, ( en.port + 1 ), - ( ( en.slot || en.port ) ? "" : "(?)" ), 0, - PCI_BUS ( en.busdevfn ), PCI_SLOT ( en.busdevfn ), - PCI_FUNC ( en.busdevfn ), en.mport ); - bofm = bofm_find_busdevfn ( en.busdevfn ); + en->slot, ( en->port + 1 ), + ( ( en->slot || en->port ) ? "" : "(?)" ), 0, + PCI_BUS ( en->busdevfn ), PCI_SLOT ( en->busdevfn ), + PCI_FUNC ( en->busdevfn ), en->mport ); + bofm = bofm_find_busdevfn ( en->busdevfn ); if ( ! bofm ) { DBG ( "BOFM: " PCI_FMT " mport %d ignored\n", 0, - PCI_BUS ( en.busdevfn ), PCI_SLOT ( en.busdevfn ), - PCI_FUNC ( en.busdevfn ), en.mport ); + PCI_BUS ( en->busdevfn ), + PCI_SLOT ( en->busdevfn ), + PCI_FUNC ( en->busdevfn ), en->mport ); continue; } - if ( ( rc = bofm_en ( bofm, &en ) ) == 0 ) { - en.options |= BOFM_EN_CSM_SUCCESS; + if ( ( rc = bofm_en ( bofm, en ) ) == 0 ) { + en->options |= BOFM_EN_CSM_SUCCESS; } else { - en.options |= BOFM_EN_CSM_FAILED; + en->options |= BOFM_EN_CSM_FAILED; } DBG2 ( "BOFM: EN entry after processing:\n" ); - DBG2_HDA ( en_offset, &en, sizeof ( en ) ); - copy_to_user ( bofmtab, en_offset, &en, sizeof ( en ) ); + DBG2_HDA ( offset, en, sizeof ( *en ) ); } bofmrc = ( BOFM_SUCCESS | skip ); diff --git a/src/interface/efi/efi_acpi.c b/src/interface/efi/efi_acpi.c index 07a225632..cb8b4a5d0 100644 --- a/src/interface/efi/efi_acpi.c +++ b/src/interface/efi/efi_acpi.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** * @file @@ -31,6 +32,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ #include <ipxe/acpi.h> +#include <ipxe/uaccess.h> #include <ipxe/efi/efi.h> #include <ipxe/efi/Guid/Acpi.h> #include <ipxe/efi/efi_acpi.h> @@ -42,15 +44,15 @@ EFI_USE_TABLE ( ACPI_10_TABLE, &rsdp, 0 ); /** * Locate ACPI root system description table * - * @ret rsdt ACPI root system description table, or UNULL + * @ret rsdt ACPI root system description table, or NULL */ -static userptr_t efi_find_rsdt ( void ) { +static const struct acpi_rsdt * efi_find_rsdt ( void ) { /* Locate RSDT via ACPI configuration table, if available */ if ( rsdp ) - return phys_to_user ( rsdp->RsdtAddress ); + return phys_to_virt ( rsdp->RsdtAddress ); - return UNULL; + return NULL; } PROVIDE_ACPI ( efi, acpi_find_rsdt, efi_find_rsdt ); diff --git a/src/interface/efi/efi_autoboot.c b/src/interface/efi/efi_autoboot.c index a103c2f19..9e0c3e42e 100644 --- a/src/interface/efi/efi_autoboot.c +++ b/src/interface/efi/efi_autoboot.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <string.h> #include <errno.h> @@ -48,30 +49,21 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ int efi_set_autoboot_ll_addr ( EFI_HANDLE device, EFI_DEVICE_PATH_PROTOCOL *path ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - union { - EFI_SIMPLE_NETWORK_PROTOCOL *snp; - void *interface; - } snp; + EFI_SIMPLE_NETWORK_PROTOCOL *snp; EFI_SIMPLE_NETWORK_MODE *mode; - EFI_STATUS efirc; unsigned int vlan; int rc; /* Look for an SNP instance on the image's device handle */ - if ( ( efirc = bs->OpenProtocol ( device, - &efi_simple_network_protocol_guid, - &snp.interface, efi_image_handle, - NULL, - EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){ - rc = -EEFI ( efirc ); + if ( ( rc = efi_open ( device, &efi_simple_network_protocol_guid, + &snp ) ) != 0 ) { 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; + mode = snp->Mode; vlan = efi_path_vlan ( path ); set_autoboot_ll_addr ( &mode->CurrentAddress, mode->HwAddressSize, vlan ); @@ -90,9 +82,5 @@ int efi_set_autoboot_ll_addr ( EFI_HANDLE device, efi_handle_name ( device ), vlan ); } - /* Close protocol */ - 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 index d9ad3b990..b63ac1602 100644 --- a/src/interface/efi/efi_autoexec.c +++ b/src/interface/efi/efi_autoexec.c @@ -22,12 +22,14 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <string.h> #include <errno.h> #include <ipxe/timer.h> #include <ipxe/image.h> #include <ipxe/netdevice.h> +#include <ipxe/uri.h> #include <ipxe/efi/efi.h> #include <ipxe/efi/efi_utils.h> #include <ipxe/efi/efi_autoexec.h> @@ -42,7 +44,10 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ /** Timeout for autoexec script downloads */ -#define EFI_AUTOEXEC_TIMEOUT ( 2 * TICKS_PER_SEC ) +#define EFI_AUTOEXEC_TIMEOUT ( 30 * TICKS_PER_SEC ) + +/** Timeout for autoexec pending operation completion */ +#define EFI_AUTOEXEC_SYNC_TIMEOUT ( 1 * TICKS_PER_SEC ) /** Autoexec script image name */ #define EFI_AUTOEXEC_NAME "autoexec.ipxe" @@ -111,6 +116,14 @@ static int efi_autoexec_network ( EFI_HANDLE handle, struct image **image ) { goto err_create; } + /* Do nothing unless we have a usable current working URI */ + if ( ! cwuri ) { + DBGC ( device, "EFI %s has no current working URI\n", + efi_handle_name ( device ) ); + rc = -ENOTTY; + goto err_cwuri; + } + /* Open network device */ if ( ( rc = netdev_open ( netdev ) ) != 0 ) { DBGC ( device, "EFI %s could not open net device: %s\n", @@ -127,9 +140,10 @@ static int efi_autoexec_network ( EFI_HANDLE handle, struct image **image ) { } /* Ensure network exchanges have completed */ - sync ( EFI_AUTOEXEC_TIMEOUT ); + sync ( EFI_AUTOEXEC_SYNC_TIMEOUT ); err_open: + err_cwuri: mnptemp_destroy ( netdev ); err_create: return rc; diff --git a/src/interface/efi/efi_block.c b/src/interface/efi/efi_block.c index a9c3d656d..0da92307b 100644 --- a/src/interface/efi/efi_block.c +++ b/src/interface/efi/efi_block.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** * @file @@ -59,8 +60,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/efi/efi_snp.h> #include <ipxe/efi/efi_path.h> #include <ipxe/efi/efi_null.h> +#include <ipxe/efi/efi_wrap.h> #include <ipxe/efi/efi_block.h> -#include <usr/efiboot.h> /** ACPI table protocol protocol */ static EFI_ACPI_TABLE_PROTOCOL *acpi; @@ -96,8 +97,9 @@ struct efi_block_data { static int efi_block_rw ( struct san_device *sandev, uint64_t lba, void *data, size_t len, int ( * sandev_rw ) ( struct san_device *sandev, - uint64_t lba, unsigned int count, - userptr_t buffer ) ) { + uint64_t lba, + unsigned int count, + void *buffer ) ) { struct efi_block_data *block = sandev->priv; unsigned int count; int rc; @@ -111,8 +113,7 @@ static int efi_block_rw ( struct san_device *sandev, uint64_t lba, } /* Read from / write to block device */ - if ( ( rc = sandev_rw ( sandev, lba, count, - virt_to_user ( data ) ) ) != 0 ) { + if ( ( rc = sandev_rw ( sandev, lba, count, data ) ) != 0 ) { DBGC ( sandev->drive, "EFIBLK %#02x I/O failed: %s\n", sandev->drive, strerror ( rc ) ); return rc; @@ -219,14 +220,10 @@ efi_block_io_flush ( EFI_BLOCK_IO_PROTOCOL *block_io ) { * @v handle Block device handle */ static void efi_block_connect ( unsigned int drive, EFI_HANDLE handle ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - EFI_STATUS efirc; int rc; /* Try to connect all possible drivers to this block device */ - if ( ( efirc = bs->ConnectController ( handle, NULL, NULL, - TRUE ) ) != 0 ) { - rc = -EEFI ( efirc ); + if ( ( rc = efi_connect ( handle, NULL ) ) != 0 ) { DBGC ( drive, "EFIBLK %#02x could not connect drivers: %s\n", drive, strerror ( rc ) ); /* May not be an error; may already be connected */ @@ -519,40 +516,27 @@ static int efi_block_describe ( void ) { */ static int efi_block_root ( unsigned int drive, EFI_HANDLE handle, EFI_FILE_PROTOCOL **root ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - EFI_GUID *protocol = &efi_simple_file_system_protocol_guid; - union { - EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *fs; - void *interface; - } u; + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *fs; EFI_STATUS efirc; int rc; /* Open filesystem protocol */ - if ( ( efirc = bs->OpenProtocol ( handle, protocol, &u.interface, - efi_image_handle, handle, - EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){ - rc = -EEFI ( efirc ); + if ( ( rc = efi_open ( handle, &efi_simple_file_system_protocol_guid, + &fs ) ) != 0 ) { DBGC ( drive, "EFIBLK %#02x could not open %s filesystem: %s\n", drive, efi_handle_name ( handle ), strerror ( rc ) ); - goto err_open; + return rc; } /* Open root volume */ - if ( ( efirc = u.fs->OpenVolume ( u.fs, root ) ) != 0 ) { + if ( ( efirc = fs->OpenVolume ( fs, root ) ) != 0 ) { rc = -EEFI ( efirc ); DBGC ( drive, "EFIBLK %#02x could not open %s root: %s\n", drive, efi_handle_name ( handle ), strerror ( rc ) ); - goto err_volume; + return rc; } - /* Success */ - rc = 0; - - err_volume: - bs->CloseProtocol ( handle, protocol, efi_image_handle, handle ); - err_open: - return rc; + return 0; } /** @@ -675,31 +659,21 @@ static int efi_block_match ( unsigned int drive, EFI_HANDLE handle, EFI_DEVICE_PATH_PROTOCOL *path, struct san_boot_config *config, EFI_DEVICE_PATH_PROTOCOL **fspath ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - EFI_GUID *protocol = &efi_device_path_protocol_guid; - union { - EFI_DEVICE_PATH_PROTOCOL *path; - void *interface; - } u; EFI_FILE *root; union uuid guid; - EFI_STATUS efirc; int rc; /* Identify device path */ - if ( ( efirc = bs->OpenProtocol ( handle, protocol, &u.interface, - efi_image_handle, handle, - EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){ - rc = -EEFI ( efirc ); + if ( ( rc = efi_open ( handle, &efi_device_path_protocol_guid, + fspath ) ) != 0 ) { DBGC ( drive, "EFIBLK %#02x could not open %s device path: " "%s\n", drive, efi_handle_name ( handle ), strerror ( rc ) ); goto err_open; } - *fspath = u.path; /* Check if filesystem is a child of this block device */ - if ( memcmp ( u.path, path, efi_path_len ( path ) ) != 0 ) { + if ( memcmp ( *fspath, path, efi_path_len ( path ) ) != 0 ) { /* Not a child device */ rc = -ENOTTY; DBGC2 ( drive, "EFIBLK %#02x is not parent of %s\n", @@ -707,11 +681,11 @@ static int efi_block_match ( unsigned int drive, EFI_HANDLE handle, goto err_not_child; } DBGC ( drive, "EFIBLK %#02x contains filesystem %s\n", - drive, efi_devpath_text ( u.path ) ); + drive, efi_devpath_text ( *fspath ) ); /* Check if filesystem matches GUID, if applicable */ if ( config->uuid ) { - if ( ( rc = efi_path_guid ( u.path, &guid ) ) != 0 ) { + if ( ( rc = efi_path_guid ( *fspath, &guid ) ) != 0 ) { DBGC ( drive, "EFIBLK %#02x could not determine GUID: " "%s\n", drive, strerror ( rc ) ); goto err_no_guid; @@ -759,7 +733,6 @@ static int efi_block_match ( unsigned int drive, EFI_HANDLE handle, err_wrong_guid: err_no_guid: err_not_child: - bs->CloseProtocol ( handle, protocol, efi_image_handle, handle ); err_open: return rc; } @@ -777,11 +750,7 @@ static int efi_block_scan ( unsigned int drive, EFI_HANDLE handle, struct san_boot_config *config, EFI_DEVICE_PATH_PROTOCOL **fspath ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - EFI_GUID *protocol = &efi_device_path_protocol_guid; - union { - EFI_DEVICE_PATH_PROTOCOL *path; - void *interface; - } u; + EFI_DEVICE_PATH_PROTOCOL *path; EFI_HANDLE *handles; UINTN count; unsigned int i; @@ -792,10 +761,8 @@ static int efi_block_scan ( unsigned int drive, EFI_HANDLE handle, efi_block_connect ( drive, handle ); /* Identify device path */ - if ( ( efirc = bs->OpenProtocol ( handle, protocol, &u.interface, - efi_image_handle, handle, - EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){ - rc = -EEFI ( efirc ); + if ( ( rc = efi_open ( handle, &efi_device_path_protocol_guid, + &path ) ) != 0 ) { DBGC ( drive, "EFIBLK %#02x could not open device path: %s\n", drive, strerror ( rc ) ); goto err_open; @@ -816,7 +783,7 @@ static int efi_block_scan ( unsigned int drive, EFI_HANDLE handle, for ( i = 0 ; i < count ; i++ ) { /* Check for a matching filesystem */ - if ( ( rc = efi_block_match ( drive, handles[i], u.path, + if ( ( rc = efi_block_match ( drive, handles[i], path, config, fspath ) ) != 0 ) continue; @@ -825,7 +792,6 @@ static int efi_block_scan ( unsigned int drive, EFI_HANDLE handle, bs->FreePool ( handles ); err_locate: - bs->CloseProtocol ( handle, protocol, efi_image_handle, handle ); err_open: return rc; } @@ -897,12 +863,18 @@ static int efi_block_exec ( unsigned int drive, } } + /* Wrap calls made by the loaded image (for debugging) */ + efi_wrap_image ( image ); + /* Start image */ efirc = bs->StartImage ( image, NULL, NULL ); rc = ( efirc ? -EEFI ( efirc ) : 0 ); DBGC ( drive, "EFIBLK %#02x boot image returned: %s\n", drive, strerror ( rc ) ); + /* Remove wrapper */ + efi_unwrap(); + err_load_security_violation: bs->UnloadImage ( image ); err_load: @@ -922,52 +894,34 @@ static int efi_block_exec ( unsigned int drive, * equivalent functionality to BIOS drive numbers. */ static int efi_block_local ( EFI_HANDLE handle ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - EFI_GUID *protocol = &efi_block_io_protocol_guid; struct san_device *sandev; struct efi_block_data *block; - union { - EFI_BLOCK_IO_PROTOCOL *blockio; - void *interface; - } u; - EFI_STATUS efirc; + EFI_BLOCK_IO_PROTOCOL *blockio; int rc; /* Check if handle belongs to a SAN device */ for_each_sandev ( sandev ) { block = sandev->priv; - if ( handle == block->handle ) { - rc = -ENOTTY; - goto err_sandev; - } + if ( handle == block->handle ) + return -ENOTTY; } /* Open block I/O protocol */ - if ( ( efirc = bs->OpenProtocol ( handle, protocol, &u.interface, - efi_image_handle, handle, - EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){ - rc = -EEFI ( efirc ); + if ( ( rc = efi_open ( handle, &efi_block_io_protocol_guid, + &blockio ) ) != 0 ) { DBGC ( handle, "EFIBLK %s could not open block I/O: %s\n", efi_handle_name ( handle ), strerror ( rc ) ); - goto err_open; + return rc; } /* Do not assign drive numbers for partitions */ - if ( u.blockio->Media->LogicalPartition ) { - rc = -ENOTTY; + if ( blockio->Media->LogicalPartition ) { DBGC2 ( handle, "EFLBLK %s is a partition\n", efi_handle_name ( handle ) ); - goto err_partition; + return -ENOTTY; } - /* Success */ - rc = 0; - - err_partition: - bs->CloseProtocol ( handle, protocol, efi_image_handle, handle ); - err_open: - err_sandev: - return rc; + return 0; } /** @@ -991,6 +945,8 @@ static int efi_block_boot ( unsigned int drive, EFI_STATUS efirc; int rc; + /* Ensure that any local drives are connected */ + efi_driver_reconnect_all(); /* Release SNP devices */ efi_snp_release(); diff --git a/src/interface/efi/efi_bofm.c b/src/interface/efi/efi_bofm.c index 15f3837cc..3d956800e 100644 --- a/src/interface/efi/efi_bofm.c +++ b/src/interface/efi/efi_bofm.c @@ -23,6 +23,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +#include <string.h> #include <errno.h> #include <ipxe/bofm.h> #include <ipxe/efi/efi.h> @@ -232,10 +233,20 @@ static int efi_bofm_start ( struct efi_device *efidev ) { EFI_STATUS efirc; int rc; - /* Open PCI device, if possible */ - if ( ( rc = efipci_open ( device, EFI_OPEN_PROTOCOL_GET_PROTOCOL, - &efipci ) ) != 0 ) + /* Get PCI device information */ + if ( ( rc = efipci_info ( device, &efipci ) ) != 0 ) { + DBGC ( device, "EFIBOFM %s cannot get PCI information: %s\n", + efi_handle_name ( device ), strerror ( rc ) ); + goto err_info; + } + + /* Open PCI I/O protocol */ + if ( ( rc = efi_open_unsafe ( device, &efi_pci_io_protocol_guid, + &efipci.io ) ) != 0 ) { + DBGC ( device, "EFIBOFM %s cannot open PCI device: %s\n", + efi_handle_name ( device ), strerror ( rc ) ); goto err_open; + } /* Locate BOFM protocol */ if ( ( efirc = bs->LocateProtocol ( &bofm1_protocol_guid, NULL, @@ -274,8 +285,7 @@ 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 ), - &efipci.pci ); + bofmrc = bofm ( ( 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", @@ -313,8 +323,9 @@ static int efi_bofm_start ( struct efi_device *efidev ) { err_set_status: err_locate_bofm: - efipci_close ( device ); + efi_close_unsafe ( device, &efi_pci_io_protocol_guid ); err_open: + err_info: return rc; } diff --git a/src/interface/efi/efi_cacert.c b/src/interface/efi/efi_cacert.c new file mode 100644 index 000000000..3e941ddc5 --- /dev/null +++ b/src/interface/efi/efi_cacert.c @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2025 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_SECBOOT ( PERMITTED ); + +/** @file + * + * EFI CA certificates + * + */ + +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <errno.h> +#include <ipxe/init.h> +#include <ipxe/x509.h> +#include <ipxe/rootcert.h> +#include <ipxe/efi/efi.h> +#include <ipxe/efi/efi_siglist.h> +#include <ipxe/efi/Guid/TlsAuthentication.h> + +/** List of EFI CA certificates */ +static struct x509_chain efi_cacerts = { + .refcnt = REF_INIT ( ref_no_free ), + .links = LIST_HEAD_INIT ( efi_cacerts.links ), +}; + +/** + * Retrieve EFI CA certificate + * + * @v data TlsCaCertificate variable data + * @v len Length of TlsCaCertificate + * @v offset Offset within data + * @v next Next offset, or negative error + */ +static int efi_cacert ( const void *data, size_t len, size_t offset ) { + struct asn1_cursor *cursor; + struct x509_certificate *cert; + int next; + int rc; + + /* Extract ASN.1 object */ + next = efisig_asn1 ( data, len, offset, &cursor ); + if ( next < 0 ) { + rc = next; + DBGC ( &efi_cacerts, "EFICA could not parse at +%#zx: %s\n", + offset, strerror ( rc ) ); + goto err_asn1; + } + + /* Append to list of EFI CA certificates */ + if ( ( rc = x509_append_raw ( &efi_cacerts, cursor->data, + cursor->len ) ) != 0 ) { + DBGC ( &efi_cacerts, "EFICA could not append at +%#zx: %s\n", + offset, strerror ( rc ) ); + goto err_append; + } + cert = x509_last ( &efi_cacerts ); + DBGC ( &efi_cacerts, "EFICA found certificate %s\n", + x509_name ( cert ) ); + + /* Mark certificate as valid (i.e. trusted) if permitted */ + if ( allow_trust_override ) { + DBGC ( &efi_cacerts, "EFICA trusting certificate %s\n", + x509_name ( cert ) ); + x509_set_valid ( cert, NULL, &root_certificates ); + } + + /* Free ASN.1 object */ + free ( cursor ); + + return next; + + err_append: + free ( cursor ); + err_asn1: + return rc; +} + +/** + * Retrieve all EFI CA certificates + * + * @ret rc Return status code + */ +static int efi_cacert_all ( void ) { + EFI_RUNTIME_SERVICES *rs = efi_systab->RuntimeServices; + EFI_GUID *guid = &efi_tls_ca_certificate_guid; + static CHAR16 *wname = EFI_TLS_CA_CERTIFICATE_VARIABLE; + int offset = 0; + UINT32 attrs; + UINTN size; + void *data; + EFI_STATUS efirc; + int rc; + + /* Get variable length */ + size = 0; + if ( ( efirc = rs->GetVariable ( wname, guid, &attrs, &size, + NULL ) ) != EFI_BUFFER_TOO_SMALL ) { + rc = -EEFI ( efirc ); + DBGC ( &efi_cacerts, "EFICA could not get %ls size: %s\n", + wname, strerror ( rc ) ); + goto err_len; + } + + /* Allocate temporary buffer */ + data = malloc ( size ); + if ( ! data ) { + rc = -ENOMEM; + goto err_alloc; + } + + /* Read variable */ + if ( ( efirc = rs->GetVariable ( wname, guid, &attrs, &size, + data ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( &efi_cacerts, "EFICA could not read %ls: %s\n", + wname, strerror ( rc ) ); + goto err_get; + } + + /* Parse certificates */ + while ( ( ( size_t ) offset ) < size ) { + offset = efi_cacert ( data, size, offset ); + if ( offset < 0 ) { + rc = offset; + goto err_cacert; + } + } + + /* Success */ + rc = 0; + + err_cacert: + err_get: + free ( data ); + err_alloc: + err_len: + return rc; +} + +/** + * Initialise EFI CA certificates + * + */ +static void efi_cacert_init ( void ) { + int rc; + + /* Initialise all certificates */ + if ( ( rc = efi_cacert_all() ) != 0 ) { + DBGC ( &efi_cacert, "EFICA could not initialise: %s\n", + strerror ( rc ) ); + /* Nothing we can do at this point */ + return; + } +} + +/** EFI CA certificates initialisation function */ +struct init_fn efi_cacert_init_fn __init_fn ( INIT_LATE ) = { + .name = "eficacert", + .initialise = efi_cacert_init, +}; + +/** + * Discard any EFI CA certificates + * + */ +static void efi_cacert_shutdown ( int booting __unused ) { + + /* Drop our references to the certificates */ + DBGC ( &efi_cacert, "EFICA discarding certificates\n" ); + x509_truncate ( &efi_cacerts, NULL ); + assert ( list_empty ( &efi_cacerts.links ) ); +} + +/** EFI CA certificates shutdown function */ +struct startup_fn efi_cacert_shutdown_fn __startup_fn ( STARTUP_NORMAL ) = { + .name = "efi_cacert", + .shutdown = efi_cacert_shutdown, +}; diff --git a/src/interface/efi/efi_cachedhcp.c b/src/interface/efi/efi_cachedhcp.c index b9e49cf48..2f33fcefb 100644 --- a/src/interface/efi/efi_cachedhcp.c +++ b/src/interface/efi/efi_cachedhcp.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <string.h> #include <errno.h> @@ -46,79 +47,57 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ int efi_cachedhcp_record ( EFI_HANDLE device, EFI_DEVICE_PATH_PROTOCOL *path ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; unsigned int vlan; - union { - EFI_PXE_BASE_CODE_PROTOCOL *pxe; - void *interface; - } pxe; + EFI_PXE_BASE_CODE_PROTOCOL *pxe; EFI_PXE_BASE_CODE_MODE *mode; - EFI_STATUS efirc; int rc; /* Get VLAN tag, if any */ vlan = efi_path_vlan ( path ); /* 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 ); + if ( ( rc = efi_open ( device, &efi_pxe_base_code_protocol_guid, + &pxe ) ) != 0 ) { DBGC ( device, "EFI %s has no PXE base code instance: %s\n", efi_handle_name ( device ), strerror ( rc ) ); - goto err_open; + return rc; } /* Do not attempt to cache IPv6 packets */ - mode = pxe.pxe->Mode; + mode = pxe->Mode; if ( mode->UsingIpv6 ) { - rc = -ENOTSUP; DBGC ( device, "EFI %s has IPv6 PXE base code\n", efi_handle_name ( device ) ); - goto err_ipv6; + return -ENOTSUP; } /* Record DHCPACK, if present */ if ( mode->DhcpAckReceived && - ( ( rc = cachedhcp_record ( &cached_dhcpack, vlan, - virt_to_user ( &mode->DhcpAck ), + ( ( rc = cachedhcp_record ( &cached_dhcpack, vlan, &mode->DhcpAck, sizeof ( mode->DhcpAck ) ) ) != 0 ) ) { DBGC ( device, "EFI %s could not record DHCPACK: %s\n", efi_handle_name ( device ), strerror ( rc ) ); - goto err_dhcpack; + return rc; } /* Record ProxyDHCPOFFER, if present */ if ( mode->ProxyOfferReceived && ( ( rc = cachedhcp_record ( &cached_proxydhcp, vlan, - virt_to_user ( &mode->ProxyOffer ), + &mode->ProxyOffer, sizeof ( mode->ProxyOffer ) ) ) != 0)){ DBGC ( device, "EFI %s could not record ProxyDHCPOFFER: %s\n", efi_handle_name ( device ), strerror ( rc ) ); - goto err_proxydhcp; + return rc; } /* Record PxeBSACK, if present */ if ( mode->PxeReplyReceived && - ( ( rc = cachedhcp_record ( &cached_pxebs, vlan, - virt_to_user ( &mode->PxeReply ), - sizeof ( mode->PxeReply ) ) ) != 0)){ + ( ( rc = cachedhcp_record ( &cached_pxebs, vlan, &mode->PxeReply, + sizeof ( mode->PxeReply ) ) ) != 0 )){ DBGC ( device, "EFI %s could not record PXEBSACK: %s\n", efi_handle_name ( device ), strerror ( rc ) ); - goto err_pxebs; + return rc; } - /* Success */ - rc = 0; - - err_pxebs: - err_proxydhcp: - err_dhcpack: - err_ipv6: - bs->CloseProtocol ( device, &efi_pxe_base_code_protocol_guid, - efi_image_handle, NULL ); - err_open: - return rc; + return 0; } diff --git a/src/interface/efi/efi_cmdline.c b/src/interface/efi/efi_cmdline.c index b33bebd8c..f5844f2ad 100644 --- a/src/interface/efi/efi_cmdline.c +++ b/src/interface/efi/efi_cmdline.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** @file * @@ -31,11 +32,13 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdio.h> #include <stdlib.h> +#include <string.h> #include <ctype.h> #include <errno.h> #include <ipxe/init.h> #include <ipxe/image.h> #include <ipxe/script.h> +#include <ipxe/uaccess.h> #include <ipxe/efi/efi.h> #include <ipxe/efi/efi_cmdline.h> @@ -57,6 +60,7 @@ static void efi_cmdline_free ( struct refcnt *refcnt ) { struct image *image = container_of ( refcnt, struct image, refcnt ); DBGC ( image, "CMDLINE freeing command line\n" ); + free_image ( refcnt ); free ( efi_cmdline_copy ); } @@ -64,6 +68,7 @@ static void efi_cmdline_free ( struct refcnt *refcnt ) { static struct image efi_cmdline_image = { .refcnt = REF_INIT ( efi_cmdline_free ), .name = "<CMDLINE>", + .flags = ( IMAGE_STATIC | IMAGE_STATIC_NAME ), .type = &script_image_type, }; @@ -110,7 +115,7 @@ static int efi_cmdline_init ( void ) { DBGC ( colour, "CMDLINE using command line \"%s\"\n", cmdline ); /* Prepare and register image */ - efi_cmdline_image.data = virt_to_user ( cmdline ); + efi_cmdline_image.data = cmdline; efi_cmdline_image.len = strlen ( cmdline ); if ( efi_cmdline_image.len && ( ( rc = register_image ( &efi_cmdline_image ) ) != 0 ) ) { diff --git a/src/interface/efi/efi_connect.c b/src/interface/efi/efi_connect.c new file mode 100644 index 000000000..f4747cf6b --- /dev/null +++ b/src/interface/efi/efi_connect.c @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2025 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_SECBOOT ( PERMITTED ); + +/** @file + * + * EFI driver connection and disconnection + * + */ + +#include <errno.h> +#include <string.h> +#include <ipxe/efi/efi.h> + +/* Disambiguate the various error causes */ +#define EINFO_EEFI_CONNECT \ + __einfo_uniqify ( EINFO_EPLATFORM, 0x01, \ + "Could not connect controllers" ) +#define EINFO_EEFI_CONNECT_PROHIBITED \ + __einfo_platformify ( EINFO_EEFI_CONNECT, \ + EFI_SECURITY_VIOLATION, \ + "Connecting controllers prohibited by " \ + "security policy" ) +#define EEFI_CONNECT_PROHIBITED \ + __einfo_error ( EINFO_EEFI_CONNECT_PROHIBITED ) +#define EEFI_CONNECT( efirc ) EPLATFORM ( EINFO_EEFI_CONNECT, efirc, \ + EEFI_CONNECT_PROHIBITED ) + +/** + * Connect UEFI driver(s) + * + * @v device EFI device handle + * @v driver EFI driver handle, or NULL + * @ret rc Return status code + */ +int efi_connect ( EFI_HANDLE device, EFI_HANDLE driver ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_HANDLE driverlist[2] = { driver, NULL }; + EFI_HANDLE *drivers = ( driver ? driverlist : NULL ); + EFI_STATUS efirc; + int rc; + + /* Attempt connection at external TPL */ + DBGC ( device, "EFI %s connecting ", efi_handle_name ( device ) ); + DBGC ( device, "%s driver at %s TPL\n", + ( driver ? efi_handle_name ( driver ) : "any" ), + efi_tpl_name ( efi_external_tpl ) ); + bs->RestoreTPL ( efi_external_tpl ); + efirc = bs->ConnectController ( device, drivers, NULL, TRUE ); + bs->RaiseTPL ( efi_internal_tpl ); + if ( efirc != 0 ) { + rc = -EEFI_CONNECT ( efirc ); + DBGC ( device, "EFI %s could not connect: %s\n", + efi_handle_name ( device ), strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Disconnect UEFI driver(s) + * + * @v device EFI device handle + * @v driver EFI driver handle, or NULL + * @ret rc Return status code + */ +int efi_disconnect ( EFI_HANDLE device, EFI_HANDLE driver ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_STATUS efirc; + int rc; + + /* Attempt disconnection at external TPL */ + DBGC ( device, "EFI %s disconnecting ", efi_handle_name ( device ) ); + DBGC ( device, "%s driver at %s TPL\n", + ( driver ? efi_handle_name ( driver ) : "any" ), + efi_tpl_name ( efi_external_tpl ) ); + bs->RestoreTPL ( efi_external_tpl ); + efirc = bs->DisconnectController ( device, driver, NULL ); + bs->RaiseTPL ( efi_internal_tpl ); + if ( ( efirc != 0 ) && ( efirc != EFI_NOT_FOUND ) ) { + rc = -EEFI ( efirc ); + DBGC ( device, "EFI %s could not disconnect: %s\n", + efi_handle_name ( device ), strerror ( rc ) ); + return rc; + } + + return 0; +} diff --git a/src/interface/efi/efi_console.c b/src/interface/efi/efi_console.c index 04bbd9e0c..afbd722ab 100644 --- a/src/interface/efi/efi_console.c +++ b/src/interface/efi/efi_console.c @@ -18,6 +18,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER ); +FILE_SECBOOT ( PERMITTED ); #include <stddef.h> #include <string.h> @@ -386,8 +387,6 @@ 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 */ @@ -395,8 +394,7 @@ static int efi_iskey ( void ) { return 1; /* Check to see if the WaitForKey event has fired */ - event = ( conin_ex ? conin_ex->WaitForKeyEx : conin->WaitForKey ); - if ( ( efirc = bs->CheckEvent ( event ) ) == 0 ) + if ( ( efirc = bs->CheckEvent ( conin->WaitForKey ) ) == 0 ) return 1; return 0; @@ -415,13 +413,7 @@ 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 @@ -441,15 +433,11 @@ static void efi_console_init ( void ) { * 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; + if ( ( rc = efi_open_unsafe ( efi_systab->ConsoleInHandle, + &efi_simple_text_input_ex_protocol_guid, + &efi_conin_ex ) ) == 0 ) { DBG ( "EFI using SimpleTextInputEx\n" ); } else { - rc = -EEFI ( efirc ); DBG ( "EFI has no SimpleTextInputEx: %s\n", strerror ( rc ) ); } } @@ -458,5 +446,6 @@ static void efi_console_init ( void ) { * EFI console initialisation function */ struct init_fn efi_console_init_fn __init_fn ( INIT_EARLY ) = { + .name = "eficonsole", .initialise = efi_console_init, }; diff --git a/src/interface/efi/efi_debug.c b/src/interface/efi/efi_debug.c index 52efebe5f..067899a72 100644 --- a/src/interface/efi/efi_debug.c +++ b/src/interface/efi/efi_debug.c @@ -31,15 +31,14 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ #include <stdio.h> -#include <string.h> #include <errno.h> -#include <ipxe/uuid.h> #include <ipxe/base16.h> #include <ipxe/vsprintf.h> #include <ipxe/efi/efi.h> #include <ipxe/efi/efi_path.h> #include <ipxe/efi/Protocol/ComponentName.h> #include <ipxe/efi/Protocol/ComponentName2.h> +#include <ipxe/efi/Protocol/DriverBinding.h> #include <ipxe/efi/Protocol/DevicePathToText.h> #include <ipxe/efi/IndustryStandard/PeImage.h> @@ -47,234 +46,25 @@ 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 ); -/** 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, - { 0xa8, 0xf4, 0x08, 0x51, 0x9b, 0xc4, 0x39, 0xdf } -}; - -/** A well-known GUID */ -struct efi_well_known_guid { - /** GUID */ - EFI_GUID *guid; - /** Name */ - const char *name; -}; - -/** Well-known GUIDs */ -static struct efi_well_known_guid efi_well_known_guids[] = { - { &efi_absolute_pointer_protocol_guid, - "AbsolutePointer" }, - { &efi_acpi_table_protocol_guid, - "AcpiTable" }, - { &efi_apple_net_boot_protocol_guid, - "AppleNetBoot" }, - { &efi_arp_protocol_guid, - "Arp" }, - { &efi_arp_service_binding_protocol_guid, - "ArpSb" }, - { &efi_block_io_protocol_guid, - "BlockIo" }, - { &efi_block_io2_protocol_guid, - "BlockIo2" }, - { &efi_bus_specific_driver_override_protocol_guid, - "BusSpecificDriverOverride" }, - { &efi_component_name_protocol_guid, - "ComponentName" }, - { &efi_component_name2_protocol_guid, - "ComponentName2" }, - { &efi_console_control_protocol_guid, - "ConsoleControl" }, - { &efi_device_path_protocol_guid, - "DevicePath" }, - { &efi_driver_binding_protocol_guid, - "DriverBinding" }, - { &efi_dhcp4_protocol_guid, - "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, - "LoadFile" }, - { &efi_load_file2_protocol_guid, - "LoadFile2" }, - { &efi_loaded_image_protocol_guid, - "LoadedImage" }, - { &efi_loaded_image_device_path_protocol_guid, - "LoadedImageDevicePath"}, - { &efi_managed_network_protocol_guid, - "ManagedNetwork" }, - { &efi_managed_network_service_binding_protocol_guid, - "ManagedNetworkSb" }, - { &efi_mtftp4_protocol_guid, - "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, - "Nii31" }, - { &efi_pci_io_protocol_guid, - "PciIo" }, - { &efi_pci_root_bridge_io_protocol_guid, - "PciRootBridgeIo" }, - { &efi_pxe_base_code_protocol_guid, - "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, - "SimpleNetwork" }, - { &efi_simple_pointer_protocol_guid, - "SimplePointer" }, - { &efi_simple_text_input_protocol_guid, - "SimpleTextInput" }, - { &efi_simple_text_input_ex_protocol_guid, - "SimpleTextInputEx" }, - { &efi_simple_text_output_protocol_guid, - "SimpleTextOutput" }, - { &efi_tcg_protocol_guid, - "Tcg" }, - { &efi_tcp4_protocol_guid, - "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, - "UnicodeCollation" }, - { &efi_usb_hc_protocol_guid, - "UsbHc" }, - { &efi_usb2_hc_protocol_guid, - "Usb2Hc" }, - { &efi_usb_io_protocol_guid, - "UsbIo" }, - { &efi_vlan_config_protocol_guid, - "VlanConfig" }, - { &efi_vlan_config_dxe_guid, - "VlanConfigDxe" }, -}; - /** - * Convert GUID to a printable string + * Name EFI TPL * - * @v guid GUID - * @ret string Printable string + * @v tpl Task priority level + * @ret text Task priority level as text */ -const __attribute__ (( pure )) char * efi_guid_ntoa ( CONST EFI_GUID *guid ) { - union { - union uuid uuid; - EFI_GUID guid; - } u; - unsigned int i; - - /* Sanity check */ - if ( ! guid ) - return NULL; - - /* Check for a match against well-known GUIDs */ - for ( i = 0 ; i < ( sizeof ( efi_well_known_guids ) / - sizeof ( efi_well_known_guids[0] ) ) ; i++ ) { - if ( memcmp ( guid, efi_well_known_guids[i].guid, - sizeof ( *guid ) ) == 0 ) { - return efi_well_known_guids[i].name; - } +const __attribute__ (( pure )) char * efi_tpl_name ( 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 GUID to standard endianness */ - memcpy ( &u.guid, guid, sizeof ( u.guid ) ); - uuid_mangle ( &u.uuid ); - return uuid_ntoa ( &u.uuid ); } /** @@ -393,15 +183,11 @@ void dbg_efi_openers ( EFI_HANDLE handle, EFI_GUID *protocol ) { * @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 ); + if ( ( rc = efi_open ( handle, protocol, &interface ) ) != 0 ) { printf ( "HANDLE %s could not identify %s: %s\n", efi_handle_name ( handle ), efi_guid_ntoa ( protocol ), strerror ( rc ) ); @@ -553,6 +339,64 @@ static const char * efi_driver_name2 ( EFI_COMPONENT_NAME2_PROTOCOL *wtf ) { } /** + * Get driver binding name + * + * @v binding Driver binding protocol + * @ret name Driver name, or NULL + */ +static const char * efi_binding_name ( EFI_DRIVER_BINDING_PROTOCOL *binding ) { + EFI_COMPONENT_NAME_PROTOCOL *name; + EFI_HANDLE image; + int rc; + + /* Sanity check */ + if ( ! binding ) { + DBG ( "[NULL DriverBinding]" ); + return NULL; + } + + /* Try to open component name protocol on image handle */ + image = binding->ImageHandle; + if ( ( rc = efi_open ( image, &efi_component_name_protocol_guid, + &name ) ) != 0 ) { + DBG ( "[DriverBinding no ComponentName]" ); + return NULL; + } + + /* Try to get name from component name protocol */ + return efi_driver_name ( name ); +} + +/** + * Get driver binding name + * + * @v binding Driver binding protocol + * @ret name Driver name, or NULL + */ +static const char * efi_binding_name2 ( EFI_DRIVER_BINDING_PROTOCOL *binding ){ + EFI_HANDLE image; + EFI_COMPONENT_NAME2_PROTOCOL *name2; + int rc; + + /* Sanity check */ + if ( ! binding ) { + DBG ( "[NULL DriverBinding]" ); + return NULL; + } + + /* Try to open component name protocol on image handle */ + image = binding->ImageHandle; + if ( ( rc = efi_open ( image, &efi_component_name2_protocol_guid, + &name2 ) ) != 0 ) { + DBG ( "[DriverBinding no ComponentName2]" ); + return NULL; + } + + /* Try to get name from component name protocol */ + return efi_driver_name2 ( name2 ); +} + +/** * Get PE/COFF debug filename * * @v loaded Loaded image @@ -770,15 +614,12 @@ struct efi_handle_name_type { /** EFI handle name types */ static struct efi_handle_name_type efi_handle_name_types[] = { - /* Device path */ - EFI_HANDLE_NAME_TYPE ( &efi_device_path_protocol_guid, - efi_devpath_text ), - /* Driver name (for driver image handles) */ - EFI_HANDLE_NAME_TYPE ( &efi_component_name2_protocol_guid, - efi_driver_name2 ), + /* Driver name (for driver binding handles) */ + EFI_HANDLE_NAME_TYPE ( &efi_driver_binding_protocol_guid, + efi_binding_name2 ), /* Driver name (via obsolete original ComponentName protocol) */ - EFI_HANDLE_NAME_TYPE ( &efi_component_name_protocol_guid, - efi_driver_name ), + EFI_HANDLE_NAME_TYPE ( &efi_driver_binding_protocol_guid, + efi_binding_name ), /* PE/COFF debug filename (for image handles) */ EFI_HANDLE_NAME_TYPE ( &efi_loaded_image_protocol_guid, efi_pecoff_debug_name ), @@ -791,6 +632,9 @@ static struct efi_handle_name_type efi_handle_name_types[] = { /* Handle's loaded image file path (for image handles) */ EFI_HANDLE_NAME_TYPE ( &efi_loaded_image_protocol_guid, efi_loaded_image_filepath_name ), + /* Device path */ + EFI_HANDLE_NAME_TYPE ( &efi_device_path_protocol_guid, + efi_devpath_text ), /* Our standard input file handle */ EFI_HANDLE_NAME_TYPE ( &efi_simple_text_input_protocol_guid, efi_conin_name ), @@ -816,6 +660,7 @@ const __attribute__ (( pure )) char * efi_handle_name ( EFI_HANDLE handle ) { void *interface; const char *name; EFI_STATUS efirc; + int rc; /* Fail immediately for NULL handles */ if ( ! handle ) @@ -828,10 +673,8 @@ const __attribute__ (( pure )) char * efi_handle_name ( EFI_HANDLE handle ) { DBG2 ( "<%d", i ); /* Try to open the applicable protocol */ - efirc = bs->OpenProtocol ( handle, type->protocol, &interface, - efi_image_handle, handle, - EFI_OPEN_PROTOCOL_GET_PROTOCOL ); - if ( efirc != 0 ) { + if ( ( rc = efi_open ( handle, type->protocol, + &interface ) ) != 0 ) { DBG2 ( ">" ); continue; } @@ -839,12 +682,7 @@ const __attribute__ (( pure )) char * efi_handle_name ( EFI_HANDLE handle ) { /* Try to get name from this protocol */ DBG2 ( "-" ); name = type->name ( interface ); - DBG2 ( "%c", ( name ? ( name[0] ? 'Y' : 'E' ) : 'N' ) ); - - /* Close protocol */ - bs->CloseProtocol ( handle, type->protocol, - efi_image_handle, handle ); - DBG2 ( ">" ); + DBG2 ( "%c>", ( name ? ( name[0] ? 'Y' : 'E' ) : 'N' ) ); /* Use this name, if possible */ if ( name && name[0] ) diff --git a/src/interface/efi/efi_download.c b/src/interface/efi/efi_download.c index 8d12bd57c..1c2f13573 100644 --- a/src/interface/efi/efi_download.c +++ b/src/interface/efi/efi_download.c @@ -17,6 +17,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER ); +FILE_SECBOOT ( PERMITTED ); #include <stdlib.h> #include <string.h> diff --git a/src/interface/efi/efi_driver.c b/src/interface/efi/efi_driver.c index fd9be5f51..cb07af401 100644 --- a/src/interface/efi/efi_driver.c +++ b/src/interface/efi/efi_driver.c @@ -18,6 +18,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER ); +FILE_SECBOOT ( PERMITTED ); #include <stddef.h> #include <stdlib.h> @@ -39,20 +40,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ -/* Disambiguate the various error causes */ -#define EINFO_EEFI_CONNECT \ - __einfo_uniqify ( EINFO_EPLATFORM, 0x01, \ - "Could not connect controllers" ) -#define EINFO_EEFI_CONNECT_PROHIBITED \ - __einfo_platformify ( EINFO_EEFI_CONNECT, \ - EFI_SECURITY_VIOLATION, \ - "Connecting controllers prohibited by " \ - "security policy" ) -#define EEFI_CONNECT_PROHIBITED \ - __einfo_error ( EINFO_EEFI_CONNECT_PROHIBITED ) -#define EEFI_CONNECT( efirc ) EPLATFORM ( EINFO_EEFI_CONNECT, efirc, \ - EEFI_CONNECT_PROHIBITED ) - static EFI_DRIVER_BINDING_PROTOCOL efi_driver_binding; /** List of controlled EFI devices */ @@ -68,45 +55,32 @@ static int efi_driver_disconnecting; * @ret efidev EFI device, or NULL on error */ struct efi_device * efidev_alloc ( EFI_HANDLE device ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_device *efidev = NULL; - union { - EFI_DEVICE_PATH_PROTOCOL *path; - void *interface; - } path; + EFI_DEVICE_PATH_PROTOCOL *path; EFI_DEVICE_PATH_PROTOCOL *path_end; size_t path_len; - EFI_STATUS efirc; int rc; /* Open device path */ - if ( ( efirc = bs->OpenProtocol ( device, - &efi_device_path_protocol_guid, - &path.interface, efi_image_handle, - device, - EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){ - rc = -EEFI ( efirc ); + if ( ( rc = efi_open ( device, &efi_device_path_protocol_guid, + &path ) ) != 0 ) { DBGC ( device, "EFIDRV %s could not open device path: %s\n", efi_handle_name ( device ), strerror ( rc ) ); - goto err_open_path; + return NULL; } - path_len = ( efi_path_len ( path.path ) + sizeof ( *path_end ) ); + path_len = ( efi_path_len ( path ) + sizeof ( *path_end ) ); /* Allocate and initialise structure */ efidev = zalloc ( sizeof ( *efidev ) + path_len ); if ( ! efidev ) - goto err_alloc; + return NULL; efidev->device = device; efidev->dev.desc.bus_type = BUS_TYPE_EFI; efidev->path = ( ( ( void * ) efidev ) + sizeof ( *efidev ) ); - memcpy ( efidev->path, path.path, path_len ); + memcpy ( efidev->path, path, path_len ); INIT_LIST_HEAD ( &efidev->dev.children ); list_add ( &efidev->dev.siblings, &efi_devices ); - err_alloc: - bs->CloseProtocol ( device, &efi_device_path_protocol_guid, - efi_image_handle, device ); - err_open_path: return efidev; } @@ -180,6 +154,7 @@ static EFI_STATUS EFIAPI efi_driver_supported ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, EFI_HANDLE device, EFI_DEVICE_PATH_PROTOCOL *child ) { struct efi_driver *efidrv; + unsigned int count; int rc; DBGCP ( device, "EFIDRV %s DRIVER_SUPPORTED", @@ -195,18 +170,24 @@ efi_driver_supported ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, return EFI_ALREADY_STARTED; } - /* Look for a driver claiming to support this device */ + /* Count drivers claiming to support this device */ + count = 0; for_each_table_entry ( efidrv, EFI_DRIVERS ) { if ( ( rc = efidrv->supported ( device ) ) == 0 ) { DBGC ( device, "EFIDRV %s has driver \"%s\"\n", efi_handle_name ( device ), efidrv->name ); - return 0; + count++; } } - DBGCP ( device, "EFIDRV %s has no driver\n", - efi_handle_name ( device ) ); - return EFI_UNSUPPORTED; + /* Check that we have at least one driver */ + if ( ! count ) { + DBGCP ( device, "EFIDRV %s has no driver\n", + efi_handle_name ( device ) ); + return EFI_UNSUPPORTED; + } + + return 0; } /** @@ -320,7 +301,7 @@ efi_driver_stop ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused, if ( ! efidev ) { DBGCP ( device, "EFIDRV %s is not started\n", efi_handle_name ( device ) ); - return EFI_DEVICE_ERROR; + return 0; } /* Raise TPL */ @@ -375,24 +356,17 @@ static EFI_STATUS EFIAPI efi_driver_controller_name ( EFI_COMPONENT_NAME2_PROTOCOL *wtf __unused, EFI_HANDLE device, EFI_HANDLE child, CHAR8 *language, CHAR16 **controller_name ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - union { - EFI_COMPONENT_NAME2_PROTOCOL *name2; - void *interface; - } name2; - EFI_STATUS efirc; + EFI_COMPONENT_NAME2_PROTOCOL *name2; + int rc; /* Delegate to the EFI_COMPONENT_NAME2_PROTOCOL instance * installed on child handle, if present. */ if ( ( child != NULL ) && - ( ( efirc = bs->OpenProtocol ( - child, &efi_component_name2_protocol_guid, - &name2.interface, NULL, NULL, - EFI_OPEN_PROTOCOL_GET_PROTOCOL ) ) == 0 ) ) { - return name2.name2->GetControllerName ( name2.name2, device, - child, language, - controller_name ); + ( ( rc = efi_open ( child, &efi_component_name2_protocol_guid, + &name2 ) ) == 0 ) ) { + return name2->GetControllerName ( name2, device, child, + language, controller_name ); } /* Otherwise, let EFI use the default Device Path Name */ @@ -455,15 +429,76 @@ void efi_driver_uninstall ( void ) { } /** + * Try to disconnect an existing EFI driver + * + * @v device EFI device + * @v protocol Protocol GUID + * @ret rc Return status code + */ +int efi_driver_exclude ( EFI_HANDLE device, EFI_GUID *protocol ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *openers; + EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *opener; + EFI_HANDLE driver; + UINTN count; + unsigned int i; + EFI_STATUS efirc; + int rc; + + /* Retrieve list of openers */ + if ( ( efirc = bs->OpenProtocolInformation ( device, protocol, &openers, + &count ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( device, "EFIDRV %s could not list %s openers: %s\n", + efi_handle_name ( device ), efi_guid_ntoa ( protocol ), + strerror ( rc ) ); + goto err_list; + } + + /* Identify BY_DRIVER opener */ + driver = NULL; + for ( i = 0 ; i < count ; i++ ) { + opener = &openers[i]; + if ( opener->Attributes & EFI_OPEN_PROTOCOL_BY_DRIVER ) { + driver = opener->AgentHandle; + break; + } + } + + /* Try to disconnect driver */ + DBGC ( device, "EFIDRV %s disconnecting %s drivers\n", + efi_handle_name ( device ), efi_guid_ntoa ( protocol ) ); + if ( driver ) { + DBGC ( device, "EFIDRV %s disconnecting %s driver ", + efi_handle_name ( device ), efi_guid_ntoa ( protocol ) ); + DBGC ( device, "%s\n", efi_handle_name ( driver ) ); + if ( ( rc = efi_disconnect ( device, driver ) ) != 0 ) { + DBGC ( device, "EFIDRV %s could not disconnect ", + efi_handle_name ( device ) ); + DBGC ( device, "%s: %s\n", + efi_handle_name ( driver ), strerror ( rc ) ); + goto err_disconnect; + } + } + + /* Success */ + rc = 0; + + err_disconnect: + bs->FreePool ( openers ); + err_list: + return rc; +} + +/** * Try to connect EFI driver * * @v device EFI device * @ret rc Return status code */ static int efi_driver_connect ( EFI_HANDLE device ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - EFI_HANDLE drivers[2] = - { efi_driver_binding.DriverBindingHandle, NULL }; + EFI_HANDLE driver = efi_driver_binding.DriverBindingHandle; + struct efi_driver *efidrv; EFI_STATUS efirc; int rc; @@ -481,13 +516,17 @@ static int efi_driver_connect ( EFI_HANDLE device ) { DBGC ( device, "EFIDRV %s disconnecting existing drivers\n", efi_handle_name ( device ) ); efi_driver_disconnecting = 1; - if ( ( efirc = bs->DisconnectController ( device, NULL, - NULL ) ) != 0 ) { - rc = -EEFI ( efirc ); - DBGC ( device, "EFIDRV %s could not disconnect existing " - "drivers: %s\n", efi_handle_name ( device ), - strerror ( rc ) ); - /* Ignore the error and attempt to connect our drivers */ + for_each_table_entry_reverse ( efidrv, EFI_DRIVERS ) { + if ( ! efidrv->exclude ) + continue; + if ( ( rc = efidrv->supported ( device ) ) != 0 ) + continue; + if ( ( rc = efidrv->exclude ( device ) ) != 0 ) { + DBGC ( device, "EFIDRV %s could not disconnect " + "drivers: %s\n", efi_handle_name ( device ), + strerror ( rc ) ); + /* Ignore the error and attempt to connect anyway */ + } } efi_driver_disconnecting = 0; DBGC2 ( device, "EFIDRV %s after disconnecting:\n", @@ -497,16 +536,14 @@ static int efi_driver_connect ( EFI_HANDLE device ) { /* Connect our driver */ DBGC ( device, "EFIDRV %s connecting new drivers\n", efi_handle_name ( device ) ); - if ( ( efirc = bs->ConnectController ( device, drivers, NULL, - TRUE ) ) != 0 ) { - rc = -EEFI_CONNECT ( efirc ); + if ( ( rc = efi_connect ( device, driver ) ) != 0 ) { DBGC ( device, "EFIDRV %s could not connect new drivers: " "%s\n", efi_handle_name ( device ), strerror ( rc ) ); DBGC ( device, "EFIDRV %s connecting driver directly\n", efi_handle_name ( device ) ); if ( ( efirc = efi_driver_start ( &efi_driver_binding, device, NULL ) ) != 0 ) { - rc = -EEFI_CONNECT ( efirc ); + rc = -EEFI ( efirc ); DBGC ( device, "EFIDRV %s could not connect driver " "directly: %s\n", efi_handle_name ( device ), strerror ( rc ) ); @@ -527,14 +564,13 @@ static int efi_driver_connect ( EFI_HANDLE device ) { * @ret rc Return status code */ static int efi_driver_disconnect ( EFI_HANDLE device ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_HANDLE driver = efi_driver_binding.DriverBindingHandle; /* Disconnect our driver */ efi_driver_disconnecting = 1; - bs->DisconnectController ( device, - efi_driver_binding.DriverBindingHandle, - NULL ); + efi_disconnect ( device, driver ); efi_driver_disconnecting = 0; + return 0; } @@ -545,10 +581,9 @@ static int efi_driver_disconnect ( EFI_HANDLE device ) { * @ret rc Return status code */ static int efi_driver_reconnect ( EFI_HANDLE device ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; /* Reconnect any available driver */ - bs->ConnectController ( device, NULL, NULL, TRUE ); + efi_connect ( device, NULL ); return 0; } diff --git a/src/interface/efi/efi_entropy.c b/src/interface/efi/efi_entropy.c index cda1c3640..b6bd12ccc 100644 --- a/src/interface/efi/efi_entropy.c +++ b/src/interface/efi/efi_entropy.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <errno.h> #include <ipxe/entropy.h> diff --git a/src/interface/efi/efi_fbcon.c b/src/interface/efi/efi_fbcon.c index e3c4d001d..afa3da5e2 100644 --- a/src/interface/efi/efi_fbcon.c +++ b/src/interface/efi/efi_fbcon.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** * @file @@ -42,6 +43,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/ansicol.h> #include <ipxe/fbcon.h> #include <ipxe/console.h> +#include <ipxe/uaccess.h> #include <ipxe/umalloc.h> #include <ipxe/rotate.h> #include <config/console.h> @@ -91,7 +93,7 @@ struct efifb { /** Font definition */ struct fbcon_font font; /** Character glyph cache */ - userptr_t glyphs; + uint8_t *glyphs; /** Dynamic characters in cache */ unsigned int dynamic[EFIFB_DYNAMIC]; /** Next dynamic character cache entry to evict */ @@ -117,14 +119,14 @@ static int efifb_draw ( unsigned int character, unsigned int index, unsigned int height; unsigned int x; unsigned int y; + uint8_t *glyph; uint8_t bitmask; - size_t offset; EFI_STATUS efirc; int rc; /* Clear existing glyph */ - offset = ( index * efifb.font.height ); - memset_user ( efifb.glyphs, offset, 0, efifb.font.height ); + glyph = &efifb.glyphs[ index * efifb.font.height ]; + memset ( glyph, 0, efifb.font.height ); /* Get glyph */ blt = NULL; @@ -157,8 +159,7 @@ static int efifb_draw ( unsigned int character, unsigned int index, pixel++; } bitmask ^= toggle; - copy_to_user ( efifb.glyphs, offset++, &bitmask, - sizeof ( bitmask ) ); + *(glyph++) = bitmask; } /* Free glyph */ @@ -223,11 +224,10 @@ static unsigned int efifb_dynamic ( unsigned int character ) { * Get character glyph * * @v character Unicode character - * @v glyph Character glyph to fill in + * @ret glyph Character glyph to fill in */ -static void efifb_glyph ( unsigned int character, uint8_t *glyph ) { +static const uint8_t * efifb_glyph ( unsigned int character ) { unsigned int index; - size_t offset; /* Identify glyph */ if ( character < EFIFB_ASCII ) { @@ -241,9 +241,8 @@ static void efifb_glyph ( unsigned int character, uint8_t *glyph ) { index = efifb_dynamic ( character ); } - /* Copy cached glyph */ - offset = ( index * efifb.font.height ); - copy_from_user ( glyph, efifb.glyphs, offset, efifb.font.height ); + /* Return cached glyph */ + return &efifb.glyphs[ index * efifb.font.height ]; } /** @@ -296,7 +295,7 @@ static int efifb_glyphs ( void ) { rc = -ENOMEM; goto err_alloc; } - memset_user ( efifb.glyphs, 0, 0, len ); + memset ( efifb.glyphs, 0, len ); /* Get font data */ for ( character = 0 ; character < EFIFB_ASCII ; character++ ) { @@ -582,10 +581,10 @@ static int efifb_init ( struct console_configuration *config ) { efifb.start = efifb.gop->Mode->FrameBufferBase; DBGC ( &efifb, "EFIFB using mode %d (%dx%d %dbpp at %#08lx)\n", mode, efifb.pixel.width, efifb.pixel.height, bpp, efifb.start ); -} + } /* Initialise frame buffer console */ - if ( ( rc = fbcon_init ( &efifb.fbcon, phys_to_user ( efifb.start ), + if ( ( rc = fbcon_init ( &efifb.fbcon, phys_to_virt ( efifb.start ), &efifb.pixel, &efifb.map, &efifb.font, config ) ) != 0 ) goto err_fbcon_init; diff --git a/src/interface/efi/efi_fdt.c b/src/interface/efi/efi_fdt.c index cd3f109df..cd8580fcb 100644 --- a/src/interface/efi/efi_fdt.c +++ b/src/interface/efi/efi_fdt.c @@ -22,11 +22,16 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <string.h> +#include <byteswap.h> #include <ipxe/fdt.h> -#include <ipxe/efi/efi.h> #include <ipxe/init.h> +#include <ipxe/efi/efi.h> +#include <ipxe/efi/efi_table.h> +#include <ipxe/efi/efi_fdt.h> +#include <ipxe/efi/Guid/Fdt.h> /** @file * @@ -34,19 +39,17 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ -#define DEVICE_TREE_TABLE_GUID \ - { 0xb1b621d5, 0xf19c, 0x41a5, \ - { 0x83, 0x0b, 0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0 } } - /** EFI Flattened Device Tree configuration table */ static struct fdt_header *efi_fdt; -EFI_USE_TABLE ( DEVICE_TREE_TABLE, &efi_fdt, 0 ); +EFI_USE_TABLE ( FDT_TABLE, &efi_fdt, 0 ); /** * Initialise EFI Flattened Device Tree * */ static void efi_fdt_init ( void ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_STATUS efirc; int rc; /* Do nothing if no configuration table is present */ @@ -56,15 +59,105 @@ static void efi_fdt_init ( void ) { } DBGC ( &efi_fdt, "EFIFDT configuration table at %p\n", efi_fdt ); - /* Register device tree */ - if ( ( rc = register_fdt ( efi_fdt ) ) != 0 ) { - DBGC ( &efi_fdt, "EFIFDT could not register: %s\n", + /* Parse as system device tree */ + if ( ( rc = fdt_parse ( &sysfdt, efi_fdt, -1UL ) ) != 0 ) { + DBGC ( &efi_fdt, "EFIFDT could not parse: %s\n", strerror ( rc ) ); return; } + + /* Create copy, since table may be removed at any time */ + if ( ( efirc = bs->AllocatePool ( EfiBootServicesData, sysfdt.len, + &sysfdt.raw ) ) != 0 ) { + DBGC ( &efi_fdt, "EFIFDT could not create copy\n" ); + sysfdt.len = 0; + return; + } + memcpy ( sysfdt.raw, efi_fdt, sysfdt.len ); } /** EFI Flattened Device Tree initialisation function */ struct init_fn efi_fdt_init_fn __init_fn ( INIT_EARLY ) = { + .name = "efifdt", .initialise = efi_fdt_init, }; + +/** + * Determine length of EFI Flattened Device Tree + * + * @v data Configuration table data (presumed valid) + * @ret len Length of table + */ +static size_t efi_fdt_len ( const void *data ) { + const struct fdt_header *hdr = data; + + return be32_to_cpu ( hdr->totalsize ); +} + +/** EFI Flattened Device Tree table type */ +static struct efi_table efi_fdt_table = { + .guid = &efi_fdt_table_guid, + .len = efi_fdt_len, +}; + +/** EFI Flattened Device Tree table backup */ +static void *efi_fdt_backup; + +/** EFI Flattened Device Tree installed table */ +static struct fdt_header *efi_fdt_installed; + +/** + * Install EFI Flattened Device Tree table + * + * @v cmdline Command line, or NULL + * @ret rc Return status code + */ +int efi_fdt_install ( const char *cmdline ) { + int rc; + + /* Create device tree */ + if ( ( rc = fdt_create ( &efi_fdt_installed, cmdline, 0, 0 ) ) != 0 ) { + DBGC ( &efi_fdt, "EFI_FDT could not install: %s\n", + strerror ( rc ) ); + goto err_create; + } + + /* Install table */ + if ( ( rc = efi_install_table ( &efi_fdt_table, efi_fdt_installed, + &efi_fdt_backup ) ) != 0 ) { + DBGC ( &efi_fdt, "EFIFDT could not install: %s\n", + strerror ( rc ) ); + goto err_install; + } + + return 0; + + efi_uninstall_table ( &efi_fdt_table, &efi_fdt_backup ); + err_install: + fdt_remove ( efi_fdt_installed ); + err_create: + return rc; +} + +/** + * Uninstall EFI Flattened Device Tree table + * + * @ret rc Return status code + */ +int efi_fdt_uninstall ( void ) { + int rc; + + /* Uninstall table */ + if ( ( rc = efi_uninstall_table ( &efi_fdt_table, + &efi_fdt_backup ) ) != 0 ) { + DBGC ( &efi_fdt, "EFIFDT could not %sinstall: %s\n", + ( efi_fdt_backup ? "re" : "un" ), strerror ( rc ) ); + /* Leak memory: there is nothing else we can do */ + return rc; + } + + /* Remove table */ + fdt_remove ( efi_fdt_installed ); + + return 0; +} diff --git a/src/interface/efi/efi_file.c b/src/interface/efi/efi_file.c index 2ae3a0cb4..2f49b1a99 100644 --- a/src/interface/efi/efi_file.c +++ b/src/interface/efi/efi_file.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** * @file @@ -39,6 +40,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <wchar.h> #include <ipxe/image.h> #include <ipxe/cpio.h> +#include <ipxe/initrd.h> #include <ipxe/efi/efi.h> #include <ipxe/efi/Protocol/SimpleFileSystem.h> #include <ipxe/efi/Protocol/BlockIo.h> @@ -177,12 +179,12 @@ static size_t efi_file_len ( struct efi_file *file ) { * Read chunk of EFI file * * @v reader EFI file reader - * @v data Input data, or UNULL to zero-fill + * @v data Input data, or NULL to zero-fill * @v len Length of input data * @ret len Length of output data */ static size_t efi_file_read_chunk ( struct efi_file_reader *reader, - userptr_t data, size_t len ) { + const void *data, size_t len ) { struct efi_file *file = reader->file; size_t offset; @@ -203,7 +205,7 @@ static size_t efi_file_read_chunk ( struct efi_file_reader *reader, /* Copy or zero output data */ if ( data ) { - copy_from_user ( reader->data, data, offset, len ); + memcpy ( reader->data, ( data + offset ), len ); } else { memset ( reader->data, 0, len ); } @@ -245,6 +247,7 @@ static size_t efi_file_read_initrd ( struct efi_file_reader *reader ) { size_t cpio_len; size_t name_len; size_t len; + unsigned int i; /* Read from file */ len = 0; @@ -261,24 +264,22 @@ static size_t efi_file_read_initrd ( struct efi_file_reader *reader ) { efi_file_name ( file ), reader->pos, ( reader->pos + pad_len ) ); } - len += efi_file_read_chunk ( reader, UNULL, pad_len ); - - /* Read CPIO header, if applicable */ - cpio_len = cpio_header ( image, &cpio ); - if ( cpio_len ) { - name = cpio_name ( image ); - name_len = cpio_name_len ( image ); - pad_len = ( cpio_len - sizeof ( cpio ) - name_len ); + len += efi_file_read_chunk ( reader, NULL, pad_len ); + + /* Read CPIO header(s), if applicable */ + name = cpio_name ( image ); + for ( i = 0 ; ( cpio_len = cpio_header ( image, i, &cpio ) ); + i++ ) { + name_len = ( cpio_len - sizeof ( cpio ) ); + pad_len = cpio_pad_len ( cpio_len ); DBGC ( file, "EFIFILE %s [%#08zx,%#08zx) %s header\n", efi_file_name ( file ), reader->pos, - ( reader->pos + cpio_len ), image->name ); - len += efi_file_read_chunk ( reader, - virt_to_user ( &cpio ), + ( reader->pos + cpio_len + pad_len ), + image->name ); + len += efi_file_read_chunk ( reader, &cpio, sizeof ( cpio ) ); - len += efi_file_read_chunk ( reader, - virt_to_user ( name ), - name_len ); - len += efi_file_read_chunk ( reader, UNULL, pad_len ); + len += efi_file_read_chunk ( reader, name, name_len ); + len += efi_file_read_chunk ( reader, NULL, pad_len ); } /* Read file data */ @@ -386,8 +387,12 @@ efi_file_open ( EFI_FILE_PROTOCOL *this, EFI_FILE_PROTOCOL **new, name++; } + /* Strip redundant path separator characters */ + while ( ( *name == '\\' ) || ( *name == '.' ) ) + name++; + /* Allow root directory itself to be opened */ - if ( ( name[0] == '\0' ) || ( name[0] == '.' ) ) + if ( ! *name ) return efi_file_open_fixed ( &efi_file_root, wname, new ); /* Fail unless opening from the root */ @@ -976,9 +981,9 @@ static EFI_DISK_IO_PROTOCOL efi_disk_io_protocol = { */ static int efi_file_path_claim ( struct efi_file_path *file ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_DEVICE_PATH_PROTOCOL *old; EFI_DEVICE_PATH_PROTOCOL *end; EFI_HANDLE handle; - VOID *old; EFI_STATUS efirc; int rc; @@ -994,10 +999,8 @@ static int efi_file_path_claim ( struct efi_file_path *file ) { } /* Locate device path protocol on this handle */ - if ( ( ( efirc = bs->HandleProtocol ( handle, - &efi_device_path_protocol_guid, - &old ) ) != 0 ) ) { - rc = -EEFI ( efirc ); + if ( ( rc = efi_open ( handle, &efi_device_path_protocol_guid, + &old ) != 0 ) ) { DBGC ( file, "EFIFILE %s could not locate %s: %s\n", efi_file_name ( &file->file ), efi_devpath_text ( file->path ), strerror ( rc ) ); @@ -1112,10 +1115,7 @@ static void efi_file_path_uninstall ( struct efi_file_path *file ) { */ int efi_file_install ( EFI_HANDLE handle ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - union { - EFI_DISK_IO_PROTOCOL *diskio; - void *interface; - } diskio; + EFI_DISK_IO_PROTOCOL *diskio; struct image *image; EFI_STATUS efirc; int rc; @@ -1163,17 +1163,14 @@ int efi_file_install ( EFI_HANDLE handle ) { * of calls to our DRIVER_STOP method when starting the EFI * shell. I have no idea why this is. */ - if ( ( efirc = bs->OpenProtocol ( handle, &efi_disk_io_protocol_guid, - &diskio.interface, efi_image_handle, - handle, - EFI_OPEN_PROTOCOL_BY_DRIVER ) ) != 0){ - rc = -EEFI ( efirc ); + if ( ( rc = efi_open_by_driver ( handle, &efi_disk_io_protocol_guid, + &diskio ) ) != 0 ) { DBGC ( handle, "Could not open disk I/O protocol: %s\n", strerror ( rc ) ); DBGC_EFI_OPENERS ( handle, handle, &efi_disk_io_protocol_guid ); goto err_open; } - assert ( diskio.diskio == &efi_disk_io_protocol ); + assert ( diskio == &efi_disk_io_protocol ); /* Claim Linux initrd fixed device path */ if ( ( rc = efi_file_path_claim ( &efi_file_initrd ) ) != 0 ) @@ -1193,8 +1190,7 @@ int efi_file_install ( EFI_HANDLE handle ) { efi_file_path_uninstall ( &efi_file_initrd ); err_initrd_install: err_initrd_claim: - bs->CloseProtocol ( handle, &efi_disk_io_protocol_guid, - efi_image_handle, handle ); + efi_close_by_driver ( handle, &efi_disk_io_protocol_guid ); err_open: bs->UninstallMultipleProtocolInterfaces ( handle, @@ -1222,8 +1218,7 @@ void efi_file_uninstall ( EFI_HANDLE handle ) { efi_file_path_uninstall ( &efi_file_initrd ); /* Close our own disk I/O protocol */ - bs->CloseProtocol ( handle, &efi_disk_io_protocol_guid, - efi_image_handle, handle ); + efi_close_by_driver ( handle, &efi_disk_io_protocol_guid ); /* We must install the file system protocol first, since * otherwise the EDK2 code will attempt to helpfully uninstall diff --git a/src/interface/efi/efi_guid.c b/src/interface/efi/efi_guid.c index f841448f1..c989aebfe 100644 --- a/src/interface/efi/efi_guid.c +++ b/src/interface/efi/efi_guid.c @@ -22,10 +22,14 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); +#include <string.h> +#include <ipxe/uuid.h> #include <ipxe/efi/efi.h> #include <ipxe/efi/Protocol/AbsolutePointer.h> #include <ipxe/efi/Protocol/AcpiTable.h> +#include <ipxe/efi/Protocol/AdapterInformation.h> #include <ipxe/efi/Protocol/AppleNetBoot.h> #include <ipxe/efi/Protocol/Arp.h> #include <ipxe/efi/Protocol/BlockIo.h> @@ -42,6 +46,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/efi/Protocol/Dns4.h> #include <ipxe/efi/Protocol/Dns6.h> #include <ipxe/efi/Protocol/DriverBinding.h> +#include <ipxe/efi/Protocol/EapConfiguration.h> #include <ipxe/efi/Protocol/GraphicsOutput.h> #include <ipxe/efi/Protocol/HiiConfigAccess.h> #include <ipxe/efi/Protocol/HiiFont.h> @@ -61,6 +66,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/efi/Protocol/PciIo.h> #include <ipxe/efi/Protocol/PciRootBridgeIo.h> #include <ipxe/efi/Protocol/PxeBaseCode.h> +#include <ipxe/efi/Protocol/Rng.h> #include <ipxe/efi/Protocol/SerialIo.h> #include <ipxe/efi/Protocol/ShimLock.h> #include <ipxe/efi/Protocol/SimpleFileSystem.h> @@ -69,7 +75,10 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/efi/Protocol/SimpleTextIn.h> #include <ipxe/efi/Protocol/SimpleTextInEx.h> #include <ipxe/efi/Protocol/SimpleTextOut.h> +#include <ipxe/efi/Protocol/StorageSecurityCommand.h> +#include <ipxe/efi/Protocol/Supplicant.h> #include <ipxe/efi/Protocol/TcgService.h> +#include <ipxe/efi/Protocol/Tcg2Protocol.h> #include <ipxe/efi/Protocol/Tcp4.h> #include <ipxe/efi/Protocol/Tcp6.h> #include <ipxe/efi/Protocol/Udp4.h> @@ -80,8 +89,16 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/efi/Protocol/Usb2HostController.h> #include <ipxe/efi/Protocol/UsbIo.h> #include <ipxe/efi/Protocol/VlanConfig.h> +#include <ipxe/efi/Protocol/WiFi2.h> +#include <ipxe/efi/Guid/Acpi.h> +#include <ipxe/efi/Guid/Fdt.h> #include <ipxe/efi/Guid/FileInfo.h> #include <ipxe/efi/Guid/FileSystemInfo.h> +#include <ipxe/efi/Guid/GlobalVariable.h> +#include <ipxe/efi/Guid/ImageAuthentication.h> +#include <ipxe/efi/Guid/MicrosoftVendor.h> +#include <ipxe/efi/Guid/SmBios.h> +#include <ipxe/efi/Guid/TlsAuthentication.h> /** @file * @@ -94,6 +111,11 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); { 0x607f766c, 0x7455, 0x42be, \ { 0x93, 0x0b, 0xe4, 0xd7, 0x6d, 0xb2, 0x72, 0x0f } } +/* Unicode collation protocol GUID was deleted from EDK2 headers */ +#define EFI_UNICODE_COLLATION_PROTOCOL_GUID \ + { 0x1d85cd7f, 0xf43d, 0x11d2, \ + { 0x9a, 0xc, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d } } + /** Absolute pointer protocol GUID */ EFI_GUID efi_absolute_pointer_protocol_guid = EFI_ABSOLUTE_POINTER_PROTOCOL_GUID; @@ -102,6 +124,10 @@ EFI_GUID efi_absolute_pointer_protocol_guid EFI_GUID efi_acpi_table_protocol_guid = EFI_ACPI_TABLE_PROTOCOL_GUID; +/** Adapter information protocol GUID */ +EFI_GUID efi_adapter_information_protocol_guid + = EFI_ADAPTER_INFORMATION_PROTOCOL_GUID; + /** Apple NetBoot protocol GUID */ EFI_GUID efi_apple_net_boot_protocol_guid = EFI_APPLE_NET_BOOT_PROTOCOL_GUID; @@ -182,6 +208,10 @@ EFI_GUID efi_dns6_service_binding_protocol_guid EFI_GUID efi_driver_binding_protocol_guid = EFI_DRIVER_BINDING_PROTOCOL_GUID; +/** EAP configuration protocol GUID */ +EFI_GUID efi_eap_configuration_protocol_guid + = EFI_EAP_CONFIGURATION_PROTOCOL_GUID; + /** Graphics output protocol GUID */ EFI_GUID efi_graphics_output_protocol_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; @@ -290,6 +320,10 @@ EFI_GUID efi_pci_root_bridge_io_protocol_guid EFI_GUID efi_pxe_base_code_protocol_guid = EFI_PXE_BASE_CODE_PROTOCOL_GUID; +/** Random number generator protocol GUID */ +EFI_GUID efi_rng_protocol_guid + = EFI_RNG_PROTOCOL_GUID; + /** Serial I/O protocol GUID */ EFI_GUID efi_serial_io_protocol_guid = EFI_SERIAL_IO_PROTOCOL_GUID; @@ -322,10 +356,22 @@ EFI_GUID efi_simple_text_input_ex_protocol_guid EFI_GUID efi_simple_text_output_protocol_guid = EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_GUID; +/** Storage security protocol GUID */ +EFI_GUID efi_storage_security_command_protocol_guid + = EFI_STORAGE_SECURITY_COMMAND_PROTOCOL_GUID; + +/** Supplicant protocol GUID */ +EFI_GUID efi_supplicant_protocol_guid + = EFI_SUPPLICANT_PROTOCOL_GUID; + /** TCG protocol GUID */ EFI_GUID efi_tcg_protocol_guid = EFI_TCG_PROTOCOL_GUID; +/** TCG2 protocol GUID */ +EFI_GUID efi_tcg2_protocol_guid + = EFI_TCG2_PROTOCOL_GUID; + /** TCPv4 protocol GUID */ EFI_GUID efi_tcp4_protocol_guid = EFI_TCP4_PROTOCOL_GUID; @@ -386,8 +432,319 @@ EFI_GUID efi_usb_io_protocol_guid EFI_GUID efi_vlan_config_protocol_guid = EFI_VLAN_CONFIG_PROTOCOL_GUID; +/** WiFi 2 protocol GUID */ +EFI_GUID efi_wifi2_protocol_guid + = EFI_WIRELESS_MAC_CONNECTION_II_PROTOCOL_GUID; + +/** ACPI 1.0 table GUID */ +EFI_GUID efi_acpi_10_table_guid + = ACPI_10_TABLE_GUID; + +/** ACPI 2.0 table GUID */ +EFI_GUID efi_acpi_20_table_guid + = EFI_ACPI_20_TABLE_GUID; + +/** FDT table GUID */ +EFI_GUID efi_fdt_table_guid + = FDT_TABLE_GUID; + +/** SMBIOS table GUID */ +EFI_GUID efi_smbios_table_guid + = SMBIOS_TABLE_GUID; + +/** SMBIOS3 table GUID */ +EFI_GUID efi_smbios3_table_guid + = SMBIOS3_TABLE_GUID; + +/** X.509 certificate GUID */ +EFI_GUID efi_cert_x509_guid = EFI_CERT_X509_GUID; + /** File information GUID */ EFI_GUID efi_file_info_id = EFI_FILE_INFO_ID; /** File system information GUID */ EFI_GUID efi_file_system_info_id = EFI_FILE_SYSTEM_INFO_ID; + +/** Global variable GUID */ +EFI_GUID efi_global_variable = EFI_GLOBAL_VARIABLE; + +/** Image security database GUID */ +EFI_GUID efi_image_security_database_guid = EFI_IMAGE_SECURITY_DATABASE_GUID; + +/** Microsoft vendor GUID */ +EFI_GUID efi_microsoft_vendor_guid = MICROSOFT_VENDOR_GUID; + +/** TLS CA certificate variable GUID */ +EFI_GUID efi_tls_ca_certificate_guid = EFI_TLS_CA_CERTIFICATE_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, + { 0xa8, 0xf4, 0x08, 0x51, 0x9b, 0xc4, 0x39, 0xdf } +}; + +/** WiFiConnectionMgrDxe module GUID */ +static EFI_GUID efi_wifi_connection_mgr_dxe_guid = { + 0x99b7c019, 0x4789, 0x4829, + { 0xa7, 0xbd, 0x0d, 0x4b, 0xaa, 0x62, 0x28, 0x72 } +}; + +/** A well-known GUID */ +struct efi_well_known_guid { + /** GUID */ + EFI_GUID *guid; + /** Name */ + const char *name; +}; + +/** Well-known GUIDs */ +static struct efi_well_known_guid efi_well_known_guids[] = { + { &efi_absolute_pointer_protocol_guid, + "AbsolutePointer" }, + { &efi_acpi_10_table_guid, + "Acpi10" }, + { &efi_acpi_20_table_guid, + "Acpi20" }, + { &efi_acpi_table_protocol_guid, + "AcpiTable" }, + { &efi_adapter_information_protocol_guid, + "AdapterInfo" }, + { &efi_apple_net_boot_protocol_guid, + "AppleNetBoot" }, + { &efi_arp_protocol_guid, + "Arp" }, + { &efi_arp_service_binding_protocol_guid, + "ArpSb" }, + { &efi_block_io_protocol_guid, + "BlockIo" }, + { &efi_block_io2_protocol_guid, + "BlockIo2" }, + { &efi_bus_specific_driver_override_protocol_guid, + "BusSpecificDriverOverride" }, + { &efi_cert_x509_guid, + "CertX509" }, + { &efi_component_name_protocol_guid, + "ComponentName" }, + { &efi_component_name2_protocol_guid, + "ComponentName2" }, + { &efi_console_control_protocol_guid, + "ConsoleControl" }, + { &efi_device_path_protocol_guid, + "DevicePath" }, + { &efi_driver_binding_protocol_guid, + "DriverBinding" }, + { &efi_dhcp4_protocol_guid, + "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_eap_configuration_protocol_guid, + "EapConfig" }, + { &efi_fdt_table_guid, + "Fdt" }, + { &efi_global_variable, + "GlobalVar" }, + { &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_image_security_database_guid, + "ImageSecDb" }, + { &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, + "LoadFile" }, + { &efi_load_file2_protocol_guid, + "LoadFile2" }, + { &efi_loaded_image_protocol_guid, + "LoadedImage" }, + { &efi_loaded_image_device_path_protocol_guid, + "LoadedImageDevicePath"}, + { &efi_managed_network_protocol_guid, + "ManagedNetwork" }, + { &efi_managed_network_service_binding_protocol_guid, + "ManagedNetworkSb" }, + { &efi_microsoft_vendor_guid, + "Microsoft" }, + { &efi_mtftp4_protocol_guid, + "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, + "Nii31" }, + { &efi_pci_io_protocol_guid, + "PciIo" }, + { &efi_pci_root_bridge_io_protocol_guid, + "PciRootBridgeIo" }, + { &efi_pxe_base_code_protocol_guid, + "PxeBaseCode" }, + { &efi_rng_protocol_guid, + "Rng" }, + { &efi_serial_io_protocol_guid, + "SerialIo" }, + { &efi_shim_lock_protocol_guid, + "ShimLock" }, + { &efi_simple_file_system_protocol_guid, + "SimpleFileSystem" }, + { &efi_simple_network_protocol_guid, + "SimpleNetwork" }, + { &efi_simple_pointer_protocol_guid, + "SimplePointer" }, + { &efi_simple_text_input_protocol_guid, + "SimpleTextInput" }, + { &efi_simple_text_input_ex_protocol_guid, + "SimpleTextInputEx" }, + { &efi_simple_text_output_protocol_guid, + "SimpleTextOutput" }, + { &efi_smbios_table_guid, + "Smbios" }, + { &efi_smbios3_table_guid, + "Smbios3" }, + { &efi_storage_security_command_protocol_guid, + "StorageSecurityCommand" }, + { &efi_supplicant_protocol_guid, + "Supplicant" }, + { &efi_tcg_protocol_guid, + "Tcg" }, + { &efi_tcg2_protocol_guid, + "Tcg2" }, + { &efi_tcp4_protocol_guid, + "Tcp4" }, + { &efi_tcp4_service_binding_protocol_guid, + "Tcp4Sb" }, + { &efi_tcp6_protocol_guid, + "Tcp6" }, + { &efi_tcp6_service_binding_protocol_guid, + "Tcp6Sb" }, + { &efi_tls_ca_certificate_guid, + "TlsCaCert" }, + { &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, + "UnicodeCollation" }, + { &efi_usb_hc_protocol_guid, + "UsbHc" }, + { &efi_usb2_hc_protocol_guid, + "Usb2Hc" }, + { &efi_usb_io_protocol_guid, + "UsbIo" }, + { &efi_vlan_config_protocol_guid, + "VlanConfig" }, + { &efi_vlan_config_dxe_guid, + "VlanConfigDxe" }, + { &efi_wifi2_protocol_guid, + "Wifi2" }, + { &efi_wifi_connection_mgr_dxe_guid, + "WiFiConnectionMgrDxe" }, +}; + +/** + * Convert GUID to a printable string + * + * @v guid GUID + * @ret string Printable string + */ +const __attribute__ (( pure )) char * efi_guid_ntoa ( CONST EFI_GUID *guid ) { + union { + union uuid uuid; + EFI_GUID guid; + } u; + unsigned int i; + + /* Sanity check */ + if ( ! guid ) + return NULL; + + /* Check for a match against well-known GUIDs */ + for ( i = 0 ; i < ( sizeof ( efi_well_known_guids ) / + sizeof ( efi_well_known_guids[0] ) ) ; i++ ) { + if ( memcmp ( guid, efi_well_known_guids[i].guid, + sizeof ( *guid ) ) == 0 ) { + return efi_well_known_guids[i].name; + } + } + + /* Convert GUID to standard endianness */ + memcpy ( &u.guid, guid, sizeof ( u.guid ) ); + uuid_mangle ( &u.uuid ); + return uuid_ntoa ( &u.uuid ); +} diff --git a/src/interface/efi/efi_hii.c b/src/interface/efi/efi_hii.c index 506fc8869..fd65ae122 100644 --- a/src/interface/efi/efi_hii.c +++ b/src/interface/efi/efi_hii.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <stdlib.h> #include <stddef.h> @@ -147,13 +148,13 @@ void efi_ifr_end_op ( struct efi_ifr_builder *ifr ) { */ void efi_ifr_false_op ( struct efi_ifr_builder *ifr ) { size_t dispaddr = ifr->ops_len; - EFI_IFR_FALSE *false; + EFI_IFR_FALSE *op; /* Add opcode */ - false = efi_ifr_op ( ifr, EFI_IFR_FALSE_OP, sizeof ( *false ) ); + op = efi_ifr_op ( ifr, EFI_IFR_FALSE_OP, sizeof ( *op ) ); DBGC ( ifr, "IFR %p false\n", ifr ); - DBGC2_HDA ( ifr, dispaddr, false, sizeof ( *false ) ); + DBGC2_HDA ( ifr, dispaddr, op, sizeof ( *op ) ); } /** @@ -462,13 +463,13 @@ void efi_ifr_text_op ( struct efi_ifr_builder *ifr, unsigned int prompt_id, */ void efi_ifr_true_op ( struct efi_ifr_builder *ifr ) { size_t dispaddr = ifr->ops_len; - EFI_IFR_TRUE *true; + EFI_IFR_TRUE *op; /* Add opcode */ - true = efi_ifr_op ( ifr, EFI_IFR_TRUE_OP, sizeof ( *true ) ); + op = efi_ifr_op ( ifr, EFI_IFR_TRUE_OP, sizeof ( *op ) ); DBGC ( ifr, "IFR %p true\n", ifr ); - DBGC2_HDA ( ifr, dispaddr, true, sizeof ( *true ) ); + DBGC2_HDA ( ifr, dispaddr, op, sizeof ( *op ) ); } /** diff --git a/src/interface/efi/efi_init.c b/src/interface/efi/efi_init.c index d3c5042d7..ac62ea747 100644 --- a/src/interface/efi/efi_init.c +++ b/src/interface/efi/efi_init.c @@ -18,6 +18,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER ); +FILE_SECBOOT ( PERMITTED ); #include <string.h> #include <errno.h> @@ -25,6 +26,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <ipxe/init.h> #include <ipxe/rotate.h> #include <ipxe/efi/efi.h> +#include <ipxe/efi/efi_table.h> #include <ipxe/efi/efi_driver.h> #include <ipxe/efi/efi_path.h> #include <ipxe/efi/efi_cmdline.h> @@ -104,24 +106,6 @@ static EFIAPI void efi_shutdown_hook ( EFI_EVENT event __unused, } /** - * Look up EFI configuration table - * - * @v guid Configuration table GUID - * @ret table Configuration table, or NULL - */ -static void * efi_find_table ( EFI_GUID *guid ) { - unsigned int i; - - for ( i = 0 ; i < efi_systab->NumberOfTableEntries ; i++ ) { - if ( memcmp ( &efi_systab->ConfigurationTable[i].VendorGuid, - guid, sizeof ( *guid ) ) == 0 ) - return efi_systab->ConfigurationTable[i].VendorTable; - } - - return NULL; -} - -/** * Construct a stack cookie value * * @v handle Image handle @@ -173,8 +157,7 @@ EFI_STATUS efi_init ( EFI_HANDLE image_handle, EFI_BOOT_SERVICES *bs; struct efi_protocol *prot; struct efi_config_table *tab; - void *loaded_image; - void *device_path; + EFI_DEVICE_PATH_PROTOCOL *device_path; void *device_path_copy; size_t device_path_len; EFI_STATUS efirc; @@ -241,17 +224,19 @@ EFI_STATUS efi_init ( EFI_HANDLE image_handle, } } - /* Get loaded image protocol */ - if ( ( efirc = bs->OpenProtocol ( image_handle, - &efi_loaded_image_protocol_guid, - &loaded_image, image_handle, NULL, - EFI_OPEN_PROTOCOL_GET_PROTOCOL ) ) != 0 ) { - rc = -EEFI ( efirc ); + /* Get loaded image protocol + * + * We assume that our loaded image protocol will not be + * uninstalled while our image code is still running. + */ + if ( ( rc = efi_open_unsafe ( image_handle, + &efi_loaded_image_protocol_guid, + &efi_loaded_image ) ) != 0 ) { DBGC ( systab, "EFI could not get loaded image protocol: %s", strerror ( rc ) ); + efirc = EFIRC ( rc ); goto err_no_loaded_image; } - efi_loaded_image = loaded_image; DBGC ( systab, "EFI image base address %p\n", efi_loaded_image->ImageBase ); @@ -260,13 +245,12 @@ EFI_STATUS efi_init ( EFI_HANDLE image_handle, efi_cmdline_len = efi_loaded_image->LoadOptionsSize; /* Get loaded image's device handle's device path */ - if ( ( efirc = bs->OpenProtocol ( efi_loaded_image->DeviceHandle, - &efi_device_path_protocol_guid, - &device_path, image_handle, NULL, - EFI_OPEN_PROTOCOL_GET_PROTOCOL ) ) != 0 ) { - rc = -EEFI ( efirc ); + if ( ( rc = efi_open ( efi_loaded_image->DeviceHandle, + &efi_device_path_protocol_guid, + &device_path ) ) != 0 ) { DBGC ( systab, "EFI could not get loaded image's device path: " "%s", strerror ( rc ) ); + efirc = EFIRC ( rc ); goto err_no_device_path; } diff --git a/src/interface/efi/efi_local.c b/src/interface/efi/efi_local.c index b28814241..27c9e3eaf 100644 --- a/src/interface/efi/efi_local.c +++ b/src/interface/efi/efi_local.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <string.h> #include <strings.h> @@ -208,42 +209,27 @@ static int efi_local_check_volume_name ( struct efi_local *local, */ static int efi_local_open_root ( struct efi_local *local, EFI_HANDLE device, EFI_FILE_PROTOCOL **root ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - union { - void *interface; - EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *fs; - } u; + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *fs; EFI_STATUS efirc; int rc; /* Open 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 ); + if ( ( rc = efi_open ( device, &efi_simple_file_system_protocol_guid, + &fs ) ) != 0 ) { DBGC ( local, "LOCAL %p could not open filesystem on %s: %s\n", local, efi_handle_name ( device ), strerror ( rc ) ); - goto err_filesystem; + return rc; } /* Open root directory */ - if ( ( efirc = u.fs->OpenVolume ( u.fs, root ) ) != 0 ) { + if ( ( efirc = fs->OpenVolume ( fs, root ) ) != 0 ) { rc = -EEFI ( efirc ); DBGC ( local, "LOCAL %p could not open volume on %s: %s\n", local, efi_handle_name ( device ), strerror ( rc ) ); - goto err_volume; + return rc; } - /* Success */ - rc = 0; - - err_volume: - bs->CloseProtocol ( device, &efi_simple_file_system_protocol_guid, - efi_image_handle, device ); - err_filesystem: - return rc; + return 0; } /** diff --git a/src/interface/efi/efi_uaccess.c b/src/interface/efi/efi_nap.c index e058be66b..9d97dae6e 100644 --- a/src/interface/efi/efi_uaccess.c +++ b/src/interface/efi/efi_nap.c @@ -22,23 +22,37 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); -#include <ipxe/uaccess.h> +#include <ipxe/nap.h> #include <ipxe/efi/efi.h> /** @file * - * iPXE user access API for EFI + * iPXE CPU sleeping API for EFI * */ -PROVIDE_UACCESS_INLINE ( efi, phys_to_user ); -PROVIDE_UACCESS_INLINE ( efi, user_to_phys ); -PROVIDE_UACCESS_INLINE ( efi, virt_to_user ); -PROVIDE_UACCESS_INLINE ( efi, user_to_virt ); -PROVIDE_UACCESS_INLINE ( efi, userptr_add ); -PROVIDE_UACCESS_INLINE ( efi, memcpy_user ); -PROVIDE_UACCESS_INLINE ( efi, memmove_user ); -PROVIDE_UACCESS_INLINE ( efi, memset_user ); -PROVIDE_UACCESS_INLINE ( efi, strlen_user ); -PROVIDE_UACCESS_INLINE ( efi, memchr_user ); +/** + * Sleep until next interrupt + * + */ +static void efi_cpu_nap ( void ) { + /* + * I can't find any EFI API that allows us to put the CPU to + * sleep. The CpuSleep() function is defined in CpuLib.h, but + * isn't part of any exposed protocol so we have no way to + * call it. + * + * The EFI shell doesn't seem to bother sleeping the CPU; it + * just sits there idly burning power. + * + * If a shutdown is in progess, there may be nothing to + * generate an interrupt since the timer is disabled in the + * first step of ExitBootServices(). + */ + if ( ! efi_shutdown_in_progress ) + cpu_halt(); +} + +PROVIDE_NAP ( efi, cpu_nap, efi_cpu_nap ); diff --git a/src/interface/efi/efi_null.c b/src/interface/efi/efi_null.c index d0f0428cc..fb9ee1780 100644 --- a/src/interface/efi/efi_null.c +++ b/src/interface/efi/efi_null.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <string.h> #include <ipxe/efi/efi.h> diff --git a/src/interface/efi/efi_open.c b/src/interface/efi/efi_open.c new file mode 100644 index 000000000..679d1946e --- /dev/null +++ b/src/interface/efi/efi_open.c @@ -0,0 +1,360 @@ +/* + * Copyright (C) 2025 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_SECBOOT ( PERMITTED ); + +/** @file + * + * EFI protocol opening and closing + * + * The UEFI model for opening and closing protocols is broken by + * design and cannot be repaired. + * + * Calling OpenProtocol() to obtain a protocol interface pointer does + * not, in general, provide any guarantees about the lifetime of that + * pointer. It is theoretically possible that the pointer has already + * become invalid by the time that OpenProtocol() returns the pointer + * to its caller. (This can happen when a USB device is physically + * removed, for example.) + * + * Various UEFI design flaws make it occasionally necessary to hold on + * to a protocol interface pointer despite the total lack of + * guarantees that the pointer will remain valid. + * + * The UEFI driver model overloads the semantics of OpenProtocol() to + * accommodate the use cases of recording a driver attachment (which + * is modelled as opening a protocol with EFI_OPEN_PROTOCOL_BY_DRIVER + * attributes) and recording the existence of a related child + * controller (which is modelled as opening a protocol with + * EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER attributes). + * + * The parameters defined for CloseProtocol() are not sufficient to + * allow the implementation to precisely identify the matching call to + * OpenProtocol(). While the UEFI model appears to allow for matched + * open and close pairs, this is merely an illusion. Calling + * CloseProtocol() will delete *all* matching records in the protocol + * open information tables. + * + * Since the parameters defined for CloseProtocol() do not include the + * attributes passed to OpenProtocol(), this means that a matched + * open/close pair using EFI_OPEN_PROTOCOL_GET_PROTOCOL can + * inadvertently end up deleting the record that defines a driver + * attachment or the existence of a child controller. This in turn + * can cause some very unexpected side effects, such as allowing other + * UEFI drivers to start controlling hardware to which iPXE believes + * it has exclusive access. This rarely ends well. + * + * To prevent this kind of inadvertent deletion, we establish a + * convention for four different types of protocol opening: + * + * - ephemeral opens: always opened with ControllerHandle = NULL + * + * - unsafe opens: always opened with ControllerHandle = AgentHandle + * + * - by-driver opens: always opened with ControllerHandle = Handle + * + * - by-child opens: always opened with ControllerHandle != Handle + * + * This convention ensures that the four types of open never overlap + * within the set of parameters defined for CloseProtocol(), and so a + * close of one type cannot inadvertently delete the record + * corresponding to a different type. + */ + +#include <assert.h> +#include <errno.h> +#include <ipxe/efi/efi.h> + +/** + * Open (or test) protocol for ephemeral use + * + * @v handle EFI handle + * @v protocol Protocol GUID + * @v interface Protocol interface pointer to fill in (or NULL to test) + * @ret rc Return status code + */ +int efi_open_untyped ( EFI_HANDLE handle, EFI_GUID *protocol, + void **interface ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_HANDLE agent = efi_image_handle; + EFI_HANDLE controller; + unsigned int attributes; + EFI_STATUS efirc; + int rc; + + /* Sanity checks */ + assert ( handle != NULL ); + assert ( protocol != NULL ); + + /* Open protocol + * + * We set ControllerHandle to NULL to avoid collisions with + * other open types. + */ + controller = NULL; + attributes = ( interface ? EFI_OPEN_PROTOCOL_GET_PROTOCOL : + EFI_OPEN_PROTOCOL_TEST_PROTOCOL ); + if ( ( efirc = bs->OpenProtocol ( handle, protocol, interface, agent, + controller, attributes ) ) != 0 ) { + rc = -EEFI ( efirc ); + if ( interface ) + *interface = NULL; + return rc; + } + + /* Close protocol immediately + * + * While it may seem prima facie unsafe to use a protocol + * after closing it, UEFI doesn't actually give us any safety + * even while the protocol is nominally open. Opening a + * protocol with EFI_OPEN_PROTOCOL_GET_PROTOCOL attributes + * does not in any way ensure that the interface pointer + * remains valid: there are no locks or notifications + * associated with these "opens". + * + * The only way to obtain a (partially) guaranteed persistent + * interface pointer is to open the protocol with the + * EFI_OPEN_PROTOCOL_BY_DRIVER attributes. This is not + * possible in the general case, since UEFI permits only a + * single image at a time to have the protocol opened with + * these attributes. + * + * We can therefore obtain at best an ephemeral interface + * pointer: one that is guaranteed to remain valid only for as + * long as we do not relinquish the thread of control. + * + * (Since UEFI permits calls to UninstallProtocolInterface() + * at levels up to and including TPL_NOTIFY, this means that + * we technically cannot rely on the pointer remaining valid + * unless the caller is itself running at TPL_NOTIFY. This is + * clearly impractical, and large portions of the EDK2 + * codebase presume that using EFI_OPEN_PROTOCOL_GET_PROTOCOL + * is safe at lower TPLs.) + * + * Closing is not strictly necessary for protocols opened + * ephemerally (i.e. using EFI_OPEN_PROTOCOL_GET_PROTOCOL or + * EFI_OPEN_PROTOCOL_TEST_PROTOCOL), but avoids polluting the + * protocol open information tables with stale data. + * + * Closing immediately also simplifies the callers' code + * paths, since they do not need to worry about closing the + * protocol. + * + * The overall effect is equivalent to using HandleProtocol(), + * but without the associated pollution of the protocol open + * information tables, and with improved traceability. + */ + bs->CloseProtocol ( handle, protocol, agent, controller ); + + return 0; +} + +/** + * Open protocol for unsafe persistent use + * + * @v handle EFI handle + * @v protocol Protocol GUID + * @v interface Protocol interface pointer to fill in + * @ret rc Return status code + */ +int efi_open_unsafe_untyped ( EFI_HANDLE handle, EFI_GUID *protocol, + void **interface ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_HANDLE agent = efi_image_handle; + EFI_HANDLE controller; + unsigned int attributes; + EFI_STATUS efirc; + int rc; + + /* Sanity checks */ + assert ( handle != NULL ); + assert ( protocol != NULL ); + assert ( interface != NULL ); + + /* Open protocol + * + * We set ControllerHandle equal to AgentHandle to avoid + * collisions with other open types. + */ + controller = agent; + attributes = EFI_OPEN_PROTOCOL_GET_PROTOCOL; + if ( ( efirc = bs->OpenProtocol ( handle, protocol, interface, agent, + controller, attributes ) ) != 0 ) { + rc = -EEFI ( efirc ); + *interface = NULL; + return rc; + } + + return 0; +} + +/** + * Close protocol opened for unsafe persistent use + * + * @v handle EFI handle + * @v protocol Protocol GUID + * @v child Child controller handle + */ +void efi_close_unsafe ( EFI_HANDLE handle, EFI_GUID *protocol ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_HANDLE agent = efi_image_handle; + EFI_HANDLE controller; + + /* Sanity checks */ + assert ( handle != NULL ); + assert ( protocol != NULL ); + + /* Close protocol */ + controller = agent; + bs->CloseProtocol ( handle, protocol, agent, controller ); +} + +/** + * Open protocol for persistent use by a driver + * + * @v handle EFI handle + * @v protocol Protocol GUID + * @v interface Protocol interface pointer to fill in + * @ret rc Return status code + */ +int efi_open_by_driver_untyped ( EFI_HANDLE handle, EFI_GUID *protocol, + void **interface ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_HANDLE agent = efi_image_handle; + EFI_HANDLE controller; + unsigned int attributes; + EFI_STATUS efirc; + int rc; + + /* Sanity checks */ + assert ( handle != NULL ); + assert ( protocol != NULL ); + assert ( interface != NULL ); + + /* Open protocol + * + * We set ControllerHandle equal to Handle to avoid collisions + * with other open types. + */ + controller = handle; + attributes = ( EFI_OPEN_PROTOCOL_BY_DRIVER | + EFI_OPEN_PROTOCOL_EXCLUSIVE ); + if ( ( efirc = bs->OpenProtocol ( handle, protocol, interface, agent, + controller, attributes ) ) != 0 ) { + rc = -EEFI ( efirc ); + *interface = NULL; + return rc; + } + + return 0; +} + +/** + * Close protocol opened for persistent use by a driver + * + * @v handle EFI handle + * @v protocol Protocol GUID + */ +void efi_close_by_driver ( EFI_HANDLE handle, EFI_GUID *protocol ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_HANDLE agent = efi_image_handle; + EFI_HANDLE controller; + + /* Sanity checks */ + assert ( handle != NULL ); + assert ( protocol != NULL ); + + /* Close protocol */ + controller = handle; + bs->CloseProtocol ( handle, protocol, agent, controller ); +} + +/** + * Open protocol for persistent use by a child controller + * + * @v handle EFI handle + * @v protocol Protocol GUID + * @v child Child controller handle + * @v interface Protocol interface pointer to fill in + * @ret rc Return status code + */ +int efi_open_by_child_untyped ( EFI_HANDLE handle, EFI_GUID *protocol, + EFI_HANDLE child, void **interface ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_HANDLE agent = efi_image_handle; + EFI_HANDLE controller; + unsigned int attributes; + EFI_STATUS efirc; + int rc; + + /* Sanity checks */ + assert ( handle != NULL ); + assert ( protocol != NULL ); + assert ( child != NULL ); + assert ( interface != NULL ); + + /* Open protocol + * + * We set ControllerHandle to a non-NULL value distinct from + * both Handle and AgentHandle to avoid collisions with other + * open types. + */ + controller = child; + assert ( controller != handle ); + assert ( controller != agent ); + attributes = EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER; + if ( ( efirc = bs->OpenProtocol ( handle, protocol, interface, agent, + controller, attributes ) ) != 0 ) { + rc = -EEFI ( efirc ); + *interface = NULL; + return rc; + } + + return 0; +} + +/** + * Close protocol opened for persistent use by a child controller + * + * @v handle EFI handle + * @v protocol Protocol GUID + * @v child Child controller handle + */ +void efi_close_by_child ( EFI_HANDLE handle, EFI_GUID *protocol, + EFI_HANDLE child ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_HANDLE agent = efi_image_handle; + EFI_HANDLE controller; + + /* Sanity checks */ + assert ( handle != NULL ); + assert ( protocol != NULL ); + assert ( child != NULL ); + + /* Close protocol */ + controller = child; + assert ( controller != handle ); + assert ( controller != agent ); + bs->CloseProtocol ( handle, protocol, agent, controller ); +} diff --git a/src/interface/efi/efi_path.c b/src/interface/efi/efi_path.c index ac3c04987..37558c36e 100644 --- a/src/interface/efi/efi_path.c +++ b/src/interface/efi/efi_path.c @@ -18,6 +18,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER ); +FILE_SECBOOT ( PERMITTED ); #include <stdlib.h> #include <stdarg.h> @@ -38,6 +39,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <ipxe/dhcp.h> #include <ipxe/efi/efi.h> #include <ipxe/efi/efi_driver.h> +#include <ipxe/efi/efi_strings.h> #include <ipxe/efi/efi_path.h> /** @file @@ -46,6 +48,34 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ +/** Dummy parent device path + * + * This is used as the parent device path when we need to construct a + * path for a device that has no EFI parent device. + */ +static struct { + BBS_BBS_DEVICE_PATH bbs; + CHAR8 tring[4]; + EFI_DEVICE_PATH_PROTOCOL end; +} __attribute__ (( packed )) efi_dummy_parent_path = { + .bbs = { + .Header = { + .Type = BBS_DEVICE_PATH, + .SubType = BBS_BBS_DP, + .Length[0] = ( sizeof ( efi_dummy_parent_path.bbs ) + + sizeof ( efi_dummy_parent_path.tring )), + }, + .DeviceType = BBS_TYPE_UNKNOWN, + .String[0] = 'i', + }, + .tring = "PXE", + .end = { + .Type = END_DEVICE_PATH_TYPE, + .SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE, + .Length[0] = sizeof ( efi_dummy_parent_path.end ), + }, +}; + /** An EFI device path settings block */ struct efi_path_settings { /** Settings interface */ @@ -148,6 +178,33 @@ size_t efi_path_len ( EFI_DEVICE_PATH_PROTOCOL *path ) { } /** + * Check that device path is well-formed + * + * @v path Device path, or NULL + * @v max Maximum device path length + * @ret rc Return status code + */ +int efi_path_check ( EFI_DEVICE_PATH_PROTOCOL *path, size_t max ) { + EFI_DEVICE_PATH_PROTOCOL *next; + size_t remaining = max; + size_t len; + + /* Check that path terminates within maximum length */ + for ( ; ; path = next ) { + if ( remaining < sizeof ( *path ) ) + return -EINVAL; + next = efi_path_next ( path ); + if ( ! next ) + break; + len = ( ( ( void * ) next ) - ( ( void * ) path ) ); + if ( remaining < len ) + return -EINVAL; + } + + return 0; +} + +/** * Get MAC address from device path * * @v path Device path @@ -325,6 +382,24 @@ EFI_DEVICE_PATH_PROTOCOL * efi_paths ( EFI_DEVICE_PATH_PROTOCOL *first, ... ) { } /** + * Construct EFI parent device path + * + * @v dev Generic device + * @ret path Parent (or dummy) device path + */ +static EFI_DEVICE_PATH_PROTOCOL * efi_parent_path ( struct device *dev ) { + struct efi_device *efidev; + + /* Use EFI parent device's path, if possible */ + efidev = efidev_parent ( dev ); + if ( efidev ) + return efidev->path; + + /* Otherwise, use a dummy parent device path */ + return &efi_dummy_parent_path.bbs.Header; +} + +/** * Construct EFI device path for network device * * @v netdev Network device @@ -334,7 +409,7 @@ EFI_DEVICE_PATH_PROTOCOL * efi_paths ( EFI_DEVICE_PATH_PROTOCOL *first, ... ) { * allocated device path. */ EFI_DEVICE_PATH_PROTOCOL * efi_netdev_path ( struct net_device *netdev ) { - struct efi_device *efidev; + EFI_DEVICE_PATH_PROTOCOL *parent; EFI_DEVICE_PATH_PROTOCOL *path; MAC_ADDR_DEVICE_PATH *macpath; VLAN_DEVICE_PATH *vlanpath; @@ -343,13 +418,11 @@ EFI_DEVICE_PATH_PROTOCOL * efi_netdev_path ( struct net_device *netdev ) { size_t prefix_len; size_t len; - /* Find parent EFI device */ - efidev = efidev_parent ( netdev->dev ); - if ( ! efidev ) - return NULL; + /* Get parent EFI device path */ + parent = efi_parent_path ( netdev->dev ); /* Calculate device path length */ - prefix_len = efi_path_len ( efidev->path ); + prefix_len = efi_path_len ( parent ); len = ( prefix_len + sizeof ( *macpath ) + sizeof ( *vlanpath ) + sizeof ( *end ) ); @@ -359,7 +432,7 @@ EFI_DEVICE_PATH_PROTOCOL * efi_netdev_path ( struct net_device *netdev ) { return NULL; /* Construct device path */ - memcpy ( path, efidev->path, prefix_len ); + memcpy ( path, parent, prefix_len ); macpath = ( ( ( void * ) path ) + prefix_len ); macpath->Header.Type = MESSAGING_DEVICE_PATH; macpath->Header.SubType = MSG_MAC_ADDR_DP; @@ -573,20 +646,18 @@ EFI_DEVICE_PATH_PROTOCOL * efi_ib_srp_path ( struct ib_srp_device *ib_srp ) { 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 *parent; 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; + /* Get parent EFI device path */ + parent = efi_parent_path ( ib_srp->ibdev->dev ); /* Calculate device path length */ - prefix_len = efi_path_len ( efidev->path ); + prefix_len = efi_path_len ( parent ); len = ( prefix_len + sizeof ( *ibpath ) + sizeof ( *end ) ); /* Allocate device path */ @@ -595,7 +666,7 @@ EFI_DEVICE_PATH_PROTOCOL * efi_ib_srp_path ( struct ib_srp_device *ib_srp ) { return NULL; /* Construct device path */ - memcpy ( path, efidev->path, prefix_len ); + memcpy ( path, parent, prefix_len ); ibpath = ( ( ( void * ) path ) + prefix_len ); ibpath->Header.Type = MESSAGING_DEVICE_PATH; ibpath->Header.SubType = MSG_INFINIBAND_DP; @@ -625,7 +696,7 @@ EFI_DEVICE_PATH_PROTOCOL * efi_ib_srp_path ( struct ib_srp_device *ib_srp ) { */ 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 *parent; EFI_DEVICE_PATH_PROTOCOL *path; EFI_DEVICE_PATH_PROTOCOL *end; USB_DEVICE_PATH *usbpath; @@ -636,14 +707,12 @@ EFI_DEVICE_PATH_PROTOCOL * efi_usb_path ( struct usb_function *func ) { /* Sanity check */ assert ( func->desc.count >= 1 ); - /* Find parent EFI device */ - efidev = efidev_parent ( &func->dev ); - if ( ! efidev ) - return NULL; + /* Get parent EFI device path */ + parent = efi_parent_path ( &func->dev ); /* Calculate device path length */ count = ( usb_depth ( usb ) + 1 ); - prefix_len = efi_path_len ( efidev->path ); + prefix_len = efi_path_len ( parent ); len = ( prefix_len + ( count * sizeof ( *usbpath ) ) + sizeof ( *end ) ); @@ -653,7 +722,7 @@ EFI_DEVICE_PATH_PROTOCOL * efi_usb_path ( struct usb_function *func ) { return NULL; /* Construct device path */ - memcpy ( path, efidev->path, prefix_len ); + memcpy ( path, parent, prefix_len ); end = ( ( ( void * ) path ) + len - sizeof ( *end ) ); efi_path_terminate ( end ); usbpath = ( ( ( void * ) end ) - sizeof ( *usbpath ) ); @@ -669,6 +738,177 @@ EFI_DEVICE_PATH_PROTOCOL * efi_usb_path ( struct usb_function *func ) { } /** + * Get EFI device path from load option + * + * @v load EFI load option + * @v len Length of EFI load option + * @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_load_path ( EFI_LOAD_OPTION *load, + size_t len ) { + EFI_DEVICE_PATH_PROTOCOL *path; + EFI_DEVICE_PATH_PROTOCOL *copy; + CHAR16 *wdesc; + size_t path_max; + size_t wmax; + size_t wlen; + + /* Check basic structure size */ + if ( len < sizeof ( *load ) ) { + DBGC ( load, "EFI load option too short for header:\n" ); + DBGC_HDA ( load, 0, load, len ); + return NULL; + } + + /* Get length of description */ + wdesc = ( ( ( void * ) load ) + sizeof ( *load ) ); + wmax = ( ( len - sizeof ( *load ) ) / sizeof ( wdesc[0] ) ); + wlen = wcsnlen ( wdesc, wmax ); + if ( wlen == wmax ) { + DBGC ( load, "EFI load option has unterminated " + "description:\n" ); + DBGC_HDA ( load, 0, load, len ); + return NULL; + } + + /* Get inline device path */ + path = ( ( ( void * ) load ) + sizeof ( *load ) + + ( wlen * sizeof ( wdesc[0] ) ) + 2 /* wNUL */ ); + path_max = ( len - sizeof ( *load ) - ( wlen * sizeof ( wdesc[0] ) ) + - 2 /* wNUL */ ); + if ( load->FilePathListLength > path_max ) { + DBGC ( load, "EFI load option too short for path(s):\n" ); + DBGC_HDA ( load, 0, load, len ); + return NULL; + } + + /* Check path length */ + if ( efi_path_check ( path, path_max ) != 0 ) { + DBGC ( load, "EFI load option has unterminated device " + "path:\n" ); + DBGC_HDA ( load, 0, load, len ); + return NULL; + } + + /* Allocate copy of path */ + copy = malloc ( path_max ); + if ( ! copy ) + return NULL; + memcpy ( copy, path, path_max ); + + return copy; +} + +/** + * Get EFI device path for numbered boot option + * + * @v number Boot option number + * @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_boot_path ( unsigned int number ) { + EFI_RUNTIME_SERVICES *rs = efi_systab->RuntimeServices; + EFI_GUID *guid = &efi_global_variable; + CHAR16 wname[ 9 /* "BootXXXX" + wNUL */ ]; + EFI_LOAD_OPTION *load; + EFI_DEVICE_PATH *path; + UINT32 attrs; + UINTN size; + EFI_STATUS efirc; + int rc; + + /* Construct variable name */ + efi_snprintf ( wname, ( sizeof ( wname ) / sizeof ( wname[0] ) ), + "Boot%04X", number ); + + /* Get variable length */ + size = 0; + if ( ( efirc = rs->GetVariable ( wname, guid, &attrs, &size, + NULL ) != EFI_BUFFER_TOO_SMALL ) ) { + rc = -EEFI ( efirc ); + DBGC ( rs, "EFI could not get size of %ls: %s\n", + wname, strerror ( rc ) ); + goto err_size; + } + + /* Allocate temporary buffer for EFI_LOAD_OPTION */ + load = malloc ( size ); + if ( ! load ) { + rc = -ENOMEM; + goto err_alloc; + } + + /* Read variable */ + if ( ( efirc = rs->GetVariable ( wname, guid, &attrs, &size, + load ) != 0 ) ) { + rc = -EEFI ( efirc ); + DBGC ( rs, "EFI could not read %ls: %s\n", + wname, strerror ( rc ) ); + goto err_read; + } + DBGC2 ( rs, "EFI boot option %ls is:\n", wname ); + DBGC2_HDA ( rs, 0, load, size ); + + /* Get device path from load option */ + path = efi_load_path ( load, size ); + if ( ! path ) { + rc = -EINVAL; + DBGC ( rs, "EFI could not parse %ls: %s\n", + wname, strerror ( rc ) ); + goto err_path; + } + + /* Free temporary buffer */ + free ( load ); + + return path; + + err_path: + err_read: + free ( load ); + err_alloc: + err_size: + return NULL; +} + +/** + * Get EFI device path for current boot option + * + * @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_current_boot_path ( void ) { + EFI_RUNTIME_SERVICES *rs = efi_systab->RuntimeServices; + EFI_GUID *guid = &efi_global_variable; + CHAR16 wname[] = L"BootCurrent"; + UINT16 current; + UINT32 attrs; + UINTN size; + EFI_STATUS efirc; + int rc; + + /* Read current boot option index */ + size = sizeof ( current ); + if ( ( efirc = rs->GetVariable ( wname, guid, &attrs, &size, + ¤t ) != 0 ) ) { + rc = -EEFI ( efirc ); + DBGC ( rs, "EFI could not read %ls: %s\n", + wname, strerror ( rc ) ); + return NULL; + } + + /* Get device path from this boot option */ + return efi_boot_path ( current ); +} + +/** * Describe object as an EFI device path * * @v intf Interface diff --git a/src/interface/efi/efi_pci.c b/src/interface/efi/efi_pci.c index e2eeeb344..4bf3977c5 100644 --- a/src/interface/efi/efi_pci.c +++ b/src/interface/efi/efi_pci.c @@ -22,8 +22,10 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <stdlib.h> +#include <string.h> #include <errno.h> #include <ipxe/pci.h> #include <ipxe/acpi.h> @@ -63,33 +65,43 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ /** - * Check for a matching PCI root bridge I/O protocol + * Find closest bus:dev.fn address range within a root bridge * - * @v pci PCI device + * @v pci Starting PCI device * @v handle EFI PCI root bridge handle - * @v root EFI PCI root bridge I/O protocol + * @v range PCI bus:dev.fn address range to fill in * @ret rc Return status code */ -static int efipci_root_match ( struct pci_device *pci, EFI_HANDLE handle, - EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *root ) { +static int efipci_discover_one ( struct pci_device *pci, EFI_HANDLE handle, + struct pci_range *range ) { + 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; + } acpi; + uint32_t best = 0; + uint32_t start; + uint32_t count; + uint32_t index; unsigned int tag; EFI_STATUS efirc; int rc; - /* Check segment number */ - if ( root->SegmentNumber != segment ) - return -ENOENT; + /* Return empty range on error */ + range->start = 0; + range->count = 0; + + /* Open root bridge I/O protocol */ + if ( ( rc = efi_open ( handle, &efi_pci_root_bridge_io_protocol_guid, + &root ) ) != 0 ) { + DBGC ( pci, "EFIPCI " PCI_FMT " cannot open %s: %s\n", + PCI_ARGS ( pci ), efi_handle_name ( handle ), + strerror ( rc ) ); + return rc; + } /* Get ACPI resource descriptors */ - if ( ( efirc = root->Configuration ( root, &u.raw ) ) != 0 ) { + if ( ( efirc = root->Configuration ( root, &acpi.raw ) ) != 0 ) { rc = -EEFI ( efirc ); DBGC ( pci, "EFIPCI " PCI_FMT " cannot get configuration for " "%s: %s\n", PCI_ARGS ( pci ), @@ -97,57 +109,79 @@ static int efipci_root_match ( struct pci_device *pci, EFI_HANDLE handle, 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 ) ) { + for ( ; ( ( tag = acpi_resource_tag ( acpi.res ) ) != + ACPI_END_RESOURCE ) ; + acpi.res = acpi_resource_next ( acpi.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 ) + if ( acpi.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; + /* Get range for this descriptor */ + start = PCI_BUSDEVFN ( root->SegmentNumber, + le64_to_cpu ( acpi.res->qword.min ), + 0, 0 ); + count = PCI_BUSDEVFN ( 0, le64_to_cpu ( acpi.res->qword.len ), + 0, 0 ); + DBGC2 ( pci, "EFIPCI " PCI_FMT " found %04x:[%02x-%02x] via " + "%s\n", PCI_ARGS ( pci ), root->SegmentNumber, + PCI_BUS ( start ), PCI_BUS ( start + count - 1 ), + efi_handle_name ( handle ) ); + + /* Check for a matching or new closest range */ + index = ( pci->busdevfn - start ); + if ( ( index < count ) || ( index > best ) ) { + range->start = start; + range->count = count; + best = index; + } - /* We have seen at least one non-matching range - * descriptor, so assume failure unless we find a - * subsequent match. - */ - rc = -ENOENT; + /* Stop if this range contains the target bus:dev.fn address */ + if ( index < count ) + break; } - return rc; + /* If no range descriptors were seen, assume that the root + * bridge has a single bus. + */ + if ( ! range->count ) { + range->start = PCI_BUSDEVFN ( root->SegmentNumber, 0, 0, 0 ); + range->count = PCI_BUSDEVFN ( 0, 1, 0, 0 ); + } + + return 0; } /** - * Open EFI PCI root bridge I/O protocol + * Find closest bus:dev.fn address range within any root bridge * - * @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 + * @v pci Starting PCI device + * @v range PCI bus:dev.fn address range to fill in + * @v handle PCI root bridge I/O handle to fill in * @ret rc Return status code */ -static int efipci_root_open ( struct pci_device *pci, EFI_HANDLE *handle, - EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL **root ) { +static int efipci_discover_any ( struct pci_device *pci, + struct pci_range *range, + EFI_HANDLE *handle ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + uint32_t best = 0; + uint32_t index; + struct pci_range tmp; EFI_HANDLE *handles; UINTN num_handles; - union { - void *interface; - EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *root; - } u; - EFI_STATUS efirc; UINTN i; + EFI_STATUS efirc; int rc; - /* Enumerate all handles */ + /* Return an empty range and no handle on error */ + range->start = 0; + range->count = 0; + *handle = NULL; + + /* Enumerate all root bridge I/O protocol handles */ if ( ( efirc = bs->LocateHandleBuffer ( ByProtocol, &efi_pci_root_bridge_io_protocol_guid, NULL, &num_handles, &handles ) ) != 0 ) { @@ -157,48 +191,87 @@ static int efipci_root_open ( struct pci_device *pci, EFI_HANDLE *handle, goto err_locate; } - /* Look for matching root bridge I/O protocol */ + /* Iterate over all root bridge I/O protocols */ for ( i = 0 ; i < num_handles ; i++ ) { - *handle = handles[i]; - if ( ( efirc = bs->OpenProtocol ( *handle, - &efi_pci_root_bridge_io_protocol_guid, - &u.interface, efi_image_handle, *handle, - EFI_OPEN_PROTOCOL_GET_PROTOCOL ) ) != 0 ) { - rc = -EEFI ( efirc ); - DBGC ( pci, "EFIPCI " PCI_FMT " cannot open %s: %s\n", - PCI_ARGS ( pci ), efi_handle_name ( *handle ), - strerror ( rc ) ); + + /* Get matching or closest range for this root bridge */ + if ( ( rc = efipci_discover_one ( pci, handles[i], + &tmp ) ) != 0 ) continue; + + /* Check for a matching or new closest range */ + index = ( pci->busdevfn - tmp.start ); + if ( ( index < tmp.count ) || ( index > best ) ) { + range->start = tmp.start; + range->count = tmp.count; + best = index; } - if ( efipci_root_match ( pci, *handle, u.root ) == 0 ) { - *root = u.root; - bs->FreePool ( handles ); - return 0; + + /* Stop if this range contains the target bus:dev.fn address */ + if ( index < tmp.count ) { + *handle = handles[i]; + break; } - bs->CloseProtocol ( *handle, - &efi_pci_root_bridge_io_protocol_guid, - efi_image_handle, *handle ); } - DBGC ( pci, "EFIPCI " PCI_FMT " found no root bridge\n", - PCI_ARGS ( pci ) ); - rc = -ENOENT; + /* Check for a range containing the target bus:dev.fn address */ + if ( ! *handle ) { + rc = -ENOENT; + goto err_range; + } + + /* Success */ + rc = 0; + + err_range: bs->FreePool ( handles ); err_locate: return rc; } /** - * Close EFI PCI root bridge I/O protocol + * Find next PCI bus:dev.fn address range in system * - * @v handle EFI PCI root bridge handle + * @v busdevfn Starting PCI bus:dev.fn address + * @v range PCI bus:dev.fn address range to fill in */ -static void efipci_root_close ( EFI_HANDLE handle ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; +static void efipci_discover ( uint32_t busdevfn, struct pci_range *range ) { + struct pci_device pci; + EFI_HANDLE handle; + + /* Find range */ + memset ( &pci, 0, sizeof ( pci ) ); + pci_init ( &pci, busdevfn ); + efipci_discover_any ( &pci, range, &handle ); +} + +/** + * Open EFI PCI root bridge I/O protocol for ephemeral use + * + * @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_open ( struct pci_device *pci, EFI_HANDLE *handle, + EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL **root ) { + struct pci_range tmp; + int rc; + + /* Find matching root bridge I/O protocol handle */ + if ( ( rc = efipci_discover_any ( pci, &tmp, handle ) ) != 0 ) + return rc; - /* Close protocol */ - bs->CloseProtocol ( handle, &efi_pci_root_bridge_io_protocol_guid, - efi_image_handle, handle ); + /* Open PCI root bridge I/O protocol */ + if ( ( rc = efi_open ( *handle, &efi_pci_root_bridge_io_protocol_guid, + root ) ) != 0 ) { + DBGC ( pci, "EFIPCI " PCI_FMT " cannot open %s: %s\n", + PCI_ARGS ( pci ), efi_handle_name ( *handle ), + strerror ( rc ) ); + return rc; + } + + return 0; } /** @@ -234,7 +307,7 @@ int efipci_read ( struct pci_device *pci, unsigned long location, /* Open root bridge */ if ( ( rc = efipci_root_open ( pci, &handle, &root ) ) != 0 ) - goto err_root; + return rc; /* Read from configuration space */ if ( ( efirc = root->Pci.Read ( root, EFIPCI_WIDTH ( location ), @@ -244,13 +317,10 @@ int efipci_read ( struct pci_device *pci, unsigned long location, DBGC ( pci, "EFIPCI " PCI_FMT " config read from offset %02lx " "failed: %s\n", PCI_ARGS ( pci ), EFIPCI_OFFSET ( location ), strerror ( rc ) ); - goto err_read; + return rc; } - err_read: - efipci_root_close ( handle ); - err_root: - return rc; + return 0; } /** @@ -270,7 +340,7 @@ int efipci_write ( struct pci_device *pci, unsigned long location, /* Open root bridge */ if ( ( rc = efipci_root_open ( pci, &handle, &root ) ) != 0 ) - goto err_root; + return rc; /* Read from configuration space */ if ( ( efirc = root->Pci.Write ( root, EFIPCI_WIDTH ( location ), @@ -280,13 +350,10 @@ int efipci_write ( struct pci_device *pci, unsigned long location, DBGC ( pci, "EFIPCI " PCI_FMT " config write to offset %02lx " "failed: %s\n", PCI_ARGS ( pci ), EFIPCI_OFFSET ( location ), strerror ( rc ) ); - goto err_write; + return rc; } - err_write: - efipci_root_close ( handle ); - err_root: - return rc; + return 0; } /** @@ -357,12 +424,12 @@ void * efipci_ioremap ( struct pci_device *pci, unsigned long bus_addr, } err_config: - efipci_root_close ( handle ); err_root: return ioremap ( bus_addr, len ); } -PROVIDE_PCIAPI_INLINE ( efi, pci_discover ); +PROVIDE_PCIAPI_INLINE ( efi, pci_can_probe ); +PROVIDE_PCIAPI ( efi, pci_discover, efipci_discover ); PROVIDE_PCIAPI_INLINE ( efi, pci_read_config_byte ); PROVIDE_PCIAPI_INLINE ( efi, pci_read_config_word ); PROVIDE_PCIAPI_INLINE ( efi, pci_read_config_dword ); @@ -370,6 +437,7 @@ 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 ); +PROVIDE_PCIAPI_RUNTIME ( efi, PCIAPI_PRIORITY_EFI ); /****************************************************************************** * @@ -389,7 +457,7 @@ PROVIDE_PCIAPI ( efi, pci_ioremap, efipci_ioremap ); * @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 ) { + void *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; @@ -398,6 +466,7 @@ static int efipci_dma_map ( struct dma_device *dma, struct dma_mapping *map, EFI_PHYSICAL_ADDRESS bus; UINTN count; VOID *mapping; + physaddr_t phys; EFI_STATUS efirc; int rc; @@ -420,18 +489,19 @@ static int efipci_dma_map ( struct dma_device *dma, struct dma_mapping *map, } /* Map buffer (if non-zero length) */ + phys = virt_to_phys ( addr ); count = len; if ( len ) { - if ( ( efirc = pci_io->Map ( pci_io, op, phys_to_virt ( addr ), - &count, &bus, &mapping ) ) != 0 ) { + if ( ( efirc = pci_io->Map ( pci_io, op, addr, &count, &bus, + &mapping ) ) != 0 ) { rc = -EEFI ( efirc ); - DBGC ( pci, "EFIPCI " PCI_FMT " cannot map %08lx+%zx: " + DBGC ( pci, "EFIPCI " PCI_FMT " cannot map %p+%zx: " "%s\n", PCI_ARGS ( pci ), addr, len, strerror ( rc ) ); goto err_map; } } else { - bus = addr; + bus = phys; mapping = NULL; } @@ -442,14 +512,14 @@ static int efipci_dma_map ( struct dma_device *dma, struct dma_mapping *map, */ if ( count != len ) { DBGC ( pci, "EFIPCI " PCI_FMT " attempted split mapping for " - "%08lx+%zx\n", PCI_ARGS ( pci ), addr, len ); + "%p+%zx\n", PCI_ARGS ( pci ), addr, len ); rc = -ENOTSUP; goto err_len; } /* Populate mapping */ map->dma = dma; - map->offset = ( bus - addr ); + map->offset = ( bus - phys ); map->token = mapping; /* Increment mapping count (for debugging) */ @@ -469,9 +539,10 @@ static int efipci_dma_map ( struct dma_device *dma, struct dma_mapping *map, * * @v dma DMA device * @v map DMA mapping + * @v len Used length */ static void efipci_dma_unmap ( struct dma_device *dma, - struct dma_mapping *map ) { + struct dma_mapping *map, size_t len __unused ) { struct efi_pci_device *efipci = container_of ( dma, struct efi_pci_device, pci.dma ); EFI_PCI_IO_PROTOCOL *pci_io = efipci->io; @@ -528,8 +599,7 @@ static void * efipci_dma_alloc ( struct dma_device *dma, memset ( addr, 0, ( pages * EFI_PAGE_SIZE ) ); /* Map buffer */ - if ( ( rc = efipci_dma_map ( dma, map, virt_to_phys ( addr ), - ( pages * EFI_PAGE_SIZE ), + if ( ( rc = efipci_dma_map ( dma, map, addr, ( pages * EFI_PAGE_SIZE ), DMA_BI ) ) != 0 ) goto err_map; @@ -539,7 +609,7 @@ static void * efipci_dma_alloc ( struct dma_device *dma, return addr; - efipci_dma_unmap ( dma, map ); + efipci_dma_unmap ( dma, map, len ); err_map: pci_io->FreeBuffer ( pci_io, pages, addr ); err_alloc: @@ -565,7 +635,7 @@ static void efipci_dma_free ( struct dma_device *dma, struct dma_mapping *map, pages = ( ( len + EFI_PAGE_SIZE - 1 ) / EFI_PAGE_SIZE ); /* Unmap buffer */ - efipci_dma_unmap ( dma, map ); + efipci_dma_unmap ( dma, map, len ); /* Free buffer */ pci_io->FreeBuffer ( pci_io, pages, addr ); @@ -576,38 +646,6 @@ static void efipci_dma_free ( struct dma_device *dma, struct dma_mapping *map, } /** - * 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 @@ -645,8 +683,8 @@ static struct dma_operations efipci_dma_operations = { .unmap = efipci_dma_unmap, .alloc = efipci_dma_alloc, .free = efipci_dma_free, - .umalloc = efipci_dma_umalloc, - .ufree = efipci_dma_ufree, + .umalloc = efipci_dma_alloc, + .ufree = efipci_dma_free, .set_mask = efipci_dma_set_mask, }; @@ -658,44 +696,35 @@ static struct dma_operations efipci_dma_operations = { */ /** - * Open EFI PCI device + * Get EFI PCI device information * * @v device EFI device handle - * @v attributes Protocol opening attributes * @v efipci EFI PCI device to fill in * @ret rc Return status code */ -int efipci_open ( EFI_HANDLE device, UINT32 attributes, - struct efi_pci_device *efipci ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - union { - EFI_PCI_IO_PROTOCOL *pci_io; - void *interface; - } pci_io; +int efipci_info ( EFI_HANDLE device, struct efi_pci_device *efipci ) { + EFI_PCI_IO_PROTOCOL *pci_io; UINTN pci_segment, pci_bus, pci_dev, pci_fn; unsigned int busdevfn; EFI_STATUS efirc; int rc; /* See if device is a PCI device */ - if ( ( efirc = bs->OpenProtocol ( device, &efi_pci_io_protocol_guid, - &pci_io.interface, efi_image_handle, - device, attributes ) ) != 0 ) { - rc = -EEFI_PCI ( efirc ); + if ( ( rc = efi_open ( device, &efi_pci_io_protocol_guid, + &pci_io ) ) != 0 ) { DBGCP ( device, "EFIPCI %s cannot open PCI protocols: %s\n", efi_handle_name ( device ), strerror ( rc ) ); - goto err_open_protocol; + return rc; } - efipci->io = pci_io.pci_io; + efipci->io = pci_io; /* Get PCI bus:dev.fn address */ - if ( ( efirc = pci_io.pci_io->GetLocation ( pci_io.pci_io, &pci_segment, - &pci_bus, &pci_dev, - &pci_fn ) ) != 0 ) { + if ( ( efirc = pci_io->GetLocation ( pci_io, &pci_segment, &pci_bus, + &pci_dev, &pci_fn ) ) != 0 ) { rc = -EEFI ( efirc ); DBGC ( device, "EFIPCI %s could not get PCI location: %s\n", efi_handle_name ( device ), strerror ( rc ) ); - goto err_get_location; + return rc; } busdevfn = PCI_BUSDEVFN ( pci_segment, pci_bus, pci_dev, pci_fn ); pci_init ( &efipci->pci, busdevfn ); @@ -709,63 +738,20 @@ int efipci_open ( EFI_HANDLE device, UINT32 attributes, * support I/O cycles). Work around any such platforms by * enabling bits individually and simply ignoring any errors. */ - pci_io.pci_io->Attributes ( pci_io.pci_io, - EfiPciIoAttributeOperationEnable, - EFI_PCI_IO_ATTRIBUTE_IO, NULL ); - pci_io.pci_io->Attributes ( pci_io.pci_io, - EfiPciIoAttributeOperationEnable, - EFI_PCI_IO_ATTRIBUTE_MEMORY, NULL ); - pci_io.pci_io->Attributes ( pci_io.pci_io, - EfiPciIoAttributeOperationEnable, - EFI_PCI_IO_ATTRIBUTE_BUS_MASTER, NULL ); + pci_io->Attributes ( pci_io, EfiPciIoAttributeOperationEnable, + EFI_PCI_IO_ATTRIBUTE_IO, NULL ); + pci_io->Attributes ( pci_io, EfiPciIoAttributeOperationEnable, + EFI_PCI_IO_ATTRIBUTE_MEMORY, NULL ); + pci_io->Attributes ( pci_io, EfiPciIoAttributeOperationEnable, + EFI_PCI_IO_ATTRIBUTE_BUS_MASTER, NULL ); /* Populate PCI device */ if ( ( rc = pci_read_config ( &efipci->pci ) ) != 0 ) { DBGC ( device, "EFIPCI " PCI_FMT " cannot read PCI " "configuration: %s\n", PCI_ARGS ( &efipci->pci ), strerror ( rc ) ); - goto err_pci_read_config; - } - - return 0; - - err_pci_read_config: - err_get_location: - bs->CloseProtocol ( device, &efi_pci_io_protocol_guid, - efi_image_handle, device ); - err_open_protocol: - return rc; -} - -/** - * Close EFI PCI device - * - * @v device EFI device handle - */ -void efipci_close ( EFI_HANDLE device ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - - bs->CloseProtocol ( device, &efi_pci_io_protocol_guid, - efi_image_handle, device ); -} - -/** - * Get EFI PCI device information - * - * @v device EFI device handle - * @v efipci EFI PCI device to fill in - * @ret rc Return status code - */ -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, - efipci ) ) != 0 ) return rc; - - /* Close PCI device */ - efipci_close ( device ); + } return 0; } @@ -817,6 +803,26 @@ static int efipci_supported ( EFI_HANDLE device ) { } /** + * Exclude existing drivers + * + * @v device EFI device handle + * @ret rc Return status code + */ +static int efipci_exclude ( EFI_HANDLE device ) { + EFI_GUID *protocol = &efi_pci_io_protocol_guid; + int rc; + + /* Exclude existing PCI I/O protocol drivers */ + if ( ( rc = efi_driver_exclude ( device, protocol ) ) != 0 ) { + DBGC ( device, "EFIPCI %s could not exclude drivers: %s\n", + efi_handle_name ( device ), strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** * Attach driver to device * * @v efidev EFI device @@ -834,10 +840,13 @@ static int efipci_start ( struct efi_device *efidev ) { goto err_alloc; } - /* Open PCI device */ - if ( ( rc = efipci_open ( device, ( EFI_OPEN_PROTOCOL_BY_DRIVER | - EFI_OPEN_PROTOCOL_EXCLUSIVE ), - efipci ) ) != 0 ) { + /* Get PCI device information */ + if ( ( rc = efipci_info ( device, efipci ) ) != 0 ) + goto err_info; + + /* Open PCI I/O protocol */ + if ( ( rc = efi_open_by_driver ( device, &efi_pci_io_protocol_guid, + &efipci->io ) ) != 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 ); @@ -872,8 +881,9 @@ static int efipci_start ( struct efi_device *efidev ) { err_probe: list_del ( &efipci->pci.dev.siblings ); err_find_driver: - efipci_close ( device ); + efi_close_by_driver ( device, &efi_pci_io_protocol_guid ); err_open: + err_info: free ( efipci ); err_alloc: return rc; @@ -892,14 +902,15 @@ static void efipci_stop ( struct efi_device *efidev ) { list_del ( &efipci->pci.dev.siblings ); assert ( efipci->pci.dma.mapped == 0 ); assert ( efipci->pci.dma.allocated == 0 ); - efipci_close ( device ); + efi_close_by_driver ( device, &efi_pci_io_protocol_guid ); free ( efipci ); } /** EFI PCI driver */ -struct efi_driver efipci_driver __efi_driver ( EFI_DRIVER_NORMAL ) = { +struct efi_driver efipci_driver __efi_driver ( EFI_DRIVER_HARDWARE ) = { .name = "PCI", .supported = efipci_supported, + .exclude = efipci_exclude, .start = efipci_start, .stop = efipci_stop, }; diff --git a/src/interface/efi/efi_pxe.c b/src/interface/efi/efi_pxe.c index 843ebb5e7..2d1a6fd73 100644 --- a/src/interface/efi/efi_pxe.c +++ b/src/interface/efi/efi_pxe.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <string.h> #include <errno.h> @@ -305,48 +306,6 @@ static int efi_pxe_ip_filter ( struct efi_pxe *pxe, EFI_IP_ADDRESS *ip ) { /****************************************************************************** * - * Data transfer buffer - * - ****************************************************************************** - */ - -/** - * Reallocate PXE data transfer buffer - * - * @v xferbuf Data transfer buffer - * @v len New length (or zero to free buffer) - * @ret rc Return status code - */ -static int efi_pxe_buf_realloc ( struct xfer_buffer *xferbuf __unused, - size_t len __unused ) { - - /* Can never reallocate: return EFI_BUFFER_TOO_SMALL */ - return -ERANGE; -} - -/** - * Write data to PXE data transfer buffer - * - * @v xferbuf Data transfer buffer - * @v offset Starting offset - * @v data Data to copy - * @v len Length of data - */ -static void efi_pxe_buf_write ( struct xfer_buffer *xferbuf, size_t offset, - const void *data, size_t len ) { - - /* Copy data to buffer */ - memcpy ( ( xferbuf->data + offset ), data, len ); -} - -/** PXE data transfer buffer operations */ -static struct xfer_buffer_operations efi_pxe_buf_operations = { - .realloc = efi_pxe_buf_realloc, - .write = efi_pxe_buf_write, -}; - -/****************************************************************************** - * * (M)TFTP download interface * ****************************************************************************** @@ -966,8 +925,7 @@ efi_pxe_mtftp ( EFI_PXE_BASE_CODE_PROTOCOL *base, pxe->blksize = ( ( callback && blksize ) ? *blksize : -1UL ); /* Initialise data transfer buffer */ - pxe->buf.data = data; - pxe->buf.len = *len; + xferbuf_fixed_init ( &pxe->buf, data, *len ); /* Open download */ if ( ( rc = efi_pxe_tftp_open ( pxe, ip, @@ -987,6 +945,7 @@ efi_pxe_mtftp ( EFI_PXE_BASE_CODE_PROTOCOL *base, err_download: efi_pxe_tftp_close ( pxe, rc ); err_open: + xferbuf_fixed_init ( &pxe->buf, NULL, 0 ); efi_snp_release(); err_opcode: return EFIRC ( rc ); @@ -1611,7 +1570,6 @@ int efi_pxe_install ( EFI_HANDLE handle, struct net_device *netdev ) { pxe->base.Mode = &pxe->mode; memcpy ( &pxe->apple, &efi_apple_net_boot_protocol, sizeof ( pxe->apple ) ); - pxe->buf.op = &efi_pxe_buf_operations; intf_init ( &pxe->tftp, &efi_pxe_tftp_desc, &pxe->refcnt ); intf_init ( &pxe->udp, &efi_pxe_udp_desc, &pxe->refcnt ); INIT_LIST_HEAD ( &pxe->queue ); @@ -1679,7 +1637,7 @@ void efi_pxe_uninstall ( EFI_HANDLE handle ) { /* Locate PXE base code */ pxe = efi_pxe_find ( handle ); - if ( ! handle ) { + if ( ! pxe ) { DBG ( "PXE could not find base code for %s\n", efi_handle_name ( handle ) ); return; diff --git a/src/interface/efi/efi_reboot.c b/src/interface/efi/efi_reboot.c index 35919221e..cc35ef7ad 100644 --- a/src/interface/efi/efi_reboot.c +++ b/src/interface/efi/efi_reboot.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** * @file @@ -31,19 +32,44 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ #include <errno.h> +#include <string.h> #include <ipxe/efi/efi.h> +#include <ipxe/efi/Guid/GlobalVariable.h> #include <ipxe/reboot.h> /** * Reboot system * - * @v warm Perform a warm reboot + * @v flags Reboot flags */ -static void efi_reboot ( int warm ) { +static void efi_reboot ( int flags ) { EFI_RUNTIME_SERVICES *rs = efi_systab->RuntimeServices; + static CHAR16 wname[] = EFI_OS_INDICATIONS_VARIABLE_NAME; + UINT64 osind; + UINT32 attrs; + EFI_RESET_TYPE type; + EFI_STATUS efirc; + int rc; + + /* Request boot to firmware setup, if applicable */ + if ( flags & REBOOT_SETUP ) { + osind = EFI_OS_INDICATIONS_BOOT_TO_FW_UI; + attrs = ( EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS | + EFI_VARIABLE_NON_VOLATILE ); + if ( ( efirc = rs->SetVariable ( wname, &efi_global_variable, + attrs, sizeof ( osind ), + &osind ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( efi_systab, "EFI could not set %ls: %s\n", + wname, strerror ( rc ) ); + /* Continue to reboot anyway */ + } + } /* Use runtime services to reset system */ - rs->ResetSystem ( ( warm ? EfiResetWarm : EfiResetCold ), 0, 0, NULL ); + type = ( ( flags & REBOOT_WARM ) ? EfiResetWarm : EfiResetCold ); + rs->ResetSystem ( type, 0, 0, NULL ); } /** diff --git a/src/interface/efi/efi_rng.c b/src/interface/efi/efi_rng.c index b76a6fc0d..66b37fe89 100644 --- a/src/interface/efi/efi_rng.c +++ b/src/interface/efi/efi_rng.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <errno.h> #include <ipxe/entropy.h> @@ -54,6 +55,15 @@ EFI_REQUEST_PROTOCOL ( EFI_RNG_PROTOCOL, &efirng ); */ #define EFIRNG_LEN 32 +/** Maximum number of times to attempting requesting data from RNG + * + * The UEFI spec allows GetRNG() to return EFI_NOT_READY, which is not + * a particularly helpful error status since there is nothing that can + * sensibly be done except to retry immediately. We retry failed + * calls to GetRNG() (for any reason) up to this number of times. + */ +#define EFIRNG_MAX_RETRY 16 + /** * Enable entropy gathering * @@ -85,29 +95,35 @@ static int efirng_enable ( void ) { */ static int efirng_get_noise ( noise_sample_t *noise ) { uint8_t buf[EFIRNG_LEN]; + unsigned int i; EFI_STATUS efirc; int rc; /* Sanity check */ assert ( efirng != NULL ); - /* Get the minimum allowed number of random bytes */ - if ( ( efirc = efirng->GetRNG ( efirng, NULL, sizeof ( buf ), - buf ) ) != 0 ) { - rc = -EEFI ( efirc ); - DBGC ( &efirng, "ENTROPY could not read from RNG: %s\n", - strerror ( rc ) ); - return rc; + /* Get random bytes, retrying if needed */ + for ( i = 0 ; i < EFIRNG_MAX_RETRY ; i++ ) { + + /* Get the minimum allowed number of random bytes */ + if ( ( efirc = efirng->GetRNG ( efirng, NULL, sizeof ( buf ), + buf ) ) != 0 ) { + rc = -EEFI ( efirc ); + continue; + } + + /* Reduce random bytes to a single noise sample. This + * seems like overkill, but we have no way of knowing + * how much entropy is actually present in the bytes + * returned by the RNG protocol. + */ + *noise = crc32_le ( 0, buf, sizeof ( buf ) ); + return 0; } - /* Reduce random bytes to a single noise sample. This seems - * like overkill, but we have no way of knowing how much - * entropy is actually present in the bytes returned by the - * RNG protocol. - */ - *noise = crc32_le ( 0, buf, sizeof ( buf ) ); - - return 0; + DBGC ( &efirng, "ENTROPY could not read from RNG: %s\n", + strerror ( rc ) ); + return rc; } /** EFI random number generator protocol entropy source */ diff --git a/src/interface/efi/efi_service.c b/src/interface/efi/efi_service.c index d4129c0d9..4e2f2e951 100644 --- a/src/interface/efi/efi_service.c +++ b/src/interface/efi/efi_service.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** @file * @@ -45,44 +46,31 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ int efi_service_add ( EFI_HANDLE service, EFI_GUID *binding, EFI_HANDLE *handle ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - union { - EFI_SERVICE_BINDING_PROTOCOL *sb; - void *interface; - } u; + EFI_SERVICE_BINDING_PROTOCOL *sb; EFI_STATUS efirc; int rc; /* Open service binding protocol */ - if ( ( efirc = bs->OpenProtocol ( service, binding, &u.interface, - efi_image_handle, service, - EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){ - rc = -EEFI ( efirc ); + if ( ( rc = efi_open ( service, binding, &sb ) ) != 0 ) { DBGC ( service, "EFISVC %s cannot open %s binding: %s\n", efi_handle_name ( service ), efi_guid_ntoa ( binding ), strerror ( rc ) ); - goto err_open; + return rc; } /* Create child handle */ - if ( ( efirc = u.sb->CreateChild ( u.sb, handle ) ) != 0 ) { + if ( ( efirc = sb->CreateChild ( sb, handle ) ) != 0 ) { rc = -EEFI ( efirc ); DBGC ( service, "EFISVC %s could not create %s child: %s\n", efi_handle_name ( service ), efi_guid_ntoa ( binding ), strerror ( rc ) ); - goto err_create; + return rc; } - /* Success */ - rc = 0; DBGC ( service, "EFISVC %s created %s child ", efi_handle_name ( service ), efi_guid_ntoa ( binding ) ); DBGC ( service, "%s\n", efi_handle_name ( *handle ) ); - - err_create: - bs->CloseProtocol ( service, binding, efi_image_handle, service ); - err_open: - return rc; + return 0; } /** @@ -95,11 +83,7 @@ int efi_service_add ( EFI_HANDLE service, EFI_GUID *binding, */ int efi_service_del ( EFI_HANDLE service, EFI_GUID *binding, EFI_HANDLE handle ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - union { - EFI_SERVICE_BINDING_PROTOCOL *sb; - void *interface; - } u; + EFI_SERVICE_BINDING_PROTOCOL *sb; EFI_STATUS efirc; int rc; @@ -108,31 +92,22 @@ int efi_service_del ( EFI_HANDLE service, EFI_GUID *binding, DBGC ( service, "%s\n", efi_handle_name ( handle ) ); /* Open service binding protocol */ - if ( ( efirc = bs->OpenProtocol ( service, binding, &u.interface, - efi_image_handle, service, - EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){ - rc = -EEFI ( efirc ); + if ( ( rc = efi_open ( service, binding, &sb ) ) != 0 ) { DBGC ( service, "EFISVC %s cannot open %s binding: %s\n", efi_handle_name ( service ), efi_guid_ntoa ( binding ), strerror ( rc ) ); - goto err_open; + return rc; } /* Destroy child handle */ - if ( ( efirc = u.sb->DestroyChild ( u.sb, handle ) ) != 0 ) { + if ( ( efirc = sb->DestroyChild ( sb, handle ) ) != 0 ) { rc = -EEFI ( efirc ); DBGC ( service, "EFISVC %s could not destroy %s child ", efi_handle_name ( service ), efi_guid_ntoa ( binding ) ); DBGC ( service, "%s: %s\n", efi_handle_name ( handle ), strerror ( rc ) ); - goto err_destroy; + return rc; } - /* Success */ - rc = 0; - - err_destroy: - bs->CloseProtocol ( service, binding, efi_image_handle, service ); - err_open: - return rc; + return 0; } diff --git a/src/interface/efi/efi_settings.c b/src/interface/efi/efi_settings.c index cde0ff8d1..95fe2c03c 100644 --- a/src/interface/efi/efi_settings.c +++ b/src/interface/efi/efi_settings.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** * @file @@ -232,5 +233,6 @@ static void efivars_init ( void ) { /** EFI variable settings initialiser */ struct init_fn efivars_init_fn __init_fn ( INIT_NORMAL ) = { + .name = "efivars", .initialise = efivars_init, }; diff --git a/src/interface/efi/efi_shim.c b/src/interface/efi/efi_shim.c index d5419512d..553cb2721 100644 --- a/src/interface/efi/efi_shim.c +++ b/src/interface/efi/efi_shim.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <string.h> #include <stdlib.h> @@ -40,6 +41,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** * Require use of a third party loader binary @@ -272,42 +274,28 @@ static EFIAPI EFI_STATUS efi_shim_get_memory_map ( UINTN *len, * @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_PXE_BASE_CODE_PROTOCOL *pxe; 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 ); + if ( ( rc = efi_open ( handle, &efi_pxe_base_code_protocol_guid, + &pxe ) ) != 0 ) { DBGC ( &efi_shim, "SHIM could not open PXE base code: %s\n", strerror ( rc ) ); - goto err_no_base; + return rc; } /* Stop PXE base code */ - if ( ( efirc = u.pxe->Stop ( u.pxe ) ) != 0 ) { + if ( ( efirc = pxe->Stop ( pxe ) ) != 0 ) { rc = -EEFI ( efirc ); DBGC ( &efi_shim, "SHIM could not stop PXE base code: %s\n", strerror ( rc ) ); - goto err_stop; + return rc; } - /* 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; + return 0; } /** diff --git a/src/interface/efi/efi_smbios.c b/src/interface/efi/efi_smbios.c index d7877b0aa..c10ba1440 100644 --- a/src/interface/efi/efi_smbios.c +++ b/src/interface/efi/efi_smbios.c @@ -18,8 +18,10 @@ */ FILE_LICENCE ( GPL2_OR_LATER ); +FILE_SECBOOT ( PERMITTED ); #include <errno.h> +#include <ipxe/uaccess.h> #include <ipxe/smbios.h> #include <ipxe/efi/efi.h> #include <ipxe/efi/Guid/SmBios.h> @@ -48,27 +50,27 @@ static int efi_find_smbios ( struct smbios *smbios ) { /* Use 64-bit table if present */ if ( smbios3_entry && ( smbios3_entry->signature == SMBIOS3_SIGNATURE ) ) { - smbios->address = phys_to_user ( smbios3_entry->smbios_address ); + smbios->address = phys_to_virt ( 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 ); + virt_to_phys ( smbios->address ), smbios->len ); return 0; } /* 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->address = phys_to_virt ( 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 ); + virt_to_phys ( smbios->address ), smbios->len ); return 0; } diff --git a/src/interface/efi/efi_snp.c b/src/interface/efi/efi_snp.c index 8443be997..dad8b33df 100644 --- a/src/interface/efi/efi_snp.c +++ b/src/interface/efi/efi_snp.c @@ -18,6 +18,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER ); +FILE_SECBOOT ( PERMITTED ); #include <stdlib.h> #include <string.h> @@ -175,7 +176,7 @@ static void efi_snp_poll ( struct efi_snp_device *snpdev ) { while ( ( iobuf = netdev_rx_dequeue ( snpdev->netdev ) ) ) { list_add_tail ( &iobuf->list, &snpdev->rx ); snpdev->interrupts |= EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT; - bs->SignalEvent ( &snpdev->snp.WaitForPacket ); + bs->SignalEvent ( snpdev->snp.WaitForPacket ); } } @@ -1792,14 +1793,6 @@ static int efi_snp_probe ( struct net_device *netdev, void *priv __unused ) { EFI_STATUS efirc; int rc; - /* Find parent EFI device */ - efidev = efidev_parent ( netdev->dev ); - if ( ! efidev ) { - DBG ( "SNP skipping non-EFI device %s\n", netdev->name ); - rc = 0; - goto err_no_efidev; - } - /* Allocate the SNP device */ snpdev = zalloc ( sizeof ( *snpdev ) ); if ( ! snpdev ) { @@ -1807,9 +1800,13 @@ static int efi_snp_probe ( struct net_device *netdev, void *priv __unused ) { goto err_alloc_snp; } snpdev->netdev = netdev_get ( netdev ); - snpdev->efidev = efidev; INIT_LIST_HEAD ( &snpdev->rx ); + /* Find parent EFI device, if any */ + efidev = efidev_parent ( netdev->dev ); + if ( efidev ) + snpdev->parent = efidev->device; + /* Sanity check */ if ( netdev->ll_protocol->ll_addr_len > sizeof ( EFI_MAC_ADDRESS ) ) { DBGC ( snpdev, "SNPDEV %p cannot support link-layer address " @@ -1916,29 +1913,25 @@ static int efi_snp_probe ( struct net_device *netdev, void *priv __unused ) { * instances to prevent SnpDxe from attempting to bind to * them. */ - if ( ( efirc = bs->OpenProtocol ( snpdev->handle, - &efi_nii_protocol_guid, &interface, - efi_image_handle, snpdev->handle, - ( EFI_OPEN_PROTOCOL_BY_DRIVER | - EFI_OPEN_PROTOCOL_EXCLUSIVE )))!=0){ - rc = -EEFI ( efirc ); + if ( ( rc = efi_open_by_driver ( snpdev->handle, + &efi_nii_protocol_guid, + &interface ) ) != 0 ) { DBGC ( snpdev, "SNPDEV %p could not open NII protocol: %s\n", snpdev, strerror ( rc ) ); goto err_open_nii; } - if ( ( efirc = bs->OpenProtocol ( snpdev->handle, - &efi_nii31_protocol_guid, &interface, - efi_image_handle, snpdev->handle, - ( EFI_OPEN_PROTOCOL_BY_DRIVER | - EFI_OPEN_PROTOCOL_EXCLUSIVE )))!=0){ - rc = -EEFI ( efirc ); + if ( ( rc = efi_open_by_driver ( snpdev->handle, + &efi_nii31_protocol_guid, + &interface ) ) != 0 ) { DBGC ( snpdev, "SNPDEV %p could not open NII31 protocol: %s\n", snpdev, strerror ( rc ) ); goto err_open_nii31; } - /* Add as child of EFI parent device */ - if ( ( rc = efi_child_add ( efidev->device, snpdev->handle ) ) != 0 ) { + /* Add as child of EFI parent device, if applicable */ + if ( snpdev->parent && + ( ( rc = efi_child_add ( snpdev->parent, + snpdev->handle ) ) != 0 ) ) { DBGC ( snpdev, "SNPDEV %p could not become child of %s: %s\n", snpdev, efi_handle_name ( efidev->device ), strerror ( rc ) ); @@ -1958,10 +1951,6 @@ static int efi_snp_probe ( struct net_device *netdev, void *priv __unused ) { /* Add to list of SNP devices */ list_add ( &snpdev->list, &efi_snp_devices ); - /* Close device path */ - bs->CloseProtocol ( efidev->device, &efi_device_path_protocol_guid, - efi_image_handle, efidev->device ); - DBGC ( snpdev, "SNPDEV %p installed for %s as device %s\n", snpdev, netdev->name, efi_handle_name ( snpdev->handle ) ); return 0; @@ -1969,13 +1958,12 @@ static int efi_snp_probe ( struct net_device *netdev, void *priv __unused ) { list_del ( &snpdev->list ); if ( snpdev->package_list ) leak |= efi_snp_hii_uninstall ( snpdev ); - efi_child_del ( efidev->device, snpdev->handle ); + if ( snpdev->parent ) + efi_child_del ( snpdev->parent, snpdev->handle ); err_efi_child_add: - bs->CloseProtocol ( snpdev->handle, &efi_nii31_protocol_guid, - efi_image_handle, snpdev->handle ); + efi_close_by_driver ( snpdev->handle, &efi_nii31_protocol_guid ); err_open_nii31: - bs->CloseProtocol ( snpdev->handle, &efi_nii_protocol_guid, - efi_image_handle, snpdev->handle ); + efi_close_by_driver ( snpdev->handle, &efi_nii_protocol_guid ); err_open_nii: if ( ( efirc = bs->UninstallMultipleProtocolInterfaces ( snpdev->handle, @@ -2008,7 +1996,6 @@ static int efi_snp_probe ( struct net_device *netdev, void *priv __unused ) { free ( snpdev ); } err_alloc_snp: - err_no_efidev: if ( leak ) DBGC ( snpdev, "SNPDEV %p nullified and leaked\n", snpdev ); return rc; @@ -2063,11 +2050,10 @@ static void efi_snp_remove ( struct net_device *netdev, void *priv __unused ) { list_del ( &snpdev->list ); if ( snpdev->package_list ) 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 ); + if ( snpdev->parent ) + efi_child_del ( snpdev->parent, snpdev->handle ); + efi_close_by_driver ( snpdev->handle, &efi_nii_protocol_guid ); + efi_close_by_driver ( snpdev->handle, &efi_nii31_protocol_guid ); if ( ( ! efi_shutdown_in_progress ) && ( ( efirc = bs->UninstallMultipleProtocolInterfaces ( snpdev->handle, diff --git a/src/interface/efi/efi_snp_hii.c b/src/interface/efi/efi_snp_hii.c index 8b65c8a78..25287673a 100644 --- a/src/interface/efi/efi_snp_hii.c +++ b/src/interface/efi/efi_snp_hii.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** * @file diff --git a/src/interface/efi/efi_strings.c b/src/interface/efi/efi_strings.c index 765b23ca6..3dae22e41 100644 --- a/src/interface/efi/efi_strings.c +++ b/src/interface/efi/efi_strings.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <stddef.h> #include <stdarg.h> diff --git a/src/interface/efi/efi_table.c b/src/interface/efi/efi_table.c new file mode 100644 index 000000000..da5966a13 --- /dev/null +++ b/src/interface/efi/efi_table.c @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2025 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_SECBOOT ( PERMITTED ); + +#include <assert.h> +#include <errno.h> +#include <string.h> +#include <ipxe/efi/efi.h> +#include <ipxe/efi/efi_table.h> + +/** @file + * + * EFI configuration tables + * + */ + +/** + * Look up EFI configuration table + * + * @v guid Configuration table GUID + * @ret table Configuration table, or NULL + */ +void * efi_find_table ( EFI_GUID *guid ) { + void *table; + unsigned int i; + + /* Scan for installed table */ + for ( i = 0 ; i < efi_systab->NumberOfTableEntries ; i++ ) { + if ( memcmp ( &efi_systab->ConfigurationTable[i].VendorGuid, + guid, sizeof ( *guid ) ) == 0 ) { + table = efi_systab->ConfigurationTable[i].VendorTable; + DBGC ( guid, "EFITAB %s is at %p\n", + efi_guid_ntoa ( guid ), table ); + return table; + } + } + + return NULL; +} + +/** + * Install EFI configuration table + * + * @v table Configuration table type + * @v data Configuration table data, or NULL to uninstall + * @v backup Table backup, or NULL to not back up old table + * @ret rc Return status code + */ +int efi_install_table ( struct efi_table *table, const void *data, + void **backup ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_GUID *guid = table->guid; + void *copy; + void *new; + void *old; + size_t old_len; + size_t new_len; + EFI_STATUS efirc; + int rc; + + /* Get currently installed table, if any */ + old = efi_find_table ( guid ); + old_len = ( old ? table->len ( old ) : 0 ); + + /* Create backup copy, if applicable */ + if ( old_len && backup ) { + if ( ( efirc = bs->AllocatePool ( EfiBootServicesData, old_len, + © ) ) != 0 ) { + rc = -EEFI ( efirc ); + goto err_backup; + } + memcpy ( copy, old, old_len ); + DBGC ( table, "EFITAB %s %p+%#zx backed up\n", + efi_guid_ntoa ( guid ), old, old_len ); + } else { + copy = NULL; + } + + /* Create installable runtime services data copy, if applicable */ + new_len = ( data ? table->len ( data ) : 0 ); + if ( new_len ) { + if ( ( efirc = bs->AllocatePool ( EfiRuntimeServicesData, + new_len, &new ) ) != 0 ) { + rc = -EEFI ( efirc ); + goto err_allocate; + } + memcpy ( new, data, new_len ); + } else { + new = NULL; + } + + /* (Un)install configuration table, if applicable */ + if ( new || old ) { + if ( ( efirc = bs->InstallConfigurationTable ( guid, + new ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( table, "EFITAB %s could not install: %s\n", + efi_guid_ntoa ( guid ), strerror ( rc ) ); + goto err_install; + } + if ( old ) { + DBGC ( table, "EFITAB %s %p+%#zx uninstalled\n", + efi_guid_ntoa ( guid ), old, old_len ); + } + if ( new ) { + DBGC ( table, "EFITAB %s %p+%#zx installed\n", + efi_guid_ntoa ( guid ), new, new_len ); + } + } + + /* Record backup copy, if applicable */ + if ( backup ) { + if ( *backup ) + bs->FreePool ( *backup ); + *backup = copy; + } + + /* Sanity check */ + assert ( efi_find_table ( guid ) == new ); + + return 0; + + err_install: + if ( new ) + bs->FreePool ( new ); + err_allocate: + if ( copy ) + bs->FreePool ( copy ); + err_backup: + return rc; +} + +/** + * Uninstall EFI configuration table + * + * @v table Configuration table type + * @v backup Table backup (or NULL to not restore old table) + * @ret rc Return status code + */ +int efi_uninstall_table ( struct efi_table *table, void **backup ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + void *old; + int rc; + + /* Uninstall or reinstall as applicable */ + old = ( backup ? *backup : NULL ); + if ( ( rc = efi_install_table ( table, old, NULL ) ) != 0 ) + return rc; + + /* Free backup copy, if applicable */ + if ( backup && *backup ) { + bs->FreePool ( *backup ); + *backup = NULL; + } + + return 0; +} diff --git a/src/interface/efi/efi_time.c b/src/interface/efi/efi_time.c index 983a0ef5c..a4c77da20 100644 --- a/src/interface/efi/efi_time.c +++ b/src/interface/efi/efi_time.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <string.h> #include <errno.h> diff --git a/src/interface/efi/efi_timer.c b/src/interface/efi/efi_timer.c index 6427eb1d8..ffb899c86 100644 --- a/src/interface/efi/efi_timer.c +++ b/src/interface/efi/efi_timer.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <string.h> #include <errno.h> diff --git a/src/interface/efi/efi_umalloc.c b/src/interface/efi/efi_umalloc.c index e3f1dacc2..257b27bec 100644 --- a/src/interface/efi/efi_umalloc.c +++ b/src/interface/efi/efi_umalloc.c @@ -22,10 +22,12 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <string.h> #include <errno.h> #include <assert.h> +#include <ipxe/uaccess.h> #include <ipxe/umalloc.h> #include <ipxe/efi/efi.h> @@ -35,25 +37,23 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ -/** Equivalent of NOWHERE for user pointers */ -#define UNOWHERE ( ~UNULL ) - /** * Reallocate external memory * - * @v old_ptr Memory previously allocated by umalloc(), or UNULL + * @v old_ptr Memory previously allocated by umalloc(), or NULL * @v new_size Requested size - * @ret new_ptr Allocated memory, or UNULL + * @ret new_ptr Allocated memory, or NULL * * Calling realloc() with a new size of zero is a valid way to free a * memory block. */ -static userptr_t efi_urealloc ( userptr_t old_ptr, size_t new_size ) { +static void * efi_urealloc ( void *old_ptr, size_t new_size ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; EFI_PHYSICAL_ADDRESS phys_addr; unsigned int new_pages, old_pages; - userptr_t new_ptr = UNOWHERE; + void *new_ptr = NOWHERE; size_t old_size; + size_t *info; EFI_STATUS efirc; int rc; @@ -69,12 +69,12 @@ static userptr_t efi_urealloc ( userptr_t old_ptr, size_t new_size ) { rc = -EEFI ( efirc ); DBG ( "EFI could not allocate %d pages: %s\n", new_pages, strerror ( rc ) ); - return UNULL; + return NULL; } assert ( phys_addr != 0 ); - new_ptr = phys_to_user ( phys_addr + EFI_PAGE_SIZE ); - copy_to_user ( new_ptr, -EFI_PAGE_SIZE, - &new_size, sizeof ( new_size ) ); + new_ptr = phys_to_virt ( phys_addr + EFI_PAGE_SIZE ); + info = ( new_ptr - EFI_PAGE_SIZE ); + *info = new_size; DBG ( "EFI allocated %d pages at %llx\n", new_pages, phys_addr ); } @@ -84,13 +84,13 @@ static userptr_t efi_urealloc ( userptr_t old_ptr, size_t new_size ) { * is valid, or (b) new_size is 0; either way, the memcpy() is * valid. */ - if ( old_ptr && ( old_ptr != UNOWHERE ) ) { - copy_from_user ( &old_size, old_ptr, -EFI_PAGE_SIZE, - sizeof ( old_size ) ); - memcpy_user ( new_ptr, 0, old_ptr, 0, - ( (old_size < new_size) ? old_size : new_size )); + if ( old_ptr && ( old_ptr != NOWHERE ) ) { + info = ( old_ptr - EFI_PAGE_SIZE ); + old_size = *info; + memcpy ( new_ptr, old_ptr, + ( (old_size < new_size) ? old_size : new_size ) ); old_pages = ( EFI_SIZE_TO_PAGES ( old_size ) + 1 ); - phys_addr = user_to_phys ( old_ptr, -EFI_PAGE_SIZE ); + phys_addr = virt_to_phys ( old_ptr - EFI_PAGE_SIZE ); if ( ( efirc = bs->FreePages ( phys_addr, old_pages ) ) != 0 ){ rc = -EEFI ( efirc ); DBG ( "EFI could not free %d pages at %llx: %s\n", diff --git a/src/interface/efi/efi_usb.c b/src/interface/efi/efi_usb.c index 28dfc8680..a3b153c88 100644 --- a/src/interface/efi/efi_usb.c +++ b/src/interface/efi/efi_usb.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <stdlib.h> #include <stdio.h> @@ -1198,7 +1199,7 @@ static void efi_usb_uninstall ( struct efi_usb_interface *usbintf ) { * when uninstalling protocols. */ if ( ! efi_shutdown_in_progress ) - bs->DisconnectController ( usbintf->handle, NULL, NULL ); + efi_disconnect ( usbintf->handle, NULL ); /* Uninstall protocols */ if ( ( ! efi_shutdown_in_progress ) && @@ -1260,7 +1261,6 @@ static void efi_usb_uninstall_all ( struct efi_usb_device *efiusb ) { */ static int efi_usb_probe ( struct usb_function *func, struct usb_configuration_descriptor *config ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct usb_device *usb = func->usb; struct efi_usb_device *usbdev; struct efi_usb_interface *usbintf; @@ -1318,7 +1318,7 @@ static int efi_usb_probe ( struct usb_function *func, /* Connect any external drivers */ list_for_each_entry ( usbintf, &usbdev->interfaces, list ) - bs->ConnectController ( usbintf->handle, NULL, NULL, TRUE ); + efi_connect ( usbintf->handle, NULL ); return 0; diff --git a/src/interface/efi/efi_utils.c b/src/interface/efi/efi_utils.c index 53f82bfe4..a7008afd4 100644 --- a/src/interface/efi/efi_utils.c +++ b/src/interface/efi/efi_utils.c @@ -18,6 +18,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER ); +FILE_SECBOOT ( PERMITTED ); #include <stdio.h> #include <string.h> @@ -45,10 +46,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); int efi_locate_device ( EFI_HANDLE device, EFI_GUID *protocol, EFI_HANDLE *parent, unsigned int skip ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - union { - EFI_DEVICE_PATH_PROTOCOL *path; - void *interface; - } u; + EFI_DEVICE_PATH_PROTOCOL *devpath; EFI_DEVICE_PATH_PROTOCOL *path; EFI_DEVICE_PATH_PROTOCOL *end; size_t len; @@ -56,25 +54,21 @@ int efi_locate_device ( EFI_HANDLE device, EFI_GUID *protocol, int rc; /* Get device path */ - if ( ( efirc = bs->OpenProtocol ( device, - &efi_device_path_protocol_guid, - &u.interface, - efi_image_handle, device, - EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){ - rc = -EEFI ( efirc ); + if ( ( rc = efi_open ( device, &efi_device_path_protocol_guid, + &devpath ) ) != 0 ) { DBGC ( device, "EFIDEV %s cannot open device path: %s\n", efi_handle_name ( device ), strerror ( rc ) ); goto err_open_device_path; } /* Create modifiable copy of device path */ - len = ( efi_path_len ( u.path ) + sizeof ( EFI_DEVICE_PATH_PROTOCOL )); + len = ( efi_path_len ( devpath ) + sizeof ( *end ) ); path = malloc ( len ); if ( ! path ) { rc = -ENOMEM; goto err_alloc_path; } - memcpy ( path, u.path, len ); + memcpy ( path, devpath, len ); /* Locate parent device(s) */ while ( 1 ) { @@ -100,14 +94,9 @@ int efi_locate_device ( EFI_HANDLE device, EFI_GUID *protocol, efi_path_terminate ( end ); } - /* Success */ - rc = 0; - err_locate_protocol: free ( path ); err_alloc_path: - bs->CloseProtocol ( device, &efi_device_path_protocol_guid, - efi_image_handle, device ); err_open_device_path: return rc; } @@ -120,19 +109,12 @@ int efi_locate_device ( EFI_HANDLE device, EFI_GUID *protocol, * @ret rc Return status code */ int efi_child_add ( EFI_HANDLE parent, EFI_HANDLE child ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - void *devpath; - EFI_STATUS efirc; + EFI_DEVICE_PATH_PROTOCOL *devpath; int rc; /* Re-open the device path protocol */ - if ( ( efirc = bs->OpenProtocol ( parent, - &efi_device_path_protocol_guid, - &devpath, - efi_image_handle, child, - EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER - ) ) != 0 ) { - rc = -EEFI ( efirc ); + if ( ( rc = efi_open_by_child ( parent, &efi_device_path_protocol_guid, + child, &devpath ) ) != 0 ) { DBGC ( parent, "EFIDEV %s could not add child", efi_handle_name ( parent ) ); DBGC ( parent, " %s: %s\n", @@ -154,10 +136,8 @@ int efi_child_add ( EFI_HANDLE parent, EFI_HANDLE child ) { * @v child EFI child device handle */ void efi_child_del ( EFI_HANDLE parent, EFI_HANDLE child ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - bs->CloseProtocol ( parent, &efi_device_path_protocol_guid, - efi_image_handle, child ); + efi_close_by_child ( parent, &efi_device_path_protocol_guid, child ); DBGC2 ( parent, "EFIDEV %s removed child", efi_handle_name ( parent ) ); DBGC2 ( parent, " %s\n", efi_handle_name ( child ) ); } @@ -170,8 +150,8 @@ void efi_child_del ( EFI_HANDLE parent, EFI_HANDLE child ) { * @v dev Generic device to fill in * @ret rc Return status code */ -static int efi_pci_info ( EFI_HANDLE device, const char *prefix, - struct device *dev ) { +static int efi_device_info_pci ( EFI_HANDLE device, const char *prefix, + struct device *dev ) { EFI_HANDLE pci_device; struct efi_pci_device efipci; int rc; @@ -211,7 +191,7 @@ void efi_device_info ( EFI_HANDLE device, const char *prefix, int rc; /* Try getting underlying PCI device information */ - if ( ( rc = efi_pci_info ( device, prefix, dev ) ) == 0 ) + if ( ( rc = efi_device_info_pci ( device, prefix, dev ) ) == 0 ) return; /* If we cannot get any underlying device information, fall diff --git a/src/interface/efi/efi_veto.c b/src/interface/efi/efi_veto.c index a3b60d65f..788515dd1 100644 --- a/src/interface/efi/efi_veto.c +++ b/src/interface/efi/efi_veto.c @@ -18,6 +18,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <stddef.h> #include <stdlib.h> @@ -29,6 +30,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/efi/Protocol/DriverBinding.h> #include <ipxe/efi/Protocol/LoadedImage.h> #include <ipxe/efi/Protocol/ComponentName.h> +#include <ipxe/efi/Protocol/ComponentName2.h> #include <ipxe/efi/efi_veto.h> /** @file @@ -46,14 +48,12 @@ struct efi_veto_candidate { * * @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 + * @v name Driver name, 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 ); }; @@ -123,9 +123,7 @@ static int efi_veto_disconnect ( struct efi_veto *veto ) { /* 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 ); + if ( ( rc = efi_disconnect ( handle, driver ) ) != 0 ) { DBGC ( driver, "EFIVETO %s could not disconnect", efi_handle_name ( driver ) ); DBGC ( driver, " %s: %s\n", @@ -154,33 +152,23 @@ static int efi_veto_disconnect ( struct efi_veto *veto ) { 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; - } binding; + EFI_DRIVER_BINDING_PROTOCOL *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 ); + if ( ( rc = efi_open ( driver, &efi_driver_binding_protocol_guid, + &binding ) ) != 0 ) { 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 ) { + binding, NULL ) ) != 0 ) { rc = -EEFI ( efirc ); DBGC ( driver, "EFIVETO %s could not uninstall driver " "binding protocol: %s\n", @@ -394,7 +382,6 @@ static int efi_veto_driver ( struct efi_veto *veto ) { * * @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 @@ -402,7 +389,6 @@ static int efi_veto_driver ( struct efi_veto *veto ) { static int efi_veto_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."; @@ -427,7 +413,6 @@ efi_veto_ip4config ( EFI_DRIVER_BINDING_PROTOCOL *binding __unused, * * @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 @@ -435,7 +420,6 @@ efi_veto_ip4config ( EFI_DRIVER_BINDING_PROTOCOL *binding __unused, 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"; @@ -468,7 +452,6 @@ efi_veto_hp_xhci ( EFI_DRIVER_BINDING_PROTOCOL *binding __unused, * * @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 @@ -476,7 +459,6 @@ efi_veto_hp_xhci ( EFI_DRIVER_BINDING_PROTOCOL *binding __unused, 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."; @@ -494,6 +476,30 @@ efi_veto_vmware_uefipxebc ( EFI_DRIVER_BINDING_PROTOCOL *binding __unused, return 1; } +/** + * Veto Dhcp6Dxe driver + * + * @v binding Driver binding protocol + * @v loaded Loaded image protocol + * @v manufacturer Manufacturer name, if present + * @v name Driver name, if present + * @ret vetoed Driver is to be vetoed + */ +static int efi_veto_dhcp6 ( EFI_DRIVER_BINDING_PROTOCOL *binding __unused, + EFI_LOADED_IMAGE_PROTOCOL *loaded __unused, + const char *manufacturer __unused, + const CHAR16 *name ) { + static const CHAR16 dhcp6[] = L"DHCP6 Protocol Driver"; + + /* Check driver name */ + if ( ! name ) + return 0; + if ( memcmp ( name, dhcp6, sizeof ( dhcp6 ) ) != 0 ) + return 0; + + return 1; +} + /** Driver vetoes */ static struct efi_veto_candidate efi_vetoes[] = { { @@ -508,6 +514,10 @@ static struct efi_veto_candidate efi_vetoes[] = { .name = "VMware UefiPxeBc", .veto = efi_veto_vmware_uefipxebc, }, + { + .name = "Dhcp6", + .veto = efi_veto_dhcp6, + }, }; /** @@ -520,70 +530,56 @@ static struct efi_veto_candidate efi_vetoes[] = { */ 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; + EFI_DRIVER_BINDING_PROTOCOL *binding; + EFI_LOADED_IMAGE_PROTOCOL *loaded; + EFI_COMPONENT_NAME2_PROTOCOL *wtf2; + EFI_COMPONENT_NAME_PROTOCOL *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 */ memset ( veto, 0, sizeof ( *veto ) ); /* 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 ); + if ( ( rc = efi_open ( driver, &efi_driver_binding_protocol_guid, + &binding ) ) != 0 ) { DBGC ( driver, "EFIVETO %s could not open driver binding " "protocol: %s\n", efi_handle_name ( driver ), strerror ( rc ) ); - goto err_binding; + return rc; } - image = binding.binding->ImageHandle; + image = 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 ); + if ( ( rc = efi_open ( image, &efi_loaded_image_protocol_guid, + &loaded ) ) != 0 ) { 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; + return rc; } - /* 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 ) { + /* Open component name protocol, if present */ + if ( ( rc = efi_open ( image, &efi_component_name2_protocol_guid, + &wtf2 ) ) != 0 ) { + /* Ignore failure; is not required to be present */ + } + + /* Open obsolete component name protocol, if present */ + if ( ( rc = efi_open ( image, &efi_component_name_protocol_guid, + &wtf ) ) != 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 ) ) ) { + if ( ( wtf2 && ( ( efirc = wtf2->GetDriverName ( wtf2, "en", + &name ) == 0 ) ) ) || + ( wtf && ( ( efirc = wtf->GetDriverName ( wtf, "eng", + &name ) == 0 ) ) ) ) { /* Driver has a name */ } else { /* Ignore failure; name is not required to be present */ @@ -591,36 +587,25 @@ static int efi_veto_find ( EFI_HANDLE driver, const char *manufacturer, } /* Check vetoes */ + DBGC2 ( &efi_vetoes, "EFIVETO checking %s [%p,%p)\n", + efi_handle_name ( driver ), loaded->ImageBase, + ( loaded->ImageBase + loaded->ImageSize ) ); 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 ) ) { + if ( efi_vetoes[i].veto ( binding, loaded, manufacturer, + name ) ) { DBGC ( driver, "EFIVETO %s is vetoed (%s)\n", efi_handle_name ( driver ), efi_vetoes[i].name ); veto->driver = driver; - veto->binding = binding.binding; + veto->binding = binding; veto->image = image; - veto->loaded = loaded.loaded; + veto->loaded = loaded; 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; + return 0; } /** diff --git a/src/interface/efi/efi_watchdog.c b/src/interface/efi/efi_watchdog.c index dcc9a5668..5e4eb626c 100644 --- a/src/interface/efi/efi_watchdog.c +++ b/src/interface/efi/efi_watchdog.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** * @file diff --git a/src/interface/efi/efi_wrap.c b/src/interface/efi/efi_wrap.c index 5d5d2caee..572d05aac 100644 --- a/src/interface/efi/efi_wrap.c +++ b/src/interface/efi/efi_wrap.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); /** * @file @@ -40,6 +41,30 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Colour for debug messages */ #define colour &efi_systab +/** Maximum size for hex dumps */ +#define EFI_WRAP_DUMP_MAX 128 + +/** Number of lines to prescroll when needed */ +#define EFI_WRAP_PRESCROLL 16 + +/** Public EFI system table pointer */ +static EFI_SYSTEM_TABLE *efi_systab_pub; + +/** Private EFI system table used while wrapping is active */ +static EFI_SYSTEM_TABLE efi_systab_priv; + +/** Original EFI boot services table pointer */ +static EFI_BOOT_SERVICES *efi_bs_orig; + +/** Backup of original EFI boot services table */ +static EFI_BOOT_SERVICES efi_bs_copy; + +/** Original EFI runtime services table pointer */ +static EFI_RUNTIME_SERVICES *efi_rs_orig; + +/** Backup of original EFI runtime services table */ +static EFI_RUNTIME_SERVICES efi_rs_copy; + /** * Convert EFI status code to text * @@ -106,27 +131,6 @@ 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 @@ -196,25 +200,86 @@ static const char * efi_timer_delay ( EFI_TIMER_DELAY type ) { } /** + * Convert EFI time to text + * + * @v time Time, or NULL + * @ret text Time as text + */ +static const char * efi_time ( EFI_TIME *time ) { + static char buf[ 20 /* "xxxx-xx-xx xx:xx:xx" + NUL */ ]; + + if ( ! time ) + return "<NULL>"; + snprintf ( buf, sizeof ( buf ), "%04d-%02d-%02d %02d:%02d:%02d", + time->Year, time->Month, time->Day, time->Hour, + time->Minute, time->Second ); + return buf; +} + +/** + * Convert EFI reset type to text + * + * @v type Reset type + * @ret text Reset type as text + */ +static const char * efi_reset_type ( EFI_RESET_TYPE type ) { + static char buf[ 11 /* "0xXXXXXXXX" + NUL */ ]; + + switch ( type ) { + case EfiResetCold: return "Cold"; + case EfiResetWarm: return "Warm"; + case EfiResetShutdown: return "Shutdown"; + case EfiResetPlatformSpecific: return "PlatformSpecific"; + default: + snprintf ( buf, sizeof ( buf ), "%#x", type ); + return buf; + } +} + +/** + * Pre-scroll display to create space for output lines + * + * @v lines Number of lines required + * @ret efirc EFI status code + */ +static int efi_prescroll ( unsigned int lines ) { + EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *conout = efi_systab->ConOut; + UINTN columns; + UINTN rows; + UINTN space; + EFI_STATUS efirc; + + /* Get number of rows and columns */ + if ( ( efirc = conout->QueryMode ( conout, conout->Mode->Mode, + &columns, &rows ) ) != 0 ) + return efirc; + + /* Calculate available space */ + space = ( rows - conout->Mode->CursorRow - 1 ); + + /* Scroll to create space */ + while ( space++ < lines ) + conout->OutputString ( conout, L"\n" ); + + /* Move cursor to start of space */ + conout->SetCursorPosition ( conout, 0, + ( conout->Mode->CursorRow - lines ) ); + + return 0; +} + +/** * Dump information about a loaded image * * @v handle Image handle */ static void efi_dump_image ( EFI_HANDLE handle ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - union { - EFI_LOADED_IMAGE_PROTOCOL *image; - void *intf; - } loaded; - EFI_STATUS efirc; + EFI_LOADED_IMAGE_PROTOCOL *loaded; int rc; /* Open loaded image protocol */ - if ( ( efirc = bs->OpenProtocol ( handle, - &efi_loaded_image_protocol_guid, - &loaded.intf, efi_image_handle, NULL, - EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){ - rc = -EEFI ( efirc ); + if ( ( rc = efi_open ( handle, &efi_loaded_image_protocol_guid, + &loaded ) ) != 0 ) { DBGC ( colour, "WRAP %s could not get loaded image protocol: " "%s\n", efi_handle_name ( handle ), strerror ( rc ) ); return; @@ -222,18 +287,14 @@ static void efi_dump_image ( EFI_HANDLE handle ) { /* Dump image information */ DBGC ( colour, "WRAP %s at base %p has protocols:\n", - efi_handle_name ( handle ), loaded.image->ImageBase ); + efi_handle_name ( handle ), loaded->ImageBase ); DBGC_EFI_PROTOCOLS ( colour, handle ); DBGC ( colour, "WRAP %s parent", efi_handle_name ( handle ) ); - DBGC ( colour, " %s\n", efi_handle_name ( loaded.image->ParentHandle )); + DBGC ( colour, " %s\n", efi_handle_name ( loaded->ParentHandle ) ); DBGC ( colour, "WRAP %s device", efi_handle_name ( handle ) ); - DBGC ( colour, " %s\n", efi_handle_name ( loaded.image->DeviceHandle )); + DBGC ( colour, " %s\n", efi_handle_name ( loaded->DeviceHandle ) ); DBGC ( colour, "WRAP %s file", efi_handle_name ( handle ) ); - DBGC ( colour, " %s\n", efi_devpath_text ( loaded.image->FilePath ) ); - - /* Close loaded image protocol */ - bs->CloseProtocol ( handle, &efi_loaded_image_protocol_guid, - efi_image_handle, NULL ); + DBGC ( colour, " %s\n", efi_devpath_text ( loaded->FilePath ) ); } /** @@ -246,9 +307,9 @@ efi_raise_tpl_wrapper ( EFI_TPL new_tpl ) { void *retaddr = __builtin_return_address ( 0 ); EFI_TPL old_tpl; - DBGCP ( colour, "RaiseTPL ( %s ) ", efi_tpl ( new_tpl ) ); + DBGCP ( colour, "RaiseTPL ( %s ) ", efi_tpl_name ( new_tpl ) ); old_tpl = bs->RaiseTPL ( new_tpl ); - DBGCP ( colour, "= %s -> %p\n", efi_tpl ( old_tpl ), retaddr ); + DBGCP ( colour, "= %s -> %p\n", efi_tpl_name ( old_tpl ), retaddr ); return old_tpl; } @@ -261,7 +322,7 @@ 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 ) ); + DBGCP ( colour, "RestoreTPL ( %s ) ", efi_tpl_name ( old_tpl ) ); bs->RestoreTPL ( old_tpl ); DBGCP ( colour, "-> %p\n", retaddr ); } @@ -398,8 +459,8 @@ efi_create_event_wrapper ( UINT32 type, EFI_TPL notify_tpl, 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 ); + DBGC ( colour, "CreateEvent ( %#x, %s, %p, %p ) ", type, + efi_tpl_name ( notify_tpl ), notify_function, notify_context ); efirc = bs->CreateEvent ( type, notify_tpl, notify_function, notify_context, event ); DBGC ( colour, "= %s ( %p ) -> %p\n", @@ -478,7 +539,7 @@ efi_close_event_wrapper ( EFI_EVENT event ) { EFI_STATUS efirc; DBGC ( colour, "CloseEvent ( %p ) ", event ); - efirc = bs->SignalEvent ( event ); + efirc = bs->CloseEvent ( event ); DBGC ( colour, "= %s -> %p\n", efi_status ( efirc ), retaddr ); return efirc; } @@ -493,7 +554,7 @@ efi_check_event_wrapper ( EFI_EVENT event ) { EFI_STATUS efirc; DBGCP ( colour, "CheckEvent ( %p ) ", event ); - efirc = bs->SignalEvent ( event ); + efirc = bs->CheckEvent ( event ); DBGCP ( colour, "= %s -> %p\n", efi_status ( efirc ), retaddr ); return efirc; } @@ -783,6 +844,15 @@ efi_exit_boot_services_wrapper ( EFI_HANDLE image_handle, UINTN map_key ) { if ( efirc != 0 ) { DBGC ( colour, "ExitBootServices ( ... ) = %s -> %p\n", efi_status ( efirc ), retaddr ); + /* On some systems, scrolling the output will cause + * the system memory map to change (and so cause + * ExitBootServices() to fail). + * + * After the first failed attempt, prescroll the + * screen to maximise the chance of the subsequent + * attempt succeeding. + */ + efi_prescroll ( EFI_WRAP_PRESCROLL ); } return efirc; } @@ -814,11 +884,11 @@ efi_stall_wrapper ( UINTN microseconds ) { void *retaddr = __builtin_return_address ( 0 ); EFI_STATUS efirc; - DBGC2 ( colour, "Stall ( %ld.%06lds ) ", + DBGCP ( 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 ); + DBGCP ( colour, "= %s -> %p\n", efi_status ( efirc ), retaddr ); return efirc; } @@ -1163,8 +1233,8 @@ efi_create_event_ex_wrapper ( UINT32 type, EFI_TPL notify_tpl, 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 ) ); + type, efi_tpl_name ( 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", @@ -1173,93 +1243,336 @@ efi_create_event_ex_wrapper ( UINT32 type, EFI_TPL notify_tpl, } /** - * Build boot services table wrapper + * Wrap GetTime() + * + */ +static EFI_STATUS EFIAPI +efi_get_time_wrapper ( EFI_TIME *time, EFI_TIME_CAPABILITIES *cap ) { + EFI_RUNTIME_SERVICES *rs = efi_systab->RuntimeServices; + void *retaddr = __builtin_return_address ( 0 ); + EFI_STATUS efirc; + + DBGCP ( colour, "GetTime() " ); + efirc = rs->GetTime ( time, cap ); + DBGCP ( colour, "= %s ( %s ) -> %p\n", + efi_status ( efirc ), efi_time ( time ), retaddr ); + return efirc; +} + +/** + * Wrap SetTime() + * + */ +static EFI_STATUS EFIAPI +efi_set_time_wrapper ( EFI_TIME *time ) { + EFI_RUNTIME_SERVICES *rs = efi_systab->RuntimeServices; + void *retaddr = __builtin_return_address ( 0 ); + EFI_STATUS efirc; + + DBGC ( colour, "SetTime ( %s ) ", efi_time ( time ) ); + efirc = rs->SetTime ( time ); + DBGC ( colour, "= %s -> %p\n", efi_status ( efirc ), retaddr ); + return efirc; +} + +/** + * Wrap GetWakeupTime() * - * @ret bs Wrapped boot services table */ -EFI_BOOT_SERVICES * efi_wrap_bs ( void ) { - static EFI_BOOT_SERVICES efi_bs_wrapper; +static EFI_STATUS EFIAPI +efi_get_wakeup_time_wrapper ( BOOLEAN *enabled, BOOLEAN *pending, + EFI_TIME *time ) { + EFI_RUNTIME_SERVICES *rs = efi_systab->RuntimeServices; + void *retaddr = __builtin_return_address ( 0 ); + EFI_STATUS efirc; + + DBGC ( colour, "GetWakeupTime() " ); + efirc = rs->GetWakeupTime ( enabled, pending, time ); + DBGC ( colour, "= %s ( %s, %s, %s ) -> %p\n", efi_status ( efirc ), + efi_boolean ( *enabled ), efi_boolean ( *pending ), + efi_time ( time ), retaddr ); + return efirc; +} + +/** + * Wrap SetWakeupTime() + * + */ +static EFI_STATUS EFIAPI +efi_set_wakeup_time_wrapper ( BOOLEAN enable, EFI_TIME *time ) { + EFI_RUNTIME_SERVICES *rs = efi_systab->RuntimeServices; + void *retaddr = __builtin_return_address ( 0 ); + EFI_STATUS efirc; + + DBGC ( colour, "SetWakeupTime ( %s, %s ) ", + efi_boolean ( enable ), efi_time ( time ) ); + efirc = rs->SetWakeupTime ( enable, time ); + DBGC ( colour, "= %s -> %p\n", efi_status ( efirc ), retaddr ); + return efirc; +} + +/** + * Wrap GetVariable() + * + */ +static EFI_STATUS EFIAPI +efi_get_variable_wrapper ( CHAR16 *name, EFI_GUID *guid, UINT32 *attrs, + UINTN *len, VOID *data ) { + EFI_RUNTIME_SERVICES *rs = efi_systab->RuntimeServices; + void *retaddr = __builtin_return_address ( 0 ); + size_t dumplen; + EFI_STATUS efirc; + + DBGC ( colour, "GetVariable ( %s:%ls, %#llx ) ", + efi_guid_ntoa ( guid ), name, ( ( unsigned long long ) *len ) ); + efirc = rs->GetVariable ( name, guid, attrs, len, data ); + DBGC ( colour, "= %s ( %#llx", + efi_status ( efirc ), ( ( unsigned long long ) *len ) ); + if ( DBG_EXTRA && ( efirc == 0 ) ) { + dumplen = *len; + if ( dumplen > EFI_WRAP_DUMP_MAX ) + dumplen = EFI_WRAP_DUMP_MAX; + DBGC2 ( colour, ",\n" ); + DBGC2_HD ( colour, data, dumplen ); + if ( dumplen != *len ) + DBGC2 ( colour, "... " ); + } else { + DBGC ( colour, " " ); + } + DBGC ( colour, ") -> %p\n", retaddr ); + return efirc; +} + +/** + * Wrap GetNextVariableName() + * + */ +static EFI_STATUS EFIAPI +efi_get_next_variable_name_wrapper ( UINTN *len, CHAR16 *name, + EFI_GUID *guid ) { + EFI_RUNTIME_SERVICES *rs = efi_systab->RuntimeServices; + void *retaddr = __builtin_return_address ( 0 ); + EFI_STATUS efirc; + + DBGC ( colour, "GetNextVariableName ( %#llx, %s:%ls ) ", + ( ( unsigned long long ) *len ), efi_guid_ntoa ( guid ), name ); + efirc = rs->GetNextVariableName ( len, name, guid ); + DBGC ( colour, "= %s ( %#llx, %s:%ls ) -> %p\n", efi_status ( efirc ), + ( ( unsigned long long ) *len ), efi_guid_ntoa ( guid ), name, + retaddr ); + return efirc; +} + +/** + * Wrap SetVariable() + * + */ +static EFI_STATUS EFIAPI +efi_set_variable_wrapper ( CHAR16 *name, EFI_GUID *guid, UINT32 attrs, + UINTN len, VOID *data ) { + EFI_RUNTIME_SERVICES *rs = efi_systab->RuntimeServices; + void *retaddr = __builtin_return_address ( 0 ); + EFI_STATUS efirc; + + DBGC ( colour, "SetVariable ( %s:%ls, %#02x", + efi_guid_ntoa ( guid ), name, attrs ); + if ( len ) { + DBGC ( colour, ",\n" ); + DBGC_HD ( colour, data, len ); + DBGC ( colour, ") " ); + } else { + DBGC ( colour, ", %#llx, %p ) ", + ( ( unsigned long long ) len ), data ); + } + efirc = rs->SetVariable ( name, guid, attrs, len, data ); + DBGC ( colour, "= %s -> %p\n", efi_status ( efirc ), retaddr ); + return efirc; +} + +/** + * Wrap ResetSystem() + * + */ +static VOID EFIAPI +efi_reset_system_wrapper ( EFI_RESET_TYPE type, EFI_STATUS status, + UINTN len, VOID *data ) { + EFI_RUNTIME_SERVICES *rs = efi_systab->RuntimeServices; + void *retaddr = __builtin_return_address ( 0 ); + + DBGC ( colour, "ResetSystem ( %s, %s, %#llx, %p ) -> %p\n", + efi_reset_type ( type ), efi_status ( status ), + ( ( unsigned long long ) len ), data, retaddr ); + rs->ResetSystem ( type, status, len, data ); +} + +/** + * Wrap a boot services table + * + * @v wrapper Boot services table to wrap + */ +void efi_wrap_bs ( EFI_BOOT_SERVICES *wrapper ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + /* Do nothing unless debugging is enabled */ + if ( ! DBG_LOG ) + return; + /* Build boot services table wrapper */ - memcpy ( &efi_bs_wrapper, bs, sizeof ( 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 + memcpy ( wrapper, bs, sizeof ( *wrapper ) ); + wrapper->RaiseTPL = efi_raise_tpl_wrapper; + wrapper->RestoreTPL = efi_restore_tpl_wrapper; + wrapper->AllocatePages = efi_allocate_pages_wrapper; + wrapper->FreePages = efi_free_pages_wrapper; + wrapper->GetMemoryMap = efi_get_memory_map_wrapper; + wrapper->AllocatePool = efi_allocate_pool_wrapper; + wrapper->FreePool = efi_free_pool_wrapper; + wrapper->CreateEvent = efi_create_event_wrapper; + wrapper->SetTimer = efi_set_timer_wrapper; + wrapper->WaitForEvent = efi_wait_for_event_wrapper; + wrapper->SignalEvent = efi_signal_event_wrapper; + wrapper->CloseEvent = efi_close_event_wrapper; + wrapper->CheckEvent = efi_check_event_wrapper; + wrapper->InstallProtocolInterface = efi_install_protocol_interface_wrapper; - efi_bs_wrapper.ReinstallProtocolInterface + wrapper->ReinstallProtocolInterface = efi_reinstall_protocol_interface_wrapper; - efi_bs_wrapper.UninstallProtocolInterface + 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 + wrapper->HandleProtocol = efi_handle_protocol_wrapper; + wrapper->RegisterProtocolNotify = efi_register_protocol_notify_wrapper; + wrapper->LocateHandle = efi_locate_handle_wrapper; + wrapper->LocateDevicePath = efi_locate_device_path_wrapper; + 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 + wrapper->LoadImage = efi_load_image_wrapper; + wrapper->StartImage = efi_start_image_wrapper; + wrapper->Exit = efi_exit_wrapper; + wrapper->UnloadImage = efi_unload_image_wrapper; + wrapper->ExitBootServices = efi_exit_boot_services_wrapper; + wrapper->GetNextMonotonicCount = efi_get_next_monotonic_count_wrapper; + wrapper->Stall = efi_stall_wrapper; + wrapper->SetWatchdogTimer = efi_set_watchdog_timer_wrapper; + wrapper->ConnectController = efi_connect_controller_wrapper; + wrapper->DisconnectController = efi_disconnect_controller_wrapper; + wrapper->OpenProtocol = efi_open_protocol_wrapper; + wrapper->CloseProtocol = efi_close_protocol_wrapper; + 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 + wrapper->ProtocolsPerHandle = efi_protocols_per_handle_wrapper; + wrapper->LocateHandleBuffer = efi_locate_handle_buffer_wrapper; + wrapper->LocateProtocol = efi_locate_protocol_wrapper; + wrapper->InstallMultipleProtocolInterfaces = efi_install_multiple_protocol_interfaces_wrapper; - efi_bs_wrapper.UninstallMultipleProtocolInterfaces + wrapper->UninstallMultipleProtocolInterfaces = efi_uninstall_multiple_protocol_interfaces_wrapper; - efi_bs_wrapper.CreateEventEx = efi_create_event_ex_wrapper; + wrapper->CreateEventEx = efi_create_event_ex_wrapper; +} - return &efi_bs_wrapper; +/** + * Wrap a runtime services table + * + * @v wrapper Runtime services table to wrap + */ +void efi_wrap_rs ( EFI_RUNTIME_SERVICES *wrapper ) { + EFI_RUNTIME_SERVICES *rs = efi_systab->RuntimeServices; + + /* Do nothing unless debugging is enabled */ + if ( ! DBG_LOG ) + return; + + /* Build boot services table wrapper */ + memcpy ( wrapper, rs, sizeof ( *wrapper ) ); + wrapper->GetTime = efi_get_time_wrapper; + wrapper->SetTime = efi_set_time_wrapper; + wrapper->GetWakeupTime = efi_get_wakeup_time_wrapper; + wrapper->SetWakeupTime = efi_set_wakeup_time_wrapper; + wrapper->GetVariable = efi_get_variable_wrapper; + wrapper->GetNextVariableName = efi_get_next_variable_name_wrapper; + wrapper->SetVariable = efi_set_variable_wrapper; + wrapper->ResetSystem = efi_reset_system_wrapper; } /** - * Wrap the calls made by a loaded image + * Wrap the public EFI system table * - * @v handle Image handle + * @v global Patch global boot services table in-place */ -void efi_wrap ( EFI_HANDLE handle ) { - static EFI_SYSTEM_TABLE efi_systab_copy; +void efi_wrap_systab ( int global ) { + static EFI_BOOT_SERVICES local_bs; + static EFI_RUNTIME_SERVICES local_rs; + EFI_BOOT_SERVICES *bs; + EFI_RUNTIME_SERVICES *rs; /* Do nothing unless debugging is enabled */ if ( ! DBG_LOG ) return; - /* Construct modified system table */ - if ( efi_systab != &efi_systab_copy ) { - memcpy ( &efi_systab_copy, efi_systab, - sizeof ( efi_systab_copy ) ); - efi_systab->BootServices = efi_wrap_bs(); - efi_systab = &efi_systab_copy; + /* Preserve original system and boot services tables */ + if ( ! efi_systab_pub ) { + efi_systab_pub = efi_systab; + efi_bs_orig = efi_systab_pub->BootServices; + efi_rs_orig = efi_systab_pub->RuntimeServices; + memcpy ( &efi_bs_copy, efi_bs_orig, sizeof ( efi_bs_copy ) ); + memcpy ( &efi_rs_copy, efi_rs_orig, sizeof ( efi_rs_copy ) ); + } + + /* Construct and use private system table */ + if ( efi_systab != &efi_systab_priv ) { + memcpy ( &efi_systab_priv, efi_systab_pub, + sizeof ( efi_systab_priv ) ); + efi_systab_priv.BootServices = &efi_bs_copy; + efi_systab_priv.RuntimeServices = &efi_rs_copy; + efi_systab = &efi_systab_priv; } + /* Wrap global or local boot services table as applicable */ + bs = ( global ? efi_bs_orig : &local_bs ); + rs = ( global ? efi_rs_orig : &local_rs ); + efi_wrap_bs ( bs ); + efi_wrap_rs ( rs ); + efi_systab_pub->BootServices = bs; + efi_systab_pub->RuntimeServices = rs; + DBGC ( colour, "WRAP installed %s wrappers\n", + ( global ? "global" : "local" ) ); +} + +/** + * Remove boot services table wrapper + * + */ +void efi_unwrap ( void ) { + + /* Do nothing unless debugging is enabled */ + if ( ! DBG_LOG ) + return; + + /* Do nothing if wrapping was never enabled */ + if ( ! efi_systab_pub ) + return; + + /* Restore original system and boot services tables */ + memcpy ( efi_bs_orig, &efi_bs_copy, sizeof ( *efi_bs_orig ) ); + efi_systab_pub->BootServices = efi_bs_orig; + + /* Switch back to using public system table */ + efi_systab = efi_systab_pub; + DBGC ( colour, "WRAP uninstalled wrappers\n" ); +} + +/** + * Wrap calls made by a newly loaded image + * + * @v handle Image handle + */ +void efi_wrap_image ( EFI_HANDLE handle ) { + + /* Do nothing unless debugging is enabled */ + if ( ! DBG_LOG ) + return; + /* Dump image information */ efi_dump_image ( handle ); + + /* Patch public system table */ + efi_wrap_systab ( 0 ); } diff --git a/src/interface/efi/efiprefix.c b/src/interface/efi/efiprefix.c index 10d8f0bf6..3c095afdc 100644 --- a/src/interface/efi/efiprefix.c +++ b/src/interface/efi/efiprefix.c @@ -18,6 +18,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER ); +FILE_SECBOOT ( PERMITTED ); #include <stdlib.h> #include <errno.h> @@ -81,13 +82,22 @@ EFI_STATUS EFIAPI _efi_start ( EFI_HANDLE image_handle, static void efi_init_application ( void ) { EFI_HANDLE device = efi_loaded_image->DeviceHandle; EFI_DEVICE_PATH_PROTOCOL *devpath = efi_loaded_image_path; + EFI_DEVICE_PATH_PROTOCOL *bootpath; struct uri *uri; /* Set current working URI from device path, if present */ + bootpath = efi_current_boot_path(); + DBGC ( device, "EFI has loaded image device path %s\n", + efi_devpath_text ( devpath ) ); + DBGC ( device, "EFI has boot option device path %s\n", + efi_devpath_text ( bootpath ) ); uri = efi_path_uri ( devpath ); + if ( bootpath && ( ! uri ) ) + uri = efi_path_uri ( bootpath ); if ( uri ) churi ( uri ); uri_put ( uri ); + free ( bootpath ); /* Identify autoboot device, if any */ efi_set_autoboot_ll_addr ( device, devpath ); @@ -98,6 +108,7 @@ static void efi_init_application ( void ) { /** EFI application initialisation function */ struct init_fn efi_init_application_fn __init_fn ( INIT_NORMAL ) = { + .name = "efi", .initialise = efi_init_application, }; diff --git a/src/interface/hyperv/vmbus.c b/src/interface/hyperv/vmbus.c index 86d2a08d7..1c44cab5e 100644 --- a/src/interface/hyperv/vmbus.c +++ b/src/interface/hyperv/vmbus.c @@ -273,11 +273,11 @@ static int vmbus_negotiate_version ( struct hv_hypervisor *hv ) { * @v len Length of data buffer * @ret gpadl GPADL ID, or negative error */ -int vmbus_establish_gpadl ( struct vmbus_device *vmdev, userptr_t data, +int vmbus_establish_gpadl ( struct vmbus_device *vmdev, void *data, size_t len ) { struct hv_hypervisor *hv = vmdev->hv; struct vmbus *vmbus = hv->vmbus; - physaddr_t addr = user_to_phys ( data, 0 ); + physaddr_t addr = virt_to_phys ( data ); unsigned int pfn_count = hv_pfn_count ( addr, len ); struct { struct vmbus_gpadl_header gpadlhdr; @@ -442,7 +442,7 @@ int vmbus_open ( struct vmbus_device *vmdev, memset ( ring, 0, len ); /* Establish GPADL for ring buffer */ - gpadl = vmbus_establish_gpadl ( vmdev, virt_to_user ( ring ), len ); + gpadl = vmbus_establish_gpadl ( vmdev, ring, len ); if ( gpadl < 0 ) { rc = gpadl; goto err_establish; diff --git a/src/interface/linux/linux_acpi.c b/src/interface/linux/linux_acpi.c index e658936f2..21a2e27cc 100644 --- a/src/interface/linux/linux_acpi.c +++ b/src/interface/linux/linux_acpi.c @@ -21,6 +21,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <stdlib.h> #include <stdio.h> +#include <string.h> #include <errno.h> #include <ipxe/linux_api.h> #include <ipxe/linux_sysfs.h> @@ -42,7 +43,7 @@ struct linux_acpi_table { /** Index */ unsigned int index; /** Cached data */ - userptr_t data; + void *data; }; /** List of cached ACPI tables */ @@ -53,9 +54,10 @@ static LIST_HEAD ( linux_acpi_tables ); * * @v signature Requested table signature * @v index Requested index of table with this signature - * @ret table Table, or UNULL if not found + * @ret table Table, or NULL if not found */ -static userptr_t linux_acpi_find ( uint32_t signature, unsigned int index ) { +static const struct acpi_header * linux_acpi_find ( uint32_t signature, + unsigned int index ) { struct linux_acpi_table *table; struct acpi_header *header; union { @@ -101,7 +103,7 @@ static userptr_t linux_acpi_find ( uint32_t signature, unsigned int index ) { filename, strerror ( rc ) ); goto err_read; } - header = user_to_virt ( table->data, 0 ); + header = table->data; if ( ( ( ( size_t ) len ) < sizeof ( *header ) ) || ( ( ( size_t ) len ) < le32_to_cpu ( header->length ) ) ) { rc = -ENOENT; @@ -121,7 +123,7 @@ static userptr_t linux_acpi_find ( uint32_t signature, unsigned int index ) { err_read: free ( table ); err_alloc: - return UNULL; + return NULL; } /** diff --git a/src/interface/linux/linux_api.c b/src/interface/linux/linux_api.c index 21024ede1..459e39fd5 100644 --- a/src/interface/linux/linux_api.c +++ b/src/interface/linux/linux_api.c @@ -225,6 +225,7 @@ int __asmcall linux_poll ( struct pollfd *fds, unsigned int nfds, ret = poll ( fds, nfds, timeout ); if ( ret == -1 ) linux_errno = errno; + return ret; } /** diff --git a/src/interface/linux/linux_pci.c b/src/interface/linux/linux_pci.c index 300844737..a3a0828c1 100644 --- a/src/interface/linux/linux_pci.c +++ b/src/interface/linux/linux_pci.c @@ -188,6 +188,7 @@ int linux_pci_write ( struct pci_device *pci, unsigned long where, return rc; } +PROVIDE_PCIAPI_INLINE ( linux, pci_can_probe ); PROVIDE_PCIAPI_INLINE ( linux, pci_discover ); PROVIDE_PCIAPI_INLINE ( linux, pci_read_config_byte ); PROVIDE_PCIAPI_INLINE ( linux, pci_read_config_word ); diff --git a/src/interface/linux/linux_smbios.c b/src/interface/linux/linux_smbios.c index 981873943..1450fcf1b 100644 --- a/src/interface/linux/linux_smbios.c +++ b/src/interface/linux/linux_smbios.c @@ -19,6 +19,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); +#include <string.h> #include <errno.h> #include <ipxe/linux_api.h> #include <ipxe/linux_sysfs.h> @@ -35,7 +36,7 @@ static const char smbios_entry_filename[] = static const char smbios_filename[] = "/sys/firmware/dmi/tables/DMI"; /** Cache SMBIOS data */ -static userptr_t smbios_data; +static void *smbios_data; /** * Find SMBIOS @@ -46,7 +47,7 @@ static userptr_t smbios_data; static int linux_find_smbios ( struct smbios *smbios ) { struct smbios3_entry *smbios3_entry; struct smbios_entry *smbios_entry; - userptr_t entry; + void *entry; void *data; int len; int rc; @@ -59,7 +60,7 @@ static int linux_find_smbios ( struct smbios *smbios ) { smbios_entry_filename, strerror ( rc ) ); goto err_entry; } - data = user_to_virt ( entry, 0 ); + data = entry; smbios3_entry = data; smbios_entry = data; if ( ( len >= ( ( int ) sizeof ( *smbios3_entry ) ) ) && @@ -98,6 +99,7 @@ static int linux_find_smbios ( struct smbios *smbios ) { return 0; ufree ( smbios_data ); + smbios_data = NULL; err_read: err_version: ufree ( entry ); @@ -116,6 +118,7 @@ static void linux_smbios_shutdown ( int booting __unused ) { /* Free SMBIOS data */ ufree ( smbios_data ); + smbios_data = NULL; } /** SMBIOS shutdown function */ diff --git a/src/interface/linux/linux_sysfs.c b/src/interface/linux/linux_sysfs.c index 4f0027cd4..321824ba9 100644 --- a/src/interface/linux/linux_sysfs.c +++ b/src/interface/linux/linux_sysfs.c @@ -42,8 +42,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); * @v data Data to fill in * @ret len Length read, or negative error */ -int linux_sysfs_read ( const char *filename, userptr_t *data ) { - userptr_t tmp; +int linux_sysfs_read ( const char *filename, void **data ) { + void *tmp; ssize_t read; size_t len; int fd; @@ -59,7 +59,7 @@ int linux_sysfs_read ( const char *filename, userptr_t *data ) { } /* Read file */ - for ( *data = UNULL, len = 0 ; ; len += read ) { + for ( *data = NULL, len = 0 ; ; len += read ) { /* (Re)allocate space */ tmp = urealloc ( *data, ( len + LINUX_SYSFS_BLKSIZE ) ); @@ -70,8 +70,7 @@ int linux_sysfs_read ( const char *filename, userptr_t *data ) { *data = tmp; /* Read from file */ - read = linux_read ( fd, user_to_virt ( *data, len ), - LINUX_SYSFS_BLKSIZE ); + read = linux_read ( fd, ( *data + len ), LINUX_SYSFS_BLKSIZE ); if ( read == 0 ) break; if ( read < 0 ) { diff --git a/src/interface/linux/linux_uaccess.c b/src/interface/linux/linux_uaccess.c index ea2d8057c..7f7f8931b 100644 --- a/src/interface/linux/linux_uaccess.c +++ b/src/interface/linux/linux_uaccess.c @@ -27,12 +27,5 @@ FILE_LICENCE(GPL2_OR_LATER); * */ -PROVIDE_UACCESS_INLINE(linux, user_to_phys); -PROVIDE_UACCESS_INLINE(linux, virt_to_user); -PROVIDE_UACCESS_INLINE(linux, user_to_virt); -PROVIDE_UACCESS_INLINE(linux, userptr_add); -PROVIDE_UACCESS_INLINE(linux, memcpy_user); -PROVIDE_UACCESS_INLINE(linux, memmove_user); -PROVIDE_UACCESS_INLINE(linux, memset_user); -PROVIDE_UACCESS_INLINE(linux, strlen_user); -PROVIDE_UACCESS_INLINE(linux, memchr_user); +PROVIDE_UACCESS_INLINE(linux, phys_to_virt); +PROVIDE_UACCESS_INLINE(linux, virt_to_phys); diff --git a/src/interface/linux/linux_umalloc.c b/src/interface/linux/linux_umalloc.c index a7250fa5b..ab5770e9c 100644 --- a/src/interface/linux/linux_umalloc.c +++ b/src/interface/linux/linux_umalloc.c @@ -31,9 +31,6 @@ FILE_LICENCE(GPL2_OR_LATER); #include <ipxe/linux_api.h> -/** Special address returned for empty allocations */ -#define NOWHERE ((void *)-1) - /** Poison to make the metadata more unique */ #define POISON 0xa5a5a5a5 #define min(a,b) (((a)<(b))?(a):(b)) @@ -47,7 +44,16 @@ struct metadata #define SIZE_MD (sizeof(struct metadata)) -/** Simple realloc which passes most of the work to mmap(), mremap() and munmap() */ +/** + * Reallocate external memory + * + * @v old_ptr Memory previously allocated by umalloc(), or NULL + * @v new_size Requested size + * @ret new_ptr Allocated memory, or NULL + * + * Calling realloc() with a new size of zero is a valid way to free a + * memory block. + */ static void * linux_realloc(void *ptr, size_t size) { struct metadata md = {0, 0}; @@ -136,19 +142,4 @@ static void * linux_realloc(void *ptr, size_t size) return ptr; } -/** - * Reallocate external memory - * - * @v old_ptr Memory previously allocated by umalloc(), or UNULL - * @v new_size Requested size - * @ret new_ptr Allocated memory, or UNULL - * - * Calling realloc() with a new size of zero is a valid way to free a - * memory block. - */ -static userptr_t linux_urealloc(userptr_t old_ptr, size_t new_size) -{ - return (userptr_t)linux_realloc((void *)old_ptr, new_size); -} - -PROVIDE_UMALLOC(linux, urealloc, linux_urealloc); +PROVIDE_UMALLOC(linux, urealloc, linux_realloc); diff --git a/src/interface/smbios/smbios.c b/src/interface/smbios/smbios.c index fdd14499f..a23d9bfa2 100644 --- a/src/interface/smbios/smbios.c +++ b/src/interface/smbios/smbios.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <stdint.h> #include <string.h> @@ -38,26 +39,24 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** SMBIOS entry point descriptor */ static struct smbios smbios = { - .address = UNULL, + .address = NULL, }; /** * Calculate SMBIOS entry point structure checksum * * @v start Start address of region - * @v offset Offset of SMBIOS entry point structure * @v len Length of entry point structure * @ret sum Byte checksum */ -static uint8_t smbios_checksum ( userptr_t start, size_t offset, size_t len ) { - size_t end = ( offset + len ); - uint8_t sum; - uint8_t byte; +static uint8_t smbios_checksum ( const void *start, size_t len ) { + const uint8_t *byte = start; + uint8_t sum = 0; + + /* Compute checksum */ + while ( len-- ) + sum += *(byte++); - for ( sum = 0 ; offset < end ; offset++ ) { - copy_from_user ( &byte, start, offset, sizeof ( byte ) ); - sum += byte; - } return sum; } @@ -66,39 +65,45 @@ static uint8_t smbios_checksum ( userptr_t start, size_t offset, size_t len ) { * * @v start Start address of region to scan * @v len Length of region to scan - * @v entry SMBIOS entry point structure to fill in - * @ret rc Return status code + * @ret entry SMBIOS entry point structure, or NULL if not found */ -int find_smbios_entry ( userptr_t start, size_t len, - struct smbios_entry *entry ) { +const struct smbios_entry * find_smbios_entry ( const void *start, + size_t len ) { static size_t offset = 0; /* Avoid repeated attempts to locate SMBIOS */ + const struct smbios_entry *entry; uint8_t sum; /* Try to find SMBIOS */ for ( ; ( offset + sizeof ( *entry ) ) <= len ; offset += 0x10 ) { - /* Read start of header and verify signature */ - copy_from_user ( entry, start, offset, sizeof ( *entry ) ); + /* Verify signature */ + entry = ( start + offset ); if ( entry->signature != SMBIOS_SIGNATURE ) continue; + /* Verify length */ + if ( ( entry->len < sizeof ( *entry ) ) || + ( ( offset + entry->len ) > len ) ) { + DBGC ( &smbios, "SMBIOS at %#08lx has bad length " + "%#02x\n", virt_to_phys ( entry ), entry->len ); + continue; + } + /* Verify checksum */ - if ( ( sum = smbios_checksum ( start, offset, - entry->len ) ) != 0 ) { - DBG ( "SMBIOS at %08lx has bad checksum %02x\n", - user_to_phys ( start, offset ), sum ); + if ( ( sum = smbios_checksum ( entry, entry->len ) ) != 0 ) { + DBGC ( &smbios, "SMBIOS at %#08lx has bad checksum " + "%#02x\n", virt_to_phys ( entry ), sum ); continue; } /* Fill result structure */ - DBG ( "Found SMBIOS v%d.%d entry point at %08lx\n", - entry->major, entry->minor, - user_to_phys ( start, offset ) ); - return 0; + DBGC ( &smbios, "Found SMBIOS v%d.%d entry point at %#08lx\n", + entry->major, entry->minor, virt_to_phys ( entry ) ); + return entry; } - DBG ( "No SMBIOS found\n" ); - return -ENODEV; + DBGC ( &smbios, "No SMBIOS found\n" ); + return NULL; } /** @@ -106,39 +111,45 @@ int find_smbios_entry ( userptr_t start, size_t len, * * @v start Start address of region to scan * @v len Length of region to scan - * @v entry SMBIOS entry point structure to fill in - * @ret rc Return status code + * @ret entry SMBIOS entry point structure, or NULL if not found */ -int find_smbios3_entry ( userptr_t start, size_t len, - struct smbios3_entry *entry ) { +const struct smbios3_entry * find_smbios3_entry ( const void *start, + size_t len ) { static size_t offset = 0; /* Avoid repeated attempts to locate SMBIOS */ + const struct smbios3_entry *entry; uint8_t sum; /* Try to find SMBIOS */ for ( ; ( offset + sizeof ( *entry ) ) <= len ; offset += 0x10 ) { - /* Read start of header and verify signature */ - copy_from_user ( entry, start, offset, sizeof ( *entry ) ); + /* Verify signature */ + entry = ( start + offset ); if ( entry->signature != SMBIOS3_SIGNATURE ) continue; + /* Verify length */ + if ( ( entry->len < sizeof ( *entry ) ) || + ( ( offset + entry->len ) > len ) ) { + DBGC ( &smbios, "SMBIOS at %#08lx has bad length " + "%#02x\n", virt_to_phys ( entry ), entry->len ); + continue; + } + /* Verify checksum */ - if ( ( sum = smbios_checksum ( start, offset, - entry->len ) ) != 0 ) { - DBG ( "SMBIOS3 at %08lx has bad checksum %02x\n", - user_to_phys ( start, offset ), sum ); + if ( ( sum = smbios_checksum ( entry, entry->len ) ) != 0 ) { + DBGC ( &smbios, "SMBIOS3 at %#08lx has bad checksum " + "%#02x\n", virt_to_phys ( entry ), sum ); continue; } /* Fill result structure */ - DBG ( "Found SMBIOS3 v%d.%d entry point at %08lx\n", - entry->major, entry->minor, - user_to_phys ( start, offset ) ); - return 0; + DBGC ( &smbios, "Found SMBIOS3 v%d.%d entry point at %#08lx\n", + entry->major, entry->minor, virt_to_phys ( entry ) ); + return entry; } - DBG ( "No SMBIOS3 found\n" ); - return -ENODEV; + DBGC ( &smbios, "No SMBIOS3 found\n" ); + return NULL; } /** @@ -148,12 +159,15 @@ int find_smbios3_entry ( userptr_t start, size_t len, * @ret offset Offset to strings terminator, or 0 if not found */ static size_t find_strings_terminator ( size_t offset ) { - size_t max_offset = ( smbios.len - 2 ); - uint16_t nulnul; + const uint16_t *nulnul __attribute__ (( aligned ( 1 ) )); - for ( ; offset <= max_offset ; offset++ ) { - copy_from_user ( &nulnul, smbios.address, offset, 2 ); - if ( nulnul == 0 ) + /* Sanity checks */ + assert ( smbios.address != NULL ); + + /* Check for presence of terminating empty string */ + for ( ; ( offset + sizeof ( *nulnul ) ) <= smbios.len ; offset++ ) { + nulnul = ( smbios.address + offset ); + if ( *nulnul == 0 ) return ( offset + 1 ); } return 0; @@ -164,61 +178,59 @@ static size_t find_strings_terminator ( size_t offset ) { * * @v type Structure type to search for * @v instance Instance of this type of structure - * @v structure SMBIOS structure descriptor to fill in - * @ret rc Return status code + * @ret structure SMBIOS structure header, or NULL if not found */ -int find_smbios_structure ( unsigned int type, unsigned int instance, - struct smbios_structure *structure ) { +const struct smbios_header * smbios_structure ( unsigned int type, + unsigned int instance ) { + const struct smbios_header *structure; unsigned int count = 0; size_t offset = 0; size_t strings_offset; size_t terminator_offset; + size_t strings_len; int rc; /* Find SMBIOS */ - if ( ( smbios.address == UNULL ) && + if ( ( smbios.address == NULL ) && ( ( rc = find_smbios ( &smbios ) ) != 0 ) ) - return rc; - assert ( smbios.address != UNULL ); + return NULL; + assert ( smbios.address != NULL ); /* Scan through list of structures */ - while ( ( ( offset + sizeof ( structure->header ) ) < smbios.len ) && + while ( ( ( offset + sizeof ( *structure ) ) < smbios.len ) && ( ( smbios.count == 0 ) || ( count < smbios.count ) ) ) { - /* Read next SMBIOS structure header */ - copy_from_user ( &structure->header, smbios.address, offset, - sizeof ( structure->header ) ); + /* Access next SMBIOS structure header */ + structure = ( smbios.address + offset ); /* Determine start and extent of strings block */ - strings_offset = ( offset + structure->header.len ); + strings_offset = ( offset + structure->len ); if ( strings_offset > smbios.len ) { - DBG ( "SMBIOS structure at offset %zx with length " - "%x extends beyond SMBIOS\n", offset, - structure->header.len ); - return -ENOENT; + DBGC ( &smbios, "SMBIOS structure at offset %#zx " + "with length %#x extends beyond SMBIOS\n", + offset, structure->len ); + return NULL; } terminator_offset = find_strings_terminator ( strings_offset ); if ( ! terminator_offset ) { - DBG ( "SMBIOS structure at offset %zx has " - "unterminated strings section\n", offset ); - return -ENOENT; + DBGC ( &smbios, "SMBIOS structure at offset %#zx has " + "unterminated strings section\n", offset ); + return NULL; } - structure->strings_len = ( terminator_offset - strings_offset); - - DBG ( "SMBIOS structure at offset %zx has type %d, length %x, " - "strings length %zx\n", offset, structure->header.type, - structure->header.len, structure->strings_len ); + strings_len = ( terminator_offset - strings_offset); + DBGC ( &smbios, "SMBIOS structure at offset %#zx has type %d, " + "length %#x, strings length %#zx\n", offset, + structure->type, structure->len, strings_len ); /* Stop if we have reached an end-of-table marker */ if ( ( smbios.count == 0 ) && - ( structure->header.type == SMBIOS_TYPE_END ) ) + ( structure->type == SMBIOS_TYPE_END ) ) break; /* If this is the structure we want, return */ - if ( ( structure->header.type == type ) && + if ( ( structure->type == type ) && ( instance-- == 0 ) ) { - structure->offset = offset; - return 0; + return structure; } /* Move to next SMBIOS structure */ @@ -226,69 +238,43 @@ int find_smbios_structure ( unsigned int type, unsigned int instance, count++; } - DBG ( "SMBIOS structure type %d not found\n", type ); - return -ENOENT; + DBGC ( &smbios, "SMBIOS structure type %d not found\n", type ); + return NULL; } /** - * Copy SMBIOS structure + * Get indexed string within SMBIOS structure * - * @v structure SMBIOS structure descriptor - * @v data Buffer to hold SMBIOS structure - * @v len Length of buffer - * @ret rc Return status code - */ -int read_smbios_structure ( struct smbios_structure *structure, - void *data, size_t len ) { - - assert ( smbios.address != UNULL ); - - if ( len > structure->header.len ) - len = structure->header.len; - copy_from_user ( data, smbios.address, structure->offset, len ); - return 0; -} - -/** - * Find indexed string within SMBIOS structure - * - * @v structure SMBIOS structure descriptor + * @v structure SMBIOS structure header * @v index String index - * @v data Buffer for string - * @v len Length of string buffer - * @ret rc Length of string, or negative error + * @ret string SMBIOS string, or NULL if not fond */ -int read_smbios_string ( struct smbios_structure *structure, - unsigned int index, void *data, size_t len ) { - size_t strings_start = ( structure->offset + structure->header.len ); - size_t strings_end = ( strings_start + structure->strings_len ); - size_t offset; - size_t string_len; - - assert ( smbios.address != UNULL ); - - /* String numbers start at 1 (0 is used to indicate "no string") */ - if ( ! index ) - return -ENOENT; - - for ( offset = strings_start ; offset < strings_end ; - offset += ( string_len + 1 ) ) { - /* Get string length. This is known safe, since the - * smbios_strings struct is constructed so as to - * always end on a string boundary. +const char * smbios_string ( const struct smbios_header *structure, + unsigned int index ) { + const char *string; + unsigned int i; + size_t len; + + /* Sanity check */ + assert ( smbios.address != NULL ); + + /* Step through strings */ + string = ( ( ( const void * ) structure ) + structure->len ); + for ( i = index ; i-- ; ) { + /* Get string length. This is known safe, since we + * check for the empty-string terminator in + * smbios_structure(). */ - string_len = strlen_user ( smbios.address, offset ); - if ( --index == 0 ) { - /* Copy string, truncating as necessary. */ - if ( len > string_len ) - len = string_len; - copy_from_user ( data, smbios.address, offset, len ); - return string_len; - } + len = strlen ( string ); + if ( ! len ) + break; + if ( i == 0 ) + return string; + string += ( len + 1 /* NUL */ ); } - DBG ( "SMBIOS string index %d not found\n", index ); - return -ENOENT; + DBGC ( &smbios, "SMBIOS string index %d not found\n", index ); + return NULL; } /** @@ -300,10 +286,10 @@ int smbios_version ( void ) { int rc; /* Find SMBIOS */ - if ( ( smbios.address == UNULL ) && + if ( ( smbios.address == NULL ) && ( ( rc = find_smbios ( &smbios ) ) != 0 ) ) return rc; - assert ( smbios.address != UNULL ); + assert ( smbios.address != NULL ); return smbios.version; } @@ -315,5 +301,5 @@ int smbios_version ( void ) { void smbios_clear ( void ) { /* Clear address */ - smbios.address = UNULL; + smbios.address = NULL; } diff --git a/src/interface/smbios/smbios_settings.c b/src/interface/smbios/smbios_settings.c index ec31b43f2..d0ef49d5f 100644 --- a/src/interface/smbios/smbios_settings.c +++ b/src/interface/smbios/smbios_settings.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <stdint.h> #include <string.h> @@ -81,15 +82,17 @@ static int smbios_applies ( struct settings *settings __unused, * @v len Length of buffer * @ret len Length of setting data, or negative error */ -static int smbios_fetch ( struct settings *settings __unused, - struct setting *setting, +static int smbios_fetch ( struct settings *settings, struct setting *setting, void *data, size_t len ) { - struct smbios_structure structure; + const struct smbios_header *structure; unsigned int tag_instance; unsigned int tag_type; unsigned int tag_offset; unsigned int tag_len; - int rc; + const void *src; + size_t src_len; + unsigned int string; + union uuid uuid; /* Split tag into instance, type, offset and length */ tag_instance = ( ( setting->tag >> 24 ) & 0xff ); @@ -98,66 +101,75 @@ static int smbios_fetch ( struct settings *settings __unused, tag_len = ( setting->tag & 0xff ); /* Find SMBIOS structure */ - if ( ( rc = find_smbios_structure ( tag_type, tag_instance, - &structure ) ) != 0 ) - return rc; - - { - uint8_t buf[structure.header.len]; - const void *raw; - union uuid uuid; - unsigned int index; + structure = smbios_structure ( tag_type, tag_instance ); + if ( ! structure ) + return -ENOENT; + src = structure; + src_len = structure->len; + string = 0; - /* Read SMBIOS structure */ - if ( ( rc = read_smbios_structure ( &structure, buf, - sizeof ( buf ) ) ) != 0 ) - return rc; + /* A <length> of zero indicates that the byte at <offset> + * contains a string index. An <offset> of zero indicates + * that the <length> contains a literal string index. + * + * Since the byte at offset zero can never contain a string + * index, and a literal string index can never be zero, the + * combination of both <length> and <offset> being zero + * indicates that the entire structure is to be read. + */ + if ( ( tag_len == 0 ) && ( tag_offset == 0 ) ) { + /* Read whole structure */ + } else if ( ( tag_len == 0 ) || ( tag_offset == 0 ) ) { + /* Read string */ + string = tag_len; + if ( ( string == 0 ) && ( tag_offset < src_len ) ) + string = *( ( uint8_t * ) src + tag_offset ); + src = smbios_string ( structure, string ); + if ( ! src ) + return -ENOENT; + assert ( string > 0 ); + src_len = strlen ( src ); + } else if ( tag_offset > src_len ) { + /* Empty read beyond end of structure */ + src_len = 0; + } else { + /* Read partial structure */ + src += tag_offset; + src_len -= tag_offset; + if ( src_len > tag_len ) + src_len = tag_len; + } - /* A <length> of zero indicates that the byte at - * <offset> contains a string index. An <offset> of - * zero indicates that the <length> contains a literal - * string index. - */ - if ( ( tag_len == 0 ) || ( tag_offset == 0 ) ) { - index = ( ( tag_offset == 0 ) ? - tag_len : buf[tag_offset] ); - if ( ( rc = read_smbios_string ( &structure, index, - data, len ) ) < 0 ) { - return rc; - } - if ( ! setting->type ) - setting->type = &setting_type_string; - return rc; - } + /* Mangle UUIDs if necessary. iPXE treats UUIDs as being in + * network byte order (big-endian). SMBIOS specification + * version 2.6 states that UUIDs are stored with little-endian + * values in the first three fields; earlier versions did not + * specify an endianness. dmidecode assumes that the byte + * order is little-endian if and only if the SMBIOS version is + * 2.6 or higher; we match this behaviour. + */ + if ( ( ( setting->type == &setting_type_uuid ) || + ( setting->type == &setting_type_guid ) ) && + ( src_len == sizeof ( uuid ) ) && + ( smbios_version() >= SMBIOS_VERSION ( 2, 6 ) ) ) { + DBGC ( settings, "SMBIOS detected mangled UUID\n" ); + memcpy ( &uuid, src, sizeof ( uuid ) ); + uuid_mangle ( &uuid ); + src = &uuid; + } - /* Mangle UUIDs if necessary. iPXE treats UUIDs as - * being in network byte order (big-endian). SMBIOS - * specification version 2.6 states that UUIDs are - * stored with little-endian values in the first three - * fields; earlier versions did not specify an - * endianness. dmidecode assumes that the byte order - * is little-endian if and only if the SMBIOS version - * is 2.6 or higher; we match this behaviour. - */ - raw = &buf[tag_offset]; - if ( ( ( setting->type == &setting_type_uuid ) || - ( setting->type == &setting_type_guid ) ) && - ( tag_len == sizeof ( uuid ) ) && - ( smbios_version() >= SMBIOS_VERSION ( 2, 6 ) ) ) { - DBG ( "SMBIOS detected mangled UUID\n" ); - memcpy ( &uuid, &buf[tag_offset], sizeof ( uuid ) ); - uuid_mangle ( &uuid ); - raw = &uuid; - } + /* Return data */ + if ( len > src_len ) + len = src_len; + memcpy ( data, src, len ); - /* Return data */ - if ( len > tag_len ) - len = tag_len; - memcpy ( data, raw, len ); - if ( ! setting->type ) - setting->type = &setting_type_hex; - return tag_len; + /* Set default type */ + if ( ! setting->type ) { + setting->type = ( string ? &setting_type_string : + &setting_type_hex ); } + + return src_len; } /** SMBIOS settings operations */ @@ -177,18 +189,19 @@ static struct settings smbios_settings = { /** Initialise SMBIOS settings */ static void smbios_init ( void ) { + struct settings *settings = &smbios_settings; int rc; - if ( ( rc = register_settings ( &smbios_settings, NULL, - "smbios" ) ) != 0 ) { - DBG ( "SMBIOS could not register settings: %s\n", - strerror ( rc ) ); + if ( ( rc = register_settings ( settings, NULL, "smbios" ) ) != 0 ) { + DBGC ( settings, "SMBIOS could not register settings: %s\n", + strerror ( rc ) ); return; } } /** SMBIOS settings initialiser */ struct init_fn smbios_init_fn __init_fn ( INIT_NORMAL ) = { + .name = "smbios", .initialise = smbios_init, }; diff --git a/src/interface/xen/xenbus.c b/src/interface/xen/xenbus.c index 5dd01dfa3..95bfdf7da 100644 --- a/src/interface/xen/xenbus.c +++ b/src/interface/xen/xenbus.c @@ -22,8 +22,10 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <stdio.h> +#include <string.h> #include <errno.h> #include <ipxe/malloc.h> #include <ipxe/device.h> diff --git a/src/interface/xen/xengrant.c b/src/interface/xen/xengrant.c index 269cd5836..b0a15010b 100644 --- a/src/interface/xen/xengrant.c +++ b/src/interface/xen/xengrant.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <stdint.h> #include <strings.h> diff --git a/src/interface/xen/xenstore.c b/src/interface/xen/xenstore.c index caeb4e934..a076cd046 100644 --- a/src/interface/xen/xenstore.c +++ b/src/interface/xen/xenstore.c @@ -22,6 +22,7 @@ */ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +FILE_SECBOOT ( PERMITTED ); #include <stdint.h> #include <stdarg.h> |
