:p
atchew
Login
This series implements FF-A 1.2 PARTITION_INFO_GET_REGS support, providing register-based partition info retrieval as an efficient alternative to the RX buffer-based PARTITION_INFO_GET. The serie is also introducing a caching system for the list of secure partitions to prevent retrieving it on each VM call as we expect it to be static and not changing over time. The patches are organized as follows: - patch 1 is modifying existing code to prepare for GET_REGS support by adding a start_index in the existing internal function - patch 2 is introducing the secure partition list caching - patch 3 is removing the existing suscribers list to directly use the cached secure partition list - patch 4 is adding GET_REGS support Bertrand Marquis (4): xen/arm: ffa: Add start_index to VM partinfo helper xen/arm: ffa: Cache SP partition info at init xen/arm: ffa: Drop SP subscriber lists xen/arm: ffa: Add cached GET_REGS support xen/arch/arm/tee/ffa.c | 16 + xen/arch/arm/tee/ffa_partinfo.c | 558 ++++++++++++++++++++++---------- xen/arch/arm/tee/ffa_private.h | 4 +- 3 files changed, 402 insertions(+), 176 deletions(-) -- 2.52.0
Windowed GET_REGS retrieval needs to emit VM entries starting from an arbitrary index, but ffa_get_vm_partinfo() always starts from index 0. Add a start_index parameter to ffa_get_vm_partinfo() and skip entries until the local index reaches start_index. Update ffa_handle_partition_info_get() to pass start_index=0 to preserve existing behavior. No functional changes. Signed-off-by: Bertrand Marquis <bertrand.marquis@arm.com> --- xen/arch/arm/tee/ffa_partinfo.c | 61 +++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/xen/arch/arm/tee/ffa_partinfo.c b/xen/arch/arm/tee/ffa_partinfo.c index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/tee/ffa_partinfo.c +++ b/xen/arch/arm/tee/ffa_partinfo.c @@ -XXX,XX +XXX,XX @@ out: return ret; } -static int32_t ffa_get_vm_partinfo(struct ffa_uuid uuid, uint32_t *vm_count, - void **dst_buf, void *end_buf, - uint32_t dst_size) +static int32_t ffa_get_vm_partinfo(struct ffa_uuid uuid, uint32_t start_index, + uint32_t *vm_count, void **dst_buf, + void *end_buf, uint32_t dst_size) { struct domain *d = current->domain; struct ffa_ctx *curr_ctx = d->arch.tee; struct ffa_ctx *dest_ctx; uint32_t count = 0; + uint32_t idx = 0; int32_t ret = FFA_RET_OK; /* * We do not have UUID info for VMs so use the 1.0 structure so that we set @@ -XXX,XX +XXX,XX @@ static int32_t ffa_get_vm_partinfo(struct ffa_uuid uuid, uint32_t *vm_count, if ( ACCESS_ONCE(curr_ctx->guest_vers) >= FFA_VERSION_1_2 ) { /* Add caller VM information */ - info.id = curr_ctx->ffa_id; - info.execution_context = curr_ctx->num_vcpus; - info.partition_properties = FFA_PART_VM_PROP; - if ( is_64bit_domain(d) ) - info.partition_properties |= FFA_PART_PROP_AARCH64_STATE; - - ret = ffa_copy_info(dst_buf, end_buf, &info, dst_size, sizeof(info)); - if ( ret ) - return ret; + if ( start_index == 0) + { + info.id = curr_ctx->ffa_id; + info.execution_context = curr_ctx->num_vcpus; + info.partition_properties = FFA_PART_VM_PROP; + if ( is_64bit_domain(d) ) + info.partition_properties |= FFA_PART_PROP_AARCH64_STATE; - count++; + ret = ffa_copy_info(dst_buf, end_buf, &info, dst_size, + sizeof(info)); + if ( ret ) + return ret; + count++; + } + idx++; } if ( IS_ENABLED(CONFIG_FFA_VM_TO_VM) ) @@ -XXX,XX +XXX,XX @@ static int32_t ffa_get_vm_partinfo(struct ffa_uuid uuid, uint32_t *vm_count, if ( dest_ctx == curr_ctx ) continue; - info.id = dest_ctx->ffa_id; - info.execution_context = dest_ctx->num_vcpus; - info.partition_properties = FFA_PART_VM_PROP; - if ( dest_ctx->is_64bit ) - info.partition_properties |= FFA_PART_PROP_AARCH64_STATE; - - ret = ffa_copy_info(dst_buf, end_buf, &info, dst_size, - sizeof(info)); - if ( ret ) + if ( idx >= start_index ) { - read_unlock(&ffa_ctx_list_rwlock); - return ret; + info.id = dest_ctx->ffa_id; + info.execution_context = dest_ctx->num_vcpus; + info.partition_properties = FFA_PART_VM_PROP; + if ( dest_ctx->is_64bit ) + info.partition_properties |= FFA_PART_PROP_AARCH64_STATE; + + ret = ffa_copy_info(dst_buf, end_buf, &info, dst_size, + sizeof(info)); + if ( ret ) + { + read_unlock(&ffa_ctx_list_rwlock); + return ret; + } + count++; } - count++; + idx++; } read_unlock(&ffa_ctx_list_rwlock); } @@ -XXX,XX +XXX,XX @@ void ffa_handle_partition_info_get(struct cpu_user_regs *regs) goto out_rx_release; } - ret = ffa_get_vm_partinfo(uuid, &ffa_vm_count, &dst_buf, end_buf, + ret = ffa_get_vm_partinfo(uuid, 0, &ffa_vm_count, &dst_buf, end_buf, dst_size); out_rx_release: -- 2.52.0
FFA_PARTITION_INFO_GET currently queries the SPMC on each call and walks the RX buffer every time. The SP list is expected to be static, so repeated firmware calls and validation are unnecessary. Cache the SPMC partition-info list at init time, keeping only secure endpoints, and reuse the cached entries for SP count and partition-info responses. Initialize the VM create/destroy subscriber lists from the cached list and free the cache on init failure. SP partition info now reflects the init-time snapshot and will not change. Signed-off-by: Bertrand Marquis <bertrand.marquis@arm.com> --- xen/arch/arm/tee/ffa_partinfo.c | 205 +++++++++++++++++++++----------- 1 file changed, 138 insertions(+), 67 deletions(-) diff --git a/xen/arch/arm/tee/ffa_partinfo.c b/xen/arch/arm/tee/ffa_partinfo.c index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/tee/ffa_partinfo.c +++ b/xen/arch/arm/tee/ffa_partinfo.c @@ -XXX,XX +XXX,XX @@ #include <xen/const.h> #include <xen/sizes.h> #include <xen/types.h> +#include <xen/xmalloc.h> #include <asm/smccc.h> #include <asm/regs.h> @@ -XXX,XX +XXX,XX @@ 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; +/* SP list cache (secure endpoints only); populated at init. */ +static void *sp_list __read_mostly; +static uint32_t sp_list_count __read_mostly; +static uint32_t sp_list_entry_size __read_mostly; static int32_t ffa_partition_info_get(struct ffa_uuid uuid, uint32_t flags, uint32_t *count, uint32_t *fpi_size) { @@ -XXX,XX +XXX,XX @@ static int32_t ffa_copy_info(void **dst, void *dst_end, const void *src, return FFA_RET_OK; } -static int32_t ffa_get_sp_count(struct ffa_uuid uuid, uint32_t *sp_count) +static bool ffa_sp_entry_matches_uuid(const void *entry, struct ffa_uuid uuid) { - uint32_t src_size; + const struct ffa_partition_info_1_1 *fpi = entry; + struct ffa_uuid sp_uuid; + + if ( ffa_uuid_is_nil(uuid) ) + return true; - return ffa_partition_info_get(uuid, FFA_PARTITION_INFO_GET_COUNT_FLAG, - sp_count, &src_size); + if ( sp_list_entry_size < sizeof(*fpi) ) + return false; + + memcpy(&sp_uuid, fpi->uuid, sizeof(sp_uuid)); + return ffa_uuid_equal(uuid, sp_uuid); } -static int32_t ffa_get_sp_partinfo(struct ffa_uuid uuid, uint32_t *sp_count, - void **dst_buf, void *end_buf, - uint32_t dst_size) +static int32_t ffa_get_sp_count(struct ffa_uuid uuid, uint32_t *sp_count) { - int32_t ret; - int32_t release_ret; - uint32_t src_size, real_sp_count; - void *src_buf; uint32_t count = 0; - bool notify_fw = false; - - /* We need to use the RX buffer to receive the list */ - src_buf = ffa_rxtx_spmc_rx_acquire(); - if ( !src_buf ) - return FFA_RET_DENIED; - - ret = ffa_partition_info_get(uuid, 0, &real_sp_count, &src_size); - if ( ret ) - goto out; - notify_fw = true; + uint32_t n; - /* Validate the src_size we got */ - if ( src_size < sizeof(struct ffa_partition_info_1_0) || - src_size >= FFA_PAGE_SIZE ) + for ( n = 0; n < sp_list_count; n++ ) { - ret = FFA_RET_NOT_SUPPORTED; - goto out; + void *entry = sp_list + n * sp_list_entry_size; + + if ( ffa_sp_entry_matches_uuid(entry, uuid) ) + count++; } - /* - * 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; + *sp_count = count; - /* 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; - } + if ( !ffa_uuid_is_nil(uuid) && !count ) + return FFA_RET_INVALID_PARAMETERS; - for ( uint32_t sp_num = 0; sp_num < real_sp_count; sp_num++ ) - { - struct ffa_partition_info_1_1 *fpi = src_buf; + return FFA_RET_OK; +} - /* filter out SP not following bit 15 convention if any */ - if ( FFA_ID_IS_SECURE(fpi->id) ) - { - /* - * If VM is 1.0 but firmware is 1.1 we could have several entries - * with the same ID but different UUIDs. In this case the VM will - * get a list with several time the same ID. - * This is a non-compliance to the specification but 1.0 VMs should - * handle that on their own to simplify Xen implementation. - */ +static int32_t ffa_get_sp_partinfo(struct ffa_uuid uuid, uint32_t *sp_count, + void **dst_buf, void *end_buf, + uint32_t dst_size) +{ + int32_t ret; + uint32_t count = 0; + uint32_t n; - ret = ffa_copy_info(dst_buf, end_buf, src_buf, dst_size, src_size); - if ( ret ) - goto out; + for ( n = 0; n < sp_list_count; n++ ) + { + void *entry = sp_list + n * sp_list_entry_size; - count++; - } + if ( !ffa_sp_entry_matches_uuid(entry, uuid) ) + continue; - src_buf += src_size; + /* + * If VM is 1.0 but firmware is 1.1 we could have several entries + * with the same ID but different UUIDs. In this case the VM will + * get a list with several time the same ID. + * This is a non-compliance to the specification but 1.0 VMs should + * handle that on their own to simplify Xen implementation. + */ + ret = ffa_copy_info(dst_buf, end_buf, entry, dst_size, + sp_list_entry_size); + if ( ret ) + return ret; + + count++; } *sp_count = count; -out: - release_ret = ffa_rxtx_spmc_rx_release(notify_fw); - if ( release_ret ) - gprintk(XENLOG_WARNING, - "ffa: Error releasing SPMC RX buffer: %d\n", release_ret); - return ret; + if ( !ffa_uuid_is_nil(uuid) && !count ) + return FFA_RET_INVALID_PARAMETERS; + + return FFA_RET_OK; } static int32_t ffa_get_vm_partinfo(struct ffa_uuid uuid, uint32_t start_index, @@ -XXX,XX +XXX,XX @@ static int32_t ffa_direct_req_send_vm(uint16_t sp_id, uint16_t vm_id, return res; } +static void ffa_sp_list_cache_free(void) +{ + XFREE(sp_list); + sp_list = NULL; + sp_list_count = 0; + sp_list_entry_size = 0; +} + static void uninit_subscribers(void) { subscr_vm_created_count = 0; @@ -XXX,XX +XXX,XX @@ static void uninit_subscribers(void) XFREE(subscr_vm_destroyed); } +static bool ffa_sp_list_cache_init(const void *buf, uint32_t count, + uint32_t fpi_size) +{ + const uint8_t *src = buf; + uint32_t secure_count = 0; + uint32_t n, idx = 0; + bool warned = false; + + if ( fpi_size < sizeof(struct ffa_partition_info_1_0) || + fpi_size >= FFA_PAGE_SIZE ) + return false; + + if ( count > (FFA_RXTX_PAGE_COUNT * FFA_PAGE_SIZE) / fpi_size ) + return false; + + for ( n = 0; n < count; n++ ) + { + const struct ffa_partition_info_1_0 *fpi = + (const void *)(src + n * fpi_size); + + if ( !FFA_ID_IS_SECURE(fpi->id) ) + { + if ( !warned ) + { + printk_once(XENLOG_ERR + "ffa: Firmware is not using bit 15 convention for IDs !!\n"); + warned = true; + } + printk(XENLOG_ERR + "ffa: Secure partition with id 0x%04x cannot be used\n", + fpi->id); + continue; + } + + secure_count++; + } + + if ( secure_count ) + { + sp_list = xzalloc_bytes(secure_count * fpi_size); + if ( !sp_list ) + return false; + } + + sp_list_count = secure_count; + sp_list_entry_size = fpi_size; + + for ( n = 0; n < count; n++ ) + { + const struct ffa_partition_info_1_0 *fpi = + (const void *)(src + n * fpi_size); + + if ( !FFA_ID_IS_SECURE(fpi->id) ) + continue; + + memcpy(sp_list + idx * fpi_size, fpi, fpi_size); + idx++; + } + + return true; +} + static bool init_subscribers(void *buf, uint16_t count, uint32_t fpi_size) { uint16_t n; @@ -XXX,XX +XXX,XX @@ bool ffa_partinfo_init(void) goto out; } - ret = init_subscribers(spmc_rx, count, fpi_size); + if ( !ffa_sp_list_cache_init(spmc_rx, count, fpi_size) ) + { + printk(XENLOG_ERR "ffa: Failed to cache SP list\n"); + goto out; + } + + ret = init_subscribers(sp_list, sp_list_count, sp_list_entry_size); out: e = ffa_rxtx_spmc_rx_release(notify_fw); if ( e ) printk(XENLOG_WARNING "ffa: Error releasing SPMC RX buffer: %d\n", e); + if ( !ret ) + uninit_subscribers(); + if ( !ret ) + ffa_sp_list_cache_free(); return ret; } -- 2.52.0
The init-time SP cache already contains partition properties, but the code still builds separate subscriber arrays for VM created/destroyed notifications. That duplicates state and allocation. Use the cached SP list directly to: - decide which SPs receive created/destroyed notifications - build the per-domain destroy bitmap - skip destroy notifications for SPs not notified on create No functional changes. Signed-off-by: Bertrand Marquis <bertrand.marquis@arm.com> --- xen/arch/arm/tee/ffa_partinfo.c | 155 ++++++++------------------------ 1 file changed, 36 insertions(+), 119 deletions(-) diff --git a/xen/arch/arm/tee/ffa_partinfo.c b/xen/arch/arm/tee/ffa_partinfo.c index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/tee/ffa_partinfo.c +++ b/xen/arch/arm/tee/ffa_partinfo.c @@ -XXX,XX +XXX,XX @@ struct ffa_partition_info_1_1 { 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; - /* SP list cache (secure endpoints only); populated at init. */ static void *sp_list __read_mostly; static uint32_t sp_list_count __read_mostly; @@ -XXX,XX +XXX,XX @@ static void ffa_sp_list_cache_free(void) sp_list_entry_size = 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 ffa_sp_list_cache_init(const void *buf, uint32_t count, uint32_t fpi_size) { @@ -XXX,XX +XXX,XX @@ static bool ffa_sp_list_cache_init(const void *buf, uint32_t count, return true; } -static bool init_subscribers(void *buf, uint16_t count, uint32_t fpi_size) -{ - uint16_t n; - uint16_t c_pos; - uint16_t d_pos; - struct ffa_partition_info_1_1 *fpi; - - if ( fpi_size < sizeof(struct ffa_partition_info_1_1) ) - { - printk(XENLOG_ERR "ffa: partition info size invalid: %u\n", fpi_size); - return false; - } - - subscr_vm_created_count = 0; - subscr_vm_destroyed_count = 0; - for ( n = 0; n < count; n++ ) - { - fpi = buf + n * fpi_size; - - /* - * We need to have secure partitions using bit 15 set convention for - * secure partition IDs. - * Inform the user with a log and discard giving created or destroy - * event to those IDs. - */ - if ( !FFA_ID_IS_SECURE(fpi->id) ) - { - printk_once(XENLOG_ERR - "ffa: Firmware is not using bit 15 convention for IDs !!\n"); - printk(XENLOG_ERR - "ffa: Secure partition with id 0x%04x cannot be used\n", - fpi->id); - } - else - { - if ( fpi->partition_properties & FFA_PART_PROP_NOTIF_CREATED ) - subscr_vm_created_count++; - if ( fpi->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++ ) - { - fpi = buf + n * fpi_size; - - if ( FFA_ID_IS_SECURE(fpi->id) ) - { - if ( fpi->partition_properties & FFA_PART_PROP_NOTIF_CREATED ) - subscr_vm_created[c_pos++] = fpi->id; - if ( fpi->partition_properties & FFA_PART_PROP_NOTIF_DESTROYED ) - subscr_vm_destroyed[d_pos++] = fpi->id; - } - } - - return true; -} - - - bool ffa_partinfo_init(void) { bool ret = false; @@ -XXX,XX +XXX,XX @@ bool ffa_partinfo_init(void) goto out; } - ret = init_subscribers(sp_list, sp_list_count, sp_list_entry_size); + if ( sp_list_entry_size < sizeof(struct ffa_partition_info_1_1) ) + { + printk(XENLOG_ERR "ffa: partition info size invalid: %u\n", + sp_list_entry_size); + goto out; + } + ret = true; out: e = ffa_rxtx_spmc_rx_release(notify_fw); if ( e ) printk(XENLOG_WARNING "ffa: Error releasing SPMC RX buffer: %d\n", e); - if ( !ret ) - uninit_subscribers(); if ( !ret ) ffa_sp_list_cache_free(); return ret; } -static bool is_in_subscr_list(const uint16_t *subscr, uint16_t start, - uint16_t end, uint16_t sp_id) +static void vm_destroy_bitmap_init(struct ffa_ctx *ctx, + unsigned int first_unnotified) { unsigned int n; + struct ffa_partition_info_1_1 *fpi; - for ( n = start; n < end; n++ ) + for ( n = 0; n < sp_list_count; n++ ) { - if ( subscr[n] == sp_id ) - return true; - } - - return false; -} + fpi = sp_list + n * sp_list_entry_size; -static void vm_destroy_bitmap_init(struct ffa_ctx *ctx, - unsigned int create_signal_count) -{ - unsigned int n; + if ( !(fpi->partition_properties & FFA_PART_PROP_NOTIF_DESTROYED) ) + continue; - 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]) ) + if ( (fpi->partition_properties & FFA_PART_PROP_NOTIF_CREATED) && + n >= first_unnotified ) continue; set_bit(n, ctx->vm_destroy_bitmap); @@ -XXX,XX +XXX,XX @@ static void vm_destroy_bitmap_init(struct ffa_ctx *ctx, int32_t ffa_partinfo_domain_init(struct domain *d) { - unsigned int count = BITS_TO_LONGS(subscr_vm_destroyed_count); + unsigned int count = BITS_TO_LONGS(sp_list_count); struct ffa_ctx *ctx = d->arch.tee; unsigned int n; + unsigned int first_unnotified = sp_list_count; int32_t res; + struct ffa_partition_info_1_1 *fpi; - if ( !ffa_fw_supports_fid(FFA_MSG_SEND_DIRECT_REQ_32) ) + if ( !ffa_fw_supports_fid(FFA_MSG_SEND_DIRECT_REQ_32) || !sp_list_count ) return 0; ctx->vm_destroy_bitmap = xzalloc_array(unsigned long, count); if ( !ctx->vm_destroy_bitmap ) return -ENOMEM; - for ( n = 0; n < subscr_vm_created_count; n++ ) + for ( n = 0; n < sp_list_count; n++ ) { - res = ffa_direct_req_send_vm(subscr_vm_created[n], ffa_get_vm_id(d), + fpi = sp_list + n * sp_list_entry_size; + if ( !(fpi->partition_properties & FFA_PART_PROP_NOTIF_CREATED) ) + continue; + + res = ffa_direct_req_send_vm(fpi->id, 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); + ffa_get_vm_id(d), fpi->id, res); + first_unnotified = n; break; } } - vm_destroy_bitmap_init(ctx, n); + vm_destroy_bitmap_init(ctx, first_unnotified); - if ( n != subscr_vm_created_count ) + if ( first_unnotified != sp_list_count ) return -EIO; return 0; @@ -XXX,XX +XXX,XX @@ bool ffa_partinfo_domain_destroy(struct domain *d) struct ffa_ctx *ctx = d->arch.tee; unsigned int n; int32_t res; + struct ffa_partition_info_1_1 *fpi; if ( !ctx->vm_destroy_bitmap ) return true; - for ( n = 0; n < subscr_vm_destroyed_count; n++ ) + for ( n = 0; n < sp_list_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), + fpi = sp_list + n * sp_list_entry_size; + res = ffa_direct_req_send_vm(fpi->id, ffa_get_vm_id(d), FFA_MSG_SEND_VM_DESTROYED); if ( res && printk_ratelimit() ) printk(XENLOG_WARNING "%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); + d, ffa_get_vm_id(d), fpi->id, res); /* * For these two error codes the hypervisor is expected to resend @@ -XXX,XX +XXX,XX @@ bool ffa_partinfo_domain_destroy(struct domain *d) clear_bit(n, ctx->vm_destroy_bitmap); } - if ( bitmap_empty(ctx->vm_destroy_bitmap, subscr_vm_destroyed_count) ) + if ( bitmap_empty(ctx->vm_destroy_bitmap, sp_list_count) ) XFREE(ctx->vm_destroy_bitmap); return !ctx->vm_destroy_bitmap; -- 2.52.0
FF-A v1.2 defines PARTITION_INFO_GET_REGS for register-based partition info retrieval, but Xen currently only supports the buffer-based GET path for guests. Implement GET_REGS using the cached SP list and VM entries, including the register window layout and input validation. Track VM list changes via the partinfo tag and use it to validate GET_REGS tag inputs. Ensure that when a non-Nil UUID is specified, the UUID fields in both GET and GET_REGS results are MBZ as required by the specification. PARTITION_INFO_GET_REGS is available to v1.2 guests, returning cached SP entries and VM entries with UUIDs zeroed for non-Nil UUID queries. Signed-off-by: Bertrand Marquis <bertrand.marquis@arm.com> --- xen/arch/arm/tee/ffa.c | 16 +++ xen/arch/arm/tee/ffa_partinfo.c | 211 ++++++++++++++++++++++++++++++++ xen/arch/arm/tee/ffa_private.h | 4 +- 3 files changed, 230 insertions(+), 1 deletion(-) diff --git a/xen/arch/arm/tee/ffa.c b/xen/arch/arm/tee/ffa.c index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/tee/ffa.c +++ b/xen/arch/arm/tee/ffa.c @@ -XXX,XX +XXX,XX @@ * - doesn't support signalling the secondary scheduler of pending * notification for secure partitions * - doesn't support notifications for Xen itself + * o FFA_PARTITION_INFO_GET/GET_REGS: + * - v1.0 guests may see duplicate SP IDs when firmware provides UUIDs + * - SP list is cached at init; SPMC tag changes are not tracked + * between calls + * - SP list is capped at FFA_MAX_NUM_SP entries * * There are some large locked sections with ffa_spmc_tx_lock and * ffa_spmc_rx_lock. Especially the ffa_spmc_tx_lock spinlock used @@ -XXX,XX +XXX,XX @@ static bool ffa_negotiate_version(struct cpu_user_regs *regs) write_lock(&ffa_ctx_list_rwlock); list_add_tail(&ctx->ctx_list, &ffa_ctx_head); write_unlock(&ffa_ctx_list_rwlock); + ffa_partinfo_inc_tag(); } goto out_continue; @@ -XXX,XX +XXX,XX @@ static void handle_features(struct cpu_user_regs *regs) case FFA_FEATURE_SCHEDULE_RECV_INTR: ffa_set_regs_success(regs, GUEST_FFA_SCHEDULE_RECV_INTR_ID, 0); break; + case FFA_PARTITION_INFO_GET_REGS: + if ( ACCESS_ONCE(ctx->guest_vers) >= FFA_VERSION_1_2 ) + ffa_set_regs_success(regs, 0, 0); + else + ffa_set_regs_error(regs, FFA_RET_NOT_SUPPORTED); + break; case FFA_NOTIFICATION_BIND: case FFA_NOTIFICATION_UNBIND: @@ -XXX,XX +XXX,XX @@ static bool ffa_handle_call(struct cpu_user_regs *regs) case FFA_PARTITION_INFO_GET: ffa_handle_partition_info_get(regs); return true; + case FFA_PARTITION_INFO_GET_REGS: + ffa_handle_partition_info_get_regs(regs); + return true; case FFA_RX_RELEASE: e = ffa_rx_release(ctx); break; @@ -XXX,XX +XXX,XX @@ static int ffa_domain_teardown(struct domain *d) write_lock(&ffa_ctx_list_rwlock); list_del(&ctx->ctx_list); write_unlock(&ffa_ctx_list_rwlock); + ffa_partinfo_inc_tag(); } ffa_rxtx_domain_destroy(d); diff --git a/xen/arch/arm/tee/ffa_partinfo.c b/xen/arch/arm/tee/ffa_partinfo.c index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/tee/ffa_partinfo.c +++ b/xen/arch/arm/tee/ffa_partinfo.c @@ -XXX,XX +XXX,XX @@ struct ffa_partition_info_1_1 { uint8_t uuid[16]; }; +/* Registers a3..a17 (15 regs) carry partition descriptors, 3 regs each. */ +#define FFA_PARTINFO_REG_MAX_ENTRIES \ + ((15 * sizeof(uint64_t)) / sizeof(struct ffa_partition_info_1_1)) + /* SP list cache (secure endpoints only); populated at init. */ static void *sp_list __read_mostly; static uint32_t sp_list_count __read_mostly; static uint32_t sp_list_entry_size __read_mostly; + +/* SP list is static; tag only moves when VMs are added/removed. */ +static atomic_t ffa_partinfo_tag = ATOMIC_INIT(1); + +void ffa_partinfo_inc_tag(void) +{ + atomic_inc(&ffa_partinfo_tag); +} + +static inline uint16_t ffa_partinfo_get_tag(void) +{ + /* + * Tag moves with VM list changes only. + * + * Limitation: we cannot detect an SPMC tag change between calls because we + * do not retain the previous SPMC tag; we only refresh it via the mandatory + * start_index=0 call and assume it stays stable while combined_tag (our + * VM/SP-count tag) is used for guest validation. This means SPMC tag + * changes alone will not trigger RETRY. + */ + if ( IS_ENABLED(CONFIG_FFA_VM_TO_VM) ) + return atomic_read(&ffa_partinfo_tag) & GENMASK(15, 0); + else + return 1; +} static int32_t ffa_partition_info_get(struct ffa_uuid uuid, uint32_t flags, uint32_t *count, uint32_t *fpi_size) { @@ -XXX,XX +XXX,XX @@ static int32_t ffa_get_sp_partinfo(struct ffa_uuid uuid, uint32_t *sp_count, for ( n = 0; n < sp_list_count; n++ ) { void *entry = sp_list + n * sp_list_entry_size; + void *dst_pos; if ( !ffa_sp_entry_matches_uuid(entry, uuid) ) continue; @@ -XXX,XX +XXX,XX @@ static int32_t ffa_get_sp_partinfo(struct ffa_uuid uuid, uint32_t *sp_count, * This is a non-compliance to the specification but 1.0 VMs should * handle that on their own to simplify Xen implementation. */ + dst_pos = *dst_buf; ret = ffa_copy_info(dst_buf, end_buf, entry, dst_size, sp_list_entry_size); if ( ret ) return ret; + if ( !ffa_uuid_is_nil(uuid) && + dst_size >= sizeof(struct ffa_partition_info_1_1) ) + { + struct ffa_partition_info_1_1 *fpi = dst_pos; + + memset(fpi->uuid, 0, sizeof(fpi->uuid)); + } + count++; } @@ -XXX,XX +XXX,XX @@ static int32_t ffa_get_sp_partinfo(struct ffa_uuid uuid, uint32_t *sp_count, return FFA_RET_OK; } +static uint16_t ffa_get_sp_partinfo_regs(struct ffa_uuid uuid, + uint16_t start_index, + uint64_t *out_regs, + uint16_t max_entries) +{ + uint32_t idx = 0; + uint16_t filled = 0; + uint32_t n; + + for ( n = 0; n < sp_list_count && filled < max_entries; n++ ) + { + void *entry = sp_list + n * sp_list_entry_size; + + if ( !ffa_sp_entry_matches_uuid(entry, uuid) ) + continue; + + if ( idx++ < start_index ) + continue; + + memcpy(&out_regs[filled * 3], entry, + sizeof(struct ffa_partition_info_1_1)); + if ( !ffa_uuid_is_nil(uuid) ) + { + out_regs[filled * 3 + 1] = 0; + out_regs[filled * 3 + 2] = 0; + } + filled++; + } + + return filled; +} + static int32_t ffa_get_vm_partinfo(struct ffa_uuid uuid, uint32_t start_index, uint32_t *vm_count, void **dst_buf, void *end_buf, uint32_t dst_size) @@ -XXX,XX +XXX,XX @@ out: } } +void ffa_handle_partition_info_get_regs(struct cpu_user_regs *regs) +{ + struct domain *d = current->domain; + struct ffa_ctx *ctx = d->arch.tee; + struct ffa_uuid uuid; + uint32_t sp_count = 0, vm_count = 0, total_count; + uint16_t start_index, tag; + uint16_t num_entries = 0; + uint64_t x3 = get_user_reg(regs, 3); + int32_t ret = FFA_RET_OK; + uint64_t out_regs[18] = { 0 }; + unsigned int n; + uint16_t tag_out; + + if ( ACCESS_ONCE(ctx->guest_vers) < FFA_VERSION_1_2 ) + { + ret = FFA_RET_NOT_SUPPORTED; + goto out; + } + + /* + * Registers a3..a17 (15 regs) carry partition descriptors, 3 regs each. + * For FF-A 1.2, that yields a maximum of 5 entries per GET_REGS call. + * Enforce the assumed layout so window sizing stays correct. + */ + BUILD_BUG_ON(FFA_PARTINFO_REG_MAX_ENTRIES != 5); + + for ( n = 4; n <= 17; n++ ) + { + if ( get_user_reg(regs, n) ) + { + ret = FFA_RET_INVALID_PARAMETERS; + goto out; + } + } + + if ( x3 >> 32 ) + { + ret = FFA_RET_INVALID_PARAMETERS; + goto out; + } + + start_index = x3 & GENMASK(15, 0); + tag = (x3 >> 16) & GENMASK(15, 0); + + /* Start index must allow room for up to 5 entries without 16-bit overflow. */ + if ( start_index > (GENMASK(15, 0) - (FFA_PARTINFO_REG_MAX_ENTRIES - 1)) ) + { + ret = FFA_RET_INVALID_PARAMETERS; + goto out; + } + + uuid.val[0] = get_user_reg(regs, 1); + uuid.val[1] = get_user_reg(regs, 2); + + if ( sp_list_count && + sp_list_entry_size != sizeof(struct ffa_partition_info_1_1) ) + { + ret = FFA_RET_NOT_SUPPORTED; + goto out; + } + + tag_out = ffa_partinfo_get_tag(); + + if ( start_index == 0 ) + { + if ( tag ) + { + ret = FFA_RET_INVALID_PARAMETERS; + goto out; + } + } + else if ( tag != tag_out ) + { + ret = FFA_RET_RETRY; + goto out; + } + + if ( ffa_uuid_is_nil(uuid) ) + { + if ( IS_ENABLED(CONFIG_FFA_VM_TO_VM) ) + vm_count = get_ffa_vm_count(); + else + vm_count = 1; /* Caller VM only */ + } + + ret = ffa_get_sp_count(uuid, &sp_count); + if ( ret ) + goto out; + + total_count = sp_count + vm_count; + + if ( total_count == 0 || start_index >= total_count ) + { + ret = FFA_RET_INVALID_PARAMETERS; + goto out; + } + + if ( start_index < sp_count ) + num_entries = ffa_get_sp_partinfo_regs(uuid, start_index, &out_regs[3], + FFA_PARTINFO_REG_MAX_ENTRIES); + + if ( num_entries < FFA_PARTINFO_REG_MAX_ENTRIES ) + { + uint32_t vm_start = start_index > sp_count ? + start_index - sp_count : 0; + uint32_t filled = 0; + void *vm_dst = &out_regs[3 + num_entries * 3]; + void *vm_end = &out_regs[18]; + + ret = ffa_get_vm_partinfo(uuid, vm_start, &filled, &vm_dst, vm_end, + sizeof(struct ffa_partition_info_1_1)); + if ( ret != FFA_RET_OK && ret != FFA_RET_NO_MEMORY ) + goto out; + + num_entries += filled; + } + + if ( num_entries == 0 ) + { + ret = FFA_RET_INVALID_PARAMETERS; + goto out; + } + + out_regs[0] = FFA_SUCCESS_64; + out_regs[2] = ((uint64_t)sizeof(struct ffa_partition_info_1_1) << 48) | + ((uint64_t)tag_out << 32) | + ((uint64_t)(start_index + num_entries - 1) << 16) | + ((uint64_t)(total_count - 1) & GENMASK(15, 0)); + + for ( n = 0; n < ARRAY_SIZE(out_regs); n++ ) + set_user_reg(regs, n, out_regs[n]); + + return; + +out: + if ( ret ) + ffa_set_regs_error(regs, ret); +} + static int32_t ffa_direct_req_send_vm(uint16_t sp_id, uint16_t vm_id, uint8_t msg) { diff --git a/xen/arch/arm/tee/ffa_private.h b/xen/arch/arm/tee/ffa_private.h index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/tee/ffa_private.h +++ b/xen/arch/arm/tee/ffa_private.h @@ -XXX,XX +XXX,XX @@ #define FFA_MSG_SEND2 0x84000086U #define FFA_CONSOLE_LOG_32 0x8400008AU #define FFA_CONSOLE_LOG_64 0xC400008AU -#define FFA_PARTITION_INFO_GET_REGS 0x8400008BU +#define FFA_PARTITION_INFO_GET_REGS 0xC400008BU #define FFA_MSG_SEND_DIRECT_REQ2 0xC400008DU #define FFA_MSG_SEND_DIRECT_RESP2 0xC400008EU @@ -XXX,XX +XXX,XX @@ bool ffa_partinfo_init(void); int32_t ffa_partinfo_domain_init(struct domain *d); bool ffa_partinfo_domain_destroy(struct domain *d); void ffa_handle_partition_info_get(struct cpu_user_regs *regs); +void ffa_handle_partition_info_get_regs(struct cpu_user_regs *regs); +void ffa_partinfo_inc_tag(void); int32_t ffa_endpoint_domain_lookup(uint16_t endpoint_id, struct domain **d_out, struct ffa_ctx **ctx_out); -- 2.52.0
This series implements FF-A 1.2 PARTITION_INFO_GET_REGS support, providing register-based partition info retrieval as an efficient alternative to the RX buffer-based PARTITION_INFO_GET. The serie is also introducing a caching system for the list of secure partitions to prevent retrieving it on each VM call as we expect it to be static and not changing over time. The patches are organized as follows: - patch 1 is modifying existing code to prepare for GET_REGS support by adding a start_index in the existing internal function - patch 2 is introducing the secure partition list caching - patch 3 is removing the existing suscribers list to directly use the cached secure partition list - patch 4 is adding GET_REGS support Changes since v1 (Jens comments): - rework SP cache handling to enforce 1.1+ structure size - ensure unaligned-safe reads - remove checks for SBZ fields - tighten tag handling Bertrand Marquis (4): xen/arm: ffa: Add start_index to VM partinfo helper xen/arm: ffa: Cache SP partition info at init xen/arm: ffa: Drop SP subscriber lists xen/arm: ffa: Add cached GET_REGS support xen/arch/arm/tee/ffa.c | 23 +- xen/arch/arm/tee/ffa_partinfo.c | 565 ++++++++++++++++++++++---------- xen/arch/arm/tee/ffa_private.h | 4 +- 3 files changed, 410 insertions(+), 182 deletions(-) -- 2.52.0
Windowed GET_REGS retrieval needs to emit VM entries starting from an arbitrary index, but ffa_get_vm_partinfo() always starts from index 0. Add a start_index parameter to ffa_get_vm_partinfo() and skip entries until the local index reaches start_index. Update ffa_handle_partition_info_get() to pass start_index=0 to preserve existing behavior. No functional changes. Signed-off-by: Bertrand Marquis <bertrand.marquis@arm.com> Reviewed-by: Jens Wiklander <jens.wiklander@linaro.org> --- Changes since v1: - Add Jens R-b --- xen/arch/arm/tee/ffa_partinfo.c | 61 +++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/xen/arch/arm/tee/ffa_partinfo.c b/xen/arch/arm/tee/ffa_partinfo.c index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/tee/ffa_partinfo.c +++ b/xen/arch/arm/tee/ffa_partinfo.c @@ -XXX,XX +XXX,XX @@ out: return ret; } -static int32_t ffa_get_vm_partinfo(struct ffa_uuid uuid, uint32_t *vm_count, - void **dst_buf, void *end_buf, - uint32_t dst_size) +static int32_t ffa_get_vm_partinfo(struct ffa_uuid uuid, uint32_t start_index, + uint32_t *vm_count, void **dst_buf, + void *end_buf, uint32_t dst_size) { struct domain *d = current->domain; struct ffa_ctx *curr_ctx = d->arch.tee; struct ffa_ctx *dest_ctx; uint32_t count = 0; + uint32_t idx = 0; int32_t ret = FFA_RET_OK; /* * We do not have UUID info for VMs so use the 1.0 structure so that we set @@ -XXX,XX +XXX,XX @@ static int32_t ffa_get_vm_partinfo(struct ffa_uuid uuid, uint32_t *vm_count, if ( ACCESS_ONCE(curr_ctx->guest_vers) >= FFA_VERSION_1_2 ) { /* Add caller VM information */ - info.id = curr_ctx->ffa_id; - info.execution_context = curr_ctx->num_vcpus; - info.partition_properties = FFA_PART_VM_PROP; - if ( is_64bit_domain(d) ) - info.partition_properties |= FFA_PART_PROP_AARCH64_STATE; - - ret = ffa_copy_info(dst_buf, end_buf, &info, dst_size, sizeof(info)); - if ( ret ) - return ret; + if ( start_index == 0) + { + info.id = curr_ctx->ffa_id; + info.execution_context = curr_ctx->num_vcpus; + info.partition_properties = FFA_PART_VM_PROP; + if ( is_64bit_domain(d) ) + info.partition_properties |= FFA_PART_PROP_AARCH64_STATE; - count++; + ret = ffa_copy_info(dst_buf, end_buf, &info, dst_size, + sizeof(info)); + if ( ret ) + return ret; + count++; + } + idx++; } if ( IS_ENABLED(CONFIG_FFA_VM_TO_VM) ) @@ -XXX,XX +XXX,XX @@ static int32_t ffa_get_vm_partinfo(struct ffa_uuid uuid, uint32_t *vm_count, if ( dest_ctx == curr_ctx ) continue; - info.id = dest_ctx->ffa_id; - info.execution_context = dest_ctx->num_vcpus; - info.partition_properties = FFA_PART_VM_PROP; - if ( dest_ctx->is_64bit ) - info.partition_properties |= FFA_PART_PROP_AARCH64_STATE; - - ret = ffa_copy_info(dst_buf, end_buf, &info, dst_size, - sizeof(info)); - if ( ret ) + if ( idx >= start_index ) { - read_unlock(&ffa_ctx_list_rwlock); - return ret; + info.id = dest_ctx->ffa_id; + info.execution_context = dest_ctx->num_vcpus; + info.partition_properties = FFA_PART_VM_PROP; + if ( dest_ctx->is_64bit ) + info.partition_properties |= FFA_PART_PROP_AARCH64_STATE; + + ret = ffa_copy_info(dst_buf, end_buf, &info, dst_size, + sizeof(info)); + if ( ret ) + { + read_unlock(&ffa_ctx_list_rwlock); + return ret; + } + count++; } - count++; + idx++; } read_unlock(&ffa_ctx_list_rwlock); } @@ -XXX,XX +XXX,XX @@ void ffa_handle_partition_info_get(struct cpu_user_regs *regs) goto out_rx_release; } - ret = ffa_get_vm_partinfo(uuid, &ffa_vm_count, &dst_buf, end_buf, + ret = ffa_get_vm_partinfo(uuid, 0, &ffa_vm_count, &dst_buf, end_buf, dst_size); out_rx_release: -- 2.52.0
FFA_PARTITION_INFO_GET currently queries the SPMC on each call and walks the RX buffer every time. The SP list is expected to be static, so repeated firmware calls and validation are unnecessary. Cache the SPMC partition-info list at init time, keeping only secure endpoints, and reuse the cached entries for SP count and partition-info responses. Initialize the VM create/destroy subscriber lists from the cached list and free the cache on init failure. SP partition info now reflects the init-time snapshot and will not change. Signed-off-by: Bertrand Marquis <bertrand.marquis@arm.com> --- Changes since v1: - removed unneeded NULL assignment after XFREE - remove warned usage and only rely on printk_once to warn on the 15-bit convention - rework ffa_partinfo_init cleanup - ensure we do not do unaligned accesses when building the SP cache - enforce SPMC partinfo size to be at least 1.1 structure size when creating and remove tests when using the cache --- xen/arch/arm/tee/ffa_partinfo.c | 216 +++++++++++++++++++++----------- 1 file changed, 146 insertions(+), 70 deletions(-) diff --git a/xen/arch/arm/tee/ffa_partinfo.c b/xen/arch/arm/tee/ffa_partinfo.c index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/tee/ffa_partinfo.c +++ b/xen/arch/arm/tee/ffa_partinfo.c @@ -XXX,XX +XXX,XX @@ #include <xen/const.h> #include <xen/sizes.h> #include <xen/types.h> +#include <xen/unaligned.h> +#include <xen/xmalloc.h> #include <asm/smccc.h> #include <asm/regs.h> @@ -XXX,XX +XXX,XX @@ 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; +/* SP list cache (secure endpoints only); populated at init. */ +static void *sp_list __read_mostly; +static uint32_t sp_list_count __read_mostly; +static uint32_t sp_list_entry_size __read_mostly; static int32_t ffa_partition_info_get(struct ffa_uuid uuid, uint32_t flags, uint32_t *count, uint32_t *fpi_size) { @@ -XXX,XX +XXX,XX @@ static int32_t ffa_copy_info(void **dst, void *dst_end, const void *src, return FFA_RET_OK; } -static int32_t ffa_get_sp_count(struct ffa_uuid uuid, uint32_t *sp_count) +static uint16_t ffa_sp_entry_read_id(const void *entry) { - uint32_t src_size; - - return ffa_partition_info_get(uuid, FFA_PARTITION_INFO_GET_COUNT_FLAG, - sp_count, &src_size); + return get_unaligned_t(uint16_t, + (const uint8_t *)entry + + offsetof(struct ffa_partition_info_1_0, id)); } -static int32_t ffa_get_sp_partinfo(struct ffa_uuid uuid, uint32_t *sp_count, - void **dst_buf, void *end_buf, - uint32_t dst_size) +static bool ffa_sp_entry_matches_uuid(const void *entry, struct ffa_uuid uuid) { - int32_t ret; - int32_t release_ret; - uint32_t src_size, real_sp_count; - void *src_buf; - uint32_t count = 0; - bool notify_fw = false; + struct ffa_uuid sp_uuid; - /* We need to use the RX buffer to receive the list */ - src_buf = ffa_rxtx_spmc_rx_acquire(); - if ( !src_buf ) - return FFA_RET_DENIED; + if ( ffa_uuid_is_nil(uuid) ) + return true; - ret = ffa_partition_info_get(uuid, 0, &real_sp_count, &src_size); - if ( ret ) - goto out; - notify_fw = true; + memcpy(&sp_uuid, + (const uint8_t *)entry + + offsetof(struct ffa_partition_info_1_1, uuid), + sizeof(sp_uuid)); + return ffa_uuid_equal(uuid, sp_uuid); +} - /* Validate the src_size we got */ - if ( src_size < sizeof(struct ffa_partition_info_1_0) || - src_size >= FFA_PAGE_SIZE ) +static int32_t ffa_get_sp_count(struct ffa_uuid uuid, uint32_t *sp_count) +{ + uint32_t count = 0; + uint32_t n; + + for ( n = 0; n < sp_list_count; n++ ) { - ret = FFA_RET_NOT_SUPPORTED; - goto out; + void *entry = sp_list + n * sp_list_entry_size; + + if ( ffa_sp_entry_matches_uuid(entry, uuid) ) + count++; } - /* - * 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; + *sp_count = count; - /* 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; - } + if ( !ffa_uuid_is_nil(uuid) && !count ) + return FFA_RET_INVALID_PARAMETERS; - for ( uint32_t sp_num = 0; sp_num < real_sp_count; sp_num++ ) - { - struct ffa_partition_info_1_1 *fpi = src_buf; + return FFA_RET_OK; +} - /* filter out SP not following bit 15 convention if any */ - if ( FFA_ID_IS_SECURE(fpi->id) ) - { - /* - * If VM is 1.0 but firmware is 1.1 we could have several entries - * with the same ID but different UUIDs. In this case the VM will - * get a list with several time the same ID. - * This is a non-compliance to the specification but 1.0 VMs should - * handle that on their own to simplify Xen implementation. - */ +static int32_t ffa_get_sp_partinfo(struct ffa_uuid uuid, uint32_t *sp_count, + void **dst_buf, void *end_buf, + uint32_t dst_size) +{ + int32_t ret; + uint32_t count = 0; + uint32_t n; - ret = ffa_copy_info(dst_buf, end_buf, src_buf, dst_size, src_size); - if ( ret ) - goto out; + for ( n = 0; n < sp_list_count; n++ ) + { + void *entry = sp_list + n * sp_list_entry_size; - count++; - } + if ( !ffa_sp_entry_matches_uuid(entry, uuid) ) + continue; + + /* + * If VM is 1.0 but firmware is 1.1 we could have several entries + * with the same ID but different UUIDs. In this case the VM will + * get a list with several time the same ID. + * This is a non-compliance to the specification but 1.0 VMs should + * handle that on their own to simplify Xen implementation. + */ + ret = ffa_copy_info(dst_buf, end_buf, entry, dst_size, + sp_list_entry_size); + if ( ret ) + return ret; - src_buf += src_size; + count++; } *sp_count = count; -out: - release_ret = ffa_rxtx_spmc_rx_release(notify_fw); - if ( release_ret ) - gprintk(XENLOG_WARNING, - "ffa: Error releasing SPMC RX buffer: %d\n", release_ret); - return ret; + if ( !ffa_uuid_is_nil(uuid) && !count ) + return FFA_RET_INVALID_PARAMETERS; + + return FFA_RET_OK; } static int32_t ffa_get_vm_partinfo(struct ffa_uuid uuid, uint32_t start_index, @@ -XXX,XX +XXX,XX @@ static int32_t ffa_direct_req_send_vm(uint16_t sp_id, uint16_t vm_id, return res; } +static void ffa_sp_list_cache_free(void) +{ + XFREE(sp_list); + sp_list_count = 0; + sp_list_entry_size = 0; +} + static void uninit_subscribers(void) { subscr_vm_created_count = 0; @@ -XXX,XX +XXX,XX @@ static void uninit_subscribers(void) XFREE(subscr_vm_destroyed); } +static bool ffa_sp_list_cache_init(const void *buf, uint32_t count, + uint32_t fpi_size) +{ + const uint8_t *src = buf; + uint32_t secure_count = 0; + uint32_t n, idx = 0; + + if ( fpi_size < sizeof(struct ffa_partition_info_1_1) || + fpi_size >= FFA_PAGE_SIZE ) + return false; + + if ( count > (FFA_RXTX_PAGE_COUNT * FFA_PAGE_SIZE) / fpi_size ) + return false; + + for ( n = 0; n < count; n++ ) + { + const uint8_t *entry = src + n * fpi_size; + uint16_t id = ffa_sp_entry_read_id(entry); + + if ( !FFA_ID_IS_SECURE(id) ) + { + printk_once(XENLOG_ERR + "ffa: Firmware is not using bit 15 convention for IDs !!\n"); + printk(XENLOG_ERR + "ffa: Secure partition with id 0x%04x cannot be used\n", + id); + continue; + } + + secure_count++; + } + + if ( secure_count ) + { + sp_list = xzalloc_bytes(secure_count * fpi_size); + if ( !sp_list ) + return false; + } + + sp_list_count = secure_count; + sp_list_entry_size = fpi_size; + + for ( n = 0; n < count; n++ ) + { + const uint8_t *entry = src + n * fpi_size; + uint16_t id = ffa_sp_entry_read_id(entry); + + if ( !FFA_ID_IS_SECURE(id) ) + continue; + + memcpy(sp_list + idx * fpi_size, entry, fpi_size); + idx++; + } + + return true; +} + static bool init_subscribers(void *buf, uint16_t count, uint32_t fpi_size) { uint16_t n; @@ -XXX,XX +XXX,XX @@ bool ffa_partinfo_init(void) if ( e ) { printk(XENLOG_ERR "ffa: Failed to get list of SPs: %d\n", e); - goto out; + goto out_release_rx; } notify_fw = true; @@ -XXX,XX +XXX,XX @@ bool ffa_partinfo_init(void) { printk(XENLOG_ERR "ffa: More SPs than the maximum supported: %u - %u\n", count, FFA_MAX_NUM_SP); - goto out; + goto out_release_rx; + } + + if ( !ffa_sp_list_cache_init(spmc_rx, count, fpi_size) ) + { + printk(XENLOG_ERR "ffa: Failed to cache SP list\n"); + goto out_release_rx; } - ret = init_subscribers(spmc_rx, count, fpi_size); + if ( !init_subscribers(sp_list, sp_list_count, sp_list_entry_size) ) + goto out_free_sp_cache; -out: + ret = true; + goto out_release_rx; + +out_free_sp_cache: + ffa_sp_list_cache_free(); + +out_release_rx: e = ffa_rxtx_spmc_rx_release(notify_fw); if ( e ) printk(XENLOG_WARNING "ffa: Error releasing SPMC RX buffer: %d\n", e); + return ret; } -- 2.52.0
The init-time SP cache already contains partition properties, but the code still builds separate subscriber arrays for VM created/destroyed notifications. That duplicates state and allocation. Use the cached SP list directly to: - decide which SPs receive created/destroyed notifications - build the per-domain destroy bitmap - skip destroy notifications for SPs not notified on create Also switch cached SP entry field reads in VM create/destroy notification paths to unaligned-safe helpers, as cache entries are variable-size and should not be dereferenced via struct pointers. No functional changes. Signed-off-by: Bertrand Marquis <bertrand.marquis@arm.com> --- Changes since v1: - use unaligned-safe reads for cached SP entry fields (id/partition_properties) in VM create/destroy notification paths --- xen/arch/arm/tee/ffa_partinfo.c | 170 +++++++++----------------------- 1 file changed, 47 insertions(+), 123 deletions(-) diff --git a/xen/arch/arm/tee/ffa_partinfo.c b/xen/arch/arm/tee/ffa_partinfo.c index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/tee/ffa_partinfo.c +++ b/xen/arch/arm/tee/ffa_partinfo.c @@ -XXX,XX +XXX,XX @@ struct ffa_partition_info_1_1 { 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; - /* SP list cache (secure endpoints only); populated at init. */ static void *sp_list __read_mostly; static uint32_t sp_list_count __read_mostly; @@ -XXX,XX +XXX,XX @@ static uint16_t ffa_sp_entry_read_id(const void *entry) offsetof(struct ffa_partition_info_1_0, id)); } +static uint32_t ffa_sp_entry_read_partition_properties(const void *entry) +{ + return get_unaligned_t(uint32_t, + (const uint8_t *)entry + + offsetof(struct ffa_partition_info_1_0, + partition_properties)); +} + static bool ffa_sp_entry_matches_uuid(const void *entry, struct ffa_uuid uuid) { struct ffa_uuid sp_uuid; @@ -XXX,XX +XXX,XX @@ static void ffa_sp_list_cache_free(void) sp_list_entry_size = 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 ffa_sp_list_cache_init(const void *buf, uint32_t count, uint32_t fpi_size) { @@ -XXX,XX +XXX,XX @@ static bool ffa_sp_list_cache_init(const void *buf, uint32_t count, return true; } -static bool init_subscribers(void *buf, uint16_t count, uint32_t fpi_size) -{ - uint16_t n; - uint16_t c_pos; - uint16_t d_pos; - struct ffa_partition_info_1_1 *fpi; - - if ( fpi_size < sizeof(struct ffa_partition_info_1_1) ) - { - printk(XENLOG_ERR "ffa: partition info size invalid: %u\n", fpi_size); - return false; - } - - subscr_vm_created_count = 0; - subscr_vm_destroyed_count = 0; - for ( n = 0; n < count; n++ ) - { - fpi = buf + n * fpi_size; - - /* - * We need to have secure partitions using bit 15 set convention for - * secure partition IDs. - * Inform the user with a log and discard giving created or destroy - * event to those IDs. - */ - if ( !FFA_ID_IS_SECURE(fpi->id) ) - { - printk_once(XENLOG_ERR - "ffa: Firmware is not using bit 15 convention for IDs !!\n"); - printk(XENLOG_ERR - "ffa: Secure partition with id 0x%04x cannot be used\n", - fpi->id); - } - else - { - if ( fpi->partition_properties & FFA_PART_PROP_NOTIF_CREATED ) - subscr_vm_created_count++; - if ( fpi->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++ ) - { - fpi = buf + n * fpi_size; - - if ( FFA_ID_IS_SECURE(fpi->id) ) - { - if ( fpi->partition_properties & FFA_PART_PROP_NOTIF_CREATED ) - subscr_vm_created[c_pos++] = fpi->id; - if ( fpi->partition_properties & FFA_PART_PROP_NOTIF_DESTROYED ) - subscr_vm_destroyed[d_pos++] = fpi->id; - } - } - - return true; -} - - - bool ffa_partinfo_init(void) { bool ret = false; @@ -XXX,XX +XXX,XX @@ bool ffa_partinfo_init(void) goto out_release_rx; } - if ( !init_subscribers(sp_list, sp_list_count, sp_list_entry_size) ) - goto out_free_sp_cache; - ret = true; goto out_release_rx; -out_free_sp_cache: - ffa_sp_list_cache_free(); - out_release_rx: e = ffa_rxtx_spmc_rx_release(notify_fw); if ( e ) printk(XENLOG_WARNING "ffa: Error releasing SPMC RX buffer: %d\n", e); - + if ( !ret ) + ffa_sp_list_cache_free(); return ret; } -static bool is_in_subscr_list(const uint16_t *subscr, uint16_t start, - uint16_t end, uint16_t sp_id) +static void vm_destroy_bitmap_init(struct ffa_ctx *ctx, + unsigned int first_unnotified) { unsigned int n; - for ( n = start; n < end; n++ ) + for ( n = 0; n < sp_list_count; n++ ) { - if ( subscr[n] == sp_id ) - return true; - } - - return false; -} + const void *entry = sp_list + n * sp_list_entry_size; + uint32_t partition_props = + ffa_sp_entry_read_partition_properties(entry); -static void vm_destroy_bitmap_init(struct ffa_ctx *ctx, - unsigned int create_signal_count) -{ - unsigned int n; + if ( !(partition_props & FFA_PART_PROP_NOTIF_DESTROYED) ) + continue; - 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]) ) + if ( (partition_props & FFA_PART_PROP_NOTIF_CREATED) && + n >= first_unnotified ) continue; set_bit(n, ctx->vm_destroy_bitmap); @@ -XXX,XX +XXX,XX @@ static void vm_destroy_bitmap_init(struct ffa_ctx *ctx, int32_t ffa_partinfo_domain_init(struct domain *d) { - unsigned int count = BITS_TO_LONGS(subscr_vm_destroyed_count); + unsigned int count = BITS_TO_LONGS(sp_list_count); struct ffa_ctx *ctx = d->arch.tee; unsigned int n; + unsigned int first_unnotified = sp_list_count; int32_t res; - if ( !ffa_fw_supports_fid(FFA_MSG_SEND_DIRECT_REQ_32) ) + if ( !ffa_fw_supports_fid(FFA_MSG_SEND_DIRECT_REQ_32) || !sp_list_count ) return 0; ctx->vm_destroy_bitmap = xzalloc_array(unsigned long, count); if ( !ctx->vm_destroy_bitmap ) return -ENOMEM; - for ( n = 0; n < subscr_vm_created_count; n++ ) + for ( n = 0; n < sp_list_count; n++ ) { - res = ffa_direct_req_send_vm(subscr_vm_created[n], ffa_get_vm_id(d), + const void *entry = sp_list + n * sp_list_entry_size; + uint32_t partition_props = + ffa_sp_entry_read_partition_properties(entry); + uint16_t id = ffa_sp_entry_read_id(entry); + + if ( !(partition_props & FFA_PART_PROP_NOTIF_CREATED) ) + continue; + + res = ffa_direct_req_send_vm(id, 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); + ffa_get_vm_id(d), id, res); + first_unnotified = n; break; } } - vm_destroy_bitmap_init(ctx, n); + vm_destroy_bitmap_init(ctx, first_unnotified); - if ( n != subscr_vm_created_count ) + if ( first_unnotified != sp_list_count ) return -EIO; return 0; @@ -XXX,XX +XXX,XX @@ bool ffa_partinfo_domain_destroy(struct domain *d) if ( !ctx->vm_destroy_bitmap ) return true; - for ( n = 0; n < subscr_vm_destroyed_count; n++ ) + for ( n = 0; n < sp_list_count; n++ ) { + const void *entry; + uint16_t id; + if ( !test_bit(n, ctx->vm_destroy_bitmap) ) continue; - res = ffa_direct_req_send_vm(subscr_vm_destroyed[n], ffa_get_vm_id(d), + entry = sp_list + n * sp_list_entry_size; + id = ffa_sp_entry_read_id(entry); + + res = ffa_direct_req_send_vm(id, ffa_get_vm_id(d), FFA_MSG_SEND_VM_DESTROYED); if ( res && printk_ratelimit() ) printk(XENLOG_WARNING "%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); + d, ffa_get_vm_id(d), id, res); /* * For these two error codes the hypervisor is expected to resend @@ -XXX,XX +XXX,XX @@ bool ffa_partinfo_domain_destroy(struct domain *d) clear_bit(n, ctx->vm_destroy_bitmap); } - if ( bitmap_empty(ctx->vm_destroy_bitmap, subscr_vm_destroyed_count) ) + if ( bitmap_empty(ctx->vm_destroy_bitmap, sp_list_count) ) XFREE(ctx->vm_destroy_bitmap); return !ctx->vm_destroy_bitmap; -- 2.52.0
FF-A v1.2 defines PARTITION_INFO_GET_REGS for register-based partition info retrieval, but Xen currently only supports the buffer-based GET path for guests. Implement GET_REGS using the cached SP list and VM entries, including the register window layout and input validation. Track VM list changes via the partinfo tag and use it to validate GET_REGS tag inputs. Ensure that when a non-Nil UUID is specified, the UUID fields in both GET and GET_REGS results are MBZ as required by the specification. PARTITION_INFO_GET_REGS is available to v1.2 guests, returning cached SP entries and VM entries with UUIDs zeroed for non-Nil UUID queries. Also publish VM membership updates (VM count, ctx list, and partinfo tag) under the same write-locked section so GET_REGS sees coherent state and concurrent changes are reliably reported via RETRY. Signed-off-by: Bertrand Marquis <bertrand.marquis@arm.com> --- Changes since v1: - ignore x4-x17 not being zero and x3 bits 63-32 not being zero (defined as SBZ in the spec) - detect tag changes during GET_REGS handling and return RETRY - remove strict check of sp_list_entry_size, larger cache entry sizes will now be accepted - publish VM count, ctx list, and partinfo tag updates under ffa_ctx_list_rwlock for coherent visibility --- xen/arch/arm/tee/ffa.c | 23 +++- xen/arch/arm/tee/ffa_partinfo.c | 200 ++++++++++++++++++++++++++++++++ xen/arch/arm/tee/ffa_private.h | 4 +- 3 files changed, 223 insertions(+), 4 deletions(-) diff --git a/xen/arch/arm/tee/ffa.c b/xen/arch/arm/tee/ffa.c index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/tee/ffa.c +++ b/xen/arch/arm/tee/ffa.c @@ -XXX,XX +XXX,XX @@ * - doesn't support signalling the secondary scheduler of pending * notification for secure partitions * - doesn't support notifications for Xen itself + * o FFA_PARTITION_INFO_GET/GET_REGS: + * - v1.0 guests may see duplicate SP IDs when firmware provides UUIDs + * - SP list is cached at init; SPMC tag changes are not tracked + * between calls + * - SP list is capped at FFA_MAX_NUM_SP entries * * There are some large locked sections with ffa_spmc_tx_lock and * ffa_spmc_rx_lock. Especially the ffa_spmc_tx_lock spinlock used @@ -XXX,XX +XXX,XX @@ static bool ffa_negotiate_version(struct cpu_user_regs *regs) if ( IS_ENABLED(CONFIG_FFA_VM_TO_VM) ) { - /* One more VM with FF-A support available */ - inc_ffa_vm_count(); write_lock(&ffa_ctx_list_rwlock); + /* Publish VM membership changes atomically with tag updates. */ + inc_ffa_vm_count(); list_add_tail(&ctx->ctx_list, &ffa_ctx_head); + ffa_partinfo_inc_tag(); write_unlock(&ffa_ctx_list_rwlock); } @@ -XXX,XX +XXX,XX @@ static void handle_features(struct cpu_user_regs *regs) case FFA_FEATURE_SCHEDULE_RECV_INTR: ffa_set_regs_success(regs, GUEST_FFA_SCHEDULE_RECV_INTR_ID, 0); break; + case FFA_PARTITION_INFO_GET_REGS: + if ( ACCESS_ONCE(ctx->guest_vers) >= FFA_VERSION_1_2 ) + ffa_set_regs_success(regs, 0, 0); + else + ffa_set_regs_error(regs, FFA_RET_NOT_SUPPORTED); + break; case FFA_NOTIFICATION_BIND: case FFA_NOTIFICATION_UNBIND: @@ -XXX,XX +XXX,XX @@ static bool ffa_handle_call(struct cpu_user_regs *regs) case FFA_PARTITION_INFO_GET: ffa_handle_partition_info_get(regs); return true; + case FFA_PARTITION_INFO_GET_REGS: + ffa_handle_partition_info_get_regs(regs); + return true; case FFA_RX_RELEASE: e = ffa_rx_release(ctx); break; @@ -XXX,XX +XXX,XX @@ static int ffa_domain_teardown(struct domain *d) if ( IS_ENABLED(CONFIG_FFA_VM_TO_VM) && ACCESS_ONCE(ctx->guest_vers) ) { - dec_ffa_vm_count(); write_lock(&ffa_ctx_list_rwlock); + /* Publish VM membership changes atomically with tag updates. */ + dec_ffa_vm_count(); list_del(&ctx->ctx_list); + ffa_partinfo_inc_tag(); write_unlock(&ffa_ctx_list_rwlock); } diff --git a/xen/arch/arm/tee/ffa_partinfo.c b/xen/arch/arm/tee/ffa_partinfo.c index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/tee/ffa_partinfo.c +++ b/xen/arch/arm/tee/ffa_partinfo.c @@ -XXX,XX +XXX,XX @@ struct ffa_partition_info_1_1 { uint8_t uuid[16]; }; +/* Registers a3..a17 (15 regs) carry partition descriptors, 3 regs each. */ +#define FFA_PARTINFO_REG_MAX_ENTRIES \ + ((15 * sizeof(uint64_t)) / sizeof(struct ffa_partition_info_1_1)) + /* SP list cache (secure endpoints only); populated at init. */ static void *sp_list __read_mostly; static uint32_t sp_list_count __read_mostly; static uint32_t sp_list_entry_size __read_mostly; + +/* SP list is static; tag only moves when VMs are added/removed. */ +static atomic_t ffa_partinfo_tag = ATOMIC_INIT(1); + +void ffa_partinfo_inc_tag(void) +{ + atomic_inc(&ffa_partinfo_tag); +} + +static inline uint16_t ffa_partinfo_get_tag(void) +{ + /* + * Tag moves with VM list changes only. + * + * Limitation: we cannot detect an SPMC tag change between calls because we + * do not retain the previous SPMC tag; we only refresh it via the mandatory + * start_index=0 call and assume it stays stable while combined_tag (our + * VM/SP-count tag) is used for guest validation. This means SPMC tag + * changes alone will not trigger RETRY. + */ + if ( IS_ENABLED(CONFIG_FFA_VM_TO_VM) ) + return atomic_read(&ffa_partinfo_tag) & GENMASK(15, 0); + else + return 1; +} static int32_t ffa_partition_info_get(struct ffa_uuid uuid, uint32_t flags, uint32_t *count, uint32_t *fpi_size) { @@ -XXX,XX +XXX,XX @@ static int32_t ffa_get_sp_partinfo(struct ffa_uuid uuid, uint32_t *sp_count, for ( n = 0; n < sp_list_count; n++ ) { void *entry = sp_list + n * sp_list_entry_size; + void *dst_pos; if ( !ffa_sp_entry_matches_uuid(entry, uuid) ) continue; @@ -XXX,XX +XXX,XX @@ static int32_t ffa_get_sp_partinfo(struct ffa_uuid uuid, uint32_t *sp_count, * This is a non-compliance to the specification but 1.0 VMs should * handle that on their own to simplify Xen implementation. */ + dst_pos = *dst_buf; ret = ffa_copy_info(dst_buf, end_buf, entry, dst_size, sp_list_entry_size); if ( ret ) return ret; + if ( !ffa_uuid_is_nil(uuid) && + dst_size >= sizeof(struct ffa_partition_info_1_1) ) + { + struct ffa_partition_info_1_1 *fpi = dst_pos; + + memset(fpi->uuid, 0, sizeof(fpi->uuid)); + } + count++; } @@ -XXX,XX +XXX,XX @@ static int32_t ffa_get_sp_partinfo(struct ffa_uuid uuid, uint32_t *sp_count, return FFA_RET_OK; } +static uint16_t ffa_get_sp_partinfo_regs(struct ffa_uuid uuid, + uint16_t start_index, + uint64_t *out_regs, + uint16_t max_entries) +{ + uint32_t idx = 0; + uint16_t filled = 0; + uint32_t n; + + for ( n = 0; n < sp_list_count && filled < max_entries; n++ ) + { + void *entry = sp_list + n * sp_list_entry_size; + + if ( !ffa_sp_entry_matches_uuid(entry, uuid) ) + continue; + + if ( idx++ < start_index ) + continue; + + memcpy(&out_regs[filled * 3], entry, + sizeof(struct ffa_partition_info_1_1)); + if ( !ffa_uuid_is_nil(uuid) ) + { + out_regs[filled * 3 + 1] = 0; + out_regs[filled * 3 + 2] = 0; + } + filled++; + } + + return filled; +} + static int32_t ffa_get_vm_partinfo(struct ffa_uuid uuid, uint32_t start_index, uint32_t *vm_count, void **dst_buf, void *end_buf, uint32_t dst_size) @@ -XXX,XX +XXX,XX @@ out: } } +void ffa_handle_partition_info_get_regs(struct cpu_user_regs *regs) +{ + struct domain *d = current->domain; + struct ffa_ctx *ctx = d->arch.tee; + struct ffa_uuid uuid; + uint32_t sp_count = 0, vm_count = 0, total_count; + uint16_t start_index, tag; + uint16_t num_entries = 0; + uint64_t x3 = get_user_reg(regs, 3); + int32_t ret = FFA_RET_OK; + uint64_t out_regs[18] = { 0 }; + unsigned int n; + uint16_t tag_out, tag_end; + + if ( ACCESS_ONCE(ctx->guest_vers) < FFA_VERSION_1_2 ) + { + ret = FFA_RET_NOT_SUPPORTED; + goto out; + } + + /* + * Registers a3..a17 (15 regs) carry partition descriptors, 3 regs each. + * For FF-A 1.2, that yields a maximum of 5 entries per GET_REGS call. + * Enforce the assumed layout so window sizing stays correct. + */ + BUILD_BUG_ON(FFA_PARTINFO_REG_MAX_ENTRIES != 5); + + start_index = x3 & GENMASK(15, 0); + tag = (x3 >> 16) & GENMASK(15, 0); + + /* Start index must allow room for up to 5 entries without 16-bit overflow. */ + if ( start_index > (GENMASK(15, 0) - (FFA_PARTINFO_REG_MAX_ENTRIES - 1)) ) + { + ret = FFA_RET_INVALID_PARAMETERS; + goto out; + } + + uuid.val[0] = get_user_reg(regs, 1); + uuid.val[1] = get_user_reg(regs, 2); + + tag_out = ffa_partinfo_get_tag(); + + if ( start_index == 0 ) + { + if ( tag ) + { + ret = FFA_RET_INVALID_PARAMETERS; + goto out; + } + } + else if ( tag != tag_out ) + { + ret = FFA_RET_RETRY; + goto out; + } + + if ( ffa_uuid_is_nil(uuid) ) + { + if ( IS_ENABLED(CONFIG_FFA_VM_TO_VM) ) + vm_count = get_ffa_vm_count(); + else + vm_count = 1; /* Caller VM only */ + } + + ret = ffa_get_sp_count(uuid, &sp_count); + if ( ret ) + goto out; + + total_count = sp_count + vm_count; + + if ( total_count == 0 || start_index >= total_count ) + { + ret = FFA_RET_INVALID_PARAMETERS; + goto out; + } + + if ( start_index < sp_count ) + num_entries = ffa_get_sp_partinfo_regs(uuid, start_index, &out_regs[3], + FFA_PARTINFO_REG_MAX_ENTRIES); + + if ( num_entries < FFA_PARTINFO_REG_MAX_ENTRIES ) + { + uint32_t vm_start = start_index > sp_count ? + start_index - sp_count : 0; + uint32_t filled = 0; + void *vm_dst = &out_regs[3 + num_entries * 3]; + void *vm_end = &out_regs[18]; + + ret = ffa_get_vm_partinfo(uuid, vm_start, &filled, &vm_dst, vm_end, + sizeof(struct ffa_partition_info_1_1)); + if ( ret != FFA_RET_OK && ret != FFA_RET_NO_MEMORY ) + goto out; + + num_entries += filled; + } + + if ( num_entries == 0 ) + { + ret = FFA_RET_INVALID_PARAMETERS; + goto out; + } + + /* + * Detect list changes while building the response so the caller can retry + * with a coherent snapshot tag. + */ + tag_end = ffa_partinfo_get_tag(); + if ( tag_end != tag_out ) + { + ret = FFA_RET_RETRY; + goto out; + } + + out_regs[0] = FFA_SUCCESS_64; + out_regs[2] = ((uint64_t)sizeof(struct ffa_partition_info_1_1) << 48) | + ((uint64_t)tag_end << 32) | + ((uint64_t)(start_index + num_entries - 1) << 16) | + ((uint64_t)(total_count - 1) & GENMASK(15, 0)); + + for ( n = 0; n < ARRAY_SIZE(out_regs); n++ ) + set_user_reg(regs, n, out_regs[n]); + + return; + +out: + if ( ret ) + ffa_set_regs_error(regs, ret); +} + static int32_t ffa_direct_req_send_vm(uint16_t sp_id, uint16_t vm_id, uint8_t msg) { diff --git a/xen/arch/arm/tee/ffa_private.h b/xen/arch/arm/tee/ffa_private.h index XXXXXXX..XXXXXXX 100644 --- a/xen/arch/arm/tee/ffa_private.h +++ b/xen/arch/arm/tee/ffa_private.h @@ -XXX,XX +XXX,XX @@ #define FFA_MSG_SEND2 0x84000086U #define FFA_CONSOLE_LOG_32 0x8400008AU #define FFA_CONSOLE_LOG_64 0xC400008AU -#define FFA_PARTITION_INFO_GET_REGS 0x8400008BU +#define FFA_PARTITION_INFO_GET_REGS 0xC400008BU #define FFA_MSG_SEND_DIRECT_REQ2 0xC400008DU #define FFA_MSG_SEND_DIRECT_RESP2 0xC400008EU @@ -XXX,XX +XXX,XX @@ bool ffa_partinfo_init(void); int32_t ffa_partinfo_domain_init(struct domain *d); bool ffa_partinfo_domain_destroy(struct domain *d); void ffa_handle_partition_info_get(struct cpu_user_regs *regs); +void ffa_handle_partition_info_get_regs(struct cpu_user_regs *regs); +void ffa_partinfo_inc_tag(void); int32_t ffa_endpoint_domain_lookup(uint16_t endpoint_id, struct domain **d_out, struct ffa_ctx **ctx_out); -- 2.52.0