efi_loader: define UpdateCapsule api

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>
This commit is contained in:
AKASHI Takahiro 2020-11-17 09:27:55 +09:00 committed by Heinrich Schuchardt
parent 96ec4b1a18
commit 2bc27ca8a0
7 changed files with 316 additions and 54 deletions

View File

@ -217,6 +217,10 @@ enum efi_reset_type {
#define CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE 0x00020000 #define CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE 0x00020000
#define CAPSULE_FLAGS_INITIATE_RESET 0x00040000 #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 { struct efi_capsule_header {
efi_guid_t capsule_guid; efi_guid_t capsule_guid;
u32 header_size; u32 header_size;
@ -224,6 +228,14 @@ struct efi_capsule_header {
u32 capsule_image_size; u32 capsule_image_size;
} __packed; } __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_GET_TIME 0x0001
#define EFI_RT_SUPPORTED_SET_TIME 0x0002 #define EFI_RT_SUPPORTED_SET_TIME 0x0002
#define EFI_RT_SUPPORTED_GET_WAKEUP_TIME 0x0004 #define EFI_RT_SUPPORTED_GET_WAKEUP_TIME 0x0004

View File

@ -210,6 +210,8 @@ extern const efi_guid_t efi_guid_cert_type_pkcs7;
/* GUID of RNG protocol */ /* GUID of RNG protocol */
extern const efi_guid_t efi_guid_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_start, __efi_runtime_stop;
extern unsigned int __efi_runtime_rel_start, __efi_runtime_rel_stop; extern unsigned int __efi_runtime_rel_start, __efi_runtime_rel_stop;
@ -812,6 +814,17 @@ void efi_memcpy_runtime(void *dest, const void *src, size_t n);
/* commonly used helper function */ /* commonly used helper function */
u16 *efi_create_indexed_name(u16 *buffer, const char *name, unsigned int index); u16 *efi_create_indexed_name(u16 *buffer, const char *name, unsigned int index);
/* 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) */ #else /* CONFIG_IS_ENABLED(EFI_LOADER) */
/* Without CONFIG_EFI_LOADER we don't have a runtime section, stub it out */ /* Without CONFIG_EFI_LOADER we don't have a runtime section, stub it out */

View File

@ -94,6 +94,17 @@ config EFI_SET_TIME
Provide the SetTime() runtime service at boottime. This service Provide the SetTime() runtime service at boottime. This service
can be used by an EFI application to adjust the real time clock. 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 config EFI_DEVICE_PATH_TO_TEXT
bool "Device path to text protocol" bool "Device path to text protocol"
default y default y

View File

@ -23,6 +23,7 @@ endif
obj-$(CONFIG_CMD_BOOTEFI_HELLO) += helloworld_efi.o obj-$(CONFIG_CMD_BOOTEFI_HELLO) += helloworld_efi.o
obj-y += efi_bootmgr.o obj-y += efi_bootmgr.o
obj-y += efi_boottime.o obj-y += efi_boottime.o
obj-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += efi_capsule.o
obj-y += efi_console.o obj-y += efi_console.o
obj-y += efi_device_path.o obj-y += efi_device_path.o
obj-$(CONFIG_EFI_DEVICE_PATH_TO_TEXT) += efi_device_path_to_text.o obj-$(CONFIG_EFI_DEVICE_PATH_TO_TEXT) += efi_device_path_to_text.o

View File

@ -0,0 +1,165 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* EFI Capsule
*
* Copyright (c) 2018 Linaro Limited
* Author: AKASHI Takahiro
*/
#include <common.h>
#include <efi_loader.h>
#include <efi_variable.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 index
*
* Retrieve the index of the capsule invoked last time from "CapsuleLast"
* variable.
*
* Return:
* * > 0 - the last capsule index invoked
* * 0xffff - on error, or no capsule invoked yet
*/
static __maybe_unused unsigned int get_last_capsule(void)
{
u16 value16[11]; /* "CapsuleXXXX": non-null-terminated */
char value[11], *p;
efi_uintn_t size;
unsigned long index = 0xffff;
efi_status_t ret;
size = sizeof(value16);
ret = efi_get_variable_int(L"CapsuleLast", &efi_guid_capsule_report,
NULL, &size, value16, NULL);
if (ret != EFI_SUCCESS || u16_strncmp(value16, L"Capsule", 7))
goto err;
p = value;
utf16_utf8_strcpy(&p, value16);
strict_strtoul(&value[7], 16, &index);
err:
return index;
}
/**
* 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 index, struct efi_capsule_header *capsule,
efi_status_t return_status)
{
u16 variable_name16[12];
struct efi_capsule_result_variable_header result;
struct efi_time time;
efi_status_t ret;
efi_create_indexed_name(variable_name16, "Capsule", index);
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_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 %ls failed\n", variable_name16);
}
/**
* 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)
{
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_UNSUPPORTED;
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;
for (i = 0, capsule = *capsule_header_array; i < capsule_count;
i++, capsule = *(++capsule_header_array)) {
/* TODO */
}
out:
return EFI_EXIT(ret);
}

View File

@ -133,6 +133,10 @@ efi_status_t efi_init_runtime_supported(void)
#ifdef CONFIG_EFI_HAVE_RUNTIME_RESET #ifdef CONFIG_EFI_HAVE_RUNTIME_RESET
rt_table->runtime_services_supported |= EFI_RT_SUPPORTED_RESET_SYSTEM; rt_table->runtime_services_supported |= EFI_RT_SUPPORTED_RESET_SYSTEM;
#endif #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, ret = efi_install_configuration_table(&efi_rt_properties_table_guid,
rt_table); rt_table);
@ -448,6 +452,50 @@ efi_status_t __weak __efi_runtime EFIAPI efi_set_time(struct efi_time *time)
return EFI_UNSUPPORTED; 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 * efi_is_runtime_service_pointer() - check if pointer points to runtime table
* *
@ -471,6 +519,13 @@ void efi_runtime_detach(void)
efi_runtime_services.reset_system = efi_reset_system; efi_runtime_services.reset_system = efi_reset_system;
efi_runtime_services.get_time = efi_get_time; efi_runtime_services.get_time = efi_get_time;
efi_runtime_services.set_time = efi_set_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 */ /* Update CRC32 */
efi_update_table_header_crc32(&efi_runtime_services.hdr); efi_update_table_header_crc32(&efi_runtime_services.hdr);
@ -879,50 +934,6 @@ static efi_status_t __efi_runtime EFIAPI efi_unimplemented(void)
return EFI_UNSUPPORTED; 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 = { struct efi_runtime_services __efi_runtime_data efi_runtime_services = {
.hdr = { .hdr = {
.signature = EFI_RUNTIME_SERVICES_SIGNATURE, .signature = EFI_RUNTIME_SERVICES_SIGNATURE,
@ -940,7 +951,12 @@ struct efi_runtime_services __efi_runtime_data efi_runtime_services = {
.set_variable = efi_set_variable, .set_variable = efi_set_variable,
.get_next_high_mono_count = (void *)&efi_unimplemented, .get_next_high_mono_count = (void *)&efi_unimplemented,
.reset_system = &efi_reset_system_boottime, .reset_system = &efi_reset_system_boottime,
#ifdef CONFIG_EFI_RUNTIME_UPDATE_CAPSULE
.update_capsule = efi_update_capsule, .update_capsule = efi_update_capsule,
.query_capsule_caps = efi_query_capsule_caps, .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, .query_variable_info = efi_query_variable_info,
}; };

View File

@ -100,9 +100,9 @@ static efi_status_t efi_init_secure_boot(void)
ret = efi_set_variable_int(L"SignatureSupport", ret = efi_set_variable_int(L"SignatureSupport",
&efi_global_variable_guid, &efi_global_variable_guid,
EFI_VARIABLE_READ_ONLY |
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS |
EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
EFI_VARIABLE_READ_ONLY,
sizeof(signature_types), sizeof(signature_types),
&signature_types, false); &signature_types, false);
if (ret != EFI_SUCCESS) if (ret != EFI_SUCCESS)
@ -117,6 +117,53 @@ static efi_status_t efi_init_secure_boot(void)
} }
#endif /* CONFIG_EFI_SECURE_BOOT */ #endif /* CONFIG_EFI_SECURE_BOOT */
/**
* efi_init_capsule - initialize capsule update state
*
* Return: status code
*/
static efi_status_t efi_init_capsule(void)
{
efi_status_t ret = EFI_SUCCESS;
if (IS_ENABLED(CONFIG_EFI_HAVE_CAPSULE_UPDATE)) {
ret = efi_set_variable_int(L"CapsuleMax",
&efi_guid_capsule_report,
EFI_VARIABLE_READ_ONLY |
EFI_VARIABLE_BOOTSERVICE_ACCESS |
EFI_VARIABLE_RUNTIME_ACCESS,
22, L"CapsuleFFFF", false);
if (ret != EFI_SUCCESS)
printf("EFI: cannot initialize CapsuleMax variable\n");
}
return ret;
}
/**
* efi_init_os_indications() - indicate supported features for OS requests
*
* Set the OsIndicationsSupported variable.
*
* Return: status code
*/
static efi_status_t efi_init_os_indications(void)
{
u64 os_indications_supported = 0;
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 * efi_init_obj_list() - Initialize and populate EFI object list
* *
@ -124,7 +171,6 @@ static efi_status_t efi_init_secure_boot(void)
*/ */
efi_status_t efi_init_obj_list(void) efi_status_t efi_init_obj_list(void)
{ {
u64 os_indications_supported = 0; /* None */
efi_status_t ret = EFI_SUCCESS; efi_status_t ret = EFI_SUCCESS;
/* Initialize once only */ /* Initialize once only */
@ -168,13 +214,7 @@ efi_status_t efi_init_obj_list(void)
goto out; goto out;
/* Indicate supported features */ /* Indicate supported features */
ret = efi_set_variable_int(L"OsIndicationsSupported", ret = efi_init_os_indications();
&efi_global_variable_guid,
EFI_VARIABLE_BOOTSERVICE_ACCESS |
EFI_VARIABLE_RUNTIME_ACCESS |
EFI_VARIABLE_READ_ONLY,
sizeof(os_indications_supported),
&os_indications_supported, false);
if (ret != EFI_SUCCESS) if (ret != EFI_SUCCESS)
goto out; goto out;
@ -233,6 +273,10 @@ efi_status_t efi_init_obj_list(void)
if (ret != EFI_SUCCESS) if (ret != EFI_SUCCESS)
goto out; goto out;
ret = efi_init_capsule();
if (ret != EFI_SUCCESS)
goto out;
/* Initialize EFI runtime services */ /* Initialize EFI runtime services */
ret = efi_reset_system_init(); ret = efi_reset_system_init();
if (ret != EFI_SUCCESS) if (ret != EFI_SUCCESS)