[RFC 10/14] efi_loader: capsule: support variable update

AKASHI Takahiro
2020-03-17T02:12:43Z

See EBBR specification v1.0.

A capsule tagged with the guid, EFI_VARIABLE_STORAGE_GUID, will be handled
as a variable update object.
What efi_update_capsule() basically does is to re-play SetVariable
against each variable entry in a capsule.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
include/efi_api.h | 18 +++++++++++++++
lib/efi_loader/Kconfig | 7 ++++++
lib/efi_loader/efi_capsule.c | 45 ++++++++++++++++++++++++++++++++++++
3 files changed, 70 insertions(+)

diff --git a/include/efi_api.h b/include/efi_api.h
index e103369186a2..30807942380b 100644
--- a/include/efi_api.h
+++ b/include/efi_api.h
@@ -229,6 +229,10 @@ enum efi_reset_type {
EFI_GUID(0x6dcbd5ed, 0xe82d, 0x4c44, 0xbd, 0xa1, \
0x71, 0x94, 0x19, 0x9a, 0xd9, 0x2a)

+#define EFI_VARIABLE_STORAGE_GUID \
+ EFI_GUID(0x1a3fb419, 0x2171, 0x458d, 0xb8, 0xb4, \
+ 0xbe, 0xa3, 0x0c, 0x9f, 0x6b, 0xab)
+
struct efi_capsule_header {
efi_guid_t capsule_guid;
u32 header_size;
@@ -283,6 +287,20 @@ struct efi_capsule_result_variable_fmp {
// u16 capsule_target[];
} __packed;

+struct efi_ebbr_variable {
+ u16 variable_name[64];
+ efi_guid_t vendor_guid;
+ u32 attributes;
+ u32 data_size;
+ u8 data[];
+};
+
+struct efi_ebbr_variable_bundle {
+ struct efi_capsule_header header;
+ u8 reserved[0];
+ struct efi_ebbr_variable variables[];
+} __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/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
index 41b1e9b5543c..616e2acbe102 100644
--- a/lib/efi_loader/Kconfig
+++ b/lib/efi_loader/Kconfig
@@ -129,6 +129,13 @@ config EFI_CAPSULE_FIT_DEVICE
help
Define storage device for storing FIT image

+config EFI_CAPSULE_UPDATE_VARIABLE
+ bool "Capsule based variable update"
+ default n
+ help
+ Select this option if you want to enable capsule-based
+ variable update support
+
endif

config EFI_CAPSULE_ON_DISK
diff --git a/lib/efi_loader/efi_capsule.c b/lib/efi_loader/efi_capsule.c
index f3526beed681..1293270aea95 100644
--- a/lib/efi_loader/efi_capsule.c
+++ b/lib/efi_loader/efi_capsule.c
@@ -18,6 +18,7 @@ static const efi_guid_t efi_guid_firmware_management_capsule_id =
EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID;
const efi_guid_t efi_guid_firmware_management_protocol =
EFI_FIRMWARE_MANAGEMENT_PROTOCOL_GUID;
+static const efi_guid_t efi_guid_variable_storage = EFI_VARIABLE_STORAGE_GUID;

/* for file system access */
static struct efi_file_handle *bootdev_root;
@@ -172,6 +173,45 @@ static efi_status_t efi_capsule_update_firmware(
}
#endif /* CONFIG_EFI_CAPSULE_UPDATE_FIRMWARE */

+#ifdef CONFIG_EFI_CAPSULE_UPDATE_VARIABLE
+/*
+ * Execute a log of variable changes
+ */
+static efi_status_t
+efi_capsule_update_variables(struct efi_ebbr_variable_bundle *bundle)
+{
+ struct efi_ebbr_variable *variable;
+ efi_status_t ret = EFI_SUCCESS;
+
+ for (variable = (void *)bundle + bundle->header.header_size;
+ (void *)variable
+ < ((void *)bundle + bundle->header.capsule_image_size);
+ variable = (struct efi_ebbr_variable *)
+ ((void *)variable
+ + sizeof(*variable) + variable->data_size)) {
+ ret = efi_set_variable(variable->variable_name,
+ &variable->vendor_guid,
+ variable->attributes,
+ variable->data_size,
+ &variable->data);
+ /* Should NOT_FOUND always be treated as success? */
+ if (ret == EFI_NOT_FOUND)
+ ret = EFI_SUCCESS;
+ EFI_PRINT("Capsule variable update %s: %ls\n",
+ ret == EFI_SUCCESS ? "succeeded" : "failed",
+ variable->variable_name);
+ }
+
+ return ret;
+}
+#else
+static efi_status_t
+efi_capsule_update_variables(struct efi_ebbr_variable_bundle *bundle)
+{
+ return EFI_UNSUPPORTED;
+}
+#endif /* CONFIG_EFI_CAPSULE_UPDATE_VARIABLE */
+
/*
* Launch a capsule
*/
@@ -214,6 +254,11 @@ efi_status_t EFIAPI efi_update_capsule(
ret = efi_capsule_update_firmware(
(struct efi_firmware_management_capsule_header *)
((void *)capsule + sizeof(*capsule)));
+ else if (!guidcmp(&capsule->capsule_guid,
+ &efi_guid_variable_storage))
+ ret = efi_capsule_update_variables(
+ (struct efi_ebbr_variable_bundle *)
+ capsule);
else
ret = EFI_UNSUPPORTED;

--
2.25.1