From patchwork Fri Jul 10 01:25:27 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AKASHI Takahiro X-Patchwork-Id: 241226 List-Id: U-Boot discussion From: takahiro.akashi at linaro.org (AKASHI Takahiro) Date: Fri, 10 Jul 2020 10:25:27 +0900 Subject: [PATCH v3 07/17] efi_loader: define UpdateCapsule api In-Reply-To: <20200710012537.6264-1-takahiro.akashi@linaro.org> References: <20200710012537.6264-1-takahiro.akashi@linaro.org> Message-ID: <20200710012537.6264-8-takahiro.akashi@linaro.org> 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 | 168 +++++++++++++++++++++++++++++++++++ lib/efi_loader/efi_runtime.c | 104 +++++++++++++--------- lib/efi_loader/efi_setup.c | 31 +++++-- 7 files changed, 289 insertions(+), 51 deletions(-) create mode 100644 lib/efi_loader/efi_capsule.c diff --git a/include/efi_api.h b/include/efi_api.h index 759d9118758e..4be01495f096 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 fc9344c7428e..ce73509200e9 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -208,6 +208,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; @@ -786,6 +788,17 @@ bool efi_secure_boot_enabled(void); bool efi_image_parse(void *efi, size_t len, struct efi_image_regions **regp, WIN_CERTIFICATE **auth, size_t *auth_len); +/* 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 f0a30a43bc48..2434ea452380 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -47,6 +47,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 57c7e66ea0e2..67bc5c9c0907 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 000000000000..cfe422bee924 --- /dev/null +++ b/lib/efi_loader/efi_capsule.c @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * EFI Capsule + * + * Copyright (c) 2018 Linaro Limited + * Author: AKASHI Takahiro + */ + +#include +#include +#include +#include +#include + +const efi_guid_t efi_guid_capsule_report = EFI_CAPSULE_REPORT_GUID; + +/** + * get_last_capsule - get the last capsule number + * + * Retrieve the number of capsule invoked last time from "CapsuleLast" + * variable. + * + * Return: + * * > 0 - the last capsule number invoked + * * 0xffff - on error, or no capsule invoked yet + */ +static __maybe_unused int get_last_capsule(void) +{ + u16 value16[11]; /* "CapsuleXXXX": non-null-terminated */ + char value[11], *p; + efi_uintn_t size; + unsigned long num = 0xffff; + efi_status_t ret; + + size = sizeof(value16); + ret = EFI_CALL(efi_get_variable(L"CapsuleLast", + &efi_guid_capsule_report, + NULL, &size, value16)); + if (ret != EFI_SUCCESS || u16_strncmp(value16, L"Capsule", 7)) + goto err; + + p = value; + utf16_utf8_strcpy(&p, value16); + strict_strtoul(&value[7], 16, &num); +err: + return (int)num; +} + +/** + * 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 num, struct efi_capsule_header *capsule, + efi_status_t return_status) +{ + char variable_name[12]; + u16 variable_name16[12], *p; + struct efi_capsule_result_variable_header result; + struct efi_time time; + efi_status_t ret; + + sprintf(variable_name, "Capsule%04X", num); + p = variable_name16; + utf8_utf16_strncpy(&p, variable_name, 11); + 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_CALL(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 %s failed\n", variable_name); +} + +/** + * 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_SUCCESS; + 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 c0bd99b86746..d5a4ac1590ae 100644 --- a/lib/efi_loader/efi_runtime.c +++ b/lib/efi_loader/efi_runtime.c @@ -131,6 +131,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); @@ -411,6 +415,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 arry 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 * @@ -434,6 +482,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); @@ -837,50 +892,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, @@ -898,7 +909,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 a3b05a4a9b18..7fafb0db9d93 100644 --- a/lib/efi_loader/efi_setup.c +++ b/lib/efi_loader/efi_setup.c @@ -115,6 +115,29 @@ static efi_status_t efi_init_secure_boot(void) } #endif /* CONFIG_EFI_SECURE_BOOT */ +/** + * 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_CALL(efi_set_variable(L"OsIndicationsSupported", + &efi_global_variable_guid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + sizeof(os_indications_supported), + &os_indications_supported)); +} + /** * efi_init_obj_list() - Initialize and populate EFI object list * @@ -122,7 +145,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 */ @@ -160,12 +182,7 @@ efi_status_t efi_init_obj_list(void) goto out; /* Indicate supported features */ - ret = EFI_CALL(efi_set_variable(L"OsIndicationsSupported", - &efi_global_variable_guid, - EFI_VARIABLE_BOOTSERVICE_ACCESS | - EFI_VARIABLE_RUNTIME_ACCESS, - sizeof(os_indications_supported), - &os_indications_supported)); + ret = efi_init_os_indications(); if (ret != EFI_SUCCESS) goto out;