diff mbox series

[v2] efi_loader: display RO attribute with TEE-backed variables

Message ID 20200709200040.436748-1-ilias.apalodimas@linaro.org
State Accepted
Commit f96744b2509622302dbc6bd6f2f669fc406c24d3
Headers show
Series [v2] efi_loader: display RO attribute with TEE-backed variables | expand

Commit Message

Ilias Apalodimas July 9, 2020, 8 p.m. UTC
A previous commit adds support for displaying variables RO flag.
Let's add it on the TEE backed variable storage as well.

Signed-off-by: Ilias Apalodimas <ilias.apalodimas at linaro.org>
---
Changes since v1:
 - Better describe smm_variable_var_check_property struct members
 - Use struct var_check_property as input in set_property_int() instead
   of passing the attributes and property as arguments
 - only set VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY bit when re-enabling RO
   property instead of overwriting the mask

 include/mm_communication.h        |  43 +++++++++
 lib/efi_loader/efi_variable_tee.c | 142 ++++++++++++++++++++++++++++--
 2 files changed, 179 insertions(+), 6 deletions(-)
diff mbox series

Patch

diff --git a/include/mm_communication.h b/include/mm_communication.h
index 193c4d157874..f9c05bb7f104 100644
--- a/include/mm_communication.h
+++ b/include/mm_communication.h
@@ -205,4 +205,47 @@  struct smm_variable_query_info {
 	u32 attr;
 };
 
+#define VAR_CHECK_VARIABLE_PROPERTY_REVISION 0x0001
+#define VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY BIT(0)
+/**
+ * struct var_check_property - Used to store variable properties in StMM
+ *
+ * @revision:   magic revision number for variable property checking
+ * @property:   properties mask for the variable used in StMM.
+ *              Currently RO flag is supported
+ * @attributes: variable attributes used in StMM checking when properties
+ *              for a variable are enabled
+ * @minsize:    minimum allowed size for variable payload checked against
+ *              smm_variable_access->datasize in StMM
+ * @maxsize:    maximum allowed size for variable payload checked against
+ *              smm_variable_access->datasize in StMM
+ *
+ * Defined in EDK2 as VAR_CHECK_VARIABLE_PROPERTY.
+ */
+struct var_check_property {
+	u16         revision;
+	u16         property;
+	u32         attributes;
+	efi_uintn_t minsize;
+	efi_uintn_t maxsize;
+};
+
+/**
+ * struct smm_variable_var_check_property - Used to communicate variable
+ *                                          properties with StMM
+ *
+ * @guid:       vendor GUID
+ * @name_size:  size of EFI name
+ * @property:   variable properties struct
+ * @name:       variable name
+ *
+ * Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_VAR_CHECK_VARIABLE_PROPERTY.
+ */
+struct smm_variable_var_check_property {
+	efi_guid_t                guid;
+	efi_uintn_t               name_size;
+	struct var_check_property property;
+	u16                       name[];
+};
+
 #endif /* _MM_COMMUNICATION_H_ */
diff --git a/lib/efi_loader/efi_variable_tee.c b/lib/efi_loader/efi_variable_tee.c
index ff90aa8e81c6..24e0663ebd42 100644
--- a/lib/efi_loader/efi_variable_tee.c
+++ b/lib/efi_loader/efi_variable_tee.c
@@ -244,10 +244,92 @@  out:
 	return ret;
 }
 
+/*
+ * StMM can store internal attributes and properties for variables, i.e enabling
+ * R/O variables
+ */
+static efi_status_t set_property_int(u16 *variable_name, efi_uintn_t name_size,
+				     const efi_guid_t *vendor,
+				     struct var_check_property *var_property)
+{
+	struct smm_variable_var_check_property *smm_property;
+	efi_uintn_t payload_size;
+	u8 *comm_buf = NULL;
+	efi_status_t ret;
+
+	payload_size = sizeof(*smm_property) + name_size;
+	if (payload_size > max_payload_size) {
+		ret = EFI_INVALID_PARAMETER;
+		goto out;
+	}
+	comm_buf = setup_mm_hdr((void **)&smm_property, payload_size,
+				SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_SET,
+				&ret);
+	if (!comm_buf)
+		goto out;
+
+	guidcpy(&smm_property->guid, vendor);
+	smm_property->name_size = name_size;
+	memcpy(&smm_property->property, var_property,
+	       sizeof(smm_property->property));
+	memcpy(smm_property->name, variable_name, name_size);
+
+	ret = mm_communicate(comm_buf, payload_size);
+
+out:
+	free(comm_buf);
+	return ret;
+}
+
+static efi_status_t get_property_int(u16 *variable_name, efi_uintn_t name_size,
+				     const efi_guid_t *vendor,
+				     struct var_check_property *var_property)
+{
+	struct smm_variable_var_check_property *smm_property;
+	efi_uintn_t payload_size;
+	u8 *comm_buf = NULL;
+	efi_status_t ret;
+
+	memset(var_property, 0, sizeof(*var_property));
+	payload_size = sizeof(*smm_property) + name_size;
+	if (payload_size > max_payload_size) {
+		ret = EFI_INVALID_PARAMETER;
+		goto out;
+	}
+	comm_buf = setup_mm_hdr((void **)&smm_property, payload_size,
+				SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_GET,
+				&ret);
+	if (!comm_buf)
+		goto out;
+
+	guidcpy(&smm_property->guid, vendor);
+	smm_property->name_size = name_size;
+	memcpy(smm_property->name, variable_name, name_size);
+
+	ret = mm_communicate(comm_buf, payload_size);
+	/*
+	 * Currently only R/O property is supported in StMM.
+	 * Variables that are not set to R/O will not set the property in StMM
+	 * and the call will return EFI_NOT_FOUND. We are setting the
+	 * properties to 0x0 so checking against that is enough for the
+	 * EFI_NOT_FOUND case.
+	 */
+	if (ret == EFI_NOT_FOUND)
+		ret = EFI_SUCCESS;
+	if (ret != EFI_SUCCESS)
+		goto out;
+	memcpy(var_property, &smm_property->property, sizeof(*var_property));
+
+out:
+	free(comm_buf);
+	return ret;
+}
+
 efi_status_t efi_get_variable_int(u16 *variable_name, const efi_guid_t *vendor,
 				  u32 *attributes, efi_uintn_t *data_size,
 				  void *data, u64 *timep)
 {
+	struct var_check_property var_property;
 	struct smm_variable_access *var_acc;
 	efi_uintn_t payload_size;
 	efi_uintn_t name_size;
@@ -299,8 +381,16 @@  efi_status_t efi_get_variable_int(u16 *variable_name, const efi_guid_t *vendor,
 	if (ret != EFI_SUCCESS)
 		goto out;
 
-	if (attributes)
+	ret = get_property_int(variable_name, name_size, vendor, &var_property);
+	if (ret != EFI_SUCCESS)
+		goto out;
+
+	if (attributes) {
 		*attributes = var_acc->attr;
+		if (var_property.property & VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY)
+			*attributes |= EFI_VARIABLE_READ_ONLY;
+	}
+
 	if (data)
 		memcpy(data, (u8 *)var_acc->name + var_acc->name_size,
 		       var_acc->data_size);
@@ -387,11 +477,13 @@  efi_status_t efi_set_variable_int(u16 *variable_name, const efi_guid_t *vendor,
 				  u32 attributes, efi_uintn_t data_size,
 				  const void *data, bool ro_check)
 {
+	efi_status_t ret, alt_ret = EFI_SUCCESS;
+	struct var_check_property var_property;
 	struct smm_variable_access *var_acc;
 	efi_uintn_t payload_size;
 	efi_uintn_t name_size;
 	u8 *comm_buf = NULL;
-	efi_status_t ret;
+	bool ro;
 
 	if (!variable_name || variable_name[0] == 0 || !vendor) {
 		ret = EFI_INVALID_PARAMETER;
@@ -401,7 +493,6 @@  efi_status_t efi_set_variable_int(u16 *variable_name, const efi_guid_t *vendor,
 		ret = EFI_INVALID_PARAMETER;
 		goto out;
 	}
-
 	/* Check payload size */
 	name_size = u16_strsize(variable_name);
 	payload_size = MM_VARIABLE_ACCESS_HEADER_SIZE + name_size + data_size;
@@ -410,12 +501,41 @@  efi_status_t efi_set_variable_int(u16 *variable_name, const efi_guid_t *vendor,
 		goto out;
 	}
 
-	/* Get communication buffer and initialize header */
+	/*
+	 * Allocate the buffer early, before switching to RW (if needed)
+	 * so we won't need to account for any failures in reading/setting
+	 * the properties, if the allocation fails
+	 */
 	comm_buf = setup_mm_hdr((void **)&var_acc, payload_size,
 				SMM_VARIABLE_FUNCTION_SET_VARIABLE, &ret);
 	if (!comm_buf)
 		goto out;
 
+	ro = !!(attributes & EFI_VARIABLE_READ_ONLY);
+	attributes &= EFI_VARIABLE_MASK;
+
+	/*
+	 * The API has the ability to override RO flags. If no RO check was
+	 * requested switch the variable to RW for the duration of this call
+	 */
+	ret = get_property_int(variable_name, name_size, vendor,
+			       &var_property);
+	if (ret != EFI_SUCCESS)
+		goto out;
+
+	if (var_property.property & VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY) {
+		/* Bypass r/o check */
+		if (!ro_check) {
+			var_property.property &= ~VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY;
+			ret = set_property_int(variable_name, name_size, vendor, &var_property);
+			if (ret != EFI_SUCCESS)
+				goto out;
+		} else {
+			ret = EFI_WRITE_PROTECTED;
+			goto out;
+		}
+	}
+
 	/* Fill in contents */
 	guidcpy(&var_acc->guid, vendor);
 	var_acc->data_size = data_size;
@@ -426,10 +546,20 @@  efi_status_t efi_set_variable_int(u16 *variable_name, const efi_guid_t *vendor,
 
 	/* Communicate */
 	ret = mm_communicate(comm_buf, payload_size);
-
+	if (ret != EFI_SUCCESS)
+		alt_ret = ret;
+
+	if (ro && !(var_property.property & VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY)) {
+		var_property.revision = VAR_CHECK_VARIABLE_PROPERTY_REVISION;
+		var_property.property |= VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY;
+		var_property.attributes = attributes;
+		var_property.minsize = 1;
+		var_property.maxsize = var_acc->data_size;
+		ret = set_property_int(variable_name, name_size, vendor, &var_property);
+	}
 out:
 	free(comm_buf);
-	return ret;
+	return alt_ret == EFI_SUCCESS ? ret : alt_ret;
 }
 
 efi_status_t efi_query_variable_info_int(u32 attributes,