summaryrefslogtreecommitdiffstats
path: root/src/interface
diff options
context:
space:
mode:
authorSimon Rettberg2023-04-04 15:12:41 +0200
committerSimon Rettberg2023-04-04 15:12:41 +0200
commit5ba496dce11d10198a0eae0c8440dccb256fbf32 (patch)
tree549903f1dab893870335a6e4767a4530444d2e83 /src/interface
parent[vesafb] Map Unicode characters to CP437 if possible (diff)
parent[tls] Handle fragmented handshake records (diff)
downloadipxe-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.c23
-rw-r--r--src/interface/efi/efi_autoexec.c85
-rw-r--r--src/interface/efi/efi_block.c4
-rw-r--r--src/interface/efi/efi_cachedhcp.c17
-rw-r--r--src/interface/efi/efi_cmdline.c151
-rw-r--r--src/interface/efi/efi_entropy.c109
-rw-r--r--src/interface/efi/efi_file.c295
-rw-r--r--src/interface/efi/efi_init.c5
-rw-r--r--src/interface/efi/efi_local.c20
-rw-r--r--src/interface/efi/efi_null.c42
-rw-r--r--src/interface/efi/efi_path.c114
-rw-r--r--src/interface/efi/efi_pci.c13
-rw-r--r--src/interface/efi/efi_pxe.c2
-rw-r--r--src/interface/efi/efi_rng.c118
-rw-r--r--src/interface/efi/efi_snp.c183
-rw-r--r--src/interface/efi/efi_snp_hii.c4
-rw-r--r--src/interface/efi/efi_utils.c56
-rw-r--r--src/interface/efi/efiprefix.c8
-rw-r--r--src/interface/linux/linux_entropy.c20
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,
+};