Make the FF-A MM communication helper usable from EFI runtime code. The
boot path copies requests into the configured FF-A shared buffer, while
the runtime path operates directly on the runtime communication buffer.

Use range-based cache maintenance on the active shared-buffer request
and response sizes, and document the shared-buffer cacheline-alignment
requirements in Kconfig.

Reviewed-by: Simon Glass <[email protected]>
Reviewed-by: Abdellatif El Khlifi <[email protected]>
Signed-off-by: Harsimran Singh Tungal <[email protected]>
---
 lib/efi_loader/Kconfig            |   4 +
 lib/efi_loader/efi_variable_tee.c | 130 +++++++++++++++++++++---------
 2 files changed, 95 insertions(+), 39 deletions(-)

diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig
index 4cb13ae7c8a..a9791b8f2e3 100644
--- a/lib/efi_loader/Kconfig
+++ b/lib/efi_loader/Kconfig
@@ -195,6 +195,8 @@ config FFA_SHARED_MM_BUF_SIZE
          the MM SP in secure world.
          The size of the memory region must be a multiple of the size of the 
maximum
          translation granule size that is specified in the ID_AA64MMFR0_EL1 
System register.
+         For arm64 FF-A cache maintenance, this size must also be aligned to
+         CONFIG_SYS_CACHELINE_SIZE.
          It is assumed that the MM SP knows the size of the shared MM 
communication buffer.
 
 config FFA_SHARED_MM_BUF_OFFSET
@@ -211,6 +213,8 @@ config FFA_SHARED_MM_BUF_ADDR
          This defines the address of the shared MM communication buffer
          used for communication between the MM feature in U-Boot and
          the MM SP in secure world.
+         For arm64 FF-A cache maintenance, this address must also be aligned to
+         CONFIG_SYS_CACHELINE_SIZE.
          It is assumed that the MM SP knows the address of the shared MM 
communication buffer.
 
 config EFI_VARIABLE_SF_OFFSET
diff --git a/lib/efi_loader/efi_variable_tee.c 
b/lib/efi_loader/efi_variable_tee.c
index 4ebba83036c..8de258bb36b 100644
--- a/lib/efi_loader/efi_variable_tee.c
+++ b/lib/efi_loader/efi_variable_tee.c
@@ -22,6 +22,7 @@
 #include <efi_api.h>
 #include <efi_loader.h>
 #include <efi_variable.h>
+#include <linux/build_bug.h>
 #include <linux/kernel.h>
 #include <malloc.h>
 #include <mapmem.h>
@@ -301,77 +302,128 @@ static int ffa_discover_mm_sp_id(void)
 }
 
 /**
- * ffa_mm_communicate() - Exchange EFI services data with  the MM partition 
using FF-A
+ * ffa_mm_communicate() - Exchange EFI services data with the MM partition 
using FF-A
  * @comm_buf:          locally allocated communication buffer used for rx/tx
- * @dsize:                             communication buffer size
+ * @comm_buf_size:     communication buffer size
  *
  * Issue a door bell event to notify the MM partition (SP) running in OP-TEE
  * that there is data to read from the shared buffer.
  * Communication with the MM SP is performed using FF-A transport.
  * On the event, MM SP can read the data from the buffer and
  * update the MM shared buffer with response data.
- * The response data is copied back to the communication buffer.
+ * The response data is copied back to the communication buffer during the
+ * boot phase. At runtime, the communication buffer is already the FF-A
+ * shared buffer and is updated in place.
  *
- * Return:
- *
- * EFI status code
+ * Return: EFI status code
  */
-static efi_status_t ffa_mm_communicate(void *comm_buf, ulong comm_buf_size)
+static efi_status_t __efi_runtime ffa_mm_communicate(void *comm_buf,
+                                                    ulong comm_buf_size)
 {
+       ulong hdr_cache_size;
        ulong tx_data_size;
+       ulong tx_cache_size;
        int ffa_ret;
        efi_status_t efi_ret;
        struct efi_mm_communicate_header *mm_hdr;
-       void *virt_shared_buf;
+       u8 *shared_buf;
+       bool at_runtime = efi_at_runtime();
 
        if (!comm_buf)
                return EFI_INVALID_PARAMETER;
 
-       /* Discover MM partition ID at boot time */
-       if (!mm_sp_id && ffa_discover_mm_sp_id()) {
-               log_err("EFI: Failure to discover MM SP ID at boot time, FF-A 
MM comms failure\n");
-               return EFI_UNSUPPORTED;
+       if (!mm_sp_id) {
+               if (at_runtime)
+                       return EFI_UNSUPPORTED;
+               if (ffa_discover_mm_sp_id())
+                       return EFI_UNSUPPORTED;
        }
 
        mm_hdr = (struct efi_mm_communicate_header *)comm_buf;
        tx_data_size = mm_hdr->message_len + sizeof(efi_guid_t) + 
sizeof(size_t);
+       hdr_cache_size = ALIGN(sizeof(*mm_hdr), CONFIG_SYS_CACHELINE_SIZE);
+       tx_cache_size = ALIGN(tx_data_size, CONFIG_SYS_CACHELINE_SIZE);
 
        if (comm_buf_size != tx_data_size || tx_data_size > 
CONFIG_FFA_SHARED_MM_BUF_SIZE)
                return EFI_INVALID_PARAMETER;
 
-       /* Copy the data to the shared buffer */
-
-       virt_shared_buf = 
map_sysmem((phys_addr_t)CONFIG_FFA_SHARED_MM_BUF_ADDR, 0);
-       memcpy(virt_shared_buf, comm_buf, tx_data_size);
+       if (at_runtime) {
+               shared_buf = comm_buf;
+       } else {
+               /* Copy the data to the shared buffer */
+               shared_buf = 
map_sysmem((phys_addr_t)CONFIG_FFA_SHARED_MM_BUF_ADDR, 0);
+               memcpy(shared_buf, comm_buf, tx_data_size);
+       }
 
        /*
-        * The secure world might have cache disabled for
-        * the device region used for shared buffer (which is the case for 
Optee).
-        * In this case, the secure world reads the data from DRAM.
-        * Let's flush the cache so the DRAM is updated with the latest data.
+        * Shared buffer cache maintenance for FF-A / OP-TEE communication:
+        *
+        * NS -> S (request path):
+        *
+        * The non-secure side populates the shared buffer. If the buffer is 
cached
+        * in NS, the updated bytes may reside in dirty D-cache lines and not 
yet be
+        * visible in DDR. Since the secure world typically reads the shared 
buffer
+        * directly from DDR (e.g. with caches disabled / non-coherent 
mapping), we
+        * must clean the corresponding cache lines to the Point of Coherency 
(PoC)
+        * before entering secure world.
+        *
+        * S -> NS (response path):
+        *
+        * The secure world may update the same shared buffer in DDR. After 
returning
+        * to non-secure, any cached copies of that region in NS may be stale. 
We
+        * therefore invalidate the shared buffer range after the FF-A call to 
drop
+        * those lines and force subsequent reads to fetch the latest data from 
DDR.
+        *
+        * Note: Whole-cache invalidation must not be used in EFI runtime 
context.
+        * After ExitBootServices(), the OS owns the cache hierarchy; global
+        * invalidation could drop OS dirty lines and violate the OS coherency
+        * model. Always operate on the shared buffer range only.
         */
-#ifdef CONFIG_ARM64
-       invalidate_dcache_all();
-#endif
+       if (IS_ENABLED(CONFIG_ARM64)) {
+               BUILD_BUG_ON(CONFIG_FFA_SHARED_MM_BUF_ADDR %
+                            CONFIG_SYS_CACHELINE_SIZE);
+               BUILD_BUG_ON(CONFIG_FFA_SHARED_MM_BUF_SIZE %
+                            CONFIG_SYS_CACHELINE_SIZE);
+               flush_dcache_range((unsigned long)shared_buf,
+                                  (unsigned long)(shared_buf +
+                                          tx_cache_size));
+       }
 
        /* Announce there is data in the shared buffer */
-
        ffa_ret = ffa_notify_mm_sp();
 
        switch (ffa_ret) {
        case 0: {
                ulong rx_data_size;
-               /* Copy the MM SP response from the shared buffer to the 
communication buffer */
-               rx_data_size = ((struct efi_mm_communicate_header 
*)virt_shared_buf)->message_len +
+               ulong rx_cache_size;
+
+               if (IS_ENABLED(CONFIG_ARM64))
+                       invalidate_dcache_range((unsigned long)shared_buf,
+                                               (unsigned long)(shared_buf +
+                                                       hdr_cache_size));
+
+               rx_data_size = ((struct efi_mm_communicate_header 
*)shared_buf)->message_len +
                        sizeof(efi_guid_t) +
                        sizeof(size_t);
 
-               if (rx_data_size > comm_buf_size) {
+               if (rx_data_size > comm_buf_size ||
+                   rx_data_size > CONFIG_FFA_SHARED_MM_BUF_SIZE) {
                        efi_ret = EFI_OUT_OF_RESOURCES;
                        break;
                }
 
-               memcpy(comm_buf, virt_shared_buf, rx_data_size);
+               if (IS_ENABLED(CONFIG_ARM64)) {
+                       rx_cache_size = ALIGN(rx_data_size,
+                                             CONFIG_SYS_CACHELINE_SIZE);
+                       if (rx_cache_size > hdr_cache_size)
+                               invalidate_dcache_range((unsigned 
long)(shared_buf +
+                                                       hdr_cache_size),
+                                               (unsigned long)(shared_buf +
+                                                       rx_cache_size));
+               }
+
+               if (!at_runtime)
+                       memcpy(comm_buf, shared_buf, rx_data_size);
                efi_ret = EFI_SUCCESS;
                break;
        }
@@ -391,7 +443,8 @@ static efi_status_t ffa_mm_communicate(void *comm_buf, 
ulong comm_buf_size)
                efi_ret = EFI_ACCESS_DENIED;
        }
 
-       unmap_sysmem(virt_shared_buf);
+       if (!at_runtime)
+               unmap_sysmem(shared_buf);
        return efi_ret;
 }
 
@@ -427,8 +480,8 @@ static enum mm_comms_select __efi_runtime get_mm_comms(void)
 #endif
 
 /**
- * mm_communicate() - Adjust the communication buffer to the MM SP and send
- * it to OP-TEE
+ * mm_communicate() - Adjust the communication buffer to the MM SP and send it
+ * to the selected MM transport
  *
  * @comm_buf:          locally allocated communication buffer
  * @dsize:             buffer size
@@ -438,11 +491,12 @@ static enum mm_comms_select __efi_runtime 
get_mm_comms(void)
  * When using the u-boot OP-TEE driver, StandAlonneMM is supported.
  * When using the u-boot FF-A  driver, any MM SP is supported.
  *
- * Return:             status code
+ * Return: status code
  */
-static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize)
+static efi_status_t __efi_runtime mm_communicate(u8 *comm_buf,
+                                                efi_uintn_t dsize)
 {
-       efi_status_t ret;
+       efi_status_t ret = EFI_UNSUPPORTED;
        struct efi_mm_communicate_header *mm_hdr;
        struct smm_variable_communicate_header *var_hdr;
 #if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT)
@@ -457,16 +511,14 @@ static efi_status_t mm_communicate(u8 *comm_buf, 
efi_uintn_t dsize)
        mm_comms = get_mm_comms();
        if (mm_comms == MM_COMMS_FFA)
                ret = ffa_mm_communicate(comm_buf, dsize);
-       else
+       else if (mm_comms == MM_COMMS_OPTEE)
                ret = optee_mm_communicate(comm_buf, dsize);
 #else
-               ret = optee_mm_communicate(comm_buf, dsize);
+       ret = optee_mm_communicate(comm_buf, dsize);
 #endif
 
-       if (ret != EFI_SUCCESS) {
-               log_err("%s failed!\n", __func__);
+       if (ret != EFI_SUCCESS)
                return ret;
-       }
 
        return var_hdr->ret_status;
 }
-- 
2.34.1

Reply via email to