Use the statically reserved FF-A shared MM buffer for EFI variable
runtime communication after ExitBootServices(), since dynamic allocation
is no longer available.

Keep the shared-buffer pointer convertible across
SetVirtualAddressMap(), add the buffer to the EFI runtime memory map,
and reject runtime requests that do not fit in the configured shared
buffer.

Reviewed-by: Simon Glass <[email protected]>
Reviewed-by: Abdellatif El Khlifi <[email protected]>
Signed-off-by: Harsimran Singh Tungal <[email protected]>
---
 lib/efi_loader/efi_variable_tee.c | 118 +++++++++++++++++++++++++++---
 1 file changed, 107 insertions(+), 11 deletions(-)

diff --git a/lib/efi_loader/efi_variable_tee.c 
b/lib/efi_loader/efi_variable_tee.c
index 8de258bb36b..5c7104662e9 100644
--- a/lib/efi_loader/efi_variable_tee.c
+++ b/lib/efi_loader/efi_variable_tee.c
@@ -54,9 +54,10 @@ static const char *mm_sp_svc_uuid = MM_SP_UUID;
 static u16 __efi_runtime_data mm_sp_id;
 #endif
 
+static void *__efi_runtime_data ffa_shared_buf;
 extern struct efi_var_file __efi_runtime_data *efi_var_buf;
-static efi_uintn_t max_buffer_size;    /* comm + var + func + data */
-static efi_uintn_t max_payload_size;   /* func + data */
+static efi_uintn_t __efi_runtime_data max_buffer_size; /* comm + var + func + 
data */
+static efi_uintn_t __efi_runtime_data max_payload_size;        /* func + data 
*/
 static const u16 __efi_runtime_rodata pk[] = u"PK";
 static bool __efi_runtime_data ebs_called;
 
@@ -524,8 +525,60 @@ static efi_status_t __efi_runtime mm_communicate(u8 
*comm_buf,
 }
 
 /**
- * setup_mm_hdr() -    Allocate a buffer for StandAloneMM and initialize the
- *                     header data.
+ * get_comm_buf() - Obtain a communication buffer for MM/FF-A exchange
+ * @payload_size: size of the payload that will be appended to the
+ *                MM communication header
+ *
+ * This helper returns a buffer suitable for constructing an
+ * EFI_MM_COMMUNICATE message. During the boot phase a new buffer is
+ * dynamically allocated. After ExitBootServices(), dynamic
+ * allocation is no longer permitted, and all runtime communication must
+ * use the statically reserved FF-A shared buffer.
+ *
+ * The caller owns the returned buffer only during the boot phase and
+ * must release it with free(). During the runtime phase, the returned
+ * pointer aliases the static FF-A shared buffer and must not be freed.
+ *
+ * Return:
+ *   Pointer to a valid communication buffer on success.
+ *   NULL if no suitable communication buffer is available.
+ */
+static __efi_runtime u8 *get_comm_buf(efi_uintn_t payload_size)
+{
+       efi_uintn_t comm_buf_size;
+       u8 *comm_buf;
+
+       comm_buf_size = MM_COMMUNICATE_HEADER_SIZE +
+                       MM_VARIABLE_COMMUNICATE_SIZE +
+                       payload_size;
+
+       /*
+        * After ExitBootServices(), dynamic allocation is no longer permitted.
+        * Use the predefined FF-A shared buffer at runtime; otherwise allocate
+        * a fresh buffer during the boot phase.
+        */
+       if (efi_at_runtime()) {
+               if (IS_ENABLED(CONFIG_ARM_FFA_RT_MODE)) {
+                       if (comm_buf_size > CONFIG_FFA_SHARED_MM_BUF_SIZE)
+                               return NULL;
+                       comm_buf = ffa_shared_buf;
+                       if (!comm_buf)
+                               return NULL;
+                       efi_memset_runtime(comm_buf, 0, comm_buf_size);
+               } else {
+                       return NULL;
+               }
+       } else {
+               comm_buf = calloc(1, comm_buf_size);
+               if (!comm_buf)
+                       return NULL;
+       }
+       return comm_buf;
+}
+
+/**
+ * setup_mm_hdr() -    Obtain a communication buffer for StandAloneMM and
+ *                     initialize the MM header
  *
  * @dptr:              pointer address of the corresponding StandAloneMM
  *                     function
@@ -534,10 +587,11 @@ static efi_status_t __efi_runtime mm_communicate(u8 
*comm_buf,
  * @ret:               EFI return code
  * Return:             buffer or NULL
  */
-static u8 *setup_mm_hdr(void **dptr, efi_uintn_t payload_size,
-                       efi_uintn_t func, efi_status_t *ret)
+static __efi_runtime u8 *setup_mm_hdr(void **dptr, efi_uintn_t payload_size,
+                                     efi_uintn_t func, efi_status_t *ret)
 {
-       const efi_guid_t mm_var_guid = EFI_MM_VARIABLE_GUID;
+       static const __efi_runtime_rodata efi_guid_t mm_var_guid =
+               EFI_MM_VARIABLE_GUID;
        struct efi_mm_communicate_header *mm_hdr;
        struct smm_variable_communicate_header *var_hdr;
        u8 *comm_buf;
@@ -555,16 +609,15 @@ static u8 *setup_mm_hdr(void **dptr, efi_uintn_t 
payload_size,
                return NULL;
        }
 
-       comm_buf = calloc(1, MM_COMMUNICATE_HEADER_SIZE +
-                         MM_VARIABLE_COMMUNICATE_SIZE +
-                         payload_size);
+       comm_buf = get_comm_buf(payload_size);
        if (!comm_buf) {
                *ret = EFI_OUT_OF_RESOURCES;
                return NULL;
        }
 
        mm_hdr = (struct efi_mm_communicate_header *)comm_buf;
-       guidcpy(&mm_hdr->header_guid, &mm_var_guid);
+       efi_memcpy_runtime(&mm_hdr->header_guid, &mm_var_guid,
+                          sizeof(mm_hdr->header_guid));
        mm_hdr->message_len = MM_VARIABLE_COMMUNICATE_SIZE + payload_size;
 
        var_hdr = (struct smm_variable_communicate_header *)mm_hdr->data;
@@ -1077,6 +1130,18 @@ void efi_variables_boot_exit_notify(void)
        ebs_called = true;
 }
 
+/**
+ * ffa_shared_buf_notify_virtual_address_map() - SetVirtualAddressMap callback
+ *
+ * @event:     callback event
+ * @context:   callback context
+ */
+static void EFIAPI __efi_runtime
+ffa_shared_buf_notify_virtual_address_map(struct efi_event *event, void 
*context)
+{
+       efi_convert_pointer(0, (void **)&ffa_shared_buf);
+}
+
 /**
  * efi_init_variables() - initialize variable services
  *
@@ -1085,6 +1150,7 @@ void efi_variables_boot_exit_notify(void)
 efi_status_t efi_init_variables(void)
 {
        efi_status_t ret;
+       struct efi_event *event;
 
        /* Create a cached copy of the variables that will be enabled on 
ExitBootServices() */
        ret = efi_var_mem_init();
@@ -1103,5 +1169,35 @@ efi_status_t efi_init_variables(void)
        if (ret != EFI_SUCCESS)
                return ret;
 
+       if (IS_ENABLED(CONFIG_ARM_FFA_RT_MODE)) {
+               /*
+                * The FF-A shared buffer is accessed by EFI runtime services, 
so
+                * keep the resident pointer convertible across
+                * SetVirtualAddressMap() and mark the region as runtime memory.
+                *
+                * CONFIG_FFA_SHARED_MM_BUF_ADDR is expected to be EFI-page 
aligned.
+                */
+               BUILD_BUG_ON(CONFIG_FFA_SHARED_MM_BUF_ADDR & EFI_PAGE_MASK);
+               ffa_shared_buf = (void *)CONFIG_FFA_SHARED_MM_BUF_ADDR;
+               ret = efi_create_event(EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE,
+                                      TPL_CALLBACK,
+                                      
ffa_shared_buf_notify_virtual_address_map,
+                                      NULL, NULL, &event);
+               if (ret != EFI_SUCCESS)
+                       return ret;
+               ret = efi_add_memory_map(CONFIG_FFA_SHARED_MM_BUF_ADDR,
+                                        CONFIG_FFA_SHARED_MM_BUF_SIZE,
+                                        EFI_RUNTIME_SERVICES_DATA);
+               if (ret != EFI_SUCCESS) {
+                       efi_close_event(event);
+                       log_err("EFI: failed to add FF-A shared buffer to 
runtime map (%lu)\n",
+                               ret);
+                       return ret;
+               }
+               log_info("EFI: FF-A shared buffer runtime map: addr=0x%lx 
size=0x%lx\n",
+                        (ulong)CONFIG_FFA_SHARED_MM_BUF_ADDR,
+                        (ulong)CONFIG_FFA_SHARED_MM_BUF_SIZE);
+       }
+
        return EFI_SUCCESS;
 }
-- 
2.34.1

Reply via email to