diff mbox series

[03/10] efi_loader: define UpdateCapsule api

Message ID 20200427094829.1140-4-takahiro.akashi@linaro.org
State New
Headers show
Series efi_loader: add capsule update support | expand

Commit Message

AKASHI Takahiro April 27, 2020, 9:48 a.m. UTC
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

Comments

Heinrich Schuchardt May 17, 2020, 8:02 a.m. UTC | #1
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;
>
>
AKASHI Takahiro May 18, 2020, 1:34 a.m. UTC | #2
Heinrich,

On Sun, May 17, 2020 at 10:02:08AM +0200, Heinrich Schuchardt wrote:
> 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?

The lines above come from my secure boot patch, while my capsule
patch is based on pre-v2020.07-rc1 in which my secure boot patch
has not been merged when I posted capsule patch.

> >
> > +#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

Okay, as I said somewhere else, I've forgot to add some function
descriptions in v1.

> > +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().

I don't get your point.
This kind of description, like "See the UEFI ...," are often seen
in efi_boottime.c and others.

> > + *
> > + * @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.

The only thing that we should do now is to export "efi_set_variable_common."
(I don't know if Ilias' StandaloneMM will accept READ_ONLY attribute.)

Please note that there is also an instance that should be read-only
in efi_init_secure_boot().

-Takahiro Akashi

> 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;
> >
> >
>
diff mbox series

Patch

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 */
 
+#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(
+		struct 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;
+}
+
+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
+ */
+/**
+ * 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.
+ *
+ * @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
  *
@@ -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
+	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;