From: Abdellatif El Khlifi <abdellatif.elkhl...@arm.com> Add to the FF-A bus FFA_MEM_SHARE ABI
The FFA_MEM_SHARE is a memory management ABI described in the FF-A v1.0 specification [1]. This ABI starts a transaction to grant access to a memory region to one or more Borrowers (aka Secure Partitions or endpoints). This work is based on the implementation in Linux kernel [2]. [1]: https://developer.arm.com/documentation/den0077/a/?lang=en [2]: commit cc2195fe536c28e192df5d07e6dd277af36814b4 Files: drivers/firmware/arm_ffa/driver.c , include/linux/arm_ffa.h Signed-off-by: Abdellatif El Khlifi <abdellatif.elkhl...@arm.com> --- doc/arch/arm64.ffa.rst | 2 + drivers/firmware/arm-ffa/arm-ffa-uclass.c | 208 ++++++++++++++++++++++ drivers/firmware/arm-ffa/arm-ffa.c | 3 +- include/arm_ffa.h | 86 ++++++++- include/arm_ffa_priv.h | 142 ++++++++++++++- 5 files changed, 437 insertions(+), 4 deletions(-) diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst index f966f8ba6af..3eec735d741 100644 --- a/doc/arch/arm64.ffa.rst +++ b/doc/arch/arm64.ffa.rst @@ -185,6 +185,7 @@ The following features are provided: - FFA_INTERRUPT - FFA_MSG_SEND_DIRECT_REQ - FFA_MSG_SEND_DIRECT_RESP + - FFA_MEM_SHARE - Support for the 64-bit version of the following ABIs: @@ -203,6 +204,7 @@ The following features are provided: - ffa_partition_info_get - ffa_sync_send_receive - ffa_rxtx_unmap + - ffa_memory_share - FF-A bus discovery makes sure FF-A framework is responsive and compatible with the driver diff --git a/drivers/firmware/arm-ffa/arm-ffa-uclass.c b/drivers/firmware/arm-ffa/arm-ffa-uclass.c index 5ec7654ed1c..8ff666c3757 100644 --- a/drivers/firmware/arm-ffa/arm-ffa-uclass.c +++ b/drivers/firmware/arm-ffa/arm-ffa-uclass.c @@ -95,6 +95,20 @@ static struct ffa_abi_errmap err_msg_map[FFA_ERRMAP_COUNT] = { "DENIED: Buffer pair already registered", }, }, + [FFA_ID_TO_ERRMAP_ID(FFA_MEM_SHARE)] = { + { + [ABORTED] = + "ABORTED: Failure in the transmission of fragments or in time slicing", + [INVALID_PARAMETERS] = + "INVALID_PARAMETERS: Validation failed for the Memory Transaction or the Endpoint memory access descriptor", + [NO_MEMORY] = + "NO_MEMORY: Insufficient memory to complete this operation", + [BUSY] = + "BUSY: The TX buffer is busy", + [DENIED] = + "DENIED: Memory region ownership, permission, access or attributes error", + }, + }, }; /** @@ -929,6 +943,178 @@ int ffa_msg_send_direct_req_hdlr(struct udevice *dev, u16 dst_part_id, return ffa_to_std_errno(ffa_errno); } +/** + * ffa_mem_desc_offset() - helper for descriptors offset calculation + * @count: The number of Endpoint memory access descriptors + * + * Calculate the offset of the Endpoint memory access descriptor and + * the Composite memory region descriptor. + * + * Return: + * + * The descriptor offset. + */ +static inline u32 ffa_mem_desc_offset(int count) +{ + u32 offset = count * sizeof(struct ffa_mem_region_attributes); + + offset += sizeof(struct ffa_mem_region); + + return offset; +} + +/** + * ffa_setup_and_transmit() - setup the memory region and invoke the memory ABI + * @dev: The FF-A bus device + * @func_id: The FF-A memory ABI (currently we support FFA_MEM_SHARE only) + * @buffer: The TX buffer + * @args: The user arguments + * + * Setup the memory transaction related to the access to a specified + * memory region. + * + * Return: + * + * 0 on success. . Otherwise, failure + */ +static int ffa_setup_and_transmit(struct udevice *dev, u32 func_id, + void *buffer, struct ffa_mem_ops_args *args) +{ + ffa_value_t res = {0}; + int ffa_errno; + u32 composite_offset; + u32 total_length; + struct ffa_mem_region *mem_region = buffer; + struct ffa_composite_mem_region *composite; + struct ffa_mem_region_addr_range *constituent; + struct ffa_mem_region_attributes *ep_mem_access; + u32 idx; + struct ffa_priv *uc_priv; + + if (!buffer || !args->attrs || !args->address) + return -EINVAL; + + uc_priv = dev_get_uclass_priv(dev); + + mem_region->tag = args->tag; + mem_region->flags = args->flags; + mem_region->sender_id = uc_priv->id; + + /* + * These attributes are only valid for FFA_MEM_SHARE. + * They are not valid for FFA_MEM_LEND (no implemented). + */ + if (func_id == FFA_MEM_SHARE) + mem_region->attributes = FFA_MEM_NORMAL | FFA_MEM_WRITE_BACK + | FFA_MEM_INNER_SHAREABLE; + else + mem_region->attributes = 0; + + mem_region->handle = 0; + mem_region->ep_count = args->nattrs; + mem_region->reserved1 = 0; + mem_region->reserved2 = 0; + + ep_mem_access = buffer + ffa_mem_desc_offset(0); + + composite_offset = ffa_mem_desc_offset(args->nattrs); + + /* Multiple borrowers supported */ + for (idx = 0; idx < args->nattrs; idx++, ep_mem_access++) { + ep_mem_access->receiver = args->attrs[idx].receiver; + ep_mem_access->attrs = args->attrs[idx].attrs; + ep_mem_access->composite_off = composite_offset; + ep_mem_access->flag = 0; + ep_mem_access->reserved = 0; + } + + /* Only one Composite and one Constituent memory region supported */ + composite = buffer + composite_offset; + composite->total_pg_cnt = args->pg_cnt; + composite->addr_range_cnt = FFA_MEM_CONSTITUENTS; + composite->reserved = 0; + + constituent = &composite->constituents[0]; + constituent->address = map_to_sysmem(args->address); + constituent->pg_cnt = args->pg_cnt; + constituent->reserved = 0; + + total_length = composite_offset + sizeof(*composite) + + sizeof(*constituent); + + /* + * Note: Time slicing is not supported. + * It's only available to EL1 and S-EL1 endpoints. + */ + + invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_32(func_id), + .a1 = total_length, + .a2 = total_length, + .a3 = 0, /* the TX buffer is used */ + .a4 = 0, /* the TX buffer is used */ + }, + &res + ); + + if (res.a0 != FFA_SMC_32(FFA_SUCCESS)) { + ffa_errno = res.a2; + ffa_print_error_log(func_id, ffa_errno); + return ffa_to_std_errno(ffa_errno); + } + + args->g_handle = PACK_HANDLE(res.a2, res.a3); + return 0; +} + +/** + * ffa_memory_ops() - wrapper for the memory management ABIs + * @dev: The FF-A bus device + * @func_id: The FF-A memory ABI (currently we support FFA_MEM_SHARE only) + * @args: The user arguments + * + * Verify the use of the TX buffer then call ffa_setup_and_transmit(). + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_memory_ops(struct udevice *dev, u32 func_id, + struct ffa_mem_ops_args *args) +{ + void *buffer; + struct ffa_priv *uc_priv = dev_get_uclass_priv(dev); + + if (!args->use_txbuf) { + log_err("only TX buffer supported\n"); + return -EPROTONOSUPPORT; + } + + buffer = uc_priv->pair.txbuf; + + return ffa_setup_and_transmit(dev, func_id, buffer, args); +} + +/** + * ffa_memory_share_hdlr() - FFA_MEM_SHARE handler function + * @dev: The FF-A bus device + * @args: The arguments needed by FFA_MEM_SHARE + * + * Implement FFA_MEM_SHARE FF-A function + * to grant access to a memory region to one or more Borrowers. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int ffa_memory_share_hdlr(struct udevice *dev, struct ffa_mem_ops_args *args) +{ + if (!args) + return -EINVAL; + + return ffa_memory_ops(dev, FFA_MEM_SHARE, args); +} + /* FF-A driver operations (used by clients for communicating with FF-A)*/ /** @@ -1006,6 +1192,28 @@ int ffa_rxtx_unmap(struct udevice *dev) return ops->rxtx_unmap(dev); } +/** + * ffa_memory_share() - FFA_MEM_SHARE driver operation + * @dev: The FF-A bus device + * @args: the arguments needed by FFA_MEM_SHARE + * + * Driver operation for FFA_MEM_SHARE. + * Please see ffa_memory_share_hdlr() description for more details. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int ffa_memory_share(struct udevice *dev, struct ffa_mem_ops_args *args) +{ + struct ffa_bus_ops *ops = ffa_get_ops(dev); + + if (!ops || !ops->memory_share) + return -ENOSYS; + + return ops->memory_share(dev, args); +} + /** * ffa_do_probe() - probing FF-A framework * @dev: the FF-A bus device (arm_ffa) diff --git a/drivers/firmware/arm-ffa/arm-ffa.c b/drivers/firmware/arm-ffa/arm-ffa.c index 94e6105cb38..c4211c953ef 100644 --- a/drivers/firmware/arm-ffa/arm-ffa.c +++ b/drivers/firmware/arm-ffa/arm-ffa.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Copyright 2022-2023 Arm Limited and/or its affiliates <open-source-off...@arm.com> + * Copyright 2022-2024 Arm Limited and/or its affiliates <open-source-off...@arm.com> * * Authors: * Abdellatif El Khlifi <abdellatif.elkhl...@arm.com> @@ -84,6 +84,7 @@ static const struct ffa_bus_ops ffa_ops = { .partition_info_get = ffa_get_partitions_info_hdlr, .sync_send_receive = ffa_msg_send_direct_req_hdlr, .rxtx_unmap = ffa_unmap_rxtx_buffers_hdlr, + .memory_share = ffa_memory_share_hdlr, }; /* Registering the FF-A driver as an SMCCC feature driver */ diff --git a/include/arm_ffa.h b/include/arm_ffa.h index db9b1be995e..ce4a3d7f440 100644 --- a/include/arm_ffa.h +++ b/include/arm_ffa.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0+ */ /* - * Copyright 2022-2023 Arm Limited and/or its affiliates <open-source-off...@arm.com> + * Copyright 2022-2024 Arm Limited and/or its affiliates <open-source-off...@arm.com> * * Authors: * Abdellatif El Khlifi <abdellatif.elkhl...@arm.com> @@ -81,11 +81,74 @@ struct ffa_send_direct_data { struct udevice; +/** + * struct ffa_mem_region_attributes - Endpoint memory access descriptor + * + * The data structure used in memory management transactions to create an + * association between an endpoint, memory access permissions and a composite + * memory region description. + * + * For more details, please refer to Table 5.16 and Table 5.15 in the FF-A + * specification v1.0. + * + * This structure was taken from Linux. + */ +struct ffa_mem_region_attributes { + /* The ID of the VM to which the memory is being given or shared. */ + u16 receiver; + /* + * The permissions with which the memory region should be mapped in the + * receiver's page table. + */ +#define FFA_MEM_EXEC BIT(3) +#define FFA_MEM_NO_EXEC BIT(2) +#define FFA_MEM_RW BIT(1) +#define FFA_MEM_RO BIT(0) + u8 attrs; + /* + * Flags used during FFA_MEM_RETRIEVE_REQ and FFA_MEM_RETRIEVE_RESP + * for memory regions with multiple borrowers. + */ +#define FFA_MEM_RETRIEVE_SELF_BORROWER BIT(0) + u8 flag; + /* + * Offset in bytes from the start of the outer `ffa_memory_region` to + * an `struct ffa_mem_region_addr_range`. + */ + u32 composite_off; + u64 reserved; +}; + +/** + * struct ffa_mem_ops_args - User arguments to the memory management ABIs + * @use_txbuf: Whether to use the TX buffer for the memory transaction + * @nattrs: Number of the borrowers + * @flags: Memory transaction flags + * @tag: The tag associated with the transaction + * @g_handle: Globally unique Handle to identify the memory region (out) + * @address: Virtual address of the memory region + * @attrs: Memory access permissions of each borrower + * + * The structured filled by the user and passed to the memory + * management ABIs (e.g: FFA_MEM_SHARE) + */ +struct ffa_mem_ops_args { + bool use_txbuf; + u32 nattrs; + u32 flags; + u64 tag; + u64 g_handle; + void *address; + u32 pg_cnt; + struct ffa_mem_region_attributes *attrs; +}; + /** * struct ffa_bus_ops - Operations for FF-A * @partition_info_get: callback for the FFA_PARTITION_INFO_GET * @sync_send_receive: callback for the FFA_MSG_SEND_DIRECT_REQ * @rxtx_unmap: callback for the FFA_RXTX_UNMAP + * @memory_share: callback for the FFA_MEM_SHARE * * The data structure providing all the operations supported by the driver. * This structure is EFI runtime resident. @@ -97,6 +160,7 @@ struct ffa_bus_ops { struct ffa_send_direct_data *msg, bool is_smc64); int (*rxtx_unmap)(struct udevice *dev); + int (*memory_share)(struct udevice *dev, struct ffa_mem_ops_args *args); }; #define ffa_get_ops(dev) ((struct ffa_bus_ops *)(dev)->driver->ops) @@ -196,6 +260,26 @@ int ffa_partition_info_get(struct udevice *dev, const char *uuid_str, int ffa_get_partitions_info_hdlr(struct udevice *dev, const char *uuid_str, u32 *sp_count, struct ffa_partition_desc **sp_descs); +/** + * ffa_memory_share() - FFA_MEM_SHARE driver operation + * Please see ffa_memory_share_hdlr() description for more details. + */ +int ffa_memory_share(struct udevice *dev, struct ffa_mem_ops_args *args); + +/** + * ffa_memory_share_hdlr() - FFA_MEM_SHARE handler function + * @dev: The FF-A bus device + * @args: the arguments needed by FFA_MEM_SHARE + * + * Implement FFA_MEM_SHARE FF-A function + * to grant access to a memory region to one or more Borrowers. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int ffa_memory_share_hdlr(struct udevice *dev, struct ffa_mem_ops_args *args); + struct ffa_priv; /** diff --git a/include/arm_ffa_priv.h b/include/arm_ffa_priv.h index d564c33c647..02e2cd89937 100644 --- a/include/arm_ffa_priv.h +++ b/include/arm_ffa_priv.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0+ */ /* - * Copyright 2022-2023 Arm Limited and/or its affiliates <open-source-off...@arm.com> + * Copyright 2022-2024 Arm Limited and/or its affiliates <open-source-off...@arm.com> * * Authors: * Abdellatif El Khlifi <abdellatif.elkhl...@arm.com> @@ -132,10 +132,11 @@ enum ffa_abis { FFA_RUN = 0x6d, FFA_MSG_SEND_DIRECT_REQ = 0x6f, FFA_MSG_SEND_DIRECT_RESP = 0x70, + FFA_MEM_SHARE = 0x73, /* To be updated when adding new FFA IDs */ FFA_FIRST_ID = FFA_ERROR, /* Lowest number ID */ - FFA_LAST_ID = FFA_MSG_SEND_DIRECT_RESP, /* Highest number ID */ + FFA_LAST_ID = FFA_MEM_SHARE, /* Highest number ID */ }; enum ffa_abi_errcode { @@ -219,6 +220,143 @@ struct ffa_priv { struct ffa_rxtxpair pair; }; +/* FF-A memory management ABIs data structures */ + +/** + * struct ffa_mem_region - Lend, donate or share memory transaction descriptor + * + * Specifies the data structure that must be used by the Owner/Lender and a + * Borrower/Receiver in a transaction to donate, lend or share a memory region. + * It specifies the memory region description, properties and other transaction + * attributes in an invocation of the following ABIs. + * + * FFA_MEM_DONATE. + * FFA_MEM_LEND. + * FFA_MEM_SHARE. + * FFA_MEM_RETRIEVE_REQ. + * FFA_MEM_RETRIEVE_RESP. + * + * For more details, please refer to the Table 5.19 in the FF-A specification + * v1.0. + * + * The interpretation of some fields depends on the ABI this structure is used + * with. This variance in behavior is also specified in the Table 5.19. + * + * This structure was taken from Linux and adapted to FF-A v1.0. + */ +struct ffa_mem_region { + /* The ID of the VM/owner which originally sent the memory region */ + u16 sender_id; +#define FFA_MEM_NORMAL BIT(5) +#define FFA_MEM_DEVICE BIT(4) + +#define FFA_MEM_WRITE_BACK (3 << 2) +#define FFA_MEM_NON_CACHEABLE BIT(2) + +#define FFA_DEV_nGnRnE (0 << 2) +#define FFA_DEV_nGnRE BIT(2) +#define FFA_DEV_nGRE (2 << 2) +#define FFA_DEV_GRE (3 << 2) + +#define FFA_MEM_NON_SHAREABLE (0) +#define FFA_MEM_OUTER_SHAREABLE (2) +#define FFA_MEM_INNER_SHAREABLE (3) + /* Memory region attributes */ + u8 attributes; + + u8 reserved1; + +/* + * Clear memory region contents after unmapping it from the sender and + * before mapping it for any receiver. + */ +#define FFA_MEM_CLEAR BIT(0) +/* + * Whether the hypervisor may time slice the memory sharing or retrieval + * operation. + */ +#define FFA_TIME_SLICE_ENABLE BIT(1) + +#define FFA_MEM_RETRIEVE_TYPE_IN_RESP (0 << 3) +#define FFA_MEM_RETRIEVE_TYPE_SHARE BIT(3) +#define FFA_MEM_RETRIEVE_TYPE_LEND (2 << 3) +#define FFA_MEM_RETRIEVE_TYPE_DONATE (3 << 3) + +#define FFA_MEM_RETRIEVE_ADDR_ALIGN_HINT BIT(9) +#define FFA_MEM_RETRIEVE_ADDR_ALIGN(x) ((x) << 5) + /* Flags to control behaviour of the transaction. */ + u32 flags; +#define HANDLE_LOW_MASK GENMASK_ULL(31, 0) +#define HANDLE_HIGH_MASK GENMASK_ULL(63, 32) +#define HANDLE_LOW(x) ((u32)(FIELD_GET(HANDLE_LOW_MASK, (x)))) +#define HANDLE_HIGH(x) ((u32)(FIELD_GET(HANDLE_HIGH_MASK, (x)))) + +#define PACK_HANDLE(l, h) \ + (FIELD_PREP(HANDLE_LOW_MASK, (l)) | FIELD_PREP(HANDLE_HIGH_MASK, (h))) + /* + * A globally-unique ID assigned by the hypervisor for a region + * of memory being sent between VMs. + */ + u64 handle; + /* + * An implementation defined value associated with the receiver and the + * memory region. + */ + u64 tag; + + u32 reserved2; + + /* + * The number of `ffa_mem_region_attributes` entries included in this + * transaction. + */ + u32 ep_count; +}; + +/** + * struct ffa_mem_region_addr_range - Constituent memory region descriptor + * + * Each descriptor specifies the base address and size of a virtually or + * physically contiguous memory region. + * + * For more details, please refer to Table 5.14 in the FF-A + * specification v1.0. + * + * This structure was taken from Linux. + */ +struct ffa_mem_region_addr_range { + /* The base IPA of the constituent memory region, aligned to 4 kiB */ + u64 address; + /* The number of 4 kiB pages in the constituent memory region. */ + u32 pg_cnt; + u32 reserved; +}; + +/** + * struct ffa_composite_mem_region - Composite memory region descriptor + * + * For more details, please refer to Table 5.13 in the FF-A + * specification v1.0. + * + * This structure was taken from Linux. + */ +struct ffa_composite_mem_region { + /* + * The total number of 4 kiB pages included in this memory region. This + * must be equal to the sum of page counts specified in each + * `struct ffa_mem_region_addr_range`. + */ + u32 total_pg_cnt; + /* The number of constituents included in this memory region range */ +#define FFA_MEM_CONSTITUENTS (1) + u32 addr_range_cnt; + u64 reserved; + /** An array of `addr_range_cnt` memory region constituents. */ + struct ffa_mem_region_addr_range constituents[]; +}; + +/* Functions prototypes */ + /** * ffa_get_version_hdlr() - FFA_VERSION handler function * @dev: The FF-A bus device -- 2.25.1