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.aka...@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

Reply via email to