Add the TEE-backed runtime SetVariable() implementation for the FF-A/MM
variable backend. The runtime path uses EFI runtime-safe memory helpers
and the shared MM communication path so SetVariable() can reach the MM
secure partition after ExitBootServices().

Add runtime-safe variable-property helpers for the SetVariable() path
and use the original data_size argument when restoring the read-only
property maxsize, instead of relying on the MM response buffer.

Reviewed-by: Simon Glass <[email protected]>
Signed-off-by: Harsimran Singh Tungal <[email protected]>

---
Changelog:
===============

v6:

Ilias:

- Guard TEE runtime handler registration with CONFIG_ARM_FFA_RT_MODE
- Restore OP-TEE runtime callbacks when FF-A runtime mode is disabled
- Rename FF-A SetVariable runtime handler with _ffa_runtime suffix
- Reuse runtime-safe property helpers in SetVariable paths

v4:

Ilias:

- Split the runtime SetVariable() support out of the combined runtime
  variable-services patch.

v3:

Simon:

- Return early on the EFI_NOT_FOUND property-get path at runtime
- Use the original data_size for the read-only property maxsize update

Ilias:
- Rename TEE/FF-A runtime handlers to private *_int_runtime names

v2:

Simon:

- Move the read-only property check before `setup_mm_hdr()` in
  efi_set_variable_runtime
- Drop the stale copied boot-path comment
---
 lib/efi_loader/efi_variable_tee.c | 179 ++++++++++++++++++++++++------
 1 file changed, 146 insertions(+), 33 deletions(-)

diff --git a/lib/efi_loader/efi_variable_tee.c 
b/lib/efi_loader/efi_variable_tee.c
index a03f2091eb4..b4cf12d6332 100644
--- a/lib/efi_loader/efi_variable_tee.c
+++ b/lib/efi_loader/efi_variable_tee.c
@@ -689,10 +689,10 @@ out:
  * StMM can store internal attributes and properties for variables, i.e 
enabling
  * R/O variables
  */
-static efi_status_t set_property_int(const u16 *variable_name,
-                                    efi_uintn_t name_size,
-                                    const efi_guid_t *vendor,
-                                    struct var_check_property *var_property)
+static efi_status_t __efi_runtime set_property_int(const 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;
@@ -700,54 +700,52 @@ static efi_status_t set_property_int(const u16 
*variable_name,
        efi_status_t ret;
 
        payload_size = sizeof(*smm_property) + name_size;
-       if (payload_size > max_payload_size) {
-               ret = EFI_INVALID_PARAMETER;
-               goto out;
-       }
+       if (payload_size > max_payload_size)
+               return EFI_INVALID_PARAMETER;
+
        comm_buf = setup_mm_hdr((void **)&smm_property, payload_size,
                                
SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_SET,
                                &ret);
        if (!comm_buf)
-               goto out;
+               return ret;
 
-       guidcpy(&smm_property->guid, vendor);
+       efi_memcpy_runtime(&smm_property->guid, vendor, 
sizeof(smm_property->guid));
        smm_property->name_size = name_size;
-       memcpy(&smm_property->property, var_property,
-              sizeof(smm_property->property));
-       memcpy(smm_property->name, variable_name, name_size);
+       efi_memcpy_runtime(&smm_property->property, var_property,
+                          sizeof(smm_property->property));
+       efi_memcpy_runtime(smm_property->name, variable_name, name_size);
 
        ret = mm_communicate(comm_buf, payload_size);
+       if (!efi_at_runtime())
+               free(comm_buf);
 
-out:
-       free(comm_buf);
        return ret;
 }
 
-static efi_status_t get_property_int(const u16 *variable_name,
-                                    efi_uintn_t name_size,
-                                    const efi_guid_t *vendor,
-                                    struct var_check_property *var_property)
+static efi_status_t __efi_runtime get_property_int(const 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));
+       efi_memset_runtime(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;
-       }
+       if (payload_size > max_payload_size)
+               return EFI_INVALID_PARAMETER;
+
        comm_buf = setup_mm_hdr((void **)&smm_property, payload_size,
                                
SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_GET,
                                &ret);
        if (!comm_buf)
-               goto out;
+               return ret;
 
-       guidcpy(&smm_property->guid, vendor);
+       efi_memcpy_runtime(&smm_property->guid, vendor, 
sizeof(smm_property->guid));
        smm_property->name_size = name_size;
-       memcpy(smm_property->name, variable_name, name_size);
+       efi_memcpy_runtime(smm_property->name, variable_name, name_size);
 
        ret = mm_communicate(comm_buf, payload_size);
        /*
@@ -757,14 +755,17 @@ static efi_status_t get_property_int(const u16 
*variable_name,
         * properties to 0x0 so checking against that is enough for the
         * EFI_NOT_FOUND case.
         */
-       if (ret == EFI_NOT_FOUND)
+       if (ret == EFI_NOT_FOUND) {
                ret = EFI_SUCCESS;
+               goto out;
+       }
        if (ret != EFI_SUCCESS)
                goto out;
-       memcpy(var_property, &smm_property->property, sizeof(*var_property));
+       efi_memcpy_runtime(var_property, &smm_property->property, 
sizeof(*var_property));
 
 out:
-       free(comm_buf);
+       if (!efi_at_runtime())
+               free(comm_buf);
        return ret;
 }
 
@@ -1001,7 +1002,7 @@ efi_status_t efi_set_variable_int(const u16 
*variable_name,
                var_property.property |= VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY;
                var_property.attributes = attributes;
                var_property.minsize = 1;
-               var_property.maxsize = var_acc->data_size;
+               var_property.maxsize = data_size;
                ret = set_property_int(variable_name, name_size, vendor, 
&var_property);
        }
 
@@ -1078,7 +1079,10 @@ efi_query_variable_info_runtime(u32 attributes, u64 
*max_variable_storage_size,
 }
 
 /**
- * efi_set_variable_runtime() - runtime implementation of SetVariable()
+ * efi_set_variable_ffa_runtime() - runtime implementation of SetVariable()
+ *
+ * This function implements the SetVariable() runtime service for OP-TEE
+ * with FF-A runtime transport.
  *
  * @variable_name:     name of the variable
  * @guid:              vendor GUID
@@ -1088,6 +1092,111 @@ efi_query_variable_info_runtime(u32 attributes, u64 
*max_variable_storage_size,
  * Return:             status code
  */
 static efi_status_t __efi_runtime EFIAPI
+efi_set_variable_ffa_runtime(u16 *variable_name, const efi_guid_t *guid,
+                            u32 attributes, efi_uintn_t data_size,
+                            const void *data)
+{
+       efi_status_t ret, mm_communicate_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;
+       bool ro;
+
+       if (!variable_name || variable_name[0] == 0 || !guid)
+               return EFI_INVALID_PARAMETER;
+
+       if (data_size > 0 && !data)
+               return EFI_INVALID_PARAMETER;
+
+       /* Check payload size */
+       name_size = u16_strsize(variable_name);
+       payload_size = MM_VARIABLE_ACCESS_HEADER_SIZE + name_size + data_size;
+       if (payload_size > max_payload_size)
+               return EFI_INVALID_PARAMETER;
+
+       ro = !!(attributes & EFI_VARIABLE_READ_ONLY);
+       attributes &= EFI_VARIABLE_MASK;
+
+       ret = get_property_int(variable_name, name_size, guid,
+                              &var_property);
+       if (ret != EFI_SUCCESS)
+               return ret;
+
+       if (var_property.property & VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY)
+               return EFI_WRITE_PROTECTED;
+
+       comm_buf = setup_mm_hdr((void **)&var_acc, payload_size,
+                               SMM_VARIABLE_FUNCTION_SET_VARIABLE, &ret);
+       if (!comm_buf)
+               return ret;
+
+       /* Fill in contents */
+       efi_memcpy_runtime(&var_acc->guid, guid, sizeof(*guid));
+       var_acc->data_size = data_size;
+       var_acc->name_size = name_size;
+       var_acc->attr = attributes;
+       efi_memcpy_runtime(var_acc->name, variable_name, name_size);
+       efi_memcpy_runtime((u8 *)var_acc->name + name_size, data, data_size);
+
+       /* Communicate */
+       ret = mm_communicate(comm_buf, payload_size);
+       if (ret != EFI_SUCCESS)
+               mm_communicate_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 = data_size;
+               ret = set_property_int(variable_name, name_size, guid, 
&var_property);
+       }
+
+       return (mm_communicate_ret == EFI_SUCCESS) ? ret : mm_communicate_ret;
+}
+
+/**
+ * efi_query_variable_info() - get information about EFI variables
+ *
+ * This function implements the QueryVariableInfo() runtime service.
+ *
+ * See the Unified Extensible Firmware Interface (UEFI) specification for
+ * details.
+ *
+ * @attributes:                         bitmask to select variables to be
+ *                                      queried
+ * @maximum_variable_storage_size:      maximum size of storage area for the
+ *                                      selected variable types
+ * @remaining_variable_storage_size:    remaining size of storage are for the
+ *                                      selected variable types
+ * @maximum_variable_size:              maximum size of a variable of the
+ *                                      selected type
+ * Return:                              status code
+ */
+efi_status_t EFIAPI __efi_runtime
+efi_query_variable_info_runtime(u32 attributes, u64 *max_variable_storage_size,
+                               u64 *remain_variable_storage_size,
+                               u64 *max_variable_size)
+{
+       return EFI_UNSUPPORTED;
+}
+
+/**
+ * efi_set_variable_runtime() - runtime implementation of SetVariable()
+ *
+ * This function implements the SetVariable() runtime service for OP-TEE
+ * without FF-A runtime transport.
+ *
+ * @variable_name:      name of the variable
+ * @guid:               vendor GUID
+ * @attributes:         attributes of the variable
+ * @data_size:          size of the buffer with the variable value
+ * @data:               buffer with the variable value
+ * Return:              status code
+ */
+static efi_status_t __efi_runtime EFIAPI
 efi_set_variable_runtime(u16 *variable_name, const efi_guid_t *guid,
                         u32 attributes, efi_uintn_t data_size,
                         const void *data)
@@ -1124,12 +1233,16 @@ void efi_variables_boot_exit_notify(void)
        free(var_buf);
 
        /* Update runtime service table */
+       if (IS_ENABLED(CONFIG_ARM_FFA_RT_MODE)) {
+               efi_runtime_services.set_variable = 
efi_set_variable_ffa_runtime;
+       } else {
+               efi_runtime_services.set_variable = efi_set_variable_runtime;
+       }
        efi_runtime_services.query_variable_info =
                        efi_query_variable_info_runtime;
        efi_runtime_services.get_variable = efi_get_variable_runtime;
        efi_runtime_services.get_next_variable_name =
                        efi_get_next_variable_name_runtime;
-       efi_runtime_services.set_variable = efi_set_variable_runtime;
        efi_update_table_header_crc32(&efi_runtime_services.hdr);
 
        /* Record that ExitBootServices() has been called */
-- 
2.43.0

Reply via email to