[PATCH 03/10] efi_loader: define UpdateCapsule api
Heinrich Schuchardt
xypron.glpk at gmx.de
Sun May 17 10:02:08 CEST 2020
On 4/27/20 11:48 AM, AKASHI Takahiro wrote:
> In this commit, skeleton functions for capsule-related API's are
> added under CONFIG_EFI_UPDATE_CAPSULE configuration.
> Detailed implementation for a specific capsule type will be added
> in the succeeding patches.
>
> Signed-off-by: AKASHI Takahiro <takahiro.akashi at linaro.org>
> ---
> include/efi_api.h | 12 +++
> include/efi_loader.h | 15 ++++
> lib/efi_loader/Kconfig | 11 +++
> lib/efi_loader/Makefile | 1 +
> lib/efi_loader/efi_capsule.c | 153 +++++++++++++++++++++++++++++++++++
> lib/efi_loader/efi_runtime.c | 104 ++++++++++++++----------
> lib/efi_loader/efi_setup.c | 31 +++++--
> 7 files changed, 276 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 77d6bf2660b9..6fa3f4a887d2 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 d4510462d616..19ffc027c171 100644
> --- a/include/efi_loader.h
> +++ b/include/efi_loader.h
> @@ -196,6 +196,8 @@ extern const efi_guid_t efi_guid_cert_type_pkcs7;
>
> /* GUID of RNG protocol */
> extern const efi_guid_t efi_guid_rng_protocol;
> +/* GUID of capsule update result */
> +extern const efi_guid_t efi_guid_capsule_report;
>
> extern unsigned int __efi_runtime_start, __efi_runtime_stop;
> extern unsigned int __efi_runtime_rel_start, __efi_runtime_rel_stop;
> @@ -778,6 +780,19 @@ bool efi_image_parse(void *efi, size_t len, struct efi_image_regions **regp,
> WIN_CERTIFICATE **auth, size_t *auth_len);
> #endif /* CONFIG_EFI_SECURE_BOOT */
This line does not exist in upstream. Instead we have:
bool efi_image_parse(void *efi, size_t len, struct efi_image_regions **regp,
WIN_CERTIFICATE **auth, size_t *auth_len);
#else /* CONFIG_IS_ENABLED(EFI_LOADER) */
Am I missing patches? Or do you have to rebase your series?
>
> +#ifdef CONFIG_EFI_HAVE_CAPSULE_SUPPORT
> +/* 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(
> + efi_capsule_header **capsule_header_array,
> + efi_uintn_t capsule_count,
> + u64 *maximum_capsule_size,
> + u32 *reset_type);
> +#endif /* CONFIG_EFI_HAVE_CAPSULE_SUPPORT */
> +
> #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 7cc2d940f848..e2b08251f26a 100644
> --- a/lib/efi_loader/Kconfig
> +++ b/lib/efi_loader/Kconfig
> @@ -45,6 +45,17 @@ config EFI_SET_TIME
> Provide the SetTime() runtime service at boottime. This service
> can be used by an EFI application to adjust the real time clock.
>
> +config EFI_HAVE_CAPSULE_SUPPORT
> + bool
> +
> +config EFI_RUNTIME_UPDATE_CAPSULE
> + bool "UpdateCapsule() runtime service"
> + default n
> + select EFI_HAVE_CAPSULE_SUPPORT
> + help
> + Select this option if you want to use UpdateCapsule and
> + QueryCapsuleCapabilities API's.
> +
> config EFI_DEVICE_PATH_TO_TEXT
> bool "Device path to text protocol"
> default y
> diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile
> index eff3c25ec301..2ee1e683d9c5 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_HAVE_CAPSULE_SUPPORT) += efi_capsule.o
> obj-y += efi_console.o
> obj-y += efi_device_path.o
> obj-$(CONFIG_EFI_DEVICE_PATH_TO_TEXT) += efi_device_path_to_text.o
> diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c
> new file mode 100644
> index 000000000000..fb104bb92a6c
> --- /dev/null
> +++ b/lib/efi_loader/efi_capsule.c
> @@ -0,0 +1,153 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * EFI Capsule
> + *
> + * Copyright (c) 2018 Linaro Limited
> + * Author: AKASHI Takahiro
> + */
> +
> +#include <common.h>
> +#include <efi_loader.h>
> +#include <fs.h>
> +#include <malloc.h>
> +#include <sort.h>
> +
> +const efi_guid_t efi_guid_capsule_report = EFI_CAPSULE_REPORT_GUID;
> +
> +static __maybe_unused int get_last_capsule(void)
> +{
> + u16 value16[11]; /* "CapsuleXXXX": non-null-terminated */
> + char value[11], *p;
> + efi_uintn_t size;
> + unsigned long num = 0xffff;
> + efi_status_t ret;
> +
> + size = sizeof(value16);
> + ret = EFI_CALL(efi_get_variable(L"CapsuleLast",
> + &efi_guid_capsule_report,
> + NULL, &size, value16));
> + if (ret != EFI_SUCCESS || u16_strncmp(value16, L"Capsule", 7))
> + goto err;
> +
> + p = value;
> + utf16_utf8_strcpy(&p, value16);
> + strict_strtoul(&value[7], 16, &num);
> +err:
> + return (int)num;
> +}
> +
Missing function description
> +static __maybe_unused
> +void set_capsule_result(int num, struct efi_capsule_header *capsule,
> + efi_status_t return_status)
> +{
> + char variable_name[12];
> + u16 variable_name16[12], *p;
> + struct efi_capsule_result_variable_header result;
> + struct efi_time time;
> + efi_status_t ret;
> +
> + sprintf(variable_name, "Capsule%04X", num);
> + p = variable_name16;
> + utf8_utf16_strncpy(&p, variable_name, 11);
> + result.variable_total_size = sizeof(result);
> + result.capsule_guid = capsule->capsule_guid;
> + ret = EFI_CALL((*efi_runtime_services.get_time)(&time, NULL));
> + if (ret == EFI_SUCCESS)
> + memcpy(&result.capsule_processed, &time, sizeof(time));
> + else
> + memset(&result.capsule_processed, 0, sizeof(time));
> + result.capsule_status = return_status;
> + ret = EFI_CALL(efi_set_variable(variable_name16,
> + &efi_guid_capsule_report,
> + EFI_VARIABLE_NON_VOLATILE |
> + EFI_VARIABLE_BOOTSERVICE_ACCESS |
> + EFI_VARIABLE_RUNTIME_ACCESS,
> + sizeof(result), &result));
> + if (ret)
> + printf("EFI: creating %s failed\n", variable_name);
> +}
> +
> +/*
> + * Launch a capsule
Please, stick to the style described in
https://www.kernel.org/doc/html/latest/doc-guide/kernel-doc.html#function-documentation
> + */
> +/**
> + * 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 6a25acbbcdf5..669e25a9589a 100644
> --- a/lib/efi_loader/efi_runtime.c
> +++ b/lib/efi_loader/efi_runtime.c
> @@ -130,6 +130,11 @@ efi_status_t efi_init_runtime_supported(void)
> #ifdef CONFIG_EFI_HAVE_RUNTIME_RESET
> rt_table->runtime_services_supported |= EFI_RT_SUPPORTED_RESET_SYSTEM;
> #endif
> +#ifdef CONFIG_EFI_RUNTIME_UPDATE_CAPSULE
> + rt_table->runtime_services_supported |=
> + (EFI_RT_SUPPORTED_UPDATE_CAPSULE |
> + EFI_RT_SUPPORTED_QUERY_CAPSULE_CAPABILITIES);
> +#endif
>
> ret = efi_install_configuration_table(&efi_rt_properties_table_guid,
> rt_table);
> @@ -410,6 +415,50 @@ efi_status_t __weak __efi_runtime EFIAPI efi_set_time(struct efi_time *time)
> return EFI_UNSUPPORTED;
> }
>
> +/**
> + * efi_update_capsule_unsupported() - process information from operating system
> + *
> + * This function implements the UpdateCapsule() runtime service.
> + *
> + * See the Unified Extensible Firmware Interface (UEFI) specification for
> + * details.
The description should differ from the description of efi_update_capsule().
> + *
> + * @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.
The description should differ from the description of
efi_query_capsule_caps().
> + *
> + * @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
> *
> @@ -433,6 +482,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_RUNTIME_UPDATE_CAPSULE
> + /* won't support at runtime */
> + efi_runtime_services.update_capsule = efi_update_capsule_unsupported;
> + efi_runtime_services.query_capsule_caps =
> + efi_query_capsule_caps_unsupported;
> +#endif
>
> /* Update CRC32 */
> efi_update_table_header_crc32(&efi_runtime_services.hdr);
> @@ -837,50 +892,6 @@ static efi_status_t __efi_runtime EFIAPI efi_unimplemented(void)
> return EFI_UNSUPPORTED;
> }
>
> -/**
> - * efi_update_capsule() - process information from operating system
> - *
> - * This function implements the UpdateCapsule() runtime service.
> - *
> - * See the Unified Extensible Firmware Interface (UEFI) specification for
> - * details.
> - *
> - * @capsule_header_array: pointer to array of virtual pointers
> - * @capsule_count: number of pointers in capsule_header_array
> - * @scatter_gather_list: pointer to arry of physical pointers
> - * Returns: status code
> - */
> -efi_status_t __efi_runtime EFIAPI efi_update_capsule(
> - struct efi_capsule_header **capsule_header_array,
> - efi_uintn_t capsule_count,
> - u64 scatter_gather_list)
> -{
> - return EFI_UNSUPPORTED;
> -}
> -
> -/**
> - * efi_query_capsule_caps() - check if capsule is supported
> - *
> - * This function implements the QueryCapsuleCapabilities() runtime service.
> - *
> - * See the Unified Extensible Firmware Interface (UEFI) specification for
> - * details.
> - *
> - * @capsule_header_array: pointer to array of virtual pointers
> - * @capsule_count: number of pointers in capsule_header_array
> - * @maximum_capsule_size: maximum capsule size
> - * @reset_type: type of reset needed for capsule update
> - * Returns: status code
> - */
> -efi_status_t __efi_runtime EFIAPI efi_query_capsule_caps(
> - struct efi_capsule_header **capsule_header_array,
> - efi_uintn_t capsule_count,
> - u64 *maximum_capsule_size,
> - u32 *reset_type)
> -{
> - return EFI_UNSUPPORTED;
> -}
> -
> struct efi_runtime_services __efi_runtime_data efi_runtime_services = {
> .hdr = {
> .signature = EFI_RUNTIME_SERVICES_SIGNATURE,
> @@ -898,7 +909,12 @@ struct efi_runtime_services __efi_runtime_data efi_runtime_services = {
> .set_variable = efi_set_variable,
> .get_next_high_mono_count = (void *)&efi_unimplemented,
> .reset_system = &efi_reset_system_boottime,
> +#ifdef CONFIG_EFI_RUNTIME_UPDATE_CAPSULE
> .update_capsule = efi_update_capsule,
> .query_capsule_caps = efi_query_capsule_caps,
> +#else
> + .update_capsule = efi_update_capsule_unsupported,
> + .query_capsule_caps = efi_query_capsule_caps_unsupported,
> +#endif
> .query_variable_info = efi_query_variable_info,
> };
> diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c
> index 1b648c84673a..8fe378bbfdfc 100644
> --- a/lib/efi_loader/efi_setup.c
> +++ b/lib/efi_loader/efi_setup.c
> @@ -115,6 +115,29 @@ static efi_status_t efi_init_secure_boot(void)
> }
> #endif /* CONFIG_EFI_SECURE_BOOT */
>
> +/**
> + * efi_init_os_indications() - indicate supported features for OS requests
> + *
> + * Set the OsIndicationsSupported variable.
> + *
> + * Return: status code
> + */
> +static efi_status_t efi_init_os_indications(void)
> +{
> + u64 os_indications_supported = 0;
> +
> +#ifdef CONFIG_EFI_HAVE_CAPSULE_SUPPORT
> + os_indications_supported |=
> + EFI_OS_INDICATIONS_CAPSULE_RESULT_VAR_SUPPORTED;
> +#endif
Though the UEFI spec does not mention it I guess we will have to treat
OsIndicationsSupported as read-only once we get that properly supported.
Best regards
Heinrich
> + return EFI_CALL(efi_set_variable(L"OsIndicationsSupported",
> + &efi_global_variable_guid,
> + EFI_VARIABLE_BOOTSERVICE_ACCESS |
> + EFI_VARIABLE_RUNTIME_ACCESS,
> + sizeof(os_indications_supported),
> + &os_indications_supported));
> +}
> +
> /**
> * efi_init_obj_list() - Initialize and populate EFI object list
> *
> @@ -122,7 +145,6 @@ static efi_status_t efi_init_secure_boot(void)
> */
> efi_status_t efi_init_obj_list(void)
> {
> - u64 os_indications_supported = 0; /* None */
> efi_status_t ret = EFI_SUCCESS;
>
> /* Initialize once only */
> @@ -146,12 +168,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;
>
>
More information about the U-Boot
mailing list