diff mbox series

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

Message ID 20200317021247.5849-11-takahiro.akashi@linaro.org
State RFC
Delegated to: Heinrich Schuchardt
Headers show
Series efi_loader: add capsule update support | expand

Commit Message

AKASHI Takahiro March 17, 2020, 2:12 a.m. UTC
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 mbox series

Patch

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;