diff mbox series

[XEN,4/6] xen/arm: ffa: separate partition info get routines

Message ID 20240325093904.3466092-5-jens.wiklander@linaro.org (mailing list archive)
State New
Headers show
Series FF-A mediator reorganisation | expand

Commit Message

Jens Wiklander March 25, 2024, 9:39 a.m. UTC
Move partition info get routines into a separate file for easier
navigation in the source code.

Add ffa_partinfo_init(), ffa_partinfo_domain_init(), and
ffa_partinfo_domain_destroy() to handle the ffa_partinfo internal things
on initialization and teardown.

Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
---
 xen/arch/arm/tee/Makefile       |   1 +
 xen/arch/arm/tee/ffa.c          | 359 +-----------------------------
 xen/arch/arm/tee/ffa_partinfo.c | 373 ++++++++++++++++++++++++++++++++
 xen/arch/arm/tee/ffa_private.h  |  14 +-
 4 files changed, 398 insertions(+), 349 deletions(-)
 create mode 100644 xen/arch/arm/tee/ffa_partinfo.c

Comments

Bertrand Marquis March 27, 2024, 1:42 p.m. UTC | #1
Hi Jens,

> On 25 Mar 2024, at 10:39, Jens Wiklander <jens.wiklander@linaro.org> wrote:
> 
> Move partition info get routines into a separate file for easier
> navigation in the source code.
> 
> Add ffa_partinfo_init(), ffa_partinfo_domain_init(), and
> ffa_partinfo_domain_destroy() to handle the ffa_partinfo internal things
> on initialization and teardown.
> 
> Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>

Reviewed-by: Bertrand Marquis <bertrand.marquis@arm.com>

Cheers
Bertrand

> ---
> xen/arch/arm/tee/Makefile       |   1 +
> xen/arch/arm/tee/ffa.c          | 359 +-----------------------------
> xen/arch/arm/tee/ffa_partinfo.c | 373 ++++++++++++++++++++++++++++++++
> xen/arch/arm/tee/ffa_private.h  |  14 +-
> 4 files changed, 398 insertions(+), 349 deletions(-)
> create mode 100644 xen/arch/arm/tee/ffa_partinfo.c
> 
> diff --git a/xen/arch/arm/tee/Makefile b/xen/arch/arm/tee/Makefile
> index 0e683d23aa9d..be644fba8055 100644
> --- a/xen/arch/arm/tee/Makefile
> +++ b/xen/arch/arm/tee/Makefile
> @@ -1,4 +1,5 @@
> obj-$(CONFIG_FFA) += ffa.o
> obj-$(CONFIG_FFA) += ffa_shm.o
> +obj-$(CONFIG_FFA) += ffa_partinfo.o
> obj-y += tee.o
> obj-$(CONFIG_OPTEE) += optee.o
> diff --git a/xen/arch/arm/tee/ffa.c b/xen/arch/arm/tee/ffa.c
> index db36292dc52f..7a2803881420 100644
> --- a/xen/arch/arm/tee/ffa.c
> +++ b/xen/arch/arm/tee/ffa.c
> @@ -70,20 +70,6 @@
>  * structs ending with _1_1 are defined in FF-A-1.1-REL0.
>  */
> 
> -/* Partition information descriptor */
> -struct ffa_partition_info_1_0 {
> -    uint16_t id;
> -    uint16_t execution_context;
> -    uint32_t partition_properties;
> -};
> -
> -struct ffa_partition_info_1_1 {
> -    uint16_t id;
> -    uint16_t execution_context;
> -    uint32_t partition_properties;
> -    uint8_t uuid[16];
> -};
> -
> /* Endpoint RX/TX descriptor */
> struct ffa_endpoint_rxtx_descriptor_1_0 {
>     uint16_t sender_id;
> @@ -102,11 +88,6 @@ struct ffa_endpoint_rxtx_descriptor_1_1 {
> /* Negotiated FF-A version to use with the SPMC */
> static uint32_t __ro_after_init ffa_version;
> 
> -/* SPs subscribing to VM_CREATE and VM_DESTROYED events */
> -static uint16_t *subscr_vm_created __read_mostly;
> -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;
> 
> /*
>  * Our rx/tx buffers shared with the SPMC. FFA_RXTX_PAGE_COUNT is the
> @@ -170,90 +151,6 @@ static int32_t ffa_rxtx_map(paddr_t tx_addr, paddr_t rx_addr,
>     return ffa_simple_call(FFA_RXTX_MAP_64, tx_addr, rx_addr, page_count, 0);
> }
> 
> -static int32_t ffa_partition_info_get(uint32_t w1, uint32_t w2, uint32_t w3,
> -                                      uint32_t w4, uint32_t w5,
> -                                      uint32_t *count, uint32_t *fpi_size)
> -{
> -    const struct arm_smccc_1_2_regs arg = {
> -        .a0 = FFA_PARTITION_INFO_GET,
> -        .a1 = w1,
> -        .a2 = w2,
> -        .a3 = w3,
> -        .a4 = w4,
> -        .a5 = w5,
> -    };
> -    struct arm_smccc_1_2_regs resp;
> -    uint32_t ret;
> -
> -    arm_smccc_1_2_smc(&arg, &resp);
> -
> -    ret = ffa_get_ret_code(&resp);
> -    if ( !ret )
> -    {
> -        *count = resp.a2;
> -        *fpi_size = resp.a3;
> -    }
> -
> -    return ret;
> -}
> -
> -static int32_t ffa_rx_release(void)
> -{
> -    return ffa_simple_call(FFA_RX_RELEASE, 0, 0, 0, 0);
> -}
> -
> -static int32_t ffa_direct_req_send_vm(uint16_t sp_id, uint16_t vm_id,
> -                                      uint8_t msg)
> -{
> -    uint32_t exp_resp = FFA_MSG_FLAG_FRAMEWORK;
> -    unsigned int retry_count = 0;
> -    int32_t res;
> -
> -    if ( msg == FFA_MSG_SEND_VM_CREATED )
> -        exp_resp |= FFA_MSG_RESP_VM_CREATED;
> -    else if ( msg == FFA_MSG_SEND_VM_DESTROYED )
> -        exp_resp |= FFA_MSG_RESP_VM_DESTROYED;
> -    else
> -        return FFA_RET_INVALID_PARAMETERS;
> -
> -    do {
> -        const struct arm_smccc_1_2_regs arg = {
> -            .a0 = FFA_MSG_SEND_DIRECT_REQ_32,
> -            .a1 = sp_id,
> -            .a2 = FFA_MSG_FLAG_FRAMEWORK | msg,
> -            .a5 = vm_id,
> -        };
> -        struct arm_smccc_1_2_regs resp;
> -
> -        arm_smccc_1_2_smc(&arg, &resp);
> -        if ( resp.a0 != FFA_MSG_SEND_DIRECT_RESP_32 || resp.a2 != exp_resp )
> -        {
> -            /*
> -             * This is an invalid response, likely due to some error in the
> -             * implementation of the ABI.
> -             */
> -            return FFA_RET_INVALID_PARAMETERS;
> -        }
> -        res = resp.a3;
> -        if ( ++retry_count > 10 )
> -        {
> -            /*
> -             * TODO
> -             * FFA_RET_INTERRUPTED means that the SPMC has a pending
> -             * non-secure interrupt, we need a way of delivering that
> -             * non-secure interrupt.
> -             * FFA_RET_RETRY is the SP telling us that it's temporarily
> -             * blocked from handling the direct request, we need a generic
> -             * way to deal with this.
> -             * For now in both cases, give up after a few retries.
> -             */
> -            return res;
> -        }
> -    } while ( res == FFA_RET_INTERRUPTED || res == FFA_RET_RETRY );
> -
> -    return res;
> -}
> -
> static void handle_version(struct cpu_user_regs *regs)
> {
>     struct domain *d = current->domain;
> @@ -371,88 +268,6 @@ static uint32_t ffa_handle_rxtx_unmap(void)
>     return FFA_RET_OK;
> }
> 
> -static int32_t ffa_handle_partition_info_get(uint32_t w1, uint32_t w2,
> -                                             uint32_t w3, uint32_t w4,
> -                                             uint32_t w5, uint32_t *count,
> -                                             uint32_t *fpi_size)
> -{
> -    int32_t ret = FFA_RET_DENIED;
> -    struct domain *d = current->domain;
> -    struct ffa_ctx *ctx = d->arch.tee;
> -
> -    /*
> -     * 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 ( w5 == FFA_PARTITION_INFO_GET_COUNT_FLAG &&
> -         ctx->guest_vers == FFA_VERSION_1_1 )
> -        return ffa_partition_info_get(w1, w2, w3, w4, w5, count, fpi_size);
> -    if ( w5 )
> -        return FFA_RET_INVALID_PARAMETERS;
> -
> -    if ( !ffa_rx )
> -        return FFA_RET_DENIED;
> -
> -    if ( !spin_trylock(&ctx->rx_lock) )
> -        return FFA_RET_BUSY;
> -
> -    if ( !ctx->page_count || !ctx->rx_is_free )
> -        goto out;
> -    spin_lock(&ffa_rx_buffer_lock);
> -    ret = ffa_partition_info_get(w1, w2, w3, w4, w5, count, fpi_size);
> -    if ( ret )
> -        goto out_rx_buf_unlock;
> -    /*
> -     * ffa_partition_info_get() succeeded so we now own the RX buffer we
> -     * share with the SPMC. We must give it back using ffa_rx_release()
> -     * once we've copied the content.
> -     */
> -
> -    if ( ctx->guest_vers == FFA_VERSION_1_0 )
> -    {
> -        size_t n;
> -        struct ffa_partition_info_1_1 *src = ffa_rx;
> -        struct ffa_partition_info_1_0 *dst = ctx->rx;
> -
> -        if ( ctx->page_count * FFA_PAGE_SIZE < *count * sizeof(*dst) )
> -        {
> -            ret = FFA_RET_NO_MEMORY;
> -            goto out_rx_release;
> -        }
> -
> -        for ( n = 0; n < *count; n++ )
> -        {
> -            dst[n].id = src[n].id;
> -            dst[n].execution_context = src[n].execution_context;
> -            dst[n].partition_properties = src[n].partition_properties;
> -        }
> -    }
> -    else
> -    {
> -        size_t sz = *count * *fpi_size;
> -
> -        if ( ctx->page_count * FFA_PAGE_SIZE < sz )
> -        {
> -            ret = FFA_RET_NO_MEMORY;
> -            goto out_rx_release;
> -        }
> -
> -        memcpy(ctx->rx, ffa_rx, sz);
> -    }
> -    ctx->rx_is_free = false;
> -out_rx_release:
> -    ffa_rx_release();
> -out_rx_buf_unlock:
> -    spin_unlock(&ffa_rx_buffer_lock);
> -out:
> -    spin_unlock(&ctx->rx_lock);
> -
> -    return ret;
> -}
> -
> static int32_t ffa_handle_rx_release(void)
> {
>     int32_t ret = FFA_RET_DENIED;
> @@ -604,46 +419,9 @@ static bool ffa_handle_call(struct cpu_user_regs *regs)
>     }
> }
> 
> -static bool is_in_subscr_list(const uint16_t *subscr, uint16_t start,
> -                              uint16_t end, uint16_t sp_id)
> -{
> -    unsigned int n;
> -
> -    for ( n = start; n < end; n++ )
> -    {
> -        if ( subscr[n] == sp_id )
> -            return true;
> -    }
> -
> -    return false;
> -}
> -
> -static void vm_destroy_bitmap_init(struct ffa_ctx *ctx,
> -                                   unsigned int create_signal_count)
> -{
> -    unsigned int n;
> -
> -    for ( n = 0; n < subscr_vm_destroyed_count; n++ )
> -    {
> -        /*
> -         * Skip SPs subscribed to the VM created event that never was
> -         * notified of the VM creation due to an error during
> -         * ffa_domain_init().
> -         */
> -        if ( is_in_subscr_list(subscr_vm_created, create_signal_count,
> -                               subscr_vm_created_count,
> -                               subscr_vm_destroyed[n]) )
> -            continue;
> -
> -        set_bit(n, ctx->vm_destroy_bitmap);
> -    }
> -}
> -
> static int ffa_domain_init(struct domain *d)
> {
>     struct ffa_ctx *ctx;
> -    unsigned int n;
> -    int32_t res;
> 
>     if ( !ffa_version )
>         return -ENODEV;
> @@ -654,8 +432,7 @@ static int ffa_domain_init(struct domain *d)
>     if ( d->domain_id >= UINT16_MAX)
>         return -ERANGE;
> 
> -    ctx = xzalloc_flex_struct(struct ffa_ctx, vm_destroy_bitmap,
> -                              BITS_TO_LONGS(subscr_vm_destroyed_count));
> +    ctx = xzalloc(struct ffa_ctx);
>     if ( !ctx )
>         return -ENOMEM;
> 
> @@ -663,66 +440,28 @@ static int ffa_domain_init(struct domain *d)
>     ctx->teardown_d = d;
>     INIT_LIST_HEAD(&ctx->shm_list);
> 
> -    for ( n = 0; n < subscr_vm_created_count; n++ )
> -    {
> -        res = ffa_direct_req_send_vm(subscr_vm_created[n], ffa_get_vm_id(d),
> -                                     FFA_MSG_SEND_VM_CREATED);
> -        if ( res )
> -        {
> -            printk(XENLOG_ERR "ffa: Failed to report creation of vm_id %u to  %u: res %d\n",
> -                   ffa_get_vm_id(d), subscr_vm_created[n], res);
> -            break;
> -        }
> -    }
> -    vm_destroy_bitmap_init(ctx, n);
> -    if ( n != subscr_vm_created_count )
> +    /*
> +     * ffa_domain_teardown() will be called if ffa_domain_init() returns an
> +     * error, so no need for cleanup in this function.
> +     */
> +
> +    if ( !ffa_partinfo_domain_init(d) )
>         return -EIO;
> 
>     return 0;
> }
> 
> -static void send_vm_destroyed(struct domain *d)
> -{
> -    struct ffa_ctx *ctx = d->arch.tee;
> -    unsigned int n;
> -    int32_t res;
> -
> -    for ( n = 0; n < subscr_vm_destroyed_count; n++ )
> -    {
> -        if ( !test_bit(n, ctx->vm_destroy_bitmap) )
> -            continue;
> -
> -        res = ffa_direct_req_send_vm(subscr_vm_destroyed[n], ffa_get_vm_id(d),
> -                                     FFA_MSG_SEND_VM_DESTROYED);
> -
> -        if ( res )
> -        {
> -            printk(XENLOG_ERR "%pd: ffa: Failed to report destruction of vm_id %u to %u: res %d\n",
> -                   d, ffa_get_vm_id(d), subscr_vm_destroyed[n], res);
> -        }
> -
> -        /*
> -         * For these two error codes the hypervisor is expected to resend
> -         * the destruction message. For the rest it is expected that the
> -         * error is permanent and that is doesn't help to resend the
> -         * destruction message.
> -         */
> -        if ( res != FFA_RET_INTERRUPTED && res != FFA_RET_RETRY )
> -            clear_bit(n, ctx->vm_destroy_bitmap);
> -    }
> -}
> -
> static void ffa_domain_teardown_continue(struct ffa_ctx *ctx, bool first_time)
> {
>     struct ffa_ctx *next_ctx = NULL;
>     bool retry = false;
> 
> -    send_vm_destroyed(ctx->teardown_d);
> +    if ( !ffa_partinfo_domain_destroy(ctx->teardown_d) )
> +        retry = true;
>     if ( !ffa_shm_domain_destroy(ctx->teardown_d) )
>         retry = true;
> 
> -    if ( retry ||
> -         !bitmap_empty(ctx->vm_destroy_bitmap, subscr_vm_destroyed_count) )
> +    if ( retry )
>     {
>         printk(XENLOG_G_INFO "%pd: ffa: Remaining cleanup, retrying\n", ctx->teardown_d);
> 
> @@ -796,82 +535,6 @@ static int ffa_relinquish_resources(struct domain *d)
>     return 0;
> }
> 
> -static void uninit_subscribers(void)
> -{
> -        subscr_vm_created_count = 0;
> -        subscr_vm_destroyed_count = 0;
> -        XFREE(subscr_vm_created);
> -        XFREE(subscr_vm_destroyed);
> -}
> -
> -static bool init_subscribers(struct ffa_partition_info_1_1 *fpi, uint16_t count)
> -{
> -    uint16_t n;
> -    uint16_t c_pos;
> -    uint16_t d_pos;
> -
> -    subscr_vm_created_count = 0;
> -    subscr_vm_destroyed_count = 0;
> -    for ( n = 0; n < count; n++ )
> -    {
> -        if ( fpi[n].partition_properties & FFA_PART_PROP_NOTIF_CREATED )
> -            subscr_vm_created_count++;
> -        if ( fpi[n].partition_properties & FFA_PART_PROP_NOTIF_DESTROYED )
> -            subscr_vm_destroyed_count++;
> -    }
> -
> -    if ( subscr_vm_created_count )
> -        subscr_vm_created = xzalloc_array(uint16_t, subscr_vm_created_count);
> -    if ( subscr_vm_destroyed_count )
> -        subscr_vm_destroyed = xzalloc_array(uint16_t,
> -                                            subscr_vm_destroyed_count);
> -    if ( (subscr_vm_created_count && !subscr_vm_created) ||
> -         (subscr_vm_destroyed_count && !subscr_vm_destroyed) )
> -    {
> -        printk(XENLOG_ERR "ffa: Failed to allocate subscription lists\n");
> -        uninit_subscribers();
> -        return false;
> -    }
> -
> -    for ( c_pos = 0, d_pos = 0, n = 0; n < count; n++ )
> -    {
> -        if ( fpi[n].partition_properties & FFA_PART_PROP_NOTIF_CREATED )
> -            subscr_vm_created[c_pos++] = fpi[n].id;
> -        if ( fpi[n].partition_properties & FFA_PART_PROP_NOTIF_DESTROYED )
> -            subscr_vm_destroyed[d_pos++] = fpi[n].id;
> -    }
> -
> -    return true;
> -}
> -
> -static bool init_sps(void)
> -{
> -    bool ret = false;
> -    uint32_t fpi_size;
> -    uint32_t count;
> -    int e;
> -
> -    e = ffa_partition_info_get(0, 0, 0, 0, 0, &count, &fpi_size);
> -    if ( e )
> -    {
> -        printk(XENLOG_ERR "ffa: Failed to get list of SPs: %d\n", e);
> -        goto out;
> -    }
> -
> -    if ( count >= UINT16_MAX )
> -    {
> -        printk(XENLOG_ERR "ffa: Impossible number of SPs: %u\n", count);
> -        goto out;
> -    }
> -
> -    ret = init_subscribers(ffa_rx, count);
> -
> -out:
> -    ffa_rx_release();
> -
> -    return ret;
> -}
> -
> static bool ffa_probe(void)
> {
>     uint32_t vers;
> @@ -949,7 +612,7 @@ static bool ffa_probe(void)
>     }
>     ffa_version = vers;
> 
> -    if ( !init_sps() )
> +    if ( !ffa_partinfo_init() )
>         goto err_free_ffa_tx;
> 
>     INIT_LIST_HEAD(&ffa_teardown_head);
> diff --git a/xen/arch/arm/tee/ffa_partinfo.c b/xen/arch/arm/tee/ffa_partinfo.c
> new file mode 100644
> index 000000000000..dc1059584828
> --- /dev/null
> +++ b/xen/arch/arm/tee/ffa_partinfo.c
> @@ -0,0 +1,373 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (C) 2024  Linaro Limited
> + */
> +
> +#include <xen/const.h>
> +#include <xen/sizes.h>
> +#include <xen/types.h>
> +
> +#include <asm/smccc.h>
> +#include <asm/regs.h>
> +
> +#include "ffa_private.h"
> +
> +/* Partition information descriptor defined in FF-A-1.0-REL */
> +struct ffa_partition_info_1_0 {
> +    uint16_t id;
> +    uint16_t execution_context;
> +    uint32_t partition_properties;
> +};
> +
> +/* Partition information descriptor defined in FF-A-1.1-REL0 */
> +struct ffa_partition_info_1_1 {
> +    uint16_t id;
> +    uint16_t execution_context;
> +    uint32_t partition_properties;
> +    uint8_t uuid[16];
> +};
> +
> +/* SPs subscribing to VM_CREATE and VM_DESTROYED events */
> +static uint16_t *subscr_vm_created __read_mostly;
> +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 w1, uint32_t w2, uint32_t w3,
> +                                      uint32_t w4, uint32_t w5, uint32_t *count,
> +                                      uint32_t *fpi_size)
> +{
> +    const struct arm_smccc_1_2_regs arg = {
> +        .a0 = FFA_PARTITION_INFO_GET,
> +        .a1 = w1,
> +        .a2 = w2,
> +        .a3 = w3,
> +        .a4 = w4,
> +        .a5 = w5,
> +    };
> +    struct arm_smccc_1_2_regs resp;
> +    uint32_t ret;
> +
> +    arm_smccc_1_2_smc(&arg, &resp);
> +
> +    ret = ffa_get_ret_code(&resp);
> +    if ( !ret )
> +    {
> +        *count = resp.a2;
> +        *fpi_size = resp.a3;
> +    }
> +
> +    return ret;
> +}
> +
> +int32_t ffa_handle_partition_info_get(uint32_t w1, uint32_t w2, uint32_t w3,
> +                                      uint32_t w4, uint32_t w5, uint32_t *count,
> +                                      uint32_t *fpi_size)
> +{
> +    int32_t ret = FFA_RET_DENIED;
> +    struct domain *d = current->domain;
> +    struct ffa_ctx *ctx = d->arch.tee;
> +
> +    /*
> +     * 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 ( w5 == FFA_PARTITION_INFO_GET_COUNT_FLAG &&
> +         ctx->guest_vers == FFA_VERSION_1_1 )
> +        return ffa_partition_info_get(w1, w2, w3, w4, w5, count, fpi_size);
> +    if ( w5 )
> +        return FFA_RET_INVALID_PARAMETERS;
> +
> +    if ( !ffa_rx )
> +        return FFA_RET_DENIED;
> +
> +    if ( !spin_trylock(&ctx->rx_lock) )
> +        return FFA_RET_BUSY;
> +
> +    if ( !ctx->page_count || !ctx->rx_is_free )
> +        goto out;
> +    spin_lock(&ffa_rx_buffer_lock);
> +    ret = ffa_partition_info_get(w1, w2, w3, w4, w5, count, fpi_size);
> +    if ( ret )
> +        goto out_rx_buf_unlock;
> +    /*
> +     * ffa_partition_info_get() succeeded so we now own the RX buffer we
> +     * share with the SPMC. We must give it back using ffa_rx_release()
> +     * once we've copied the content.
> +     */
> +
> +    if ( ctx->guest_vers == FFA_VERSION_1_0 )
> +    {
> +        size_t n;
> +        struct ffa_partition_info_1_1 *src = ffa_rx;
> +        struct ffa_partition_info_1_0 *dst = ctx->rx;
> +
> +        if ( ctx->page_count * FFA_PAGE_SIZE < *count * sizeof(*dst) )
> +        {
> +            ret = FFA_RET_NO_MEMORY;
> +            goto out_rx_release;
> +        }
> +
> +        for ( n = 0; n < *count; n++ )
> +        {
> +            dst[n].id = src[n].id;
> +            dst[n].execution_context = src[n].execution_context;
> +            dst[n].partition_properties = src[n].partition_properties;
> +        }
> +    }
> +    else
> +    {
> +        size_t sz = *count * *fpi_size;
> +
> +        if ( ctx->page_count * FFA_PAGE_SIZE < sz )
> +        {
> +            ret = FFA_RET_NO_MEMORY;
> +            goto out_rx_release;
> +        }
> +
> +        memcpy(ctx->rx, ffa_rx, sz);
> +    }
> +    ctx->rx_is_free = false;
> +out_rx_release:
> +    ffa_rx_release();
> +out_rx_buf_unlock:
> +    spin_unlock(&ffa_rx_buffer_lock);
> +out:
> +    spin_unlock(&ctx->rx_lock);
> +
> +    return ret;
> +}
> +
> +static int32_t ffa_direct_req_send_vm(uint16_t sp_id, uint16_t vm_id,
> +                                      uint8_t msg)
> +{
> +    uint32_t exp_resp = FFA_MSG_FLAG_FRAMEWORK;
> +    unsigned int retry_count = 0;
> +    int32_t res;
> +
> +    if ( msg == FFA_MSG_SEND_VM_CREATED )
> +        exp_resp |= FFA_MSG_RESP_VM_CREATED;
> +    else if ( msg == FFA_MSG_SEND_VM_DESTROYED )
> +        exp_resp |= FFA_MSG_RESP_VM_DESTROYED;
> +    else
> +        return FFA_RET_INVALID_PARAMETERS;
> +
> +    do {
> +        const struct arm_smccc_1_2_regs arg = {
> +            .a0 = FFA_MSG_SEND_DIRECT_REQ_32,
> +            .a1 = sp_id,
> +            .a2 = FFA_MSG_FLAG_FRAMEWORK | msg,
> +            .a5 = vm_id,
> +        };
> +        struct arm_smccc_1_2_regs resp;
> +
> +        arm_smccc_1_2_smc(&arg, &resp);
> +        if ( resp.a0 != FFA_MSG_SEND_DIRECT_RESP_32 || resp.a2 != exp_resp )
> +        {
> +            /*
> +             * This is an invalid response, likely due to some error in the
> +             * implementation of the ABI.
> +             */
> +            return FFA_RET_INVALID_PARAMETERS;
> +        }
> +        res = resp.a3;
> +        if ( ++retry_count > 10 )
> +        {
> +            /*
> +             * TODO
> +             * FFA_RET_INTERRUPTED means that the SPMC has a pending
> +             * non-secure interrupt, we need a way of delivering that
> +             * non-secure interrupt.
> +             * FFA_RET_RETRY is the SP telling us that it's temporarily
> +             * blocked from handling the direct request, we need a generic
> +             * way to deal with this.
> +             * For now in both cases, give up after a few retries.
> +             */
> +            return res;
> +        }
> +    } while ( res == FFA_RET_INTERRUPTED || res == FFA_RET_RETRY );
> +
> +    return res;
> +}
> +
> +static void uninit_subscribers(void)
> +{
> +        subscr_vm_created_count = 0;
> +        subscr_vm_destroyed_count = 0;
> +        XFREE(subscr_vm_created);
> +        XFREE(subscr_vm_destroyed);
> +}
> +
> +static bool init_subscribers(struct ffa_partition_info_1_1 *fpi, uint16_t count)
> +{
> +    uint16_t n;
> +    uint16_t c_pos;
> +    uint16_t d_pos;
> +
> +    subscr_vm_created_count = 0;
> +    subscr_vm_destroyed_count = 0;
> +    for ( n = 0; n < count; n++ )
> +    {
> +        if ( fpi[n].partition_properties & FFA_PART_PROP_NOTIF_CREATED )
> +            subscr_vm_created_count++;
> +        if ( fpi[n].partition_properties & FFA_PART_PROP_NOTIF_DESTROYED )
> +            subscr_vm_destroyed_count++;
> +    }
> +
> +    if ( subscr_vm_created_count )
> +        subscr_vm_created = xzalloc_array(uint16_t, subscr_vm_created_count);
> +    if ( subscr_vm_destroyed_count )
> +        subscr_vm_destroyed = xzalloc_array(uint16_t,
> +                                            subscr_vm_destroyed_count);
> +    if ( (subscr_vm_created_count && !subscr_vm_created) ||
> +         (subscr_vm_destroyed_count && !subscr_vm_destroyed) )
> +    {
> +        printk(XENLOG_ERR "ffa: Failed to allocate subscription lists\n");
> +        uninit_subscribers();
> +        return false;
> +    }
> +
> +    for ( c_pos = 0, d_pos = 0, n = 0; n < count; n++ )
> +    {
> +        if ( fpi[n].partition_properties & FFA_PART_PROP_NOTIF_CREATED )
> +            subscr_vm_created[c_pos++] = fpi[n].id;
> +        if ( fpi[n].partition_properties & FFA_PART_PROP_NOTIF_DESTROYED )
> +            subscr_vm_destroyed[d_pos++] = fpi[n].id;
> +    }
> +
> +    return true;
> +}
> +
> +
> +
> +bool ffa_partinfo_init(void)
> +{
> +    bool ret = false;
> +    uint32_t fpi_size;
> +    uint32_t count;
> +    int e;
> +
> +    e = ffa_partition_info_get(0, 0, 0, 0, 0, &count, &fpi_size);
> +    if ( e )
> +    {
> +        printk(XENLOG_ERR "ffa: Failed to get list of SPs: %d\n", e);
> +        goto out;
> +    }
> +
> +    if ( count >= UINT16_MAX )
> +    {
> +        printk(XENLOG_ERR "ffa: Impossible number of SPs: %u\n", count);
> +        goto out;
> +    }
> +
> +    ret = init_subscribers(ffa_rx, count);
> +
> +out:
> +    ffa_rx_release();
> +
> +    return ret;
> +}
> +
> +static bool is_in_subscr_list(const uint16_t *subscr, uint16_t start,
> +                              uint16_t end, uint16_t sp_id)
> +{
> +    unsigned int n;
> +
> +    for ( n = start; n < end; n++ )
> +    {
> +        if ( subscr[n] == sp_id )
> +            return true;
> +    }
> +
> +    return false;
> +}
> +
> +static void vm_destroy_bitmap_init(struct ffa_ctx *ctx,
> +                                   unsigned int create_signal_count)
> +{
> +    unsigned int n;
> +
> +    for ( n = 0; n < subscr_vm_destroyed_count; n++ )
> +    {
> +        /*
> +         * Skip SPs subscribed to the VM created event that never was
> +         * notified of the VM creation due to an error during
> +         * ffa_domain_init().
> +         */
> +        if ( is_in_subscr_list(subscr_vm_created, create_signal_count,
> +                               subscr_vm_created_count,
> +                               subscr_vm_destroyed[n]) )
> +            continue;
> +
> +        set_bit(n, ctx->vm_destroy_bitmap);
> +    }
> +}
> +
> +bool ffa_partinfo_domain_init(struct domain *d)
> +{
> +    unsigned int count = BITS_TO_LONGS(subscr_vm_destroyed_count);
> +    struct ffa_ctx *ctx = d->arch.tee;
> +    unsigned int n;
> +    int32_t res;
> +
> +    ctx->vm_destroy_bitmap = xzalloc_array(unsigned long, count);
> +    if ( !ctx->vm_destroy_bitmap )
> +        return false;
> +
> +    for ( n = 0; n < subscr_vm_created_count; n++ )
> +    {
> +        res = ffa_direct_req_send_vm(subscr_vm_created[n], ffa_get_vm_id(d),
> +                                     FFA_MSG_SEND_VM_CREATED);
> +        if ( res )
> +        {
> +            printk(XENLOG_ERR "ffa: Failed to report creation of vm_id %u to  %u: res %d\n",
> +                   ffa_get_vm_id(d), subscr_vm_created[n], res);
> +            break;
> +        }
> +    }
> +    vm_destroy_bitmap_init(ctx, n);
> +
> +    return n == subscr_vm_created_count;
> +}
> +
> +bool ffa_partinfo_domain_destroy(struct domain *d)
> +{
> +    struct ffa_ctx *ctx = d->arch.tee;
> +    unsigned int n;
> +    int32_t res;
> +
> +    if ( !ctx->vm_destroy_bitmap )
> +        return true;
> +
> +    for ( n = 0; n < subscr_vm_destroyed_count; n++ )
> +    {
> +        if ( !test_bit(n, ctx->vm_destroy_bitmap) )
> +            continue;
> +
> +        res = ffa_direct_req_send_vm(subscr_vm_destroyed[n], ffa_get_vm_id(d),
> +                                     FFA_MSG_SEND_VM_DESTROYED);
> +
> +        if ( res )
> +        {
> +            printk(XENLOG_ERR "%pd: ffa: Failed to report destruction of vm_id %u to %u: res %d\n",
> +                   d, ffa_get_vm_id(d), subscr_vm_destroyed[n], res);
> +        }
> +
> +        /*
> +         * For these two error codes the hypervisor is expected to resend
> +         * the destruction message. For the rest it is expected that the
> +         * error is permanent and that is doesn't help to resend the
> +         * destruction message.
> +         */
> +        if ( res != FFA_RET_INTERRUPTED && res != FFA_RET_RETRY )
> +            clear_bit(n, ctx->vm_destroy_bitmap);
> +    }
> +
> +    if ( bitmap_empty(ctx->vm_destroy_bitmap, subscr_vm_destroyed_count) )
> +        XFREE(ctx->vm_destroy_bitmap);
> +
> +    return !ctx->vm_destroy_bitmap;
> +}
> diff --git a/xen/arch/arm/tee/ffa_private.h b/xen/arch/arm/tee/ffa_private.h
> index f3e2f42e573e..6b32b69cfe90 100644
> --- a/xen/arch/arm/tee/ffa_private.h
> +++ b/xen/arch/arm/tee/ffa_private.h
> @@ -244,7 +244,7 @@ struct ffa_ctx {
>      * Used for ffa_domain_teardown() to keep track of which SPs should be
>      * notified that this guest is being destroyed.
>      */
> -    unsigned long vm_destroy_bitmap[];
> +    unsigned long *vm_destroy_bitmap;
> };
> 
> extern void *ffa_rx;
> @@ -256,6 +256,13 @@ bool ffa_shm_domain_destroy(struct domain *d);
> void ffa_handle_mem_share(struct cpu_user_regs *regs);
> int ffa_handle_mem_reclaim(uint64_t handle, uint32_t flags);
> 
> +bool ffa_partinfo_init(void);
> +bool ffa_partinfo_domain_init(struct domain *d);
> +bool ffa_partinfo_domain_destroy(struct domain *d);
> +int32_t ffa_handle_partition_info_get(uint32_t w1, uint32_t w2, uint32_t w3,
> +                                      uint32_t w4, uint32_t w5, uint32_t *count,
> +                                      uint32_t *fpi_size);
> +
> 
> static inline uint16_t ffa_get_vm_id(const struct domain *d)
> {
> @@ -325,4 +332,9 @@ static inline int32_t ffa_simple_call(uint32_t fid, register_t a1,
>     return ffa_get_ret_code(&resp);
> }
> 
> +static inline int32_t ffa_rx_release(void)
> +{
> +    return ffa_simple_call(FFA_RX_RELEASE, 0, 0, 0, 0);
> +}
> +
> #endif /*__FFA_PRIVATE_H__*/
> -- 
> 2.34.1
>
diff mbox series

Patch

diff --git a/xen/arch/arm/tee/Makefile b/xen/arch/arm/tee/Makefile
index 0e683d23aa9d..be644fba8055 100644
--- a/xen/arch/arm/tee/Makefile
+++ b/xen/arch/arm/tee/Makefile
@@ -1,4 +1,5 @@ 
 obj-$(CONFIG_FFA) += ffa.o
 obj-$(CONFIG_FFA) += ffa_shm.o
+obj-$(CONFIG_FFA) += ffa_partinfo.o
 obj-y += tee.o
 obj-$(CONFIG_OPTEE) += optee.o
diff --git a/xen/arch/arm/tee/ffa.c b/xen/arch/arm/tee/ffa.c
index db36292dc52f..7a2803881420 100644
--- a/xen/arch/arm/tee/ffa.c
+++ b/xen/arch/arm/tee/ffa.c
@@ -70,20 +70,6 @@ 
  * structs ending with _1_1 are defined in FF-A-1.1-REL0.
  */
 
-/* Partition information descriptor */
-struct ffa_partition_info_1_0 {
-    uint16_t id;
-    uint16_t execution_context;
-    uint32_t partition_properties;
-};
-
-struct ffa_partition_info_1_1 {
-    uint16_t id;
-    uint16_t execution_context;
-    uint32_t partition_properties;
-    uint8_t uuid[16];
-};
-
 /* Endpoint RX/TX descriptor */
 struct ffa_endpoint_rxtx_descriptor_1_0 {
     uint16_t sender_id;
@@ -102,11 +88,6 @@  struct ffa_endpoint_rxtx_descriptor_1_1 {
 /* Negotiated FF-A version to use with the SPMC */
 static uint32_t __ro_after_init ffa_version;
 
-/* SPs subscribing to VM_CREATE and VM_DESTROYED events */
-static uint16_t *subscr_vm_created __read_mostly;
-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;
 
 /*
  * Our rx/tx buffers shared with the SPMC. FFA_RXTX_PAGE_COUNT is the
@@ -170,90 +151,6 @@  static int32_t ffa_rxtx_map(paddr_t tx_addr, paddr_t rx_addr,
     return ffa_simple_call(FFA_RXTX_MAP_64, tx_addr, rx_addr, page_count, 0);
 }
 
-static int32_t ffa_partition_info_get(uint32_t w1, uint32_t w2, uint32_t w3,
-                                      uint32_t w4, uint32_t w5,
-                                      uint32_t *count, uint32_t *fpi_size)
-{
-    const struct arm_smccc_1_2_regs arg = {
-        .a0 = FFA_PARTITION_INFO_GET,
-        .a1 = w1,
-        .a2 = w2,
-        .a3 = w3,
-        .a4 = w4,
-        .a5 = w5,
-    };
-    struct arm_smccc_1_2_regs resp;
-    uint32_t ret;
-
-    arm_smccc_1_2_smc(&arg, &resp);
-
-    ret = ffa_get_ret_code(&resp);
-    if ( !ret )
-    {
-        *count = resp.a2;
-        *fpi_size = resp.a3;
-    }
-
-    return ret;
-}
-
-static int32_t ffa_rx_release(void)
-{
-    return ffa_simple_call(FFA_RX_RELEASE, 0, 0, 0, 0);
-}
-
-static int32_t ffa_direct_req_send_vm(uint16_t sp_id, uint16_t vm_id,
-                                      uint8_t msg)
-{
-    uint32_t exp_resp = FFA_MSG_FLAG_FRAMEWORK;
-    unsigned int retry_count = 0;
-    int32_t res;
-
-    if ( msg == FFA_MSG_SEND_VM_CREATED )
-        exp_resp |= FFA_MSG_RESP_VM_CREATED;
-    else if ( msg == FFA_MSG_SEND_VM_DESTROYED )
-        exp_resp |= FFA_MSG_RESP_VM_DESTROYED;
-    else
-        return FFA_RET_INVALID_PARAMETERS;
-
-    do {
-        const struct arm_smccc_1_2_regs arg = {
-            .a0 = FFA_MSG_SEND_DIRECT_REQ_32,
-            .a1 = sp_id,
-            .a2 = FFA_MSG_FLAG_FRAMEWORK | msg,
-            .a5 = vm_id,
-        };
-        struct arm_smccc_1_2_regs resp;
-
-        arm_smccc_1_2_smc(&arg, &resp);
-        if ( resp.a0 != FFA_MSG_SEND_DIRECT_RESP_32 || resp.a2 != exp_resp )
-        {
-            /*
-             * This is an invalid response, likely due to some error in the
-             * implementation of the ABI.
-             */
-            return FFA_RET_INVALID_PARAMETERS;
-        }
-        res = resp.a3;
-        if ( ++retry_count > 10 )
-        {
-            /*
-             * TODO
-             * FFA_RET_INTERRUPTED means that the SPMC has a pending
-             * non-secure interrupt, we need a way of delivering that
-             * non-secure interrupt.
-             * FFA_RET_RETRY is the SP telling us that it's temporarily
-             * blocked from handling the direct request, we need a generic
-             * way to deal with this.
-             * For now in both cases, give up after a few retries.
-             */
-            return res;
-        }
-    } while ( res == FFA_RET_INTERRUPTED || res == FFA_RET_RETRY );
-
-    return res;
-}
-
 static void handle_version(struct cpu_user_regs *regs)
 {
     struct domain *d = current->domain;
@@ -371,88 +268,6 @@  static uint32_t ffa_handle_rxtx_unmap(void)
     return FFA_RET_OK;
 }
 
-static int32_t ffa_handle_partition_info_get(uint32_t w1, uint32_t w2,
-                                             uint32_t w3, uint32_t w4,
-                                             uint32_t w5, uint32_t *count,
-                                             uint32_t *fpi_size)
-{
-    int32_t ret = FFA_RET_DENIED;
-    struct domain *d = current->domain;
-    struct ffa_ctx *ctx = d->arch.tee;
-
-    /*
-     * 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 ( w5 == FFA_PARTITION_INFO_GET_COUNT_FLAG &&
-         ctx->guest_vers == FFA_VERSION_1_1 )
-        return ffa_partition_info_get(w1, w2, w3, w4, w5, count, fpi_size);
-    if ( w5 )
-        return FFA_RET_INVALID_PARAMETERS;
-
-    if ( !ffa_rx )
-        return FFA_RET_DENIED;
-
-    if ( !spin_trylock(&ctx->rx_lock) )
-        return FFA_RET_BUSY;
-
-    if ( !ctx->page_count || !ctx->rx_is_free )
-        goto out;
-    spin_lock(&ffa_rx_buffer_lock);
-    ret = ffa_partition_info_get(w1, w2, w3, w4, w5, count, fpi_size);
-    if ( ret )
-        goto out_rx_buf_unlock;
-    /*
-     * ffa_partition_info_get() succeeded so we now own the RX buffer we
-     * share with the SPMC. We must give it back using ffa_rx_release()
-     * once we've copied the content.
-     */
-
-    if ( ctx->guest_vers == FFA_VERSION_1_0 )
-    {
-        size_t n;
-        struct ffa_partition_info_1_1 *src = ffa_rx;
-        struct ffa_partition_info_1_0 *dst = ctx->rx;
-
-        if ( ctx->page_count * FFA_PAGE_SIZE < *count * sizeof(*dst) )
-        {
-            ret = FFA_RET_NO_MEMORY;
-            goto out_rx_release;
-        }
-
-        for ( n = 0; n < *count; n++ )
-        {
-            dst[n].id = src[n].id;
-            dst[n].execution_context = src[n].execution_context;
-            dst[n].partition_properties = src[n].partition_properties;
-        }
-    }
-    else
-    {
-        size_t sz = *count * *fpi_size;
-
-        if ( ctx->page_count * FFA_PAGE_SIZE < sz )
-        {
-            ret = FFA_RET_NO_MEMORY;
-            goto out_rx_release;
-        }
-
-        memcpy(ctx->rx, ffa_rx, sz);
-    }
-    ctx->rx_is_free = false;
-out_rx_release:
-    ffa_rx_release();
-out_rx_buf_unlock:
-    spin_unlock(&ffa_rx_buffer_lock);
-out:
-    spin_unlock(&ctx->rx_lock);
-
-    return ret;
-}
-
 static int32_t ffa_handle_rx_release(void)
 {
     int32_t ret = FFA_RET_DENIED;
@@ -604,46 +419,9 @@  static bool ffa_handle_call(struct cpu_user_regs *regs)
     }
 }
 
-static bool is_in_subscr_list(const uint16_t *subscr, uint16_t start,
-                              uint16_t end, uint16_t sp_id)
-{
-    unsigned int n;
-
-    for ( n = start; n < end; n++ )
-    {
-        if ( subscr[n] == sp_id )
-            return true;
-    }
-
-    return false;
-}
-
-static void vm_destroy_bitmap_init(struct ffa_ctx *ctx,
-                                   unsigned int create_signal_count)
-{
-    unsigned int n;
-
-    for ( n = 0; n < subscr_vm_destroyed_count; n++ )
-    {
-        /*
-         * Skip SPs subscribed to the VM created event that never was
-         * notified of the VM creation due to an error during
-         * ffa_domain_init().
-         */
-        if ( is_in_subscr_list(subscr_vm_created, create_signal_count,
-                               subscr_vm_created_count,
-                               subscr_vm_destroyed[n]) )
-            continue;
-
-        set_bit(n, ctx->vm_destroy_bitmap);
-    }
-}
-
 static int ffa_domain_init(struct domain *d)
 {
     struct ffa_ctx *ctx;
-    unsigned int n;
-    int32_t res;
 
     if ( !ffa_version )
         return -ENODEV;
@@ -654,8 +432,7 @@  static int ffa_domain_init(struct domain *d)
     if ( d->domain_id >= UINT16_MAX)
         return -ERANGE;
 
-    ctx = xzalloc_flex_struct(struct ffa_ctx, vm_destroy_bitmap,
-                              BITS_TO_LONGS(subscr_vm_destroyed_count));
+    ctx = xzalloc(struct ffa_ctx);
     if ( !ctx )
         return -ENOMEM;
 
@@ -663,66 +440,28 @@  static int ffa_domain_init(struct domain *d)
     ctx->teardown_d = d;
     INIT_LIST_HEAD(&ctx->shm_list);
 
-    for ( n = 0; n < subscr_vm_created_count; n++ )
-    {
-        res = ffa_direct_req_send_vm(subscr_vm_created[n], ffa_get_vm_id(d),
-                                     FFA_MSG_SEND_VM_CREATED);
-        if ( res )
-        {
-            printk(XENLOG_ERR "ffa: Failed to report creation of vm_id %u to  %u: res %d\n",
-                   ffa_get_vm_id(d), subscr_vm_created[n], res);
-            break;
-        }
-    }
-    vm_destroy_bitmap_init(ctx, n);
-    if ( n != subscr_vm_created_count )
+    /*
+     * ffa_domain_teardown() will be called if ffa_domain_init() returns an
+     * error, so no need for cleanup in this function.
+     */
+
+    if ( !ffa_partinfo_domain_init(d) )
         return -EIO;
 
     return 0;
 }
 
-static void send_vm_destroyed(struct domain *d)
-{
-    struct ffa_ctx *ctx = d->arch.tee;
-    unsigned int n;
-    int32_t res;
-
-    for ( n = 0; n < subscr_vm_destroyed_count; n++ )
-    {
-        if ( !test_bit(n, ctx->vm_destroy_bitmap) )
-            continue;
-
-        res = ffa_direct_req_send_vm(subscr_vm_destroyed[n], ffa_get_vm_id(d),
-                                     FFA_MSG_SEND_VM_DESTROYED);
-
-        if ( res )
-        {
-            printk(XENLOG_ERR "%pd: ffa: Failed to report destruction of vm_id %u to %u: res %d\n",
-                   d, ffa_get_vm_id(d), subscr_vm_destroyed[n], res);
-        }
-
-        /*
-         * For these two error codes the hypervisor is expected to resend
-         * the destruction message. For the rest it is expected that the
-         * error is permanent and that is doesn't help to resend the
-         * destruction message.
-         */
-        if ( res != FFA_RET_INTERRUPTED && res != FFA_RET_RETRY )
-            clear_bit(n, ctx->vm_destroy_bitmap);
-    }
-}
-
 static void ffa_domain_teardown_continue(struct ffa_ctx *ctx, bool first_time)
 {
     struct ffa_ctx *next_ctx = NULL;
     bool retry = false;
 
-    send_vm_destroyed(ctx->teardown_d);
+    if ( !ffa_partinfo_domain_destroy(ctx->teardown_d) )
+        retry = true;
     if ( !ffa_shm_domain_destroy(ctx->teardown_d) )
         retry = true;
 
-    if ( retry ||
-         !bitmap_empty(ctx->vm_destroy_bitmap, subscr_vm_destroyed_count) )
+    if ( retry )
     {
         printk(XENLOG_G_INFO "%pd: ffa: Remaining cleanup, retrying\n", ctx->teardown_d);
 
@@ -796,82 +535,6 @@  static int ffa_relinquish_resources(struct domain *d)
     return 0;
 }
 
-static void uninit_subscribers(void)
-{
-        subscr_vm_created_count = 0;
-        subscr_vm_destroyed_count = 0;
-        XFREE(subscr_vm_created);
-        XFREE(subscr_vm_destroyed);
-}
-
-static bool init_subscribers(struct ffa_partition_info_1_1 *fpi, uint16_t count)
-{
-    uint16_t n;
-    uint16_t c_pos;
-    uint16_t d_pos;
-
-    subscr_vm_created_count = 0;
-    subscr_vm_destroyed_count = 0;
-    for ( n = 0; n < count; n++ )
-    {
-        if ( fpi[n].partition_properties & FFA_PART_PROP_NOTIF_CREATED )
-            subscr_vm_created_count++;
-        if ( fpi[n].partition_properties & FFA_PART_PROP_NOTIF_DESTROYED )
-            subscr_vm_destroyed_count++;
-    }
-
-    if ( subscr_vm_created_count )
-        subscr_vm_created = xzalloc_array(uint16_t, subscr_vm_created_count);
-    if ( subscr_vm_destroyed_count )
-        subscr_vm_destroyed = xzalloc_array(uint16_t,
-                                            subscr_vm_destroyed_count);
-    if ( (subscr_vm_created_count && !subscr_vm_created) ||
-         (subscr_vm_destroyed_count && !subscr_vm_destroyed) )
-    {
-        printk(XENLOG_ERR "ffa: Failed to allocate subscription lists\n");
-        uninit_subscribers();
-        return false;
-    }
-
-    for ( c_pos = 0, d_pos = 0, n = 0; n < count; n++ )
-    {
-        if ( fpi[n].partition_properties & FFA_PART_PROP_NOTIF_CREATED )
-            subscr_vm_created[c_pos++] = fpi[n].id;
-        if ( fpi[n].partition_properties & FFA_PART_PROP_NOTIF_DESTROYED )
-            subscr_vm_destroyed[d_pos++] = fpi[n].id;
-    }
-
-    return true;
-}
-
-static bool init_sps(void)
-{
-    bool ret = false;
-    uint32_t fpi_size;
-    uint32_t count;
-    int e;
-
-    e = ffa_partition_info_get(0, 0, 0, 0, 0, &count, &fpi_size);
-    if ( e )
-    {
-        printk(XENLOG_ERR "ffa: Failed to get list of SPs: %d\n", e);
-        goto out;
-    }
-
-    if ( count >= UINT16_MAX )
-    {
-        printk(XENLOG_ERR "ffa: Impossible number of SPs: %u\n", count);
-        goto out;
-    }
-
-    ret = init_subscribers(ffa_rx, count);
-
-out:
-    ffa_rx_release();
-
-    return ret;
-}
-
 static bool ffa_probe(void)
 {
     uint32_t vers;
@@ -949,7 +612,7 @@  static bool ffa_probe(void)
     }
     ffa_version = vers;
 
-    if ( !init_sps() )
+    if ( !ffa_partinfo_init() )
         goto err_free_ffa_tx;
 
     INIT_LIST_HEAD(&ffa_teardown_head);
diff --git a/xen/arch/arm/tee/ffa_partinfo.c b/xen/arch/arm/tee/ffa_partinfo.c
new file mode 100644
index 000000000000..dc1059584828
--- /dev/null
+++ b/xen/arch/arm/tee/ffa_partinfo.c
@@ -0,0 +1,373 @@ 
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2024  Linaro Limited
+ */
+
+#include <xen/const.h>
+#include <xen/sizes.h>
+#include <xen/types.h>
+
+#include <asm/smccc.h>
+#include <asm/regs.h>
+
+#include "ffa_private.h"
+
+/* Partition information descriptor defined in FF-A-1.0-REL */
+struct ffa_partition_info_1_0 {
+    uint16_t id;
+    uint16_t execution_context;
+    uint32_t partition_properties;
+};
+
+/* Partition information descriptor defined in FF-A-1.1-REL0 */
+struct ffa_partition_info_1_1 {
+    uint16_t id;
+    uint16_t execution_context;
+    uint32_t partition_properties;
+    uint8_t uuid[16];
+};
+
+/* SPs subscribing to VM_CREATE and VM_DESTROYED events */
+static uint16_t *subscr_vm_created __read_mostly;
+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 w1, uint32_t w2, uint32_t w3,
+                                      uint32_t w4, uint32_t w5, uint32_t *count,
+                                      uint32_t *fpi_size)
+{
+    const struct arm_smccc_1_2_regs arg = {
+        .a0 = FFA_PARTITION_INFO_GET,
+        .a1 = w1,
+        .a2 = w2,
+        .a3 = w3,
+        .a4 = w4,
+        .a5 = w5,
+    };
+    struct arm_smccc_1_2_regs resp;
+    uint32_t ret;
+
+    arm_smccc_1_2_smc(&arg, &resp);
+
+    ret = ffa_get_ret_code(&resp);
+    if ( !ret )
+    {
+        *count = resp.a2;
+        *fpi_size = resp.a3;
+    }
+
+    return ret;
+}
+
+int32_t ffa_handle_partition_info_get(uint32_t w1, uint32_t w2, uint32_t w3,
+                                      uint32_t w4, uint32_t w5, uint32_t *count,
+                                      uint32_t *fpi_size)
+{
+    int32_t ret = FFA_RET_DENIED;
+    struct domain *d = current->domain;
+    struct ffa_ctx *ctx = d->arch.tee;
+
+    /*
+     * 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 ( w5 == FFA_PARTITION_INFO_GET_COUNT_FLAG &&
+         ctx->guest_vers == FFA_VERSION_1_1 )
+        return ffa_partition_info_get(w1, w2, w3, w4, w5, count, fpi_size);
+    if ( w5 )
+        return FFA_RET_INVALID_PARAMETERS;
+
+    if ( !ffa_rx )
+        return FFA_RET_DENIED;
+
+    if ( !spin_trylock(&ctx->rx_lock) )
+        return FFA_RET_BUSY;
+
+    if ( !ctx->page_count || !ctx->rx_is_free )
+        goto out;
+    spin_lock(&ffa_rx_buffer_lock);
+    ret = ffa_partition_info_get(w1, w2, w3, w4, w5, count, fpi_size);
+    if ( ret )
+        goto out_rx_buf_unlock;
+    /*
+     * ffa_partition_info_get() succeeded so we now own the RX buffer we
+     * share with the SPMC. We must give it back using ffa_rx_release()
+     * once we've copied the content.
+     */
+
+    if ( ctx->guest_vers == FFA_VERSION_1_0 )
+    {
+        size_t n;
+        struct ffa_partition_info_1_1 *src = ffa_rx;
+        struct ffa_partition_info_1_0 *dst = ctx->rx;
+
+        if ( ctx->page_count * FFA_PAGE_SIZE < *count * sizeof(*dst) )
+        {
+            ret = FFA_RET_NO_MEMORY;
+            goto out_rx_release;
+        }
+
+        for ( n = 0; n < *count; n++ )
+        {
+            dst[n].id = src[n].id;
+            dst[n].execution_context = src[n].execution_context;
+            dst[n].partition_properties = src[n].partition_properties;
+        }
+    }
+    else
+    {
+        size_t sz = *count * *fpi_size;
+
+        if ( ctx->page_count * FFA_PAGE_SIZE < sz )
+        {
+            ret = FFA_RET_NO_MEMORY;
+            goto out_rx_release;
+        }
+
+        memcpy(ctx->rx, ffa_rx, sz);
+    }
+    ctx->rx_is_free = false;
+out_rx_release:
+    ffa_rx_release();
+out_rx_buf_unlock:
+    spin_unlock(&ffa_rx_buffer_lock);
+out:
+    spin_unlock(&ctx->rx_lock);
+
+    return ret;
+}
+
+static int32_t ffa_direct_req_send_vm(uint16_t sp_id, uint16_t vm_id,
+                                      uint8_t msg)
+{
+    uint32_t exp_resp = FFA_MSG_FLAG_FRAMEWORK;
+    unsigned int retry_count = 0;
+    int32_t res;
+
+    if ( msg == FFA_MSG_SEND_VM_CREATED )
+        exp_resp |= FFA_MSG_RESP_VM_CREATED;
+    else if ( msg == FFA_MSG_SEND_VM_DESTROYED )
+        exp_resp |= FFA_MSG_RESP_VM_DESTROYED;
+    else
+        return FFA_RET_INVALID_PARAMETERS;
+
+    do {
+        const struct arm_smccc_1_2_regs arg = {
+            .a0 = FFA_MSG_SEND_DIRECT_REQ_32,
+            .a1 = sp_id,
+            .a2 = FFA_MSG_FLAG_FRAMEWORK | msg,
+            .a5 = vm_id,
+        };
+        struct arm_smccc_1_2_regs resp;
+
+        arm_smccc_1_2_smc(&arg, &resp);
+        if ( resp.a0 != FFA_MSG_SEND_DIRECT_RESP_32 || resp.a2 != exp_resp )
+        {
+            /*
+             * This is an invalid response, likely due to some error in the
+             * implementation of the ABI.
+             */
+            return FFA_RET_INVALID_PARAMETERS;
+        }
+        res = resp.a3;
+        if ( ++retry_count > 10 )
+        {
+            /*
+             * TODO
+             * FFA_RET_INTERRUPTED means that the SPMC has a pending
+             * non-secure interrupt, we need a way of delivering that
+             * non-secure interrupt.
+             * FFA_RET_RETRY is the SP telling us that it's temporarily
+             * blocked from handling the direct request, we need a generic
+             * way to deal with this.
+             * For now in both cases, give up after a few retries.
+             */
+            return res;
+        }
+    } while ( res == FFA_RET_INTERRUPTED || res == FFA_RET_RETRY );
+
+    return res;
+}
+
+static void uninit_subscribers(void)
+{
+        subscr_vm_created_count = 0;
+        subscr_vm_destroyed_count = 0;
+        XFREE(subscr_vm_created);
+        XFREE(subscr_vm_destroyed);
+}
+
+static bool init_subscribers(struct ffa_partition_info_1_1 *fpi, uint16_t count)
+{
+    uint16_t n;
+    uint16_t c_pos;
+    uint16_t d_pos;
+
+    subscr_vm_created_count = 0;
+    subscr_vm_destroyed_count = 0;
+    for ( n = 0; n < count; n++ )
+    {
+        if ( fpi[n].partition_properties & FFA_PART_PROP_NOTIF_CREATED )
+            subscr_vm_created_count++;
+        if ( fpi[n].partition_properties & FFA_PART_PROP_NOTIF_DESTROYED )
+            subscr_vm_destroyed_count++;
+    }
+
+    if ( subscr_vm_created_count )
+        subscr_vm_created = xzalloc_array(uint16_t, subscr_vm_created_count);
+    if ( subscr_vm_destroyed_count )
+        subscr_vm_destroyed = xzalloc_array(uint16_t,
+                                            subscr_vm_destroyed_count);
+    if ( (subscr_vm_created_count && !subscr_vm_created) ||
+         (subscr_vm_destroyed_count && !subscr_vm_destroyed) )
+    {
+        printk(XENLOG_ERR "ffa: Failed to allocate subscription lists\n");
+        uninit_subscribers();
+        return false;
+    }
+
+    for ( c_pos = 0, d_pos = 0, n = 0; n < count; n++ )
+    {
+        if ( fpi[n].partition_properties & FFA_PART_PROP_NOTIF_CREATED )
+            subscr_vm_created[c_pos++] = fpi[n].id;
+        if ( fpi[n].partition_properties & FFA_PART_PROP_NOTIF_DESTROYED )
+            subscr_vm_destroyed[d_pos++] = fpi[n].id;
+    }
+
+    return true;
+}
+
+
+
+bool ffa_partinfo_init(void)
+{
+    bool ret = false;
+    uint32_t fpi_size;
+    uint32_t count;
+    int e;
+
+    e = ffa_partition_info_get(0, 0, 0, 0, 0, &count, &fpi_size);
+    if ( e )
+    {
+        printk(XENLOG_ERR "ffa: Failed to get list of SPs: %d\n", e);
+        goto out;
+    }
+
+    if ( count >= UINT16_MAX )
+    {
+        printk(XENLOG_ERR "ffa: Impossible number of SPs: %u\n", count);
+        goto out;
+    }
+
+    ret = init_subscribers(ffa_rx, count);
+
+out:
+    ffa_rx_release();
+
+    return ret;
+}
+
+static bool is_in_subscr_list(const uint16_t *subscr, uint16_t start,
+                              uint16_t end, uint16_t sp_id)
+{
+    unsigned int n;
+
+    for ( n = start; n < end; n++ )
+    {
+        if ( subscr[n] == sp_id )
+            return true;
+    }
+
+    return false;
+}
+
+static void vm_destroy_bitmap_init(struct ffa_ctx *ctx,
+                                   unsigned int create_signal_count)
+{
+    unsigned int n;
+
+    for ( n = 0; n < subscr_vm_destroyed_count; n++ )
+    {
+        /*
+         * Skip SPs subscribed to the VM created event that never was
+         * notified of the VM creation due to an error during
+         * ffa_domain_init().
+         */
+        if ( is_in_subscr_list(subscr_vm_created, create_signal_count,
+                               subscr_vm_created_count,
+                               subscr_vm_destroyed[n]) )
+            continue;
+
+        set_bit(n, ctx->vm_destroy_bitmap);
+    }
+}
+
+bool ffa_partinfo_domain_init(struct domain *d)
+{
+    unsigned int count = BITS_TO_LONGS(subscr_vm_destroyed_count);
+    struct ffa_ctx *ctx = d->arch.tee;
+    unsigned int n;
+    int32_t res;
+
+    ctx->vm_destroy_bitmap = xzalloc_array(unsigned long, count);
+    if ( !ctx->vm_destroy_bitmap )
+        return false;
+
+    for ( n = 0; n < subscr_vm_created_count; n++ )
+    {
+        res = ffa_direct_req_send_vm(subscr_vm_created[n], ffa_get_vm_id(d),
+                                     FFA_MSG_SEND_VM_CREATED);
+        if ( res )
+        {
+            printk(XENLOG_ERR "ffa: Failed to report creation of vm_id %u to  %u: res %d\n",
+                   ffa_get_vm_id(d), subscr_vm_created[n], res);
+            break;
+        }
+    }
+    vm_destroy_bitmap_init(ctx, n);
+
+    return n == subscr_vm_created_count;
+}
+
+bool ffa_partinfo_domain_destroy(struct domain *d)
+{
+    struct ffa_ctx *ctx = d->arch.tee;
+    unsigned int n;
+    int32_t res;
+
+    if ( !ctx->vm_destroy_bitmap )
+        return true;
+
+    for ( n = 0; n < subscr_vm_destroyed_count; n++ )
+    {
+        if ( !test_bit(n, ctx->vm_destroy_bitmap) )
+            continue;
+
+        res = ffa_direct_req_send_vm(subscr_vm_destroyed[n], ffa_get_vm_id(d),
+                                     FFA_MSG_SEND_VM_DESTROYED);
+
+        if ( res )
+        {
+            printk(XENLOG_ERR "%pd: ffa: Failed to report destruction of vm_id %u to %u: res %d\n",
+                   d, ffa_get_vm_id(d), subscr_vm_destroyed[n], res);
+        }
+
+        /*
+         * For these two error codes the hypervisor is expected to resend
+         * the destruction message. For the rest it is expected that the
+         * error is permanent and that is doesn't help to resend the
+         * destruction message.
+         */
+        if ( res != FFA_RET_INTERRUPTED && res != FFA_RET_RETRY )
+            clear_bit(n, ctx->vm_destroy_bitmap);
+    }
+
+    if ( bitmap_empty(ctx->vm_destroy_bitmap, subscr_vm_destroyed_count) )
+        XFREE(ctx->vm_destroy_bitmap);
+
+    return !ctx->vm_destroy_bitmap;
+}
diff --git a/xen/arch/arm/tee/ffa_private.h b/xen/arch/arm/tee/ffa_private.h
index f3e2f42e573e..6b32b69cfe90 100644
--- a/xen/arch/arm/tee/ffa_private.h
+++ b/xen/arch/arm/tee/ffa_private.h
@@ -244,7 +244,7 @@  struct ffa_ctx {
      * Used for ffa_domain_teardown() to keep track of which SPs should be
      * notified that this guest is being destroyed.
      */
-    unsigned long vm_destroy_bitmap[];
+    unsigned long *vm_destroy_bitmap;
 };
 
 extern void *ffa_rx;
@@ -256,6 +256,13 @@  bool ffa_shm_domain_destroy(struct domain *d);
 void ffa_handle_mem_share(struct cpu_user_regs *regs);
 int ffa_handle_mem_reclaim(uint64_t handle, uint32_t flags);
 
+bool ffa_partinfo_init(void);
+bool ffa_partinfo_domain_init(struct domain *d);
+bool ffa_partinfo_domain_destroy(struct domain *d);
+int32_t ffa_handle_partition_info_get(uint32_t w1, uint32_t w2, uint32_t w3,
+                                      uint32_t w4, uint32_t w5, uint32_t *count,
+                                      uint32_t *fpi_size);
+
 
 static inline uint16_t ffa_get_vm_id(const struct domain *d)
 {
@@ -325,4 +332,9 @@  static inline int32_t ffa_simple_call(uint32_t fid, register_t a1,
     return ffa_get_ret_code(&resp);
 }
 
+static inline int32_t ffa_rx_release(void)
+{
+    return ffa_simple_call(FFA_RX_RELEASE, 0, 0, 0, 0);
+}
+
 #endif /*__FFA_PRIVATE_H__*/