efi_loader: define UpdateCapsule api
authorAKASHI Takahiro <takahiro.akashi@linaro.org>
Tue, 17 Nov 2020 00:27:55 +0000 (09:27 +0900)
committerHeinrich Schuchardt <xypron.glpk@gmx.de>
Thu, 3 Dec 2020 20:22:49 +0000 (21:22 +0100)
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
include/efi_loader.h
lib/efi_loader/Kconfig
lib/efi_loader/Makefile
lib/efi_loader/efi_capsule.c [new file with mode: 0644]
lib/efi_loader/efi_runtime.c
lib/efi_loader/efi_setup.c

index 364c578..a466e2c 100644 (file)
@@ -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
index 3c68b85..abd6856 100644 (file)
@@ -210,6 +210,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;
@@ -812,6 +814,17 @@ void efi_memcpy_runtime(void *dest, const void *src, size_t n);
 /* commonly used helper function */
 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) */
 
 /* Without CONFIG_EFI_LOADER we don't have a runtime section, stub it out */
index 1b983fe..1681b37 100644 (file)
@@ -94,6 +94,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
index cd4b252..056376f 100644 (file)
@@ -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 (file)
index 0000000..575fa75
--- /dev/null
@@ -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);
+}
index 1fa1595..0b171c1 100644 (file)
@@ -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);
@@ -449,6 +453,50 @@ efi_status_t __weak __efi_runtime EFIAPI efi_set_time(struct efi_time *time)
 }
 
 /**
+ * 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
  *
  * @p:         pointer to check
@@ -471,6 +519,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);
@@ -879,50 +934,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,
@@ -940,7 +951,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,
 };
index 2bb2c3c..3dd3f56 100644 (file)
@@ -100,9 +100,9 @@ static efi_status_t efi_init_secure_boot(void)
 
        ret = efi_set_variable_int(L"SignatureSupport",
                                   &efi_global_variable_guid,
+                                  EFI_VARIABLE_READ_ONLY |
                                   EFI_VARIABLE_BOOTSERVICE_ACCESS |
-                                  EFI_VARIABLE_RUNTIME_ACCESS |
-                                  EFI_VARIABLE_READ_ONLY,
+                                  EFI_VARIABLE_RUNTIME_ACCESS,
                                   sizeof(signature_types),
                                   &signature_types, false);
        if (ret != EFI_SUCCESS)
@@ -118,13 +118,59 @@ static efi_status_t efi_init_secure_boot(void)
 #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
  *
  * Return:     status code
  */
 efi_status_t efi_init_obj_list(void)
 {
-       u64 os_indications_supported = 0; /* None */
        efi_status_t ret = EFI_SUCCESS;
 
        /* Initialize once only */
@@ -168,13 +214,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;
 
@@ -233,6 +273,10 @@ efi_status_t efi_init_obj_list(void)
        if (ret != EFI_SUCCESS)
                goto out;
 
+       ret = efi_init_capsule();
+       if (ret != EFI_SUCCESS)
+               goto out;
+
        /* Initialize EFI runtime services */
        ret = efi_reset_system_init();
        if (ret != EFI_SUCCESS)