[PATCH v4 06/16] efi_loader: define UpdateCapsule api

Heinrich Schuchardt xypron.glpk at gmx.de
Thu Jul 23 17:54:27 CEST 2020


On 22.07.20 08:05, 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         |  13 +++
>  lib/efi_loader/Kconfig       |  11 +++
>  lib/efi_loader/Makefile      |   1 +
>  lib/efi_loader/efi_capsule.c | 168 +++++++++++++++++++++++++++++++++++
>  lib/efi_loader/efi_runtime.c | 104 +++++++++++++---------
>  lib/efi_loader/efi_setup.c   |  33 +++++--
>  7 files changed, 290 insertions(+), 52 deletions(-)
>  create mode 100644 lib/efi_loader/efi_capsule.c
>
> diff --git a/include/efi_api.h b/include/efi_api.h
> index 5744f6aed86d..c128a0a66ce8 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 df8dc377257c..a754fb0ed460 100644
> --- a/include/efi_loader.h
> +++ b/include/efi_loader.h
> @@ -208,6 +208,8 @@ extern const efi_guid_t efi_guid_cert_type_pkcs7;
>
>  /* GUID of RNG protocol */
>  extern const efi_guid_t efi_guid_rng_protocol;
> +/* GUID of capsule update result */
> +extern const efi_guid_t efi_guid_capsule_report;
>
>  extern unsigned int __efi_runtime_start, __efi_runtime_stop;
>  extern unsigned int __efi_runtime_rel_start, __efi_runtime_rel_stop;
> @@ -795,6 +797,17 @@ bool efi_image_parse(void *efi, size_t len, struct efi_image_regions **regp,
>  /* runtime implementation of memcpy() */
>  void efi_memcpy_runtime(void *dest, const void *src, size_t n);
>
> +/* 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 cbd8fe8c0ad2..ee9ebe348ad9 100644
> --- a/lib/efi_loader/Kconfig
> +++ b/lib/efi_loader/Kconfig
> @@ -93,6 +93,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
> +

This symbol is not needed. You already have EFI_RUNTIME_UPDATE_CAPSULE.

> +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 441ac9432e99..54de0fe51b94 100644
> --- a/lib/efi_loader/Makefile
> +++ b/lib/efi_loader/Makefile
> @@ -23,6 +23,7 @@ endif
>  obj-$(CONFIG_CMD_BOOTEFI_HELLO) += helloworld_efi.o
>  obj-y += efi_bootmgr.o
>  obj-y += efi_boottime.o
> +obj-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += efi_capsule.o
>  obj-y += efi_console.o
>  obj-y += efi_device_path.o
>  obj-$(CONFIG_EFI_DEVICE_PATH_TO_TEXT) += efi_device_path_to_text.o
> diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c
> new file mode 100644
> index 000000000000..cfe422bee924
> --- /dev/null
> +++ b/lib/efi_loader/efi_capsule.c
> @@ -0,0 +1,168 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + *  EFI Capsule
> + *
> + *  Copyright (c) 2018 Linaro Limited
> + *			Author: AKASHI Takahiro
> + */
> +
> +#include <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;
> +
> +/**
> + * get_last_capsule - get the last capsule number
> + *
> + * Retrieve the number of capsule invoked last time from "CapsuleLast"

Retrieve the index of the last capsule from "CapsuleLast"

> + * variable.
> + *
> + * Return:
> + * * > 0	- the last capsule number invoked

The UEFI spec says: "starting at Capsule0000".

> + * * 0xffff	- on error, or no capsule invoked yet

CapsuleFFFF may exist and is not an error.

If there is no capsule, return -1. So the next number will be 0000.

> + */
> +static __maybe_unused int get_last_capsule(void)
> +{

This function is called by efi_launch_capsules(). Why should it be
__maybe_unused?

Just move the function into the same patch as the function consuming it.

> +	u16 value16[11]; /* "CapsuleXXXX": non-null-terminated */
> +	char value[11], *p;
> +	efi_uintn_t size;
> +	unsigned long num = 0xffff;

int num = -1;

> +	efi_status_t ret;
> +
> +	size = sizeof(value16);
> +	ret = EFI_CALL(efi_get_variable(L"CapsuleLast",

Please, avoid EFI_CALL(). Use efi_get_variable_int().

> +					&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;

Please, avoid superflous conversions.

> +}
> +
> +/**
> + * set_capsule_result - set a result variable
> + * @capsule:		Capsule
> + * @return_status:	Return status
> + *
> + * Create and set a result variable, "CapsuleXXXX", for the capsule,
> + * @capsule.
> + */
> +static __maybe_unused
> +void set_capsule_result(int num, struct efi_capsule_header *capsule,
> +			efi_status_t return_status)
> +{
> +	char variable_name[12];
> +	u16 variable_name16[12], *p;
> +	struct efi_capsule_result_variable_header result;
> +	struct efi_time time;
> +	efi_status_t ret;
> +
> +	sprintf(variable_name, "Capsule%04X", num);
> +	p = variable_name16;
> +	utf8_utf16_strncpy(&p, variable_name, 11);
> +	result.variable_total_size = sizeof(result);
> +	result.capsule_guid = capsule->capsule_guid;
> +	ret = EFI_CALL((*efi_runtime_services.get_time)(&time, NULL));
> +	if (ret == EFI_SUCCESS)
> +		memcpy(&result.capsule_processed, &time, sizeof(time));
> +	else
> +		memset(&result.capsule_processed, 0, sizeof(time));
> +	result.capsule_status = return_status;
> +	ret = EFI_CALL(efi_set_variable(variable_name16,

Please, avoid EFI_CALL().

> +					&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);

Please, use log_err().

> +}
> +
> +/**
> + * efi_update_capsule() - process information from operating system
> + * @capsule_header_array:	Array of virtual address pointers
> + * @capsule_count:		Number of pointers in capsule_header_array
> + * @scatter_gather_list:	Array of physical address pointers
> + *
> + * This function implements the UpdateCapsule() runtime service.
> + *
> + * See the Unified Extensible Firmware Interface (UEFI) specification for
> + * details.
> + *
> + * Return:			status code
> + */
> +efi_status_t EFIAPI efi_update_capsule(
> +		struct efi_capsule_header **capsule_header_array,
> +		efi_uintn_t capsule_count,
> +		u64 scatter_gather_list)

All parameters - especially efi_capsule_header - should be marked as const.

> +{
> +	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;

In the current state (after merging patches 1-6) the function does not
update. So the return value must be EFI_UNSUPPORTED.

Can't you move the contents of this function to patch 7/16? Then there
is one patch with the whole code of the function to review.

> +	for (i = 0, capsule = *capsule_header_array; i < capsule_count;
> +	     i++, capsule = *(++capsule_header_array)) {
> +	}
> +out:
> +	return EFI_EXIT(ret);
> +}
> +
> +/**
> + * efi_query_capsule_caps() - check if capsule is supported
> + * @capsule_header_array:	Array of virtual pointers
> + * @capsule_count:		Number of pointers in capsule_header_array
> + * @maximum_capsule_size:	Maximum capsule size
> + * @reset_type:			Type of reset needed for capsule update
> + *
> + * This function implements the QueryCapsuleCapabilities() runtime service.
> + *
> + * See the Unified Extensible Firmware Interface (UEFI) specification for
> + * details.
> + *
> + * Return:			status code
> + */
> +efi_status_t EFIAPI efi_query_capsule_caps(
> +		struct efi_capsule_header **capsule_header_array,
> +		efi_uintn_t capsule_count,
> +		u64 *maximum_capsule_size,
> +		u32 *reset_type)
> +{
> +	struct efi_capsule_header *capsule __attribute__((unused));
> +	unsigned int i;
> +	efi_status_t ret;
> +
> +	EFI_ENTRY("%p, %lu, %p, %p\n", capsule_header_array, capsule_count,
> +		  maximum_capsule_size, reset_type);
> +
> +	if (!maximum_capsule_size) {
> +		ret = EFI_INVALID_PARAMETER;
> +		goto out;
> +	}
> +
> +	*maximum_capsule_size = U64_MAX;
> +	*reset_type = EFI_RESET_COLD;
> +
> +	ret = EFI_SUCCESS;

EFI_UNSUPPORTED!

Best regards

Heinrich

> +	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 91a45514488e..6227bda3a268 100644
> --- a/lib/efi_loader/efi_runtime.c
> +++ b/lib/efi_loader/efi_runtime.c
> @@ -133,6 +133,10 @@ efi_status_t efi_init_runtime_supported(void)
>  #ifdef CONFIG_EFI_HAVE_RUNTIME_RESET
>  	rt_table->runtime_services_supported |= EFI_RT_SUPPORTED_RESET_SYSTEM;
>  #endif
> +	if (IS_ENABLED(CONFIG_EFI_RUNTIME_UPDATE_CAPSULE))
> +		rt_table->runtime_services_supported |=
> +			(EFI_RT_SUPPORTED_UPDATE_CAPSULE |
> +			 EFI_RT_SUPPORTED_QUERY_CAPSULE_CAPABILITIES);
>
>  	ret = efi_install_configuration_table(&efi_rt_properties_table_guid,
>  					      rt_table);
> @@ -432,6 +436,50 @@ efi_status_t __weak __efi_runtime EFIAPI efi_set_time(struct efi_time *time)
>  	return EFI_UNSUPPORTED;
>  }
>
> +/**
> + * efi_update_capsule_unsupported() - process information from operating system
> + *
> + * This function implements the UpdateCapsule() runtime service.
> + *
> + * See the Unified Extensible Firmware Interface (UEFI) specification for
> + * details.
> + *
> + * @capsule_header_array:	pointer to array of virtual pointers
> + * @capsule_count:		number of pointers in capsule_header_array
> + * @scatter_gather_list:	pointer to array of physical pointers
> + * Returns:			status code
> + */
> +efi_status_t __efi_runtime EFIAPI efi_update_capsule_unsupported(
> +			struct efi_capsule_header **capsule_header_array,
> +			efi_uintn_t capsule_count,
> +			u64 scatter_gather_list)
> +{
> +	return EFI_UNSUPPORTED;
> +}
> +
> +/**
> + * efi_query_capsule_caps_unsupported() - check if capsule is supported
> + *
> + * This function implements the QueryCapsuleCapabilities() runtime service.
> + *
> + * See the Unified Extensible Firmware Interface (UEFI) specification for
> + * details.
> + *
> + * @capsule_header_array:	pointer to array of virtual pointers
> + * @capsule_count:		number of pointers in capsule_header_array
> + * @maximum_capsule_size:	maximum capsule size
> + * @reset_type:			type of reset needed for capsule update
> + * Returns:			status code
> + */
> +efi_status_t __efi_runtime EFIAPI efi_query_capsule_caps_unsupported(
> +			struct efi_capsule_header **capsule_header_array,
> +			efi_uintn_t capsule_count,
> +			u64 *maximum_capsule_size,
> +			u32 *reset_type)
> +{
> +	return EFI_UNSUPPORTED;
> +}
> +
>  /**
>   * efi_is_runtime_service_pointer() - check if pointer points to runtime table
>   *
> @@ -455,6 +503,13 @@ void efi_runtime_detach(void)
>  	efi_runtime_services.reset_system = efi_reset_system;
>  	efi_runtime_services.get_time = efi_get_time;
>  	efi_runtime_services.set_time = efi_set_time;
> +	if (IS_ENABLED(CONFIG_EFI_RUNTIME_UPDATE_CAPSULE)) {
> +		/* won't support at runtime */
> +		efi_runtime_services.update_capsule =
> +				efi_update_capsule_unsupported;
> +		efi_runtime_services.query_capsule_caps =
> +				efi_query_capsule_caps_unsupported;
> +	}
>
>  	/* Update CRC32 */
>  	efi_update_table_header_crc32(&efi_runtime_services.hdr);
> @@ -863,50 +918,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,
> @@ -924,7 +935,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 6196c0a06cd7..2fc0c5d091b8 100644
> --- a/lib/efi_loader/efi_setup.c
> +++ b/lib/efi_loader/efi_setup.c
> @@ -117,6 +117,30 @@ static efi_status_t efi_init_secure_boot(void)
>  }
>  #endif /* CONFIG_EFI_SECURE_BOOT */
>
> +/**
> + * efi_init_os_indications() - indicate supported features for OS requests
> + *
> + * Set the OsIndicationsSupported variable.
> + *
> + * Return:	status code
> + */
> +static efi_status_t efi_init_os_indications(void)
> +{
> +	u64 os_indications_supported = 0;
> +
> +	if (IS_ENABLED(CONFIG_EFI_HAVE_CAPSULE_SUPPORT))
> +		os_indications_supported |=
> +			EFI_OS_INDICATIONS_CAPSULE_RESULT_VAR_SUPPORTED;
> +
> +	return efi_set_variable_int(L"OsIndicationsSupported",
> +				    &efi_global_variable_guid,
> +				    EFI_VARIABLE_BOOTSERVICE_ACCESS |
> +				    EFI_VARIABLE_RUNTIME_ACCESS |
> +				    EFI_VARIABLE_READ_ONLY,
> +				    sizeof(os_indications_supported),
> +				    &os_indications_supported, false);
> +}
> +
>  /**
>   * efi_init_obj_list() - Initialize and populate EFI object list
>   *
> @@ -124,7 +148,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 */
> @@ -162,13 +185,7 @@ efi_status_t efi_init_obj_list(void)
>  		goto out;
>
>  	/* Indicate supported features */
> -	ret = efi_set_variable_int(L"OsIndicationsSupported",
> -				   &efi_global_variable_guid,
> -				   EFI_VARIABLE_BOOTSERVICE_ACCESS |
> -				   EFI_VARIABLE_RUNTIME_ACCESS |
> -				   EFI_VARIABLE_READ_ONLY,
> -				   sizeof(os_indications_supported),
> -				   &os_indications_supported, false);
> +	ret = efi_init_os_indications();
>  	if (ret != EFI_SUCCESS)
>  		goto out;
>
>



More information about the U-Boot mailing list