From 3ef772230db077a7122de11d93cfc4ff570fb4a7 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Tue, 22 Jun 2021 17:38:51 +0300 Subject: [PATCH 1/7] efi: Fix to use null handle to create new handle for efi_fmp_raw When running the efidebug capsule disk-update command, the efi_fmp_raw protocol installation fails with 2 (EFI_INVALID_PARAMETER) as below. This is because the code passes efi_root instead of the handle local var. => efidebug capsule disk-update EFI: Call: efi_install_multiple_protocol_interfaces( &handle, &efi_guid_firmware_management_protocol, &efi_fmp_fit, NULL) EFI: Entry efi_install_multiple_protocol_interfaces(00000000fbaf5988) EFI: Call: efi_install_protocol_interface( handle, protocol, EFI_NATIVE_INTERFACE, protocol_interface) EFI: Entry efi_install_protocol_interface(00000000fbaf5988, 86c77a67-0b97-4633-a187-49104d0685c7, 0, 00000000fbfa6ee8) EFI: new handle 00000000fbb37520 EFI: Exit: efi_install_protocol_interface: 0 EFI: 0 returned by efi_install_protocol_interface( handle, protocol, EFI_NATIVE_INTERFACE, protocol_interface) EFI: Exit: efi_install_multiple_protocol_interfaces: 0 EFI: 0 returned by efi_install_multiple_protocol_interfaces( &handle, &efi_guid_firmware_management_protocol, &efi_fmp_fit, NULL) EFI: Call: efi_install_multiple_protocol_interfaces( &efi_root, &efi_guid_firmware_management_protocol, &efi_fmp_raw, NULL) EFI: Entry efi_install_multiple_protocol_interfaces(00000000fbfec648) EFI: Call: efi_install_protocol_interface( handle, protocol, EFI_NATIVE_INTERFACE, protocol_interface) EFI: Entry efi_install_protocol_interface(00000000fbfec648, 86c77a67-0b97-4633-a187-49104d0685c7, 0, 00000000fbfa6f18) EFI: handle 00000000fbaf8520 EFI: Exit: efi_install_protocol_interface: 2 EFI: 2 returned by efi_install_protocol_interface( handle, protocol, EFI_NATIVE_INTERFACE, protocol_interface) EFI: Exit: efi_install_multiple_protocol_interfaces: 2 EFI: 2 returned by efi_install_multiple_protocol_interfaces( &efi_root, &efi_guid_firmware_management_protocol, &efi_fmp_raw, NULL) Command failed, result=1 To fix this issue, pass the handle local var which is set NULL right before installing efi_fmp_raw as same as the installing efi_fmp_fit. (In both cases, the local reference to the handle will be just discarded) Signed-off-by: Masami Hiramatsu Signed-off-by: Ilias Apalodimas Reviewed-by: Heinrich Schuchardt --- lib/efi_loader/efi_capsule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c index 60309d4a07d..cc6ed453ed2 100644 --- a/lib/efi_loader/efi_capsule.c +++ b/lib/efi_loader/efi_capsule.c @@ -940,7 +940,7 @@ efi_status_t __weak arch_efi_load_capsule_drivers(void) if (IS_ENABLED(CONFIG_EFI_CAPSULE_FIRMWARE_RAW)) { handle = NULL; ret = EFI_CALL(efi_install_multiple_protocol_interfaces( - &efi_root, + &handle, &efi_guid_firmware_management_protocol, &efi_fmp_raw, NULL)); } From b891ff18f8999c89d00e1a963a4bc07afe3ae061 Mon Sep 17 00:00:00 2001 From: Ilias Apalodimas Date: Tue, 22 Jun 2021 17:38:52 +0300 Subject: [PATCH 2/7] efi_loader: Force a single FMP instance per hardware store Chapter 23 of the EFI spec (rev 2.9) says: "A specific updatable hardware firmware store must be represented by exactly one FMP instance". This is not the case for us, since both of our FMP protocols can be installed at the same time because they are controlled by a single 'dfu_alt_info' env variable. So make the config options depend on each other and allow the user to install one of them at any given time. If we fix the meta-data provided by the 'dfu_alt_info' in the future, to hint about the capsule type (fit or raw) we can revise this and enable both FMPs to be installed, as long as they target different firmware hardware stores Note that we are not using a Kconfig 'choice' on purpose, since we want to allow both of those to be installed and tested in sandbox Signed-off-by: Ilias Apalodimas Reviewed-by: Heinrich Schuchardt --- configs/xilinx_zynqmp_virt_defconfig | 1 - lib/efi_loader/Kconfig | 45 ++++++++++++++-------------- 2 files changed, 22 insertions(+), 24 deletions(-) diff --git a/configs/xilinx_zynqmp_virt_defconfig b/configs/xilinx_zynqmp_virt_defconfig index b9351d45042..a3d944d94b6 100644 --- a/configs/xilinx_zynqmp_virt_defconfig +++ b/configs/xilinx_zynqmp_virt_defconfig @@ -187,5 +187,4 @@ CONFIG_OF_LIBFDT_OVERLAY=y CONFIG_EFI_SET_TIME=y CONFIG_EFI_RUNTIME_UPDATE_CAPSULE=y CONFIG_EFI_CAPSULE_ON_DISK=y -CONFIG_EFI_CAPSULE_FIRMWARE_FIT=y CONFIG_EFI_CAPSULE_FIRMWARE_RAW=y diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index 6242caceb7f..684adfb6237 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -161,6 +161,28 @@ config EFI_CAPSULE_FIRMWARE_MANAGEMENT Select this option if you want to enable capsule-based firmware update using Firmware Management Protocol. +config EFI_CAPSULE_FIRMWARE_FIT + bool "FMP driver for FIT images" + depends on FIT + depends on EFI_CAPSULE_FIRMWARE_MANAGEMENT + select UPDATE_FIT + select DFU + select EFI_CAPSULE_FIRMWARE + help + Select this option if you want to enable firmware management protocol + driver for FIT image + +config EFI_CAPSULE_FIRMWARE_RAW + bool "FMP driver for raw images" + depends on EFI_CAPSULE_FIRMWARE_MANAGEMENT + depends on SANDBOX || (!SANDBOX && !EFI_CAPSULE_FIRMWARE_FIT) + select DFU_WRITE_ALT + select DFU + select EFI_CAPSULE_FIRMWARE + help + Select this option if you want to enable firmware management protocol + driver for raw image + config EFI_CAPSULE_AUTHENTICATE bool "Update Capsule authentication" depends on EFI_CAPSULE_FIRMWARE @@ -181,29 +203,6 @@ config EFI_CAPSULE_AUTHENTICATE Select this option if you want to enable capsule authentication -config EFI_CAPSULE_FIRMWARE_FIT - bool "FMP driver for FIT image" - depends on EFI_CAPSULE_FIRMWARE_MANAGEMENT - depends on FIT - select UPDATE_FIT - select DFU - select EFI_CAPSULE_FIRMWARE - default n - help - Select this option if you want to enable firmware management protocol - driver for FIT image - -config EFI_CAPSULE_FIRMWARE_RAW - bool "FMP driver for raw image" - depends on EFI_CAPSULE_FIRMWARE_MANAGEMENT - select DFU - select DFU_WRITE_ALT - select EFI_CAPSULE_FIRMWARE - default n - help - Select this option if you want to enable firmware management protocol - driver for raw image - config EFI_DEVICE_PATH_TO_TEXT bool "Device path to text protocol" default y From 6e0184b86f689045a0acc268e1899ce7757c65d0 Mon Sep 17 00:00:00 2001 From: Ilias Apalodimas Date: Tue, 22 Jun 2021 17:38:53 +0300 Subject: [PATCH 3/7] efi_loader: Always install FMPs We only install FMPs if a CapsuleUpdate is requested. Since we now have an ESRT table which relies on FMPs to build the required information, it makes more sense to unconditionally install them. This will allow userspace applications (e.g fwupd) to make use of the ERST and provide us with files we can use to run CapsuleUpdate on-disk Signed-off-by: Ilias Apalodimas Reviewed-by: Heinrich Schuchardt --- include/efi_loader.h | 1 + lib/efi_loader/efi_capsule.c | 11 +++-------- lib/efi_loader/efi_setup.c | 6 ++++++ 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/include/efi_loader.h b/include/efi_loader.h index 0a9c82a257e..b81180cfda8 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -972,4 +972,5 @@ efi_status_t efi_esrt_register(void); * - error code otherwise. */ efi_status_t efi_esrt_populate(void); +efi_status_t efi_load_capsule_drivers(void); #endif /* _EFI_LOADER_H */ diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c index cc6ed453ed2..d7136035d8f 100644 --- a/lib/efi_loader/efi_capsule.c +++ b/lib/efi_loader/efi_capsule.c @@ -919,13 +919,13 @@ static void efi_capsule_scan_done(void) } /** - * arch_efi_load_capsule_drivers - initialize capsule drivers + * efi_load_capsule_drivers - initialize capsule drivers * - * Architecture or board specific initialization routine + * Generic FMP drivers backed by DFU * * Return: status code */ -efi_status_t __weak arch_efi_load_capsule_drivers(void) +efi_status_t __weak efi_load_capsule_drivers(void) { __maybe_unused efi_handle_t handle; efi_status_t ret = EFI_SUCCESS; @@ -976,11 +976,6 @@ efi_status_t efi_launch_capsules(void) index = get_last_capsule(); - /* Load capsule drivers */ - ret = arch_efi_load_capsule_drivers(); - if (ret != EFI_SUCCESS) - return ret; - /* * Find capsules on disk. * All the capsules are collected at the beginning because diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c index 3c5cf9a4357..2dc6ba8337a 100644 --- a/lib/efi_loader/efi_setup.c +++ b/lib/efi_loader/efi_setup.c @@ -254,6 +254,12 @@ efi_status_t efi_init_obj_list(void) if (ret != EFI_SUCCESS) goto out; + if (IS_ENABLED(CONFIG_EFI_HAVE_CAPSULE_SUPPORT)) { + ret = efi_load_capsule_drivers(); + if (ret != EFI_SUCCESS) + goto out; + } + #if defined(CONFIG_LCD) || defined(CONFIG_DM_VIDEO) ret = efi_gop_register(); if (ret != EFI_SUCCESS) From abd62e4d41112893d70ddcbe7faad887116ed323 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 29 Jun 2021 10:09:14 +0200 Subject: [PATCH 4/7] efi_loader: missing parentheses in query_console_size After if we should use parentheses to keep the code readable. Fixes: a95f4c885991 ("efi_loader: NULL dereference in EFI console") Signed-off-by: Heinrich Schuchardt --- lib/efi_loader/efi_console.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/efi_loader/efi_console.c b/lib/efi_loader/efi_console.c index 6040f3a99ab..3b012e1a669 100644 --- a/lib/efi_loader/efi_console.c +++ b/lib/efi_loader/efi_console.c @@ -342,7 +342,7 @@ static void query_console_size(void) int rows = 25, cols = 80; int ret = -ENODEV; - if IS_ENABLED(CONFIG_DM_VIDEO) + if (IS_ENABLED(CONFIG_DM_VIDEO)) ret = query_vidconsole(&rows, &cols); if (ret) ret = query_console_serial(&rows, &cols); From 417a3c24c9991d3d9df8401063188324bf404952 Mon Sep 17 00:00:00 2001 From: Masami Hiramatsu Date: Thu, 1 Jul 2021 00:49:48 +0900 Subject: [PATCH 5/7] efi_loader: Improve the parameter check for QueryVariableInfo() Improve efi_query_variable_info() to check the parameter settings and return correct error code according to the UEFI Specification 2.9, and the Self Certification Test (SCT) II Case Specification, June 2017, chapter 4.1.4 QueryVariableInfo(). Reported-by: Kazuhiko Sakamoto Signed-off-by: Masami Hiramatsu Reviewed-by: Heinrich Schuchardt --- lib/efi_loader/efi_var_common.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/efi_loader/efi_var_common.c b/lib/efi_loader/efi_var_common.c index 83479dd142a..3d92afe2ebd 100644 --- a/lib/efi_loader/efi_var_common.c +++ b/lib/efi_loader/efi_var_common.c @@ -163,6 +163,19 @@ efi_status_t EFIAPI efi_query_variable_info( EFI_ENTRY("%x %p %p %p", attributes, maximum_variable_storage_size, remaining_variable_storage_size, maximum_variable_size); + if (!maximum_variable_storage_size || + !remaining_variable_storage_size || + !maximum_variable_size || + !(attributes & EFI_VARIABLE_BOOTSERVICE_ACCESS)) + return EFI_EXIT(EFI_INVALID_PARAMETER); + + if ((attributes & ~(u32)EFI_VARIABLE_MASK) || + (attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) || + (attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD) || + (!IS_ENABLED(CONFIG_EFI_SECURE_BOOT) && + (attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS))) + return EFI_EXIT(EFI_UNSUPPORTED); + ret = efi_query_variable_info_int(attributes, maximum_variable_storage_size, remaining_variable_storage_size, From 149108a3eb594b03561073aa6fd512cacab57c62 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Tue, 29 Jun 2021 13:33:09 +0200 Subject: [PATCH 6/7] efi_loader: clear OsIndications After each reboot we must clear flag EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED in variable OsIndications. Signed-off-by: Heinrich Schuchardt --- lib/efi_loader/efi_setup.c | 40 +++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c index 2dc6ba8337a..a2338d74afa 100644 --- a/lib/efi_loader/efi_setup.c +++ b/lib/efi_loader/efi_setup.c @@ -5,9 +5,12 @@ * Copyright (c) 2016-2018 Alexander Graf et al. */ +#define LOG_CATEGORY LOGC_EFI + #include #include #include +#include #define OBJ_LIST_NOT_INITIALIZED 1 @@ -171,6 +174,37 @@ static efi_status_t efi_init_os_indications(void) &os_indications_supported, false); } + +/** + * efi_clear_os_indications() - clear OsIndications + * + * Clear EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED + */ +static efi_status_t efi_clear_os_indications(void) +{ + efi_uintn_t size; + u64 os_indications; + efi_status_t ret; + + size = sizeof(os_indications); + ret = efi_get_variable_int(L"OsIndications", &efi_global_variable_guid, + NULL, &size, &os_indications, NULL); + if (ret != EFI_SUCCESS) + os_indications = 0; + else + os_indications &= + ~EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED; + ret = efi_set_variable_int(L"OsIndications", &efi_global_variable_guid, + EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + sizeof(os_indications), &os_indications, + false); + if (ret != EFI_SUCCESS) + log_err("Setting %ls failed\n", L"OsIndications"); + return ret; +} + /** * efi_init_obj_list() - Initialize and populate EFI object list * @@ -178,7 +212,7 @@ static efi_status_t efi_init_os_indications(void) */ efi_status_t efi_init_obj_list(void) { - efi_status_t ret = EFI_SUCCESS; + efi_status_t r, ret = EFI_SUCCESS; /* Initialize once only */ if (efi_obj_list_initialized != OBJ_LIST_NOT_INITIALIZED) @@ -297,7 +331,11 @@ efi_status_t efi_init_obj_list(void) if (IS_ENABLED(CONFIG_EFI_CAPSULE_ON_DISK) && !IS_ENABLED(CONFIG_EFI_CAPSULE_ON_DISK_EARLY)) ret = efi_launch_capsules(); + out: + r = efi_clear_os_indications(); + if (ret == EFI_SUCCESS) + ret = r; efi_obj_list_initialized = ret; return ret; } From 0fa5020c024e49222ca97ead3502b332d35dea76 Mon Sep 17 00:00:00 2001 From: Ilias Apalodimas Date: Tue, 29 Jun 2021 07:55:51 +0300 Subject: [PATCH 7/7] efi_loader: Allow capsule update on-disk without checking OsIndications Although U-Boot supports capsule update on-disk, it's lack of support for SetVariable at runtime prevents applications like fwupd from using it. In order to perform the capsule update on-disk the spec says that the OS must copy the capsule to the \EFI\UpdateCapsule directory and set a bit in the OsIndications variable. The firmware then checks for the EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED bit in OsIndications variable, which is set by the submitter to trigger processing of the capsule on the next reboot. Let's add a config option which ignores the bit and just relies on the capsule being present. Since U-Boot deletes the capsule while processing it, we won't end up applying it multiple times. Note that this is allowed for all capsules. In the future, once authenticated capsules are fully supported, we can limit the functionality to those only. Signed-off-by: apalos Reword Kconfig description. Reviewed-by: Heinrich Schuchardt Signed-off-by: Heinrich Schuchardt --- lib/efi_loader/Kconfig | 10 ++++++++++ lib/efi_loader/efi_capsule.c | 36 ++++++++++++++++++++++++++++-------- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index 684adfb6237..156b3915211 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -137,6 +137,16 @@ config EFI_CAPSULE_ON_DISK under a specific directory on UEFI system partition instead of via UpdateCapsule API. +config EFI_IGNORE_OSINDICATIONS + bool "Ignore OsIndications for CapsuleUpdate on-disk" + depends on EFI_CAPSULE_ON_DISK + default n + help + There are boards where U-Boot does not support SetVariable at runtime. + Select this option if you want to use the capsule-on-disk feature + without setting the EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED + flag in variable OsIndications. + config EFI_CAPSULE_ON_DISK_EARLY bool "Initiate capsule-on-disk at U-Boot boottime" depends on EFI_CAPSULE_ON_DISK diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c index d7136035d8f..50bed32bfb3 100644 --- a/lib/efi_loader/efi_capsule.c +++ b/lib/efi_loader/efi_capsule.c @@ -948,6 +948,33 @@ efi_status_t __weak efi_load_capsule_drivers(void) return ret; } +/** + * check_run_capsules - Check whether capsule update should run + * + * The spec says OsIndications must be set in order to run the capsule update + * on-disk. Since U-Boot doesn't support runtime SetVariable, allow capsules to + * run explicitly if CONFIG_EFI_IGNORE_OSINDICATIONS is selected + */ +static bool check_run_capsules(void) +{ + u64 os_indications; + efi_uintn_t size; + efi_status_t ret; + + if (IS_ENABLED(CONFIG_EFI_IGNORE_OSINDICATIONS)) + return true; + + size = sizeof(os_indications); + ret = efi_get_variable_int(L"OsIndications", &efi_global_variable_guid, + NULL, &size, &os_indications, NULL); + if (ret == EFI_SUCCESS && + (os_indications + & EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED)) + return true; + + return false; +} + /** * efi_launch_capsule - launch capsules * @@ -958,20 +985,13 @@ efi_status_t __weak efi_load_capsule_drivers(void) */ efi_status_t efi_launch_capsules(void) { - u64 os_indications; - efi_uintn_t size; struct efi_capsule_header *capsule = NULL; u16 **files; unsigned int nfiles, index, i; u16 variable_name16[12]; efi_status_t ret; - size = sizeof(os_indications); - ret = efi_get_variable_int(L"OsIndications", &efi_global_variable_guid, - NULL, &size, &os_indications, NULL); - if (ret != EFI_SUCCESS || - !(os_indications - & EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED)) + if (!check_run_capsules()) return EFI_SUCCESS; index = get_last_capsule();