summaryrefslogtreecommitdiffstats
path: root/src/interface/efi
diff options
context:
space:
mode:
authorSimon Rettberg2020-08-12 12:27:02 +0200
committerSimon Rettberg2020-08-12 12:27:02 +0200
commitdf1f1c469e65156c18dd98931618a7d37c9d8691 (patch)
tree12bf4fe1a74701b5f86a696d1e28822dac055765 /src/interface/efi
parentRevert "Revert "[build] Construct full version number automatically from git ... (diff)
parent[efi] Use device path to locate filesystem from which we were loaded (diff)
downloadipxe-df1f1c469e65156c18dd98931618a7d37c9d8691.tar.gz
ipxe-df1f1c469e65156c18dd98931618a7d37c9d8691.tar.xz
ipxe-df1f1c469e65156c18dd98931618a7d37c9d8691.zip
Merge branch 'master' into openslx
Diffstat (limited to 'src/interface/efi')
-rw-r--r--src/interface/efi/efi_block.c3
-rw-r--r--src/interface/efi/efi_download.c5
-rw-r--r--src/interface/efi/efi_driver.c27
-rw-r--r--src/interface/efi/efi_entropy.c12
-rw-r--r--src/interface/efi/efi_init.c121
-rw-r--r--src/interface/efi/efi_local.c15
-rw-r--r--src/interface/efi/efi_snp_hii.c9
-rw-r--r--src/interface/efi/efi_usb.c2
-rw-r--r--src/interface/efi/efidrvprefix.c12
-rw-r--r--src/interface/efi/efiprefix.c3
10 files changed, 199 insertions, 10 deletions
diff --git a/src/interface/efi/efi_block.c b/src/interface/efi/efi_block.c
index 91f830a11..64d1e1980 100644
--- a/src/interface/efi/efi_block.c
+++ b/src/interface/efi/efi_block.c
@@ -594,11 +594,14 @@ static int efi_block_boot_image ( struct san_device *sandev, EFI_HANDLE handle,
sandev->drive, efi_devpath_text ( boot_path ) );
/* Try loading boot image from this device */
+ *image = NULL;
if ( ( efirc = bs->LoadImage ( FALSE, efi_image_handle, boot_path,
NULL, 0, image ) ) != 0 ) {
rc = -EEFI ( efirc );
DBGC ( sandev, "EFIBLK %#02x could not load image: %s\n",
sandev->drive, strerror ( rc ) );
+ if ( efirc == EFI_SECURITY_VIOLATION )
+ bs->UnloadImage ( *image );
goto err_load_image;
}
diff --git a/src/interface/efi/efi_download.c b/src/interface/efi/efi_download.c
index 1218852e2..8d12bd57c 100644
--- a/src/interface/efi/efi_download.c
+++ b/src/interface/efi/efi_download.c
@@ -138,8 +138,11 @@ efi_download_start ( IPXE_DOWNLOAD_PROTOCOL *This __unused,
struct efi_download_file *file;
int rc;
+ efi_snp_claim();
+
file = malloc ( sizeof ( struct efi_download_file ) );
if ( file == NULL ) {
+ efi_snp_release();
return EFI_OUT_OF_RESOURCES;
}
@@ -147,10 +150,10 @@ efi_download_start ( IPXE_DOWNLOAD_PROTOCOL *This __unused,
rc = xfer_open ( &file->xfer, LOCATION_URI_STRING, Url );
if ( rc ) {
free ( file );
+ efi_snp_release();
return EFIRC ( rc );
}
- efi_snp_claim();
file->pos = 0;
file->data_callback = DataCallback;
file->finish_callback = FinishCallback;
diff --git a/src/interface/efi/efi_driver.c b/src/interface/efi/efi_driver.c
index 7be2e585d..760ee41a8 100644
--- a/src/interface/efi/efi_driver.c
+++ b/src/interface/efi/efi_driver.c
@@ -39,6 +39,20 @@ FILE_LICENCE ( GPL2_OR_LATER );
*
*/
+/* Disambiguate the various error causes */
+#define EINFO_EEFI_CONNECT \
+ __einfo_uniqify ( EINFO_EPLATFORM, 0x01, \
+ "Could not connect controllers" )
+#define EINFO_EEFI_CONNECT_PROHIBITED \
+ __einfo_platformify ( EINFO_EEFI_CONNECT, \
+ EFI_SECURITY_VIOLATION, \
+ "Connecting controllers prohibited by " \
+ "security policy" )
+#define EEFI_CONNECT_PROHIBITED \
+ __einfo_error ( EINFO_EEFI_CONNECT_PROHIBITED )
+#define EEFI_CONNECT( efirc ) EPLATFORM ( EINFO_EEFI_CONNECT, efirc, \
+ EEFI_CONNECT_PROHIBITED )
+
static EFI_DRIVER_BINDING_PROTOCOL efi_driver_binding;
/** List of controlled EFI devices */
@@ -457,10 +471,19 @@ static int efi_driver_connect ( EFI_HANDLE device ) {
efi_handle_name ( device ) );
if ( ( efirc = bs->ConnectController ( device, drivers, NULL,
FALSE ) ) != 0 ) {
- rc = -EEFI ( efirc );
+ rc = -EEFI_CONNECT ( efirc );
DBGC ( device, "EFIDRV %s could not connect new drivers: "
"%s\n", efi_handle_name ( device ), strerror ( rc ) );
- return rc;
+ DBGC ( device, "EFIDRV %s connecting driver directly\n",
+ efi_handle_name ( device ) );
+ if ( ( efirc = efi_driver_start ( &efi_driver_binding, device,
+ NULL ) ) != 0 ) {
+ rc = -EEFI_CONNECT ( efirc );
+ DBGC ( device, "EFIDRV %s could not connect driver "
+ "directly: %s\n", efi_handle_name ( device ),
+ strerror ( rc ) );
+ return rc;
+ }
}
DBGC2 ( device, "EFIDRV %s after connecting:\n",
efi_handle_name ( device ) );
diff --git a/src/interface/efi/efi_entropy.c b/src/interface/efi/efi_entropy.c
index a865bad59..f6e82e2c0 100644
--- a/src/interface/efi/efi_entropy.c
+++ b/src/interface/efi/efi_entropy.c
@@ -222,6 +222,7 @@ static int efi_get_noise_ticks ( noise_sample_t *noise ) {
* @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;
@@ -239,6 +240,17 @@ static int efi_get_noise_rng ( noise_sample_t *noise ) {
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
diff --git a/src/interface/efi/efi_init.c b/src/interface/efi/efi_init.c
index ed9707f2c..70212b184 100644
--- a/src/interface/efi/efi_init.c
+++ b/src/interface/efi/efi_init.c
@@ -21,9 +21,12 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <string.h>
#include <errno.h>
+#include <endian.h>
#include <ipxe/init.h>
+#include <ipxe/rotate.h>
#include <ipxe/efi/efi.h>
#include <ipxe/efi/efi_driver.h>
+#include <ipxe/efi/efi_utils.h>
#include <ipxe/efi/Protocol/LoadedImage.h>
/** Image handle passed to entry point */
@@ -32,6 +35,9 @@ EFI_HANDLE efi_image_handle;
/** Loaded image protocol for this image */
EFI_LOADED_IMAGE_PROTOCOL *efi_loaded_image;
+/** Device path for the loaded image's device handle */
+EFI_DEVICE_PATH_PROTOCOL *efi_loaded_image_path;
+
/** System table passed to entry point
*
* We construct the symbol name efi_systab via the PLATFORM macro.
@@ -47,6 +53,16 @@ int efi_shutdown_in_progress;
/** Event used to signal shutdown */
static EFI_EVENT efi_shutdown_event;
+/** Stack cookie */
+unsigned long __stack_chk_guard;
+
+/** Exit function
+ *
+ * Cached to minimise external dependencies when a stack check
+ * failure is triggered.
+ */
+static EFI_EXIT efi_exit;
+
/* Forward declarations */
static EFI_STATUS EFIAPI efi_unload ( EFI_HANDLE image_handle );
@@ -88,6 +104,46 @@ static void * efi_find_table ( EFI_GUID *guid ) {
}
/**
+ * Construct a stack cookie value
+ *
+ * @v handle Image handle
+ * @ret cookie Stack cookie
+ */
+__attribute__ (( noinline )) unsigned long
+efi_stack_cookie ( EFI_HANDLE handle ) {
+ unsigned long cookie = 0;
+ unsigned int rotation = ( 8 * sizeof ( cookie ) / 4 );
+
+ /* There is no viable source of entropy available at this
+ * point. Construct a value that is at least likely to vary
+ * between platforms and invocations.
+ */
+ cookie ^= ( ( unsigned long ) handle );
+ cookie = roll ( cookie, rotation );
+ cookie ^= ( ( unsigned long ) &handle );
+ cookie = roll ( cookie, rotation );
+ cookie ^= profile_timestamp();
+ cookie = roll ( cookie, rotation );
+ cookie ^= build_id;
+
+ /* Ensure that the value contains a NUL byte, to act as a
+ * runaway string terminator. Construct the NUL using a shift
+ * rather than a mask, to avoid losing valuable entropy in the
+ * lower-order bits.
+ */
+ cookie <<= 8;
+
+ /* Ensure that the NUL byte is placed at the bottom of the
+ * stack cookie, to avoid potential disclosure via an
+ * unterminated string.
+ */
+ if ( __BYTE_ORDER == __BIG_ENDIAN )
+ cookie >>= 8;
+
+ return cookie;
+}
+
+/**
* Initialise EFI environment
*
* @v image_handle Image handle
@@ -100,6 +156,9 @@ EFI_STATUS efi_init ( EFI_HANDLE image_handle,
struct efi_protocol *prot;
struct efi_config_table *tab;
void *loaded_image;
+ void *device_path;
+ void *device_path_copy;
+ size_t device_path_len;
EFI_STATUS efirc;
int rc;
@@ -130,6 +189,9 @@ EFI_STATUS efi_init ( EFI_HANDLE image_handle,
DBGC ( systab, "EFI handle %p systab %p\n", image_handle, systab );
bs = systab->BootServices;
+ /* Store abort function pointer */
+ efi_exit = bs->Exit;
+
/* Look up used protocols */
for_each_table_entry ( prot, EFI_PROTOCOLS ) {
if ( ( efirc = bs->LocateProtocol ( &prot->guid, NULL,
@@ -175,6 +237,33 @@ EFI_STATUS efi_init ( EFI_HANDLE image_handle,
DBGC ( systab, "EFI image base address %p\n",
efi_loaded_image->ImageBase );
+ /* Get loaded image's device handle's device path */
+ if ( ( efirc = bs->OpenProtocol ( efi_loaded_image->DeviceHandle,
+ &efi_device_path_protocol_guid,
+ &device_path, image_handle, NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL ) ) != 0 ) {
+ rc = -EEFI ( efirc );
+ DBGC ( systab, "EFI could not get loaded image's device path: "
+ "%s", strerror ( rc ) );
+ goto err_no_device_path;
+ }
+
+ /* Make a copy of the loaded image's device handle's device
+ * path, since the device handle itself may become invalidated
+ * when we load our own drivers.
+ */
+ device_path_len = ( efi_devpath_len ( device_path ) +
+ sizeof ( EFI_DEVICE_PATH_PROTOCOL ) );
+ if ( ( efirc = bs->AllocatePool ( EfiBootServicesData, device_path_len,
+ &device_path_copy ) ) != 0 ) {
+ rc = -EEFI ( efirc );
+ goto err_alloc_device_path;
+ }
+ memcpy ( device_path_copy, device_path, device_path_len );
+ efi_loaded_image_path = device_path_copy;
+ DBGC ( systab, "EFI image device path %s\n",
+ efi_devpath_text ( efi_loaded_image_path ) );
+
/* EFI is perfectly capable of gracefully shutting down any
* loaded devices if it decides to fall back to a legacy boot.
* For no particularly comprehensible reason, it doesn't
@@ -206,6 +295,9 @@ EFI_STATUS efi_init ( EFI_HANDLE image_handle,
err_driver_install:
bs->CloseEvent ( efi_shutdown_event );
err_create_event:
+ bs->FreePool ( efi_loaded_image_path );
+ err_alloc_device_path:
+ err_no_device_path:
err_no_loaded_image:
err_missing_table:
err_missing_protocol:
@@ -236,7 +328,36 @@ static EFI_STATUS EFIAPI efi_unload ( EFI_HANDLE image_handle __unused ) {
/* Uninstall exit boot services event */
bs->CloseEvent ( efi_shutdown_event );
+ /* Free copy of loaded image's device handle's device path */
+ bs->FreePool ( efi_loaded_image_path );
+
DBGC ( systab, "EFI image unloaded\n" );
return 0;
}
+
+/**
+ * Abort on stack check failure
+ *
+ */
+__attribute__ (( noreturn )) void __stack_chk_fail ( void ) {
+ EFI_STATUS efirc;
+ int rc;
+
+ /* Report failure (when debugging) */
+ DBGC ( efi_systab, "EFI stack check failed (cookie %#lx); aborting\n",
+ __stack_chk_guard );
+
+ /* Attempt to exit cleanly with an error status */
+ if ( efi_exit ) {
+ efirc = efi_exit ( efi_image_handle, EFI_COMPROMISED_DATA,
+ 0, NULL );
+ rc = -EEFI ( efirc );
+ DBGC ( efi_systab, "EFI stack check exit failed: %s\n",
+ strerror ( rc ) );
+ }
+
+ /* If the exit fails for any reason, lock the system */
+ while ( 1 ) {}
+
+}
diff --git a/src/interface/efi/efi_local.c b/src/interface/efi/efi_local.c
index bd010ad2e..79ea822f3 100644
--- a/src/interface/efi/efi_local.c
+++ b/src/interface/efi/efi_local.c
@@ -307,6 +307,7 @@ static int efi_local_open_volume ( struct efi_local *local,
EFI_GUID *protocol = &efi_simple_file_system_protocol_guid;
int ( * check ) ( struct efi_local *local, EFI_HANDLE device,
EFI_FILE_PROTOCOL *root, const char *volume );
+ EFI_DEVICE_PATH_PROTOCOL *path;
EFI_FILE_PROTOCOL *root;
EFI_HANDLE *handles;
EFI_HANDLE device;
@@ -328,8 +329,18 @@ static int efi_local_open_volume ( struct efi_local *local,
}
check = efi_local_check_volume_name;
} else {
- /* Use our loaded image's device handle */
- handles = &efi_loaded_image->DeviceHandle;
+ /* Locate filesystem from which we were loaded */
+ path = efi_loaded_image_path;
+ if ( ( efirc = bs->LocateDevicePath ( protocol, &path,
+ &device ) ) != 0 ) {
+ rc = -EEFI ( efirc );
+ DBGC ( local, "LOCAL %p could not locate file system "
+ "on %s: %s\n", local,
+ efi_devpath_text ( efi_loaded_image_path ),
+ strerror ( rc ) );
+ return rc;
+ }
+ handles = &device;
num_handles = 1;
check = NULL;
}
diff --git a/src/interface/efi/efi_snp_hii.c b/src/interface/efi/efi_snp_hii.c
index 651bef040..1e681a429 100644
--- a/src/interface/efi/efi_snp_hii.c
+++ b/src/interface/efi/efi_snp_hii.c
@@ -247,16 +247,17 @@ static int efi_snp_hii_append ( struct efi_snp_device *snpdev __unused,
const char *key, const char *value,
wchar_t **results ) {
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ EFI_STATUS efirc;
size_t len;
void *new;
/* Allocate new string */
len = ( ( *results ? ( wcslen ( *results ) + 1 /* "&" */ ) : 0 ) +
strlen ( key ) + 1 /* "=" */ + strlen ( value ) + 1 /* NUL */ );
- bs->AllocatePool ( EfiBootServicesData, ( len * sizeof ( wchar_t ) ),
- &new );
- if ( ! new )
- return -ENOMEM;
+ if ( ( efirc = bs->AllocatePool ( EfiBootServicesData,
+ ( len * sizeof ( wchar_t ) ),
+ &new ) ) != 0 )
+ return -EEFI ( efirc );
/* Populate string */
efi_snprintf ( new, len, "%ls%s%s=%s", ( *results ? *results : L"" ),
diff --git a/src/interface/efi/efi_usb.c b/src/interface/efi/efi_usb.c
index 48274f1d6..6f49b369e 100644
--- a/src/interface/efi/efi_usb.c
+++ b/src/interface/efi/efi_usb.c
@@ -1100,7 +1100,7 @@ static int efi_usb_install ( struct efi_usb_device *usbdev,
usbpath->Header.Type = MESSAGING_DEVICE_PATH;
usbpath->Header.SubType = MSG_USB_DP;
usbpath->Header.Length[0] = sizeof ( *usbpath );
- usbpath->ParentPortNumber = usb->port->address;
+ usbpath->ParentPortNumber = ( usb->port->address - 1 );
}
/* Add to list of interfaces */
diff --git a/src/interface/efi/efidrvprefix.c b/src/interface/efi/efidrvprefix.c
index 4fbb19ff7..ac7d94374 100644
--- a/src/interface/efi/efidrvprefix.c
+++ b/src/interface/efi/efidrvprefix.c
@@ -34,16 +34,28 @@ FILE_LICENCE ( GPL2_OR_LATER );
*/
EFI_STATUS EFIAPI _efidrv_start ( EFI_HANDLE image_handle,
EFI_SYSTEM_TABLE *systab ) {
+ EFI_BOOT_SERVICES *bs;
+ EFI_TPL saved_tpl;
EFI_STATUS efirc;
+ /* Initialise stack cookie */
+ efi_init_stack_guard ( image_handle );
+
/* Initialise EFI environment */
if ( ( efirc = efi_init ( image_handle, systab ) ) != 0 )
return efirc;
+ /* Raise TPL */
+ bs = efi_systab->BootServices;
+ saved_tpl = bs->RaiseTPL ( TPL_CALLBACK );
+
/* Initialise iPXE environment */
initialise();
startup();
+ /* Restore TPL */
+ bs->RestoreTPL ( saved_tpl );
+
return 0;
}
diff --git a/src/interface/efi/efiprefix.c b/src/interface/efi/efiprefix.c
index de3572c75..2c5a5b31d 100644
--- a/src/interface/efi/efiprefix.c
+++ b/src/interface/efi/efiprefix.c
@@ -41,6 +41,9 @@ EFI_STATUS EFIAPI _efi_start ( EFI_HANDLE image_handle,
EFI_STATUS efirc;
int rc;
+ /* Initialise stack cookie */
+ efi_init_stack_guard ( image_handle );
+
/* Initialise EFI environment */
if ( ( efirc = efi_init ( image_handle, systab ) ) != 0 )
goto err_init;