@@ -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;
}
@@ -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
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@arm.com> --- 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(-)