lib: arm_ffa: Support FF-A version 1.1

Update the use of ffa_mtd to support the new
layout in v1.1.

Bug: 284057071
Change-Id: I2fcd34b810c85b5e586a2fd159faa2d8a2a9c396
diff --git a/lib/arm_ffa/arm_ffa.c b/lib/arm_ffa/arm_ffa.c
index e5d56ef..cac3053 100644
--- a/lib/arm_ffa/arm_ffa.c
+++ b/lib/arm_ffa/arm_ffa.c
@@ -39,6 +39,7 @@
 #include <trace.h>
 
 static enum arm_ffa_init_state ffa_init_state = ARM_FFA_INIT_UNINIT;
+static uint32_t ffa_version;
 static uint16_t ffa_local_id;
 static size_t ffa_buf_size;
 static void* ffa_tx;
@@ -151,18 +152,24 @@
 
 /*
  * Call with ffa_rxtx_buffer_lock acquired and the ffa_tx buffer already
- * populated with struct ffa_mtd. Transmit in a single fragment.
+ * populated with struct ffa_mtd_common. Transmit in a single fragment.
  */
 static status_t arm_ffa_call_mem_retrieve_req(uint32_t* total_len,
                                               uint32_t* fragment_len) {
     struct smc_ret8 smc_ret;
-    struct ffa_mtd* req = ffa_tx;
+    struct ffa_mtd_v1_0* req_v1_0 = ffa_tx;
+    struct ffa_mtd_v1_1* req_v1_1 = ffa_tx;
     size_t len;
 
     DEBUG_ASSERT(is_mutex_held(&ffa_rxtx_buffer_lock));
 
-    len = offsetof(struct ffa_mtd, emad[0]) +
-          req->emad_count * sizeof(struct ffa_emad);
+    if (ffa_version < FFA_VERSION(1, 1)) {
+        len = offsetof(struct ffa_mtd_v1_0, emad[0]) +
+              req_v1_0->emad_count * sizeof(struct ffa_emad);
+    } else {
+        len = req_v1_1->emad_offset +
+              req_v1_1->emad_count * req_v1_1->emad_size;
+    }
 
     smc_ret = smc8(SMC_FC_FFA_MEM_RETRIEVE_REQ, len, len, 0, 0, 0, 0, 0);
 
@@ -584,23 +591,39 @@
 static void arm_ffa_populate_receive_req_tx_buffer(uint16_t sender_id,
                                                    uint64_t handle,
                                                    uint64_t tag) {
-    struct ffa_mtd* req = ffa_tx;
+    struct ffa_mtd_v1_0* req_v1_0 = ffa_tx;
+    struct ffa_mtd_v1_1* req_v1_1 = ffa_tx;
+    struct ffa_mtd_common* req = ffa_tx;
+    struct ffa_emad* emad;
     DEBUG_ASSERT(is_mutex_held(&ffa_rxtx_buffer_lock));
 
-    memset(req, 0, sizeof(struct ffa_mtd));
+    if (ffa_version < FFA_VERSION(1, 1)) {
+        memset(req_v1_0, 0, sizeof(struct ffa_mtd_v1_0));
+    } else {
+        memset(req_v1_1, 0, sizeof(struct ffa_mtd_v1_1));
+    }
 
     req->sender_id = sender_id;
     req->handle = handle;
     /* We must use the same tag as the one used by the sender to retrieve. */
     req->tag = tag;
 
-    /*
-     * We only support retrieving memory for ourselves for now.
-     * TODO: Also support stream endpoints. Possibly more than one.
-     */
-    req->emad_count = 1;
-    memset(req->emad, 0, sizeof(struct ffa_emad));
-    req->emad[0].mapd.endpoint_id = ffa_local_id;
+    if (ffa_version < FFA_VERSION(1, 1)) {
+        /*
+         * We only support retrieving memory for ourselves for now.
+         * TODO: Also support stream endpoints. Possibly more than one.
+         */
+        req_v1_0->emad_count = 1;
+        emad = req_v1_0->emad;
+    } else {
+        req_v1_1->emad_count = 1;
+        req_v1_1->emad_size = sizeof(struct ffa_emad);
+        req_v1_1->emad_offset = sizeof(struct ffa_mtd_v1_1);
+        emad = (struct ffa_emad*)((uint8_t*)req_v1_1 + req_v1_1->emad_offset);
+    }
+
+    memset(emad, 0, sizeof(struct ffa_emad));
+    emad[0].mapd.endpoint_id = ffa_local_id;
 }
 
 /* *desc_buffer is malloc'd and on success passes responsibility to free to
@@ -686,7 +709,9 @@
                                     uint* arch_mmu_flags,
                                     struct arm_ffa_mem_frag_info* frag_info) {
     status_t res;
-    struct ffa_mtd* mtd;
+    struct ffa_mtd_v1_0* mtd_v1_0;
+    struct ffa_mtd_v1_1* mtd_v1_1;
+    struct ffa_mtd_common* mtd;
     struct ffa_emad* emad;
     struct ffa_comp_mrd* comp_mrd;
     uint32_t computed_len;
@@ -706,22 +731,63 @@
         return res;
     }
 
-    if (fragment_len <
-        offsetof(struct ffa_mtd, emad) + sizeof(struct ffa_emad)) {
-        TRACEF("Fragment too short for memory transaction descriptor\n");
-        return ERR_IO;
-    }
-
     mtd = ffa_rx;
-    emad = mtd->emad;
+    if (ffa_version < FFA_VERSION(1, 1)) {
+        if (fragment_len < sizeof(struct ffa_mtd_v1_0)) {
+            TRACEF("Fragment too short for memory transaction descriptor\n");
+            return ERR_IO;
+        }
 
-    /*
-     * We don't retrieve the memory on behalf of anyone else, so we only
-     * expect one receiver address range descriptor.
-     */
-    if (mtd->emad_count != 1) {
-        TRACEF("unexpected response count %d != 1\n", mtd->emad_count);
-        return ERR_IO;
+        mtd_v1_0 = ffa_rx;
+        if (fragment_len <
+            offsetof(struct ffa_mtd_v1_0, emad) + sizeof(struct ffa_emad)) {
+            TRACEF("Fragment too short for endpoint memory access descriptor\n");
+            return ERR_IO;
+        }
+        emad = mtd_v1_0->emad;
+
+        /*
+         * We don't retrieve the memory on behalf of anyone else, so we only
+         * expect one receiver address range descriptor.
+         */
+        if (mtd_v1_0->emad_count != 1) {
+            TRACEF("unexpected response count %d != 1\n", mtd_v1_0->emad_count);
+            return ERR_IO;
+        }
+    } else {
+        if (fragment_len < sizeof(struct ffa_mtd_v1_1)) {
+            TRACEF("Fragment too short for memory transaction descriptor\n");
+            return ERR_IO;
+        }
+
+        mtd_v1_1 = ffa_rx;
+        /*
+         * We know from the check above that
+         *   fragment_len >= sizeof(ffa_mtd_v1) >= sizeof(ffa_emad)
+         * so we can rewrite the following
+         *   fragment_len < emad_offset + sizeof(ffa_emad)
+         * into
+         *   fragment_len - sizeof(ffa_emad) < emad_offset
+         * to avoid a potential overflow.
+         */
+        if (fragment_len - sizeof(struct ffa_emad) < mtd_v1_1->emad_offset) {
+            TRACEF("Fragment too short for endpoint memory access descriptor\n");
+            return ERR_IO;
+        }
+        if (mtd_v1_1->emad_offset < sizeof(struct ffa_mtd_v1_1)) {
+            TRACEF("Endpoint memory access descriptor offset too short\n");
+            return ERR_IO;
+        }
+        if (!IS_ALIGNED(mtd_v1_1->emad_offset, 16)) {
+            TRACEF("Endpoint memory access descriptor not aligned to 16 bytes\n");
+            return ERR_IO;
+        }
+        emad = (struct ffa_emad*)((uint8_t*)mtd_v1_1 + mtd_v1_1->emad_offset);
+
+        if (mtd_v1_1->emad_count != 1) {
+            TRACEF("unexpected response count %d != 1\n", mtd_v1_1->emad_count);
+            return ERR_IO;
+        }
     }
 
     LTRACEF("comp_mrd_offset: %u\n", emad->comp_mrd_offset);
@@ -917,14 +983,17 @@
     if (res != NO_ERROR) {
         TRACEF("No compatible FF-A version found\n");
         return res;
-    } else if (FFA_CURRENT_VERSION_MAJOR != ver_major_ret ||
-               FFA_CURRENT_VERSION_MINOR > ver_minor_ret) {
-        /* When trusty supports more FF-A versions downgrade may be possible */
+    } else if (FFA_CURRENT_VERSION_MAJOR != ver_major_ret) {
+        /* Allow downgrade within the same major version */
         TRACEF("Incompatible FF-A interface version, %" PRIu16 ".%" PRIu16 "\n",
                ver_major_ret, ver_minor_ret);
         return ERR_NOT_SUPPORTED;
     }
 
+    ffa_version = FFA_VERSION(ver_major_ret, ver_minor_ret);
+    LTRACEF("Negotiated FF-A version %" PRIu16 ".%" PRIu16 "\n", ver_major_ret,
+            ver_minor_ret);
+
     res = arm_ffa_call_id_get(&ffa_local_id);
     if (res != NO_ERROR) {
         TRACEF("Failed to get FF-A partition id (err=%d)\n", res);