diff options
| author | Simon Rettberg | 2023-04-04 15:12:41 +0200 |
|---|---|---|
| committer | Simon Rettberg | 2023-04-04 15:12:41 +0200 |
| commit | 5ba496dce11d10198a0eae0c8440dccb256fbf32 (patch) | |
| tree | 549903f1dab893870335a6e4767a4530444d2e83 /src/interface | |
| parent | [vesafb] Map Unicode characters to CP437 if possible (diff) | |
| parent | [tls] Handle fragmented handshake records (diff) | |
| download | ipxe-5ba496dce11d10198a0eae0c8440dccb256fbf32.tar.gz ipxe-5ba496dce11d10198a0eae0c8440dccb256fbf32.tar.xz ipxe-5ba496dce11d10198a0eae0c8440dccb256fbf32.zip | |
Merge branch 'master' into openslx
Diffstat (limited to 'src/interface')
| -rw-r--r-- | src/interface/efi/efi_autoboot.c | 23 | ||||
| -rw-r--r-- | src/interface/efi/efi_autoexec.c | 85 | ||||
| -rw-r--r-- | src/interface/efi/efi_block.c | 4 | ||||
| -rw-r--r-- | src/interface/efi/efi_cachedhcp.c | 17 | ||||
| -rw-r--r-- | src/interface/efi/efi_cmdline.c | 151 | ||||
| -rw-r--r-- | src/interface/efi/efi_entropy.c | 109 | ||||
| -rw-r--r-- | src/interface/efi/efi_file.c | 295 | ||||
| -rw-r--r-- | src/interface/efi/efi_init.c | 5 | ||||
| -rw-r--r-- | src/interface/efi/efi_local.c | 20 | ||||
| -rw-r--r-- | src/interface/efi/efi_null.c | 42 | ||||
| -rw-r--r-- | src/interface/efi/efi_path.c | 114 | ||||
| -rw-r--r-- | src/interface/efi/efi_pci.c | 13 | ||||
| -rw-r--r-- | src/interface/efi/efi_pxe.c | 2 | ||||
| -rw-r--r-- | src/interface/efi/efi_rng.c | 118 | ||||
| -rw-r--r-- | src/interface/efi/efi_snp.c | 183 | ||||
| -rw-r--r-- | src/interface/efi/efi_snp_hii.c | 4 | ||||
| -rw-r--r-- | src/interface/efi/efi_utils.c | 56 | ||||
| -rw-r--r-- | src/interface/efi/efiprefix.c | 8 | ||||
| -rw-r--r-- | src/interface/linux/linux_entropy.c | 20 |
19 files changed, 1054 insertions, 215 deletions
diff --git a/src/interface/efi/efi_autoboot.c b/src/interface/efi/efi_autoboot.c index 08d67f761..a103c2f19 100644 --- a/src/interface/efi/efi_autoboot.c +++ b/src/interface/efi/efi_autoboot.c @@ -25,7 +25,10 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <string.h> #include <errno.h> +#include <ipxe/if_ether.h> +#include <ipxe/vlan.h> #include <ipxe/efi/efi.h> +#include <ipxe/efi/efi_path.h> #include <ipxe/efi/efi_autoboot.h> #include <ipxe/efi/Protocol/SimpleNetwork.h> #include <usr/autoboot.h> @@ -40,9 +43,11 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * Identify autoboot device * * @v device Device handle + * @v path Device path * @ret rc Return status code */ -int efi_set_autoboot_ll_addr ( EFI_HANDLE device ) { +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; @@ -50,6 +55,7 @@ int efi_set_autoboot_ll_addr ( EFI_HANDLE device ) { } 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 */ @@ -66,10 +72,23 @@ int efi_set_autoboot_ll_addr ( EFI_HANDLE device ) { /* Record autoboot device */ mode = snp.snp->Mode; - set_autoboot_ll_addr ( &mode->CurrentAddress, mode->HwAddressSize ); + vlan = efi_path_vlan ( path ); + set_autoboot_ll_addr ( &mode->CurrentAddress, mode->HwAddressSize, + vlan ); DBGC ( device, "EFI %s found autoboot link-layer address:\n", efi_handle_name ( device ) ); DBGC_HDA ( device, 0, &mode->CurrentAddress, mode->HwAddressSize ); + if ( vlan ) { + DBGC ( device, "EFI %s found autoboot VLAN %d\n", + efi_handle_name ( device ), vlan ); + } + + /* Configure automatic VLAN device, if applicable */ + if ( vlan && ( mode->HwAddressSize == ETH_ALEN ) ) { + vlan_auto ( &mode->CurrentAddress, vlan ); + DBGC ( device, "EFI %s configured automatic VLAN %d\n", + efi_handle_name ( device ), vlan ); + } /* Close protocol */ bs->CloseProtocol ( device, &efi_simple_network_protocol_guid, diff --git a/src/interface/efi/efi_autoexec.c b/src/interface/efi/efi_autoexec.c index 79d4a4caf..fb12cef08 100644 --- a/src/interface/efi/efi_autoexec.c +++ b/src/interface/efi/efi_autoexec.c @@ -54,12 +54,14 @@ static void *efi_autoexec; static size_t efi_autoexec_len; /** - * Load autoexec script from filesystem + * Load autoexec script from path within filesystem * * @v device Device handle + * @v path Relative path to image, or NULL to load from root * @ret rc Return status code */ -static int efi_autoexec_filesystem ( EFI_HANDLE device ) { +static int efi_autoexec_filesystem ( EFI_HANDLE device, + EFI_DEVICE_PATH_PROTOCOL *path ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; union { void *interface; @@ -70,13 +72,58 @@ static int efi_autoexec_filesystem ( EFI_HANDLE device ) { CHAR16 name[ sizeof ( efi_autoexec_wname ) / sizeof ( efi_autoexec_wname[0] ) ]; } info; + FILEPATH_DEVICE_PATH *filepath; EFI_FILE_PROTOCOL *root; EFI_FILE_PROTOCOL *file; UINTN size; VOID *data; + unsigned int dirlen; + size_t len; + CHAR16 *wname; EFI_STATUS efirc; int rc; + /* Identify directory */ + if ( path ) { + + /* Check relative device path is a file path */ + if ( ! ( ( path->Type == MEDIA_DEVICE_PATH ) && + ( path->SubType == MEDIA_FILEPATH_DP ) ) ) { + DBGC ( device, "EFI %s image path ", + efi_handle_name ( device ) ); + DBGC ( device, " \"%s\" is not a file path\n", + efi_devpath_text ( path ) ); + rc = -ENOTTY; + goto err_not_filepath; + } + filepath = container_of ( path, FILEPATH_DEVICE_PATH, Header ); + + /* Find length of containing directory */ + dirlen = ( ( ( ( path->Length[1] << 8 ) | path->Length[0] ) + - offsetof ( typeof ( *filepath ), PathName ) ) + / sizeof ( filepath->PathName[0] ) ); + for ( ; dirlen ; dirlen-- ) { + if ( filepath->PathName[ dirlen - 1 ] == L'\\' ) + break; + } + + } else { + + /* Use root directory */ + filepath = NULL; + dirlen = 0; + } + + /* Allocate filename */ + len = ( ( dirlen * sizeof ( wname[0] ) ) + sizeof ( efi_autoexec_wname ) ); + wname = malloc ( len ); + if ( ! wname ) { + rc = -ENOMEM; + goto err_wname; + } + memcpy ( wname, filepath->PathName, ( dirlen * sizeof ( wname[0] ) ) ); + memcpy ( &wname[dirlen], efi_autoexec_wname, sizeof ( efi_autoexec_wname ) ); + /* Open simple file system protocol */ if ( ( efirc = bs->OpenProtocol ( device, &efi_simple_file_system_protocol_guid, @@ -98,12 +145,11 @@ static int efi_autoexec_filesystem ( EFI_HANDLE device ) { } /* Open autoexec script */ - if ( ( efirc = root->Open ( root, &file, efi_autoexec_wname, + if ( ( efirc = root->Open ( root, &file, wname, EFI_FILE_MODE_READ, 0 ) ) != 0 ) { rc = -EEFI ( efirc ); DBGC ( device, "EFI %s has no %ls: %s\n", - efi_handle_name ( device ), efi_autoexec_wname, - strerror ( rc ) ); + efi_handle_name ( device ), wname, strerror ( rc ) ); goto err_open; } @@ -113,8 +159,7 @@ static int efi_autoexec_filesystem ( EFI_HANDLE device ) { &info ) ) != 0 ) { rc = -EEFI ( efirc ); DBGC ( device, "EFI %s could not get %ls info: %s\n", - efi_handle_name ( device ), efi_autoexec_wname, - strerror ( rc ) ); + efi_handle_name ( device ), wname, strerror ( rc ) ); goto err_getinfo; } size = info.info.FileSize; @@ -123,7 +168,7 @@ static int efi_autoexec_filesystem ( EFI_HANDLE device ) { if ( ! size ) { rc = -EINVAL; DBGC ( device, "EFI %s has zero-length %ls\n", - efi_handle_name ( device ), efi_autoexec_wname ); + efi_handle_name ( device ), wname ); goto err_empty; } @@ -132,8 +177,7 @@ static int efi_autoexec_filesystem ( EFI_HANDLE device ) { &data ) ) != 0 ) { rc = -EEFI ( efirc ); DBGC ( device, "EFI %s could not allocate %ls: %s\n", - efi_handle_name ( device ), efi_autoexec_wname, - strerror ( rc ) ); + efi_handle_name ( device ), wname, strerror ( rc ) ); goto err_alloc; } @@ -141,8 +185,7 @@ static int efi_autoexec_filesystem ( EFI_HANDLE device ) { if ( ( efirc = file->Read ( file, &size, data ) ) != 0 ) { rc = -EEFI ( efirc ); DBGC ( device, "EFI %s could not read %ls: %s\n", - efi_handle_name ( device ), efi_autoexec_wname, - strerror ( rc ) ); + efi_handle_name ( device ), wname, strerror ( rc ) ); goto err_read; } @@ -150,8 +193,7 @@ static int efi_autoexec_filesystem ( EFI_HANDLE device ) { efi_autoexec = data; efi_autoexec_len = size; data = NULL; - DBGC ( device, "EFI %s found %ls\n", - efi_handle_name ( device ), efi_autoexec_wname ); + DBGC ( device, "EFI %s found %ls\n", efi_handle_name ( device ), wname ); /* Success */ rc = 0; @@ -169,6 +211,9 @@ static int efi_autoexec_filesystem ( EFI_HANDLE device ) { bs->CloseProtocol ( device, &efi_simple_file_system_protocol_guid, efi_image_handle, device ); err_filesystem: + free ( wname ); + err_wname: + err_not_filepath: return rc; } @@ -345,17 +390,23 @@ static int efi_autoexec_tftp ( EFI_HANDLE device ) { * Load autoexec script * * @v device Device handle + * @v path Image path within device handle * @ret rc Return status code */ -int efi_autoexec_load ( EFI_HANDLE device ) { +int efi_autoexec_load ( EFI_HANDLE device, + EFI_DEVICE_PATH_PROTOCOL *path ) { int rc; /* Sanity check */ assert ( efi_autoexec == NULL ); assert ( efi_autoexec_len == 0 ); - /* Try loading from file system, if supported */ - if ( ( rc = efi_autoexec_filesystem ( device ) ) == 0 ) + /* Try loading from file system loaded image directory, if supported */ + if ( ( rc = efi_autoexec_filesystem ( device, path ) ) == 0 ) + return 0; + + /* Try loading from file system root directory, if supported */ + if ( ( rc = efi_autoexec_filesystem ( device, NULL ) ) == 0 ) return 0; /* Try loading via TFTP, if supported */ diff --git a/src/interface/efi/efi_block.c b/src/interface/efi/efi_block.c index 18b1504e7..da9062105 100644 --- a/src/interface/efi/efi_block.c +++ b/src/interface/efi/efi_block.c @@ -583,9 +583,7 @@ static int efi_block_boot_image ( struct san_device *sandev, EFI_HANDLE handle, sizeof ( efi_block_boot_filename ) ); } end = ( ( ( void * ) filepath ) + filepath_len ); - end->Type = END_DEVICE_PATH_TYPE; - end->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE; - end->Length[0] = sizeof ( *end ); + efi_path_terminate ( end ); DBGC ( sandev, "EFIBLK %#02x trying to load %s\n", sandev->drive, efi_devpath_text ( boot_path ) ); diff --git a/src/interface/efi/efi_cachedhcp.c b/src/interface/efi/efi_cachedhcp.c index 1d4b98fd6..b9e49cf48 100644 --- a/src/interface/efi/efi_cachedhcp.c +++ b/src/interface/efi/efi_cachedhcp.c @@ -27,6 +27,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <errno.h> #include <ipxe/cachedhcp.h> #include <ipxe/efi/efi.h> +#include <ipxe/efi/efi_path.h> #include <ipxe/efi/efi_cachedhcp.h> #include <ipxe/efi/Protocol/PxeBaseCode.h> @@ -40,10 +41,13 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * Record cached DHCP packet * * @v device Device handle + * @v path Device path * @ret rc Return status code */ -int efi_cachedhcp_record ( EFI_HANDLE device ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; +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; @@ -52,6 +56,9 @@ int efi_cachedhcp_record ( EFI_HANDLE device ) { 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, @@ -75,7 +82,7 @@ int efi_cachedhcp_record ( EFI_HANDLE device ) { /* Record DHCPACK, if present */ if ( mode->DhcpAckReceived && - ( ( rc = cachedhcp_record ( &cached_dhcpack, + ( ( rc = cachedhcp_record ( &cached_dhcpack, vlan, virt_to_user ( &mode->DhcpAck ), sizeof ( mode->DhcpAck ) ) ) != 0 ) ) { DBGC ( device, "EFI %s could not record DHCPACK: %s\n", @@ -85,7 +92,7 @@ int efi_cachedhcp_record ( EFI_HANDLE device ) { /* Record ProxyDHCPOFFER, if present */ if ( mode->ProxyOfferReceived && - ( ( rc = cachedhcp_record ( &cached_proxydhcp, + ( ( rc = cachedhcp_record ( &cached_proxydhcp, vlan, virt_to_user ( &mode->ProxyOffer ), sizeof ( mode->ProxyOffer ) ) ) != 0)){ DBGC ( device, "EFI %s could not record ProxyDHCPOFFER: %s\n", @@ -95,7 +102,7 @@ int efi_cachedhcp_record ( EFI_HANDLE device ) { /* Record PxeBSACK, if present */ if ( mode->PxeReplyReceived && - ( ( rc = cachedhcp_record ( &cached_pxebs, + ( ( rc = cachedhcp_record ( &cached_pxebs, vlan, virt_to_user ( &mode->PxeReply ), sizeof ( mode->PxeReply ) ) ) != 0)){ DBGC ( device, "EFI %s could not record PXEBSACK: %s\n", diff --git a/src/interface/efi/efi_cmdline.c b/src/interface/efi/efi_cmdline.c new file mode 100644 index 000000000..b33bebd8c --- /dev/null +++ b/src/interface/efi/efi_cmdline.c @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2023 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * EFI command line + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <errno.h> +#include <ipxe/init.h> +#include <ipxe/image.h> +#include <ipxe/script.h> +#include <ipxe/efi/efi.h> +#include <ipxe/efi/efi_cmdline.h> + +/** EFI command line (may not be wNUL-terminated */ +const wchar_t *efi_cmdline; + +/** Length of EFI command line (in bytes) */ +size_t efi_cmdline_len; + +/** Internal copy of the command line */ +static char *efi_cmdline_copy; + +/** + * Free command line image + * + * @v refcnt Reference count + */ +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 ( efi_cmdline_copy ); +} + +/** Embedded script representing the command line */ +static struct image efi_cmdline_image = { + .refcnt = REF_INIT ( efi_cmdline_free ), + .name = "<CMDLINE>", + .type = &script_image_type, +}; + +/** Colour for debug messages */ +#define colour &efi_cmdline_image + +/** + * Initialise EFI command line + * + * @ret rc Return status code + */ +static int efi_cmdline_init ( void ) { + char *cmdline; + size_t len; + int rc; + + /* Do nothing if no command line was specified */ + if ( ! efi_cmdline_len ) { + DBGC ( colour, "CMDLINE found no command line\n" ); + return 0; + } + + /* Allocate ASCII copy of command line */ + len = ( ( efi_cmdline_len / sizeof ( efi_cmdline[0] ) ) + 1 /* NUL */ ); + efi_cmdline_copy = malloc ( len ); + if ( ! efi_cmdline_copy ) { + rc = -ENOMEM; + goto err_alloc; + } + cmdline = efi_cmdline_copy; + snprintf ( cmdline, len, "%ls", efi_cmdline ); + DBGC ( colour, "CMDLINE found command line \"%s\"\n", cmdline ); + + /* Mark command line as consumed */ + efi_cmdline_len = 0; + + /* Strip image name and surrounding whitespace */ + while ( isspace ( *cmdline ) ) + cmdline++; + while ( *cmdline && ( ! isspace ( *cmdline ) ) ) + cmdline++; + while ( isspace ( *cmdline ) ) + cmdline++; + DBGC ( colour, "CMDLINE using command line \"%s\"\n", cmdline ); + + /* Prepare and register image */ + efi_cmdline_image.data = virt_to_user ( cmdline ); + efi_cmdline_image.len = strlen ( cmdline ); + if ( efi_cmdline_image.len && + ( ( rc = register_image ( &efi_cmdline_image ) ) != 0 ) ) { + DBGC ( colour, "CMDLINE could not register command line: %s\n", + strerror ( rc ) ); + goto err_register_image; + } + + /* Drop our reference to the image */ + image_put ( &efi_cmdline_image ); + + return 0; + + err_register_image: + image_put ( &efi_cmdline_image ); + err_alloc: + return rc; +} + +/** + * EFI command line startup function + * + */ +static void efi_cmdline_startup ( void ) { + int rc; + + /* Initialise command line */ + if ( ( rc = efi_cmdline_init() ) != 0 ) { + /* No way to report failure */ + return; + } +} + +/** Command line and initrd initialisation function */ +struct startup_fn efi_cmdline_startup_fn __startup_fn ( STARTUP_NORMAL ) = { + .name = "efi_cmdline", + .startup = efi_cmdline_startup, +}; diff --git a/src/interface/efi/efi_entropy.c b/src/interface/efi/efi_entropy.c index 71341d9d4..8e55c77b1 100644 --- a/src/interface/efi/efi_entropy.c +++ b/src/interface/efi/efi_entropy.c @@ -25,10 +25,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <errno.h> #include <ipxe/entropy.h> -#include <ipxe/crc32.h> #include <ipxe/profile.h> #include <ipxe/efi/efi.h> -#include <ipxe/efi/Protocol/Rng.h> #if defined(__i386) || defined(__x86_64) #include <ipxe/cpuid.h> @@ -41,22 +39,7 @@ static char have_hwrnd = 0; * */ -/** Random number generator protocol */ -static EFI_RNG_PROTOCOL *efirng; -EFI_REQUEST_PROTOCOL ( EFI_RNG_PROTOCOL, &efirng ); - -/** Minimum number of bytes to request from RNG - * - * The UEFI spec states (for no apparently good reason) that "When a - * Deterministic Random Bit Generator (DRBG) is used on the output of - * a (raw) entropy source, its security level must be at least 256 - * bits." The EDK2 codebase (mis)interprets this to mean that the - * call to GetRNG() should fail if given a buffer less than 32 bytes. - * - * Incidentally, nothing in the EFI RNG protocol provides any way to - * report the actual amount of entropy returned by GetRNG(). - */ -#define EFI_ENTROPY_RNG_LEN 32 +struct entropy_source efitick_entropy __entropy_source ( ENTROPY_FALLBACK ); /** Time (in 100ns units) to delay waiting for timer tick * @@ -81,9 +64,6 @@ static int efi_entropy_enable ( void ) { EFI_STATUS efirc; int rc; - DBGC ( &tick, "ENTROPY %s RNG protocol\n", - ( efirng ? "has" : "has no" ) ); - /* Drop to external TPL to allow timer tick event to take place */ bs->RestoreTPL ( efi_external_tpl ); @@ -96,6 +76,12 @@ static int efi_entropy_enable ( void ) { return rc; } + /* We use essentially the same mechanism as for the BIOS + * RTC-based entropy source, and so assume the same + * min-entropy per sample. + */ + entropy_init ( &efitick_entropy, MIN_ENTROPY ( 1.3 ) ); + return 0; } @@ -190,11 +176,15 @@ static int efi_get_noise_rdrand ( noise_sample_t *noise ) { * @ret noise Noise sample * @ret rc Return status code */ -static int efi_get_noise_ticks ( noise_sample_t *noise ) { +static int efi_get_noise ( noise_sample_t *noise ) { int before; int after; int rc; + rc = efi_get_noise_rdrand ( noise ); + if ( rc == 0 ) + return 0; + /* Wait for a timer tick */ before = efi_entropy_tick(); if ( before < 0 ) { @@ -215,71 +205,10 @@ static int efi_get_noise_ticks ( noise_sample_t *noise ) { return 0; } -/** - * Get noise sample from RNG protocol - * - * @ret noise Noise sample - * @ret rc Return status code - */ -static int efi_get_noise_rng ( noise_sample_t *noise ) { - static uint8_t prev[EFI_ENTROPY_RNG_LEN]; - uint8_t buf[EFI_ENTROPY_RNG_LEN]; - EFI_STATUS efirc; - int rc; - - /* Fail if we have no EFI RNG protocol */ - if ( ! efirng ) - return -ENOTSUP; - - /* Get the minimum allowed number of random bytes */ - if ( ( efirc = efirng->GetRNG ( efirng, NULL, EFI_ENTROPY_RNG_LEN, - buf ) ) != 0 ) { - rc = -EEFI ( efirc ); - DBGC ( &tick, "ENTROPY could not read from RNG: %s\n", - strerror ( rc ) ); - return rc; - } - - /* Fail (and permanently disable the EFI RNG) if we get - * consecutive identical results. - */ - if ( memcmp ( buf, prev, sizeof ( buf ) ) == 0 ) { - DBGC ( &tick, "ENTROPY detected broken EFI RNG:\n" ); - DBGC_HDA ( &tick, 0, buf, sizeof ( buf ) ); - efirng = NULL; - return -EIO; - } - memcpy ( prev, buf, sizeof ( prev ) ); - - /* 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; -} - -/** - * Get noise sample - * - * @ret noise Noise sample - * @ret rc Return status code - */ -static int efi_get_noise ( noise_sample_t *noise ) { - int rc; - - /* Try RNG first, falling back to timer ticks */ - if ( ( ( rc = efi_get_noise_rng ( noise ) ) != 0 ) && - ( ( rc = efi_get_noise_rdrand ( noise ) ) != 0 ) && - ( ( rc = efi_get_noise_ticks ( noise ) ) != 0 ) ) - return rc; - - return 0; -} - -PROVIDE_ENTROPY_INLINE ( efi, min_entropy_per_sample ); -PROVIDE_ENTROPY ( efi, entropy_enable, efi_entropy_enable ); -PROVIDE_ENTROPY ( efi, entropy_disable, efi_entropy_disable ); -PROVIDE_ENTROPY ( efi, get_noise, efi_get_noise ); +/** EFI entropy source */ +struct entropy_source efitick_entropy __entropy_source ( ENTROPY_FALLBACK ) = { + .name = "efitick", + .enable = efi_entropy_enable, + .disable = efi_entropy_disable, + .get_noise = efi_get_noise, +}; diff --git a/src/interface/efi/efi_file.c b/src/interface/efi/efi_file.c index fc64b369c..b232591dc 100644 --- a/src/interface/efi/efi_file.c +++ b/src/interface/efi/efi_file.c @@ -43,14 +43,21 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/efi/Protocol/SimpleFileSystem.h> #include <ipxe/efi/Protocol/BlockIo.h> #include <ipxe/efi/Protocol/DiskIo.h> +#include <ipxe/efi/Protocol/LoadFile2.h> #include <ipxe/efi/Guid/FileInfo.h> #include <ipxe/efi/Guid/FileSystemInfo.h> #include <ipxe/efi/efi_strings.h> +#include <ipxe/efi/efi_path.h> #include <ipxe/efi/efi_file.h> /** EFI media ID */ #define EFI_MEDIA_ID_MAGIC 0x69505845 +/** Linux initrd fixed device path vendor GUID */ +#define LINUX_INITRD_VENDOR_GUID \ + { 0x5568e427, 0x68fc, 0x4f3d, \ + { 0xac, 0x74, 0xca, 0x55, 0x52, 0x31, 0xcc, 0x68 } } + /** An EFI virtual file reader */ struct efi_file_reader { /** EFI file */ @@ -69,6 +76,8 @@ struct efi_file { struct refcnt refcnt; /** EFI file protocol */ EFI_FILE_PROTOCOL file; + /** EFI load file protocol */ + EFI_LOAD_FILE2_PROTOCOL load; /** Image (if any) */ struct image *image; /** Filename */ @@ -84,8 +93,18 @@ struct efi_file { size_t ( * read ) ( struct efi_file_reader *reader ); }; +/** An EFI fixed device path file */ +struct efi_file_path { + /** EFI file */ + struct efi_file file; + /** Device path */ + EFI_DEVICE_PATH_PROTOCOL *path; + /** EFI handle */ + EFI_HANDLE handle; +}; + static struct efi_file efi_file_root; -static struct efi_file efi_file_initrd; +static struct efi_file_path efi_file_initrd; /** * Free EFI file @@ -121,7 +140,7 @@ static struct image * efi_file_find ( const char *name ) { struct image *image; /* Find image */ - list_for_each_entry ( image, &images, list ) { + for_each_image ( image ) { if ( strcasecmp ( image->name, name ) == 0 ) return image; } @@ -231,10 +250,6 @@ static size_t efi_file_read_initrd ( struct efi_file_reader *reader ) { len = 0; for_each_image ( image ) { - /* Ignore currently executing image */ - if ( image == current_image ) - continue; - /* Pad to alignment boundary */ pad_len = ( ( -reader->pos ) & ( INITRD_ALIGN - 1 ) ); if ( pad_len ) { @@ -353,8 +368,8 @@ efi_file_open ( EFI_FILE_PROTOCOL *this, EFI_FILE_PROTOCOL **new, } /* Allow magic initrd to be opened */ - if ( strcasecmp ( name, efi_file_initrd.name ) == 0 ) - return efi_file_open_fixed ( &efi_file_initrd, new ); + if ( strcasecmp ( name, efi_file_initrd.file.name ) == 0 ) + return efi_file_open_fixed ( &efi_file_initrd.file, new ); /* Identify image */ image = efi_file_find ( name ); @@ -370,6 +385,8 @@ efi_file_open ( EFI_FILE_PROTOCOL *this, EFI_FILE_PROTOCOL **new, ref_init ( &file->refcnt, efi_file_free ); memcpy ( &new_file->file, &efi_file_root.file, sizeof ( new_file->file ) ); + memcpy ( &new_file->load, &efi_file_root.load, + sizeof ( new_file->load ) ); efi_file_image ( new_file, image_get ( image ) ); *new = &new_file->file; DBGC ( new_file, "EFIFILE %s opened\n", efi_file_name ( new_file ) ); @@ -528,7 +545,7 @@ static EFI_STATUS EFIAPI efi_file_read ( EFI_FILE_PROTOCOL *this, /* Read from the file */ DBGC ( file, "EFIFILE %s read [%#08zx,%#08zx)\n", - efi_file_name ( file ), pos, file->pos ); + efi_file_name ( file ), pos, ( ( size_t ) ( pos + *len ) ) ); *len = file->read ( &reader ); assert ( ( pos + *len ) == file->pos ); @@ -684,6 +701,44 @@ static EFI_STATUS EFIAPI efi_file_flush ( EFI_FILE_PROTOCOL *this ) { return 0; } +/** + * Load file + * + * @v this EFI file loader + * @v path File path + * @v boot Boot policy + * @v len Buffer size + * @v data Buffer, or NULL + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI +efi_file_load ( EFI_LOAD_FILE2_PROTOCOL *this, + EFI_DEVICE_PATH_PROTOCOL *path __unused, + BOOLEAN boot __unused, UINTN *len, VOID *data ) { + struct efi_file *file = container_of ( this, struct efi_file, load ); + size_t max_len; + size_t file_len; + EFI_STATUS efirc; + + /* Calculate maximum length */ + max_len = ( data ? *len : 0 ); + DBGC ( file, "EFIFILE %s load at %p+%#zx\n", + efi_file_name ( file ), data, max_len ); + + /* Check buffer size */ + file_len = efi_file_len ( file ); + if ( file_len > max_len ) { + *len = file_len; + return EFI_BUFFER_TOO_SMALL; + } + + /* Read from file */ + if ( ( efirc = efi_file_read ( &file->file, len, data ) ) != 0 ) + return efirc; + + return 0; +} + /** Root directory */ static struct efi_file efi_file_root = { .refcnt = REF_INIT ( ref_no_free ), @@ -700,29 +755,58 @@ static struct efi_file efi_file_root = { .SetInfo = efi_file_set_info, .Flush = efi_file_flush, }, + .load = { + .LoadFile = efi_file_load, + }, .image = NULL, .name = "", }; +/** Linux initrd fixed device path */ +static struct { + VENDOR_DEVICE_PATH vendor; + EFI_DEVICE_PATH_PROTOCOL end; +} __attribute__ (( packed )) efi_file_initrd_path = { + .vendor = { + .Header = { + .Type = MEDIA_DEVICE_PATH, + .SubType = MEDIA_VENDOR_DP, + .Length[0] = sizeof ( efi_file_initrd_path.vendor ), + }, + .Guid = LINUX_INITRD_VENDOR_GUID, + }, + .end = { + .Type = END_DEVICE_PATH_TYPE, + .SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE, + .Length[0] = sizeof ( efi_file_initrd_path.end ), + }, +}; + /** Magic initrd file */ -static struct efi_file efi_file_initrd = { - .refcnt = REF_INIT ( ref_no_free ), +static struct efi_file_path efi_file_initrd = { .file = { - .Revision = EFI_FILE_PROTOCOL_REVISION, - .Open = efi_file_open, - .Close = efi_file_close, - .Delete = efi_file_delete, - .Read = efi_file_read, - .Write = efi_file_write, - .GetPosition = efi_file_get_position, - .SetPosition = efi_file_set_position, - .GetInfo = efi_file_get_info, - .SetInfo = efi_file_set_info, - .Flush = efi_file_flush, + .refcnt = REF_INIT ( ref_no_free ), + .file = { + .Revision = EFI_FILE_PROTOCOL_REVISION, + .Open = efi_file_open, + .Close = efi_file_close, + .Delete = efi_file_delete, + .Read = efi_file_read, + .Write = efi_file_write, + .GetPosition = efi_file_get_position, + .SetPosition = efi_file_set_position, + .GetInfo = efi_file_get_info, + .SetInfo = efi_file_set_info, + .Flush = efi_file_flush, + }, + .load = { + .LoadFile = efi_file_load, + }, + .image = NULL, + .name = "initrd.magic", + .read = efi_file_read_initrd, }, - .image = NULL, - .name = "initrd.magic", - .read = efi_file_read_initrd, + .path = &efi_file_initrd_path.vendor.Header, }; /** @@ -834,6 +918,151 @@ static EFI_DISK_IO_PROTOCOL efi_disk_io_protocol = { }; /** + * Claim use of fixed device path + * + * @v file Fixed device path file + * @ret rc Return status code + * + * The design choice in Linux of using a single fixed device path is + * unfortunately messy to support, since device paths must be unique + * within a system. When multiple bootloaders are used (e.g. GRUB + * loading iPXE loading Linux) then only one bootloader can ever + * install the device path onto a handle. Bootloaders must therefore + * be prepared to locate an existing handle and uninstall its device + * path protocol instance before installing a new handle with the + * required device path. + */ +static int efi_file_path_claim ( struct efi_file_path *file ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_DEVICE_PATH_PROTOCOL *end; + EFI_HANDLE handle; + VOID *old; + EFI_STATUS efirc; + int rc; + + /* Sanity check */ + assert ( file->handle == NULL ); + + /* Locate handle with this device path, if any */ + end = file->path; + if ( ( ( efirc = bs->LocateDevicePath ( &efi_device_path_protocol_guid, + &end, &handle ) ) != 0 ) || + ( end->Type != END_DEVICE_PATH_TYPE ) ) { + return 0; + } + + /* Locate device path protocol on this handle */ + if ( ( ( efirc = bs->HandleProtocol ( handle, + &efi_device_path_protocol_guid, + &old ) ) != 0 ) ) { + rc = -EEFI ( efirc ); + DBGC ( file, "EFIFILE %s could not locate %s: %s\n", + efi_file_name ( &file->file ), + efi_devpath_text ( file->path ), strerror ( rc ) ); + return rc; + } + + /* Uninstall device path protocol, leaving other protocols untouched */ + if ( ( efirc = bs->UninstallMultipleProtocolInterfaces ( + handle, + &efi_device_path_protocol_guid, old, + NULL ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( file, "EFIFILE %s could not claim %s: %s\n", + efi_file_name ( &file->file ), + efi_devpath_text ( file->path ), strerror ( rc ) ); + return rc; + } + + DBGC ( file, "EFIFILE %s claimed %s", + efi_file_name ( &file->file ), efi_devpath_text ( file->path ) ); + DBGC ( file, " from %s\n", efi_handle_name ( handle ) ); + return 0; +} + +/** + * Install fixed device path file + * + * @v file Fixed device path file + * @ret rc Return status code + * + * Linux 5.7 added the ability to autodetect an initrd by searching + * for a handle via a fixed vendor-specific "Linux initrd device path" + * and then locating and using the EFI_LOAD_FILE2_PROTOCOL instance on + * that handle. + */ +static int efi_file_path_install ( struct efi_file_path *file ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_STATUS efirc; + int rc; + + /* Sanity check */ + assert ( file->handle == NULL ); + + /* Create a new handle with this device path */ + if ( ( efirc = bs->InstallMultipleProtocolInterfaces ( + &file->handle, + &efi_device_path_protocol_guid, file->path, + &efi_load_file2_protocol_guid, &file->file.load, + NULL ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( file, "EFIFILE %s could not install %s: %s\n", + efi_file_name ( &file->file ), + efi_devpath_text ( file->path ), strerror ( rc ) ); + return rc; + } + + DBGC ( file, "EFIFILE %s installed as %s\n", + efi_file_name ( &file->file ), efi_devpath_text ( file->path ) ); + return 0; +} + +/** + * Uninstall fixed device path file + * + * @v file Fixed device path file + * @ret rc Return status code + */ +static void efi_file_path_uninstall ( struct efi_file_path *file ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_STATUS efirc; + int rc; + + /* Do nothing if file is already uninstalled */ + if ( ! file->handle ) + return; + + /* Uninstall protocols. Do this via two separate calls, in + * case another executable has already uninstalled the device + * path protocol from our handle. + */ + if ( ( efirc = bs->UninstallMultipleProtocolInterfaces ( + file->handle, + &efi_device_path_protocol_guid, file->path, + NULL ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( file, "EFIFILE %s could not uninstall %s: %s\n", + efi_file_name ( &file->file ), + efi_devpath_text ( file->path ), strerror ( rc ) ); + /* Continue uninstalling */ + } + if ( ( efirc = bs->UninstallMultipleProtocolInterfaces ( + file->handle, + &efi_load_file2_protocol_guid, &file->file.load, + NULL ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( file, "EFIFILE %s could not uninstall %s: %s\n", + efi_file_name ( &file->file ), + efi_guid_ntoa ( &efi_load_file2_protocol_guid ), + strerror ( rc ) ); + /* Continue uninstalling */ + } + + /* Mark handle as uninstalled */ + file->handle = NULL; +} + +/** * Install EFI simple file system protocol * * @v handle EFI handle @@ -903,8 +1132,21 @@ int efi_file_install ( EFI_HANDLE handle ) { } assert ( diskio.diskio == &efi_disk_io_protocol ); + /* Claim Linux initrd fixed device path */ + if ( ( rc = efi_file_path_claim ( &efi_file_initrd ) ) != 0 ) + goto err_initrd_claim; + + /* Install Linux initrd fixed device path file if non-empty */ + if ( have_images() && + ( ( rc = efi_file_path_install ( &efi_file_initrd ) ) != 0 ) ) { + goto err_initrd_install; + } + return 0; + 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 ); err_open: @@ -930,6 +1172,9 @@ void efi_file_uninstall ( EFI_HANDLE handle ) { EFI_STATUS efirc; int rc; + /* Uninstall Linux initrd fixed device path file */ + 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 ); diff --git a/src/interface/efi/efi_init.c b/src/interface/efi/efi_init.c index 5d98f9ff7..d3c5042d7 100644 --- a/src/interface/efi/efi_init.c +++ b/src/interface/efi/efi_init.c @@ -27,6 +27,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <ipxe/efi/efi.h> #include <ipxe/efi/efi_driver.h> #include <ipxe/efi/efi_path.h> +#include <ipxe/efi/efi_cmdline.h> #include <ipxe/efi/Protocol/LoadedImage.h> /** Image handle passed to entry point */ @@ -254,6 +255,10 @@ EFI_STATUS efi_init ( EFI_HANDLE image_handle, DBGC ( systab, "EFI image base address %p\n", efi_loaded_image->ImageBase ); + /* Record command line */ + efi_cmdline = efi_loaded_image->LoadOptions; + 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, diff --git a/src/interface/efi/efi_local.c b/src/interface/efi/efi_local.c index 64e0addd0..d3ac3d54b 100644 --- a/src/interface/efi/efi_local.c +++ b/src/interface/efi/efi_local.c @@ -475,14 +475,15 @@ static int efi_local_open_resolved ( struct efi_local *local, * Open specified path * * @v local Local file - * @v path Path to file + * @v filename Path to file relative to our own image * @ret rc Return status code */ -static int efi_local_open_path ( struct efi_local *local, const char *path ) { - FILEPATH_DEVICE_PATH *fp = container_of ( efi_loaded_image->FilePath, - FILEPATH_DEVICE_PATH, Header); - size_t fp_len = ( fp ? efi_path_len ( &fp->Header ) : 0 ); - char base[ fp_len / 2 /* Cannot exceed this length */ ]; +static int efi_local_open_path ( struct efi_local *local, + const char *filename ) { + EFI_DEVICE_PATH_PROTOCOL *path = efi_loaded_image->FilePath; + EFI_DEVICE_PATH_PROTOCOL *next; + FILEPATH_DEVICE_PATH *fp; + char base[ efi_path_len ( path ) / 2 /* Cannot exceed this length */ ]; size_t remaining = sizeof ( base ); size_t len; char *resolved; @@ -492,13 +493,12 @@ static int efi_local_open_path ( struct efi_local *local, const char *path ) { /* Construct base path to our own image, if possible */ memset ( base, 0, sizeof ( base ) ); tmp = base; - while ( fp && ( fp->Header.Type != END_DEVICE_PATH_TYPE ) ) { + for ( ; ( next = efi_path_next ( path ) ) ; path = next ) { + fp = container_of ( path, FILEPATH_DEVICE_PATH, Header ); len = snprintf ( tmp, remaining, "%ls", fp->PathName ); assert ( len < remaining ); tmp += len; remaining -= len; - fp = ( ( ( void * ) fp ) + ( ( fp->Header.Length[1] << 8 ) | - fp->Header.Length[0] ) ); } DBGC2 ( local, "LOCAL %p base path \"%s\"\n", local, base ); @@ -510,7 +510,7 @@ static int efi_local_open_path ( struct efi_local *local, const char *path ) { } /* Resolve path */ - resolved = resolve_path ( base, path ); + resolved = resolve_path ( base, filename ); if ( ! resolved ) { rc = -ENOMEM; goto err_resolve; diff --git a/src/interface/efi/efi_null.c b/src/interface/efi/efi_null.c index 29ca5b9b6..d0f0428cc 100644 --- a/src/interface/efi/efi_null.c +++ b/src/interface/efi/efi_null.c @@ -195,6 +195,48 @@ void efi_nullify_nii ( EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL *nii ) { /****************************************************************************** * + * VLAN configuration protocol + * + ****************************************************************************** + */ + +static EFI_STATUS EFIAPI +efi_null_vlan_set ( EFI_VLAN_CONFIG_PROTOCOL *vcfg __unused, + UINT16 tag __unused, UINT8 priority __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI +efi_null_vlan_find ( EFI_VLAN_CONFIG_PROTOCOL *vcfg __unused, + UINT16 *filter __unused, UINT16 *count __unused, + EFI_VLAN_FIND_DATA **entries __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_STATUS EFIAPI +efi_null_vlan_remove ( EFI_VLAN_CONFIG_PROTOCOL *vcfg __unused, + UINT16 tag __unused ) { + return EFI_UNSUPPORTED; +} + +static EFI_VLAN_CONFIG_PROTOCOL efi_null_vlan = { + .Set = efi_null_vlan_set, + .Find = efi_null_vlan_find, + .Remove = efi_null_vlan_remove, +}; + +/** + * Nullify VLAN configuration interface + * + * @v vcfg VLAN configuration protocol + */ +void efi_nullify_vlan ( EFI_VLAN_CONFIG_PROTOCOL *vcfg ) { + + memcpy ( vcfg, &efi_null_vlan, sizeof ( *vcfg ) ); +} + +/****************************************************************************** + * * Component name protocol * ****************************************************************************** diff --git a/src/interface/efi/efi_path.c b/src/interface/efi/efi_path.c index bae0ac4b5..a78f97fce 100644 --- a/src/interface/efi/efi_path.c +++ b/src/interface/efi/efi_path.c @@ -17,6 +17,8 @@ * 02110-1301, USA. */ +FILE_LICENCE ( GPL2_OR_LATER ); + #include <stdlib.h> #include <stdarg.h> #include <string.h> @@ -41,27 +43,64 @@ */ /** - * Find end of device path + * Find next element in device path * - * @v path Path to device - * @ret path_end End of device path + * @v path Device path, or NULL + * @v next Next element in device path, or NULL if at end */ -EFI_DEVICE_PATH_PROTOCOL * efi_path_end ( EFI_DEVICE_PATH_PROTOCOL *path ) { +EFI_DEVICE_PATH_PROTOCOL * efi_path_next ( EFI_DEVICE_PATH_PROTOCOL *path ) { + + /* Check for non-existent device path */ + if ( ! path ) + return NULL; + + /* Check for end of device path */ + if ( path->Type == END_DEVICE_PATH_TYPE ) + return NULL; + + /* Move to next component of the device path */ + path = ( ( ( void * ) path ) + + /* There's this amazing new-fangled thing known as + * a UINT16, but who wants to use one of those? */ + ( ( path->Length[1] << 8 ) | path->Length[0] ) ); + + return path; +} + +/** + * Find previous element of device path + * + * @v path Device path, or NULL for no path + * @v curr Current element in device path, or NULL for end of path + * @ret prev Previous element in device path, or NULL + */ +EFI_DEVICE_PATH_PROTOCOL * efi_path_prev ( EFI_DEVICE_PATH_PROTOCOL *path, + EFI_DEVICE_PATH_PROTOCOL *curr ) { + EFI_DEVICE_PATH_PROTOCOL *tmp; - while ( path->Type != END_DEVICE_PATH_TYPE ) { - path = ( ( ( void * ) path ) + - /* There's this amazing new-fangled thing known as - * a UINT16, but who wants to use one of those? */ - ( ( path->Length[1] << 8 ) | path->Length[0] ) ); + /* Find immediately preceding element */ + while ( ( tmp = efi_path_next ( path ) ) != curr ) { + path = tmp; } return path; } /** + * Find end of device path + * + * @v path Device path, or NULL + * @ret path_end End of device path, or NULL + */ +EFI_DEVICE_PATH_PROTOCOL * efi_path_end ( EFI_DEVICE_PATH_PROTOCOL *path ) { + + return efi_path_prev ( path, NULL ); +} + +/** * Find length of device path (excluding terminator) * - * @v path Path to device + * @v path Device path, or NULL * @ret path_len Length of device path */ size_t efi_path_len ( EFI_DEVICE_PATH_PROTOCOL *path ) { @@ -71,6 +110,29 @@ size_t efi_path_len ( EFI_DEVICE_PATH_PROTOCOL *path ) { } /** + * Get VLAN tag from device path + * + * @v path Device path + * @ret tag VLAN tag, or 0 if not a VLAN + */ +unsigned int efi_path_vlan ( EFI_DEVICE_PATH_PROTOCOL *path ) { + EFI_DEVICE_PATH_PROTOCOL *next; + VLAN_DEVICE_PATH *vlan; + + /* Search for VLAN device path */ + for ( ; ( next = efi_path_next ( path ) ) ; path = next ) { + if ( ( path->Type == MESSAGING_DEVICE_PATH ) && + ( path->SubType == MSG_VLAN_DP ) ) { + vlan = container_of ( path, VLAN_DEVICE_PATH, Header ); + return vlan->VlanId; + } + } + + /* No VLAN device path found */ + return 0; +} + +/** * Concatenate EFI device paths * * @v ... List of device paths (NULL terminated) @@ -114,9 +176,7 @@ EFI_DEVICE_PATH_PROTOCOL * efi_paths ( EFI_DEVICE_PATH_PROTOCOL *first, ... ) { } va_end ( args ); end = dst; - end->Type = END_DEVICE_PATH_TYPE; - end->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE; - end->Length[0] = sizeof ( *end ); + efi_path_terminate ( end ); return path; } @@ -176,9 +236,7 @@ EFI_DEVICE_PATH_PROTOCOL * efi_netdev_path ( struct net_device *netdev ) { } else { end = ( ( ( void * ) macpath ) + sizeof ( *macpath ) ); } - end->Type = END_DEVICE_PATH_TYPE; - end->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE; - end->Length[0] = sizeof ( *end ); + efi_path_terminate ( end ); return path; } @@ -218,9 +276,7 @@ EFI_DEVICE_PATH_PROTOCOL * efi_uri_path ( struct uri *uri ) { uripath->Header.Length[1] = ( uripath_len >> 8 ); format_uri ( uri, uripath->Uri, uri_len ); end = ( ( ( void * ) path ) + uripath_len ); - end->Type = END_DEVICE_PATH_TYPE; - end->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE; - end->Length[0] = sizeof ( *end ); + efi_path_terminate ( end ); return path; } @@ -277,9 +333,7 @@ EFI_DEVICE_PATH_PROTOCOL * efi_iscsi_path ( struct iscsi_session *iscsi ) { name = ( ( ( void * ) iscsipath ) + sizeof ( *iscsipath ) ); memcpy ( name, iscsi->target_iqn, name_len ); end = ( ( ( void * ) name ) + name_len ); - end->Type = END_DEVICE_PATH_TYPE; - end->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE; - end->Length[0] = sizeof ( *end ); + efi_path_terminate ( end ); /* Free temporary paths */ free ( netpath ); @@ -319,9 +373,7 @@ EFI_DEVICE_PATH_PROTOCOL * efi_aoe_path ( struct aoe_device *aoedev ) { satapath.sata.Header.Length[0] = sizeof ( satapath.sata ); satapath.sata.HBAPortNumber = aoedev->major; satapath.sata.PortMultiplierPortNumber = aoedev->minor; - satapath.end.Type = END_DEVICE_PATH_TYPE; - satapath.end.SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE; - satapath.end.Length[0] = sizeof ( satapath.end ); + efi_path_terminate ( &satapath.end ); /* Construct overall device path */ path = efi_paths ( netpath, &satapath, NULL ); @@ -362,9 +414,7 @@ EFI_DEVICE_PATH_PROTOCOL * efi_fcp_path ( struct fcp_description *desc ) { path->fc.Header.Length[0] = sizeof ( path->fc ); memcpy ( path->fc.WWN, &desc->wwn, sizeof ( path->fc.WWN ) ); memcpy ( path->fc.Lun, &desc->lun, sizeof ( path->fc.Lun ) ); - path->end.Type = END_DEVICE_PATH_TYPE; - path->end.SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE; - path->end.Length[0] = sizeof ( path->end ); + efi_path_terminate ( &path->end ); return &path->fc.Header; } @@ -416,9 +466,7 @@ EFI_DEVICE_PATH_PROTOCOL * efi_ib_srp_path ( struct ib_srp_device *ib_srp ) { memcpy ( &ibpath->DeviceId, &id->ib.id_ext, sizeof ( ibpath->DeviceId ) ); end = ( ( ( void * ) ibpath ) + sizeof ( *ibpath ) ); - end->Type = END_DEVICE_PATH_TYPE; - end->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE; - end->Length[0] = sizeof ( *end ); + efi_path_terminate ( end ); return path; } @@ -464,9 +512,7 @@ EFI_DEVICE_PATH_PROTOCOL * efi_usb_path ( struct usb_function *func ) { /* Construct device path */ memcpy ( path, efidev->path, prefix_len ); end = ( ( ( void * ) path ) + len - sizeof ( *end ) ); - end->Type = END_DEVICE_PATH_TYPE; - end->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE; - end->Length[0] = sizeof ( *end ); + efi_path_terminate ( end ); usbpath = ( ( ( void * ) end ) - sizeof ( *usbpath ) ); usbpath->InterfaceNumber = func->interface[0]; for ( ; usb ; usbpath--, usb = usb->port->hub->usb ) { diff --git a/src/interface/efi/efi_pci.c b/src/interface/efi/efi_pci.c index 19e341707..e2eeeb344 100644 --- a/src/interface/efi/efi_pci.c +++ b/src/interface/efi/efi_pci.c @@ -524,6 +524,9 @@ static void * efipci_dma_alloc ( struct dma_device *dma, goto err_alloc; } + /* Clear buffer */ + memset ( addr, 0, ( pages * EFI_PAGE_SIZE ) ); + /* Map buffer */ if ( ( rc = efipci_dma_map ( dma, map, virt_to_phys ( addr ), ( pages * EFI_PAGE_SIZE ), @@ -782,12 +785,22 @@ int efipci_info ( EFI_HANDLE device, struct efi_pci_device *efipci ) { */ static int efipci_supported ( EFI_HANDLE device ) { struct efi_pci_device efipci; + uint8_t hdrtype; int rc; /* Get PCI device information */ if ( ( rc = efipci_info ( device, &efipci ) ) != 0 ) return rc; + /* Do not attempt to drive bridges */ + hdrtype = efipci.pci.hdrtype; + if ( ( hdrtype & PCI_HEADER_TYPE_MASK ) != PCI_HEADER_TYPE_NORMAL ) { + DBGC ( device, "EFIPCI " PCI_FMT " type %02x is not type %02x\n", + PCI_ARGS ( &efipci.pci ), hdrtype, + PCI_HEADER_TYPE_NORMAL ); + return -ENOTTY; + } + /* Look for a driver */ if ( ( rc = pci_find_driver ( &efipci.pci ) ) != 0 ) { DBGC ( device, "EFIPCI " PCI_FMT " (%04x:%04x class %06x) " diff --git a/src/interface/efi/efi_pxe.c b/src/interface/efi/efi_pxe.c index 15224a5e4..843ebb5e7 100644 --- a/src/interface/efi/efi_pxe.c +++ b/src/interface/efi/efi_pxe.c @@ -199,7 +199,7 @@ static void efi_pxe_ip_sockaddr ( struct efi_pxe *pxe, EFI_IP_ADDRESS *ip, memset ( sockaddr, 0, sizeof ( *sockaddr ) ); sockaddr->sa.sa_family = pxe->tcpip->sa_family; memcpy ( &sockaddr->se.se_addr, ip, pxe->net->net_addr_len ); - sockaddr->se.se_scope_id = pxe->netdev->index; + sockaddr->se.se_scope_id = pxe->netdev->scope_id; } /** diff --git a/src/interface/efi/efi_rng.c b/src/interface/efi/efi_rng.c new file mode 100644 index 000000000..b76a6fc0d --- /dev/null +++ b/src/interface/efi/efi_rng.c @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <errno.h> +#include <ipxe/entropy.h> +#include <ipxe/crc32.h> +#include <ipxe/efi/efi.h> +#include <ipxe/efi/Protocol/Rng.h> + +/** @file + * + * EFI random number generator protocol entropy source + * + */ + +struct entropy_source efirng_entropy __entropy_source ( ENTROPY_NORMAL ); + +/** Random number generator protocol */ +static EFI_RNG_PROTOCOL *efirng; +EFI_REQUEST_PROTOCOL ( EFI_RNG_PROTOCOL, &efirng ); + +/** Minimum number of bytes to request from RNG + * + * The UEFI spec states (for no apparently good reason) that "When a + * Deterministic Random Bit Generator (DRBG) is used on the output of + * a (raw) entropy source, its security level must be at least 256 + * bits." The EDK2 codebase (mis)interprets this to mean that the + * call to GetRNG() should fail if given a buffer less than 32 bytes. + * + * Incidentally, nothing in the EFI RNG protocol provides any way to + * report the actual amount of entropy returned by GetRNG(). + */ +#define EFIRNG_LEN 32 + +/** + * Enable entropy gathering + * + * @ret rc Return status code + */ +static int efirng_enable ( void ) { + + /* Check for RNG protocol support */ + if ( ! efirng ) { + DBGC ( &efirng, "EFIRNG has no RNG protocol\n" ); + return -ENOTSUP; + } + + /* Nothing in the EFI specification provides any clue as to + * how much entropy will be returned by GetRNG(). Make a + * totally uninformed (and conservative guess) that each + * sample will contain at least one bit of entropy. + */ + entropy_init ( &efirng_entropy, MIN_ENTROPY ( 1.0 ) ); + + return 0; +} + +/** + * Get noise sample from RNG protocol + * + * @ret noise Noise sample + * @ret rc Return status code + */ +static int efirng_get_noise ( noise_sample_t *noise ) { + uint8_t buf[EFIRNG_LEN]; + 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; + } + + /* 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; +} + +/** EFI random number generator protocol entropy source */ +struct entropy_source efirng_entropy __entropy_source ( ENTROPY_NORMAL ) = { + .name = "efirng", + .enable = efirng_enable, + .get_noise = efirng_get_noise, +}; diff --git a/src/interface/efi/efi_snp.c b/src/interface/efi/efi_snp.c index 6649eb1b0..c4f7d4ea8 100644 --- a/src/interface/efi/efi_snp.c +++ b/src/interface/efi/efi_snp.c @@ -934,11 +934,11 @@ static uint8_t efi_undi_checksum ( void *data, size_t len ) { */ static unsigned int efi_undi_ifnum ( struct efi_snp_device *snpdev ) { - /* iPXE network device indexes are one-based (leaving zero + /* iPXE network device scope IDs are one-based (leaving zero * meaning "unspecified"). UNDI interface numbers are * zero-based. */ - return ( snpdev->netdev->index - 1 ); + return ( snpdev->netdev->scope_id - 1 ); } /** @@ -1490,6 +1490,164 @@ static EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL efi_snp_device_nii = { /****************************************************************************** * + * VLAN configuration protocol + * + ****************************************************************************** + */ + +/** + * Create or modify VLAN device + * + * @v vcfg VLAN configuration protocol + * @v tag VLAN tag + * @v priority Default VLAN priority + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI efi_vlan_set ( EFI_VLAN_CONFIG_PROTOCOL *vcfg, + UINT16 tag, UINT8 priority ) { + struct efi_snp_device *snpdev = + container_of ( vcfg, struct efi_snp_device, vcfg ); + struct net_device *trunk = snpdev->netdev; + struct efi_saved_tpl tpl; + int rc; + + /* Raise TPL */ + efi_raise_tpl ( &tpl ); + + /* Create or modify VLAN device */ + if ( ( rc = vlan_create ( trunk, tag, priority ) ) != 0 ) { + DBGC ( snpdev, "SNPDEV %p could not create VLAN tag %d: %s\n", + snpdev, tag, strerror ( rc ) ); + goto err_create; + } + DBGC ( snpdev, "SNPDEV %p created VLAN tag %d priority %d\n", + snpdev, tag, priority ); + + err_create: + efi_restore_tpl ( &tpl ); + return EFIRC ( rc ); +} + +/** + * Find VLAN device(s) + * + * @v vcfg VLAN configuration protocol + * @v filter VLAN tag, or NULL to find all VLANs + * @v count Number of VLANs + * @v entries List of VLANs + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI efi_vlan_find ( EFI_VLAN_CONFIG_PROTOCOL *vcfg, + UINT16 *filter, UINT16 *count, + EFI_VLAN_FIND_DATA **entries ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + struct efi_snp_device *snpdev = + container_of ( vcfg, struct efi_snp_device, vcfg ); + struct net_device *trunk = snpdev->netdev; + struct net_device *vlan; + struct efi_saved_tpl tpl; + EFI_VLAN_FIND_DATA *entry; + VOID *buffer; + unsigned int tag; + unsigned int tci; + size_t len; + EFI_STATUS efirc; + int rc; + + /* Raise TPL */ + efi_raise_tpl ( &tpl ); + + /* Count number of matching VLANs */ + *count = 0; + for ( tag = 1 ; VLAN_TAG_IS_VALID ( tag ) ; tag++ ) { + if ( filter && ( tag != *filter ) ) + continue; + if ( ! ( vlan = vlan_find ( trunk, tag ) ) ) + continue; + (*count)++; + } + + /* Allocate buffer to hold results */ + len = ( (*count) * sizeof ( *entry ) ); + if ( ( efirc = bs->AllocatePool ( EfiBootServicesData, len, + &buffer ) ) != 0 ) { + rc = -EEFI ( efirc ); + goto err_alloc; + } + + /* Fill in buffer */ + *entries = buffer; + entry = *entries; + for ( tag = 1 ; VLAN_TAG_IS_VALID ( tag ) ; tag++ ) { + if ( filter && ( tag != *filter ) ) + continue; + if ( ! ( vlan = vlan_find ( trunk, tag ) ) ) + continue; + tci = vlan_tci ( vlan ); + entry->VlanId = VLAN_TAG ( tci ); + entry->Priority = VLAN_PRIORITY ( tci ); + assert ( entry->VlanId == tag ); + entry++; + } + assert ( entry == &(*entries)[*count] ); + + /* Success */ + rc = 0; + + err_alloc: + efi_restore_tpl ( &tpl ); + return EFIRC ( rc ); +} + +/** + * Remove VLAN device + * + * @v vcfg VLAN configuration protocol + * @v tag VLAN tag + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI efi_vlan_remove ( EFI_VLAN_CONFIG_PROTOCOL *vcfg, + UINT16 tag ) { + struct efi_snp_device *snpdev = + container_of ( vcfg, struct efi_snp_device, vcfg ); + struct net_device *trunk = snpdev->netdev; + struct net_device *vlan; + struct efi_saved_tpl tpl; + int rc; + + /* Raise TPL */ + efi_raise_tpl ( &tpl ); + + /* Identify VLAN device */ + vlan = vlan_find ( trunk, tag ); + if ( ! vlan ) { + DBGC ( snpdev, "SNPDEV %p could not find VLAN tag %d\n", + snpdev, tag ); + rc = -ENOENT; + goto err_find; + } + + /* Remove VLAN device */ + vlan_destroy ( vlan ); + DBGC ( snpdev, "SNPDEV %p removed VLAN tag %d\n", snpdev, tag ); + + /* Success */ + rc = 0; + + err_find: + efi_restore_tpl ( &tpl ); + return EFIRC ( rc ); +} + +/** VLAN configuration protocol */ +static EFI_VLAN_CONFIG_PROTOCOL efi_vlan = { + .Set = efi_vlan_set, + .Find = efi_vlan_find, + .Remove = efi_vlan_remove, +}; + +/****************************************************************************** + * * Component name protocol * ****************************************************************************** @@ -1627,6 +1785,8 @@ static int efi_snp_probe ( struct net_device *netdev ) { struct efi_snp_device *snpdev; unsigned int ifcnt; void *interface; + unsigned int tci; + char vlan_name[ 12 /* ", VLAN xxxx" + NUL */ ]; int leak = 0; EFI_STATUS efirc; int rc; @@ -1687,17 +1847,27 @@ static int efi_snp_probe ( struct net_device *netdev ) { efi_snp_undi.Fudge -= efi_undi_checksum ( &efi_snp_undi, sizeof ( efi_snp_undi ) ); + /* Populate the VLAN configuration protocol */ + memcpy ( &snpdev->vcfg, &efi_vlan, sizeof ( snpdev->vcfg ) ); + /* Populate the component name structure */ efi_snprintf ( snpdev->driver_name, ( sizeof ( snpdev->driver_name ) / sizeof ( snpdev->driver_name[0] ) ), "%s %s", product_short_name, netdev->dev->driver_name ); + tci = vlan_tci ( netdev ); + if ( tci ) { + snprintf ( vlan_name, sizeof ( vlan_name ), ", VLAN %d", + VLAN_TAG ( tci ) ); + } else { + vlan_name[0] = '\0'; + } efi_snprintf ( snpdev->controller_name, ( sizeof ( snpdev->controller_name ) / sizeof ( snpdev->controller_name[0] ) ), - "%s %s (%s, %s)", product_short_name, + "%s %s (%s, %s%s)", product_short_name, netdev->dev->driver_name, netdev->dev->name, - netdev_addr ( netdev ) ); + netdev_addr ( netdev ), vlan_name ); snpdev->name2.GetDriverName = efi_snp_get_driver_name; snpdev->name2.GetControllerName = efi_snp_get_controller_name; snpdev->name2.SupportedLanguages = "en"; @@ -1725,6 +1895,7 @@ static int efi_snp_probe ( struct net_device *netdev ) { &efi_device_path_protocol_guid, snpdev->path, &efi_nii_protocol_guid, &snpdev->nii, &efi_nii31_protocol_guid, &snpdev->nii, + &efi_vlan_config_protocol_guid, &snpdev->vcfg, &efi_component_name2_protocol_guid, &snpdev->name2, &efi_load_file_protocol_guid, &snpdev->load_file, NULL ) ) != 0 ) { @@ -1811,6 +1982,7 @@ static int efi_snp_probe ( struct net_device *netdev ) { &efi_device_path_protocol_guid, snpdev->path, &efi_nii_protocol_guid, &snpdev->nii, &efi_nii31_protocol_guid, &snpdev->nii, + &efi_vlan_config_protocol_guid, &snpdev->vcfg, &efi_component_name2_protocol_guid, &snpdev->name2, &efi_load_file_protocol_guid, &snpdev->load_file, NULL ) ) != 0 ) { @@ -1820,6 +1992,7 @@ static int efi_snp_probe ( struct net_device *netdev ) { } efi_nullify_snp ( &snpdev->snp ); efi_nullify_nii ( &snpdev->nii ); + efi_nullify_vlan ( &snpdev->vcfg ); efi_nullify_name2 ( &snpdev->name2 ); efi_nullify_load_file ( &snpdev->load_file ); err_install_protocol_interface: @@ -1899,6 +2072,7 @@ static void efi_snp_remove ( struct net_device *netdev ) { &efi_device_path_protocol_guid, snpdev->path, &efi_nii_protocol_guid, &snpdev->nii, &efi_nii31_protocol_guid, &snpdev->nii, + &efi_vlan_config_protocol_guid, &snpdev->vcfg, &efi_component_name2_protocol_guid, &snpdev->name2, &efi_load_file_protocol_guid, &snpdev->load_file, NULL ) ) != 0 ) ) { @@ -1908,6 +2082,7 @@ static void efi_snp_remove ( struct net_device *netdev ) { } efi_nullify_snp ( &snpdev->snp ); efi_nullify_nii ( &snpdev->nii ); + efi_nullify_vlan ( &snpdev->vcfg ); efi_nullify_name2 ( &snpdev->name2 ); efi_nullify_load_file ( &snpdev->load_file ); if ( ! leak ) diff --git a/src/interface/efi/efi_snp_hii.c b/src/interface/efi/efi_snp_hii.c index 5d5f80cd7..8b65c8a78 100644 --- a/src/interface/efi/efi_snp_hii.c +++ b/src/interface/efi/efi_snp_hii.c @@ -704,9 +704,7 @@ int efi_snp_hii_install ( struct efi_snp_device *snpdev ) { vendor_path->Header.Length[0] = sizeof ( *vendor_path ); efi_snp_hii_random_guid ( &vendor_path->Guid ); path_end = ( ( void * ) ( vendor_path + 1 ) ); - path_end->Type = END_DEVICE_PATH_TYPE; - path_end->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE; - path_end->Length[0] = sizeof ( *path_end ); + efi_path_terminate ( path_end ); /* Create device path and child handle for HII association */ if ( ( efirc = bs->InstallMultipleProtocolInterfaces ( diff --git a/src/interface/efi/efi_utils.c b/src/interface/efi/efi_utils.c index 8e660e9d7..53f82bfe4 100644 --- a/src/interface/efi/efi_utils.c +++ b/src/interface/efi/efi_utils.c @@ -23,6 +23,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <string.h> #include <errno.h> #include <ipxe/efi/efi.h> +#include <ipxe/efi/efi_path.h> #include <ipxe/efi/efi_pci.h> #include <ipxe/efi/efi_utils.h> @@ -38,23 +39,26 @@ FILE_LICENCE ( GPL2_OR_LATER ); * @v device EFI device handle * @v protocol Protocol GUID * @v parent Parent EFI device handle to fill in + * @v skip Number of protocol-supporting parent devices to skip * @ret rc Return status code */ int efi_locate_device ( EFI_HANDLE device, EFI_GUID *protocol, - EFI_HANDLE *parent ) { + EFI_HANDLE *parent, unsigned int skip ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; union { EFI_DEVICE_PATH_PROTOCOL *path; void *interface; - } path; - EFI_DEVICE_PATH_PROTOCOL *devpath; + } u; + EFI_DEVICE_PATH_PROTOCOL *path; + EFI_DEVICE_PATH_PROTOCOL *end; + size_t len; EFI_STATUS efirc; int rc; /* Get device path */ if ( ( efirc = bs->OpenProtocol ( device, &efi_device_path_protocol_guid, - &path.interface, + &u.interface, efi_image_handle, device, EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){ rc = -EEFI ( efirc ); @@ -62,22 +66,46 @@ int efi_locate_device ( EFI_HANDLE device, EFI_GUID *protocol, efi_handle_name ( device ), strerror ( rc ) ); goto err_open_device_path; } - devpath = path.path; - /* Check for presence of specified protocol */ - if ( ( efirc = bs->LocateDevicePath ( protocol, &devpath, - parent ) ) != 0 ) { - rc = -EEFI ( efirc ); - DBGC ( device, "EFIDEV %s has no parent supporting %s: %s\n", - efi_handle_name ( device ), - efi_guid_ntoa ( protocol ), strerror ( rc ) ); - goto err_locate_protocol; + /* Create modifiable copy of device path */ + len = ( efi_path_len ( u.path ) + sizeof ( EFI_DEVICE_PATH_PROTOCOL )); + path = malloc ( len ); + if ( ! path ) { + rc = -ENOMEM; + goto err_alloc_path; + } + memcpy ( path, u.path, len ); + + /* Locate parent device(s) */ + while ( 1 ) { + + /* Check for presence of specified protocol */ + end = path; + if ( ( efirc = bs->LocateDevicePath ( protocol, &end, + parent ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( device, "EFIDEV %s has no parent supporting " + "%s: %s\n", efi_devpath_text ( path ), + efi_guid_ntoa ( protocol ), strerror ( rc ) ); + goto err_locate_protocol; + } + + /* Stop if we have skipped the requested number of devices */ + if ( ! skip-- ) + break; + + /* Trim device path */ + efi_path_terminate ( end ); + end = efi_path_prev ( path, end ); + 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: @@ -150,7 +178,7 @@ static int efi_pci_info ( EFI_HANDLE device, const char *prefix, /* Find parent PCI device */ if ( ( rc = efi_locate_device ( device, &efi_pci_io_protocol_guid, - &pci_device ) ) != 0 ) { + &pci_device, 0 ) ) != 0 ) { DBGC ( device, "EFIDEV %s is not a PCI device: %s\n", efi_handle_name ( device ), strerror ( rc ) ); return rc; diff --git a/src/interface/efi/efiprefix.c b/src/interface/efi/efiprefix.c index 126c813d7..261160681 100644 --- a/src/interface/efi/efiprefix.c +++ b/src/interface/efi/efiprefix.c @@ -78,15 +78,17 @@ 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 *filepath = efi_loaded_image->FilePath; /* Identify autoboot device, if any */ - efi_set_autoboot_ll_addr ( device ); + efi_set_autoboot_ll_addr ( device, devpath ); /* Store cached DHCP packet, if any */ - efi_cachedhcp_record ( device ); + efi_cachedhcp_record ( device, devpath ); /* Load autoexec script, if any */ - efi_autoexec_load ( device ); + efi_autoexec_load ( device, filepath ); } /** EFI application initialisation function */ diff --git a/src/interface/linux/linux_entropy.c b/src/interface/linux/linux_entropy.c index 257e993a0..f24969794 100644 --- a/src/interface/linux/linux_entropy.c +++ b/src/interface/linux/linux_entropy.c @@ -34,6 +34,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/linux_api.h> #include <ipxe/entropy.h> +struct entropy_source linux_entropy __entropy_source ( ENTROPY_NORMAL ); + /** Entropy source filename */ static const char entropy_filename[] = "/dev/random"; @@ -55,6 +57,13 @@ static int linux_entropy_enable ( void ) { return entropy_fd; } + /* linux_get_noise() reads a single byte from /dev/random, + * which is supposed to block until a sufficient amount of + * entropy is available. We therefore assume that each sample + * contains exactly 8 bits of entropy. + */ + entropy_init ( &linux_entropy, MIN_ENTROPY ( 8.0 ) ); + return 0; } @@ -95,7 +104,10 @@ static int linux_get_noise ( noise_sample_t *noise ) { return 0; } -PROVIDE_ENTROPY_INLINE ( linux, min_entropy_per_sample ); -PROVIDE_ENTROPY ( linux, entropy_enable, linux_entropy_enable ); -PROVIDE_ENTROPY ( linux, entropy_disable, linux_entropy_disable ); -PROVIDE_ENTROPY ( linux, get_noise, linux_get_noise ); +/** Linux entropy source */ +struct entropy_source linux_entropy __entropy_source ( ENTROPY_NORMAL ) = { + .name = "linux", + .enable = linux_entropy_enable, + .disable = linux_entropy_disable, + .get_noise = linux_get_noise, +}; |
