From nobody Mon Apr 13 03:28:44 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) client-ip=192.237.175.120; envelope-from=xen-devel-bounces@lists.xenproject.org; helo=lists.xenproject.org; Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of lists.xenproject.org designates 192.237.175.120 as permitted sender) smtp.mailfrom=xen-devel-bounces@lists.xenproject.org; dmarc=fail(p=none dis=none) header.from=arm.com Return-Path: Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) by mx.zohomail.com with SMTPS id 1770830188199274.2876039535513; Wed, 11 Feb 2026 09:16:28 -0800 (PST) Received: from list by lists.xenproject.org with outflank-mailman.1227996.1534386 (Exim 4.92) (envelope-from ) id 1vqDos-0005TZ-3a; Wed, 11 Feb 2026 17:16:06 +0000 Received: by outflank-mailman (output) from mailman id 1227996.1534386; Wed, 11 Feb 2026 17:16:06 +0000 Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1vqDos-0005TL-0k; Wed, 11 Feb 2026 17:16:06 +0000 Received: by outflank-mailman (input) for mailman id 1227996; Wed, 11 Feb 2026 17:16:04 +0000 Received: from se1-gles-flk1-in.inumbo.com ([94.247.172.50] helo=se1-gles-flk1.inumbo.com) by lists.xenproject.org with esmtp (Exim 4.92) (envelope-from ) id 1vqDoq-0004U4-JT for xen-devel@lists.xenproject.org; Wed, 11 Feb 2026 17:16:04 +0000 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by se1-gles-flk1.inumbo.com (Halon) with ESMTP id 576c8058-076d-11f1-9ccf-f158ae23cfc8; Wed, 11 Feb 2026 18:16:02 +0100 (CET) Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id BBD89497; Wed, 11 Feb 2026 09:15:55 -0800 (PST) Received: from C3HXLD123V.arm.com (unknown [10.57.53.46]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id D0E933F63F; Wed, 11 Feb 2026 09:16:00 -0800 (PST) X-Outflank-Mailman: Message body and most headers restored to incoming version X-BeenThere: xen-devel@lists.xenproject.org List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xenproject.org Precedence: list Sender: "Xen-devel" X-Inumbo-ID: 576c8058-076d-11f1-9ccf-f158ae23cfc8 From: Bertrand Marquis To: xen-devel@lists.xenproject.org Cc: Volodymyr Babchuk , Jens Wiklander , Stefano Stabellini , Julien Grall , Michal Orzel Subject: [PATCH v2 04/12] xen/arm: ffa: Add FF-A 1.2 endpoint memory access descriptors Date: Wed, 11 Feb 2026 18:15:28 +0100 Message-ID: <8ddb97095d8f7f4140e660d2ff13ec2ccc82cc62.1770826406.git.bertrand.marquis@arm.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-ZM-MESSAGEID: 1770830189904158500 Content-Type: text/plain; charset="utf-8" FF-A 1.2 extends the endpoint memory access descriptor (EMAD) from 16 to 32 bytes, adding implementation-defined (IMPDEF) fields and reserved space. The MEM_SHARE path currently assumes the 1.1 EMAD size and rejects the 1.2 layout. Add FF-A 1.2 EMAD support to MEM_SHARE: - define ffa_mem_access_1_2 and store IMPDEF payload in ffa_shm_mem - emit 1.2 EMADs to the SPMC for FF-A 1.2 guests, forwarding IMPDEF - refactor header parsing into read_mem_transaction() for 1.0/1.1+ - detect EMAD format by mem_access_size to allow 1.1 on 1.2 guests Export ffa_fw_version to build memory descriptors according to the firware version to relay share memory requests and remove unused offset macros. Functional impact: MEM_SHARE supports FF-A 1.2 EMADs. Signed-off-by: Bertrand Marquis Reviewed-by: Jens Wiklander --- Changes since v1: - export ffa_fw_version and use it to construct descriptors according to firmware version - remove unused offset macros --- xen/arch/arm/tee/ffa.c | 2 +- xen/arch/arm/tee/ffa_private.h | 1 + xen/arch/arm/tee/ffa_shm.c | 121 +++++++++++++++++++++++---------- 3 files changed, 87 insertions(+), 37 deletions(-) diff --git a/xen/arch/arm/tee/ffa.c b/xen/arch/arm/tee/ffa.c index 6de2b9f8ac8e..23e1f408485b 100644 --- a/xen/arch/arm/tee/ffa.c +++ b/xen/arch/arm/tee/ffa.c @@ -70,7 +70,7 @@ #include "ffa_private.h" =20 /* Negotiated FF-A version to use with the SPMC, 0 if not there or support= ed */ -static uint32_t __ro_after_init ffa_fw_version; +uint32_t __ro_after_init ffa_fw_version; =20 /* Features supported by the SPMC or secure world when present */ DECLARE_BITMAP(ffa_fw_abi_supported, FFA_ABI_BITMAP_SIZE); diff --git a/xen/arch/arm/tee/ffa_private.h b/xen/arch/arm/tee/ffa_private.h index 58562d8e733c..ccef2f7631f6 100644 --- a/xen/arch/arm/tee/ffa_private.h +++ b/xen/arch/arm/tee/ffa_private.h @@ -436,6 +436,7 @@ struct ffa_ctx { }; =20 extern DECLARE_BITMAP(ffa_fw_abi_supported, FFA_ABI_BITMAP_SIZE); +extern uint32_t __ro_after_init ffa_fw_version; =20 extern struct list_head ffa_ctx_head; extern rwlock_t ffa_ctx_list_rwlock; diff --git a/xen/arch/arm/tee/ffa_shm.c b/xen/arch/arm/tee/ffa_shm.c index adc7e645a1c7..070babce9627 100644 --- a/xen/arch/arm/tee/ffa_shm.c +++ b/xen/arch/arm/tee/ffa_shm.c @@ -30,6 +30,14 @@ struct ffa_mem_access { uint64_t reserved; }; =20 +/* Endpoint memory access descriptor (FF-A 1.2) */ +struct ffa_mem_access_1_2 { + struct ffa_mem_access_perm access_perm; + uint32_t region_offs; + uint8_t impdef[16]; + uint8_t reserved[8]; +}; + /* Lend, donate or share memory transaction descriptor */ struct ffa_mem_transaction_1_0 { uint16_t sender_id; @@ -55,25 +63,10 @@ struct ffa_mem_transaction_1_1 { uint8_t reserved[12]; }; =20 -/* Calculate offset of struct ffa_mem_access from start of buffer */ -#define MEM_ACCESS_OFFSET(access_idx) \ - ( sizeof(struct ffa_mem_transaction_1_1) + \ - ( access_idx ) * sizeof(struct ffa_mem_access) ) - -/* Calculate offset of struct ffa_mem_region from start of buffer */ -#define REGION_OFFSET(access_count, region_idx) \ - ( MEM_ACCESS_OFFSET(access_count) + \ - ( region_idx ) * sizeof(struct ffa_mem_region) ) - -/* Calculate offset of struct ffa_address_range from start of buffer */ -#define ADDR_RANGE_OFFSET(access_count, region_count, range_idx) \ - ( REGION_OFFSET(access_count, region_count) + \ - ( range_idx ) * sizeof(struct ffa_address_range) ) - /* * The parts needed from struct ffa_mem_transaction_1_0 or struct * ffa_mem_transaction_1_1, used to provide an abstraction of difference in - * data structures between version 1.0 and 1.1. This is just an internal + * data structures between version 1.0 and 1.2. This is just an internal * interface and can be changed without changing any ABI. */ struct ffa_mem_transaction_int { @@ -92,6 +85,8 @@ struct ffa_shm_mem { uint16_t sender_id; uint16_t ep_id; /* endpoint, the one lending */ uint64_t handle; /* FFA_HANDLE_INVALID if not set yet */ + /* Endpoint memory access descriptor IMPDEF value (FF-A 1.2). */ + uint64_t impdef[2]; unsigned int page_count; struct page_info *pages[]; }; @@ -297,16 +292,20 @@ static void init_range(struct ffa_address_range *addr= _range, * This function uses the ffa_spmc tx buffer to transmit the memory transa= ction * descriptor. */ -static int share_shm(struct ffa_shm_mem *shm) +static int share_shm(struct ffa_shm_mem *shm, uint32_t ffa_vers) { const uint32_t max_frag_len =3D FFA_RXTX_PAGE_COUNT * FFA_PAGE_SIZE; struct ffa_mem_access *mem_access_array; + struct ffa_mem_access_1_2 *mem_access_array_1_2; struct ffa_mem_transaction_1_1 *descr; struct ffa_address_range *addr_range; struct ffa_mem_region *region_descr; - const unsigned int region_count =3D 1; uint32_t tot_len; + uint32_t mem_access_size; + uint32_t mem_access_offs; + uint32_t region_offs; paddr_t last_pa; + uint32_t range_count; unsigned int n; paddr_t pa; int32_t ret; @@ -325,16 +324,35 @@ static int share_shm(struct ffa_shm_mem *shm) descr->handle =3D shm->handle; descr->mem_reg_attr =3D FFA_NORMAL_MEM_REG_ATTR; descr->mem_access_count =3D 1; - descr->mem_access_size =3D sizeof(*mem_access_array); - descr->mem_access_offs =3D MEM_ACCESS_OFFSET(0); + if ( ffa_vers >=3D FFA_VERSION_1_2 ) + mem_access_size =3D sizeof(struct ffa_mem_access_1_2); + else + mem_access_size =3D sizeof(struct ffa_mem_access); + mem_access_offs =3D sizeof(struct ffa_mem_transaction_1_1); + region_offs =3D mem_access_offs + mem_access_size; + descr->mem_access_size =3D mem_access_size; + descr->mem_access_offs =3D mem_access_offs; =20 - mem_access_array =3D buf + descr->mem_access_offs; - memset(mem_access_array, 0, sizeof(*mem_access_array)); - mem_access_array[0].access_perm.endpoint_id =3D shm->ep_id; - mem_access_array[0].access_perm.perm =3D FFA_MEM_ACC_RW; - mem_access_array[0].region_offs =3D REGION_OFFSET(descr->mem_access_co= unt, 0); + if ( ffa_vers >=3D FFA_VERSION_1_2 ) + { + mem_access_array_1_2 =3D buf + mem_access_offs; + memset(mem_access_array_1_2, 0, sizeof(*mem_access_array_1_2)); + mem_access_array_1_2[0].access_perm.endpoint_id =3D shm->ep_id; + mem_access_array_1_2[0].access_perm.perm =3D FFA_MEM_ACC_RW; + mem_access_array_1_2[0].region_offs =3D region_offs; + memcpy(mem_access_array_1_2[0].impdef, shm->impdef, + sizeof(mem_access_array_1_2[0].impdef)); + } + else + { + mem_access_array =3D buf + mem_access_offs; + memset(mem_access_array, 0, sizeof(*mem_access_array)); + mem_access_array[0].access_perm.endpoint_id =3D shm->ep_id; + mem_access_array[0].access_perm.perm =3D FFA_MEM_ACC_RW; + mem_access_array[0].region_offs =3D region_offs; + } =20 - region_descr =3D buf + mem_access_array[0].region_offs; + region_descr =3D buf + region_offs; memset(region_descr, 0, sizeof(*region_descr)); region_descr->total_page_count =3D shm->page_count; =20 @@ -348,8 +366,9 @@ static int share_shm(struct ffa_shm_mem *shm) region_descr->address_range_count++; } =20 - tot_len =3D ADDR_RANGE_OFFSET(descr->mem_access_count, region_count, - region_descr->address_range_count); + range_count =3D region_descr->address_range_count; + tot_len =3D region_offs + sizeof(*region_descr) + + range_count * sizeof(struct ffa_address_range); if ( tot_len > max_frag_len ) { ret =3D FFA_RET_NOT_SUPPORTED; @@ -445,6 +464,12 @@ static int read_mem_transaction(uint32_t ffa_vers, con= st void *buf, size_t blen, if ( size * count + offs > blen ) return FFA_RET_INVALID_PARAMETERS; =20 + if ( size < sizeof(struct ffa_mem_access) ) + return FFA_RET_INVALID_PARAMETERS; + + if ( offs & 0xF ) + return FFA_RET_INVALID_PARAMETERS; + trans->mem_reg_attr =3D mem_reg_attr; trans->flags =3D flags; trans->mem_access_size =3D size; @@ -461,7 +486,7 @@ void ffa_handle_mem_share(struct cpu_user_regs *regs) uint64_t addr =3D get_user_reg(regs, 3); uint32_t page_count =3D get_user_reg(regs, 4); const struct ffa_mem_region *region_descr; - const struct ffa_mem_access *mem_access; + const struct ffa_mem_access_1_2 *mem_access; struct ffa_mem_transaction_int trans; struct domain *d =3D current->domain; struct ffa_ctx *ctx =3D d->arch.tee; @@ -471,9 +496,12 @@ void ffa_handle_mem_share(struct cpu_user_regs *regs) register_t handle_hi =3D 0; register_t handle_lo =3D 0; int ret =3D FFA_RET_DENIED; + uint32_t ffa_vers; uint32_t range_count; uint32_t region_offs; uint16_t dst_id; + uint8_t perm; + uint64_t impdef[2]; =20 if ( !ffa_fw_supports_fid(FFA_MEM_SHARE_64) ) { @@ -512,8 +540,8 @@ void ffa_handle_mem_share(struct cpu_user_regs *regs) if ( frag_len > tx_size ) goto out_unlock; =20 - ret =3D read_mem_transaction(ACCESS_ONCE(ctx->guest_vers), tx_buf, - frag_len, &trans); + ffa_vers =3D ACCESS_ONCE(ctx->guest_vers); + ret =3D read_mem_transaction(ffa_vers, tx_buf, frag_len, &trans); if ( ret ) goto out_unlock; =20 @@ -542,13 +570,35 @@ void ffa_handle_mem_share(struct cpu_user_regs *regs) goto out_unlock; } =20 + if ( trans.mem_access_size < sizeof(struct ffa_mem_access) ) + { + ret =3D FFA_RET_INVALID_PARAMETERS; + goto out_unlock; + } + /* Check that it fits in the supplied data */ if ( trans.mem_access_offs + trans.mem_access_size > frag_len ) goto out_unlock; =20 mem_access =3D tx_buf + trans.mem_access_offs; - dst_id =3D ACCESS_ONCE(mem_access->access_perm.endpoint_id); + perm =3D ACCESS_ONCE(mem_access->access_perm.perm); + region_offs =3D ACCESS_ONCE(mem_access->region_offs); + + /* + * FF-A 1.2 introduced an extended mem_access descriptor with impdef + * fields, but guests can still use the 1.1 format if they don't need + * implementation-defined data. Detect which format is used based on + * the mem_access_size field rather than the negotiated FF-A version. + */ + if ( trans.mem_access_size >=3D sizeof(struct ffa_mem_access_1_2) ) + memcpy(impdef, mem_access->impdef, sizeof(impdef)); + else + { + impdef[0] =3D 0; + impdef[1] =3D 0; + } + if ( !FFA_ID_IS_SECURE(dst_id) ) { /* we do not support sharing with VMs */ @@ -556,13 +606,11 @@ void ffa_handle_mem_share(struct cpu_user_regs *regs) goto out_unlock; } =20 - if ( ACCESS_ONCE(mem_access->access_perm.perm) !=3D FFA_MEM_ACC_RW ) + if ( perm !=3D FFA_MEM_ACC_RW ) { ret =3D FFA_RET_NOT_SUPPORTED; goto out_unlock; } - - region_offs =3D ACCESS_ONCE(mem_access->region_offs); if ( sizeof(*region_descr) + region_offs > frag_len ) { ret =3D FFA_RET_NOT_SUPPORTED; @@ -587,6 +635,7 @@ void ffa_handle_mem_share(struct cpu_user_regs *regs) } shm->sender_id =3D trans.sender_id; shm->ep_id =3D dst_id; + memcpy(shm->impdef, impdef, sizeof(shm->impdef)); =20 /* * Check that the Composite memory region descriptor fits. @@ -602,7 +651,7 @@ void ffa_handle_mem_share(struct cpu_user_regs *regs) if ( ret ) goto out; =20 - ret =3D share_shm(shm); + ret =3D share_shm(shm, ffa_fw_version); if ( ret ) goto out; =20 --=20 2.52.0