[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH v5 2/6] xen/arm: ffa: Rework partinfo_get implementation



This patch is in preparation for VM to VM support in order to do the
changes on the SP handling part of partinfo_get before adding support
for the VM part.

This patches is doing the following changes:
- split partinfo_get into 3 functions to have the locking handling and
  proper exit on error handled more clearly
- add some potential overflow checks to validate the offset and sizes
  passed by the VM on partinfo call.
- Introduce a maximum number of SPs (for now set to 64) to prevent
  holding the CPU for too long in case there would be a lot of
  partitions in the secure world. The limit currently set is thought to
  be realistic for most use cases as 64 secure partitions is a very high
  number compared to current seen usage (more 3 or 4).
- fix include ordering in ffa_private.h to be in alphabetic order

Signed-off-by: Bertrand Marquis <bertrand.marquis@xxxxxxx>
---
Changes in v5:
- patch added
---
 xen/arch/arm/tee/ffa_partinfo.c | 201 +++++++++++++++++++-------------
 xen/arch/arm/tee/ffa_private.h  |  18 ++-
 2 files changed, 131 insertions(+), 88 deletions(-)

diff --git a/xen/arch/arm/tee/ffa_partinfo.c b/xen/arch/arm/tee/ffa_partinfo.c
index c0510ceb8338..e524445c6fb8 100644
--- a/xen/arch/arm/tee/ffa_partinfo.c
+++ b/xen/arch/arm/tee/ffa_partinfo.c
@@ -63,9 +63,95 @@ static int32_t ffa_partition_info_get(uint32_t *uuid, 
uint32_t flags,
     return ret;
 }
 
-void ffa_handle_partition_info_get(struct cpu_user_regs *regs)
+static int32_t ffa_get_sp_count(uint32_t *uuid, uint32_t *sp_count)
+{
+    uint32_t src_size;
+
+    return ffa_partition_info_get(uuid, FFA_PARTITION_INFO_GET_COUNT_FLAG,
+                                  sp_count, &src_size);
+}
+
+static int32_t ffa_get_sp_partinfo(uint32_t *uuid, uint32_t *sp_count,
+                                   void *dst_buf, void *end_buf,
+                                   uint32_t dst_size)
 {
     int32_t ret;
+    uint32_t src_size, real_sp_count;
+    void *src_buf = ffa_rx;
+    uint32_t count = 0;
+
+    /* Do we have a RX buffer with the SPMC */
+    if ( !ffa_rx )
+        return FFA_RET_DENIED;
+
+    /* We need to use the RX buffer to receive the list */
+    spin_lock(&ffa_rx_buffer_lock);
+
+    ret = ffa_partition_info_get(uuid, 0, &real_sp_count, &src_size);
+    if ( ret )
+        goto out;
+
+    /* We now own the RX buffer */
+
+    /* Validate the src_size we got */
+    if ( src_size < sizeof(struct ffa_partition_info_1_0) ||
+         src_size >= FFA_PAGE_SIZE )
+    {
+        ret = FFA_RET_NOT_SUPPORTED;
+        goto out_release;
+    }
+
+    /*
+     * Limit the maximum time we hold the CPU by limiting the number of SPs.
+     * We just ignore the extra ones as this is tested during init in
+     * ffa_partinfo_init so the only possible reason is SP have been added
+     * since boot.
+     */
+    if ( real_sp_count > FFA_MAX_NUM_SP )
+        real_sp_count = FFA_MAX_NUM_SP;
+
+    /* Make sure the data fits in our buffer */
+    if ( real_sp_count > (FFA_RXTX_PAGE_COUNT * FFA_PAGE_SIZE) / src_size )
+    {
+        ret = FFA_RET_NOT_SUPPORTED;
+        goto out_release;
+    }
+
+    for ( uint32_t sp_num = 0; sp_num < real_sp_count; sp_num++ )
+    {
+        struct ffa_partition_info_1_1 *fpi = src_buf;
+
+        /* 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_release;
+            }
+
+            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);
+
+            dst_buf += dst_size;
+            count++;
+        }
+
+        src_buf += src_size;
+    }
+
+    *sp_count = count;
+
+out_release:
+    ffa_hyp_rx_release();
+out:
+    spin_unlock(&ffa_rx_buffer_lock);
+    return ret;
+}
+void ffa_handle_partition_info_get(struct cpu_user_regs *regs)
+{
+    int32_t ret = FFA_RET_OK;
     struct domain *d = current->domain;
     struct ffa_ctx *ctx = d->arch.tee;
     uint32_t flags = get_user_reg(regs, 5);
@@ -75,8 +161,8 @@ void ffa_handle_partition_info_get(struct cpu_user_regs 
*regs)
         get_user_reg(regs, 3),
         get_user_reg(regs, 4),
     };
-    uint32_t src_size, dst_size;
-    void *dst_buf;
+    uint32_t dst_size = 0;
+    void *dst_buf, *end_buf;
     uint32_t ffa_sp_count = 0;
 
     /*
@@ -89,31 +175,26 @@ void ffa_handle_partition_info_get(struct cpu_user_regs 
*regs)
     else
         dst_size = sizeof(struct ffa_partition_info_1_1);
 
-    /*
-     * FF-A v1.0 has w5 MBZ while v1.1 allows
-     * FFA_PARTITION_INFO_GET_COUNT_FLAG to be non-zero.
-     *
-     * FFA_PARTITION_INFO_GET_COUNT is only using registers and not the
-     * rxtx buffer so do the partition_info_get directly.
-     */
-    if ( flags == FFA_PARTITION_INFO_GET_COUNT_FLAG &&
-         ctx->guest_vers == FFA_VERSION_1_1 )
+    /* Only count requested */
+    if ( flags )
     {
+        /*
+         * FF-A v1.0 has w5 MBZ while v1.1 allows
+         * FFA_PARTITION_INFO_GET_COUNT_FLAG to be non-zero.
+         */
+        if ( ctx->guest_vers == FFA_VERSION_1_0 ||
+                flags != FFA_PARTITION_INFO_GET_COUNT_FLAG )
+        {
+            ret = FFA_RET_INVALID_PARAMETERS;
+            goto out;
+        }
+
         if ( ffa_fw_supports_fid(FFA_PARTITION_INFO_GET) )
-            ret = ffa_partition_info_get(uuid, flags, &ffa_sp_count,
-                                        &src_size);
-        else
-            ret = FFA_RET_OK;
+            ret = ffa_get_sp_count(uuid, &ffa_sp_count);
 
         goto out;
     }
 
-    if ( flags )
-    {
-        ret = FFA_RET_INVALID_PARAMETERS;
-        goto out;
-    }
-
     if ( !ffa_fw_supports_fid(FFA_PARTITION_INFO_GET) )
     {
         /* Just give an empty partition list to the caller */
@@ -121,80 +202,33 @@ void ffa_handle_partition_info_get(struct cpu_user_regs 
*regs)
         goto out;
     }
 
+    /* Get the RX buffer to write the list of partitions */
     ret = ffa_rx_acquire(d);
     if ( ret != FFA_RET_OK )
         goto out;
 
     dst_buf = ctx->rx;
+    end_buf = ctx->rx + ctx->page_count * FFA_PAGE_SIZE;
 
-    if ( !ffa_rx )
-    {
-        ret = FFA_RET_DENIED;
-        goto out_rx_release;
-    }
-
-    spin_lock(&ffa_rx_buffer_lock);
-
-    ret = ffa_partition_info_get(uuid, 0, &ffa_sp_count, &src_size);
-
-    if ( ret )
-        goto out_rx_hyp_unlock;
+    /* An entry should be smaller than a page */
+    BUILD_BUG_ON(sizeof(struct ffa_partition_info_1_1) > FFA_PAGE_SIZE);
 
     /*
-     * ffa_partition_info_get() succeeded so we now own the RX buffer we
-     * share with the SPMC. We must give it back using ffa_hyp_rx_release()
-     * once we've copied the content.
+     * Check for overflow and that we can at least store one entry.
+     * page_count cannot be 0 so we have at least one page.
      */
-
-    /* we cannot have a size smaller than 1.0 structure */
-    if ( src_size < sizeof(struct ffa_partition_info_1_0) )
-    {
-        ret = FFA_RET_NOT_SUPPORTED;
-        goto out_rx_hyp_release;
-    }
-
-    if ( ctx->page_count * FFA_PAGE_SIZE < ffa_sp_count * dst_size )
+    if ( dst_buf >= end_buf || dst_buf > (end_buf - dst_size) )
     {
-        ret = FFA_RET_NO_MEMORY;
-        goto out_rx_hyp_release;
+        ret = FFA_RET_INVALID_PARAMETERS;
+        goto out_rx_release;
     }
 
-    if ( ffa_sp_count > 0 )
-    {
-        uint32_t n, n_limit = ffa_sp_count;
-        void *src_buf = ffa_rx;
-
-        /* copy the secure partitions info */
-        for ( n = 0; n < n_limit; n++ )
-        {
-            struct ffa_partition_info_1_1 *fpi = src_buf;
-
-            /* filter out SP not following bit 15 convention if any */
-            if ( FFA_ID_IS_SECURE(fpi->id) )
-            {
-                memcpy(dst_buf, src_buf, dst_size);
-                dst_buf += dst_size;
-            }
-            else
-                ffa_sp_count--;
+    ret = ffa_get_sp_partinfo(uuid, &ffa_sp_count, dst_buf, end_buf,
+                              dst_size);
 
-            src_buf += src_size;
-        }
-    }
 
-out_rx_hyp_release:
-    ffa_hyp_rx_release();
-out_rx_hyp_unlock:
-    spin_unlock(&ffa_rx_buffer_lock);
 out_rx_release:
-    /*
-     * The calling VM RX buffer only contains data to be used by the VM if the
-     * call was successful, in which case the VM has to release the buffer
-     * once it has used the data.
-     * If something went wrong during the call, we have to release the RX
-     * buffer back to the SPMC as the VM will not do it.
-     */
-    if ( ret != FFA_RET_OK )
+    if ( ret )
         ffa_rx_release(d);
 out:
     if ( ret )
@@ -353,9 +387,10 @@ bool ffa_partinfo_init(void)
         goto out;
     }
 
-    if ( count >= UINT16_MAX )
+    if ( count >= FFA_MAX_NUM_SP )
     {
-        printk(XENLOG_ERR "ffa: Impossible number of SPs: %u\n", count);
+        printk(XENLOG_ERR "ffa: More SPs than the maximum supported: %u - 
%u\n",
+               count, FFA_MAX_NUM_SP);
         goto out;
     }
 
diff --git a/xen/arch/arm/tee/ffa_private.h b/xen/arch/arm/tee/ffa_private.h
index c4cd65538908..0a9c1082db28 100644
--- a/xen/arch/arm/tee/ffa_private.h
+++ b/xen/arch/arm/tee/ffa_private.h
@@ -6,15 +6,15 @@
 #ifndef __FFA_PRIVATE_H__
 #define __FFA_PRIVATE_H__
 
+#include <xen/bitmap.h>
 #include <xen/const.h>
-#include <xen/sizes.h>
-#include <xen/types.h>
-#include <xen/mm.h>
 #include <xen/list.h>
-#include <xen/spinlock.h>
+#include <xen/mm.h>
 #include <xen/sched.h>
+#include <xen/sizes.h>
+#include <xen/spinlock.h>
 #include <xen/time.h>
-#include <xen/bitmap.h>
+#include <xen/types.h>
 
 /* Error codes */
 #define FFA_RET_OK                      0
@@ -108,6 +108,14 @@
  */
 #define FFA_CTX_TEARDOWN_DELAY          SECONDS(1)
 
+/*
+ * The maximum number of Secure partitions we support for partinfo_get.
+ * This prevents holding the CPU during potentially to long time during
+ * a partinfo_get call. Value choosen seems realistic for any configuration
+ * but can be incremented here if needed.
+ */
+#define FFA_MAX_NUM_SP                  64
+
 /*
  * We rely on the convention suggested but not mandated by the FF-A
  * specification that secure world endpoint identifiers have the bit 15
-- 
2.47.1




 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.