
Heinrich,
On Thu, Jul 23, 2020 at 05:54:27PM +0200, Heinrich Schuchardt wrote:
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@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.
If you carefully read my code, you will notice that EFI_CAPSULE_ON_DISK can be enabled without EFI_RUNTIME_UDPATE_CAPSULE which allows for exporting UpdateCapsule API.
Due to the nature of this API, I'm reluctant to enable this interface, which currently has a limited functionality under my implementation, unconditionally as runtime API. That is why I introduced a separate EFI_HAVE_CAPSULE_SUPPORT.
+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"
Okay. 'Index' would be less confusing.
- variable.
- Return:
0 - the last capsule number invokedThe UEFI spec says: "starting at Capsule0000".
I don't get your point.
The index used to store a capsule update result will be incremented at every time an update is invoked either by API or a capsule-on-disk capsule file.
- 0xffff - on error, or no capsule invoked yet
I admit that the description can be misleading, but
CapsuleFFFF may exist and is not an error.
If there is no capsule, return -1. So the next number will be 0000.
The result is the same. The new index starts at '0000'. We don't have to distinguish the two cases.
- */
+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;
See above.
- efi_status_t ret;
- size = sizeof(value16);
- ret = EFI_CALL(efi_get_variable(L"CapsuleLast",
Please, avoid EFI_CALL(). Use efi_get_variable_int().
Okay.
&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.
Okay if it doesn't generate a compiler warning.
+}
+/**
- 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().
I think that I have made the same comment several times before. printf() is used widely even in efi_loader.
+}
+/**
- 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.
No. This is the API defined in UEFI specification.
+{
- 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.
Okay.
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.
This patch is a remnant when I had another commit against supporting variable updates via capsule files.
Patch#6 provides the whole framework for capsules while patch#7 (and later) supports a specific capsule type. So I think that we should have separate patches.
- 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!
No. At least, two parameters are actually returned.
-Takahiro Akashi
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;