Route EFI runtime variable APIs through FF-A MM communication

Route EFI runtime variable services through the FF-A/MM backend in
lib/efi_loader/efi_variable_tee.c. After ExitBootServices(),
GetVariable(), SetVariable(), GetNextVariableName(), and
QueryVariableInfo() use the runtime entry points and continue to reach
the MM secure partition.

Keep the existing boot-time helpers unchanged and add runtime service
wrappers for variable access and property handling. Reuse the
runtime-safe setup_mm_hdr() and common mm_communicate() path, which
selects the FF-A transport appropriate for the current phase, and use
the EFI runtime-safe memory helpers in the runtime-only code.

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

---

Changelog:
===============

v2:

Simon:

- Cache attributes before reusing the FF-A shared buffer
- Move the read-only property check before `setup_mm_hdr()` in
  efi_set_variable_runtime
- Drop the stale copied boot-path comment
- Split the `u16_strsize()` runtime annotation to a separate patch

 lib/efi_loader/efi_variable_tee.c | 320 +++++++++++++++++++++++++++++-
 1 file changed, 315 insertions(+), 5 deletions(-)

diff --git a/lib/efi_loader/efi_variable_tee.c 
b/lib/efi_loader/efi_variable_tee.c
index ccd8d94a51e..8acd0ed127b 100644
--- a/lib/efi_loader/efi_variable_tee.c
+++ b/lib/efi_loader/efi_variable_tee.c
@@ -698,6 +698,38 @@ out:
        return ret;
 }
 
+static efi_status_t __efi_runtime set_property_int_runtime(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;
+
+       payload_size = sizeof(*smm_property) + name_size;
+       if (payload_size > max_payload_size) {
+               ret = EFI_INVALID_PARAMETER;
+               return ret;
+       }
+       comm_buf = setup_mm_hdr((void **)&smm_property, payload_size,
+                               
SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_SET,
+                               &ret);
+       if (!comm_buf)
+               return ret;
+
+       efi_memcpy_runtime(&smm_property->guid, vendor, sizeof(*vendor));
+       smm_property->name_size = 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);
+
+       return ret;
+}
+
 static efi_status_t get_property_int(const u16 *variable_name,
                                     efi_uintn_t name_size,
                                     const efi_guid_t *vendor,
@@ -743,6 +775,49 @@ out:
        return ret;
 }
 
+static efi_status_t __efi_runtime get_property_int_runtime(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;
+
+       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;
+               return ret;
+       }
+       comm_buf = setup_mm_hdr((void **)&smm_property, payload_size,
+                               
SMM_VARIABLE_FUNCTION_VAR_CHECK_VARIABLE_PROPERTY_GET,
+                               &ret);
+       if (!comm_buf)
+               return ret;
+
+       efi_memcpy_runtime(&smm_property->guid, vendor, 
sizeof(smm_property->guid));
+       smm_property->name_size = name_size;
+       efi_memcpy_runtime(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)
+               return ret;
+       efi_memcpy_runtime(var_property, &smm_property->property, 
sizeof(*var_property));
+
+       return ret;
+}
+
 efi_status_t efi_get_variable_int(const u16 *variable_name,
                                  const efi_guid_t *vendor,
                                  u32 *attributes, efi_uintn_t *data_size,
@@ -830,6 +905,95 @@ out:
        return ret;
 }
 
+efi_status_t __efi_runtime efi_get_variable_runtime(u16 *variable_name,
+                                                   const efi_guid_t *vendor,
+                                                   u32 *attributes,
+                                                   efi_uintn_t *data_size,
+                                                   void *data)
+{
+       struct var_check_property var_property;
+       struct smm_variable_access *var_acc;
+       efi_uintn_t payload_size;
+       efi_uintn_t name_size;
+       efi_uintn_t tmp_dsize;
+       u8 *comm_buf = NULL;
+       efi_status_t ret, tmp;
+       u32 var_attr = 0;
+
+       if (!variable_name || !vendor || !data_size) {
+               ret = EFI_INVALID_PARAMETER;
+               return ret;
+       }
+
+       /* Check payload size */
+       name_size = u16_strsize(variable_name);
+       if (name_size > max_payload_size - MM_VARIABLE_ACCESS_HEADER_SIZE) {
+               ret = EFI_INVALID_PARAMETER;
+               return ret;
+       }
+
+       /* Trim output buffer size */
+       tmp_dsize = *data_size;
+       if (name_size + tmp_dsize >
+                       max_payload_size - MM_VARIABLE_ACCESS_HEADER_SIZE) {
+               tmp_dsize = max_payload_size -
+                               MM_VARIABLE_ACCESS_HEADER_SIZE -
+                               name_size;
+       }
+
+       /* Get communication buffer and initialize header */
+       payload_size = MM_VARIABLE_ACCESS_HEADER_SIZE + name_size + tmp_dsize;
+       comm_buf = setup_mm_hdr((void **)&var_acc, payload_size,
+                               SMM_VARIABLE_FUNCTION_GET_VARIABLE, &ret);
+       if (!comm_buf)
+               return ret;
+
+       /* Fill in contents */
+       efi_memcpy_runtime(&var_acc->guid, vendor, sizeof(var_acc->guid));
+       var_acc->data_size = tmp_dsize;
+       var_acc->name_size = name_size;
+       var_acc->attr = attributes ? *attributes : 0;
+       efi_memcpy_runtime(var_acc->name, variable_name, name_size);
+
+       /* Communicate */
+       ret = mm_communicate(comm_buf, payload_size);
+       if (ret != EFI_SUCCESS && ret != EFI_BUFFER_TOO_SMALL)
+               return ret;
+
+       /* Update with reported data size for trimmed case */
+       *data_size = var_acc->data_size;
+       if (attributes)
+               var_attr = var_acc->attr;
+
+       /* Copy the data if ret is EFI_SUCCESS  */
+       if (ret == EFI_SUCCESS) {
+               if (data)
+                       efi_memcpy_runtime(data, (u8 *)var_acc->name + 
var_acc->name_size,
+                                          var_acc->data_size);
+               else
+                       ret = EFI_INVALID_PARAMETER;
+       }
+
+       /*
+        * UEFI > 2.7 needs the attributes set even if the buffer is
+        * smaller
+        */
+       if (attributes) {
+               tmp = get_property_int_runtime(variable_name, name_size, vendor,
+                                              &var_property);
+               if (tmp != EFI_SUCCESS) {
+                       ret = tmp;
+                       return ret;
+               }
+               *attributes = var_attr;
+               if (var_property.property &
+                   VAR_CHECK_VARIABLE_PROPERTY_READ_ONLY)
+                       *attributes |= EFI_VARIABLE_READ_ONLY;
+       }
+
+       return ret;
+}
+
 efi_status_t efi_get_next_variable_name_int(efi_uintn_t *variable_name_size,
                                            u16 *variable_name,
                                            efi_guid_t *guid)
@@ -894,6 +1058,68 @@ out:
        return ret;
 }
 
+efi_status_t __efi_runtime efi_get_next_variable_name_runtime(efi_uintn_t 
*variable_name_size,
+                                                             u16 
*variable_name,
+                                                             efi_guid_t *guid)
+{
+       struct smm_variable_getnext *var_getnext;
+       efi_uintn_t payload_size;
+       efi_uintn_t out_name_size;
+       efi_uintn_t in_name_size;
+       u8 *comm_buf = NULL;
+       efi_status_t ret;
+
+       if (!variable_name_size || !variable_name || !guid) {
+               ret = EFI_INVALID_PARAMETER;
+               return ret;
+       }
+
+       out_name_size = *variable_name_size;
+       in_name_size = u16_strsize(variable_name);
+
+       if (out_name_size < in_name_size) {
+               ret = EFI_INVALID_PARAMETER;
+               return ret;
+       }
+
+       if (in_name_size > max_payload_size - MM_VARIABLE_GET_NEXT_HEADER_SIZE) 
{
+               ret = EFI_INVALID_PARAMETER;
+               return ret;
+       }
+
+       /* Trim output buffer size */
+       if (out_name_size > max_payload_size - MM_VARIABLE_GET_NEXT_HEADER_SIZE)
+               out_name_size = max_payload_size - 
MM_VARIABLE_GET_NEXT_HEADER_SIZE;
+
+       payload_size = MM_VARIABLE_GET_NEXT_HEADER_SIZE + out_name_size;
+       comm_buf = setup_mm_hdr((void **)&var_getnext, payload_size,
+                               SMM_VARIABLE_FUNCTION_GET_NEXT_VARIABLE_NAME,
+                               &ret);
+       if (!comm_buf)
+               return ret;
+
+       /* Fill in contents */
+       efi_memcpy_runtime(&var_getnext->guid, guid, sizeof(*guid));
+       var_getnext->name_size = out_name_size;
+       efi_memcpy_runtime(var_getnext->name, variable_name, in_name_size);
+       efi_memset_runtime((u8 *)var_getnext->name + in_name_size, 0x0,
+                          out_name_size - in_name_size);
+
+       /* Communicate */
+       ret = mm_communicate(comm_buf, payload_size);
+       if (ret == EFI_SUCCESS || ret == EFI_BUFFER_TOO_SMALL) {
+               /* Update with reported data size for trimmed case */
+               *variable_name_size = var_getnext->name_size;
+       }
+       if (ret != EFI_SUCCESS)
+               return ret;
+
+       efi_memcpy_runtime(guid, &var_getnext->guid, sizeof(*guid));
+       efi_memcpy_runtime(variable_name, var_getnext->name, 
var_getnext->name_size);
+
+       return ret;
+}
+
 efi_status_t efi_set_variable_int(const u16 *variable_name,
                                  const efi_guid_t *vendor, u32 attributes,
                                  efi_uintn_t data_size, const void *data,
@@ -1036,11 +1262,11 @@ out:
  *
  * @attributes:                                bitmask to select variables to 
be
  *                                     queried
- * @maximum_variable_storage_size:     maximum size of storage area for the
+ * @max_variable_storage_size:         maximum size of storage area for the
  *                                     selected variable types
- * @remaining_variable_storage_size:   remaining size of storage are for the
+ * @remain_variable_storage_size:      remaining size of storage are for the
  *                                     selected variable types
- * @maximum_variable_size:             maximum size of a variable of the
+ * @max_variable_size:                 maximum size of a variable of the
  *                                     selected type
  * Return:                             status code
  */
@@ -1049,7 +1275,33 @@ efi_query_variable_info_runtime(u32 attributes, u64 
*max_variable_storage_size,
                                u64 *remain_variable_storage_size,
                                u64 *max_variable_size)
 {
-       return EFI_UNSUPPORTED;
+       struct smm_variable_query_info *mm_query_info;
+       efi_uintn_t payload_size;
+       efi_status_t ret;
+       u8 *comm_buf;
+
+       if (!max_variable_storage_size ||
+           !remain_variable_storage_size ||
+           !max_variable_size || !attributes)
+               return EFI_INVALID_PARAMETER;
+
+       payload_size = sizeof(*mm_query_info);
+       comm_buf = setup_mm_hdr((void **)&mm_query_info, payload_size,
+                               SMM_VARIABLE_FUNCTION_QUERY_VARIABLE_INFO,
+                               &ret);
+       if (!comm_buf)
+               return ret;
+
+       mm_query_info->attr = attributes;
+       ret = mm_communicate(comm_buf, payload_size);
+       if (ret != EFI_SUCCESS)
+               return ret;
+       *max_variable_storage_size = mm_query_info->max_variable_storage;
+       *remain_variable_storage_size =
+                       mm_query_info->remaining_variable_storage;
+       *max_variable_size = mm_query_info->max_variable_size;
+
+       return ret;
 }
 
 /**
@@ -1067,7 +1319,65 @@ efi_set_variable_runtime(u16 *variable_name, const 
efi_guid_t *guid,
                         u32 attributes, efi_uintn_t data_size,
                         const void *data)
 {
-       return EFI_UNSUPPORTED;
+       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_runtime(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 = var_acc->data_size;
+               ret = set_property_int_runtime(variable_name, name_size, guid, 
&var_property);
+       }
+
+       return (mm_communicate_ret == EFI_SUCCESS) ? ret : mm_communicate_ret;
 }
 
 /**
-- 
2.34.1

Reply via email to