From patchwork Tue Mar 17 02:12:34 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AKASHI Takahiro X-Patchwork-Id: 243718 List-Id: U-Boot discussion From: takahiro.akashi at linaro.org (AKASHI Takahiro) Date: Tue, 17 Mar 2020 11:12:34 +0900 Subject: [RFC 01/14] efi_loader: define OsIndicationsSupported flags In-Reply-To: <20200317021247.5849-1-takahiro.akashi@linaro.org> References: <20200317021247.5849-1-takahiro.akashi@linaro.org> Message-ID: <20200317021247.5849-2-takahiro.akashi@linaro.org> These flags are expected to be set in OsIndicationsSupported variable if corresponding features are supported. See UEFI specification, section 8.5.4. In particular, capsule-related flags will be used in my capsule update patch. Signed-off-by: AKASHI Takahiro Reviewed-by: Heinrich Schuchardt --- include/efi_api.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/include/efi_api.h b/include/efi_api.h index 22396172e15f..73c0722b0168 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -57,6 +57,17 @@ typedef u16 efi_form_id_t; struct efi_event; +/* OsIndicationsSupported flags */ +#define EFI_OS_INDICATIONS_BOOT_TO_RW_UI 0x0000000000000001 +#define EFI_OS_INDICATIONS_TIMESTAMP_REVOCATION 0x0000000000000002 +#define EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED \ + 0x0000000000000004 +#define EFI_OS_INDICATIONS_FMP_CAPSULE_SUPPORTED 0x0000000000000008 +#define EFI_OS_INDICATIONS_CAPSULE_RESULT_VAR_SUPPORTED 0x0000000000000010 +#define EFI_OS_INDICATIONS_START_OS_RECOVERY 0x0000000000000020 +#define EFI_OS_INDICATIONS_START_PLATFORM_RECOVERY 0x0000000000000040 +#define EFI_OS_INDICATIONS_JSON_CONFIG_DATA_REFRESH 0x0000000000000080 + /* EFI Boot Services table */ #define EFI_BOOT_SERVICES_SIGNATURE 0x56524553544f4f42 struct efi_boot_services { From patchwork Tue Mar 17 02:12:35 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AKASHI Takahiro X-Patchwork-Id: 243719 List-Id: U-Boot discussion From: takahiro.akashi at linaro.org (AKASHI Takahiro) Date: Tue, 17 Mar 2020 11:12:35 +0900 Subject: [RFC 02/14] efi_loader: define System Resource Table macros In-Reply-To: <20200317021247.5849-1-takahiro.akashi@linaro.org> References: <20200317021247.5849-1-takahiro.akashi@linaro.org> Message-ID: <20200317021247.5849-3-takahiro.akashi@linaro.org> Some of those values will be used in an implementation of UEFI firmware management protocol as part of my capsule update patch. Signed-off-by: AKASHI Takahiro Reviewed-by: Heinrich Schuchardt --- include/efi_api.h | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/include/efi_api.h b/include/efi_api.h index 73c0722b0168..a99cc7acc475 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -1627,4 +1627,31 @@ struct efi_unicode_collation_protocol { #define LOAD_OPTION_CATEGORY_BOOT 0x00000000 #define LOAD_OPTION_CATEGORY_APP 0x00000100 +/* + * System Resource Table + */ +/* Firmware Type Definitions */ +#define ESRT_FW_TYPE_UNKNOWN 0x00000000 +#define ESRT_FW_TYPE_SYSTEMFIRMWARE 0x00000001 +#define ESRT_FW_TYPE_DEVICEFIRMWARE 0x00000002 +#define ESRT_FW_TYPE_UEFIDRIVER 0x00000003 + +/* Last Attempt Status Values */ +#define LAST_ATTEMPT_STATUS_SUCCESS 0x00000000 +#define LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL 0x00000001 +#define LAST_ATTEMPT_STATUS_ERROR_INSUFFICIENT_RESOURCES 0x00000002 +#define LAST_ATTEMPT_STATUS_ERROR_INCORRECT_VERSION 0x00000003 +#define LAST_ATTEMPT_STATUS_ERROR_INVALID_FORMAT 0x00000004 +#define LAST_ATTEMPT_STATUS_ERROR_AUTH_ERROR 0x00000005 +#define LAST_ATTEMPT_STATUS_ERROR_PWR_EVT_AC 0x00000006 +#define LAST_ATTEMPT_STATUS_ERROR_PWR_EVT_BATT 0x00000007 +#define LAST_ATTEMPT_STATUS_ERROR_UNSATISFIED_DEPENDENCIES 0x00000008 + +/* + * The LastAttemptStatus values of 0x1000 - 0x4000 are reserved for vendor + * usage. + */ +#define LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL_VENDOR_RANGE_MIN 0x00001000 +#define LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL_VENDOR_RANGE_MAX 0x00004000 + #endif From patchwork Tue Mar 17 02:12:36 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AKASHI Takahiro X-Patchwork-Id: 243720 List-Id: U-Boot discussion From: takahiro.akashi at linaro.org (AKASHI Takahiro) Date: Tue, 17 Mar 2020 11:12:36 +0900 Subject: [RFC 03/14] efi_loader: export a couple of protocol related functions In-Reply-To: <20200317021247.5849-1-takahiro.akashi@linaro.org> References: <20200317021247.5849-1-takahiro.akashi@linaro.org> Message-ID: <20200317021247.5849-4-takahiro.akashi@linaro.org> This is a preparatory patch. Those functions will be used in an implementation of UEFI firmware management protocol as part of my capsule update patch. Signed-off-by: AKASHI Takahiro Reviewed-by: Heinrich Schuchardt --- include/efi_loader.h | 14 ++++++++++++++ lib/efi_loader/efi_boottime.c | 16 ++++++++-------- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/include/efi_loader.h b/include/efi_loader.h index d4c59b54c48b..c55bade6a508 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -456,6 +456,20 @@ efi_status_t efi_remove_all_protocols(const efi_handle_t handle); /* Install multiple protocol interfaces */ efi_status_t EFIAPI efi_install_multiple_protocol_interfaces (efi_handle_t *handle, ...); +/* Get handles that support a given protocol */ +efi_status_t EFIAPI efi_locate_handle_buffer( + enum efi_locate_search_type search_type, + const efi_guid_t *protocol, void *search_key, + efi_uintn_t *no_handles, efi_handle_t **buffer); +/* Close an previously opened protocol interface */ +efi_status_t EFIAPI efi_close_protocol(efi_handle_t handle, + const efi_guid_t *protocol, + efi_handle_t agent_handle, + efi_handle_t controller_handle); +/* Open a protocol interface */ +efi_status_t EFIAPI efi_handle_protocol(efi_handle_t handle, + const efi_guid_t *protocol, + void **protocol_interface); /* Call this to create an event */ efi_status_t efi_create_event(uint32_t type, efi_uintn_t notify_tpl, void (EFIAPI *notify_function) ( diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 1f598b357a5c..9860d5047502 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -2106,10 +2106,10 @@ static efi_status_t EFIAPI efi_set_watchdog_timer(unsigned long timeout, * * Return: status code */ -static efi_status_t EFIAPI efi_close_protocol(efi_handle_t handle, - const efi_guid_t *protocol, - efi_handle_t agent_handle, - efi_handle_t controller_handle) +efi_status_t EFIAPI efi_close_protocol(efi_handle_t handle, + const efi_guid_t *protocol, + efi_handle_t agent_handle, + efi_handle_t controller_handle) { struct efi_handler *handler; struct efi_open_protocol_info_item *item; @@ -2282,7 +2282,7 @@ static efi_status_t EFIAPI efi_protocols_per_handle( * * Return: status code */ -static efi_status_t EFIAPI efi_locate_handle_buffer( +efi_status_t EFIAPI efi_locate_handle_buffer( enum efi_locate_search_type search_type, const efi_guid_t *protocol, void *search_key, efi_uintn_t *no_handles, efi_handle_t **buffer) @@ -3182,9 +3182,9 @@ out: * * Return: status code */ -static efi_status_t EFIAPI efi_handle_protocol(efi_handle_t handle, - const efi_guid_t *protocol, - void **protocol_interface) +efi_status_t EFIAPI efi_handle_protocol(efi_handle_t handle, + const efi_guid_t *protocol, + void **protocol_interface) { return efi_open_protocol(handle, protocol, protocol_interface, efi_root, NULL, EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL); From patchwork Tue Mar 17 02:12:37 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AKASHI Takahiro X-Patchwork-Id: 243721 List-Id: U-Boot discussion From: takahiro.akashi at linaro.org (AKASHI Takahiro) Date: Tue, 17 Mar 2020 11:12:37 +0900 Subject: [RFC 04/14] efi_loader: correct a definition of struct efi_capsule_header In-Reply-To: <20200317021247.5849-1-takahiro.akashi@linaro.org> References: <20200317021247.5849-1-takahiro.akashi@linaro.org> Message-ID: <20200317021247.5849-5-takahiro.akashi@linaro.org> See UEFI specification, section 8.5.3. In addition, the structure, efi_capsule_header, should be "packed" as it is a serialized binary format in a capsule file. Signed-off-by: AKASHI Takahiro Reviewed-by: Heinrich Schuchardt --- include/efi_api.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/efi_api.h b/include/efi_api.h index a99cc7acc475..4040c44855fa 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -218,11 +218,11 @@ enum efi_reset_type { #define CAPSULE_FLAGS_INITIATE_RESET 0x00040000 struct efi_capsule_header { - efi_guid_t *capsule_guid; + efi_guid_t capsule_guid; u32 header_size; u32 flags; u32 capsule_image_size; -}; +} __packed; #define EFI_RT_SUPPORTED_GET_TIME 0x0001 #define EFI_RT_SUPPORTED_SET_TIME 0x0002 From patchwork Tue Mar 17 02:12:38 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AKASHI Takahiro X-Patchwork-Id: 243722 List-Id: U-Boot discussion From: takahiro.akashi at linaro.org (AKASHI Takahiro) Date: Tue, 17 Mar 2020 11:12:38 +0900 Subject: [RFC 05/14] efi_loader: define UpdateCapsule api In-Reply-To: <20200317021247.5849-1-takahiro.akashi@linaro.org> References: <20200317021247.5849-1-takahiro.akashi@linaro.org> Message-ID: <20200317021247.5849-6-takahiro.akashi@linaro.org> In this commit, skeleton functions for capsule-related API's are added under CONFIG_EFI_CAPSULE_UPDATE configuration. Detailed implementation will be added in the succeeding patches. Signed-off-by: AKASHI Takahiro --- include/efi_api.h | 12 ++++ include/efi_loader.h | 11 ++++ lib/efi_loader/Kconfig | 7 +++ lib/efi_loader/Makefile | 1 + lib/efi_loader/efi_capsule.c | 98 +++++++++++++++++++++++++++++++++ lib/efi_loader/efi_runtime.c | 104 ++++++++++++++++++++--------------- lib/efi_loader/efi_setup.c | 31 ++++++++--- 7 files changed, 213 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 4040c44855fa..ac2b38801c0c 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 c55bade6a508..c3cb7735bf50 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -679,6 +679,17 @@ void efi_deserialize_load_option(struct efi_load_option *lo, u8 *data); unsigned long efi_serialize_load_option(struct efi_load_option *lo, u8 **data); efi_status_t efi_bootmgr_load(efi_handle_t *handle); +/* 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 a7afa3f29e88..2ef6cb124f3a 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -90,6 +90,13 @@ config EFI_UNICODE_COLLATION_PROTOCOL endif +config EFI_CAPSULE_UPDATE + bool "Enable capsule update support" + default n + help + Select this option if you want to use capsule update feature, + including firmware updates and variable updates. + config EFI_LOADER_BOUNCE_BUFFER bool "EFI Applications use bounce buffers for DMA operations" depends on ARM64 diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile index 04dc8648512b..f19096924bef 100644 --- a/lib/efi_loader/Makefile +++ b/lib/efi_loader/Makefile @@ -22,6 +22,7 @@ endif obj-$(CONFIG_CMD_BOOTEFI_HELLO) += helloworld_efi.o obj-y += efi_bootmgr.o obj-y += efi_boottime.o +obj-$(CONFIG_EFI_CAPSULE_UPDATE) += 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..d3f931910d10 --- /dev/null +++ b/lib/efi_loader/efi_capsule.c @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * EFI Capsule + * + * Copyright (c) 2018 Linaro Limited + * Author: AKASHI Takahiro + */ + +#include +#include +#include +#include +#include + +/* + * Launch a capsule + */ +/** + * 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 + * 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 + * + * 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 + * 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 4b3c843b2ced..1dce8d06abdb 100644 --- a/lib/efi_loader/efi_runtime.c +++ b/lib/efi_loader/efi_runtime.c @@ -107,6 +107,11 @@ efi_status_t efi_init_runtime_supported(void) #ifdef CONFIG_EFI_HAVE_RUNTIME_RESET efi_runtime_services_supported |= EFI_RT_SUPPORTED_RESET_SYSTEM; #endif +#ifdef CONFIG_EFI_CAPSULE_UPDATE + efi_runtime_services_supported |= + (EFI_RT_SUPPORTED_UPDATE_CAPSULE | + EFI_RT_SUPPORTED_QUERY_CAPSULE_CAPABILITIES); +#endif return EFI_CALL(efi_set_variable(L"RuntimeServicesSupported", &efi_global_variable_guid, @@ -390,6 +395,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 * @@ -413,6 +462,12 @@ 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; +#ifdef CONFIG_EFI_CAPSULE_UPDATE + /* 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; +#endif /* Update CRC32 */ efi_update_table_header_crc32(&efi_runtime_services.hdr); @@ -817,50 +872,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, @@ -878,7 +889,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_CAPSULE_UPDATE .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 de7b616c6daa..c485cad34022 100644 --- a/lib/efi_loader/efi_setup.c +++ b/lib/efi_loader/efi_setup.c @@ -82,6 +82,29 @@ out: 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; + +#ifdef CONFIG_EFI_CAPSULE_UPDATE + os_indications_supported |= + EFI_OS_INDICATIONS_CAPSULE_RESULT_VAR_SUPPORTED; +#endif + 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 * @@ -89,7 +112,6 @@ out: */ efi_status_t efi_init_obj_list(void) { - u64 os_indications_supported = 0; /* None */ efi_status_t ret = EFI_SUCCESS; /* Initialize once only */ @@ -113,12 +135,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; From patchwork Tue Mar 17 02:12:39 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AKASHI Takahiro X-Patchwork-Id: 243724 List-Id: U-Boot discussion From: takahiro.akashi at linaro.org (AKASHI Takahiro) Date: Tue, 17 Mar 2020 11:12:39 +0900 Subject: [RFC 06/14] efi_loader: capsule: add capsule_on_disk support In-Reply-To: <20200317021247.5849-1-takahiro.akashi@linaro.org> References: <20200317021247.5849-1-takahiro.akashi@linaro.org> Message-ID: <20200317021247.5849-7-takahiro.akashi@linaro.org> 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 identified only if they are located in a specific directory on a device that is determined by "BootXXXX" variables. Signed-off-by: AKASHI Takahiro --- include/efi_loader.h | 18 ++ lib/efi_loader/Kconfig | 7 + lib/efi_loader/efi_boottime.c | 3 + lib/efi_loader/efi_capsule.c | 548 ++++++++++++++++++++++++++++++++++ lib/efi_loader/efi_setup.c | 6 + 5 files changed, 582 insertions(+) diff --git a/include/efi_loader.h b/include/efi_loader.h index c3cb7735bf50..c701672e18db 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -178,6 +178,8 @@ extern const efi_guid_t efi_guid_hii_config_routing_protocol; extern const efi_guid_t efi_guid_hii_config_access_protocol; extern const efi_guid_t efi_guid_hii_database_protocol; extern const efi_guid_t efi_guid_hii_string_protocol; +/* GUID of capsule update result */ +extern const efi_guid_t efi_guid_capsule_report; /* GUID of RNG protocol */ extern const efi_guid_t efi_guid_rng_protocol; @@ -690,6 +692,22 @@ efi_status_t EFIAPI efi_query_capsule_caps( u64 *maximum_capsule_size, u32 *reset_type); +#ifdef CONFIG_EFI_CAPSULE_ON_DISK +#define EFI_CAPSULE_DIR L"\\EFI\\UpdateCapsule\\" + +/* Hook at initialization */ +efi_status_t efi_launch_capsules(void); +/* Notify ExitBootServices() is called */ +void efi_capsule_boot_exit_notify(void); +#else +static inline efi_status_t efi_launch_capsules(void) +{ + return EFI_SUCCESS; +} + +static inline efi_capsule_boot_exit_notify(void) {} +#endif /* CONFIG_EFI_CAPSULE_ON_DISK */ + #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 2ef6cb124f3a..95e10f7d981b 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -97,6 +97,13 @@ config EFI_CAPSULE_UPDATE Select this option if you want to use capsule update feature, including firmware updates and variable updates. +config EFI_CAPSULE_ON_DISK + bool "Enable capsule-on-disk support" + depends on EFI_CAPSULE_UPDATE + default n + help + Select this option if you want to use capsule-on-disk feature. + config EFI_LOADER_BOUNCE_BUFFER bool "EFI Applications use bounce buffers for DMA operations" depends on ARM64 diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 9860d5047502..c2a789b4f910 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -1981,6 +1981,9 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, /* Notify variable services */ efi_variables_boot_exit_notify(); + /* Notify capsule services */ + efi_capsule_boot_exit_notify(); + /* Remove all events except EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE */ list_for_each_entry_safe(evt, next_event, &efi_events, link) { if (evt->type != EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE) diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c index d3f931910d10..f3e2a555a6b9 100644 --- a/lib/efi_loader/efi_capsule.c +++ b/lib/efi_loader/efi_capsule.c @@ -10,8 +10,14 @@ #include #include #include +#include #include +const efi_guid_t efi_guid_capsule_report = EFI_CAPSULE_REPORT_GUID; + +/* for file system access */ +static struct efi_file_handle *bootdev_root; + /* * Launch a capsule */ @@ -96,3 +102,545 @@ efi_status_t EFIAPI efi_query_capsule_caps( out: return EFI_EXIT(ret); } + +#ifdef CONFIG_EFI_CAPSULE_ON_DISK +static void efi_capsule_result_variable(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) + EFI_PRINT("EFI Capsule: creating %s failed\n", variable_name); +} + +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_CALL(efi_get_variable(boot_var, &efi_global_variable_guid, + NULL, &size, NULL)); + if (ret == EFI_BUFFER_TOO_SMALL) { + buf = malloc(size); + if (!buf) + return EFI_OUT_OF_RESOURCES; + ret = EFI_CALL(efi_get_variable(boot_var, + &efi_global_variable_guid, + NULL, &size, buf)); + } + if (ret != EFI_SUCCESS) + return ret; + + efi_deserialize_load_option(&lo, buf); + + 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; +} + +static bool device_is_present(struct efi_device_path *dp) +{ + efi_handle_t handle; + struct efi_handler *handler; + efi_status_t ret; + + handle = efi_dp_find_obj(dp, NULL); + if (!handle) + return false; + + /* check if this is a block device */ + ret = efi_search_protocol(handle, &efi_block_io_guid, &handler); + if (ret != EFI_SUCCESS) + return false; + + return true; +} + +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_CALL(efi_get_variable(L"BootNext", + (efi_guid_t *)&efi_global_variable_guid, + NULL, &size, &bootnext)); + 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(boot_dev)) { + goto out; + } else { + efi_free_pool(boot_dev); + boot_dev = NULL; + } + } + } + +skip: + /* find active boot device in BootOrder */ + size = 0; + ret = EFI_CALL(efi_get_variable(L"BootOrder", &efi_global_variable_guid, + NULL, &size, NULL)); + if (ret == EFI_BUFFER_TOO_SMALL) { + boot_order = malloc(size); + if (!boot_order) { + ret = EFI_OUT_OF_RESOURCES; + goto out; + } + + ret = EFI_CALL(efi_get_variable( + L"BootOrder", &efi_global_variable_guid, + NULL, &size, boot_order)); + } + 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(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; +} + +/* + * Traverse a capsule directory in boot device + * Called by initialization code, and returns an array of capsule file + * names in @files + */ +static efi_status_t efi_capsule_scan_dir(u16 ***files, int *num) +{ + struct efi_file_handle *dirh; + struct efi_file_info *dirent; + efi_uintn_t dirent_size, tmp_size; + 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; +} + +/* + * Read in a capsule file + */ +static efi_status_t efi_capsule_read_file(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, 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; +} + +static efi_status_t efi_capsule_delete_file(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, filename, + EFI_FILE_MODE_READ, 0)); + /* ignore an error */ + EFI_CALL((*dirh->close)(dirh)); + + ret = EFI_CALL((*fh->delete)(fh)); + + return ret; +} + +static void efi_capsule_scan_done(void) +{ + EFI_CALL((*bootdev_root->close)(bootdev_root)); + bootdev_root = NULL; +} + +efi_status_t __weak arch_efi_load_capsule_drivers(void) +{ + return EFI_SUCCESS; +} + +static 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; +} + +/* + * Launch all the capsules in system at boot time + * + * Called by efi init code + */ +efi_status_t efi_launch_capsules(void) +{ + struct efi_capsule_header *capsule = NULL; + u16 **files; + int nfiles, num, i; + char variable_name[12]; + u16 variable_name16[12], *p; + efi_status_t ret; + + num = 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, ++num; i < nfiles; i++, num++) { + EFI_PRINT("EFI Capsule from %ls ...\n", files[i]); + if (num > 0xffff) + num = 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) + EFI_PRINT("EFI Capsule update failed at %ls\n", + files[i]); + + free(capsule); + } else { + EFI_PRINT("EFI Capsule read failed\n"); + } + /* create CapsuleXXXX */ + efi_capsule_result_variable(num, capsule, ret); + + /* delete a capsule either in case of success or failure */ + ret = efi_capsule_delete_file(files[i]); + if (ret != EFI_SUCCESS) + EFI_PRINT("EFI Capsule deletion of capsule failed at %ls\n", + files[i]); + } + efi_capsule_scan_done(); + + for (i = 0; i < nfiles; i++) + free(files[i]); + free(files); + + /* CapsuleMax */ + p = variable_name16; + utf8_utf16_strncpy(&p, "CapsuleFFFF", 11); + EFI_CALL(efi_set_variable(L"CapsuleMax", &efi_guid_capsule_report, + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + 22, variable_name16)); + + /* CapsuleLast */ + sprintf(variable_name, "Capsule%04X", num - 1); + p = variable_name16; + utf8_utf16_strncpy(&p, variable_name, 11); + EFI_CALL(efi_set_variable(L"CapsuleLast", &efi_guid_capsule_report, + EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + 22, variable_name16)); + + return ret; +} + +/* + * Dummy functions after ExitBootServices() + */ +efi_status_t EFIAPI efi_update_capsule_runtime( + struct efi_capsule_header **capsule_header_array, + efi_uintn_t capsule_count, + u64 scatter_gather_list) +{ + return EFI_UNSUPPORTED; +} + +efi_status_t EFIAPI efi_query_capsule_caps_runtime( + struct efi_capsule_header **capsule_header_array, + efi_uintn_t capsule_count, + u64 *maximum_capsule_size, + u32 *reset_type) +{ + return EFI_UNSUPPORTED; +} + +/** + * efi_capsule_boot_exit_notify() - notify ExitBootServices() is called + */ +void efi_capsule_boot_exit_notify(void) +{ + efi_runtime_services.update_capsule = efi_update_capsule_runtime; + efi_runtime_services.query_capsule_caps = + efi_query_capsule_caps_runtime; + efi_update_table_header_crc32(&efi_runtime_services.hdr); +} +#else +/* + * Dummy functions for runtime services + */ +efi_status_t EFIAPI efi_update_capsule( + struct efi_capsule_header **capsule_header_array, + efi_uintn_t capsule_count, + u64 scatter_gather_list) +{ + return EFI_UNSUPPORTED; +} + +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) +{ + return EFI_UNSUPPORTED; +} +#endif /* CONFIG_EFI_CAPSULE_ON_DISK */ diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c index c485cad34022..309defc5e40d 100644 --- a/lib/efi_loader/efi_setup.c +++ b/lib/efi_loader/efi_setup.c @@ -96,6 +96,10 @@ static efi_status_t efi_init_os_indications(void) #ifdef CONFIG_EFI_CAPSULE_UPDATE os_indications_supported |= EFI_OS_INDICATIONS_CAPSULE_RESULT_VAR_SUPPORTED; +#endif +#ifdef CONFIG_EFI_CAPSULE_ON_DISK + os_indications_supported |= + EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED; #endif return EFI_CALL(efi_set_variable(L"OsIndicationsSupported", &efi_global_variable_guid, @@ -196,6 +200,8 @@ efi_status_t efi_init_obj_list(void) if (ret != EFI_SUCCESS) goto out; + /* Execute capsules after reboot */ + ret = efi_launch_capsules(); out: efi_obj_list_initialized = ret; return ret; From patchwork Tue Mar 17 02:12:40 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AKASHI Takahiro X-Patchwork-Id: 243723 List-Id: U-Boot discussion From: takahiro.akashi at linaro.org (AKASHI Takahiro) Date: Tue, 17 Mar 2020 11:12:40 +0900 Subject: [RFC 07/14] efi_loader: capsule: add memory range capsule definitions In-Reply-To: <20200317021247.5849-1-takahiro.akashi@linaro.org> References: <20200317021247.5849-1-takahiro.akashi@linaro.org> Message-ID: <20200317021247.5849-8-takahiro.akashi@linaro.org> Memory range capsule gives us a way to notify that some memory regions should be left untouched across the next reset. See UEFI specification, section 8.5.3. Since how we should handle this kind of capsule is totally up to the system, no implementation will be added in this commit. Signed-off-by: AKASHI Takahiro --- include/efi_api.h | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/include/efi_api.h b/include/efi_api.h index ac2b38801c0c..b7bf21cac7ad 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -221,6 +221,10 @@ enum efi_reset_type { EFI_GUID(0x39b68c46, 0xf7fb, 0x441b, 0xb6, 0xec, \ 0x16, 0xb0, 0xf6, 0x98, 0x21, 0xf3) +#define EFI_MEMORY_RANGE_CAPSULE_GUID \ + EFI_GUID(0xde9f0ec, 0x88b6, 0x428f, 0x97, 0x7a, \ + 0x25, 0x8f, 0x1d, 0xe, 0x5e, 0x72) + struct efi_capsule_header { efi_guid_t capsule_guid; u32 header_size; @@ -236,6 +240,19 @@ struct efi_capsule_result_variable_header { efi_status_t capsule_status; } __packed; +struct efi_memory_range { + efi_physical_addr_t address; + u64 length; +}; + +struct efi_memory_range_capsule { + struct efi_capsule_header *header; + /* EFI_MEMORY_TYPE: 0x80000000-0xFFFFFFFF */ + enum efi_mem_type os_requested_memory_type; + u64 number_of_memory_ranges; + struct efi_memory_range memory_ranges[]; +} __packed; + #define EFI_RT_SUPPORTED_GET_TIME 0x0001 #define EFI_RT_SUPPORTED_SET_TIME 0x0002 #define EFI_RT_SUPPORTED_GET_WAKEUP_TIME 0x0004 From patchwork Tue Mar 17 02:12:41 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AKASHI Takahiro X-Patchwork-Id: 243725 List-Id: U-Boot discussion From: takahiro.akashi at linaro.org (AKASHI Takahiro) Date: Tue, 17 Mar 2020 11:12:41 +0900 Subject: [RFC 08/14] efi_loader: capsule: support firmware update In-Reply-To: <20200317021247.5849-1-takahiro.akashi@linaro.org> References: <20200317021247.5849-1-takahiro.akashi@linaro.org> Message-ID: <20200317021247.5849-9-takahiro.akashi@linaro.org> 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, installing drivers is not supported yet. 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 | 127 +++++++++++++++++++++++++++ lib/efi_loader/Kconfig | 12 +++ lib/efi_loader/efi_capsule.c | 165 +++++++++++++++++++++++++++++++++++ lib/efi_loader/efi_setup.c | 4 + 4 files changed, 308 insertions(+) diff --git a/include/efi_api.h b/include/efi_api.h index b7bf21cac7ad..e103369186a2 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -225,6 +225,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 +257,32 @@ 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; +} __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 @@ -1683,4 +1713,101 @@ struct efi_unicode_collation_protocol { #define LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL_VENDOR_RANGE_MIN 0x00001000 #define LAST_ATTEMPT_STATUS_ERROR_UNSUCCESSFUL_VENDOR_RANGE_MAX 0x00004000 +/* + * Firmware management protocol + */ +#define EFI_FIRMWARE_MANAGEMENT_PROTOCOL_GUID \ + EFI_GUID(0x86c77a67, 0x0b97, 0x4633, 0xa1, 0x87, \ + 0x49, 0x10, 0x4d, 0x06, 0x85, 0xc7) + +#define EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID \ + EFI_GUID(0xae13ff2d, 0x9ad4, 0x4e25, 0x9a, 0xc8, \ + 0x6d, 0x80, 0xb3, 0xb2, 0x21, 0x47) + +#define EFI_IMAGE_ATTRIBUTE_IMAGE_UPDATABLE 0x1 +#define EFI_IMAGE_ATTRIBUTE_RESET_REQUIRED 0x2 +#define EFI_IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED 0x4 +#define EFI_IMAGE_ATTRIBUTE_IN_USE 0x8 +#define EFI_IMAGE_ATTRIBUTE_UEFI_IMAGE 0x10 + +#define EFI_IMAGE_COMPATIBILITY_CHECK_SUPPORTED 0x1 +#define EFI_FIRMWARE_IMAGE_DESCRIPTOR_VERSION 4 + +#define EFI_IMAGE_UPDATABLE_VALID 0x1 +#define EFI_IMAGE_UPDATABLE_INVALID 0x2 +#define EFI_IMAGE_UPDATABLE_INVALID_TYPE 0x4 +#define EFI_IMAGE_UPDATABLE_INVALID_OLLD 0x8 +#define EFI_IMAGE_UPDATABLE_VALID_WITH_VENDOR_CODE 0x10 + +#define EFI_PACKAGE_ATTRIBUTE_VERSION_UPDATABLE 0x1 +#define EFI_PACKAGE_ATTRIBUTE_RESET_REQUIRED 0x2 +#define EFI_PACKAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED 0x4 + +typedef struct efi_firmware_image_dependencies { + u8 dependencies[0]; +} efi_fmp_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_fmp_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/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index 95e10f7d981b..43d6f75d557a 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -97,6 +97,18 @@ config EFI_CAPSULE_UPDATE Select this option if you want to use capsule update feature, including firmware updates and variable updates. + +if EFI_CAPSULE_UPDATE + +config EFI_CAPSULE_UPDATE_FIRMWARE + bool "Capsule based firmware update" + default n + help + Select this option if you want to enable capsule-based + firmware update + +endif + config EFI_CAPSULE_ON_DISK bool "Enable capsule-on-disk support" depends on EFI_CAPSULE_UPDATE diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c index f3e2a555a6b9..f3526beed681 100644 --- a/lib/efi_loader/efi_capsule.c +++ b/lib/efi_loader/efi_capsule.c @@ -14,10 +14,164 @@ #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; /* for file system access */ static struct efi_file_handle *bootdev_root; +#ifdef CONFIG_EFI_CAPSULE_UPDATE_FIRMWARE +/* + * Handle firmware management capsules + */ +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; + bool found = false; + int i; + 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; + ret = fmp->get_image_info(fmp, &info_size, image_info, + &descriptor_version, &descriptor_count, + &descriptor_size, NULL, NULL); + if (ret != EFI_BUFFER_TOO_SMALL) + goto skip; + image_info = malloc(info_size); + if (!image_info) + goto skip; + + ret = fmp->get_image_info(fmp, &info_size, image_info, + &descriptor_version, &descriptor_count, + &descriptor_size, NULL, NULL); + if (ret != EFI_SUCCESS) + goto skip; + + /* matching */ + for (i = 0, desc = image_info; i < descriptor_count; + i++, desc = (void *)desc + descriptor_size) + if (!guidcmp(&desc->image_type_id, image_type) && + (!instance || + ((descriptor_version >= 3) && + (desc->hardware_instance == instance)))) + found = true; + +skip: + 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; +} + +static efi_status_t efi_capsule_update_firmware( + struct efi_firmware_management_capsule_header *capsule) +{ + struct efi_firmware_management_capsule_image_header *image; + 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; + + if (capsule->version != 0x00000001) + return EFI_INVALID_PARAMETER; + + /* Drivers */ + /* TODO: support loading drivers */ + + 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++) { + image = (struct efi_firmware_management_capsule_image_header *) + ((void *)capsule + capsule->item_offset_list[item]); + + if (image->version != 0x00000001 && + image->version != 0x00000002 && + image->version != 0x00000003) { + ret = EFI_INVALID_PARAMETER; + goto out; + } + + /* find a device for update firmware */ + fmp = efi_fmp_find(&image->update_image_type_id, + image->version == 0x1 ? 0 : + image->update_hardware_instance, + handles, no_handles); + if (!fmp) { + printf("EFI Capsule: not support firmware type: %pUl\n", + &image->update_image_type_id); + ret = EFI_UNSUPPORTED; + goto out; + } + + /* do it */ + image_binary = (void *)image + sizeof(*image); + vendor_code = image_binary + image->update_image_size; + + abort_reason = NULL; + ret = fmp->set_image(fmp, image->update_image_index, + image_binary, image->update_image_size, + vendor_code, NULL, &abort_reason); + if (ret != EFI_SUCCESS) { + printf("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_firmware_management_capsule_header *capsule) +{ + return EFI_UNSUPPORTED; +} +#endif /* CONFIG_EFI_CAPSULE_UPDATE_FIRMWARE */ + /* * Launch a capsule */ @@ -54,6 +208,17 @@ efi_status_t EFIAPI efi_update_capsule( ret = EFI_SUCCESS; for (i = 0, capsule = *capsule_header_array; i < capsule_count; i++, capsule = *(++capsule_header_array)) { + EFI_PRINT("EFI Capsule (guid:%pUl)\n", &capsule->capsule_guid); + if (!guidcmp(&capsule->capsule_guid, + &efi_guid_firmware_management_capsule_id)) + ret = efi_capsule_update_firmware( + (struct efi_firmware_management_capsule_header *) + ((void *)capsule + sizeof(*capsule))); + else + ret = EFI_UNSUPPORTED; + + if (ret != EFI_SUCCESS) + goto out; } out: return EFI_EXIT(ret); diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c index 309defc5e40d..ca2d03c44610 100644 --- a/lib/efi_loader/efi_setup.c +++ b/lib/efi_loader/efi_setup.c @@ -100,6 +100,10 @@ static efi_status_t efi_init_os_indications(void) #ifdef CONFIG_EFI_CAPSULE_ON_DISK os_indications_supported |= EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED; +#endif +#ifdef CONFIG_EFI_CAPSULE_UPDATE_FIRMWARE + os_indications_supported |= + EFI_OS_INDICATIONS_FMP_CAPSULE_SUPPORTED; #endif return EFI_CALL(efi_set_variable(L"OsIndicationsSupported", &efi_global_variable_guid, From patchwork Tue Mar 17 02:12:42 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AKASHI Takahiro X-Patchwork-Id: 243727 List-Id: U-Boot discussion From: takahiro.akashi at linaro.org (AKASHI Takahiro) Date: Tue, 17 Mar 2020 11:12:42 +0900 Subject: [RFC 09/14] efi_loader: add simple firmware management protocol for FIT image In-Reply-To: <20200317021247.5849-1-takahiro.akashi@linaro.org> References: <20200317021247.5849-1-takahiro.akashi@linaro.org> Message-ID: <20200317021247.5849-10-takahiro.akashi@linaro.org> In this commit, a very simple firmware management protocol driver is implemented. It will take a single FIT image firmware in a capsule and apply the data using an existing update_tftp() interface. To specify a device and location to be updated, CONFIG_EFI_CAPSULE_FIT_INTERFACE, and CONFIG_EFI_CAPSULE_FIT_DEVICE are used. Signed-off-by: AKASHI Takahiro --- include/efi_loader.h | 3 + lib/efi_loader/Kconfig | 24 ++++- lib/efi_loader/Makefile | 1 + lib/efi_loader/efi_firmware.c | 191 ++++++++++++++++++++++++++++++++++ 4 files changed, 218 insertions(+), 1 deletion(-) create mode 100644 lib/efi_loader/efi_firmware.c diff --git a/include/efi_loader.h b/include/efi_loader.h index c701672e18db..79bdf9586d24 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -134,6 +134,7 @@ extern const struct efi_hii_config_access_protocol efi_hii_config_access; extern const struct efi_hii_database_protocol efi_hii_database; extern const struct efi_hii_string_protocol efi_hii_string; extern const struct efi_rng_protocol efi_rng_protocol; +extern const struct efi_firmware_management_protocol efi_fmp_fit; uint16_t *efi_dp_str(struct efi_device_path *dp); @@ -180,6 +181,8 @@ extern const efi_guid_t efi_guid_hii_database_protocol; extern const efi_guid_t efi_guid_hii_string_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; /* GUID of RNG protocol */ extern const efi_guid_t efi_guid_rng_protocol; diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index 43d6f75d557a..41b1e9b5543c 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -97,7 +97,6 @@ config EFI_CAPSULE_UPDATE Select this option if you want to use capsule update feature, including firmware updates and variable updates. - if EFI_CAPSULE_UPDATE config EFI_CAPSULE_UPDATE_FIRMWARE @@ -107,6 +106,29 @@ config EFI_CAPSULE_UPDATE_FIRMWARE Select this option if you want to enable capsule-based firmware update +config EFI_CAPSULE_FIT_SIMPLE + bool "Firmware management protocol for simple FIT image" + depends on EFI_CAPSULE_UPDATE_FIRMWARE + depends on FIT + select UPDATE_TFTP + select DFU + default n + help + Select this option if you want to enable firmware management protocol + for simple FIT image + +config EFI_CAPSULE_FIT_INTERFACE + string "Storage interface for storing FIT image" + depends on EFI_CAPSULE_FIT_SIMPLE + help + Define storage interface for storing FIT image + +config EFI_CAPSULE_FIT_DEVICE + string "Storage device for storing FIT image" + depends on EFI_CAPSULE_FIT_SIMPLE + help + Define storage device for storing FIT image + endif config EFI_CAPSULE_ON_DISK diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile index f19096924bef..50da10e0e3d9 100644 --- a/lib/efi_loader/Makefile +++ b/lib/efi_loader/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_CMD_BOOTEFI_HELLO) += helloworld_efi.o obj-y += efi_bootmgr.o obj-y += efi_boottime.o obj-$(CONFIG_EFI_CAPSULE_UPDATE) += efi_capsule.o +obj-$(CONFIG_EFI_CAPSULE_FIT_SIMPLE) += efi_firmware.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_firmware.c b/lib/efi_loader/efi_firmware.c new file mode 100644 index 000000000000..021c93196242 --- /dev/null +++ b/lib/efi_loader/efi_firmware.c @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * EFI Firmware management protocol for FIT image + * + * Copyright (c) 2018 Linaro Limited + * Author: AKASHI Takahiro + */ + +#include +#include +#include + +/* + * This FIMRWARE_MANAGEMENT_PROTOCOL driver provides a simple + * firmware update method, and handles + * - a single region of firmware via DFU + * but doesn't support + * - versioning of firmware image + * - package information + */ +const efi_guid_t efi_firmware_image_type_uboot_fit = + EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID; + +static +efi_status_t EFIAPI efi_fmp_fit_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 ret = EFI_SUCCESS; + + EFI_ENTRY("%p %p %p %p %p %p %p %p\n", this, + image_info_size, image_info, + descriptor_version, descriptor_count, descriptor_size, + package_version, package_version_name); + + if (!image_info_size) + return EFI_EXIT(EFI_INVALID_PARAMETER); + + if (*image_info_size < sizeof(*image_info)) { + *image_info_size = sizeof(*image_info); + return EFI_EXIT(EFI_BUFFER_TOO_SMALL); + } + + if (!image_info) + return EFI_EXIT(EFI_INVALID_PARAMETER); + + *image_info_size = sizeof(*image_info); + if (descriptor_version) + *descriptor_version = EFI_FIRMWARE_IMAGE_DESCRIPTOR_VERSION; + if (descriptor_count) + *descriptor_count = 1; + if (descriptor_size) + *descriptor_size = sizeof(*image_info); + if (package_version) + *package_version = 0xffffffff; /* not supported */ + if (package_version_name) + *package_version_name = NULL; /* not supported */ + + image_info[0].image_index = 1; + image_info[0].image_type_id = efi_firmware_image_type_uboot_fit; + image_info[0].image_id = 0; + image_info[0].image_id_name = L"fw_simple"; + image_info[0].version = 0; /* not supported */ + image_info[0].version_name = NULL; /* not supported */ + image_info[0].size = 0; +#if defined(CONFIG_FIT) && defined(CONFIG_UPDATE_TFTP) + image_info[0].attributes_supported = + EFI_IMAGE_ATTRIBUTE_IMAGE_UPDATABLE; + image_info[0].attributes_setting = EFI_IMAGE_ATTRIBUTE_IMAGE_UPDATABLE; +#else + image_info[0].attributes_supported = 0; + image_info[0].attributes_setting = 0; +#endif + image_info[0].lowest_supported_image_version = 0; + image_info[0].last_attempt_version = 0; + image_info[0].last_attempt_status = LAST_ATTEMPT_STATUS_SUCCESS; + image_info[0].hardware_instance = 1; + image_info[0].dependencies = NULL; + + return EFI_EXIT(ret); +} + +static +efi_status_t EFIAPI efi_fmp_fit_get_image( + struct efi_firmware_management_protocol *this, + u8 image_index, + void *image, + efi_uintn_t *image_size) +{ + EFI_ENTRY("%p %d %p %p\n", this, image_index, image, image_size); + + return EFI_EXIT(EFI_UNSUPPORTED); +} + +static +efi_status_t EFIAPI efi_fmp_fit_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_ENTRY("%p %d %p %ld %p %p %p\n", this, image_index, image, + image_size, vendor_code, progress, abort_reason); + +#if defined(CONFIG_FIT) && defined(CONFIG_UPDATE_TFTP) + if (!image || image_index != 1) + return EFI_EXIT(EFI_INVALID_PARAMETER); + + if (update_tftp((ulong)image, CONFIG_EFI_CAPSULE_FIT_INTERFACE, + CONFIG_EFI_CAPSULE_FIT_DEVICE)) + return EFI_EXIT(EFI_DEVICE_ERROR); + + return EFI_EXIT(EFI_SUCCESS); +#else + return EFI_EXIT(EFI_UNSUPPORTED); +#endif +} + +static +efi_status_t EFIAPI efi_fmp_fit_check_image( + struct efi_firmware_management_protocol *this, + u8 image_index, + const void *image, + efi_uintn_t *image_size, + u32 *image_updatable) +{ + EFI_ENTRY("%p %d %p %p %p\n", this, image_index, image, image_size, + image_updatable); + + return EFI_EXIT(EFI_UNSUPPORTED); +} + +static +efi_status_t EFIAPI efi_fmp_fit_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_ENTRY("%p %p %p %p %p %p\n", this, package_version, + package_version_name, package_version_name_maxlen, + attributes_supported, attributes_setting); + + return EFI_EXIT(EFI_UNSUPPORTED); +} + +static +efi_status_t EFIAPI efi_fmp_fit_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) +{ + EFI_ENTRY("%p %p %p %p %x %p\n", this, image, image_size, vendor_code, + package_version, package_version_name); + + return EFI_EXIT(EFI_UNSUPPORTED); +} + +const struct efi_firmware_management_protocol efi_fmp_fit_simple = { + .get_image_info = efi_fmp_fit_get_image_info, + .get_image = efi_fmp_fit_get_image, + .set_image = efi_fmp_fit_set_image, + .check_image = efi_fmp_fit_check_image, + .get_package_info = efi_fmp_fit_get_package_info, + .set_package_info = efi_fmp_fit_set_package_info, +}; + +efi_status_t arch_efi_load_capsule_drivers(void) +{ + efi_status_t ret; + + ret = EFI_CALL(efi_install_multiple_protocol_interfaces( + &efi_root, &efi_guid_firmware_management_protocol, + &efi_fmp_fit_simple, NULL)); + + return ret; +} From patchwork Tue Mar 17 02:12:43 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AKASHI Takahiro X-Patchwork-Id: 243726 List-Id: U-Boot discussion From: takahiro.akashi at linaro.org (AKASHI Takahiro) Date: Tue, 17 Mar 2020 11:12:43 +0900 Subject: [RFC 10/14] efi_loader: capsule: support variable update In-Reply-To: <20200317021247.5849-1-takahiro.akashi@linaro.org> References: <20200317021247.5849-1-takahiro.akashi@linaro.org> Message-ID: <20200317021247.5849-11-takahiro.akashi@linaro.org> See EBBR specification v1.0. A capsule tagged with the guid, EFI_VARIABLE_STORAGE_GUID, will be handled as a variable update object. What efi_update_capsule() basically does is to re-play SetVariable against each variable entry in a capsule. Signed-off-by: AKASHI Takahiro --- include/efi_api.h | 18 +++++++++++++++ lib/efi_loader/Kconfig | 7 ++++++ lib/efi_loader/efi_capsule.c | 45 ++++++++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+) diff --git a/include/efi_api.h b/include/efi_api.h index e103369186a2..30807942380b 100644 --- a/include/efi_api.h +++ b/include/efi_api.h @@ -229,6 +229,10 @@ enum efi_reset_type { EFI_GUID(0x6dcbd5ed, 0xe82d, 0x4c44, 0xbd, 0xa1, \ 0x71, 0x94, 0x19, 0x9a, 0xd9, 0x2a) +#define EFI_VARIABLE_STORAGE_GUID \ + EFI_GUID(0x1a3fb419, 0x2171, 0x458d, 0xb8, 0xb4, \ + 0xbe, 0xa3, 0x0c, 0x9f, 0x6b, 0xab) + struct efi_capsule_header { efi_guid_t capsule_guid; u32 header_size; @@ -283,6 +287,20 @@ struct efi_capsule_result_variable_fmp { // u16 capsule_target[]; } __packed; +struct efi_ebbr_variable { + u16 variable_name[64]; + efi_guid_t vendor_guid; + u32 attributes; + u32 data_size; + u8 data[]; +}; + +struct efi_ebbr_variable_bundle { + struct efi_capsule_header header; + u8 reserved[0]; + struct efi_ebbr_variable variables[]; +} __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/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index 41b1e9b5543c..616e2acbe102 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -129,6 +129,13 @@ config EFI_CAPSULE_FIT_DEVICE help Define storage device for storing FIT image +config EFI_CAPSULE_UPDATE_VARIABLE + bool "Capsule based variable update" + default n + help + Select this option if you want to enable capsule-based + variable update support + endif config EFI_CAPSULE_ON_DISK diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c index f3526beed681..1293270aea95 100644 --- a/lib/efi_loader/efi_capsule.c +++ b/lib/efi_loader/efi_capsule.c @@ -18,6 +18,7 @@ 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; +static const efi_guid_t efi_guid_variable_storage = EFI_VARIABLE_STORAGE_GUID; /* for file system access */ static struct efi_file_handle *bootdev_root; @@ -172,6 +173,45 @@ static efi_status_t efi_capsule_update_firmware( } #endif /* CONFIG_EFI_CAPSULE_UPDATE_FIRMWARE */ +#ifdef CONFIG_EFI_CAPSULE_UPDATE_VARIABLE +/* + * Execute a log of variable changes + */ +static efi_status_t +efi_capsule_update_variables(struct efi_ebbr_variable_bundle *bundle) +{ + struct efi_ebbr_variable *variable; + efi_status_t ret = EFI_SUCCESS; + + for (variable = (void *)bundle + bundle->header.header_size; + (void *)variable + < ((void *)bundle + bundle->header.capsule_image_size); + variable = (struct efi_ebbr_variable *) + ((void *)variable + + sizeof(*variable) + variable->data_size)) { + ret = efi_set_variable(variable->variable_name, + &variable->vendor_guid, + variable->attributes, + variable->data_size, + &variable->data); + /* Should NOT_FOUND always be treated as success? */ + if (ret == EFI_NOT_FOUND) + ret = EFI_SUCCESS; + EFI_PRINT("Capsule variable update %s: %ls\n", + ret == EFI_SUCCESS ? "succeeded" : "failed", + variable->variable_name); + } + + return ret; +} +#else +static efi_status_t +efi_capsule_update_variables(struct efi_ebbr_variable_bundle *bundle) +{ + return EFI_UNSUPPORTED; +} +#endif /* CONFIG_EFI_CAPSULE_UPDATE_VARIABLE */ + /* * Launch a capsule */ @@ -214,6 +254,11 @@ efi_status_t EFIAPI efi_update_capsule( ret = efi_capsule_update_firmware( (struct efi_firmware_management_capsule_header *) ((void *)capsule + sizeof(*capsule))); + else if (!guidcmp(&capsule->capsule_guid, + &efi_guid_variable_storage)) + ret = efi_capsule_update_variables( + (struct efi_ebbr_variable_bundle *) + capsule); else ret = EFI_UNSUPPORTED; From patchwork Tue Mar 17 02:12:44 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AKASHI Takahiro X-Patchwork-Id: 243728 List-Id: U-Boot discussion From: takahiro.akashi at linaro.org (AKASHI Takahiro) Date: Tue, 17 Mar 2020 11:12:44 +0900 Subject: [RFC 11/14] efi_loader: variable: export variables table for runtime access In-Reply-To: <20200317021247.5849-1-takahiro.akashi@linaro.org> References: <20200317021247.5849-1-takahiro.akashi@linaro.org> Message-ID: <20200317021247.5849-12-takahiro.akashi@linaro.org> There are platforms on which OS's won't be able to access UEFI variables at runtime (i.e. after ExitBootServices). With this patch, all the UEFI variables are exported as a configuration table so as to enable retrieving variables' values from the table, and later modifying them via capsule-on-disk if necessary. The idea is based on Peter's proposal[1] in boot-arch ML. [1] https://lists.linaro.org/pipermail/boot-architecture/2018-October/000883.html Signed-off-by: AKASHI Takahiro --- include/efi_loader.h | 7 +++ lib/efi_loader/Kconfig | 10 ++++ lib/efi_loader/efi_boottime.c | 10 ++++ lib/efi_loader/efi_capsule.c | 4 ++ lib/efi_loader/efi_variable.c | 109 ++++++++++++++++++++++++++++++++++ 5 files changed, 140 insertions(+) diff --git a/include/efi_loader.h b/include/efi_loader.h index 79bdf9586d24..93ed5502821c 100644 --- a/include/efi_loader.h +++ b/include/efi_loader.h @@ -183,6 +183,8 @@ extern const efi_guid_t efi_guid_hii_string_protocol; extern const efi_guid_t efi_guid_capsule_report; /* GUID of firmware management protocol */ extern const efi_guid_t efi_guid_firmware_management_protocol; +/* GUID of runtime variable access */ +extern const efi_guid_t efi_guid_variable_storage; /* GUID of RNG protocol */ extern const efi_guid_t efi_guid_rng_protocol; @@ -659,6 +661,10 @@ efi_status_t EFIAPI efi_query_variable_info( u64 *remaining_variable_storage_size, u64 *maximum_variable_size); +#ifdef CONFIG_EFI_VARIABLE_EXPORT +efi_status_t efi_install_variables_table(void); +#endif + /* * See section 3.1.3 in the v2.7 UEFI spec for more details on * the layout of EFI_LOAD_OPTION. In short it is: @@ -683,6 +689,7 @@ struct efi_load_option { void efi_deserialize_load_option(struct efi_load_option *lo, u8 *data); unsigned long efi_serialize_load_option(struct efi_load_option *lo, u8 **data); efi_status_t efi_bootmgr_load(efi_handle_t *handle); +efi_status_t efi_install_variables_table(void); /* Capsule update */ efi_status_t EFIAPI efi_update_capsule( diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index 616e2acbe102..edb8d19059ea 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -41,6 +41,16 @@ 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_VARIABLE_EXPORT + bool "Export variables table as configuration table" + depends on !EFI_VARIABLE_RUNTIME_ACCESS + depends on EFI_CAPSULE_UPDATE_VARIABLE + default y + help + Select this option if you want to export UEFI variables as + configuration table so that OS can still get UEFI variable's + value. + config EFI_DEVICE_PATH_TO_TEXT bool "Device path to text protocol" default y diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index c2a789b4f910..406644e4da67 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -2898,6 +2898,16 @@ efi_status_t EFIAPI efi_start_image(efi_handle_t image_handle, if (ret != EFI_SUCCESS) return EFI_EXIT(EFI_INVALID_PARAMETER); +#ifdef CONFIG_EFI_VARIABLE_EXPORT + /* Install variables database as a configuration table */ + ret = efi_install_variables_table(); + if (ret != EFI_SUCCESS) + return EFI_EXIT(ret); +#endif + /* + * TODO: remove a table after image is terminated. + */ + image_obj->exit_data_size = exit_data_size; image_obj->exit_data = exit_data; diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c index 1293270aea95..6b737aec1b28 100644 --- a/lib/efi_loader/efi_capsule.c +++ b/lib/efi_loader/efi_capsule.c @@ -18,7 +18,11 @@ 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_VARIABLE_EXPORT +const efi_guid_t efi_guid_variable_storage = EFI_VARIABLE_STORAGE_GUID; +#else static const efi_guid_t efi_guid_variable_storage = EFI_VARIABLE_STORAGE_GUID; +#endif /* for file system access */ static struct efi_file_handle *bootdev_root; diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c index c316bdfec0e4..270e5211f633 100644 --- a/lib/efi_loader/efi_variable.c +++ b/lib/efi_loader/efi_variable.c @@ -641,3 +641,112 @@ efi_status_t efi_init_variables(void) { return EFI_SUCCESS; } + +#ifdef CONFIG_EFI_VARIABLE_EXPORT +/** + * efi_install_variables_table() - install variables table + * + * This function adds a configuration table that contains all the cache + * data of UEFI variables for runtime access. + * + * Return: status code + */ +efi_status_t efi_install_variables_table(void) +{ + u16 var_name16[64]; + efi_uintn_t var_name16_len; + efi_guid_t guid; + u32 attributes; + struct efi_capsule_header capsule; + u8 *data = NULL, *p; + efi_uintn_t capsule_size; + struct efi_ebbr_variable variable; + u8 *var_buf; + efi_uintn_t var_size, var_max_size; + efi_status_t ret; + + capsule.capsule_guid = efi_guid_variable_storage; + capsule.header_size = sizeof(capsule); + capsule.flags = CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE; + capsule_size = sizeof(capsule); + + /* Determine size of capsule */ + var_name16[0] = 0; + var_name16_len = 64; + var_buf = NULL; + var_max_size = 0; + for (;;) { + ret = EFI_CALL(efi_get_next_variable_name(&var_name16_len, + var_name16, + &guid)); + if (ret == EFI_NOT_FOUND) + break; + else if (ret != EFI_SUCCESS) + goto out; + + var_size = 0; + ret = EFI_CALL(efi_get_variable(var_name16, &guid, &attributes, + &var_size, var_buf)); + if (ret != EFI_BUFFER_TOO_SMALL) + goto out; + + capsule_size += sizeof(variable) + var_size; + if (var_size > var_max_size) + var_max_size = var_size; + } + capsule.capsule_image_size = capsule_size; + + ret = efi_allocate_pool(EFI_RUNTIME_SERVICES_DATA, + capsule_size, (void **)&data); + if (ret != EFI_SUCCESS) + return EFI_OUT_OF_RESOURCES; + + var_buf = malloc(var_max_size); + if (!var_buf) { + ret = EFI_OUT_OF_RESOURCES; + goto out; + } + + /* Copy data */ + p = data; + memcpy(p, &capsule, sizeof(capsule)); + p += sizeof(capsule); + + var_name16[0] = 0; + for (;;) { + ret = EFI_CALL(efi_get_next_variable_name(&var_name16_len, + var_name16, &guid)); + if (ret == EFI_NOT_FOUND) + break; + else if (ret != EFI_SUCCESS) + goto out; + + var_size = var_max_size; + ret = EFI_CALL(efi_get_variable(var_name16, &guid, &attributes, + &var_size, var_buf)); + if (ret != EFI_SUCCESS) + /* should not happen */ + goto out; + + u16_strcpy(variable.variable_name, var_name16); + variable.vendor_guid = guid; + variable.attributes = attributes; + variable.data_size = var_size; + + memcpy(p, &variable, sizeof(variable)); + p += sizeof(variable); + memcpy(p, var_buf, var_size); + p += var_size; + } + + ret = EFI_CALL(efi_install_configuration_table( + &efi_guid_variable_storage, data)); + +out: + free(var_buf); + if (ret != EFI_SUCCESS && data) + efi_free_pool(data); + + return ret; +} +#endif /* CONFIG_EFI_VARIABLE_EXPORT */ From patchwork Tue Mar 17 02:12:45 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AKASHI Takahiro X-Patchwork-Id: 243730 List-Id: U-Boot discussion From: takahiro.akashi at linaro.org (AKASHI Takahiro) Date: Tue, 17 Mar 2020 11:12:45 +0900 Subject: [RFC 12/14] cmd: add "efidebug capsule" command In-Reply-To: <20200317021247.5849-1-takahiro.akashi@linaro.org> References: <20200317021247.5849-1-takahiro.akashi@linaro.org> Message-ID: <20200317021247.5849-13-takahiro.akashi@linaro.org> "efidebug capsule" is more or less a debugging utility. efidebug capsule update: invoke UpdateCapsule against data on memory efidebug capsule show: show a header of a capsule efidebug capsule result: dump a capsule result variable Signed-off-by: AKASHI Takahiro --- cmd/efidebug.c | 234 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 234 insertions(+) diff --git a/cmd/efidebug.c b/cmd/efidebug.c index 3a50dafbbca6..604f4b42e9ad 100644 --- a/cmd/efidebug.c +++ b/cmd/efidebug.c @@ -18,6 +18,227 @@ #define BS systab.boottime #define RT systab.runtime +#ifdef CONFIG_EFI_CAPSULE_UPDATE +/** + * do_efi_capsule_update() - process a capsule update + * + * @cmdtp: Command table + * @flag: Command flag + * @argc: Number of arguments + * @argv: Argument array + * Return: CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure + * + * Implement efidebug "capsule update" sub-command. + * process a capsule update. + * + * efidebug capsule update [-v] + */ +static int do_efi_capsule_update(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + struct efi_capsule_header *capsule; + int verbose = 0; + char *endp; + efi_status_t ret; + + if (argc != 2 && argc != 3) + return CMD_RET_USAGE; + + if (argc == 3) { + if (strcmp(argv[1], "-v")) + return CMD_RET_USAGE; + + verbose = 1; + argc--; + argv++; + } + + capsule = (typeof(capsule))simple_strtoul(argv[1], &endp, 16); + if (endp == argv[1]) { + printf("Invalid address: %s", argv[1]); + return CMD_RET_FAILURE; + } + + if (verbose) { + printf("Capsule guid: %pUl\n", &capsule->capsule_guid); + printf("Capsule flags: 0x%x\n", capsule->flags); + printf("Capsule header size: 0x%x\n", capsule->header_size); + printf("Capsule image size: 0x%x\n", + capsule->capsule_image_size); + } + + ret = EFI_CALL(RT->update_capsule(&capsule, 1, (u64)NULL)); + if (ret) { + printf("Cannot handle a capsule at %p", capsule); + return CMD_RET_FAILURE; + } + + return CMD_RET_SUCCESS; +} + +/** + * do_efi_capsule_show() - show capsule information + * + * @cmdtp: Command table + * @flag: Command flag + * @argc: Number of arguments + * @argv: Argument array + * Return: CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure + * + * Implement efidebug "capsule show" sub-command. + * show capsule information. + * + * efidebug capsule show + */ +static int do_efi_capsule_show(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + struct efi_capsule_header *capsule; + char *endp; + + if (argc != 2) + return CMD_RET_USAGE; + + capsule = (typeof(capsule))simple_strtoul(argv[1], &endp, 16); + if (endp == argv[1]) { + printf("Invalid address: %s", argv[1]); + return CMD_RET_FAILURE; + } + + printf("Capsule guid: %pUl\n", &capsule->capsule_guid); + printf("Capsule flags: 0x%x\n", capsule->flags); + printf("Capsule header size: 0x%x\n", capsule->header_size); + printf("Capsule image size: 0x%x\n", + capsule->capsule_image_size); + + return CMD_RET_SUCCESS; +} + +/** + * do_efi_capsule_res() - show a capsule update result + * + * @cmdtp: Command table + * @flag: Command flag + * @argc: Number of arguments + * @argv: Argument array + * Return: CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure + * + * Implement efidebug "capsule result" sub-command. + * show a capsule update result. + * If result number is not specified, CapsuleLast will be shown. + * + * efidebug capsule result [] + */ +static int do_efi_capsule_res(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + int capsule_id; + char *endp; + char var_name[12]; + u16 var_name16[12], *p; + efi_guid_t guid; + struct efi_capsule_result_variable_header *result = NULL; + efi_uintn_t size; + efi_status_t ret; + + if (argc != 1 && argc != 2) + return CMD_RET_USAGE; + + guid = efi_guid_capsule_report; + if (argc == 1) { + size = sizeof(var_name16); + ret = EFI_CALL(RT->get_variable(L"CapsuleLast", &guid, NULL, + &size, var_name16)); + if (ret != EFI_SUCCESS) { + if (ret == EFI_NOT_FOUND) + printf("CapsuleLast doesn't exist\n"); + else + printf("Failed to get CapsuleLast\n"); + + return CMD_RET_FAILURE; + } + printf("CapsuleLast is %ls\n", var_name16); + } else { + argc--; + argv++; + + capsule_id = simple_strtoul(argv[0], &endp, 16); + if (capsule_id < 0 || capsule_id > 0xffff) + return CMD_RET_USAGE; + + sprintf(var_name, "Capsule%04X", capsule_id); + p = var_name16; + utf8_utf16_strncpy(&p, var_name, 9); + } + + size = 0; + ret = EFI_CALL(RT->get_variable(var_name16, &guid, NULL, &size, NULL)); + if (ret == EFI_BUFFER_TOO_SMALL) { + result = malloc(size); + ret = EFI_CALL(RT->get_variable(var_name16, &guid, NULL, &size, + result)); + if (ret != EFI_SUCCESS) { + free(result); + printf("Failed to get %ls\n", var_name16); + + return CMD_RET_FAILURE; + } + } + + printf("Result total size: 0x%x\n", result->variable_total_size); + printf("Capsule guid: %pUl\n", &result->capsule_guid); + printf("Time processed: %04d-%02d-%02d %02d:%02d:%02d\n", + result->capsule_processed.year, result->capsule_processed.month, + result->capsule_processed.day, result->capsule_processed.hour, + result->capsule_processed.minute, + result->capsule_processed.second); + printf("Capsule status: 0x%lx\n", result->capsule_status); + + free(result); + + return CMD_RET_SUCCESS; +} + +static cmd_tbl_t cmd_efidebug_capsule_sub[] = { + U_BOOT_CMD_MKENT(update, CONFIG_SYS_MAXARGS, 1, do_efi_capsule_update, + "", ""), + U_BOOT_CMD_MKENT(show, CONFIG_SYS_MAXARGS, 1, do_efi_capsule_show, + "", ""), + U_BOOT_CMD_MKENT(result, CONFIG_SYS_MAXARGS, 1, do_efi_capsule_res, + "", ""), +}; + +/** + * do_efi_capsule() - manage UEFI capsules + * + * @cmdtp: Command table + * @flag: Command flag + * @argc: Number of arguments + * @argv: Argument array + * Return: CMD_RET_SUCCESS on success, + * CMD_RET_USAGE or CMD_RET_RET_FAILURE on failure + * + * Implement efidebug "capsule" sub-command. + */ +static int do_efi_capsule(cmd_tbl_t *cmdtp, int flag, + int argc, char * const argv[]) +{ + cmd_tbl_t *cp; + + if (argc < 2) + return CMD_RET_USAGE; + + argc--; argv++; + + cp = find_cmd_tbl(argv[0], cmd_efidebug_capsule_sub, + ARRAY_SIZE(cmd_efidebug_capsule_sub)); + if (!cp) + return CMD_RET_USAGE; + + return cp->cmd(cmdtp, flag, argc, argv); +} +#endif /* CONFIG_EFI_CAPSULE_UPDATE */ + /** * efi_get_device_handle_info() - get information of UEFI device * @@ -1079,6 +1300,10 @@ static int do_efi_boot_opt(cmd_tbl_t *cmdtp, int flag, static cmd_tbl_t cmd_efidebug_sub[] = { U_BOOT_CMD_MKENT(boot, CONFIG_SYS_MAXARGS, 1, do_efi_boot_opt, "", ""), +#ifdef CONFIG_EFI_CAPSULE_UPDATE + U_BOOT_CMD_MKENT(capsule, CONFIG_SYS_MAXARGS, 1, do_efi_capsule, + "", ""), +#endif U_BOOT_CMD_MKENT(devices, CONFIG_SYS_MAXARGS, 1, do_efi_show_devices, "", ""), U_BOOT_CMD_MKENT(drivers, CONFIG_SYS_MAXARGS, 1, do_efi_show_drivers, @@ -1149,6 +1374,15 @@ static char efidebug_help_text[] = "efidebug boot order [ [ [ [...]]]]\n" " - set/show UEFI boot order\n" "\n" +#ifdef CONFIG_EFI_CAPSULE_UPDATE + "efidebug capsule update [-v] \n" + " - process a capsule\n" + "efidebug capsule show \n" + " - show capsule information\n" + "efidebug capsule result []\n" + " - show a capsule update result\n" + "\n" +#endif "efidebug devices\n" " - show UEFI devices\n" "efidebug drivers\n" From patchwork Tue Mar 17 02:12:46 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AKASHI Takahiro X-Patchwork-Id: 243731 List-Id: U-Boot discussion From: takahiro.akashi at linaro.org (AKASHI Takahiro) Date: Tue, 17 Mar 2020 11:12:46 +0900 Subject: [RFC 13/14] tools: add mkeficapsule command for UEFI capsule update test In-Reply-To: <20200317021247.5849-1-takahiro.akashi@linaro.org> References: <20200317021247.5849-1-takahiro.akashi@linaro.org> Message-ID: <20200317021247.5849-14-takahiro.akashi@linaro.org> This is a utility mainly for test purpose. mkeficapsule -f: create a FIT image firmware capsule mkeficapsule -v: create a specific test case capsule for variables Having said that, you will be able to customize the code to fit your specific requirements for your platform. Signed-off-by: AKASHI Takahiro --- tools/Makefile | 3 + tools/mkeficapsule.c | 501 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 504 insertions(+) create mode 100644 tools/mkeficapsule.c diff --git a/tools/Makefile b/tools/Makefile index 99be724b82a5..fe07f8952f49 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -222,6 +222,9 @@ hostprogs-$(CONFIG_MIPS) += mips-relocs hostprogs-$(CONFIG_ASN1_COMPILER) += asn1_compiler HOSTCFLAGS_asn1_compiler.o = -idirafter $(srctree)/include +# TODO: only build this for capsule pytest +hostprogs-$(CONFIG_EFI_CAPSULE_UPDATE) += mkeficapsule + # We build some files with extra pedantic flags to try to minimize things # that won't build on some weird host compiler -- though there are lots of # exceptions for files that aren't complaint. diff --git a/tools/mkeficapsule.c b/tools/mkeficapsule.c new file mode 100644 index 000000000000..732a54811df9 --- /dev/null +++ b/tools/mkeficapsule.c @@ -0,0 +1,501 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2018 Linaro Limited + * Author: AKASHI Takahiro + */ + +#include +#include +#include +#include +#include +/* + * TODO: use libefi/libgnuefi headers + */ + +typedef u_int8_t u8; +typedef u_int16_t u16; +typedef u_int32_t u32; +typedef u_int64_t u64; + +/* include/efi.h */ +typedef struct { + u8 b[16]; +} efi_guid_t; + +#define EFI_GUID(a, b, c, d0, d1, d2, d3, d4, d5, d6, d7) \ + {{ (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, \ + ((a) >> 24) & 0xff, \ + (b) & 0xff, ((b) >> 8) & 0xff, \ + (c) & 0xff, ((c) >> 8) & 0xff, \ + (d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) } } + +#define EFI_VARIABLE_NON_VOLATILE 0x0000000000000001 +#define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x0000000000000002 +#define EFI_VARIABLE_RUNTIME_ACCESS 0x0000000000000004 + +/* include/efi_api.h */ +#define EFI_GLOBAL_VARIABLE_GUID \ + EFI_GUID(0x8be4df61, 0x93ca, 0x11d2, 0xaa, 0x0d, \ + 0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c) + +#define CAPSULE_FLAGS_PERSIST_ACROSS_RESET 0x00010000 +#define CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE 0x00020000 +#define CAPSULE_FLAGS_INITIATE_RESET 0x00040000 + +#define EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID \ + EFI_GUID(0xae13ff2d, 0x9ad4, 0x4e25, 0x9a, 0xc8, \ + 0x6d, 0x80, 0xb3, 0xb2, 0x21, 0x47) + +#define EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID \ + EFI_GUID(0x6dcbd5ed, 0xe82d, 0x4c44, 0xbd, 0xa1, \ + 0x71, 0x94, 0x19, 0x9a, 0xd9, 0x2a) + +#define EFI_VARIABLE_STORAGE_GUID \ + EFI_GUID(0x1a3fb419, 0x2171, 0x458d, 0xb8, 0xb4, \ + 0xbe, 0xa3, 0x0c, 0x9f, 0x6b, 0xab) + +struct efi_capsule_header { + efi_guid_t capsule_guid; + u32 header_size; + u32 flags; + u32 capsule_image_size; +} __attribute__((packed)); + +struct efi_capsule_block_descriptor { + u64 length; + union { + u64 data_block; + u64 continuation_ptr; + }; +} __attribute__((packed)); + +struct efi_firmware_management_capsule_header { + u32 version; + u16 embedded_driver_count; + u16 payload_item_count; + u64 item_offset_list[]; +} __attribute__((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; +} __attribute__((packed)); + +struct efi_ebbr_variable { + u16 variable_name[64]; + efi_guid_t vendor_guid; + u32 attributes; + u32 data_size; + u8 data[]; +}; + +struct efi_ebbr_variable_bundle { + struct efi_capsule_header header; + u8 reserved[0]; + struct efi_ebbr_variable variables[]; +} __attribute__((packed)); + +efi_guid_t efi_guid_fm_capsule = EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID; +efi_guid_t efi_guid_image_type_uboot_fit = + EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID; +efi_guid_t efi_guid_variable_storage = EFI_VARIABLE_STORAGE_GUID; +efi_guid_t efi_global_variable_guid = EFI_GLOBAL_VARIABLE_GUID; + +/* generated by efi boot add 3 TEST scsi 0:0 /EFI/Images/hello.efi "" */ +char boot_var3[] = "0100000052005400450053005400000001041400b9731de684a3cc4aaeab82e828f3628b0302080000000000040432005c005c004500460049005c0049006d0061006700650073005c00680065006c006c006f002e0065006600690000007fff040000"; +u16 boot_order[] = { + 0x0003, + 0x0002, +}; + +/* efi_variable.c */ +static int hex(int ch) +{ + if (ch >= 'a' && ch <= 'f') + return ch - 'a' + 10; + if (ch >= '0' && ch <= '9') + return ch - '0'; + if (ch >= 'A' && ch <= 'F') + return ch - 'A' + 10; + return -1; +} + +static int hex2mem(u8 *mem, const char *hexstr, int size) +{ + int nibble; + int i; + + for (i = 0; i < size; i++) { + if (*hexstr == '\0') + break; + + nibble = hex(*hexstr); + if (nibble < 0) + return -1; + + *mem = nibble; + hexstr++; + + nibble = hex(*hexstr); + if (nibble < 0) + return -1; + + *mem = (*mem << 4) | nibble; + hexstr++; + mem++; + } + + return i; +} + +void wcharcpy(u16 *dst, wchar_t *src, size_t n) +{ + int i; + + for (i = 0; i < n; i++) + *dst++ = *src++; +} + +static int create_fwbin(char *bin, char *path) +{ + struct efi_capsule_header header; + struct efi_firmware_management_capsule_header capsule; + struct efi_firmware_management_capsule_image_header image; + FILE *f, *g; + struct stat bin_stat; + u8 *data; + size_t size; + + g = fopen(bin, "r"); + if (!g) { + printf("cannot open %s\n", bin); + return -1; + } + if (stat(bin, &bin_stat) < 0) { + printf("cannot determine the size of %s\n", bin); + goto err_1; + } + data = malloc(bin_stat.st_size); + if (!data) { + printf("cannot allocate memory: %lx\n", bin_stat.st_size); + goto err_1; + } + f = fopen(path, "w"); + if (!f) { + printf("cannot open %s\n", path); + goto err_2; + } + header.capsule_guid = efi_guid_fm_capsule; + header.header_size = sizeof(header); + header.flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET; /* TODO */ + header.capsule_image_size = sizeof(header) + + sizeof(capsule) + sizeof(u64) + + sizeof(image) + + bin_stat.st_size; + + size = fwrite(&header, 1, sizeof(header), f); + if (size < sizeof(header)) { + printf("write failed (%lx)\n", size); + goto err_3; + } + + capsule.version = 0x00000001; + capsule.embedded_driver_count = 0; + capsule.payload_item_count = 1; + capsule.item_offset_list[0] = sizeof(capsule) + sizeof(u64); + size = fwrite(&capsule, 1, sizeof(capsule) + sizeof(u64), f); + if (size < (sizeof(capsule) + sizeof(u64))) { + printf("write failed (%lx)\n", size); + goto err_3; + } + + image.version = 0x00000002; + image.update_image_type_id = efi_guid_image_type_uboot_fit; + image.update_image_index = 1; + image.update_image_size = bin_stat.st_size; + image.update_vendor_code_size = 0; /* none */ + image.update_hardware_instance = 1; + + size = fwrite(&image, 1, sizeof(image), f); + if (size < sizeof(image)) { + printf("write failed (%lx)\n", size); + goto err_3; + } + size = fread(data, 1, bin_stat.st_size, g); + if (size < bin_stat.st_size) { + printf("read failed (%lx)\n", size); + goto err_3; + } + size = fwrite(data, 1, bin_stat.st_size, f); + if (size < bin_stat.st_size) { + printf("write failed (%lx)\n", size); + goto err_3; + } + + fclose(f); + fclose(g); + free(data); + + return 0; + +err_3: + fclose(f); +err_2: + free(data); +err_1: + fclose(g); + + return -1; +} + +static int create_test1(char *path) +{ + FILE *f; + size_t size; + struct efi_capsule_header header; + struct efi_ebbr_variable variable; + u8 *data; + size_t data_size; + + f = fopen(path, "w"); + if (!f) { + printf("cannot open %s\n", path); + return -1; + } + header.capsule_guid = efi_guid_variable_storage; + header.header_size = sizeof(header); + header.flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET; /* TODO */ + header.capsule_image_size = sizeof(header); + + size = fwrite(&header, 1, sizeof(header), f); + if (size < sizeof(header)) { + printf("write failed (%lx)\n", size); + return -1; + } + + /* add a new variable */ + data_size = sizeof(boot_var3) / 2; + data = malloc(data_size); + if (!data) { + printf("memory allocation failed\n"); + return -1; + } + hex2mem(data, boot_var3, data_size); + wcharcpy(variable.variable_name, L"Boot0003", 9); + variable.vendor_guid = efi_global_variable_guid; + variable.attributes = (EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS); + variable.data_size = data_size; + header.capsule_image_size += sizeof(variable) + variable.data_size; + + size = fwrite(&variable, 1, sizeof(variable), f); + if (size < sizeof(variable)) { + printf("write failed (%lx)\n", size); + return -1; + } + size = fwrite(data, 1, data_size, f); + if (size < data_size) { + printf("write failed (%lx)\n", size); + return -1; + } + + /* update capsule image size */ + rewind(f); + size = fwrite(&header, 1, sizeof(header), f); + if (size < sizeof(header)) { + printf("write failed (%lx)\n", size); + return -1; + } + + fclose(f); + + return 0; +} + +static int create_test2(char *path) +{ + FILE *f; + size_t size; + struct efi_capsule_header header; + struct efi_ebbr_variable variable; + + f = fopen(path, "w"); + if (!f) { + printf("cannot open %s\n", path); + return -1; + } + header.capsule_guid = efi_guid_variable_storage; + header.header_size = sizeof(header); + header.flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET; /* TODO */ + header.capsule_image_size = sizeof(header); + + size = fwrite(&header, 1, sizeof(header), f); + if (size < sizeof(header)) { + printf("write failed (%lx)\n", size); + return -1; + } + + /* modify an existing variable */ + wcharcpy(variable.variable_name, L"BootOrder", 10); + variable.vendor_guid = efi_global_variable_guid; + variable.attributes = (EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS); + variable.data_size = sizeof(boot_order); + header.capsule_image_size += sizeof(variable) + variable.data_size; + + size = fwrite(&variable, 1, sizeof(variable), f); + if (size < sizeof(variable)) { + printf("write failed (%lx)\n", size); + return -1; + } + size = fwrite(boot_order, 1, sizeof(boot_order), f); + if (size < sizeof(boot_order)) { + printf("write failed (%lx)\n", size); + return -1; + } + + /* delete an existing variable */ + wcharcpy(variable.variable_name, L"Boot0001", 9); + variable.vendor_guid = efi_global_variable_guid; + variable.attributes = (EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS); + variable.data_size = 0; + header.capsule_image_size += sizeof(variable) + variable.data_size; + + size = fwrite(&variable, 1, sizeof(variable), f); + if (size < sizeof(variable)) { + printf("write failed (%lx)\n", size); + return -1; + } + + /* update capsule image size */ + rewind(f); + size = fwrite(&header, 1, sizeof(header), f); + if (size < sizeof(header)) { + printf("write failed (%lx)\n", size); + return -1; + } + + fclose(f); + + return 0; +} + +static int create_test3(char *path) +{ + FILE *f; + size_t size; + struct efi_capsule_header header; + struct efi_ebbr_variable variable; + + f = fopen(path, "w"); + if (!f) { + printf("cannot open %s\n", path); + return -1; + } + header.capsule_guid = efi_guid_variable_storage; + header.header_size = sizeof(header); + header.flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET; /* TODO */ + header.capsule_image_size = sizeof(header); + + size = fwrite(&header, 1, sizeof(header), f); + if (size < sizeof(header)) { + printf("write failed (%lx)\n", size); + return -1; + } + + /* delete a non-existing variable */ + wcharcpy(variable.variable_name, L"FooVar", 9); + variable.vendor_guid = efi_global_variable_guid; + variable.attributes = (EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS); + variable.data_size = 0; + header.capsule_image_size += sizeof(variable) + variable.data_size; + + size = fwrite(&variable, 1, sizeof(variable), f); + if (size < sizeof(variable)) { + printf("write failed (%lx)\n", size); + return -1; + } + + /* update capsule image size */ + rewind(f); + size = fwrite(&header, 1, sizeof(header), f); + if (size < sizeof(header)) { + printf("write failed (%lx)\n", size); + return -1; + } + + fclose(f); + + return 0; +} + +void print_usage(void) +{ + printf("mkeficapsule [-f | -t ]\n"); +} + +/* + * Usage: + * $ mkeficapsule [-f | -t ] + */ +int main(int argc, char **argv) +{ + int test_no; + + if (argc != 4) { + print_usage(); + return -1; + } + + if (!strcmp(argv[1], "-f")) { + if (create_fwbin(argv[2], argv[3]) < 0) { + printf("Creating firmware capsule for %s failed\n", + argv[2]); + return -1; + } + + return 0; + } else if (!strcmp(argv[1], "-v")) { + test_no = atoi(argv[2]); + + switch (test_no) { + case 1: + if (create_test1(argv[3]) < 0) { + printf("Creating test%02x failed\n", test_no); + return -1; + } + break; + case 2: + if (create_test2(argv[3]) < 0) { + printf("Creating test%02x failed\n", test_no); + return -1; + } + break; + case 3: + if (create_test3(argv[3]) < 0) { + printf("Creating test%02x failed\n", test_no); + return -1; + } + break; + default: + printf("Invalid test number: %s\n", argv[2]); + return -1; + } + + return 0; + } + + print_usage(); + return -1; +} From patchwork Tue Mar 17 02:12:47 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: AKASHI Takahiro X-Patchwork-Id: 243729 List-Id: U-Boot discussion From: takahiro.akashi at linaro.org (AKASHI Takahiro) Date: Tue, 17 Mar 2020 11:12:47 +0900 Subject: [RFC 14/14] test/py: add efi capsule test In-Reply-To: <20200317021247.5849-1-takahiro.akashi@linaro.org> References: <20200317021247.5849-1-takahiro.akashi@linaro.org> Message-ID: <20200317021247.5849-15-takahiro.akashi@linaro.org> We will have two pytest scenarios for capsule update here. * for firmware update (based on simple firmware management protocol) * for variable update Both can run on sandbox build and gives you some idea about how they should work on production system. Signed-off-by: AKASHI Takahiro --- test/py/tests/test_efi_capsule/conftest.py | 109 ++++++++++++++ test/py/tests/test_efi_capsule/defs.py | 21 +++ .../test_efi_capsule/test_capsule_firmware.py | 102 +++++++++++++ .../test_efi_capsule/test_capsule_variable.py | 141 ++++++++++++++++++ test/py/tests/test_efi_capsule/uboot_env.its | 25 ++++ 5 files changed, 398 insertions(+) create mode 100644 test/py/tests/test_efi_capsule/conftest.py create mode 100644 test/py/tests/test_efi_capsule/defs.py create mode 100644 test/py/tests/test_efi_capsule/test_capsule_firmware.py create mode 100644 test/py/tests/test_efi_capsule/test_capsule_variable.py create mode 100644 test/py/tests/test_efi_capsule/uboot_env.its diff --git a/test/py/tests/test_efi_capsule/conftest.py b/test/py/tests/test_efi_capsule/conftest.py new file mode 100644 index 000000000000..5d50df95cdf0 --- /dev/null +++ b/test/py/tests/test_efi_capsule/conftest.py @@ -0,0 +1,109 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2020, Linaro Limited +# Author: AKASHI Takahiro + +import os +import os.path +import pytest +import re +from subprocess import call, check_call, check_output, CalledProcessError +from defs import * + +# from test/py/conftest.py +def tool_is_in_path(tool): + for path in os.environ["PATH"].split(os.pathsep): + fn = os.path.join(path, tool) + if os.path.isfile(fn) and os.access(fn, os.X_OK): + return True + return False + +# +# Fixture for UEFI secure boot test +# + at pytest.fixture(scope='session') +def efi_capsule_data(request, u_boot_config): + """Set up a file system to be used in UEFI capsule test. + + Args: + request: Pytest request object. + u_boot_config: U-boot configuration. + + Return: + A path to disk image to be used for testing + """ + image_path = u_boot_config.persistent_data_dir + image_path = image_path + '/' + EFI_BOOTDEV_IMAGE_NAME + image_size = EFI_BOOTDEV_IMAGE_SIZE + part_size = EFI_BOOTDEV_PART_SIZE + fs_type = EFI_BOOTDEV_FS_TYPE + + try: + # non_root = tool_is_in_path('udisksctl') + non_root = False + + # create a disk/partition + check_call('dd if=/dev/zero of=%s bs=1MiB count=%d' + % (image_path, image_size), shell=True) + check_call('sgdisk %s -n 1:0:+%dMiB' + % (image_path, part_size), shell=True) + # create a file system + check_call('dd if=/dev/zero of=%s.tmp bs=1MiB count=%d' + % (image_path, part_size), shell=True) + check_call('mkfs -t %s %s.tmp' % (fs_type, image_path), shell=True) + check_call('dd if=%s.tmp of=%s bs=1MiB seek=1 count=%d conv=notrunc' + % (image_path, image_path, 1), shell=True) + check_call('rm %s.tmp' % image_path, shell=True) + if non_root: + out_data = check_output('udisksctl loop-setup -f %s -o %d' + % (image_path, 1048576), shell=True).decode() + m = re.search('(?<= as )(.*)\.', out_data) + loop_dev = m.group(1) + # print('loop device is: %s' % loop_dev) + out_data = check_output('udisksctl info -b %s' + % loop_dev, shell=True).decode() + m = re.search('MountPoints:[ \t]+(.*)', out_data) + mnt_point = m.group(1) + else: + loop_dev = check_output('sudo losetup -o 1MiB --sizelimit %dMiB --show -f %s | tr -d "\n"' + % (part_size, image_path), shell=True).decode() + mnt_point = '/mnt' + check_call('sudo mount -t %s -o umask=000 %s %s' + % (fs_type, loop_dev, mnt_point), shell=True) + + # print('1: mount point is: %s' % mnt_point) + + # Add variable capsules + check_call('mkdir -p %s%s' % (mnt_point, CAPSULE_DATA_DIR), shell=True) + check_call('mkdir -p %s%s' % (mnt_point, CAPSULE_INSTALL_DIR), shell=True) + check_call('%s/tools/mkeficapsule -v 1 %s%s/tESt01' + % (u_boot_config.build_dir, mnt_point, + CAPSULE_DATA_DIR), shell=True) + check_call('%s/tools/mkeficapsule -v 2 %s%s/Test02' + % (u_boot_config.build_dir, + mnt_point, CAPSULE_DATA_DIR), shell=True) + check_call('%s/tools/mkeficapsule -v 3 %s%s/TeST03' + % (u_boot_config.build_dir, + mnt_point, CAPSULE_DATA_DIR), shell=True) + + # Create its for FIT image + check_call('sed -e \"s?BINFILE?%s%s/%s?\" %s/test/py/tests/test_efi_capsule/uboot_env.its > %s%s/uboot_env.its' + % (mnt_point, CAPSULE_DATA_DIR, FW_BIN, + u_boot_config.source_dir, + mnt_point, CAPSULE_DATA_DIR), shell=True) + + if non_root: + call('udisksctl unmount -b %s' % loop_dev, shell=True) + # not needed + # check_call('udisksctl loop-delete -b %s' % loop_dev, shell=True) + else: + call('sudo umount %s' % loop_dev, shell=True) + call('sudo losetup -d %s' % loop_dev, shell=True) + + except CalledProcessError as e: + pytest.skip('Setup failed: %s' % e.cmd) + return + else: + yield image_path + finally: + print('1: mount point is: %s' % mnt_point) + # call('rm -f %s' % image_path, shell=True) diff --git a/test/py/tests/test_efi_capsule/defs.py b/test/py/tests/test_efi_capsule/defs.py new file mode 100644 index 000000000000..c2083b59c19f --- /dev/null +++ b/test/py/tests/test_efi_capsule/defs.py @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: GPL-2.0+ + +# Disk image name +EFI_BOOTDEV_IMAGE_NAME='test_efi_capsule.img' + +# Size in MiB +EFI_BOOTDEV_IMAGE_SIZE=16 +EFI_BOOTDEV_PART_SIZE=8 + +# Partition file system type +EFI_BOOTDEV_FS_TYPE='vfat' + +# Owner guid +GUID='11111111-2222-3333-4444-123456789abc' + +# Directories +CAPSULE_DATA_DIR='/EFI/CapsuleTestData' +CAPSULE_INSTALL_DIR='/EFI/UpdateCapsule' + +# +FW_BIN='spi_sf.bin' diff --git a/test/py/tests/test_efi_capsule/test_capsule_firmware.py b/test/py/tests/test_efi_capsule/test_capsule_firmware.py new file mode 100644 index 000000000000..52f8fa108ebd --- /dev/null +++ b/test/py/tests/test_efi_capsule/test_capsule_firmware.py @@ -0,0 +1,102 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2020, Linaro Limited +# Author: AKASHI Takahiro +# +# U-Boot UEFI: Capsule Update for Simple FIT Image Test + +""" +This test verifies capsule-on-disk firmware update +""" + +import pytest +import re +from defs import * +from subprocess import call, check_call, check_output, CalledProcessError + + at pytest.mark.boardspec('sandbox') + at pytest.mark.buildconfigspec('efi_capsule_fit_simple') + at pytest.mark.buildconfigspec('efi_capsule_on_disk') + at pytest.mark.buildconfigspec('dfu') + at pytest.mark.buildconfigspec('dfu_sf') + at pytest.mark.buildconfigspec('dfu_tftp') + at pytest.mark.buildconfigspec('cmd_efidebug') + at pytest.mark.buildconfigspec('cmd_fat') + at pytest.mark.buildconfigspec('cmd_nvedit_efi') + at pytest.mark.slow +class TestEfiCapsuleFirmwareSimple(object): + def test_efi_capsule_fw1(self, u_boot_config, u_boot_console, efi_capsule_data): + """ + Test Case 1 - Update U-Boot environment on SPI Flash + """ + disk_img = efi_capsule_data + with u_boot_console.log.section('Test Case 1-a, before reboot'): + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'efidebug boot add 1 TEST host 0:1 /helloworld.efi ""', + 'efidebug boot order 1', + 'env set dfu_alt_info sf raw 0 0x200000', + 'env set FW_STATUS This is Old environment', + 'env print FW_STATUS', + 'env save']) + assert('Old environment' in ''.join(output)) + + output = u_boot_console.run_command_list([ + 'env set FW_STATUS This is New environment', + 'env export -c 5000000', + 'fatwrite host 0:1 5000000 %s/%s $filesize' + % (CAPSULE_DATA_DIR, FW_BIN), + 'setenv -e -guid 39b68c46-f7fb-441b-b6ec-16b0f69821f3 Capsule0000', + 'fatls host 0:1 %s' % CAPSULE_DATA_DIR + ]) + assert(('%s' % FW_BIN) in ''.join(output)) + + + try: + part_size = EFI_BOOTDEV_PART_SIZE + fs_type = EFI_BOOTDEV_FS_TYPE + mnt_point = '/mnt' + + loop_dev = check_output('sudo losetup -o 1MiB --sizelimit %dMiB --show -f %s | tr -d "\n"' + % (part_size, disk_img), shell=True).decode() + check_call('sudo mount -t %s -o umask=000 %s %s' + % (fs_type, loop_dev, mnt_point), shell=True) + check_call('%s/tools/mkimage -f %s%s/uboot_env.its %s%s/uboot_env.itb' + % (u_boot_config.build_dir, + mnt_point, CAPSULE_DATA_DIR, + mnt_point, CAPSULE_DATA_DIR), shell=True) + check_call('%s/tools/mkeficapsule -f %s%s/uboot_env.itb %s%s/Test01' + % (u_boot_config.build_dir, + mnt_point, CAPSULE_DATA_DIR, + mnt_point, CAPSULE_INSTALL_DIR), shell=True) + check_call('sudo umount %s' % loop_dev, shell=True) + check_call('sudo losetup -d %s' % loop_dev, shell=True) + except CalledProcessError: + assert('failed to create firmware capsule') + + u_boot_console.restart_uboot() + + with u_boot_console.log.section('Test Case 1-b, after reboot'): + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'env print FW_STATUS']) + assert('Old environment' in ''.join(output)) + + output = u_boot_console.run_command('fatls host 0:1 %s' + % CAPSULE_INSTALL_DIR) + assert('Test01' in output) + + output = u_boot_console.run_command('env print -e -all Capsule0000') + # assert('Capsule0000 not found' in output) + + # output = u_boot_console.run_command('printenv -e -all Capsule0000') + # assert('00000000: 01' in output) + + u_boot_console.restart_uboot() + + with u_boot_console.log.section('Test Case 1-c, after reboot'): + output = u_boot_console.run_command('env print FW_STATUS') + assert('New environment' in ''.join(output)) + + output = u_boot_console.run_command( + 'fatls host 0:1 %s/Test01' % CAPSULE_INSTALL_DIR) + assert('' == output) diff --git a/test/py/tests/test_efi_capsule/test_capsule_variable.py b/test/py/tests/test_efi_capsule/test_capsule_variable.py new file mode 100644 index 000000000000..baf4329e9a27 --- /dev/null +++ b/test/py/tests/test_efi_capsule/test_capsule_variable.py @@ -0,0 +1,141 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2020, Linaro Limited +# Author: AKASHI Takahiro +# +# U-Boot UEFI: Capsule Update for Variables Test + +""" +This test verifies capsule-on-disk variable update +""" + +import pytest +import re +from defs import * + + at pytest.mark.boardspec('sandbox') + at pytest.mark.buildconfigspec('efi_capsule_update_variable') + at pytest.mark.buildconfigspec('efi_capsule_on_disk') + at pytest.mark.buildconfigspec('cmd_fat') + at pytest.mark.buildconfigspec('cmd_nvedit_efi') + at pytest.mark.slow +class TestEfiCapsuleVariable(object): + def test_efi_capsule_var1(self, u_boot_console, efi_capsule_data): + """ + Test Case 1 - Add a new UEFI variable + """ + u_boot_console.restart_uboot() + + disk_img = efi_capsule_data + with u_boot_console.log.section('Test Case 1, before reboot'): + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'efidebug boot add 1 TEST host 0:1 /helloworld.efi ""', + 'efidebug boot order 1', + 'setenv -e -guid 39b68c46-f7fb-441b-b6ec-16b0f69821f3 Capsule0000', + 'setenv -e -guid 39b68c46-f7fb-441b-b6ec-16b0f69821f3 Capsule0001', + 'setenv -e -guid 39b68c46-f7fb-441b-b6ec-16b0f69821f3 Capsule0002', + 'setenv -e Boot0003', + 'saveenv', + 'fatload host 0:1 0x5000000 %s/tESt01' % CAPSULE_DATA_DIR, + 'fatwrite host 0:1 0x5000000 %s/tESt01 $filesize' % CAPSULE_INSTALL_DIR, + 'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR]) + assert('tESt01' in ''.join(output)) + + u_boot_console.restart_uboot() + + with u_boot_console.log.section('Test Case 1, after reboot'): + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'printenv -e Boot0003']) + assert('Boot0003:' in ''.join(output)) + + output = u_boot_console.run_command( + 'printenv -e -all') + + output = u_boot_console.run_command( + 'efidebug capsule result') + assert('CapsuleLast is' in output) + assert('Capsule status: 0x0' in output) + + output = u_boot_console.run_command( + 'fatls host 0:1 %s/tESt01' % CAPSULE_INSTALL_DIR) + u_boot_console.run_command('fatrm host 0:1 %s/tESt01' + % CAPSULE_INSTALL_DIR) + assert('' == output) + + def test_efi_capsule_var2(self, u_boot_console, efi_capsule_data): + """ + Test Case 2 - Modify/Delete existing variables + """ + u_boot_console.restart_uboot() + + disk_img = efi_capsule_data + with u_boot_console.log.section('Test Case 2, before reboot'): + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'efidebug boot add 1 TEST1 host 0:1 /helloworld.efi ""', + 'efidebug boot add 2 TEST2 host 0:1 /helloworld.efi ""', + 'efidebug boot order 1 2', + 'saveenv', + 'fatload host 0:1 0x5000000 %s/Test02' % CAPSULE_DATA_DIR, + 'fatwrite host 0:1 0x5000000 %s/Test02 $filesize' % CAPSULE_INSTALL_DIR, + 'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR]) + assert('Test02' in ''.join(output)) + + u_boot_console.restart_uboot() + + with u_boot_console.log.section('Test Case 2, after reboot'): + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'printenv -e BootOrder']) + assert('00000000: 03 00 02 00' in ''.join(output)) + + output = u_boot_console.run_command( + 'printenv -e Boot0001') + assert('"Boot0001" not defined' in output) + + output = u_boot_console.run_command( + 'efidebug capsule result') + assert('CapsuleLast is' in output) + assert('Capsule status: 0x0' in output) + + output = u_boot_console.run_command( + 'fatls host 0:1 %s/Test02' % CAPSULE_INSTALL_DIR) + u_boot_console.run_command('fatrm host 0:1 %s/Test02' % CAPSULE_INSTALL_DIR) + assert('' == ''.join(output)) + + def test_efi_capsule_var3(self, u_boot_console, efi_capsule_data): + """ + Test Case 3 - Modify a non-existing variable + """ + u_boot_console.restart_uboot() + + disk_img = efi_capsule_data + with u_boot_console.log.section('Test Case 3, before reboot'): + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'efidebug boot add 1 TEST host 0:1 /helloworld.efi ""', + 'efidebug boot order 1', + 'saveenv', + 'fatload host 0:1 0x5000000 %s/TeST03' % CAPSULE_DATA_DIR, + 'fatwrite host 0:1 0x5000000 %s/TeST03 $filesize' % CAPSULE_INSTALL_DIR, + 'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR]) + assert('TeST03' in ''.join(output)) + + u_boot_console.restart_uboot() + + with u_boot_console.log.section('Test Case 3, after reboot'): + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'printenv -e FooVar']) + assert('"FooVar" not defined' in ''.join(output)) + + output = u_boot_console.run_command( + 'efidebug capsule result') + assert('CapsuleLast is' in output) + assert('Capsule status: 0x0' in output) + + output = u_boot_console.run_command( + 'fatls host 0:1 %s/TeST03' % CAPSULE_INSTALL_DIR) + u_boot_console.run_command('fatrm host 0:1 %s/TeST03' % CAPSULE_INSTALL_DIR) + assert('' == ''.join(output)) diff --git a/test/py/tests/test_efi_capsule/uboot_env.its b/test/py/tests/test_efi_capsule/uboot_env.its new file mode 100644 index 000000000000..a4484db45834 --- /dev/null +++ b/test/py/tests/test_efi_capsule/uboot_env.its @@ -0,0 +1,25 @@ +/* + * Automatic software update for U-Boot + * Make sure the flashing addresses ('load' prop) is correct for your board! + */ + +/dts-v1/; + +/ { + description = "Automatic U-Boot environment update"; + #address-cells = <2>; + + images { + sf at 0 { + description = "U-Boot environment on SPI Flash"; + data = /incbin/("BINFILE"); + compression = "none"; + type = "firmware"; + arch = "sandbox"; + load = <0>; + hash-1 { + algo = "sha1"; + }; + }; + }; +};