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

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



Hi Bertrand,

On Wed, Apr 16, 2025 at 9:40 AM Bertrand Marquis
<bertrand.marquis@xxxxxxx> wrote:
>
> 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;
> +}

Please add an empty line before the next function. With that fixed:
Reviewed-by: Jens Wiklander <jens.wiklander@xxxxxxxxxx>

Cheers,
Jens

> +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®.