Hi,

On Wed, May 21, 2025 at 5:11 PM Bertrand Marquis
<bertrand.marq...@arm.com> wrote:
>
> Hi Jens,
>
> > On 21 May 2025, at 16:54, Jens Wiklander <jens.wiklan...@linaro.org> wrote:
> >
> > Hi Bertrand,
> >
> > On Wed, Apr 16, 2025 at 9:40 AM Bertrand Marquis
> > <bertrand.marq...@arm.com> wrote:
> >>
> >> Create a CONFIG_FFA_VM_TO_VM parameter to activate FFA communication
> >> between VMs.
> >> When activated list VMs in the system with FF-A support in part_info_get.
> >>
> >> When VM to VM is activated, Xen will be tainted as Insecure and a
> >> message is displayed to the user during the boot as there is no
> >> filtering of VMs in FF-A so any VM can communicate or see any other VM
> >> in the system.
> >>
> >> WARNING: There is no filtering for now and all VMs are listed !!
> >>
> >> This patch is reorganizing the ffa_ctx structure to make clear which
> >> lock is protecting what parts.
> >>
> >> This patch is introducing a chain list of the ffa_ctx with a FFA Version
> >> negociated allowing to create the partinfo results for VMs without
> >> taking a lock on the global domain list in Xen.
> >>
> >> Signed-off-by: Bertrand Marquis <bertrand.marq...@arm.com>
> >> ---
> >> Changes in v5:
> >> - remove invalid comment about 1.1 firmware support
> >> - rename variables from d and dom to curr_d and dest_d (Julien)
> >> - add a TODO in the code for potential holding for long of the CPU
> >>  (Julien)
> >> - use an atomic global variable to store the number of VMs instead of
> >>  recomputing the value each time (Julien)
> >> - add partinfo information in ffa_ctx (id, cpus and 64bit) and create a
> >>  chain list of ctx. Use this chain list to create the partinfo result
> >>  without holding a global lock to prevent concurrency issues.
> >> - Move some changes in a preparation patch modifying partinfo for sps to
> >>  reduce this patch size and make the review easier
> >> Changes in v4:
> >> - properly handle SPMC version 1.0 header size case in partinfo_get
> >> - switch to local counting variables instead of *pointer += 1 form
> >> - coding style issue with missing spaces in if ()
> >> Changes in v3:
> >> - break partinfo_get in several sub functions to make the implementation
> >>  easier to understand and lock handling easier
> >> - rework implementation to check size along the way and prevent previous
> >>  implementation limits which had to check that the number of VMs or SPs
> >>  did not change
> >> - taint Xen as INSECURE when VM to VM is enabled
> >> Changes in v2:
> >> - Switch ifdef to IS_ENABLED
> >> - dom was not switched to d as requested by Jan because there is already
> >>  a variable d pointing to the current domain and it must not be
> >>  shadowed.
> >> ---
> >> xen/arch/arm/tee/Kconfig        |  11 ++++
> >> xen/arch/arm/tee/ffa.c          |  47 +++++++++++++-
> >> xen/arch/arm/tee/ffa_partinfo.c |  95 ++++++++++++++++++++++++---
> >> xen/arch/arm/tee/ffa_private.h  | 112 ++++++++++++++++++++++++++------
> >> 4 files changed, 233 insertions(+), 32 deletions(-)
> >>
> >> diff --git a/xen/arch/arm/tee/Kconfig b/xen/arch/arm/tee/Kconfig
> >> index c5b0f88d7522..88a4c4c99154 100644
> >> --- a/xen/arch/arm/tee/Kconfig
> >> +++ b/xen/arch/arm/tee/Kconfig
> >> @@ -28,5 +28,16 @@ config FFA
> >>
> >>          [1] https://developer.arm.com/documentation/den0077/latest
> >>
> >> +config FFA_VM_TO_VM
> >> +    bool "Enable FF-A between VMs (UNSUPPORTED)" if UNSUPPORTED
> >> +    default n
> >> +    depends on FFA
> >> +    help
> >> +      This option enables to use FF-A between VMs.
> >> +      This is experimental and there is no access control so any
> >> +      guest can communicate with any other guest.
> >> +
> >> +      If unsure, say N.
> >> +
> >> endmenu
> >>
> >> diff --git a/xen/arch/arm/tee/ffa.c b/xen/arch/arm/tee/ffa.c
> >> index 3bbdd7168a6b..c1c4c0957091 100644
> >> --- a/xen/arch/arm/tee/ffa.c
> >> +++ b/xen/arch/arm/tee/ffa.c
> >> @@ -118,6 +118,13 @@ void *ffa_tx __read_mostly;
> >> DEFINE_SPINLOCK(ffa_rx_buffer_lock);
> >> DEFINE_SPINLOCK(ffa_tx_buffer_lock);
> >>
> >> +struct list_head ffa_ctx_head;
> >> +/* Lock to protect addition/removal in ffa_ctx_head */
> >> +DEFINE_SPINLOCK(ffa_ctx_list_lock);
> >> +
> >> +#ifdef CONFIG_FFA_VM_TO_VM
> >> +atomic_t ffa_vm_count;
> >> +#endif
> >>
> >> /* Used to track domains that could not be torn down immediately. */
> >> static struct timer ffa_teardown_timer;
> >> @@ -160,10 +167,21 @@ static void handle_version(struct cpu_user_regs 
> >> *regs)
> >>      */
> >>     if ( FFA_VERSION_MAJOR(vers) == FFA_MY_VERSION_MAJOR )
> >>     {
> >> +        uint32_t old_vers = ACCESS_ONCE(ctx->guest_vers);
> >> +
> >>         if ( FFA_VERSION_MINOR(vers) > FFA_MY_VERSION_MINOR )
> >> -            ctx->guest_vers = FFA_MY_VERSION;
> >> +            ACCESS_ONCE(ctx->guest_vers) = FFA_MY_VERSION;
> >>         else
> >> -            ctx->guest_vers = vers;
> >> +            ACCESS_ONCE(ctx->guest_vers) = vers;
> >
> > What is the ACCESS_ONCE() scheme intended for?
> >
> >> +
> >> +        if ( IS_ENABLED(CONFIG_FFA_VM_TO_VM) && !old_vers )
> >
> > If handle_version() is called concurrently on two CPUs, it might be
> > possible for a context to be added twice.
> > How about a separate flag to indicate whether a context has been added
> > to the list?
>
> I wanted to avoid having guest_vers as atomic or introduce an other lock.
> But i think now that this is kind of impossible to avoid and this way to do
> it is wrong.
>
> I will take the context lock to do this processing to avoid this corner case
> and remove the ACCESS_ONCE to protect modifications to guest_vers:
>
> diff --git a/xen/arch/arm/tee/ffa.c b/xen/arch/arm/tee/ffa.c
> index b86c88cefa8c..306edb97863a 100644
> --- a/xen/arch/arm/tee/ffa.c
> +++ b/xen/arch/arm/tee/ffa.c
> @@ -158,6 +158,7 @@ static void handle_version(struct cpu_user_regs *regs)
>      struct domain *d = current->domain;
>      struct ffa_ctx *ctx = d->arch.tee;
>      uint32_t vers = get_user_reg(regs, 1);
> +    uint32_t old_vers;
>
>      /*
>       * Guest will use the version it requested if it is our major and minor
> @@ -167,12 +168,14 @@ static void handle_version(struct cpu_user_regs *regs)
>       */
>      if ( FFA_VERSION_MAJOR(vers) == FFA_MY_VERSION_MAJOR )
>      {
> -        uint32_t old_vers = ACCESS_ONCE(ctx->guest_vers);
> +        spin_lock(&ctx->lock);
> +        old_vers = ctx->guest_vers;
>
>          if ( FFA_VERSION_MINOR(vers) > FFA_MY_VERSION_MINOR )
> -            ACCESS_ONCE(ctx->guest_vers) = FFA_MY_VERSION;
> +           ctx->guest_vers = FFA_MY_VERSION;
>          else
> -            ACCESS_ONCE(ctx->guest_vers) = vers;
> +           ctx->guest_vers = vers;
> +        spin_unlock(&ctx->lock);
>
>          if ( IS_ENABLED(CONFIG_FFA_VM_TO_VM) && !old_vers )
>          {
>
> What do you think ?

That works. It might be worth adding a comment that ctx->guest_vers is
accessed unlocked elsewhere, and why that is OK.

Cheers,
Jens

Reply via email to