Introduce struct ffa_uuid together with nil/equality/set helpers, and
use it end-to-end in the partition-info plumbing.

The SP and VM enumeration paths now build UUIDs from the guest
registers, call a new ffa_copy_info() helper and ensure non-nil UUID
queries only return matching SP entries, relying on firmware UUID
filtering. VM entries are skipped because we do not track per-VM UUIDs.

Count requests and subscriber initialisation are updated accordingly so
firmware is always called with an explicit UUID. This keeps count and
listing requests aligned with the FF-A v1.1 rules while preserving the
Linux compatibility workaround for v1.2 requesters.

Signed-off-by: Bertrand Marquis <[email protected]>
---
 xen/arch/arm/tee/ffa_partinfo.c | 206 ++++++++++++++++++++------------
 xen/arch/arm/tee/ffa_private.h  |  21 ++++
 2 files changed, 152 insertions(+), 75 deletions(-)

diff --git a/xen/arch/arm/tee/ffa_partinfo.c b/xen/arch/arm/tee/ffa_partinfo.c
index 3f4a779f4146..4adbe2736c94 100644
--- a/xen/arch/arm/tee/ffa_partinfo.c
+++ b/xen/arch/arm/tee/ffa_partinfo.c
@@ -33,7 +33,7 @@ static uint16_t subscr_vm_created_count __read_mostly;
 static uint16_t *subscr_vm_destroyed __read_mostly;
 static uint16_t subscr_vm_destroyed_count __read_mostly;
 
-static int32_t ffa_partition_info_get(uint32_t *uuid, uint32_t flags,
+static int32_t ffa_partition_info_get(struct ffa_uuid uuid, uint32_t flags,
                                       uint32_t *count, uint32_t *fpi_size)
 {
     struct arm_smccc_1_2_regs arg = {
@@ -41,15 +41,12 @@ static int32_t ffa_partition_info_get(uint32_t *uuid, 
uint32_t flags,
         .a5 = flags,
     };
     struct arm_smccc_1_2_regs resp;
-    uint32_t ret;
+    int32_t ret;
 
-    if ( uuid )
-    {
-        arg.a1 = uuid[0];
-        arg.a2 = uuid[1];
-        arg.a3 = uuid[2];
-        arg.a4 = uuid[3];
-    }
+    arg.a1 = uuid.val[0] & 0xFFFFFFFFU;
+    arg.a2 = (uuid.val[0] >> 32) & 0xFFFFFFFFU;
+    arg.a3 = uuid.val[1] & 0xFFFFFFFFU;
+    arg.a4 = (uuid.val[1] >> 32) & 0xFFFFFFFFU;
 
     arm_smccc_1_2_smc(&arg, &resp);
 
@@ -63,7 +60,26 @@ static int32_t ffa_partition_info_get(uint32_t *uuid, 
uint32_t flags,
     return ret;
 }
 
-static int32_t ffa_get_sp_count(uint32_t *uuid, uint32_t *sp_count)
+static int32_t ffa_copy_info(void **dst, void *dst_end, const void *src,
+                             uint32_t dst_size, uint32_t src_size)
+{
+    uint8_t *pos = *dst;
+    uint8_t *end = dst_end;
+
+    if ( pos > end - dst_size )
+        return FFA_RET_NO_MEMORY;
+
+    memcpy(pos, src, MIN(dst_size, src_size));
+
+    if ( dst_size > src_size )
+        memset(pos + src_size, 0, dst_size - src_size);
+
+    *dst = pos + dst_size;
+
+    return FFA_RET_OK;
+}
+
+static int32_t ffa_get_sp_count(struct ffa_uuid uuid, uint32_t *sp_count)
 {
     uint32_t src_size;
 
@@ -71,8 +87,8 @@ static int32_t ffa_get_sp_count(uint32_t *uuid, uint32_t 
*sp_count)
                                   sp_count, &src_size);
 }
 
-static int32_t ffa_get_sp_partinfo(uint32_t *uuid, uint32_t *sp_count,
-                                   void *dst_buf, void *end_buf,
+static int32_t ffa_get_sp_partinfo(struct ffa_uuid uuid, uint32_t *sp_count,
+                                   void **dst_buf, void *end_buf,
                                    uint32_t dst_size)
 {
     int32_t ret;
@@ -120,17 +136,18 @@ static int32_t ffa_get_sp_partinfo(uint32_t *uuid, 
uint32_t *sp_count,
         /* filter out SP not following bit 15 convention if any */
         if ( FFA_ID_IS_SECURE(fpi->id) )
         {
-            if ( dst_buf > (end_buf - dst_size) )
-            {
-                ret = FFA_RET_NO_MEMORY;
-                goto out;
-            }
+            /*
+             * If VM is 1.0 but firmware is 1.1 we could have several entries
+             * with the same ID but different UUIDs. In this case the VM will
+             * get a list with several time the same ID.
+             * This is a non-compliance to the specification but 1.0 VMs should
+             * handle that on their own to simplify Xen implementation.
+             */
 
-            memcpy(dst_buf, src_buf, MIN(src_size, dst_size));
-            if ( dst_size > src_size )
-                memset(dst_buf + src_size, 0, dst_size - src_size);
+            ret = ffa_copy_info(dst_buf, end_buf, src_buf, dst_size, src_size);
+            if ( ret )
+                goto out;
 
-            dst_buf += dst_size;
             count++;
         }
 
@@ -144,69 +161,89 @@ out:
     return ret;
 }
 
-static int32_t ffa_get_vm_partinfo(uint32_t *vm_count, void *dst_buf,
-                                   void *end_buf, uint32_t dst_size)
+static int32_t ffa_get_vm_partinfo(struct ffa_uuid uuid, uint32_t *vm_count,
+                                   void **dst_buf, void *end_buf,
+                                   uint32_t dst_size)
 {
     struct ffa_ctx *curr_ctx = current->domain->arch.tee;
     struct ffa_ctx *dest_ctx;
     uint32_t count = 0;
     int32_t ret = FFA_RET_OK;
+    /*
+     * We do not have UUID info for VMs so use the 1.0 structure so that we set
+     * UUIDs to zero using memset
+     */
+    struct ffa_partition_info_1_0 info;
 
     /*
-     * There could potentially be a lot of VMs in the system and we could
-     * hold the CPU for long here.
-     * Right now there is no solution in FF-A specification to split
-     * the work in this case.
-     * TODO: Check how we could delay the work or have preemption checks.
+     * We do not have protocol UUIDs for VMs so if a request has non Nil UUID
+     * we must return an empty list.
      */
-    read_lock(&ffa_ctx_list_rwlock);
-    list_for_each_entry(dest_ctx, &ffa_ctx_head, ctx_list)
+    if ( !ffa_uuid_is_nil(uuid) )
+    {
+        *vm_count = 0;
+        return FFA_RET_OK;
+    }
+
+    /*
+     * Workaround for Linux FF-A Driver not accepting to have its own
+     * entry in the list before FF-A v1.2 was supported.
+     * This workaround is generally acceptable for other implementations
+     * as the specification was not completely clear on wether or not
+     * the requester endpoint information should be included or not
+     */
+    if ( curr_ctx->guest_vers >= FFA_VERSION_1_2 )
+    {
+        /* Add caller VM information */
+        info.id = curr_ctx->ffa_id;
+        info.execution_context = curr_ctx->num_vcpus;
+        info.partition_properties = FFA_PART_VM_PROP;
+        if ( curr_ctx->is_64bit )
+            info.partition_properties |= FFA_PART_PROP_AARCH64_STATE;
+
+        ret = ffa_copy_info(dst_buf, end_buf, &info, dst_size, sizeof(info));
+        if ( ret )
+            return ret;
+
+        count++;
+    }
+
+    if ( IS_ENABLED(CONFIG_FFA_VM_TO_VM) )
     {
         /*
-         * Do not include an entry for the caller VM as the spec is not
-         * clearly mandating it and it is not supported by Linux.
+         * There could potentially be a lot of VMs in the system and we could
+         * hold the CPU for long here.
+         * Right now there is no solution in FF-A specification to split
+         * the work in this case.
+         * TODO: Check how we could delay the work or have preemption checks.
          */
-        if ( dest_ctx != curr_ctx )
+        read_lock(&ffa_ctx_list_rwlock);
+        list_for_each_entry(dest_ctx, &ffa_ctx_head, ctx_list)
         {
-            /*
-             * We do not have UUID info for VMs so use
-             * the 1.0 structure so that we set UUIDs to
-             * zero using memset
-             */
-            struct ffa_partition_info_1_0 info;
-
-            if  ( dst_buf > (end_buf - dst_size) )
-            {
-                ret = FFA_RET_NO_MEMORY;
-                goto out;
-            }
+            /* Ignore the caller entry as it was already added */
+            if ( dest_ctx == curr_ctx )
+                continue;
 
-            /*
-             * Context might has been removed since we go it or being removed
-             * right now so we might return information for a VM not existing
-             * anymore. This is acceptable as we return a view of the system
-             * which could change at any time.
-             */
             info.id = dest_ctx->ffa_id;
             info.execution_context = dest_ctx->num_vcpus;
             info.partition_properties = FFA_PART_VM_PROP;
             if ( dest_ctx->is_64bit )
                 info.partition_properties |= FFA_PART_PROP_AARCH64_STATE;
 
-            memcpy(dst_buf, &info, MIN(sizeof(info), dst_size));
-
-            if ( dst_size > sizeof(info) )
-                memset(dst_buf + sizeof(info), 0,
-                       dst_size - sizeof(info));
+            ret = ffa_copy_info(dst_buf, end_buf, &info, dst_size,
+                                sizeof(info));
+            if ( ret )
+            {
+                read_unlock(&ffa_ctx_list_rwlock);
+                return ret;
+            }
 
-            dst_buf += dst_size;
             count++;
         }
+        read_unlock(&ffa_ctx_list_rwlock);
     }
-    *vm_count = count;
 
-out:
-    read_unlock(&ffa_ctx_list_rwlock);
+    *vm_count = count;
 
     return ret;
 }
@@ -217,16 +254,17 @@ void ffa_handle_partition_info_get(struct cpu_user_regs 
*regs)
     struct domain *d = current->domain;
     struct ffa_ctx *ctx = d->arch.tee;
     uint32_t flags = get_user_reg(regs, 5);
-    uint32_t uuid[4] = {
-        get_user_reg(regs, 1),
-        get_user_reg(regs, 2),
-        get_user_reg(regs, 3),
-        get_user_reg(regs, 4),
-    };
+    struct ffa_uuid uuid;
     uint32_t dst_size = 0;
     void *dst_buf, *end_buf;
     uint32_t ffa_vm_count = 0, ffa_sp_count = 0;
 
+    ffa_uuid_set(&uuid,
+             get_user_reg(regs, 1),
+             get_user_reg(regs, 2),
+             get_user_reg(regs, 3),
+             get_user_reg(regs, 4));
+
     /*
      * If the guest is v1.0, he does not get back the entry size so we must
      * use the v1.0 structure size in the destination buffer.
@@ -259,10 +297,23 @@ void ffa_handle_partition_info_get(struct cpu_user_regs 
*regs)
         }
 
         /*
-         * Do not count the caller VM as the spec is not clearly mandating it
-         * and it is not supported by Linux.
+         * We do not have protocol UUIDs for VMs so if a request has non Nil
+         * UUID we must return a vm_count of 0
          */
-        ffa_vm_count = get_ffa_vm_count() - 1;
+        if ( ffa_uuid_is_nil(uuid) )
+        {
+            ffa_vm_count = get_ffa_vm_count();
+
+            /*
+             * Workaround for Linux FF-A Driver not accepting to have its own
+             * entry in the list before FF-A v1.2 was supported.
+             * This workaround is generally acceptable for other 
implementations
+             * as the specification was not completely clear on wether or not
+             * the requester endpoint information should be included or not
+             */
+            if ( ctx->guest_vers < FFA_VERSION_1_2 )
+                ffa_vm_count -= 1;
+        }
 
         goto out;
     }
@@ -290,17 +341,15 @@ void ffa_handle_partition_info_get(struct cpu_user_regs 
*regs)
 
     if ( ffa_fw_supports_fid(FFA_PARTITION_INFO_GET) )
     {
-        ret = ffa_get_sp_partinfo(uuid, &ffa_sp_count, dst_buf, end_buf,
+        ret = ffa_get_sp_partinfo(uuid, &ffa_sp_count, &dst_buf, end_buf,
                                   dst_size);
 
         if ( ret )
             goto out_rx_release;
-
-        dst_buf += ffa_sp_count * dst_size;
     }
 
-    if ( IS_ENABLED(CONFIG_FFA_VM_TO_VM) )
-        ret = ffa_get_vm_partinfo(&ffa_vm_count, dst_buf, end_buf, dst_size);
+    ret = ffa_get_vm_partinfo(uuid, &ffa_vm_count, &dst_buf, end_buf,
+                              dst_size);
 
 out_rx_release:
     if ( ret )
@@ -309,7 +358,13 @@ out:
     if ( ret )
         ffa_set_regs_error(regs, ret);
     else
+    {
+        /* Size should be 0 on count request and was not supported in 1.0 */
+        if ( flags || ctx->guest_vers == FFA_VERSION_1_0 )
+            dst_size = 0;
+
         ffa_set_regs_success(regs, ffa_sp_count + ffa_vm_count, dst_size);
+    }
 }
 
 static int32_t ffa_direct_req_send_vm(uint16_t sp_id, uint16_t vm_id,
@@ -450,6 +505,7 @@ bool ffa_partinfo_init(void)
     uint32_t count;
     int32_t e;
     void *spmc_rx;
+    struct ffa_uuid nil_uuid = { .val = { 0ULL, 0ULL } };
 
     if ( !ffa_fw_supports_fid(FFA_PARTITION_INFO_GET) ||
          !ffa_fw_supports_fid(FFA_MSG_SEND_DIRECT_REQ_32))
@@ -459,7 +515,7 @@ bool ffa_partinfo_init(void)
     if (!spmc_rx)
         return false;
 
-    e = ffa_partition_info_get(NULL, 0, &count, &fpi_size);
+    e = ffa_partition_info_get(nil_uuid, 0, &count, &fpi_size);
     if ( e )
     {
         printk(XENLOG_ERR "ffa: Failed to get list of SPs: %d\n", e);
diff --git a/xen/arch/arm/tee/ffa_private.h b/xen/arch/arm/tee/ffa_private.h
index 9cae238f972c..c1dac09c75ca 100644
--- a/xen/arch/arm/tee/ffa_private.h
+++ b/xen/arch/arm/tee/ffa_private.h
@@ -306,6 +306,10 @@ struct ffa_mem_region {
     struct ffa_address_range address_range_array[];
 };
 
+struct ffa_uuid {
+    uint64_t val[2];
+};
+
 struct ffa_ctx_notif {
     /*
      * True if domain is reported by FFA_NOTIFICATION_INFO_GET to have
@@ -567,4 +571,21 @@ static inline bool ffa_fw_supports_fid(uint32_t fid)
     return test_bit(FFA_ABI_BITNUM(fid), ffa_fw_abi_supported);
 }
 
+static inline bool ffa_uuid_is_nil(struct ffa_uuid id)
+{
+    return id.val[0] == 0 && id.val[1] == 0;
+}
+
+static inline bool ffa_uuid_equal(struct ffa_uuid id1, struct ffa_uuid id2)
+{
+    return id1.val[0] == id2.val[0] && id1.val[1] == id2.val[1];
+}
+
+static inline void ffa_uuid_set(struct ffa_uuid *id, uint32_t val0,
+                                uint32_t val1, uint32_t val2, uint32_t val3)
+{
+    id->val[0] = ((uint64_t)val1 << 32U) | val0;
+    id->val[1] = ((uint64_t)val3 << 32U) | val2;
+}
+
 #endif /*__FFA_PRIVATE_H__*/
-- 
2.51.2


Reply via email to