From c8d0fd582576ff7cc67d0053282430476201fd33 Mon Sep 17 00:00:00 2001 From: Ilias Apalodimas Date: Mon, 30 Nov 2020 11:47:40 +0200 Subject: efi_loader: Introduce eventlog support for TCG2_PROTOCOL In the previous patches we only introduced a minimal subset of the EFI_TCG2_PROTOCOL protocol implementing GetCapability(). So let's continue adding features to it, introducing the GetEventLog() and HashLogExtendEvent() functions. In order to do that we first need to construct the eventlog in memory, specifically in EFI_BOOT_SERVICES_DATA memory and a configuration table from EFI_ACPI_MEMORY_NVS. U-Boot won't currently add any events to the log or measure any components, but will expose the necessary EFI APIs for applications to do so. Signed-off-by: Ilias Apalodimas --- lib/efi_loader/efi_setup.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'lib/efi_loader/efi_setup.c') diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c index e206b60bb8..2bb2c3c7aa 100644 --- a/lib/efi_loader/efi_setup.c +++ b/lib/efi_loader/efi_setup.c @@ -157,12 +157,6 @@ efi_status_t efi_init_obj_list(void) goto out; } - if (IS_ENABLED(CONFIG_EFI_TCG2_PROTOCOL)) { - ret = efi_tcg2_register(); - if (ret != EFI_SUCCESS) - goto out; - } - /* Initialize variable services */ ret = efi_init_variables(); if (ret != EFI_SUCCESS) @@ -189,6 +183,12 @@ efi_status_t efi_init_obj_list(void) if (ret != EFI_SUCCESS) goto out; + if (IS_ENABLED(CONFIG_EFI_TCG2_PROTOCOL)) { + ret = efi_tcg2_register(); + if (ret != EFI_SUCCESS) + goto out; + } + /* Secure boot */ ret = efi_init_secure_boot(); if (ret != EFI_SUCCESS) -- cgit v1.2.3 From 2bc27ca8a04aea89c82b5fc4412e889d1ac9f756 Mon Sep 17 00:00:00 2001 From: AKASHI Takahiro Date: Tue, 17 Nov 2020 09:27:55 +0900 Subject: efi_loader: define UpdateCapsule api In this commit, skeleton functions for capsule-related API's are added under CONFIG_EFI_UPDATE_CAPSULE configuration. Detailed implementation for a specific capsule type will be added in the succeeding patches. Signed-off-by: AKASHI Takahiro --- include/efi_api.h | 12 ++++ include/efi_loader.h | 13 ++++ lib/efi_loader/Kconfig | 11 +++ lib/efi_loader/Makefile | 1 + lib/efi_loader/efi_capsule.c | 165 +++++++++++++++++++++++++++++++++++++++++++ lib/efi_loader/efi_runtime.c | 104 +++++++++++++++------------ lib/efi_loader/efi_setup.c | 64 ++++++++++++++--- 7 files changed, 316 insertions(+), 54 deletions(-) create mode 100644 lib/efi_loader/efi_capsule.c (limited to 'lib/efi_loader/efi_setup.c') diff --git a/include/efi_api.h b/include/efi_api.h index 364c578a3b..a466e2c9e6 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -217,6 +217,10 @@ enum efi_reset_type { #define CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE 0x00020000 #define CAPSULE_FLAGS_INITIATE_RESET 0x00040000 +#define EFI_CAPSULE_REPORT_GUID \ + EFI_GUID(0x39b68c46, 0xf7fb, 0x441b, 0xb6, 0xec, \ + 0x16, 0xb0, 0xf6, 0x98, 0x21, 0xf3) + struct efi_capsule_header { efi_guid_t capsule_guid; u32 header_size; @@ -224,6 +228,14 @@ struct efi_capsule_header { u32 capsule_image_size; } __packed; +struct efi_capsule_result_variable_header { + u32 variable_total_size; + u32 reserved; + efi_guid_t capsule_guid; + struct efi_time capsule_processed; + efi_status_t capsule_status; +} __packed; + #define EFI_RT_SUPPORTED_GET_TIME 0x0001 #define EFI_RT_SUPPORTED_SET_TIME 0x0002 #define EFI_RT_SUPPORTED_GET_WAKEUP_TIME 0x0004 diff --git a/include/efi_loader.h b/include/efi_loader.h index 3c68b85b68..abd6856b3d 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -210,6 +210,8 @@ extern const efi_guid_t efi_guid_cert_type_pkcs7; /* GUID of RNG protocol */ extern const efi_guid_t efi_guid_rng_protocol; +/* GUID of capsule update result */ +extern const efi_guid_t efi_guid_capsule_report; extern unsigned int __efi_runtime_start, __efi_runtime_stop; extern unsigned int __efi_runtime_rel_start, __efi_runtime_rel_stop; @@ -812,6 +814,17 @@ void efi_memcpy_runtime(void *dest, const void *src, size_t n); /* commonly used helper function */ u16 *efi_create_indexed_name(u16 *buffer, const char *name, unsigned int index); +/* Capsule update */ +efi_status_t EFIAPI efi_update_capsule( + struct efi_capsule_header **capsule_header_array, + efi_uintn_t capsule_count, + u64 scatter_gather_list); +efi_status_t EFIAPI efi_query_capsule_caps( + struct efi_capsule_header **capsule_header_array, + efi_uintn_t capsule_count, + u64 *maximum_capsule_size, + u32 *reset_type); + #else /* CONFIG_IS_ENABLED(EFI_LOADER) */ /* Without CONFIG_EFI_LOADER we don't have a runtime section, stub it out */ diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index 1b983fe580..1681b37c49 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -94,6 +94,17 @@ config EFI_SET_TIME Provide the SetTime() runtime service at boottime. This service can be used by an EFI application to adjust the real time clock. +config EFI_HAVE_CAPSULE_SUPPORT + bool + +config EFI_RUNTIME_UPDATE_CAPSULE + bool "UpdateCapsule() runtime service" + default n + select EFI_HAVE_CAPSULE_SUPPORT + help + Select this option if you want to use UpdateCapsule and + QueryCapsuleCapabilities API's. + config EFI_DEVICE_PATH_TO_TEXT bool "Device path to text protocol" default y diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile index cd4b252a41..056376ff7a 100644 --- a/lib/efi_loader/Makefile +++ b/lib/efi_loader/Makefile @@ -23,6 +23,7 @@ endif obj-$(CONFIG_CMD_BOOTEFI_HELLO) += helloworld_efi.o obj-y += efi_bootmgr.o obj-y += efi_boottime.o +obj-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += efi_capsule.o obj-y += efi_console.o obj-y += efi_device_path.o obj-$(CONFIG_EFI_DEVICE_PATH_TO_TEXT) += efi_device_path_to_text.o diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c new file mode 100644 index 0000000000..575fa75b0a --- /dev/null +++ b/lib/efi_loader/efi_capsule.c @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * EFI Capsule + * + * Copyright (c) 2018 Linaro Limited + * Author: AKASHI Takahiro + */ + +#include +#include +#include +#include +#include +#include + +const efi_guid_t efi_guid_capsule_report = EFI_CAPSULE_REPORT_GUID; + +/** + * get_last_capsule - get the last capsule index + * + * Retrieve the index of the capsule invoked last time from "CapsuleLast" + * variable. + * + * Return: + * * > 0 - the last capsule index invoked + * * 0xffff - on error, or no capsule invoked yet + */ +static __maybe_unused unsigned int get_last_capsule(void) +{ + u16 value16[11]; /* "CapsuleXXXX": non-null-terminated */ + char value[11], *p; + efi_uintn_t size; + unsigned long index = 0xffff; + efi_status_t ret; + + size = sizeof(value16); + ret = efi_get_variable_int(L"CapsuleLast", &efi_guid_capsule_report, + NULL, &size, value16, NULL); + if (ret != EFI_SUCCESS || u16_strncmp(value16, L"Capsule", 7)) + goto err; + + p = value; + utf16_utf8_strcpy(&p, value16); + strict_strtoul(&value[7], 16, &index); +err: + return index; +} + +/** + * set_capsule_result - set a result variable + * @capsule: Capsule + * @return_status: Return status + * + * Create and set a result variable, "CapsuleXXXX", for the capsule, + * @capsule. + */ +static __maybe_unused +void set_capsule_result(int index, struct efi_capsule_header *capsule, + efi_status_t return_status) +{ + u16 variable_name16[12]; + struct efi_capsule_result_variable_header result; + struct efi_time time; + efi_status_t ret; + + efi_create_indexed_name(variable_name16, "Capsule", index); + + result.variable_total_size = sizeof(result); + result.capsule_guid = capsule->capsule_guid; + ret = EFI_CALL((*efi_runtime_services.get_time)(&time, NULL)); + if (ret == EFI_SUCCESS) + memcpy(&result.capsule_processed, &time, sizeof(time)); + else + memset(&result.capsule_processed, 0, sizeof(time)); + result.capsule_status = return_status; + ret = efi_set_variable(variable_name16, &efi_guid_capsule_report, + EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + sizeof(result), &result); + if (ret) + printf("EFI: creating %ls failed\n", variable_name16); +} + +/** + * efi_update_capsule() - process information from operating system + * @capsule_header_array: Array of virtual address pointers + * @capsule_count: Number of pointers in capsule_header_array + * @scatter_gather_list: Array of physical address pointers + * + * This function implements the UpdateCapsule() runtime service. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + * + * Return: status code + */ +efi_status_t EFIAPI efi_update_capsule( + struct efi_capsule_header **capsule_header_array, + efi_uintn_t capsule_count, + u64 scatter_gather_list) +{ + struct efi_capsule_header *capsule; + unsigned int i; + efi_status_t ret; + + EFI_ENTRY("%p, %lu, %llu\n", capsule_header_array, capsule_count, + scatter_gather_list); + + if (!capsule_count) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + ret = EFI_UNSUPPORTED; + for (i = 0, capsule = *capsule_header_array; i < capsule_count; + i++, capsule = *(++capsule_header_array)) { + } +out: + return EFI_EXIT(ret); +} + +/** + * efi_query_capsule_caps() - check if capsule is supported + * @capsule_header_array: Array of virtual pointers + * @capsule_count: Number of pointers in capsule_header_array + * @maximum_capsule_size: Maximum capsule size + * @reset_type: Type of reset needed for capsule update + * + * This function implements the QueryCapsuleCapabilities() runtime service. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + * + * Return: status code + */ +efi_status_t EFIAPI efi_query_capsule_caps( + struct efi_capsule_header **capsule_header_array, + efi_uintn_t capsule_count, + u64 *maximum_capsule_size, + u32 *reset_type) +{ + struct efi_capsule_header *capsule __attribute__((unused)); + unsigned int i; + efi_status_t ret; + + EFI_ENTRY("%p, %lu, %p, %p\n", capsule_header_array, capsule_count, + maximum_capsule_size, reset_type); + + if (!maximum_capsule_size) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + *maximum_capsule_size = U64_MAX; + *reset_type = EFI_RESET_COLD; + + ret = EFI_SUCCESS; + for (i = 0, capsule = *capsule_header_array; i < capsule_count; + i++, capsule = *(++capsule_header_array)) { + /* TODO */ + } +out: + return EFI_EXIT(ret); +} diff --git a/lib/efi_loader/efi_runtime.c b/lib/efi_loader/efi_runtime.c index 1fa1595e40..0b171c1ff7 100644 --- a/lib/efi_loader/efi_runtime.c +++ b/lib/efi_loader/efi_runtime.c @@ -133,6 +133,10 @@ efi_status_t efi_init_runtime_supported(void) #ifdef CONFIG_EFI_HAVE_RUNTIME_RESET rt_table->runtime_services_supported |= EFI_RT_SUPPORTED_RESET_SYSTEM; #endif + if (IS_ENABLED(CONFIG_EFI_RUNTIME_UPDATE_CAPSULE)) + rt_table->runtime_services_supported |= + (EFI_RT_SUPPORTED_UPDATE_CAPSULE | + EFI_RT_SUPPORTED_QUERY_CAPSULE_CAPABILITIES); ret = efi_install_configuration_table(&efi_rt_properties_table_guid, rt_table); @@ -448,6 +452,50 @@ efi_status_t __weak __efi_runtime EFIAPI efi_set_time(struct efi_time *time) return EFI_UNSUPPORTED; } +/** + * efi_update_capsule_unsupported() - process information from operating system + * + * This function implements the UpdateCapsule() runtime service. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + * + * @capsule_header_array: pointer to array of virtual pointers + * @capsule_count: number of pointers in capsule_header_array + * @scatter_gather_list: pointer to array of physical pointers + * Returns: status code + */ +efi_status_t __efi_runtime EFIAPI efi_update_capsule_unsupported( + struct efi_capsule_header **capsule_header_array, + efi_uintn_t capsule_count, + u64 scatter_gather_list) +{ + return EFI_UNSUPPORTED; +} + +/** + * efi_query_capsule_caps_unsupported() - check if capsule is supported + * + * This function implements the QueryCapsuleCapabilities() runtime service. + * + * See the Unified Extensible Firmware Interface (UEFI) specification for + * details. + * + * @capsule_header_array: pointer to array of virtual pointers + * @capsule_count: number of pointers in capsule_header_array + * @maximum_capsule_size: maximum capsule size + * @reset_type: type of reset needed for capsule update + * Returns: status code + */ +efi_status_t __efi_runtime EFIAPI efi_query_capsule_caps_unsupported( + struct efi_capsule_header **capsule_header_array, + efi_uintn_t capsule_count, + u64 *maximum_capsule_size, + u32 *reset_type) +{ + return EFI_UNSUPPORTED; +} + /** * efi_is_runtime_service_pointer() - check if pointer points to runtime table * @@ -471,6 +519,13 @@ void efi_runtime_detach(void) efi_runtime_services.reset_system = efi_reset_system; efi_runtime_services.get_time = efi_get_time; efi_runtime_services.set_time = efi_set_time; + if (IS_ENABLED(CONFIG_EFI_RUNTIME_UPDATE_CAPSULE)) { + /* won't support at runtime */ + efi_runtime_services.update_capsule = + efi_update_capsule_unsupported; + efi_runtime_services.query_capsule_caps = + efi_query_capsule_caps_unsupported; + } /* Update CRC32 */ efi_update_table_header_crc32(&efi_runtime_services.hdr); @@ -879,50 +934,6 @@ static efi_status_t __efi_runtime EFIAPI efi_unimplemented(void) return EFI_UNSUPPORTED; } -/** - * efi_update_capsule() - process information from operating system - * - * This function implements the UpdateCapsule() runtime service. - * - * See the Unified Extensible Firmware Interface (UEFI) specification for - * details. - * - * @capsule_header_array: pointer to array of virtual pointers - * @capsule_count: number of pointers in capsule_header_array - * @scatter_gather_list: pointer to arry of physical pointers - * Returns: status code - */ -efi_status_t __efi_runtime EFIAPI efi_update_capsule( - struct efi_capsule_header **capsule_header_array, - efi_uintn_t capsule_count, - u64 scatter_gather_list) -{ - return EFI_UNSUPPORTED; -} - -/** - * efi_query_capsule_caps() - check if capsule is supported - * - * This function implements the QueryCapsuleCapabilities() runtime service. - * - * See the Unified Extensible Firmware Interface (UEFI) specification for - * details. - * - * @capsule_header_array: pointer to array of virtual pointers - * @capsule_count: number of pointers in capsule_header_array - * @maximum_capsule_size: maximum capsule size - * @reset_type: type of reset needed for capsule update - * Returns: status code - */ -efi_status_t __efi_runtime EFIAPI efi_query_capsule_caps( - struct efi_capsule_header **capsule_header_array, - efi_uintn_t capsule_count, - u64 *maximum_capsule_size, - u32 *reset_type) -{ - return EFI_UNSUPPORTED; -} - struct efi_runtime_services __efi_runtime_data efi_runtime_services = { .hdr = { .signature = EFI_RUNTIME_SERVICES_SIGNATURE, @@ -940,7 +951,12 @@ struct efi_runtime_services __efi_runtime_data efi_runtime_services = { .set_variable = efi_set_variable, .get_next_high_mono_count = (void *)&efi_unimplemented, .reset_system = &efi_reset_system_boottime, +#ifdef CONFIG_EFI_RUNTIME_UPDATE_CAPSULE .update_capsule = efi_update_capsule, .query_capsule_caps = efi_query_capsule_caps, +#else + .update_capsule = efi_update_capsule_unsupported, + .query_capsule_caps = efi_query_capsule_caps_unsupported, +#endif .query_variable_info = efi_query_variable_info, }; diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c index 2bb2c3c7aa..3dd3f56669 100644 --- a/lib/efi_loader/efi_setup.c +++ b/lib/efi_loader/efi_setup.c @@ -100,9 +100,9 @@ static efi_status_t efi_init_secure_boot(void) ret = efi_set_variable_int(L"SignatureSupport", &efi_global_variable_guid, + EFI_VARIABLE_READ_ONLY | EFI_VARIABLE_BOOTSERVICE_ACCESS | - EFI_VARIABLE_RUNTIME_ACCESS | - EFI_VARIABLE_READ_ONLY, + EFI_VARIABLE_RUNTIME_ACCESS, sizeof(signature_types), &signature_types, false); if (ret != EFI_SUCCESS) @@ -117,6 +117,53 @@ static efi_status_t efi_init_secure_boot(void) } #endif /* CONFIG_EFI_SECURE_BOOT */ +/** + * efi_init_capsule - initialize capsule update state + * + * Return: status code + */ +static efi_status_t efi_init_capsule(void) +{ + efi_status_t ret = EFI_SUCCESS; + + if (IS_ENABLED(CONFIG_EFI_HAVE_CAPSULE_UPDATE)) { + ret = efi_set_variable_int(L"CapsuleMax", + &efi_guid_capsule_report, + EFI_VARIABLE_READ_ONLY | + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + 22, L"CapsuleFFFF", false); + if (ret != EFI_SUCCESS) + printf("EFI: cannot initialize CapsuleMax variable\n"); + } + + return ret; +} + +/** + * efi_init_os_indications() - indicate supported features for OS requests + * + * Set the OsIndicationsSupported variable. + * + * Return: status code + */ +static efi_status_t efi_init_os_indications(void) +{ + u64 os_indications_supported = 0; + + if (IS_ENABLED(CONFIG_EFI_HAVE_CAPSULE_SUPPORT)) + os_indications_supported |= + EFI_OS_INDICATIONS_CAPSULE_RESULT_VAR_SUPPORTED; + + return efi_set_variable_int(L"OsIndicationsSupported", + &efi_global_variable_guid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS | + EFI_VARIABLE_READ_ONLY, + sizeof(os_indications_supported), + &os_indications_supported, false); +} + /** * efi_init_obj_list() - Initialize and populate EFI object list * @@ -124,7 +171,6 @@ static efi_status_t efi_init_secure_boot(void) */ efi_status_t efi_init_obj_list(void) { - u64 os_indications_supported = 0; /* None */ efi_status_t ret = EFI_SUCCESS; /* Initialize once only */ @@ -168,13 +214,7 @@ efi_status_t efi_init_obj_list(void) goto out; /* Indicate supported features */ - ret = efi_set_variable_int(L"OsIndicationsSupported", - &efi_global_variable_guid, - EFI_VARIABLE_BOOTSERVICE_ACCESS | - EFI_VARIABLE_RUNTIME_ACCESS | - EFI_VARIABLE_READ_ONLY, - sizeof(os_indications_supported), - &os_indications_supported, false); + ret = efi_init_os_indications(); if (ret != EFI_SUCCESS) goto out; @@ -233,6 +273,10 @@ efi_status_t efi_init_obj_list(void) if (ret != EFI_SUCCESS) goto out; + ret = efi_init_capsule(); + if (ret != EFI_SUCCESS) + goto out; + /* Initialize EFI runtime services */ ret = efi_reset_system_init(); if (ret != EFI_SUCCESS) -- cgit v1.2.3 From c74cd8bd08d1bbff366a1bfb0cc82a2413fdf106 Mon Sep 17 00:00:00 2001 From: AKASHI Takahiro Date: Tue, 17 Nov 2020 09:27:56 +0900 Subject: efi_loader: capsule: add capsule_on_disk support Capsule data can be loaded into the system either via UpdateCapsule runtime service or files on a file system (of boot device). The latter case is called "capsules on disk", and actual updates will take place at the next boot time. In this commit, we will support capsule on disk mechanism. Please note that U-Boot itself has no notion of "boot device" and all the capsule files to be executed will be detected only if they are located in a specific directory, \EFI\UpdateCapsule, on a device that is identified as a boot device by "BootXXXX" variables. Signed-off-by: AKASHI Takahiro --- common/main.c | 4 + include/efi_loader.h | 9 + lib/efi_loader/Kconfig | 22 ++ lib/efi_loader/efi_capsule.c | 498 +++++++++++++++++++++++++++++++++++++++++++ lib/efi_loader/efi_setup.c | 8 + 5 files changed, 541 insertions(+) (limited to 'lib/efi_loader/efi_setup.c') diff --git a/common/main.c b/common/main.c index 4b3cd302c3..ae5bcdb32f 100644 --- a/common/main.c +++ b/common/main.c @@ -16,6 +16,7 @@ #include #include #include +#include static void run_preboot_environment_command(void) { @@ -53,6 +54,9 @@ void main_loop(void) if (IS_ENABLED(CONFIG_UPDATE_TFTP)) update_tftp(0UL, NULL, NULL); + if (IS_ENABLED(CONFIG_EFI_CAPSULE_ON_DISK_EARLY)) + efi_launch_capsules(); + s = bootdelay_process(); if (cli_process_fdt(&s)) cli_secure_boot_cmd(s); diff --git a/include/efi_loader.h b/include/efi_loader.h index abd6856b3d..de1d24b333 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -825,6 +825,11 @@ efi_status_t EFIAPI efi_query_capsule_caps( u64 *maximum_capsule_size, u32 *reset_type); +#define EFI_CAPSULE_DIR L"\\EFI\\UpdateCapsule\\" + +/* Hook at initialization */ +efi_status_t efi_launch_capsules(void); + #else /* CONFIG_IS_ENABLED(EFI_LOADER) */ /* Without CONFIG_EFI_LOADER we don't have a runtime section, stub it out */ @@ -841,6 +846,10 @@ static inline void efi_set_bootdev(const char *dev, const char *devnr, const char *path) { } static inline void efi_net_set_dhcp_ack(void *pkt, int len) { } static inline void efi_print_image_infos(void *pc) { } +static inline efi_status_t efi_launch_capsules(void) +{ + return EFI_SUCCESS; +} #endif /* CONFIG_IS_ENABLED(EFI_LOADER) */ diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index 1681b37c49..23ba065e04 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -105,6 +105,28 @@ config EFI_RUNTIME_UPDATE_CAPSULE Select this option if you want to use UpdateCapsule and QueryCapsuleCapabilities API's. +config EFI_CAPSULE_ON_DISK + bool "Enable capsule-on-disk support" + select EFI_HAVE_CAPSULE_SUPPORT + default n + help + Select this option if you want to use capsule-on-disk feature, + that is, capsules can be fetched and executed from files + under a specific directory on UEFI system partition instead of + via UpdateCapsule API. + +config EFI_CAPSULE_ON_DISK_EARLY + bool "Initiate capsule-on-disk at U-Boot boottime" + depends on EFI_CAPSULE_ON_DISK + default n + select EFI_SETUP_EARLY + help + Normally, without this option enabled, capsules will be + executed only at the first time of invoking one of efi command. + If this option is enabled, capsules will be enforced to be + executed as part of U-Boot initialisation so that they will + surely take place whatever is set to distro_bootcmd. + config EFI_DEVICE_PATH_TO_TEXT bool "Device path to text protocol" default y diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c index 575fa75b0a..b3c7d1b735 100644 --- a/lib/efi_loader/efi_capsule.c +++ b/lib/efi_loader/efi_capsule.c @@ -11,10 +11,16 @@ #include #include #include +#include #include const efi_guid_t efi_guid_capsule_report = EFI_CAPSULE_REPORT_GUID; +#ifdef CONFIG_EFI_CAPSULE_ON_DISK +/* for file system access */ +static struct efi_file_handle *bootdev_root; +#endif + /** * get_last_capsule - get the last capsule index * @@ -163,3 +169,495 @@ efi_status_t EFIAPI efi_query_capsule_caps( out: return EFI_EXIT(ret); } + +#ifdef CONFIG_EFI_CAPSULE_ON_DISK +/** + * get_dp_device - retrieve a device path from boot variable + * @boot_var: Boot variable name + * @device_dp Device path + * + * Retrieve a device patch from boot variable, @boot_var. + * + * Return: status code + */ +static efi_status_t get_dp_device(u16 *boot_var, + struct efi_device_path **device_dp) +{ + void *buf = NULL; + efi_uintn_t size; + struct efi_load_option lo; + struct efi_device_path *file_dp; + efi_status_t ret; + + size = 0; + ret = efi_get_variable_int(boot_var, &efi_global_variable_guid, + NULL, &size, NULL, NULL); + if (ret == EFI_BUFFER_TOO_SMALL) { + buf = malloc(size); + if (!buf) + return EFI_OUT_OF_RESOURCES; + ret = efi_get_variable_int(boot_var, &efi_global_variable_guid, + NULL, &size, buf, NULL); + } + if (ret != EFI_SUCCESS) + return ret; + + efi_deserialize_load_option(&lo, buf, &size); + + if (lo.attributes & LOAD_OPTION_ACTIVE) { + efi_dp_split_file_path(lo.file_path, device_dp, &file_dp); + efi_free_pool(file_dp); + + ret = EFI_SUCCESS; + } else { + ret = EFI_NOT_FOUND; + } + + free(buf); + + return ret; +} + +/** + * device_is_present_and_system_part - check if a device exists + * @dp Device path + * + * Check if a device pointed to by the device path, @dp, exists and is + * located in UEFI system partition. + * + * Return: true - yes, false - no + */ +static bool device_is_present_and_system_part(struct efi_device_path *dp) +{ + efi_handle_t handle; + + handle = efi_dp_find_obj(dp, NULL); + if (!handle) + return false; + + return efi_disk_is_system_part(handle); +} + +/** + * find_boot_device - identify the boot device + * + * Identify the boot device from boot-related variables as UEFI + * specification describes and put its handle into bootdev_root. + * + * Return: status code + */ +static efi_status_t find_boot_device(void) +{ + char boot_var[9]; + u16 boot_var16[9], *p, bootnext, *boot_order = NULL; + efi_uintn_t size; + int i, num; + struct efi_simple_file_system_protocol *volume; + struct efi_device_path *boot_dev = NULL; + efi_status_t ret; + + /* find active boot device in BootNext */ + bootnext = 0; + size = sizeof(bootnext); + ret = efi_get_variable_int(L"BootNext", + (efi_guid_t *)&efi_global_variable_guid, + NULL, &size, &bootnext, NULL); + if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL) { + /* BootNext does exist here */ + if (ret == EFI_BUFFER_TOO_SMALL || size != sizeof(u16)) { + printf("BootNext must be 16-bit integer\n"); + goto skip; + } + sprintf((char *)boot_var, "Boot%04X", bootnext); + p = boot_var16; + utf8_utf16_strcpy(&p, boot_var); + + ret = get_dp_device(boot_var16, &boot_dev); + if (ret == EFI_SUCCESS) { + if (device_is_present_and_system_part(boot_dev)) { + goto out; + } else { + efi_free_pool(boot_dev); + boot_dev = NULL; + } + } + } + +skip: + /* find active boot device in BootOrder */ + size = 0; + ret = efi_get_variable_int(L"BootOrder", &efi_global_variable_guid, + NULL, &size, NULL, NULL); + if (ret == EFI_BUFFER_TOO_SMALL) { + boot_order = malloc(size); + if (!boot_order) { + ret = EFI_OUT_OF_RESOURCES; + goto out; + } + + ret = efi_get_variable_int(L"BootOrder", + &efi_global_variable_guid, + NULL, &size, boot_order, NULL); + } + if (ret != EFI_SUCCESS) + goto out; + + /* check in higher order */ + num = size / sizeof(u16); + for (i = 0; i < num; i++) { + sprintf((char *)boot_var, "Boot%04X", boot_order[i]); + p = boot_var16; + utf8_utf16_strcpy(&p, boot_var); + ret = get_dp_device(boot_var16, &boot_dev); + if (ret != EFI_SUCCESS) + continue; + + if (device_is_present_and_system_part(boot_dev)) + break; + + efi_free_pool(boot_dev); + boot_dev = NULL; + } +out: + if (boot_dev) { + u16 *path_str; + + path_str = efi_dp_str(boot_dev); + EFI_PRINT("EFI Capsule: bootdev is %ls\n", path_str); + efi_free_pool(path_str); + + volume = efi_fs_from_path(boot_dev); + if (!volume) + ret = EFI_DEVICE_ERROR; + else + ret = EFI_CALL(volume->open_volume(volume, + &bootdev_root)); + efi_free_pool(boot_dev); + } else { + ret = EFI_NOT_FOUND; + } + free(boot_order); + + return ret; +} + +/** + * efi_capsule_scan_dir - traverse a capsule directory in boot device + * @files: Array of file names + * @num: Number of elements in @files + * + * Traverse a capsule directory in boot device. + * Called by initialization code, and returns an array of capsule file + * names in @files. + * + * Return: status code + */ +static efi_status_t efi_capsule_scan_dir(u16 ***files, unsigned int *num) +{ + struct efi_file_handle *dirh; + struct efi_file_info *dirent; + efi_uintn_t dirent_size, tmp_size; + unsigned int count; + u16 **tmp_files; + efi_status_t ret; + + ret = find_boot_device(); + if (ret == EFI_NOT_FOUND) { + EFI_PRINT("EFI Capsule: bootdev is not set\n"); + *num = 0; + return EFI_SUCCESS; + } else if (ret != EFI_SUCCESS) { + return EFI_DEVICE_ERROR; + } + + /* count capsule files */ + ret = EFI_CALL((*bootdev_root->open)(bootdev_root, &dirh, + EFI_CAPSULE_DIR, + EFI_FILE_MODE_READ, 0)); + if (ret != EFI_SUCCESS) { + *num = 0; + return EFI_SUCCESS; + } + + dirent_size = 256; + dirent = malloc(dirent_size); + if (!dirent) + return EFI_OUT_OF_RESOURCES; + + count = 0; + while (1) { + tmp_size = dirent_size; + ret = EFI_CALL((*dirh->read)(dirh, &tmp_size, dirent)); + if (ret == EFI_BUFFER_TOO_SMALL) { + dirent = realloc(dirent, tmp_size); + if (!dirent) { + ret = EFI_OUT_OF_RESOURCES; + goto err; + } + dirent_size = tmp_size; + ret = EFI_CALL((*dirh->read)(dirh, &tmp_size, dirent)); + } + if (ret != EFI_SUCCESS) + goto err; + if (!tmp_size) + break; + + if (!(dirent->attribute & EFI_FILE_DIRECTORY) && + u16_strcmp(dirent->file_name, L".") && + u16_strcmp(dirent->file_name, L"..")) + count++; + } + + ret = EFI_CALL((*dirh->setpos)(dirh, 0)); + if (ret != EFI_SUCCESS) + goto err; + + /* make a list */ + tmp_files = malloc(count * sizeof(*files)); + if (!tmp_files) { + ret = EFI_OUT_OF_RESOURCES; + goto err; + } + + count = 0; + while (1) { + tmp_size = dirent_size; + ret = EFI_CALL((*dirh->read)(dirh, &tmp_size, dirent)); + if (ret != EFI_SUCCESS) + goto err; + if (!tmp_size) + break; + + if (!(dirent->attribute & EFI_FILE_DIRECTORY) && + u16_strcmp(dirent->file_name, L".") && + u16_strcmp(dirent->file_name, L"..")) + tmp_files[count++] = u16_strdup(dirent->file_name); + } + /* ignore an error */ + EFI_CALL((*dirh->close)(dirh)); + + /* in ascii order */ + /* FIXME: u16 version of strcasecmp */ + qsort(tmp_files, count, sizeof(*tmp_files), + (int (*)(const void *, const void *))strcasecmp); + *files = tmp_files; + *num = count; + ret = EFI_SUCCESS; +err: + free(dirent); + + return ret; +} + +/** + * efi_capsule_read_file - read in a capsule file + * @filename: File name + * @capsule: Pointer to buffer for capsule + * + * Read a capsule file and put its content in @capsule. + * + * Return: status code + */ +static efi_status_t efi_capsule_read_file(const u16 *filename, + struct efi_capsule_header **capsule) +{ + struct efi_file_handle *dirh, *fh; + struct efi_file_info *file_info = NULL; + struct efi_capsule_header *buf = NULL; + efi_uintn_t size; + efi_status_t ret; + + ret = EFI_CALL((*bootdev_root->open)(bootdev_root, &dirh, + EFI_CAPSULE_DIR, + EFI_FILE_MODE_READ, 0)); + if (ret != EFI_SUCCESS) + return ret; + ret = EFI_CALL((*dirh->open)(dirh, &fh, (u16 *)filename, + EFI_FILE_MODE_READ, 0)); + /* ignore an error */ + EFI_CALL((*dirh->close)(dirh)); + if (ret != EFI_SUCCESS) + return ret; + + /* file size */ + size = 0; + ret = EFI_CALL((*fh->getinfo)(fh, &efi_file_info_guid, + &size, file_info)); + if (ret == EFI_BUFFER_TOO_SMALL) { + file_info = malloc(size); + if (!file_info) { + ret = EFI_OUT_OF_RESOURCES; + goto err; + } + ret = EFI_CALL((*fh->getinfo)(fh, &efi_file_info_guid, + &size, file_info)); + } + if (ret != EFI_SUCCESS) + goto err; + size = file_info->file_size; + free(file_info); + buf = malloc(size); + if (!buf) { + ret = EFI_OUT_OF_RESOURCES; + goto err; + } + + /* fetch data */ + ret = EFI_CALL((*fh->read)(fh, &size, buf)); + if (ret == EFI_SUCCESS) { + if (size >= buf->capsule_image_size) { + *capsule = buf; + } else { + free(buf); + ret = EFI_INVALID_PARAMETER; + } + } else { + free(buf); + } +err: + EFI_CALL((*fh->close)(fh)); + + return ret; +} + +/** + * efi_capsule_delete_file - delete a capsule file + * @filename: File name + * + * Delete a capsule file from capsule directory. + * + * Return: status code + */ +static efi_status_t efi_capsule_delete_file(const u16 *filename) +{ + struct efi_file_handle *dirh, *fh; + efi_status_t ret; + + ret = EFI_CALL((*bootdev_root->open)(bootdev_root, &dirh, + EFI_CAPSULE_DIR, + EFI_FILE_MODE_READ, 0)); + if (ret != EFI_SUCCESS) + return ret; + ret = EFI_CALL((*dirh->open)(dirh, &fh, (u16 *)filename, + EFI_FILE_MODE_READ, 0)); + /* ignore an error */ + EFI_CALL((*dirh->close)(dirh)); + + ret = EFI_CALL((*fh->delete)(fh)); + + return ret; +} + +/** + * efi_capsule_scan_done - reset a scan help function + * + * Reset a scan help function + */ +static void efi_capsule_scan_done(void) +{ + EFI_CALL((*bootdev_root->close)(bootdev_root)); + bootdev_root = NULL; +} + +/** + * arch_efi_load_capsule_drivers - initialize capsule drivers + * + * Architecture or board specific initialization routine + * + * Return: status code + */ +efi_status_t __weak arch_efi_load_capsule_drivers(void) +{ + return EFI_SUCCESS; +} + +/** + * efi_launch_capsule - launch capsules + * + * Launch all the capsules in system at boot time. + * Called by efi init code + * + * Return: status codde + */ +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)) + return EFI_SUCCESS; + + 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 + * capsule files will be removed instantly. + */ + nfiles = 0; + files = NULL; + ret = efi_capsule_scan_dir(&files, &nfiles); + if (ret != EFI_SUCCESS) + return ret; + if (!nfiles) + return EFI_SUCCESS; + + /* Launch capsules */ + for (i = 0, ++index; i < nfiles; i++, index++) { + EFI_PRINT("capsule from %ls ...\n", files[i]); + if (index > 0xffff) + index = 0; + ret = efi_capsule_read_file(files[i], &capsule); + if (ret == EFI_SUCCESS) { + ret = EFI_CALL(efi_update_capsule(&capsule, 1, 0)); + if (ret != EFI_SUCCESS) + printf("EFI Capsule update failed at %ls\n", + files[i]); + + free(capsule); + } else { + printf("EFI: reading capsule failed: %ls\n", + files[i]); + } + /* create CapsuleXXXX */ + set_capsule_result(index, capsule, ret); + + /* delete a capsule either in case of success or failure */ + ret = efi_capsule_delete_file(files[i]); + if (ret != EFI_SUCCESS) + printf("EFI: deleting a capsule file failed: %ls\n", + files[i]); + } + efi_capsule_scan_done(); + + for (i = 0; i < nfiles; i++) + free(files[i]); + free(files); + + /* CapsuleLast */ + efi_create_indexed_name(variable_name16, "Capsule", index - 1); + efi_set_variable_int(L"CapsuleLast", &efi_guid_capsule_report, + EFI_VARIABLE_READ_ONLY | + EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + 22, variable_name16, false); + + return ret; +} +#endif /* CONFIG_EFI_CAPSULE_ON_DISK */ diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c index 3dd3f56669..a126a59f18 100644 --- a/lib/efi_loader/efi_setup.c +++ b/lib/efi_loader/efi_setup.c @@ -155,6 +155,10 @@ static efi_status_t efi_init_os_indications(void) os_indications_supported |= EFI_OS_INDICATIONS_CAPSULE_RESULT_VAR_SUPPORTED; + if (IS_ENABLED(CONFIG_EFI_CAPSULE_ON_DISK)) + os_indications_supported |= + EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED; + return efi_set_variable_int(L"OsIndicationsSupported", &efi_global_variable_guid, EFI_VARIABLE_BOOTSERVICE_ACCESS | @@ -282,6 +286,10 @@ efi_status_t efi_init_obj_list(void) if (ret != EFI_SUCCESS) goto out; + /* Execute capsules after reboot */ + if (IS_ENABLED(CONFIG_EFI_CAPSULE_ON_DISK) && + !IS_ENABLED(CONFIG_EFI_CAPSULE_ON_DISK_EARLY)) + ret = efi_launch_capsules(); out: efi_obj_list_initialized = ret; return ret; -- cgit v1.2.3 From 8d99026f06978ddf2ed72ccaed6cd3ad0887e4e5 Mon Sep 17 00:00:00 2001 From: AKASHI Takahiro Date: Mon, 30 Nov 2020 18:12:11 +0900 Subject: efi_loader: capsule: support firmware update A capsule tagged with the guid, EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID, is handled as a firmware update object. What efi_update_capsule() basically does is to load any firmware management protocol (or fmp) drivers contained in a capsule, find out an appropriate fmp driver and then invoke its set_image() interface against each binary in a capsule. In this commit, however, loading drivers is not supported. The result of applying a capsule is set to be stored in "CapsuleXXXX" variable, but its implementation is deferred to a fmp driver. Signed-off-by: AKASHI Takahiro --- include/efi_api.h | 129 ++++++++++++++++++++++ include/efi_loader.h | 2 + lib/efi_loader/Kconfig | 8 ++ lib/efi_loader/efi_capsule.c | 252 ++++++++++++++++++++++++++++++++++++++++--- lib/efi_loader/efi_setup.c | 4 + 5 files changed, 383 insertions(+), 12 deletions(-) (limited to 'lib/efi_loader/efi_setup.c') diff --git a/include/efi_api.h b/include/efi_api.h index fadf40e8ff..5d2f7bbbe3 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -217,6 +217,9 @@ enum efi_reset_type { #define CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE 0x00020000 #define CAPSULE_FLAGS_INITIATE_RESET 0x00040000 +#define CAPSULE_SUPPORT_AUTHENTICATION 0x0000000000000001 +#define CAPSULE_SUPPORT_DEPENDENCY 0x0000000000000002 + #define EFI_CAPSULE_REPORT_GUID \ EFI_GUID(0x39b68c46, 0xf7fb, 0x441b, 0xb6, 0xec, \ 0x16, 0xb0, 0xf6, 0x98, 0x21, 0xf3) @@ -225,6 +228,10 @@ enum efi_reset_type { EFI_GUID(0xde9f0ec, 0x88b6, 0x428f, 0x97, 0x7a, \ 0x25, 0x8f, 0x1d, 0xe, 0x5e, 0x72) +#define EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID \ + EFI_GUID(0x6dcbd5ed, 0xe82d, 0x4c44, 0xbd, 0xa1, \ + 0x71, 0x94, 0x19, 0x9a, 0xd9, 0x2a) + struct efi_capsule_header { efi_guid_t capsule_guid; u32 header_size; @@ -253,6 +260,33 @@ struct efi_memory_range_capsule { struct efi_memory_range memory_ranges[]; } __packed; +struct efi_firmware_management_capsule_header { + u32 version; + u16 embedded_driver_count; + u16 payload_item_count; + u64 item_offset_list[]; +} __packed; + +struct efi_firmware_management_capsule_image_header { + u32 version; + efi_guid_t update_image_type_id; + u8 update_image_index; + u8 reserved[3]; + u32 update_image_size; + u32 update_vendor_code_size; + u64 update_hardware_instance; + u64 image_capsule_support; +} __packed; + +struct efi_capsule_result_variable_fmp { + u16 version; + u8 payload_index; + u8 update_image_index; + efi_guid_t update_image_type_id; + // u16 capsule_file_name[]; + // u16 capsule_target[]; +} __packed; + #define EFI_RT_SUPPORTED_GET_TIME 0x0001 #define EFI_RT_SUPPORTED_SET_TIME 0x0002 #define EFI_RT_SUPPORTED_GET_WAKEUP_TIME 0x0004 @@ -1812,4 +1846,99 @@ struct efi_signature_list { /* struct efi_signature_data signatures[...][signature_size]; */ } __attribute__((__packed__)); +/* + * Firmware management protocol + */ +#define EFI_FIRMWARE_MANAGEMENT_PROTOCOL_GUID \ + EFI_GUID(0x86c77a67, 0x0b97, 0x4633, 0xa1, 0x87, \ + 0x49, 0x10, 0x4d, 0x06, 0x85, 0xc7) + +#define IMAGE_ATTRIBUTE_IMAGE_UPDATABLE 0x0000000000000001 +#define IMAGE_ATTRIBUTE_RESET_REQUIRED 0x0000000000000002 +#define IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED 0x0000000000000004 +#define IMAGE_ATTRIBUTE_IN_USE 0x0000000000000008 +#define IMAGE_ATTRIBUTE_UEFI_IMAGE 0x0000000000000010 +#define IMAGE_ATTRIBUTE_DEPENDENCY 0x0000000000000020 + +#define IMAGE_COMPATIBILITY_CHECK_SUPPORTED 0x0000000000000001 + +#define IMAGE_UPDATABLE_VALID 0x0000000000000001 +#define IMAGE_UPDATABLE_INVALID 0x0000000000000002 +#define IMAGE_UPDATABLE_INVALID_TYPE 0x0000000000000004 +#define IMAGE_UPDATABLE_INVALID_OLLD 0x0000000000000008 +#define IMAGE_UPDATABLE_VALID_WITH_VENDOR_CODE 0x0000000000000010 + +#define PACKAGE_ATTRIBUTE_VERSION_UPDATABLE 0x0000000000000001 +#define PACKAGE_ATTRIBUTE_RESET_REQUIRED 0x0000000000000002 +#define PACKAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED 0x0000000000000004 + +#define EFI_FIRMWARE_IMAGE_DESCRIPTOR_VERSION 4 + +typedef struct efi_firmware_image_dependencies { + u8 dependencies[0]; +} efi_firmware_image_dep_t; + +struct efi_firmware_image_descriptor { + u8 image_index; + efi_guid_t image_type_id; + u64 image_id; + u16 *image_id_name; + u32 version; + u16 *version_name; + efi_uintn_t size; + u64 attributes_supported; + u64 attributes_setting; + u64 compatibilities; + u32 lowest_supported_image_version; + u32 last_attempt_version; + u32 last_attempt_status; + u64 hardware_instance; + efi_firmware_image_dep_t *dependencies; +}; + +struct efi_firmware_management_protocol { + efi_status_t (EFIAPI *get_image_info)( + struct efi_firmware_management_protocol *this, + efi_uintn_t *image_info_size, + struct efi_firmware_image_descriptor *image_info, + u32 *descriptor_version, + u8 *descriptor_count, + efi_uintn_t *descriptor_size, + u32 *package_version, + u16 **package_version_name); + efi_status_t (EFIAPI *get_image)( + struct efi_firmware_management_protocol *this, + u8 image_index, + void *image, + efi_uintn_t *image_size); + efi_status_t (EFIAPI *set_image)( + struct efi_firmware_management_protocol *this, + u8 image_index, + const void *image, + efi_uintn_t image_size, + const void *vendor_code, + efi_status_t (*progress)(efi_uintn_t completion), + u16 **abort_reason); + efi_status_t (EFIAPI *check_image)( + struct efi_firmware_management_protocol *this, + u8 image_index, + const void *image, + efi_uintn_t *image_size, + u32 *image_updatable); + efi_status_t (EFIAPI *get_package_info)( + struct efi_firmware_management_protocol *this, + u32 *package_version, + u16 **package_version_name, + u32 *package_version_name_maxlen, + u64 *attributes_supported, + u64 *attributes_setting); + efi_status_t (EFIAPI *set_package_info)( + struct efi_firmware_management_protocol *this, + const void *image, + efi_uintn_t *image_size, + const void *vendor_code, + u32 package_version, + const u16 *package_version_name); +}; + #endif diff --git a/include/efi_loader.h b/include/efi_loader.h index de1d24b333..ed5a49b520 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -212,6 +212,8 @@ extern const efi_guid_t efi_guid_cert_type_pkcs7; extern const efi_guid_t efi_guid_rng_protocol; /* GUID of capsule update result */ extern const efi_guid_t efi_guid_capsule_report; +/* GUID of firmware management protocol */ +extern const efi_guid_t efi_guid_firmware_management_protocol; extern unsigned int __efi_runtime_start, __efi_runtime_stop; extern unsigned int __efi_runtime_rel_start, __efi_runtime_rel_stop; diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index 23ba065e04..13e793014d 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -127,6 +127,14 @@ config EFI_CAPSULE_ON_DISK_EARLY executed as part of U-Boot initialisation so that they will surely take place whatever is set to distro_bootcmd. +config EFI_CAPSULE_FIRMWARE_MANAGEMENT + bool "Capsule: Firmware Management Protocol" + depends on EFI_HAVE_CAPSULE_SUPPORT + default y + help + Select this option if you want to enable capsule-based + firmware update using Firmware Management Protocol. + config EFI_DEVICE_PATH_TO_TEXT bool "Device path to text protocol" default y diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c index b3c7d1b735..3e7ad470d4 100644 --- a/lib/efi_loader/efi_capsule.c +++ b/lib/efi_loader/efi_capsule.c @@ -15,6 +15,10 @@ #include const efi_guid_t efi_guid_capsule_report = EFI_CAPSULE_REPORT_GUID; +static const efi_guid_t efi_guid_firmware_management_capsule_id = + EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID; +const efi_guid_t efi_guid_firmware_management_protocol = + EFI_FIRMWARE_MANAGEMENT_PROTOCOL_GUID; #ifdef CONFIG_EFI_CAPSULE_ON_DISK /* for file system access */ @@ -85,9 +89,214 @@ void set_capsule_result(int index, struct efi_capsule_header *capsule, EFI_VARIABLE_RUNTIME_ACCESS, sizeof(result), &result); if (ret) - printf("EFI: creating %ls failed\n", variable_name16); + log_err("EFI: creating %ls failed\n", variable_name16); } +#ifdef CONFIG_EFI_CAPSULE_FIRMWARE_MANAGEMENT +/** + * efi_fmp_find - search for Firmware Management Protocol drivers + * @image_type: Image type guid + * @instance: Instance number + * @handles: Handles of FMP drivers + * @no_handles: Number of handles + * + * Search for Firmware Management Protocol drivers, matching the image + * type, @image_type and the machine instance, @instance, from the list, + * @handles. + * + * Return: + * * Protocol instance - on success + * * NULL - on failure + */ +static struct efi_firmware_management_protocol * +efi_fmp_find(efi_guid_t *image_type, u64 instance, efi_handle_t *handles, + efi_uintn_t no_handles) +{ + efi_handle_t *handle; + struct efi_firmware_management_protocol *fmp; + struct efi_firmware_image_descriptor *image_info, *desc; + efi_uintn_t info_size, descriptor_size; + u32 descriptor_version; + u8 descriptor_count; + u32 package_version; + u16 *package_version_name; + bool found = false; + int i, j; + efi_status_t ret; + + for (i = 0, handle = handles; i < no_handles; i++, handle++) { + ret = EFI_CALL(efi_handle_protocol( + *handle, + &efi_guid_firmware_management_protocol, + (void **)&fmp)); + if (ret != EFI_SUCCESS) + continue; + + /* get device's image info */ + info_size = 0; + image_info = NULL; + descriptor_version = 0; + descriptor_count = 0; + descriptor_size = 0; + package_version = 0; + package_version_name = NULL; + ret = EFI_CALL(fmp->get_image_info(fmp, &info_size, + image_info, + &descriptor_version, + &descriptor_count, + &descriptor_size, + &package_version, + &package_version_name)); + if (ret != EFI_BUFFER_TOO_SMALL) + goto skip; + + image_info = malloc(info_size); + if (!image_info) + goto skip; + + ret = EFI_CALL(fmp->get_image_info(fmp, &info_size, + image_info, + &descriptor_version, + &descriptor_count, + &descriptor_size, + &package_version, + &package_version_name)); + if (ret != EFI_SUCCESS || + descriptor_version != EFI_FIRMWARE_IMAGE_DESCRIPTOR_VERSION) + goto skip; + + /* matching */ + for (j = 0, desc = image_info; j < descriptor_count; + j++, desc = (void *)desc + descriptor_size) { + log_debug("+++ desc[%d] index: %d, name: %ls\n", + j, desc->image_index, desc->image_id_name); + if (!guidcmp(&desc->image_type_id, image_type) && + (!instance || + !desc->hardware_instance || + desc->hardware_instance == instance)) + found = true; + } + +skip: + efi_free_pool(package_version_name); + free(image_info); + EFI_CALL(efi_close_protocol( + (efi_handle_t)fmp, + &efi_guid_firmware_management_protocol, + NULL, NULL)); + if (found) + return fmp; + } + + return NULL; +} + +/** + * efi_capsule_update_firmware - update firmware from capsule + * @capsule_data: Capsule + * + * Update firmware, using a capsule, @capsule_data. Loading any FMP + * drivers embedded in a capsule is not supported. + * + * Return: status code + */ +static efi_status_t efi_capsule_update_firmware( + struct efi_capsule_header *capsule_data) +{ + struct efi_firmware_management_capsule_header *capsule; + struct efi_firmware_management_capsule_image_header *image; + size_t capsule_size; + void *image_binary, *vendor_code; + efi_handle_t *handles; + efi_uintn_t no_handles; + int item; + struct efi_firmware_management_protocol *fmp; + u16 *abort_reason; + efi_status_t ret = EFI_SUCCESS; + + /* sanity check */ + if (capsule_data->header_size < sizeof(*capsule) || + capsule_data->header_size >= capsule_data->capsule_image_size) + return EFI_INVALID_PARAMETER; + + capsule = (void *)capsule_data + capsule_data->header_size; + capsule_size = capsule_data->capsule_image_size + - capsule_data->header_size; + + if (capsule->version != 0x00000001) + return EFI_UNSUPPORTED; + + handles = NULL; + ret = EFI_CALL(efi_locate_handle_buffer( + BY_PROTOCOL, + &efi_guid_firmware_management_protocol, + NULL, &no_handles, (efi_handle_t **)&handles)); + if (ret != EFI_SUCCESS) + return EFI_UNSUPPORTED; + + /* Payload */ + for (item = capsule->embedded_driver_count; + item < capsule->embedded_driver_count + + capsule->payload_item_count; item++) { + /* sanity check */ + if ((capsule->item_offset_list[item] + sizeof(*image) + >= capsule_size)) { + log_err("EFI: A capsule has not enough data\n"); + ret = EFI_INVALID_PARAMETER; + goto out; + } + + image = (void *)capsule + capsule->item_offset_list[item]; + + if (image->version != 0x00000003) { + ret = EFI_UNSUPPORTED; + goto out; + } + + /* find a device for update firmware */ + /* TODO: should we pass index as well, or nothing but type? */ + fmp = efi_fmp_find(&image->update_image_type_id, + image->update_hardware_instance, + handles, no_handles); + if (!fmp) { + log_err("EFI Capsule: driver not found for firmware type: %pUl, hardware instance: %lld\n", + &image->update_image_type_id, + image->update_hardware_instance); + ret = EFI_UNSUPPORTED; + goto out; + } + + /* do update */ + image_binary = (void *)image + sizeof(*image); + vendor_code = image_binary + image->update_image_size; + + abort_reason = NULL; + ret = EFI_CALL(fmp->set_image(fmp, image->update_image_index, + image_binary, + image->update_image_size, + vendor_code, NULL, + &abort_reason)); + if (ret != EFI_SUCCESS) { + log_err("EFI Capsule: firmware update failed: %ls\n", + abort_reason); + efi_free_pool(abort_reason); + goto out; + } + } + +out: + efi_free_pool(handles); + + return ret; +} +#else +static efi_status_t efi_capsule_update_firmware( + struct efi_capsule_header *capsule_data) +{ + return EFI_UNSUPPORTED; +} +#endif /* CONFIG_EFI_CAPSULE_FIRMWARE_MANAGEMENT */ + /** * efi_update_capsule() - process information from operating system * @capsule_header_array: Array of virtual address pointers @@ -118,9 +327,29 @@ efi_status_t EFIAPI efi_update_capsule( goto out; } - ret = EFI_UNSUPPORTED; + ret = EFI_SUCCESS; for (i = 0, capsule = *capsule_header_array; i < capsule_count; i++, capsule = *(++capsule_header_array)) { + /* sanity check */ + if (capsule->header_size < sizeof(*capsule) || + capsule->capsule_image_size < sizeof(*capsule)) { + log_err("EFI: A capsule has not enough data\n"); + continue; + } + + log_debug("Capsule[%d] (guid:%pUl)\n", + i, &capsule->capsule_guid); + if (!guidcmp(&capsule->capsule_guid, + &efi_guid_firmware_management_capsule_id)) { + ret = efi_capsule_update_firmware(capsule); + } else { + log_err("EFI: not support capsule type: %pUl\n", + &capsule->capsule_guid); + ret = EFI_UNSUPPORTED; + } + + if (ret != EFI_SUCCESS) + goto out; } out: return EFI_EXIT(ret); @@ -265,7 +494,7 @@ static efi_status_t find_boot_device(void) if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL) { /* BootNext does exist here */ if (ret == EFI_BUFFER_TOO_SMALL || size != sizeof(u16)) { - printf("BootNext must be 16-bit integer\n"); + log_err("BootNext must be 16-bit integer\n"); goto skip; } sprintf((char *)boot_var, "Boot%04X", bootnext); @@ -323,7 +552,7 @@ out: u16 *path_str; path_str = efi_dp_str(boot_dev); - EFI_PRINT("EFI Capsule: bootdev is %ls\n", path_str); + log_debug("EFI Capsule: bootdev is %ls\n", path_str); efi_free_pool(path_str); volume = efi_fs_from_path(boot_dev); @@ -363,7 +592,7 @@ static efi_status_t efi_capsule_scan_dir(u16 ***files, unsigned int *num) ret = find_boot_device(); if (ret == EFI_NOT_FOUND) { - EFI_PRINT("EFI Capsule: bootdev is not set\n"); + log_debug("EFI Capsule: bootdev is not set\n"); *num = 0; return EFI_SUCCESS; } else if (ret != EFI_SUCCESS) { @@ -619,20 +848,19 @@ efi_status_t efi_launch_capsules(void) /* Launch capsules */ for (i = 0, ++index; i < nfiles; i++, index++) { - EFI_PRINT("capsule from %ls ...\n", files[i]); + log_debug("capsule from %ls ...\n", files[i]); if (index > 0xffff) index = 0; ret = efi_capsule_read_file(files[i], &capsule); if (ret == EFI_SUCCESS) { ret = EFI_CALL(efi_update_capsule(&capsule, 1, 0)); if (ret != EFI_SUCCESS) - printf("EFI Capsule update failed at %ls\n", - files[i]); + log_err("EFI Capsule update failed at %ls\n", + files[i]); free(capsule); } else { - printf("EFI: reading capsule failed: %ls\n", - files[i]); + log_err("EFI: reading capsule failed: %ls\n", files[i]); } /* create CapsuleXXXX */ set_capsule_result(index, capsule, ret); @@ -640,8 +868,8 @@ efi_status_t efi_launch_capsules(void) /* delete a capsule either in case of success or failure */ ret = efi_capsule_delete_file(files[i]); if (ret != EFI_SUCCESS) - printf("EFI: deleting a capsule file failed: %ls\n", - files[i]); + log_err("EFI: deleting a capsule file failed: %ls\n", + files[i]); } efi_capsule_scan_done(); diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c index a126a59f18..ce6292f559 100644 --- a/lib/efi_loader/efi_setup.c +++ b/lib/efi_loader/efi_setup.c @@ -159,6 +159,10 @@ static efi_status_t efi_init_os_indications(void) os_indications_supported |= EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED; + if (IS_ENABLED(CONFIG_EFI_CAPSULE_FIRMWARE_MANAGEMENT)) + os_indications_supported |= + EFI_OS_INDICATIONS_FMP_CAPSULE_SUPPORTED; + return efi_set_variable_int(L"OsIndicationsSupported", &efi_global_variable_guid, EFI_VARIABLE_BOOTSERVICE_ACCESS | -- cgit v1.2.3 From 47d2b3b9c98e1adf231f8143bc01b0046ebd5c9c Mon Sep 17 00:00:00 2001 From: Ilias Apalodimas Date: Wed, 30 Dec 2020 17:07:14 +0200 Subject: efi_loader: Remove unconditional installation of file2 protocol for initrd Up to now we install the EFI_LOAD_FILE2_PROTOCOL to load an initrd unconditionally. Although we correctly return various EFI exit codes depending on the file status (i.e EFI_NO_MEDIA, EFI_NOT_FOUND etc), the kernel loader only falls back to the cmdline interpreted initrd if the protocol is not installed. This creates a problem for EFI installers, since they won't be able to load their own initrd and start the installation. A following patch introduces a different logic where we search for an initrd path defined in an EFI variable named 'Initrd####'. If the bootmgr is used to launch the EFI payload, we'll will try to match the BootCurrent value and find the corresponding initrd (i.e Boot0000 -> Initrd0000 etc). If the file is found, we'll install the required protocol which the kernel's efi-stub can use and load our initrd. Signed-off-by: Ilias Apalodimas Signed-off-by: Heinrich Schuchardt --- lib/efi_loader/efi_setup.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'lib/efi_loader/efi_setup.c') diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c index ce6292f559..5800cbf6d4 100644 --- a/lib/efi_loader/efi_setup.c +++ b/lib/efi_loader/efi_setup.c @@ -257,11 +257,6 @@ efi_status_t efi_init_obj_list(void) if (ret != EFI_SUCCESS) goto out; #endif -#ifdef CONFIG_EFI_LOAD_FILE2_INITRD - ret = efi_initrd_register(); - if (ret != EFI_SUCCESS) - goto out; -#endif #ifdef CONFIG_NET ret = efi_net_register(); if (ret != EFI_SUCCESS) -- cgit v1.2.3