From d99873517907b806fe35f5a009c2b729c39d5808 Mon Sep 17 00:00:00 2001 From: Abdellatif El Khlifi Date: Fri, 4 Aug 2023 14:33:37 +0100 Subject: [PATCH 1/9] arm64: smccc: add support for SMCCCv1.2 x0-x17 registers add support for x0-x17 registers used by the SMC calls In SMCCC v1.2 [1] arguments are passed in registers x1-x17. Results are returned in x0-x17. This work is inspired from the following kernel commit: arm64: smccc: Add support for SMCCCv1.2 extended input/output registers [1]: https://documentation-service.arm.com/static/5f8edaeff86e16515cdbe4c6?token= Signed-off-by: Abdellatif El Khlifi Reviewed-by: Ilias Apalodimas Reviewed-by: Jens Wiklander Reviewed-by: Simon Glass Cc: Tom Rini --- arch/arm/cpu/armv8/smccc-call.S | 57 ++++++++++++++++++++++++++++++++- arch/arm/lib/asm-offsets.c | 16 +++++++++ include/linux/arm-smccc.h | 45 ++++++++++++++++++++++++++ 3 files changed, 117 insertions(+), 1 deletion(-) diff --git a/arch/arm/cpu/armv8/smccc-call.S b/arch/arm/cpu/armv8/smccc-call.S index dc92b28777c..93f66d33668 100644 --- a/arch/arm/cpu/armv8/smccc-call.S +++ b/arch/arm/cpu/armv8/smccc-call.S @@ -1,7 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2015, Linaro Limited - */ + * Copyright 2022-2023 Arm Limited and/or its affiliates + * + * Authors: + * Abdellatif El Khlifi +*/ #include #include #include @@ -45,3 +49,54 @@ ENDPROC(__arm_smccc_smc) ENTRY(__arm_smccc_hvc) SMCCC hvc ENDPROC(__arm_smccc_hvc) + +#ifdef CONFIG_ARM64 + + .macro SMCCC_1_2 instr + /* Save `res` and free a GPR that won't be clobbered */ + stp x1, x19, [sp, #-16]! + + /* Ensure `args` won't be clobbered while loading regs in next step */ + mov x19, x0 + + /* Load the registers x0 - x17 from the struct arm_smccc_1_2_regs */ + ldp x0, x1, [x19, #ARM_SMCCC_1_2_REGS_X0_OFFS] + ldp x2, x3, [x19, #ARM_SMCCC_1_2_REGS_X2_OFFS] + ldp x4, x5, [x19, #ARM_SMCCC_1_2_REGS_X4_OFFS] + ldp x6, x7, [x19, #ARM_SMCCC_1_2_REGS_X6_OFFS] + ldp x8, x9, [x19, #ARM_SMCCC_1_2_REGS_X8_OFFS] + ldp x10, x11, [x19, #ARM_SMCCC_1_2_REGS_X10_OFFS] + ldp x12, x13, [x19, #ARM_SMCCC_1_2_REGS_X12_OFFS] + ldp x14, x15, [x19, #ARM_SMCCC_1_2_REGS_X14_OFFS] + ldp x16, x17, [x19, #ARM_SMCCC_1_2_REGS_X16_OFFS] + + \instr #0 + + /* Load the `res` from the stack */ + ldr x19, [sp] + + /* Store the registers x0 - x17 into the result structure */ + stp x0, x1, [x19, #ARM_SMCCC_1_2_REGS_X0_OFFS] + stp x2, x3, [x19, #ARM_SMCCC_1_2_REGS_X2_OFFS] + stp x4, x5, [x19, #ARM_SMCCC_1_2_REGS_X4_OFFS] + stp x6, x7, [x19, #ARM_SMCCC_1_2_REGS_X6_OFFS] + stp x8, x9, [x19, #ARM_SMCCC_1_2_REGS_X8_OFFS] + stp x10, x11, [x19, #ARM_SMCCC_1_2_REGS_X10_OFFS] + stp x12, x13, [x19, #ARM_SMCCC_1_2_REGS_X12_OFFS] + stp x14, x15, [x19, #ARM_SMCCC_1_2_REGS_X14_OFFS] + stp x16, x17, [x19, #ARM_SMCCC_1_2_REGS_X16_OFFS] + + /* Restore original x19 */ + ldp xzr, x19, [sp], #16 + ret + .endm + +/* + * void arm_smccc_1_2_smc(const struct arm_smccc_1_2_regs *args, + * struct arm_smccc_1_2_regs *res); + */ +ENTRY(arm_smccc_1_2_smc) + SMCCC_1_2 smc +ENDPROC(arm_smccc_1_2_smc) + +#endif diff --git a/arch/arm/lib/asm-offsets.c b/arch/arm/lib/asm-offsets.c index 6de0ce91524..181a8ac4c27 100644 --- a/arch/arm/lib/asm-offsets.c +++ b/arch/arm/lib/asm-offsets.c @@ -9,6 +9,11 @@ * generate asm statements containing #defines, * compile this file to assembler, and then extract the * #defines from the assembly-language output. + * + * Copyright 2022-2023 Arm Limited and/or its affiliates + * + * Authors: + * Abdellatif El Khlifi */ #include @@ -90,6 +95,17 @@ int main(void) DEFINE(ARM_SMCCC_RES_X2_OFFS, offsetof(struct arm_smccc_res, a2)); DEFINE(ARM_SMCCC_QUIRK_ID_OFFS, offsetof(struct arm_smccc_quirk, id)); DEFINE(ARM_SMCCC_QUIRK_STATE_OFFS, offsetof(struct arm_smccc_quirk, state)); +#ifdef CONFIG_ARM64 + DEFINE(ARM_SMCCC_1_2_REGS_X0_OFFS, offsetof(struct arm_smccc_1_2_regs, a0)); + DEFINE(ARM_SMCCC_1_2_REGS_X2_OFFS, offsetof(struct arm_smccc_1_2_regs, a2)); + DEFINE(ARM_SMCCC_1_2_REGS_X4_OFFS, offsetof(struct arm_smccc_1_2_regs, a4)); + DEFINE(ARM_SMCCC_1_2_REGS_X6_OFFS, offsetof(struct arm_smccc_1_2_regs, a6)); + DEFINE(ARM_SMCCC_1_2_REGS_X8_OFFS, offsetof(struct arm_smccc_1_2_regs, a8)); + DEFINE(ARM_SMCCC_1_2_REGS_X10_OFFS, offsetof(struct arm_smccc_1_2_regs, a10)); + DEFINE(ARM_SMCCC_1_2_REGS_X12_OFFS, offsetof(struct arm_smccc_1_2_regs, a12)); + DEFINE(ARM_SMCCC_1_2_REGS_X14_OFFS, offsetof(struct arm_smccc_1_2_regs, a14)); + DEFINE(ARM_SMCCC_1_2_REGS_X16_OFFS, offsetof(struct arm_smccc_1_2_regs, a16)); +#endif #endif return 0; diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h index e1d09884a1c..f44e9e8f930 100644 --- a/include/linux/arm-smccc.h +++ b/include/linux/arm-smccc.h @@ -1,6 +1,10 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2015, Linaro Limited + * Copyright 2022-2023 Arm Limited and/or its affiliates + * + * Authors: + * Abdellatif El Khlifi */ #ifndef __LINUX_ARM_SMCCC_H #define __LINUX_ARM_SMCCC_H @@ -70,6 +74,47 @@ struct arm_smccc_res { unsigned long a3; }; +#ifdef CONFIG_ARM64 +/** + * struct arm_smccc_1_2_regs - Arguments for or Results from SMC call + * @a0-a17 argument values from registers 0 to 17 + */ +struct arm_smccc_1_2_regs { + unsigned long a0; + unsigned long a1; + unsigned long a2; + unsigned long a3; + unsigned long a4; + unsigned long a5; + unsigned long a6; + unsigned long a7; + unsigned long a8; + unsigned long a9; + unsigned long a10; + unsigned long a11; + unsigned long a12; + unsigned long a13; + unsigned long a14; + unsigned long a15; + unsigned long a16; + unsigned long a17; +}; + +/** + * arm_smccc_1_2_smc() - make SMC calls + * @args: arguments passed via struct arm_smccc_1_2_regs + * @res: result values via struct arm_smccc_1_2_regs + * + * This function is used to make SMC calls following SMC Calling Convention + * v1.2 or above. The content of the supplied param are copied from the + * structure to registers prior to the SMC instruction. The return values + * are updated with the content from registers on return from the SMC + * instruction. + */ +asmlinkage void arm_smccc_1_2_smc(const struct arm_smccc_1_2_regs *args, + struct arm_smccc_1_2_regs *res); +#endif + /** * struct arm_smccc_quirk - Contains quirk information * @id: quirk identification From 7048f26ccb181fc106a995e7e170b219087769f5 Mon Sep 17 00:00:00 2001 From: Abdellatif El Khlifi Date: Fri, 4 Aug 2023 14:33:38 +0100 Subject: [PATCH 2/9] lib: uuid: introduce uuid_str_to_le_bin function convert UUID string to little endian binary data Signed-off-by: Abdellatif El Khlifi Reviewed-by: Simon Glass Cc: Tom Rini Cc: Ilias Apalodimas Cc: Jens Wiklander --- include/uuid.h | 15 +++++++++++++++ lib/uuid.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/include/uuid.h b/include/uuid.h index 4a4883d3b5b..89b93e642b7 100644 --- a/include/uuid.h +++ b/include/uuid.h @@ -2,6 +2,10 @@ /* * Copyright (C) 2014 Samsung Electronics * Przemyslaw Marczak + * Copyright 2022-2023 Arm Limited and/or its affiliates + * + * Authors: + * Abdellatif El Khlifi */ #ifndef __UUID_H__ #define __UUID_H__ @@ -44,4 +48,15 @@ int uuid_guid_get_bin(const char *guid_str, unsigned char *guid_bin); const char *uuid_guid_get_str(const unsigned char *guid_bin); void gen_rand_uuid(unsigned char *uuid_bin); void gen_rand_uuid_str(char *uuid_str, int str_format); + +/** + * uuid_str_to_le_bin() - Convert string UUID to little endian binary data. + * @uuid_str: pointer to UUID string + * @uuid_bin: pointer to allocated array for little endian output [16B] + * Return: + * uuid_bin filled with little endian UUID data + * On success 0 is returned. Otherwise, failure code. + */ +int uuid_str_to_le_bin(const char *uuid_str, unsigned char *uuid_bin); + #endif diff --git a/lib/uuid.c b/lib/uuid.c index ab30fbf9152..d0187007d0e 100644 --- a/lib/uuid.c +++ b/lib/uuid.c @@ -1,6 +1,10 @@ // SPDX-License-Identifier: GPL-2.0+ /* * Copyright 2011 Calxeda, Inc. + * Copyright 2022-2023 Arm Limited and/or its affiliates + * + * Authors: + * Abdellatif El Khlifi */ #include @@ -354,6 +358,50 @@ int uuid_str_to_bin(const char *uuid_str, unsigned char *uuid_bin, return 0; } +/** + * uuid_str_to_le_bin() - Convert string UUID to little endian binary data. + * @uuid_str: pointer to UUID string + * @uuid_bin: pointer to allocated array for little endian output [16B] + * + * UUID string is 36 characters (36 bytes): + * + * xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + * + * where x is a hexadecimal character. Fields are separated by '-'s. + * When converting to a little endian binary UUID, the string fields are reversed. + * + * Return: + * + * uuid_bin filled with little endian UUID data + * On success 0 is returned. Otherwise, failure code. + */ +int uuid_str_to_le_bin(const char *uuid_str, unsigned char *uuid_bin) +{ + u16 tmp16; + u32 tmp32; + u64 tmp64; + + if (!uuid_str_valid(uuid_str) || !uuid_bin) + return -EINVAL; + + tmp32 = cpu_to_le32(hextoul(uuid_str, NULL)); + memcpy(uuid_bin, &tmp32, 4); + + tmp16 = cpu_to_le16(hextoul(uuid_str + 9, NULL)); + memcpy(uuid_bin + 4, &tmp16, 2); + + tmp16 = cpu_to_le16(hextoul(uuid_str + 14, NULL)); + memcpy(uuid_bin + 6, &tmp16, 2); + + tmp16 = cpu_to_le16(hextoul(uuid_str + 19, NULL)); + memcpy(uuid_bin + 8, &tmp16, 2); + + tmp64 = cpu_to_le64(simple_strtoull(uuid_str + 24, NULL, 16)); + memcpy(uuid_bin + 10, &tmp64, 6); + + return 0; +} + /* * uuid_bin_to_str() - convert big endian binary data to string UUID or GUID. * From b83dc8df6471f472340b88f1e3f3230e836b1fa9 Mon Sep 17 00:00:00 2001 From: Abdellatif El Khlifi Date: Fri, 4 Aug 2023 14:33:39 +0100 Subject: [PATCH 3/9] lib: uuid: introduce testcase for uuid_str_to_le_bin provide a test case Signed-off-by: Abdellatif El Khlifi Reviewed-by: Simon Glass Cc: Tom Rini --- MAINTAINERS | 5 +++++ test/lib/Makefile | 1 + test/lib/uuid.c | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100644 test/lib/uuid.c diff --git a/MAINTAINERS b/MAINTAINERS index 2db052961b2..830c556ddb5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1605,6 +1605,11 @@ T: git https://source.denx.de/u-boot/custodians/u-boot-usb.git topic-xhci F: drivers/usb/host/xhci* F: include/usb/xhci.h +UUID testing +M: Abdellatif El Khlifi +S: Maintained +F: test/lib/uuid.c + VIDEO M: Anatolij Gustschin S: Maintained diff --git a/test/lib/Makefile b/test/lib/Makefile index e0bd9e04e8f..e75a263e6a4 100644 --- a/test/lib/Makefile +++ b/test/lib/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_AES) += test_aes.o obj-$(CONFIG_GETOPT) += getopt.o obj-$(CONFIG_CRC8) += test_crc8.o obj-$(CONFIG_UT_LIB_CRYPT) += test_crypt.o +obj-$(CONFIG_LIB_UUID) += uuid.o else obj-$(CONFIG_SANDBOX) += kconfig_spl.o endif diff --git a/test/lib/uuid.c b/test/lib/uuid.c new file mode 100644 index 00000000000..e24331a1366 --- /dev/null +++ b/test/lib/uuid.c @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Functional tests for UCLASS_FFA class + * + * Copyright 2022-2023 Arm Limited and/or its affiliates + * + * Authors: + * Abdellatif El Khlifi + */ + +#include +#include +#include +#include +#include + +/* test UUID */ +#define TEST_SVC_UUID "ed32d533-4209-99e6-2d72-cdd998a79cc0" + +#define UUID_SIZE 16 + +/* The UUID binary data (little-endian format) */ +static const u8 ref_uuid_bin[UUID_SIZE] = { + 0x33, 0xd5, 0x32, 0xed, + 0x09, 0x42, 0xe6, 0x99, + 0x72, 0x2d, 0xc0, 0x9c, + 0xa7, 0x98, 0xd9, 0xcd +}; + +static int lib_test_uuid_to_le(struct unit_test_state *uts) +{ + const char *uuid_str = TEST_SVC_UUID; + u8 ret_uuid_bin[UUID_SIZE] = {0}; + + ut_assertok(uuid_str_to_le_bin(uuid_str, ret_uuid_bin)); + ut_asserteq_mem(ref_uuid_bin, ret_uuid_bin, UUID_SIZE); + + return 0; +} + +LIB_TEST(lib_test_uuid_to_le, 0); From 39d383bdace4b39b10665c7df2f1ef17399f6f1e Mon Sep 17 00:00:00 2001 From: Abdellatif El Khlifi Date: Fri, 4 Aug 2023 14:33:40 +0100 Subject: [PATCH 4/9] arm_ffa: introduce Arm FF-A support Add Arm FF-A support implementing Arm Firmware Framework for Armv8-A v1.0 The Firmware Framework for Arm A-profile processors (FF-A v1.0) [1] describes interfaces (ABIs) that standardize communication between the Secure World and Normal World leveraging TrustZone technology. This driver uses 64-bit registers as per SMCCCv1.2 spec and comes on top of the SMCCC layer. The driver provides the FF-A ABIs needed for querying the FF-A framework from the secure world. The driver uses SMC32 calling convention which means using the first 32-bit data of the Xn registers. All supported ABIs come with their 32-bit version except FFA_RXTX_MAP which has 64-bit version supported. Both 32-bit and 64-bit direct messaging are supported which allows both 32-bit and 64-bit clients to use the FF-A bus. FF-A is a discoverable bus and similar to architecture features. FF-A bus is discovered using ARM_SMCCC_FEATURES mechanism performed by the PSCI driver. Clients are able to probe then use the FF-A bus by calling the DM class searching APIs (e.g: uclass_first_device). The Secure World is considered as one entity to communicate with using the FF-A bus. FF-A communication is handled by one device and one instance (the bus). This FF-A driver takes care of all the interactions between Normal world and Secure World. The driver exports its operations to be used by upper layers. Exported operations: - ffa_partition_info_get - ffa_sync_send_receive - ffa_rxtx_unmap Generic FF-A methods are implemented in the Uclass (arm-ffa-uclass.c). Arm specific methods are implemented in the Arm driver (arm-ffa.c). For more details please refer to the driver documentation [2]. [1]: https://developer.arm.com/documentation/den0077/latest/ [2]: doc/arch/arm64.ffa.rst Signed-off-by: Abdellatif El Khlifi Reviewed-by: Simon Glass Reviewed-by: Ilias Apalodimas Cc: Tom Rini Cc: Jens Wiklander Cc: Heinrich Schuchardt --- MAINTAINERS | 8 + doc/arch/arm64.ffa.rst | 236 ++++ doc/arch/index.rst | 1 + drivers/Makefile | 1 + drivers/firmware/Kconfig | 1 + drivers/firmware/arm-ffa/Kconfig | 36 + drivers/firmware/arm-ffa/Makefile | 8 + drivers/firmware/arm-ffa/arm-ffa-uclass.c | 1065 +++++++++++++++++ drivers/firmware/arm-ffa/arm-ffa.c | 104 ++ .../firmware/arm-ffa/sandbox_arm_ffa_priv.h | 14 + include/arm_ffa.h | 213 ++++ include/arm_ffa_priv.h | 246 ++++ include/dm/uclass-id.h | 6 + 13 files changed, 1939 insertions(+) create mode 100644 doc/arch/arm64.ffa.rst create mode 100644 drivers/firmware/arm-ffa/Kconfig create mode 100644 drivers/firmware/arm-ffa/Makefile create mode 100644 drivers/firmware/arm-ffa/arm-ffa-uclass.c create mode 100644 drivers/firmware/arm-ffa/arm-ffa.c create mode 100644 drivers/firmware/arm-ffa/sandbox_arm_ffa_priv.h create mode 100644 include/arm_ffa.h create mode 100644 include/arm_ffa_priv.h diff --git a/MAINTAINERS b/MAINTAINERS index 830c556ddb5..fcca162dee2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -266,6 +266,14 @@ F: drivers/net/cortina_ni.h F: drivers/net/phy/ca_phy.c F: configs/cortina_presidio-asic-pnand_defconfig +ARM FF-A +M: Abdellatif El Khlifi +S: Maintained +F: doc/arch/arm64.ffa.rst +F: drivers/firmware/arm-ffa/ +F: include/arm_ffa.h +F: include/sandbox_arm_ffa.h + ARM FREESCALE IMX M: Stefano Babic M: Fabio Estevam diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst new file mode 100644 index 00000000000..df189487741 --- /dev/null +++ b/doc/arch/arm64.ffa.rst @@ -0,0 +1,236 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +Arm FF-A Support +================ + +Summary +------- + +FF-A stands for Firmware Framework for Arm A-profile processors. + +FF-A specifies interfaces that enable a pair of software execution environments aka partitions to +communicate with each other. A partition could be a VM in the Normal or Secure world, an +application in S-EL0, or a Trusted OS in S-EL1. + +The U-Boot FF-A support (the bus) implements the interfaces to communicate +with partitions in the Secure world aka Secure partitions (SPs). + +The FF-A support specifically focuses on communicating with SPs that +isolate portions of EFI runtime services that must run in a protected +environment which is inaccessible by the Host OS or Hypervisor. +Examples of such services are set/get variables. + +The FF-A support uses the SMC ABIs defined by the FF-A specification to: + +- Discover the presence of SPs of interest +- Access an SP's service through communication protocols + e.g. EFI MM communication protocol + +At this stage of development only EFI boot-time services are supported. +Runtime support will be added in future developments. + +The U-Boot FF-A support provides the following parts: + +- A Uclass driver providing generic FF-A methods. +- An Arm FF-A device driver providing Arm-specific methods and reusing the Uclass methods. + +FF-A and SMC specifications +------------------------------------------- + +The current implementation of the U-Boot FF-A support relies on +`FF-A v1.0 specification`_ and uses SMC32 calling convention which +means using the first 32-bit data of the Xn registers. + +At this stage we only need the FF-A v1.0 features. + +The FF-A support has been tested with OP-TEE which supports SMC32 calling +convention. + +Hypervisors are supported if they are configured to trap SMC calls. + +The FF-A support uses 64-bit registers as per `SMC Calling Convention v1.2 specification`_. + +Supported hardware +-------------------------------- + +Aarch64 plaforms + +Configuration +---------------------- + +CONFIG_ARM_FFA_TRANSPORT + Enables the FF-A support. Turn this on if you want to use FF-A + communication. + When using an Arm 64-bit platform, the Arm FF-A driver will be used. + +FF-A ABIs under the hood +--------------------------------------- + +Invoking an FF-A ABI involves providing to the secure world/hypervisor the +expected arguments from the ABI. + +On an Arm 64-bit platform, the ABI arguments are stored in x0 to x7 registers. +Then, an SMC instruction is executed. + +At the secure side level or hypervisor the ABI is handled at a higher exception +level and the arguments are read and processed. + +The response is put back through x0 to x7 registers and control is given back +to the U-Boot Arm FF-A driver (non-secure world). + +The driver reads the response and processes it accordingly. + +This methodology applies to all the FF-A ABIs. + +FF-A bus discovery on Arm 64-bit platforms +--------------------------------------------- + +When CONFIG_ARM_FFA_TRANSPORT is enabled, the FF-A bus is considered as +an architecture feature and discovered using ARM_SMCCC_FEATURES mechanism. +This discovery mechanism is performed by the PSCI driver. + +The PSCI driver comes with a PSCI device tree node which is the root node for all +architecture features including FF-A bus. + +:: + + => dm tree + + Class Index Probed Driver Name + ----------------------------------------------------------- + ... + firmware 0 [ + ] psci |-- psci + ffa 0 [ ] arm_ffa | `-- arm_ffa + ... + +The PSCI driver is bound to the PSCI device and when probed it tries to discover +the architecture features by calling a callback the features drivers provide. + +In case of FF-A, the callback is arm_ffa_is_supported() which tries to discover the +FF-A framework by querying the FF-A framework version from secure world using +FFA_VERSION ABI. When discovery is successful, the ARM_SMCCC_FEATURES +mechanism creates a U-Boot device for the FF-A bus and binds the Arm FF-A driver +with the device using device_bind_driver(). + +At this stage the FF-A bus is registered with the DM and can be interacted with using +the DM APIs. + +Clients are able to probe then use the FF-A bus by calling uclass_first_device(). + +When calling uclass_first_device(), the FF-A driver is probed and ends up calling +ffa_do_probe() provided by the Uclass which does the following: + + - saving the FF-A framework version in uc_priv + - querying from secure world the u-boot endpoint ID + - querying from secure world the supported features of FFA_RXTX_MAP + - mapping the RX/TX buffers + - querying from secure world all the partitions information + +When one of the above actions fails, probing fails and the driver stays not active +and can be probed again if needed. + +Requirements for clients +------------------------------------- + +When using the FF-A bus with EFI, clients must query the SPs they are looking for +during EFI boot-time mode using the service UUID. + +The RX/TX buffers are only available at EFI boot-time. Querying partitions is +done at boot time and data is cached for future use. + +RX/TX buffers should be unmapped before EFI runtime mode starts. +The driver provides a bus operation for that called ffa_rxtx_unmap(). + +The user should call ffa_rxtx_unmap() to unmap the RX/TX buffers when required +(e.g: at efi_exit_boot_services()). + +The Linux kernel allocates its own RX/TX buffers. To be able to register these kernel buffers +with secure world, the U-Boot's RX/TX buffers should be unmapped before EFI runtime starts. + +When invoking FF-A direct messaging, clients should specify which ABI protocol +they want to use (32-bit vs 64-bit). Selecting the protocol means using +the 32-bit or 64-bit version of FFA_MSG_SEND_DIRECT_{REQ, RESP}. +The calling convention between U-Boot and the secure world stays the same: SMC32. + +Requirements for user drivers +------------------------------------- + +Users who want to implement their custom FF-A device driver while reusing the FF-A Uclass can do so +by implementing their own invoke_ffa_fn() in the user driver. + +The bus driver layer +------------------------------ + +FF-A support comes on top of the SMCCC layer and is implemented by the FF-A Uclass drivers/firmware/arm-ffa/arm-ffa-uclass.c + +The following features are provided: + +- Support for the 32-bit version of the following ABIs: + + - FFA_VERSION + - FFA_ID_GET + - FFA_FEATURES + - FFA_PARTITION_INFO_GET + - FFA_RXTX_UNMAP + - FFA_RX_RELEASE + - FFA_RUN + - FFA_ERROR + - FFA_SUCCESS + - FFA_INTERRUPT + - FFA_MSG_SEND_DIRECT_REQ + - FFA_MSG_SEND_DIRECT_RESP + +- Support for the 64-bit version of the following ABIs: + + - FFA_RXTX_MAP + - FFA_MSG_SEND_DIRECT_REQ + - FFA_MSG_SEND_DIRECT_RESP + +- Processing the received data from the secure world/hypervisor and caching it + +- Hiding from upper layers the FF-A protocol and registers details. Upper + layers focus on exchanged data, FF-A support takes care of how to transport + that to the secure world/hypervisor using FF-A + +- FF-A support provides driver operations to be used by upper layers: + + - ffa_partition_info_get + - ffa_sync_send_receive + - ffa_rxtx_unmap + +- FF-A bus discovery makes sure FF-A framework is responsive and compatible + with the driver + +- FF-A bus can be compiled and used without EFI + +Example of boot logs with FF-A enabled +-------------------------------------- + +For example, when using FF-A with Corstone-1000 the logs are as follows: + +:: + + U-Boot 2023.01 (May 10 2023 - 11:08:07 +0000) corstone1000 aarch64 + + DRAM: 2 GiB + Arm FF-A framework discovery + FF-A driver 1.0 + FF-A framework 1.0 + FF-A versions are compatible + ... + FF-A driver 1.0 + FF-A framework 1.0 + FF-A versions are compatible + EFI: MM partition ID 0x8003 + ... + EFI stub: Booting Linux Kernel... + ... + Linux version 6.1.9-yocto-standard (oe-user@oe-host) (aarch64-poky-linux-musl-gcc (GCC) 12.2.0, GNU ld (GNU Binutils) 2.40.202301193 + Machine model: ARM Corstone1000 FPGA MPS3 board + +Contributors +------------ + * Abdellatif El Khlifi + +.. _`FF-A v1.0 specification`: https://documentation-service.arm.com/static/5fb7e8a6ca04df4095c1d65e +.. _`SMC Calling Convention v1.2 specification`: https://documentation-service.arm.com/static/5f8edaeff86e16515cdbe4c6 diff --git a/doc/arch/index.rst b/doc/arch/index.rst index b8da4b8c8e5..2f916f4026c 100644 --- a/doc/arch/index.rst +++ b/doc/arch/index.rst @@ -8,6 +8,7 @@ Architecture-specific doc arc arm64 + arm64.ffa m68k mips nios2 diff --git a/drivers/Makefile b/drivers/Makefile index 3bc6d279d7c..2e38c7552cc 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -115,6 +115,7 @@ obj-y += iommu/ obj-y += smem/ obj-y += thermal/ obj-$(CONFIG_TEE) += tee/ +obj-$(CONFIG_ARM_FFA_TRANSPORT) += firmware/arm-ffa/ obj-y += axi/ obj-y += ufs/ obj-$(CONFIG_W1) += w1/ diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index eae1c8ddc9f..8789b1ea141 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -45,4 +45,5 @@ config ARM_SMCCC_FEATURES the PSCI driver is always probed and binds dirvers registered to the Arm SMCCC services if any and reported as supported by the SMCCC firmware. +source "drivers/firmware/arm-ffa/Kconfig" source "drivers/firmware/scmi/Kconfig" diff --git a/drivers/firmware/arm-ffa/Kconfig b/drivers/firmware/arm-ffa/Kconfig new file mode 100644 index 00000000000..9200c8028b8 --- /dev/null +++ b/drivers/firmware/arm-ffa/Kconfig @@ -0,0 +1,36 @@ +# SPDX-License-Identifier: GPL-2.0 + +config ARM_FFA_TRANSPORT + bool "Enable Arm Firmware Framework for Armv8-A driver" + depends on DM && ARM64 + select ARM_SMCCC + select ARM_SMCCC_FEATURES + select LIB_UUID + select DEVRES + help + The Firmware Framework for Arm A-profile processors (FF-A) + describes interfaces (ABIs) that standardize communication + between the Secure World and Normal World leveraging TrustZone + technology. + + The FF-A support in U-Boot is based on FF-A specification v1.0 and uses SMC32 + calling convention. + + FF-A specification: + + https://developer.arm.com/documentation/den0077/a/?lang=en + + In U-Boot FF-A design, FF-A is considered as a discoverable bus. + FF-A bus is discovered using ARM_SMCCC_FEATURES mechanism performed + by the PSCI driver. + The Secure World is considered as one entity to communicate with + using the FF-A bus. + FF-A communication is handled by one device and one instance (the bus). + The FF-A support on U-Boot takes care of all the interactions between Normal + world and Secure World. + + Generic FF-A methods are implemented in the Uclass (arm-ffa-uclass.c). + Arm specific methods are implemented in the Arm driver (arm-ffa.c). + + For more details about the FF-A support, please refer to doc/arch/arm64.ffa.rst + diff --git a/drivers/firmware/arm-ffa/Makefile b/drivers/firmware/arm-ffa/Makefile new file mode 100644 index 00000000000..11b17662853 --- /dev/null +++ b/drivers/firmware/arm-ffa/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright 2022-2023 Arm Limited and/or its affiliates +# +# Authors: +# Abdellatif El Khlifi + +obj-y += arm-ffa-uclass.o arm-ffa.o diff --git a/drivers/firmware/arm-ffa/arm-ffa-uclass.c b/drivers/firmware/arm-ffa/arm-ffa-uclass.c new file mode 100644 index 00000000000..ffa9d81fa74 --- /dev/null +++ b/drivers/firmware/arm-ffa/arm-ffa-uclass.c @@ -0,0 +1,1065 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2022-2023 Arm Limited and/or its affiliates + * + * Authors: + * Abdellatif El Khlifi + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +/* Error mapping declarations */ + +int ffa_to_std_errmap[MAX_NUMBER_FFA_ERR] = { + [NOT_SUPPORTED] = -EOPNOTSUPP, + [INVALID_PARAMETERS] = -EINVAL, + [NO_MEMORY] = -ENOMEM, + [BUSY] = -EBUSY, + [INTERRUPTED] = -EINTR, + [DENIED] = -EACCES, + [RETRY] = -EAGAIN, + [ABORTED] = -ECANCELED, +}; + +static struct ffa_abi_errmap err_msg_map[FFA_ERRMAP_COUNT] = { + [FFA_ID_TO_ERRMAP_ID(FFA_VERSION)] = { + { + [NOT_SUPPORTED] = + "NOT_SUPPORTED: A Firmware Framework implementation does not exist", + }, + }, + [FFA_ID_TO_ERRMAP_ID(FFA_ID_GET)] = { + { + [NOT_SUPPORTED] = + "NOT_SUPPORTED: This function is not implemented at this FF-A instance", + }, + }, + [FFA_ID_TO_ERRMAP_ID(FFA_FEATURES)] = { + { + [NOT_SUPPORTED] = + "NOT_SUPPORTED: FFA_RXTX_MAP is not implemented at this FF-A instance", + }, + }, + [FFA_ID_TO_ERRMAP_ID(FFA_PARTITION_INFO_GET)] = { + { + [NOT_SUPPORTED] = + "NOT_SUPPORTED: This function is not implemented at this FF-A instance", + [INVALID_PARAMETERS] = + "INVALID_PARAMETERS: Unrecognized UUID", + [NO_MEMORY] = + "NO_MEMORY: Results cannot fit in RX buffer of the caller", + [BUSY] = + "BUSY: RX buffer of the caller is not free", + [DENIED] = + "DENIED: Callee is not in a state to handle this request", + }, + }, + [FFA_ID_TO_ERRMAP_ID(FFA_RXTX_UNMAP)] = { + { + [NOT_SUPPORTED] = + "NOT_SUPPORTED: FFA_RXTX_UNMAP is not implemented at this FF-A instance", + [INVALID_PARAMETERS] = + "INVALID_PARAMETERS: No buffer pair registered on behalf of the caller", + }, + }, + [FFA_ID_TO_ERRMAP_ID(FFA_RX_RELEASE)] = { + { + [NOT_SUPPORTED] = + "NOT_SUPPORTED: FFA_RX_RELEASE is not implemented at this FF-A instance", + [DENIED] = + "DENIED: Caller did not have ownership of the RX buffer", + }, + }, + [FFA_ID_TO_ERRMAP_ID(FFA_RXTX_MAP)] = { + { + [NOT_SUPPORTED] = + "NOT_SUPPORTED: This function is not implemented at this FF-A instance", + [INVALID_PARAMETERS] = + "INVALID_PARAMETERS: Field(s) in input parameters incorrectly encoded", + [NO_MEMORY] = + "NO_MEMORY: Not enough memory", + [DENIED] = + "DENIED: Buffer pair already registered", + }, + }, +}; + +/** + * ffa_to_std_errno() - convert FF-A error code to standard error code + * @ffa_errno: Error code returned by the FF-A ABI + * + * Map the given FF-A error code as specified + * by the spec to a u-boot standard error code. + * + * Return: + * + * The standard error code on success. . Otherwise, failure + */ +static int ffa_to_std_errno(int ffa_errno) +{ + int err_idx = -ffa_errno; + + /* Map the FF-A error code to the standard u-boot error code */ + if (err_idx > 0 && err_idx < MAX_NUMBER_FFA_ERR) + return ffa_to_std_errmap[err_idx]; + return -EINVAL; +} + +/** + * ffa_print_error_log() - print the error log corresponding to the selected FF-A ABI + * @ffa_id: FF-A ABI ID + * @ffa_errno: Error code returned by the FF-A ABI + * + * Map the FF-A error code to the error log relevant to the + * selected FF-A ABI. Then the error log is printed. + * + * Return: + * + * 0 on success. . Otherwise, failure + */ +static int ffa_print_error_log(u32 ffa_id, int ffa_errno) +{ + int err_idx = -ffa_errno, abi_idx = 0; + + /* Map the FF-A error code to the corresponding error log */ + + if (err_idx <= 0 || err_idx >= MAX_NUMBER_FFA_ERR) + return -EINVAL; + + if (ffa_id < FFA_FIRST_ID || ffa_id > FFA_LAST_ID) + return -EINVAL; + + abi_idx = FFA_ID_TO_ERRMAP_ID(ffa_id); + if (abi_idx < 0 || abi_idx >= FFA_ERRMAP_COUNT) + return -EINVAL; + + if (!err_msg_map[abi_idx].err_str[err_idx]) + return -EINVAL; + + log_err("%s\n", err_msg_map[abi_idx].err_str[err_idx]); + + return 0; +} + +/* FF-A ABIs implementation (U-Boot side) */ + +/** + * invoke_ffa_fn() - SMC wrapper + * @args: FF-A ABI arguments to be copied to Xn registers + * @res: FF-A ABI return data to be copied from Xn registers + * + * Calls low level SMC implementation. + * This function should be implemented by the user driver. + */ +void __weak invoke_ffa_fn(ffa_value_t args, ffa_value_t *res) +{ +} + +/** + * ffa_get_version_hdlr() - FFA_VERSION handler function + * @dev: The FF-A bus device + * + * Implement FFA_VERSION FF-A function + * to get from the secure world the FF-A framework version + * FFA_VERSION is used to discover the FF-A framework. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int ffa_get_version_hdlr(struct udevice *dev) +{ + u16 major, minor; + ffa_value_t res = {0}; + int ffa_errno; + struct ffa_priv *uc_priv; + + invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_32(FFA_VERSION), .a1 = FFA_VERSION_1_0, + }, &res); + + ffa_errno = res.a0; + if (ffa_errno < 0) { + ffa_print_error_log(FFA_VERSION, ffa_errno); + return ffa_to_std_errno(ffa_errno); + } + + major = GET_FFA_MAJOR_VERSION(res.a0); + minor = GET_FFA_MINOR_VERSION(res.a0); + + log_info("FF-A driver %d.%d\nFF-A framework %d.%d\n", + FFA_MAJOR_VERSION, FFA_MINOR_VERSION, major, minor); + + if (major == FFA_MAJOR_VERSION && minor >= FFA_MINOR_VERSION) { + log_info("FF-A versions are compatible\n"); + + if (dev) { + uc_priv = dev_get_uclass_priv(dev); + if (uc_priv) + uc_priv->fwk_version = res.a0; + } + + return 0; + } + + log_err("versions are incompatible\nExpected: %d.%d , Found: %d.%d\n", + FFA_MAJOR_VERSION, FFA_MINOR_VERSION, major, minor); + + return -EPROTONOSUPPORT; +} + +/** + * ffa_get_endpoint_id() - FFA_ID_GET handler function + * @dev: The FF-A bus device + * + * Implement FFA_ID_GET FF-A function + * to get from the secure world u-boot endpoint ID + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_get_endpoint_id(struct udevice *dev) +{ + ffa_value_t res = {0}; + int ffa_errno; + struct ffa_priv *uc_priv = dev_get_uclass_priv(dev); + + invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_32(FFA_ID_GET), + }, &res); + + if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) { + uc_priv->id = GET_SELF_ENDPOINT_ID((u32)res.a2); + log_debug("FF-A endpoint ID is %u\n", uc_priv->id); + + return 0; + } + + ffa_errno = res.a2; + + ffa_print_error_log(FFA_ID_GET, ffa_errno); + + return ffa_to_std_errno(ffa_errno); +} + +/** + * ffa_set_rxtx_buffers_pages_cnt() - set the minimum number of pages in each of the RX/TX buffers + * @dev: The FF-A bus device + * @prop_field: properties field obtained from FFA_FEATURES ABI + * + * Set the minimum number of pages in each of the RX/TX buffers in uc_priv + * + * Return: + * + * rxtx_min_pages field contains the returned number of pages + * 0 on success. Otherwise, failure + */ +static int ffa_set_rxtx_buffers_pages_cnt(struct udevice *dev, u32 prop_field) +{ + struct ffa_priv *uc_priv = dev_get_uclass_priv(dev); + + switch (prop_field) { + case RXTX_4K: + uc_priv->pair.rxtx_min_pages = 1; + break; + case RXTX_16K: + uc_priv->pair.rxtx_min_pages = 4; + break; + case RXTX_64K: + uc_priv->pair.rxtx_min_pages = 16; + break; + default: + log_err("RX/TX buffer size not supported\n"); + return -EINVAL; + } + + return 0; +} + +/** + * ffa_get_rxtx_map_features_hdlr() - FFA_FEATURES handler function with FFA_RXTX_MAP argument + * @dev: The FF-A bus device + * + * Implement FFA_FEATURES FF-A function to retrieve the FFA_RXTX_MAP features + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_get_rxtx_map_features_hdlr(struct udevice *dev) +{ + ffa_value_t res = {0}; + int ffa_errno; + + invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_32(FFA_FEATURES), + .a1 = FFA_SMC_64(FFA_RXTX_MAP), + }, &res); + + if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) + return ffa_set_rxtx_buffers_pages_cnt(dev, res.a2); + + ffa_errno = res.a2; + ffa_print_error_log(FFA_FEATURES, ffa_errno); + + return ffa_to_std_errno(ffa_errno); +} + +/** + * ffa_free_rxtx_buffers() - free the RX/TX buffers + * @dev: The FF-A bus device + * + * Free the RX/TX buffers + */ +static void ffa_free_rxtx_buffers(struct udevice *dev) +{ + struct ffa_priv *uc_priv = dev_get_uclass_priv(dev); + + log_debug("Freeing FF-A RX/TX buffers\n"); + + if (uc_priv->pair.rxbuf) { + free(uc_priv->pair.rxbuf); + uc_priv->pair.rxbuf = NULL; + } + + if (uc_priv->pair.txbuf) { + free(uc_priv->pair.txbuf); + uc_priv->pair.txbuf = NULL; + } +} + +/** + * ffa_alloc_rxtx_buffers() - allocate the RX/TX buffers + * @dev: The FF-A bus device + * + * Used by ffa_map_rxtx_buffers to allocate + * the RX/TX buffers before mapping them. The allocated memory is physically + * contiguous since memalign ends up calling malloc which allocates + * contiguous memory in u-boot. + * The size of the memory allocated is the minimum allowed. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_alloc_rxtx_buffers(struct udevice *dev) +{ + u64 bytes; + struct ffa_priv *uc_priv = dev_get_uclass_priv(dev); + + log_debug("Using %lu 4KB page(s) for FF-A RX/TX buffers size\n", + uc_priv->pair.rxtx_min_pages); + + bytes = uc_priv->pair.rxtx_min_pages * SZ_4K; + + /* + * The alignment of the RX and TX buffers must be equal + * to the larger translation granule size + * Assumption: Memory allocated with memalign is always physically contiguous + */ + + uc_priv->pair.rxbuf = memalign(bytes, bytes); + if (!uc_priv->pair.rxbuf) { + log_err("failure to allocate RX buffer\n"); + return -ENOBUFS; + } + + log_debug("FF-A RX buffer at virtual address %p\n", uc_priv->pair.rxbuf); + + uc_priv->pair.txbuf = memalign(bytes, bytes); + if (!uc_priv->pair.txbuf) { + free(uc_priv->pair.rxbuf); + uc_priv->pair.rxbuf = NULL; + log_err("failure to allocate the TX buffer\n"); + return -ENOBUFS; + } + + log_debug("FF-A TX buffer at virtual address %p\n", uc_priv->pair.txbuf); + + /* Make sure the buffers are cleared before use */ + memset(uc_priv->pair.rxbuf, 0, bytes); + memset(uc_priv->pair.txbuf, 0, bytes); + + return 0; +} + +/** + * ffa_map_rxtx_buffers_hdlr() - FFA_RXTX_MAP handler function + * @dev: The FF-A bus device + * + * Implement FFA_RXTX_MAP FF-A function to map the RX/TX buffers + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_map_rxtx_buffers_hdlr(struct udevice *dev) +{ + int ret; + ffa_value_t res = {0}; + int ffa_errno; + struct ffa_priv *uc_priv = dev_get_uclass_priv(dev); + + ret = ffa_alloc_rxtx_buffers(dev); + if (ret) + return ret; + + /* + * we need to pass the physical addresses of the RX/TX buffers + * in u-boot physical/virtual mapping is 1:1 + * no need to convert from virtual to physical + */ + + invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_64(FFA_RXTX_MAP), + .a1 = map_to_sysmem(uc_priv->pair.txbuf), + .a2 = map_to_sysmem(uc_priv->pair.rxbuf), + .a3 = uc_priv->pair.rxtx_min_pages, + }, &res); + + if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) { + log_debug("FF-A RX/TX buffers mapped\n"); + return 0; + } + + ffa_errno = res.a2; + ffa_print_error_log(FFA_RXTX_MAP, ffa_errno); + + ffa_free_rxtx_buffers(dev); + + return ffa_to_std_errno(ffa_errno); +} + +/** + * ffa_unmap_rxtx_buffers_hdlr() - FFA_RXTX_UNMAP handler function + * @dev: The FF-A bus device + * + * Implement FFA_RXTX_UNMAP FF-A function to unmap the RX/TX buffers + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int ffa_unmap_rxtx_buffers_hdlr(struct udevice *dev) +{ + ffa_value_t res = {0}; + int ffa_errno; + struct ffa_priv *uc_priv; + + log_debug("unmapping FF-A RX/TX buffers\n"); + + uc_priv = dev_get_uclass_priv(dev); + + invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_32(FFA_RXTX_UNMAP), + .a1 = PREP_SELF_ENDPOINT_ID(uc_priv->id), + }, &res); + + if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) { + ffa_free_rxtx_buffers(dev); + return 0; + } + + ffa_errno = res.a2; + ffa_print_error_log(FFA_RXTX_UNMAP, ffa_errno); + + return ffa_to_std_errno(ffa_errno); +} + +/** + * ffa_release_rx_buffer_hdlr() - FFA_RX_RELEASE handler function + * @dev: The FF-A bus device + * + * Invoke FFA_RX_RELEASE FF-A function to release the ownership of the RX buffer + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_release_rx_buffer_hdlr(struct udevice *dev) +{ + ffa_value_t res = {0}; + int ffa_errno; + + invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_32(FFA_RX_RELEASE), + }, &res); + + if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) + return 0; + + ffa_errno = res.a2; + ffa_print_error_log(FFA_RX_RELEASE, ffa_errno); + + return ffa_to_std_errno(ffa_errno); +} + +/** + * ffa_uuid_are_identical() - check whether two given UUIDs are identical + * @uuid1: first UUID + * @uuid2: second UUID + * + * Used by ffa_read_partitions_info to search for a UUID in the partitions descriptors table + * + * Return: + * + * 1 when UUIDs match. Otherwise, 0 + */ +static bool ffa_uuid_are_identical(const struct ffa_partition_uuid *uuid1, + const struct ffa_partition_uuid *uuid2) +{ + if (!uuid1 || !uuid2) + return 0; + + return !memcmp(uuid1, uuid2, sizeof(struct ffa_partition_uuid)); +} + +/** + * ffa_read_partitions_info() - read queried partition data + * @dev: The FF-A bus device + * @count: The number of partitions queried + * @part_uuid: Pointer to the partition(s) UUID + * + * Read the partitions information returned by the FFA_PARTITION_INFO_GET and saves it in uc_priv + * + * Return: + * + * uc_priv is updated with the partition(s) information + * 0 is returned on success. Otherwise, failure + */ +static int ffa_read_partitions_info(struct udevice *dev, u32 count, + struct ffa_partition_uuid *part_uuid) +{ + struct ffa_priv *uc_priv = dev_get_uclass_priv(dev); + + if (!count) { + log_err("no partition detected\n"); + return -ENODATA; + } + + log_debug("Reading FF-A partitions data from the RX buffer\n"); + + if (!part_uuid) { + /* Querying information of all partitions */ + u64 buf_bytes; + u64 data_bytes; + u32 desc_idx; + struct ffa_partition_info *parts_info; + + data_bytes = count * sizeof(struct ffa_partition_desc); + + buf_bytes = uc_priv->pair.rxtx_min_pages * SZ_4K; + + if (data_bytes > buf_bytes) { + log_err("partitions data size exceeds the RX buffer size:\n"); + log_err(" sizes in bytes: data %llu , RX buffer %llu\n", + data_bytes, + buf_bytes); + + return -ENOMEM; + } + + uc_priv->partitions.descs = devm_kmalloc(dev, data_bytes, __GFP_ZERO); + if (!uc_priv->partitions.descs) { + log_err("cannot allocate partitions data buffer\n"); + return -ENOMEM; + } + + parts_info = uc_priv->pair.rxbuf; + + for (desc_idx = 0 ; desc_idx < count ; desc_idx++) { + uc_priv->partitions.descs[desc_idx].info = + parts_info[desc_idx]; + + log_debug("FF-A partition ID %x : info cached\n", + uc_priv->partitions.descs[desc_idx].info.id); + } + + uc_priv->partitions.count = count; + + log_debug("%d FF-A partition(s) found and cached\n", count); + + } else { + u32 rx_desc_idx, cached_desc_idx; + struct ffa_partition_info *parts_info; + u8 desc_found; + + parts_info = uc_priv->pair.rxbuf; + + /* + * Search for the SP IDs read from the RX buffer + * in the already cached SPs. + * Update the UUID when ID found. + */ + for (rx_desc_idx = 0; rx_desc_idx < count ; rx_desc_idx++) { + desc_found = 0; + + /* Search the current ID in the cached partitions */ + for (cached_desc_idx = 0; + cached_desc_idx < uc_priv->partitions.count; + cached_desc_idx++) { + /* Save the UUID */ + if (uc_priv->partitions.descs[cached_desc_idx].info.id == + parts_info[rx_desc_idx].id) { + uc_priv->partitions.descs[cached_desc_idx].sp_uuid = + *part_uuid; + + desc_found = 1; + break; + } + } + + if (!desc_found) + return -ENODATA; + } + } + + return 0; +} + +/** + * ffa_query_partitions_info() - invoke FFA_PARTITION_INFO_GET and save partitions data + * @dev: The FF-A bus device + * @part_uuid: Pointer to the partition(s) UUID + * @pcount: Pointer to the number of partitions variable filled when querying + * + * Execute the FFA_PARTITION_INFO_GET to query the partitions data. + * Then, call ffa_read_partitions_info to save the data in uc_priv. + * + * After reading the data the RX buffer is released using ffa_release_rx_buffer + * + * Return: + * + * When part_uuid is NULL, all partitions data are retrieved from secure world + * When part_uuid is non NULL, data for partitions matching the given UUID are + * retrieved and the number of partitions is returned + * 0 is returned on success. Otherwise, failure + */ +static int ffa_query_partitions_info(struct udevice *dev, struct ffa_partition_uuid *part_uuid, + u32 *pcount) +{ + struct ffa_partition_uuid query_uuid = {0}; + ffa_value_t res = {0}; + int ffa_errno; + + /* + * If a UUID is specified. Information for one or more + * partitions in the system is queried. Otherwise, information + * for all installed partitions is queried + */ + + if (part_uuid) { + if (!pcount) + return -EINVAL; + + query_uuid = *part_uuid; + } else if (pcount) { + return -EINVAL; + } + + invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_32(FFA_PARTITION_INFO_GET), + .a1 = query_uuid.a1, + .a2 = query_uuid.a2, + .a3 = query_uuid.a3, + .a4 = query_uuid.a4, + }, &res); + + if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) { + int ret; + + /* + * res.a2 contains the count of partition information descriptors + * populated in the RX buffer + */ + if (res.a2) { + ret = ffa_read_partitions_info(dev, (u32)res.a2, part_uuid); + if (ret) { + log_err("failed reading SP(s) data , err (%d)\n", ret); + ffa_release_rx_buffer_hdlr(dev); + return -EINVAL; + } + } + + /* Return the SP count (when querying using a UUID) */ + if (pcount) + *pcount = (u32)res.a2; + + /* + * After calling FFA_PARTITION_INFO_GET the buffer ownership + * is assigned to the consumer (u-boot). So, we need to give + * the ownership back to the SPM or hypervisor + */ + ret = ffa_release_rx_buffer_hdlr(dev); + + return ret; + } + + ffa_errno = res.a2; + ffa_print_error_log(FFA_PARTITION_INFO_GET, ffa_errno); + + return ffa_to_std_errno(ffa_errno); +} + +/** + * ffa_get_partitions_info_hdlr() - FFA_PARTITION_INFO_GET handler function + * @uuid_str: pointer to the UUID string + * @sp_count: address of the variable containing the number of partitions matching the UUID + * The variable is set by the driver + * @sp_descs: address of the descriptors of the partitions matching the UUID + * The address is set by the driver + * + * Return the number of partitions and their descriptors matching the UUID + * + * Query the secure partition data from uc_priv. + * If not found, invoke FFA_PARTITION_INFO_GET FF-A function to query the partition information + * from secure world. + * + * A client of the FF-A driver should know the UUID of the service it wants to + * access. It should use the UUID to request the FF-A driver to provide the + * partition(s) information of the service. The FF-A driver uses + * PARTITION_INFO_GET to obtain this information. This is implemented through + * ffa_get_partitions_info_hdlr() function. + * If the partition(s) matching the UUID found, the partition(s) information and the + * number are returned. + * If no partition matching the UUID is found in the cached area, a new FFA_PARTITION_INFO_GET + * call is issued. + * If not done yet, the UUID is updated in the cached area. + * This assumes that partitions data does not change in the secure world. + * Otherwise u-boot will have an outdated partition data. The benefit of caching + * the information in the FF-A driver is to accommodate discovery after + * ExitBootServices(). + * + * Return: + * + * @sp_count: the number of partitions + * @sp_descs: address of the partitions descriptors + * + * On success 0 is returned. Otherwise, failure + */ +int ffa_get_partitions_info_hdlr(struct udevice *dev, const char *uuid_str, + u32 *sp_count, struct ffa_partition_desc **sp_descs) +{ + u32 i; + struct ffa_partition_uuid part_uuid = {0}; + struct ffa_priv *uc_priv; + struct ffa_partition_desc *rx_descs; + + uc_priv = dev_get_uclass_priv(dev); + + if (!uc_priv->partitions.count || !uc_priv->partitions.descs) { + log_err("no partition installed\n"); + return -EINVAL; + } + + if (!uuid_str) { + log_err("no UUID provided\n"); + return -EINVAL; + } + + if (!sp_count) { + log_err("no count argument provided\n"); + return -EINVAL; + } + + if (!sp_descs) { + log_err("no info argument provided\n"); + return -EINVAL; + } + + if (uuid_str_to_le_bin(uuid_str, (unsigned char *)&part_uuid)) { + log_err("invalid UUID\n"); + return -EINVAL; + } + + log_debug("Searching FF-A partitions using the provided UUID\n"); + + *sp_count = 0; + *sp_descs = uc_priv->pair.rxbuf; + rx_descs = *sp_descs; + + /* Search in the cached partitions */ + for (i = 0; i < uc_priv->partitions.count; i++) + if (ffa_uuid_are_identical(&uc_priv->partitions.descs[i].sp_uuid, + &part_uuid)) { + log_debug("FF-A partition ID %x matches the provided UUID\n", + uc_priv->partitions.descs[i].info.id); + + (*sp_count)++; + *rx_descs++ = uc_priv->partitions.descs[i]; + } + + if (!(*sp_count)) { + int ret; + + log_debug("No FF-A partition found. Querying framework ...\n"); + + ret = ffa_query_partitions_info(dev, &part_uuid, sp_count); + + if (!ret) { + log_debug("Number of FF-A partition(s) matching the UUID: %d\n", *sp_count); + + if (*sp_count) + ret = ffa_get_partitions_info_hdlr(dev, uuid_str, sp_count, + sp_descs); + else + ret = -ENODATA; + } + + return ret; + } + + return 0; +} + +/** + * ffa_cache_partitions_info() - Query and saves all secure partitions data + * @dev: The FF-A bus device + * + * Invoke FFA_PARTITION_INFO_GET FF-A function to query from secure world + * all partitions information. + * + * The FFA_PARTITION_INFO_GET call is issued with nil UUID as an argument. + * All installed partitions information are returned. We cache them in uc_priv + * and we keep the UUID field empty (in FF-A 1.0 UUID is not provided by the partition descriptor) + * + * Called at the device probing level. + * ffa_cache_partitions_info uses ffa_query_partitions_info to get the data + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_cache_partitions_info(struct udevice *dev) +{ + return ffa_query_partitions_info(dev, NULL, NULL); +} + +/** + * ffa_msg_send_direct_req_hdlr() - FFA_MSG_SEND_DIRECT_{REQ,RESP} handler function + * @dev: The FF-A bus device + * @dst_part_id: destination partition ID + * @msg: pointer to the message data preallocated by the client (in/out) + * @is_smc64: select 64-bit or 32-bit FF-A ABI + * + * Implement FFA_MSG_SEND_DIRECT_{REQ,RESP} + * FF-A functions. + * + * FFA_MSG_SEND_DIRECT_REQ is used to send the data to the secure partition. + * The response from the secure partition is handled by reading the + * FFA_MSG_SEND_DIRECT_RESP arguments. + * + * The maximum size of the data that can be exchanged is 40 bytes which is + * sizeof(struct ffa_send_direct_data) as defined by the FF-A specification 1.0 + * in the section relevant to FFA_MSG_SEND_DIRECT_{REQ,RESP} + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int ffa_msg_send_direct_req_hdlr(struct udevice *dev, u16 dst_part_id, + struct ffa_send_direct_data *msg, bool is_smc64) +{ + ffa_value_t res = {0}; + int ffa_errno; + u64 req_mode, resp_mode; + struct ffa_priv *uc_priv; + + uc_priv = dev_get_uclass_priv(dev); + + /* No partition installed */ + if (!uc_priv->partitions.count || !uc_priv->partitions.descs) + return -ENODEV; + + if (is_smc64) { + req_mode = FFA_SMC_64(FFA_MSG_SEND_DIRECT_REQ); + resp_mode = FFA_SMC_64(FFA_MSG_SEND_DIRECT_RESP); + } else { + req_mode = FFA_SMC_32(FFA_MSG_SEND_DIRECT_REQ); + resp_mode = FFA_SMC_32(FFA_MSG_SEND_DIRECT_RESP); + } + + invoke_ffa_fn((ffa_value_t){ + .a0 = req_mode, + .a1 = PREP_SELF_ENDPOINT_ID(uc_priv->id) | + PREP_PART_ENDPOINT_ID(dst_part_id), + .a2 = 0, + .a3 = msg->data0, + .a4 = msg->data1, + .a5 = msg->data2, + .a6 = msg->data3, + .a7 = msg->data4, + }, &res); + + while (res.a0 == FFA_SMC_32(FFA_INTERRUPT)) + invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_32(FFA_RUN), + .a1 = res.a1, + }, &res); + + if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) { + /* Message sent with no response */ + return 0; + } + + if (res.a0 == resp_mode) { + /* Message sent with response extract the return data */ + msg->data0 = res.a3; + msg->data1 = res.a4; + msg->data2 = res.a5; + msg->data3 = res.a6; + msg->data4 = res.a7; + + return 0; + } + + ffa_errno = res.a2; + return ffa_to_std_errno(ffa_errno); +} + +/* FF-A driver operations (used by clients for communicating with FF-A)*/ + +/** + * ffa_partition_info_get() - FFA_PARTITION_INFO_GET driver operation + * @uuid_str: pointer to the UUID string + * @sp_count: address of the variable containing the number of partitions matching the UUID + * The variable is set by the driver + * @sp_descs: address of the descriptors of the partitions matching the UUID + * The address is set by the driver + * + * Driver operation for FFA_PARTITION_INFO_GET. + * Please see ffa_get_partitions_info_hdlr() description for more details. + * + * Return: + * + * @sp_count: the number of partitions + * @sp_descs: address of the partitions descriptors + * + * On success 0 is returned. Otherwise, failure + */ +int ffa_partition_info_get(struct udevice *dev, const char *uuid_str, + u32 *sp_count, struct ffa_partition_desc **sp_descs) +{ + struct ffa_bus_ops *ops = ffa_get_ops(dev); + + if (!ops->partition_info_get) + return -ENOSYS; + + return ops->partition_info_get(dev, uuid_str, sp_count, sp_descs); +} + +/** + * ffa_sync_send_receive() - FFA_MSG_SEND_DIRECT_{REQ,RESP} driver operation + * @dev: The FF-A bus device + * @dst_part_id: destination partition ID + * @msg: pointer to the message data preallocated by the client (in/out) + * @is_smc64: select 64-bit or 32-bit FF-A ABI + * + * Driver operation for FFA_MSG_SEND_DIRECT_{REQ,RESP}. + * Please see ffa_msg_send_direct_req_hdlr() description for more details. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int ffa_sync_send_receive(struct udevice *dev, u16 dst_part_id, + struct ffa_send_direct_data *msg, bool is_smc64) +{ + struct ffa_bus_ops *ops = ffa_get_ops(dev); + + if (!ops->sync_send_receive) + return -ENOSYS; + + return ops->sync_send_receive(dev, dst_part_id, msg, is_smc64); +} + +/** + * ffa_rxtx_unmap() - FFA_RXTX_UNMAP driver operation + * @dev: The FF-A bus device + * + * Driver operation for FFA_RXTX_UNMAP. + * Please see ffa_unmap_rxtx_buffers_hdlr() description for more details. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int ffa_rxtx_unmap(struct udevice *dev) +{ + struct ffa_bus_ops *ops = ffa_get_ops(dev); + + if (!ops->rxtx_unmap) + return -ENOSYS; + + return ops->rxtx_unmap(dev); +} + +/** + * ffa_do_probe() - probing FF-A framework + * @dev: the FF-A bus device (arm_ffa) + * + * Probing is triggered on demand by clients searching for the uclass. + * At probe level the following actions are done: + * - saving the FF-A framework version in uc_priv + * - querying from secure world the u-boot endpoint ID + * - querying from secure world the supported features of FFA_RXTX_MAP + * - mapping the RX/TX buffers + * - querying from secure world all the partitions information + * + * All data queried from secure world is saved in uc_priv. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_do_probe(struct udevice *dev) +{ + int ret; + + ret = ffa_get_version_hdlr(dev); + if (ret) + return ret; + + ret = ffa_get_endpoint_id(dev); + if (ret) + return ret; + + ret = ffa_get_rxtx_map_features_hdlr(dev); + if (ret) + return ret; + + ret = ffa_map_rxtx_buffers_hdlr(dev); + if (ret) + return ret; + + ret = ffa_cache_partitions_info(dev); + if (ret) { + ffa_unmap_rxtx_buffers_hdlr(dev); + return ret; + } + + return 0; +} + +UCLASS_DRIVER(ffa) = { + .name = "ffa", + .id = UCLASS_FFA, + .pre_probe = ffa_do_probe, + .pre_remove = ffa_unmap_rxtx_buffers_hdlr, + .per_device_auto = sizeof(struct ffa_priv) +}; diff --git a/drivers/firmware/arm-ffa/arm-ffa.c b/drivers/firmware/arm-ffa/arm-ffa.c new file mode 100644 index 00000000000..68df75bd9e5 --- /dev/null +++ b/drivers/firmware/arm-ffa/arm-ffa.c @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2022-2023 Arm Limited and/or its affiliates + * + * Authors: + * Abdellatif El Khlifi + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +/** + * invoke_ffa_fn() - SMC wrapper + * @args: FF-A ABI arguments to be copied to Xn registers + * @res: FF-A ABI return data to be copied from Xn registers + * + * Calls low level SMC assembly function + */ +void invoke_ffa_fn(ffa_value_t args, ffa_value_t *res) +{ + arm_smccc_1_2_smc(&args, res); +} + +/** + * arm_ffa_discover() - perform FF-A discovery + * @dev: The Arm FF-A bus device (arm_ffa) + * Try to discover the FF-A framework. Discovery is performed by + * querying the FF-A framework version from secure world using the FFA_VERSION ABI. + * Return: + * + * true on success. Otherwise, false. + */ +static bool arm_ffa_discover(struct udevice *dev) +{ + int ret; + + log_info("Arm FF-A framework discovery\n"); + + ret = ffa_get_version_hdlr(dev); + if (ret) + return false; + + return true; +} + +/** + * arm_ffa_is_supported() - FF-A bus discovery callback + * @invoke_fn: legacy SMC invoke function (not used) + * + * Perform FF-A discovery by calling arm_ffa_discover(). + * Discovery is performed by querying the FF-A framework version from + * secure world using the FFA_VERSION ABI. + * + * The FF-A driver is registered as an SMCCC feature driver. So, features discovery + * callbacks are called by the PSCI driver (PSCI device is the SMCCC features + * root device). + * + * The FF-A driver supports the SMCCCv1.2 extended input/output registers. + * So, the legacy SMC invocation is not used. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static bool arm_ffa_is_supported(void (*invoke_fn)(ulong a0, ulong a1, + ulong a2, ulong a3, + ulong a4, ulong a5, + ulong a6, ulong a7, + struct arm_smccc_res *res)) +{ + return arm_ffa_discover(NULL); +} + +/* Arm FF-A driver operations */ + +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, +}; + +/* Registering the FF-A driver as an SMCCC feature driver */ + +ARM_SMCCC_FEATURE_DRIVER(arm_ffa) = { + .driver_name = FFA_DRV_NAME, + .is_supported = arm_ffa_is_supported, +}; + +/* Declaring the FF-A driver under UCLASS_FFA */ + +U_BOOT_DRIVER(arm_ffa) = { + .name = FFA_DRV_NAME, + .id = UCLASS_FFA, + .flags = DM_REMOVE_OS_PREPARE, + .ops = &ffa_ops, +}; diff --git a/drivers/firmware/arm-ffa/sandbox_arm_ffa_priv.h b/drivers/firmware/arm-ffa/sandbox_arm_ffa_priv.h new file mode 100644 index 00000000000..4338f9c9b17 --- /dev/null +++ b/drivers/firmware/arm-ffa/sandbox_arm_ffa_priv.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2022-2023 Arm Limited and/or its affiliates + * + * Authors: + * Abdellatif El Khlifi + */ + +#ifndef __SANDBOX_ARM_FFA_PRV_H +#define __SANDBOX_ARM_FFA_PRV_H + +/* Future sandbox support private declarations */ + +#endif diff --git a/include/arm_ffa.h b/include/arm_ffa.h new file mode 100644 index 00000000000..db9b1be995e --- /dev/null +++ b/include/arm_ffa.h @@ -0,0 +1,213 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2022-2023 Arm Limited and/or its affiliates + * + * Authors: + * Abdellatif El Khlifi + */ + +#ifndef __ARM_FFA_H +#define __ARM_FFA_H + +#include + +/* + * This header is public. It can be used by clients to access + * data structures and definitions they need + */ + +/* + * struct ffa_partition_info - Partition information descriptor + * @id: Partition ID + * @exec_ctxt: Execution context count + * @properties: Partition properties + * + * Data structure containing information about partitions instantiated in the system + * This structure is filled with the data queried by FFA_PARTITION_INFO_GET + */ +struct ffa_partition_info { + u16 id; + u16 exec_ctxt; +/* partition supports receipt of direct requests */ +#define FFA_PARTITION_DIRECT_RECV BIT(0) +/* partition can send direct requests. */ +#define FFA_PARTITION_DIRECT_SEND BIT(1) +/* partition can send and receive indirect messages. */ +#define FFA_PARTITION_INDIRECT_MSG BIT(2) + u32 properties; +}; + +/* + * struct ffa_partition_uuid - 16 bytes UUID transmitted by FFA_PARTITION_INFO_GET + * @a1-4: 32-bit words access to the UUID data + * + */ +struct ffa_partition_uuid { + u32 a1; /* w1 */ + u32 a2; /* w2 */ + u32 a3; /* w3 */ + u32 a4; /* w4 */ +}; + +/** + * struct ffa_partition_desc - the secure partition descriptor + * @info: partition information + * @sp_uuid: the secure partition UUID + * + * Each partition has its descriptor containing the partitions information and the UUID + */ +struct ffa_partition_desc { + struct ffa_partition_info info; + struct ffa_partition_uuid sp_uuid; +}; + +/* + * struct ffa_send_direct_data - Data structure hosting the data + * used by FFA_MSG_SEND_DIRECT_{REQ,RESP} + * @data0-4: Data read/written from/to x3-x7 registers + * + * Data structure containing the data to be sent by FFA_MSG_SEND_DIRECT_REQ + * or read from FFA_MSG_SEND_DIRECT_RESP + */ + +/* For use with FFA_MSG_SEND_DIRECT_{REQ,RESP} which pass data via registers */ +struct ffa_send_direct_data { + ulong data0; /* w3/x3 */ + ulong data1; /* w4/x4 */ + ulong data2; /* w5/x5 */ + ulong data3; /* w6/x6 */ + ulong data4; /* w7/x7 */ +}; + +struct udevice; + +/** + * 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 + * + * The data structure providing all the operations supported by the driver. + * This structure is EFI runtime resident. + */ +struct ffa_bus_ops { + int (*partition_info_get)(struct udevice *dev, const char *uuid_str, + u32 *sp_count, struct ffa_partition_desc **sp_descs); + int (*sync_send_receive)(struct udevice *dev, u16 dst_part_id, + struct ffa_send_direct_data *msg, + bool is_smc64); + int (*rxtx_unmap)(struct udevice *dev); +}; + +#define ffa_get_ops(dev) ((struct ffa_bus_ops *)(dev)->driver->ops) + +/** + * ffa_rxtx_unmap() - FFA_RXTX_UNMAP driver operation + * Please see ffa_unmap_rxtx_buffers_hdlr() description for more details. + */ +int ffa_rxtx_unmap(struct udevice *dev); + +/** + * ffa_unmap_rxtx_buffers_hdlr() - FFA_RXTX_UNMAP handler function + * @dev: The arm_ffa bus device + * + * This function implements FFA_RXTX_UNMAP FF-A function + * to unmap the RX/TX buffers + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int ffa_unmap_rxtx_buffers_hdlr(struct udevice *dev); + +/** + * ffa_sync_send_receive() - FFA_MSG_SEND_DIRECT_{REQ,RESP} driver operation + * Please see ffa_msg_send_direct_req_hdlr() description for more details. + */ +int ffa_sync_send_receive(struct udevice *dev, u16 dst_part_id, + struct ffa_send_direct_data *msg, bool is_smc64); + +/** + * ffa_msg_send_direct_req_hdlr() - FFA_MSG_SEND_DIRECT_{REQ,RESP} handler function + * @dev: The arm_ffa bus device + * @dst_part_id: destination partition ID + * @msg: pointer to the message data preallocated by the client (in/out) + * @is_smc64: select 64-bit or 32-bit FF-A ABI + * + * This function implements FFA_MSG_SEND_DIRECT_{REQ,RESP} + * FF-A functions. + * + * FFA_MSG_SEND_DIRECT_REQ is used to send the data to the secure partition. + * The response from the secure partition is handled by reading the + * FFA_MSG_SEND_DIRECT_RESP arguments. + * + * The maximum size of the data that can be exchanged is 40 bytes which is + * sizeof(struct ffa_send_direct_data) as defined by the FF-A specification 1.0 + * in the section relevant to FFA_MSG_SEND_DIRECT_{REQ,RESP} + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int ffa_msg_send_direct_req_hdlr(struct udevice *dev, u16 dst_part_id, + struct ffa_send_direct_data *msg, bool is_smc64); + +/** + * ffa_partition_info_get() - FFA_PARTITION_INFO_GET driver operation + * Please see ffa_get_partitions_info_hdlr() description for more details. + */ +int ffa_partition_info_get(struct udevice *dev, const char *uuid_str, + u32 *sp_count, struct ffa_partition_desc **sp_descs); + +/** + * ffa_get_partitions_info_hdlr() - FFA_PARTITION_INFO_GET handler function + * @uuid_str: pointer to the UUID string + * @sp_count: address of the variable containing the number of partitions matching the UUID + * The variable is set by the driver + * @sp_descs: address of the descriptors of the partitions matching the UUID + * The address is set by the driver + * + * Return the number of partitions and their descriptors matching the UUID + * + * Query the secure partition data from uc_priv. + * If not found, invoke FFA_PARTITION_INFO_GET + * FF-A function to query the partition information from secure world. + * + * A client of the FF-A driver should know the UUID of the service it wants to + * access. It should use the UUID to request the FF-A driver to provide the + * partition(s) information of the service. The FF-A driver uses + * PARTITION_INFO_GET to obtain this information. This is implemented through + * ffa_get_partitions_info_hdlr() function. + * A new FFA_PARTITION_INFO_GET call is issued (first one performed through + * ffa_cache_partitions_info) allowing to retrieve the partition(s) information. + * They are not saved (already done). We only update the UUID in the cached area. + * This assumes that partitions data does not change in the secure world. + * Otherwise u-boot will have an outdated partition data. The benefit of caching + * the information in the FF-A driver is to accommodate discovery after + * ExitBootServices(). + * + * Return: + * + * @sp_count: the number of partitions + * @sp_descs: address of the partitions descriptors + * + * On success 0 is returned. Otherwise, failure + */ +int ffa_get_partitions_info_hdlr(struct udevice *dev, const char *uuid_str, + u32 *sp_count, struct ffa_partition_desc **sp_descs); + +struct ffa_priv; + +/** + * ffa_set_smc_conduit() - Set the SMC conduit + * @dev: The FF-A bus device + * + * Selects the SMC conduit by setting the FF-A ABI invoke function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int ffa_set_smc_conduit(struct udevice *dev); + +#endif diff --git a/include/arm_ffa_priv.h b/include/arm_ffa_priv.h new file mode 100644 index 00000000000..d564c33c647 --- /dev/null +++ b/include/arm_ffa_priv.h @@ -0,0 +1,246 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2022-2023 Arm Limited and/or its affiliates + * + * Authors: + * Abdellatif El Khlifi + */ + +#ifndef __ARM_FFA_PRV_H +#define __ARM_FFA_PRV_H + +#include +#include +#include + +/* This header is exclusively used by the FF-A Uclass and FF-A driver(s) */ + +/* Arm FF-A driver name */ +#define FFA_DRV_NAME "arm_ffa" + +/* The FF-A SMC function definitions */ + +#if CONFIG_IS_ENABLED(SANDBOX) + +/* Providing Arm SMCCC declarations to sandbox */ + +/** + * struct sandbox_smccc_1_2_regs - emulated SMC call arguments or results + * @a0-a17 argument values from registers 0 to 17 + */ +struct sandbox_smccc_1_2_regs { + ulong a0; + ulong a1; + ulong a2; + ulong a3; + ulong a4; + ulong a5; + ulong a6; + ulong a7; + ulong a8; + ulong a9; + ulong a10; + ulong a11; + ulong a12; + ulong a13; + ulong a14; + ulong a15; + ulong a16; + ulong a17; +}; + +typedef struct sandbox_smccc_1_2_regs ffa_value_t; + +#define ARM_SMCCC_FAST_CALL 1UL +#define ARM_SMCCC_OWNER_STANDARD 4 +#define ARM_SMCCC_SMC_32 0 +#define ARM_SMCCC_SMC_64 1 +#define ARM_SMCCC_TYPE_SHIFT 31 +#define ARM_SMCCC_CALL_CONV_SHIFT 30 +#define ARM_SMCCC_OWNER_MASK 0x3f +#define ARM_SMCCC_OWNER_SHIFT 24 +#define ARM_SMCCC_FUNC_MASK 0xffff + +#define ARM_SMCCC_CALL_VAL(type, calling_convention, owner, func_num) \ + (((type) << ARM_SMCCC_TYPE_SHIFT) | \ + ((calling_convention) << ARM_SMCCC_CALL_CONV_SHIFT) | \ + (((owner) & ARM_SMCCC_OWNER_MASK) << ARM_SMCCC_OWNER_SHIFT) | \ + ((func_num) & ARM_SMCCC_FUNC_MASK)) + +#else +/* CONFIG_ARM64 */ +#include +typedef struct arm_smccc_1_2_regs ffa_value_t; +#endif + +/* Defining the function pointer type for the function executing the FF-A ABIs */ +typedef void (*invoke_ffa_fn_t)(ffa_value_t args, ffa_value_t *res); + +/* FF-A driver version definitions */ + +#define MAJOR_VERSION_MASK GENMASK(30, 16) +#define MINOR_VERSION_MASK GENMASK(15, 0) +#define GET_FFA_MAJOR_VERSION(x) \ + ((u16)(FIELD_GET(MAJOR_VERSION_MASK, (x)))) +#define GET_FFA_MINOR_VERSION(x) \ + ((u16)(FIELD_GET(MINOR_VERSION_MASK, (x)))) +#define PACK_VERSION_INFO(major, minor) \ + (FIELD_PREP(MAJOR_VERSION_MASK, (major)) | \ + FIELD_PREP(MINOR_VERSION_MASK, (minor))) + +#define FFA_MAJOR_VERSION (1) +#define FFA_MINOR_VERSION (0) +#define FFA_VERSION_1_0 \ + PACK_VERSION_INFO(FFA_MAJOR_VERSION, FFA_MINOR_VERSION) + +/* Endpoint ID mask (u-boot endpoint ID) */ + +#define GET_SELF_ENDPOINT_ID_MASK GENMASK(15, 0) +#define GET_SELF_ENDPOINT_ID(x) \ + ((u16)(FIELD_GET(GET_SELF_ENDPOINT_ID_MASK, (x)))) + +#define PREP_SELF_ENDPOINT_ID_MASK GENMASK(31, 16) +#define PREP_SELF_ENDPOINT_ID(x) \ + (FIELD_PREP(PREP_SELF_ENDPOINT_ID_MASK, (x))) + +/* Partition endpoint ID mask (partition with which u-boot communicates with) */ + +#define PREP_PART_ENDPOINT_ID_MASK GENMASK(15, 0) +#define PREP_PART_ENDPOINT_ID(x) \ + (FIELD_PREP(PREP_PART_ENDPOINT_ID_MASK, (x))) + +/* Definitions of the Arm FF-A interfaces supported by the Arm FF-A driver */ + +#define FFA_SMC(calling_convention, func_num) \ + ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, (calling_convention), \ + ARM_SMCCC_OWNER_STANDARD, (func_num)) + +#define FFA_SMC_32(func_num) FFA_SMC(ARM_SMCCC_SMC_32, (func_num)) +#define FFA_SMC_64(func_num) FFA_SMC(ARM_SMCCC_SMC_64, (func_num)) + +enum ffa_abis { + FFA_ERROR = 0x60, + FFA_SUCCESS = 0x61, + FFA_INTERRUPT = 0x62, + FFA_VERSION = 0x63, + FFA_FEATURES = 0x64, + FFA_RX_RELEASE = 0x65, + FFA_RXTX_MAP = 0x66, + FFA_RXTX_UNMAP = 0x67, + FFA_PARTITION_INFO_GET = 0x68, + FFA_ID_GET = 0x69, + FFA_RUN = 0x6d, + FFA_MSG_SEND_DIRECT_REQ = 0x6f, + FFA_MSG_SEND_DIRECT_RESP = 0x70, + + /* 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 */ +}; + +enum ffa_abi_errcode { + NOT_SUPPORTED = 1, + INVALID_PARAMETERS, + NO_MEMORY, + BUSY, + INTERRUPTED, + DENIED, + RETRY, + ABORTED, + MAX_NUMBER_FFA_ERR +}; + +extern int ffa_to_std_errmap[MAX_NUMBER_FFA_ERR]; + +/* Container structure and helper macros to map between an FF-A error and relevant error log */ +struct ffa_abi_errmap { + char *err_str[MAX_NUMBER_FFA_ERR]; +}; + +#define FFA_ERRMAP_COUNT (FFA_LAST_ID - FFA_FIRST_ID + 1) +#define FFA_ID_TO_ERRMAP_ID(ffa_id) ((ffa_id) - FFA_FIRST_ID) + +/** + * enum ffa_rxtx_buf_sizes - minimum sizes supported + * for the RX/TX buffers + */ +enum ffa_rxtx_buf_sizes { + RXTX_4K, + RXTX_64K, + RXTX_16K +}; + +/** + * struct ffa_rxtxpair - Hosts the RX/TX buffers virtual addresses + * @rxbuf: virtual address of the RX buffer + * @txbuf: virtual address of the TX buffer + * @rxtx_min_pages: RX/TX buffers minimum size in pages + * + * Hosts the virtual addresses of the mapped RX/TX buffers + * These addresses are used by the FF-A functions that use the RX/TX buffers + */ +struct ffa_rxtxpair { + void *rxbuf; /* Virtual address returned by memalign */ + void *txbuf; /* Virtual address returned by memalign */ + size_t rxtx_min_pages; /* Minimum number of pages in each of the RX/TX buffers */ +}; + +struct ffa_partition_desc; + +/** + * struct ffa_partitions - descriptors for all secure partitions + * @count: The number of partitions descriptors + * @descs The partitions descriptors table + * + * Contains the partitions descriptors table + */ +struct ffa_partitions { + u32 count; + struct ffa_partition_desc *descs; /* Virtual address */ +}; + +/** + * struct ffa_priv - the driver private data structure + * + * @fwk_version: FF-A framework version + * @emul: FF-A sandbox emulator + * @id: u-boot endpoint ID + * @partitions: The partitions descriptors structure + * @pair: The RX/TX buffers pair + * + * The device private data structure containing all the + * data read from secure world. + */ +struct ffa_priv { + u32 fwk_version; + struct udevice *emul; + u16 id; + struct ffa_partitions partitions; + struct ffa_rxtxpair pair; +}; + +/** + * ffa_get_version_hdlr() - FFA_VERSION handler function + * @dev: The FF-A bus device + * + * Implement FFA_VERSION FF-A function + * to get from the secure world the FF-A framework version + * FFA_VERSION is used to discover the FF-A framework. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int ffa_get_version_hdlr(struct udevice *dev); + +/** + * invoke_ffa_fn() - SMC wrapper + * @args: FF-A ABI arguments to be copied to Xn registers + * @res: FF-A ABI return data to be copied from Xn registers + * + * Calls low level SMC implementation. + * This function should be implemented by the user driver. + */ +void invoke_ffa_fn(ffa_value_t args, ffa_value_t *res); + +#endif diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 307ad6931ca..3c6af2e3d2f 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -4,6 +4,11 @@ * * (C) Copyright 2012 * Pavel Herrmann + * + * Copyright 2022-2023 Arm Limited and/or its affiliates + * + * Authors: + * Abdellatif El Khlifi */ #ifndef _DM_UCLASS_ID_H @@ -57,6 +62,7 @@ enum uclass_id { UCLASS_ETH, /* Ethernet device */ UCLASS_ETH_PHY, /* Ethernet PHY device */ UCLASS_EXTCON, /* External Connector Class */ + UCLASS_FFA, /* Arm Firmware Framework for Armv8-A */ UCLASS_FIRMWARE, /* Firmware */ UCLASS_FPGA, /* FPGA device */ UCLASS_FUZZING_ENGINE, /* Fuzzing engine */ From a09852d862bc203b80368ca671ff36e80bcb510f Mon Sep 17 00:00:00 2001 From: Abdellatif El Khlifi Date: Fri, 4 Aug 2023 14:33:41 +0100 Subject: [PATCH 5/9] arm_ffa: introduce sandbox FF-A support Emulate Secure World's FF-A ABIs and allow testing U-Boot FF-A support Features of the sandbox FF-A support: - Introduce an FF-A emulator - Introduce an FF-A device driver for FF-A comms with emulated Secure World - Provides test methods allowing to read the status of the inspected ABIs The sandbox FF-A emulator supports only 64-bit direct messaging. Signed-off-by: Abdellatif El Khlifi Reviewed-by: Simon Glass Cc: Tom Rini Cc: Ilias Apalodimas Cc: Jens Wiklander Cc: Heinrich Schuchardt --- MAINTAINERS | 3 +- arch/sandbox/dts/sandbox.dtsi | 9 + arch/sandbox/dts/test.dts | 8 + arch/sandbox/include/asm/sandbox_arm_ffa.h | 72 ++ .../include/asm/sandbox_arm_ffa_priv.h | 121 +++ configs/sandbox64_defconfig | 1 + configs/sandbox_defconfig | 1 + doc/arch/arm64.ffa.rst | 19 +- doc/arch/sandbox/sandbox.rst | 1 + drivers/firmware/arm-ffa/Kconfig | 13 +- drivers/firmware/arm-ffa/Makefile | 10 +- drivers/firmware/arm-ffa/ffa-emul-uclass.c | 720 ++++++++++++++++++ .../firmware/arm-ffa/sandbox_arm_ffa_priv.h | 14 - drivers/firmware/arm-ffa/sandbox_ffa.c | 110 +++ include/dm/uclass-id.h | 1 + 15 files changed, 1081 insertions(+), 22 deletions(-) create mode 100644 arch/sandbox/include/asm/sandbox_arm_ffa.h create mode 100644 arch/sandbox/include/asm/sandbox_arm_ffa_priv.h create mode 100644 drivers/firmware/arm-ffa/ffa-emul-uclass.c delete mode 100644 drivers/firmware/arm-ffa/sandbox_arm_ffa_priv.h create mode 100644 drivers/firmware/arm-ffa/sandbox_ffa.c diff --git a/MAINTAINERS b/MAINTAINERS index fcca162dee2..9d59ac09516 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -269,10 +269,11 @@ F: configs/cortina_presidio-asic-pnand_defconfig ARM FF-A M: Abdellatif El Khlifi S: Maintained +F: arch/sandbox/include/asm/sandbox_arm_ffa.h +F: arch/sandbox/include/asm/sandbox_arm_ffa_priv.h F: doc/arch/arm64.ffa.rst F: drivers/firmware/arm-ffa/ F: include/arm_ffa.h -F: include/sandbox_arm_ffa.h ARM FREESCALE IMX M: Stefano Babic diff --git a/arch/sandbox/dts/sandbox.dtsi b/arch/sandbox/dts/sandbox.dtsi index f0ee0b3481a..8aaf911a41a 100644 --- a/arch/sandbox/dts/sandbox.dtsi +++ b/arch/sandbox/dts/sandbox.dtsi @@ -451,6 +451,15 @@ thermal { compatible = "sandbox,thermal"; }; + + arm-ffa-emul { + compatible = "sandbox,arm-ffa-emul"; + + sandbox-arm-ffa { + compatible = "sandbox,arm-ffa"; + }; + }; + }; &cros_ec { diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index b5509eee8cf..f351d5cb84b 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -1831,6 +1831,14 @@ extcon { compatible = "sandbox,extcon"; }; + + arm-ffa-emul { + compatible = "sandbox,arm-ffa-emul"; + + sandbox-arm-ffa { + compatible = "sandbox,arm-ffa"; + }; + }; }; #include "sandbox_pmic.dtsi" diff --git a/arch/sandbox/include/asm/sandbox_arm_ffa.h b/arch/sandbox/include/asm/sandbox_arm_ffa.h new file mode 100644 index 00000000000..be2790f4960 --- /dev/null +++ b/arch/sandbox/include/asm/sandbox_arm_ffa.h @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2022-2023 Arm Limited and/or its affiliates + * + * Authors: + * Abdellatif El Khlifi + */ + +#ifndef __SANDBOX_ARM_FFA_H +#define __SANDBOX_ARM_FFA_H + +#include + +/* + * This header provides public sandbox FF-A emulator declarations + * and declarations needed by FF-A sandbox clients + */ + +/* UUIDs strings of the emulated services */ +#define SANDBOX_SERVICE1_UUID "ed32d533-4209-99e6-2d72-cdd998a79cc0" +#define SANDBOX_SERVICE2_UUID "ed32d544-4209-99e6-2d72-cdd998a79cc0" + +/* IDs of the emulated secure partitions (SPs) */ +#define SANDBOX_SP1_ID 0x1245 +#define SANDBOX_SP2_ID 0x9836 +#define SANDBOX_SP3_ID 0x6452 +#define SANDBOX_SP4_ID 0x7814 + +/* Invalid service UUID (no matching SP) */ +#define SANDBOX_SERVICE3_UUID "55d532ed-0942-e699-722d-c09ca798d9cd" + +/* Invalid service UUID (invalid UUID string format) */ +#define SANDBOX_SERVICE4_UUID "32ed-0942-e699-722d-c09ca798d9cd" + +/* Number of valid services */ +#define SANDBOX_SP_COUNT_PER_VALID_SERVICE 2 + +/** + * struct ffa_sandbox_data - query ABI state data structure + * @data0_size: size of the first argument + * @data0: pointer to the first argument + * @data1_size>: size of the second argument + * @data1: pointer to the second argument + * + * Used to pass various types of data with different sizes between + * the test cases and the sandbox emulator. + * The data is for querying FF-A ABIs state. + */ +struct ffa_sandbox_data { + u32 data0_size; /* size of the first argument */ + void *data0; /* pointer to the first argument */ + u32 data1_size; /* size of the second argument */ + void *data1; /* pointer to the second argument */ +}; + +/* The sandbox FF-A emulator public functions */ + +/** + * sandbox_query_ffa_emul_state() - Inspect the FF-A ABIs + * @queried_func_id: The FF-A function to be queried + * @func_data: Pointer to the FF-A function arguments container structure + * + * Query the status of FF-A ABI specified in the input argument. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int sandbox_query_ffa_emul_state(u32 queried_func_id, + struct ffa_sandbox_data *func_data); + +#endif diff --git a/arch/sandbox/include/asm/sandbox_arm_ffa_priv.h b/arch/sandbox/include/asm/sandbox_arm_ffa_priv.h new file mode 100644 index 00000000000..b0881822d78 --- /dev/null +++ b/arch/sandbox/include/asm/sandbox_arm_ffa_priv.h @@ -0,0 +1,121 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2022-2023 Arm Limited and/or its affiliates + * + * Authors: + * Abdellatif El Khlifi + */ + +#ifndef __SANDBOX_ARM_FFA_PRV_H +#define __SANDBOX_ARM_FFA_PRV_H + +#include + +/* This header is exclusively used by the Sandbox FF-A driver and emulator */ + +/* Non-secure physical FF-A instance */ +#define NS_PHYS_ENDPOINT_ID (0) + +#define GET_NS_PHYS_ENDPOINT_ID_MASK GENMASK(31, 16) +#define GET_NS_PHYS_ENDPOINT_ID(x) \ + ((u16)(FIELD_GET(GET_NS_PHYS_ENDPOINT_ID_MASK, (x)))) + +/* Helper macro for reading the destination partition ID */ +#define GET_DST_SP_ID_MASK GENMASK(15, 0) +#define GET_DST_SP_ID(x) \ + ((u16)(FIELD_GET(GET_DST_SP_ID_MASK, (x)))) + +/* Helper macro for setting the source partition ID */ +#define PREP_SRC_SP_ID_MASK GENMASK(31, 16) +#define PREP_SRC_SP_ID(x) \ + (FIELD_PREP(PREP_SRC_SP_ID_MASK, (x))) + +/* Helper macro for setting the destination endpoint ID */ +#define PREP_NS_PHYS_ENDPOINT_ID_MASK GENMASK(15, 0) +#define PREP_NS_PHYS_ENDPOINT_ID(x) \ + (FIELD_PREP(PREP_NS_PHYS_ENDPOINT_ID_MASK, (x))) + +/* RX/TX buffers minimum size */ +#define RXTX_BUFFERS_MIN_SIZE (RXTX_4K) +#define RXTX_BUFFERS_MIN_PAGES (1) + +/* MBZ registers info */ + +/* x1-x7 MBZ */ +#define FFA_X1X7_MBZ_CNT (7) +#define FFA_X1X7_MBZ_REG_START (&res->a1) + +/* x4-x7 MBZ */ +#define FFA_X4X7_MBZ_CNT (4) +#define FFA_X4X7_MBZ_REG_START (&res->a4) + +/* x3-x7 MBZ */ +#define FFA_X3X7_MBZ_CNT (5) +#define FFA_X3_MBZ_REG_START (&res->a3) + +/* number of emulated FF-A secure partitions (SPs) */ +#define SANDBOX_PARTITIONS_CNT (4) + +/* Binary data of the emulated services UUIDs */ + +/* service 1 UUID binary data (little-endian format) */ +#define SANDBOX_SERVICE1_UUID_A1 0xed32d533 +#define SANDBOX_SERVICE1_UUID_A2 0x99e64209 +#define SANDBOX_SERVICE1_UUID_A3 0x9cc02d72 +#define SANDBOX_SERVICE1_UUID_A4 0xcdd998a7 + +/* service 2 UUID binary data (little-endian format) */ +#define SANDBOX_SERVICE2_UUID_A1 0xed32d544 +#define SANDBOX_SERVICE2_UUID_A2 0x99e64209 +#define SANDBOX_SERVICE2_UUID_A3 0x9cc02d72 +#define SANDBOX_SERVICE2_UUID_A4 0xcdd998a7 + +/** + * struct ffa_rxtxpair_info - structure hosting the RX/TX buffers flags + * @rxbuf_owned: RX buffer ownership flag (the owner is non secure world) + * @rxbuf_mapped: RX buffer mapping flag + * @txbuf_owned TX buffer ownership flag + * @txbuf_mapped: TX buffer mapping flag + * @rxtx_buf_size: RX/TX buffers size + * + * Hosts the ownership/mapping flags of the RX/TX buffers + * When a buffer is owned/mapped its corresponding flag is set to 1 otherwise 0. + */ +struct ffa_rxtxpair_info { + u8 rxbuf_owned; + u8 rxbuf_mapped; + u8 txbuf_owned; + u8 txbuf_mapped; + u32 rxtx_buf_size; +}; + +/** + * struct sandbox_ffa_emul - emulator data + * + * @fwk_version: FF-A framework version + * @id: u-boot endpoint ID + * @partitions: The partitions descriptors structure + * @pair: The RX/TX buffers pair + * @pair_info: The RX/TX buffers pair flags and size + * @test_ffa_data: The data of the FF-A bus under test + * + * Hosts all the emulated secure world data. + */ +struct sandbox_ffa_emul { + u32 fwk_version; + u16 id; + struct ffa_partitions partitions; + struct ffa_rxtxpair pair; + struct ffa_rxtxpair_info pair_info; +}; + +/** + * ffa_emul_find() - Finds the FF-A emulator + * @dev: the sandbox FF-A device (sandbox-arm-ffa) + * @emulp: the FF-A emulator device (sandbox-ffa-emul) + * Return: + * 0 on success. Otherwise, failure + */ +int ffa_emul_find(struct udevice *dev, struct udevice **emulp); + +#endif diff --git a/configs/sandbox64_defconfig b/configs/sandbox64_defconfig index bf3ef0c4007..69e5efe8748 100644 --- a/configs/sandbox64_defconfig +++ b/configs/sandbox64_defconfig @@ -260,3 +260,4 @@ CONFIG_FWU_MULTI_BANK_UPDATE=y CONFIG_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y +CONFIG_ARM_FFA_TRANSPORT=y diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index ea2b2566aa1..1cd1c2ed7cd 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -345,3 +345,4 @@ CONFIG_TEST_FDTDEC=y CONFIG_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y +CONFIG_ARM_FFA_TRANSPORT=y diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst index df189487741..792898321a9 100644 --- a/doc/arch/arm64.ffa.rst +++ b/doc/arch/arm64.ffa.rst @@ -33,6 +33,10 @@ The U-Boot FF-A support provides the following parts: - A Uclass driver providing generic FF-A methods. - An Arm FF-A device driver providing Arm-specific methods and reusing the Uclass methods. +- A sandbox emulator for Arm FF-A, emulates the FF-A side of the Secure World and provides + FF-A ABIs inspection methods. +- An FF-A sandbox device driver for FF-A communication with the emulated Secure World. + The driver leverages the FF-A Uclass to establish FF-A communication. FF-A and SMC specifications ------------------------------------------- @@ -62,6 +66,7 @@ CONFIG_ARM_FFA_TRANSPORT Enables the FF-A support. Turn this on if you want to use FF-A communication. When using an Arm 64-bit platform, the Arm FF-A driver will be used. + When using sandbox, the sandbox FF-A emulator and FF-A sandbox driver will be used. FF-A ABIs under the hood --------------------------------------- @@ -98,10 +103,8 @@ architecture features including FF-A bus. Class Index Probed Driver Name ----------------------------------------------------------- - ... firmware 0 [ + ] psci |-- psci ffa 0 [ ] arm_ffa | `-- arm_ffa - ... The PSCI driver is bound to the PSCI device and when probed it tries to discover the architecture features by calling a callback the features drivers provide. @@ -203,6 +206,18 @@ The following features are provided: - FF-A bus can be compiled and used without EFI +Relationship between the sandbox emulator and the FF-A device +--------------------------------------------------------------- + +:: + + => dm tree + + Class Index Probed Driver Name + ----------------------------------------------------------- + ffa_emul 0 [ + ] sandbox_ffa_emul `-- arm-ffa-emul + ffa 0 [ ] sandbox_arm_ffa `-- sandbox-arm-ffa + Example of boot logs with FF-A enabled -------------------------------------- diff --git a/doc/arch/sandbox/sandbox.rst b/doc/arch/sandbox/sandbox.rst index 77ca6bc4cc7..a3631de7493 100644 --- a/doc/arch/sandbox/sandbox.rst +++ b/doc/arch/sandbox/sandbox.rst @@ -200,6 +200,7 @@ Supported Drivers U-Boot sandbox supports these emulations: +- Arm FF-A - Block devices - Chrome OS EC - GPIO diff --git a/drivers/firmware/arm-ffa/Kconfig b/drivers/firmware/arm-ffa/Kconfig index 9200c8028b8..fc668c109d6 100644 --- a/drivers/firmware/arm-ffa/Kconfig +++ b/drivers/firmware/arm-ffa/Kconfig @@ -2,9 +2,9 @@ config ARM_FFA_TRANSPORT bool "Enable Arm Firmware Framework for Armv8-A driver" - depends on DM && ARM64 - select ARM_SMCCC - select ARM_SMCCC_FEATURES + depends on DM && (ARM64 || SANDBOX) + select ARM_SMCCC if !SANDBOX + select ARM_SMCCC_FEATURES if !SANDBOX select LIB_UUID select DEVRES help @@ -32,5 +32,10 @@ config ARM_FFA_TRANSPORT Generic FF-A methods are implemented in the Uclass (arm-ffa-uclass.c). Arm specific methods are implemented in the Arm driver (arm-ffa.c). - For more details about the FF-A support, please refer to doc/arch/arm64.ffa.rst + FF-A sandbox is provided to run FF-A under sandbox and allows to test the FF-A Uclass. + Sandbox support includes an emulator for Arm FF-A which emulates the FF-A side of + the Secure World and provides FF-A ABIs inspection methods (ffa-emul-uclass.c). + An FF-A sandbox driver is also provided for FF-A communication with the emulated + Secure World (sandbox_ffa.c). + For more details about the FF-A support, please refer to doc/arch/arm64.ffa.rst diff --git a/drivers/firmware/arm-ffa/Makefile b/drivers/firmware/arm-ffa/Makefile index 11b17662853..318123a7f42 100644 --- a/drivers/firmware/arm-ffa/Makefile +++ b/drivers/firmware/arm-ffa/Makefile @@ -5,4 +5,12 @@ # Authors: # Abdellatif El Khlifi -obj-y += arm-ffa-uclass.o arm-ffa.o +# build the generic FF-A methods +obj-y += arm-ffa-uclass.o +ifeq ($(CONFIG_SANDBOX),y) +# build the FF-A sandbox emulator and driver +obj-y += ffa-emul-uclass.o sandbox_ffa.o +else +# build the Arm64 FF-A driver +obj-y += arm-ffa.o +endif diff --git a/drivers/firmware/arm-ffa/ffa-emul-uclass.c b/drivers/firmware/arm-ffa/ffa-emul-uclass.c new file mode 100644 index 00000000000..5562bbaac3a --- /dev/null +++ b/drivers/firmware/arm-ffa/ffa-emul-uclass.c @@ -0,0 +1,720 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2022-2023 Arm Limited and/or its affiliates + * + * Authors: + * Abdellatif El Khlifi + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +/* The partitions (SPs) table */ +static struct ffa_partition_desc sandbox_partitions[SANDBOX_PARTITIONS_CNT] = { + { + .info = { .id = SANDBOX_SP1_ID, .exec_ctxt = 0x5687, .properties = 0x89325621 }, + .sp_uuid = { + .a1 = SANDBOX_SERVICE1_UUID_A1, + .a2 = SANDBOX_SERVICE1_UUID_A2, + .a3 = SANDBOX_SERVICE1_UUID_A3, + .a4 = SANDBOX_SERVICE1_UUID_A4, + } + }, + { + .info = { .id = SANDBOX_SP3_ID, .exec_ctxt = 0x7687, .properties = 0x23325621 }, + .sp_uuid = { + .a1 = SANDBOX_SERVICE2_UUID_A1, + .a2 = SANDBOX_SERVICE2_UUID_A2, + .a3 = SANDBOX_SERVICE2_UUID_A3, + .a4 = SANDBOX_SERVICE2_UUID_A4, + } + }, + { + .info = { .id = SANDBOX_SP2_ID, .exec_ctxt = 0x9587, .properties = 0x45325621 }, + .sp_uuid = { + .a1 = SANDBOX_SERVICE1_UUID_A1, + .a2 = SANDBOX_SERVICE1_UUID_A2, + .a3 = SANDBOX_SERVICE1_UUID_A3, + .a4 = SANDBOX_SERVICE1_UUID_A4, + } + }, + { + .info = { .id = SANDBOX_SP4_ID, .exec_ctxt = 0x1487, .properties = 0x70325621 }, + .sp_uuid = { + .a1 = SANDBOX_SERVICE2_UUID_A1, + .a2 = SANDBOX_SERVICE2_UUID_A2, + .a3 = SANDBOX_SERVICE2_UUID_A3, + .a4 = SANDBOX_SERVICE2_UUID_A4, + } + } + +}; + +/* The emulator functions */ + +/** + * sandbox_ffa_version() - Emulated FFA_VERSION handler function + * @emul: The sandbox FF-A emulator device + * @pargs: The SMC call input arguments a0-a7 + * @res: The SMC return data + * + * Emulate FFA_VERSION FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ + +static int sandbox_ffa_version(struct udevice *emul, ffa_value_t *pargs, ffa_value_t *res) +{ + struct sandbox_ffa_emul *priv = dev_get_priv(emul); + + priv->fwk_version = FFA_VERSION_1_0; + res->a0 = priv->fwk_version; + + /* x1-x7 MBZ */ + memset(FFA_X1X7_MBZ_REG_START, 0, FFA_X1X7_MBZ_CNT * sizeof(ulong)); + + return 0; +} + +/** + * sandbox_ffa_id_get() - Emulated FFA_ID_GET handler function + * @emul: The sandbox FF-A emulator device + * @pargs: The SMC call input arguments a0-a7 + * @res: The SMC return data + * + * Emulate FFA_ID_GET FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int sandbox_ffa_id_get(struct udevice *emul, ffa_value_t *pargs, ffa_value_t *res) +{ + struct sandbox_ffa_emul *priv = dev_get_priv(emul); + + res->a0 = FFA_SMC_32(FFA_SUCCESS); + res->a1 = 0; + + priv->id = NS_PHYS_ENDPOINT_ID; + res->a2 = priv->id; + + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, 0, FFA_X3X7_MBZ_CNT * sizeof(ulong)); + + return 0; +} + +/** + * sandbox_ffa_features() - Emulated FFA_FEATURES handler function + * @pargs: The SMC call input arguments a0-a7 + * @res: The SMC return data + * + * Emulate FFA_FEATURES FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int sandbox_ffa_features(ffa_value_t *pargs, ffa_value_t *res) +{ + res->a1 = 0; + + if (pargs->a1 == FFA_SMC_64(FFA_RXTX_MAP)) { + res->a0 = FFA_SMC_32(FFA_SUCCESS); + res->a2 = RXTX_BUFFERS_MIN_SIZE; + res->a3 = 0; + /* x4-x7 MBZ */ + memset(FFA_X4X7_MBZ_REG_START, 0, FFA_X4X7_MBZ_CNT * sizeof(ulong)); + return 0; + } + + res->a0 = FFA_SMC_32(FFA_ERROR); + res->a2 = -NOT_SUPPORTED; + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, 0, FFA_X3X7_MBZ_CNT * sizeof(ulong)); + log_err("FF-A interface %lx not implemented\n", pargs->a1); + + return ffa_to_std_errmap[NOT_SUPPORTED]; +} + +/** + * sandbox_ffa_partition_info_get() - Emulated FFA_PARTITION_INFO_GET handler + * @emul: The sandbox FF-A emulator device + * @pargs: The SMC call input arguments a0-a7 + * @res: The SMC return data + * + * Emulate FFA_PARTITION_INFO_GET FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int sandbox_ffa_partition_info_get(struct udevice *emul, ffa_value_t *pargs, + ffa_value_t *res) +{ + struct ffa_partition_info *rxbuf_desc_info = NULL; + u32 descs_cnt; + u32 descs_size_bytes; + int ret; + struct sandbox_ffa_emul *priv = dev_get_priv(emul); + + res->a0 = FFA_SMC_32(FFA_ERROR); + + if (!priv->pair.rxbuf) { + res->a2 = -DENIED; + ret = ffa_to_std_errmap[DENIED]; + goto cleanup; + } + + if (priv->pair_info.rxbuf_owned) { + res->a2 = -BUSY; + ret = ffa_to_std_errmap[BUSY]; + goto cleanup; + } + + if (!priv->partitions.descs) { + priv->partitions.descs = sandbox_partitions; + priv->partitions.count = SANDBOX_PARTITIONS_CNT; + } + + descs_size_bytes = SANDBOX_PARTITIONS_CNT * + sizeof(struct ffa_partition_desc); + + /* Abort if the RX buffer size is smaller than the descs buffer size */ + if ((priv->pair_info.rxtx_buf_size * SZ_4K) < descs_size_bytes) { + res->a2 = -NO_MEMORY; + ret = ffa_to_std_errmap[NO_MEMORY]; + goto cleanup; + } + + rxbuf_desc_info = priv->pair.rxbuf; + + /* No UUID specified. Return the information of all partitions */ + if (!pargs->a1 && !pargs->a2 && !pargs->a3 && !pargs->a4) { + for (descs_cnt = 0; descs_cnt < SANDBOX_PARTITIONS_CNT; descs_cnt++) + *(rxbuf_desc_info++) = priv->partitions.descs[descs_cnt].info; + + res->a0 = FFA_SMC_32(FFA_SUCCESS); + res->a2 = SANDBOX_PARTITIONS_CNT; + /* Transfer ownership to the consumer: the non secure world */ + priv->pair_info.rxbuf_owned = 1; + ret = 0; + + goto cleanup; + } + + /* A UUID specified. Return the info of all SPs matching the UUID */ + + for (descs_cnt = 0 ; descs_cnt < SANDBOX_PARTITIONS_CNT ; descs_cnt++) + if (pargs->a1 == priv->partitions.descs[descs_cnt].sp_uuid.a1 && + pargs->a2 == priv->partitions.descs[descs_cnt].sp_uuid.a2 && + pargs->a3 == priv->partitions.descs[descs_cnt].sp_uuid.a3 && + pargs->a4 == priv->partitions.descs[descs_cnt].sp_uuid.a4) { + *(rxbuf_desc_info++) = priv->partitions.descs[descs_cnt].info; + } + + if (rxbuf_desc_info != priv->pair.rxbuf) { + res->a0 = FFA_SMC_32(FFA_SUCCESS); + /* Store the partitions count */ + res->a2 = (ulong) + (rxbuf_desc_info - (struct ffa_partition_info *) + priv->pair.rxbuf); + ret = 0; + + /* Transfer ownership to the consumer: the non secure world */ + priv->pair_info.rxbuf_owned = 1; + } else { + /* Unrecognized UUID */ + res->a2 = -INVALID_PARAMETERS; + ret = ffa_to_std_errmap[INVALID_PARAMETERS]; + } + +cleanup: + + log_err("FFA_PARTITION_INFO_GET (%ld)\n", res->a2); + + res->a1 = 0; + + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, 0, FFA_X3X7_MBZ_CNT * sizeof(ulong)); + + return ret; +} + +/** + * sandbox_ffa_rxtx_map() - Emulated FFA_RXTX_MAP handler + * @emul: The sandbox FF-A emulator device + * @pargs: The SMC call input arguments a0-a7 + * @res: The SMC return data + * + * Emulate FFA_RXTX_MAP FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int sandbox_ffa_rxtx_map(struct udevice *emul, ffa_value_t *pargs, ffa_value_t *res) +{ + int ret; + struct sandbox_ffa_emul *priv = dev_get_priv(emul); + + res->a0 = FFA_SMC_32(FFA_ERROR); + + if (priv->pair.txbuf && priv->pair.rxbuf) { + res->a2 = -DENIED; + ret = ffa_to_std_errmap[DENIED]; + goto feedback; + } + + if (pargs->a3 >= RXTX_BUFFERS_MIN_PAGES && pargs->a1 && pargs->a2) { + priv->pair.txbuf = map_sysmem(pargs->a1, 0); + priv->pair.rxbuf = map_sysmem(pargs->a2, 0); + priv->pair_info.rxtx_buf_size = pargs->a3; + priv->pair_info.rxbuf_mapped = 1; + res->a0 = FFA_SMC_32(FFA_SUCCESS); + res->a2 = 0; + ret = 0; + goto feedback; + } + + if (!pargs->a1 || !pargs->a2) { + res->a2 = -INVALID_PARAMETERS; + ret = ffa_to_std_errmap[INVALID_PARAMETERS]; + } else { + res->a2 = -NO_MEMORY; + ret = ffa_to_std_errmap[NO_MEMORY]; + } + + log_err("Error in FFA_RXTX_MAP arguments (%d)\n", + (int)res->a2); + +feedback: + + res->a1 = 0; + + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, 0, FFA_X3X7_MBZ_CNT * sizeof(ulong)); + + return ret; +} + +/** + * sandbox_ffa_rxtx_unmap() - Emulated FFA_RXTX_UNMAP handler + * @emul: The sandbox FF-A emulator device + * @pargs: The SMC call input arguments a0-a7 + * @res: The SMC return data + * + * Emulate FFA_RXTX_UNMAP FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int sandbox_ffa_rxtx_unmap(struct udevice *emul, ffa_value_t *pargs, ffa_value_t *res) +{ + int ret; + struct sandbox_ffa_emul *priv = dev_get_priv(emul); + + res->a0 = FFA_SMC_32(FFA_ERROR); + res->a2 = -INVALID_PARAMETERS; + ret = ffa_to_std_errmap[INVALID_PARAMETERS]; + + if (GET_NS_PHYS_ENDPOINT_ID(pargs->a1) != priv->id) + goto feedback; + + if (priv->pair.txbuf && priv->pair.rxbuf) { + priv->pair.txbuf = 0; + priv->pair.rxbuf = 0; + priv->pair_info.rxtx_buf_size = 0; + priv->pair_info.rxbuf_mapped = 0; + res->a0 = FFA_SMC_32(FFA_SUCCESS); + res->a2 = 0; + ret = 0; + goto feedback; + } + + log_err("No buffer pair registered on behalf of the caller\n"); + +feedback: + + res->a1 = 0; + + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, 0, FFA_X3X7_MBZ_CNT * sizeof(ulong)); + + return ret; +} + +/** + * sandbox_ffa_rx_release() - Emulated FFA_RX_RELEASE handler + * @emul: The sandbox FF-A emulator device + * @pargs: The SMC call input arguments a0-a7 + * @res: The SMC return data + * + * Emulate FFA_RX_RELEASE FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int sandbox_ffa_rx_release(struct udevice *emul, ffa_value_t *pargs, ffa_value_t *res) +{ + int ret; + struct sandbox_ffa_emul *priv = dev_get_priv(emul); + + if (!priv->pair_info.rxbuf_owned) { + res->a0 = FFA_SMC_32(FFA_ERROR); + res->a2 = -DENIED; + ret = ffa_to_std_errmap[DENIED]; + } else { + priv->pair_info.rxbuf_owned = 0; + res->a0 = FFA_SMC_32(FFA_SUCCESS); + res->a2 = 0; + ret = 0; + } + + res->a1 = 0; + + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, 0, FFA_X3X7_MBZ_CNT * sizeof(ulong)); + + return ret; +} + +/** + * sandbox_ffa_sp_valid() - Check SP validity + * @emul: The sandbox FF-A emulator device + * @part_id: partition ID to check + * + * Search the input ID in the descriptors table. + * + * Return: + * + * 1 on success (Partition found). Otherwise, failure + */ +static int sandbox_ffa_sp_valid(struct udevice *emul, u16 part_id) +{ + u32 descs_cnt; + struct sandbox_ffa_emul *priv = dev_get_priv(emul); + + for (descs_cnt = 0 ; descs_cnt < SANDBOX_PARTITIONS_CNT ; descs_cnt++) + if (priv->partitions.descs[descs_cnt].info.id == part_id) + return 1; + + return 0; +} + +/** + * sandbox_ffa_msg_send_direct_req() - Emulated FFA_MSG_SEND_DIRECT_{REQ,RESP} handler + * @emul: The sandbox FF-A emulator device + * @pargs: The SMC call input arguments a0-a7 + * @res: The SMC return data + * + * Emulate FFA_MSG_SEND_DIRECT_{REQ,RESP} FF-A ABIs. + * Only SMC 64-bit is supported in Sandbox. + * + * Emulating interrupts is not supported. So, FFA_RUN and FFA_INTERRUPT are not + * supported. In case of success FFA_MSG_SEND_DIRECT_RESP is returned with + * default pattern data (0xff). + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int sandbox_ffa_msg_send_direct_req(struct udevice *emul, + ffa_value_t *pargs, ffa_value_t *res) +{ + u16 part_id; + struct sandbox_ffa_emul *priv = dev_get_priv(emul); + + part_id = GET_DST_SP_ID(pargs->a1); + + if (GET_NS_PHYS_ENDPOINT_ID(pargs->a1) != priv->id || + !sandbox_ffa_sp_valid(emul, part_id) || pargs->a2) { + res->a0 = FFA_SMC_32(FFA_ERROR); + res->a1 = 0; + res->a2 = -INVALID_PARAMETERS; + + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, 0, FFA_X3X7_MBZ_CNT * sizeof(ulong)); + + return ffa_to_std_errmap[INVALID_PARAMETERS]; + } + + res->a0 = FFA_SMC_64(FFA_MSG_SEND_DIRECT_RESP); + + res->a1 = PREP_SRC_SP_ID(part_id) | + PREP_NS_PHYS_ENDPOINT_ID(priv->id); + + res->a2 = 0; + + /* Return 0xff bytes as a response */ + res->a3 = -1UL; + res->a4 = -1UL; + res->a5 = -1UL; + res->a6 = -1UL; + res->a7 = -1UL; + + return 0; +} + +/** + * sandbox_ffa_get_rxbuf_flags() - Read the mapping/ownership flags + * @emul: The sandbox FF-A emulator device + * @queried_func_id: The FF-A function to be queried + * @func_data: Pointer to the FF-A function arguments container structure + * + * Query the status flags of the following emulated + * ABIs: FFA_RXTX_MAP, FFA_RXTX_UNMAP, FFA_RX_RELEASE. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int sandbox_ffa_get_rxbuf_flags(struct udevice *emul, u32 queried_func_id, + struct ffa_sandbox_data *func_data) +{ + struct sandbox_ffa_emul *priv = dev_get_priv(emul); + + if (!func_data) + return -EINVAL; + + if (!func_data->data0 || func_data->data0_size != sizeof(u8)) + return -EINVAL; + + switch (queried_func_id) { + case FFA_RXTX_MAP: + case FFA_RXTX_UNMAP: + *((u8 *)func_data->data0) = priv->pair_info.rxbuf_mapped; + return 0; + case FFA_RX_RELEASE: + *((u8 *)func_data->data0) = priv->pair_info.rxbuf_owned; + return 0; + default: + log_err("The querried FF-A interface flag (%d) undefined\n", + queried_func_id); + return -EINVAL; + } +} + +/** + * sandbox_ffa_get_fwk_version() - Return the FFA framework version + * @emul: The sandbox FF-A emulator device + * @func_data: Pointer to the FF-A function arguments container structure + * + * Return the FFA framework version read from the FF-A emulator data. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int sandbox_ffa_get_fwk_version(struct udevice *emul, struct ffa_sandbox_data *func_data) +{ + struct sandbox_ffa_emul *priv = dev_get_priv(emul); + + if (!func_data) + return -EINVAL; + + if (!func_data->data0 || + func_data->data0_size != sizeof(priv->fwk_version)) + return -EINVAL; + + *((u32 *)func_data->data0) = priv->fwk_version; + + return 0; +} + +/** + * sandbox_ffa_get_parts() - Return the address of partitions data + * @emul: The sandbox FF-A emulator device + * @func_data: Pointer to the FF-A function arguments container structure + * + * Return the address of partitions data read from the FF-A emulator data. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int sandbox_ffa_get_parts(struct udevice *emul, struct ffa_sandbox_data *func_data) +{ + struct sandbox_ffa_emul *priv = dev_get_priv(emul); + + if (!func_data) + return -EINVAL; + + if (!func_data->data0 || + func_data->data0_size != sizeof(struct ffa_partitions *)) + return -EINVAL; + + *((struct ffa_partitions **)func_data->data0) = &priv->partitions; + + return 0; +} + +/** + * sandbox_query_ffa_emul_state() - Inspect the FF-A ABIs + * @queried_func_id: The FF-A function to be queried + * @func_data: Pointer to the FF-A function arguments container structure + * + * Query the status of FF-A ABI specified in the input argument. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int sandbox_query_ffa_emul_state(u32 queried_func_id, + struct ffa_sandbox_data *func_data) +{ + struct udevice *emul; + int ret; + + ret = uclass_first_device_err(UCLASS_FFA_EMUL, &emul); + if (ret) { + log_err("Cannot find FF-A emulator during querying state\n"); + return ret; + } + + switch (queried_func_id) { + case FFA_RXTX_MAP: + case FFA_RXTX_UNMAP: + case FFA_RX_RELEASE: + return sandbox_ffa_get_rxbuf_flags(emul, queried_func_id, func_data); + case FFA_VERSION: + return sandbox_ffa_get_fwk_version(emul, func_data); + case FFA_PARTITION_INFO_GET: + return sandbox_ffa_get_parts(emul, func_data); + default: + log_err("Undefined FF-A interface (%d)\n", + queried_func_id); + return -EINVAL; + } +} + +/** + * sandbox_arm_ffa_smccc_smc() - FF-A SMC call emulation + * @args: the SMC call arguments + * @res: the SMC call returned data + * + * Emulate the FF-A ABIs SMC call. + * The emulated FF-A ABI is identified and invoked. + * FF-A emulation is based on the FF-A specification 1.0 + * + * Return: + * + * 0 on success. Otherwise, failure. + * FF-A protocol error codes are returned using the registers arguments as + * described by the specification + */ +void sandbox_arm_ffa_smccc_smc(ffa_value_t *args, ffa_value_t *res) +{ + int ret = 0; + struct udevice *emul; + + ret = uclass_first_device_err(UCLASS_FFA_EMUL, &emul); + if (ret) { + log_err("Cannot find FF-A emulator during SMC emulation\n"); + return; + } + + switch (args->a0) { + case FFA_SMC_32(FFA_VERSION): + ret = sandbox_ffa_version(emul, args, res); + break; + case FFA_SMC_32(FFA_PARTITION_INFO_GET): + ret = sandbox_ffa_partition_info_get(emul, args, res); + break; + case FFA_SMC_32(FFA_RXTX_UNMAP): + ret = sandbox_ffa_rxtx_unmap(emul, args, res); + break; + case FFA_SMC_64(FFA_MSG_SEND_DIRECT_REQ): + ret = sandbox_ffa_msg_send_direct_req(emul, args, res); + break; + case FFA_SMC_32(FFA_ID_GET): + ret = sandbox_ffa_id_get(emul, args, res); + break; + case FFA_SMC_32(FFA_FEATURES): + ret = sandbox_ffa_features(args, res); + break; + case FFA_SMC_64(FFA_RXTX_MAP): + ret = sandbox_ffa_rxtx_map(emul, args, res); + break; + case FFA_SMC_32(FFA_RX_RELEASE): + ret = sandbox_ffa_rx_release(emul, args, res); + break; + default: + log_err("Undefined FF-A interface (%lx)\n", + args->a0); + } + + if (ret != 0) + log_err("FF-A ABI internal failure (%d)\n", ret); +} + +/** + * invoke_ffa_fn() - SMC wrapper + * @args: FF-A ABI arguments to be copied to Xn registers + * @res: FF-A ABI return data to be copied from Xn registers + * + * Calls the emulated SMC call. + */ +void invoke_ffa_fn(ffa_value_t args, ffa_value_t *res) +{ + sandbox_arm_ffa_smccc_smc(&args, res); +} + +/** + * ffa_emul_find() - Find the FF-A emulator + * @dev: the sandbox FF-A device (sandbox-arm-ffa) + * @emulp: the FF-A emulator device (sandbox-ffa-emul) + * + * Search for the FF-A emulator and returns its device pointer. + * + * Return: + * 0 on success. Otherwise, failure + */ +int ffa_emul_find(struct udevice *dev, struct udevice **emulp) +{ + int ret; + + ret = uclass_first_device_err(UCLASS_FFA_EMUL, emulp); + if (ret) { + log_err("Cannot find FF-A emulator\n"); + return ret; + } + + log_info("FF-A emulator ready to use\n"); + + return 0; +} + +UCLASS_DRIVER(ffa_emul) = { + .name = "ffa_emul", + .id = UCLASS_FFA_EMUL, + .post_bind = dm_scan_fdt_dev, +}; + +static const struct udevice_id sandbox_ffa_emul_ids[] = { + { .compatible = "sandbox,arm-ffa-emul" }, + { } +}; + +/* Declaring the sandbox FF-A emulator under UCLASS_FFA_EMUL */ +U_BOOT_DRIVER(sandbox_ffa_emul) = { + .name = "sandbox_ffa_emul", + .id = UCLASS_FFA_EMUL, + .of_match = sandbox_ffa_emul_ids, + .priv_auto = sizeof(struct sandbox_ffa_emul), +}; diff --git a/drivers/firmware/arm-ffa/sandbox_arm_ffa_priv.h b/drivers/firmware/arm-ffa/sandbox_arm_ffa_priv.h deleted file mode 100644 index 4338f9c9b17..00000000000 --- a/drivers/firmware/arm-ffa/sandbox_arm_ffa_priv.h +++ /dev/null @@ -1,14 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright 2022-2023 Arm Limited and/or its affiliates - * - * Authors: - * Abdellatif El Khlifi - */ - -#ifndef __SANDBOX_ARM_FFA_PRV_H -#define __SANDBOX_ARM_FFA_PRV_H - -/* Future sandbox support private declarations */ - -#endif diff --git a/drivers/firmware/arm-ffa/sandbox_ffa.c b/drivers/firmware/arm-ffa/sandbox_ffa.c new file mode 100644 index 00000000000..ef9491ccead --- /dev/null +++ b/drivers/firmware/arm-ffa/sandbox_ffa.c @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2022-2023 Arm Limited and/or its affiliates + * + * Authors: + * Abdellatif El Khlifi + */ +#include +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +/** + * sandbox_ffa_discover() - perform sandbox FF-A discovery + * @dev: The sandbox FF-A bus device + * Try to discover the FF-A framework. Discovery is performed by + * querying the FF-A framework version from secure world using the FFA_VERSION ABI. + * Return: + * + * 0 on success. Otherwise, failure + */ +static int sandbox_ffa_discover(struct udevice *dev) +{ + int ret; + struct udevice *emul; + + log_info("Emulated FF-A framework discovery\n"); + + ret = ffa_emul_find(dev, &emul); + if (ret) { + log_err("Cannot find FF-A emulator\n"); + return ret; + } + + ret = ffa_get_version_hdlr(dev); + if (ret) + return ret; + + return 0; +} + +/** + * sandbox_ffa_probe() - The sandbox FF-A driver probe function + * @dev: the sandbox-arm-ffa device + * Save the emulator device in uc_priv. + * Return: + * + * 0 on success. + */ +static int sandbox_ffa_probe(struct udevice *dev) +{ + int ret; + struct ffa_priv *uc_priv = dev_get_uclass_priv(dev); + + ret = uclass_first_device_err(UCLASS_FFA_EMUL, &uc_priv->emul); + if (ret) { + log_err("Cannot find FF-A emulator\n"); + return ret; + } + + return 0; +} + +/** + * sandbox_ffa_bind() - The sandbox FF-A driver bind function + * @dev: the sandbox-arm-ffa device + * Try to discover the emulated FF-A bus. + * Return: + * + * 0 on success. + */ +static int sandbox_ffa_bind(struct udevice *dev) +{ + int ret; + + ret = sandbox_ffa_discover(dev); + if (ret) + return ret; + + return 0; +} + +/* Sandbox Arm FF-A emulator operations */ + +static const struct ffa_bus_ops sandbox_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, +}; + +static const struct udevice_id sandbox_ffa_id[] = { + { "sandbox,arm-ffa", 0 }, + { }, +}; + +/* Declaring the sandbox FF-A driver under UCLASS_FFA */ +U_BOOT_DRIVER(sandbox_arm_ffa) = { + .name = "sandbox_arm_ffa", + .of_match = sandbox_ffa_id, + .id = UCLASS_FFA, + .bind = sandbox_ffa_bind, + .probe = sandbox_ffa_probe, + .ops = &sandbox_ffa_ops, +}; diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 3c6af2e3d2f..0432c95c9ed 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -63,6 +63,7 @@ enum uclass_id { UCLASS_ETH_PHY, /* Ethernet PHY device */ UCLASS_EXTCON, /* External Connector Class */ UCLASS_FFA, /* Arm Firmware Framework for Armv8-A */ + UCLASS_FFA_EMUL, /* sandbox FF-A device emulator */ UCLASS_FIRMWARE, /* Firmware */ UCLASS_FPGA, /* FPGA device */ UCLASS_FUZZING_ENGINE, /* Fuzzing engine */ From a2f5c91cda9215d9abf042fc778b7a471994cf6b Mon Sep 17 00:00:00 2001 From: Abdellatif El Khlifi Date: Fri, 4 Aug 2023 14:33:42 +0100 Subject: [PATCH 6/9] arm_ffa: introduce sandbox test cases for UCLASS_FFA Add functional test cases for the FF-A support These tests rely on the FF-A sandbox emulator and FF-A sandbox driver which help in inspecting the FF-A communication. Signed-off-by: Abdellatif El Khlifi Reviewed-by: Simon Glass Cc: Tom Rini Cc: Ilias Apalodimas Cc: Jens Wiklander Cc: Heinrich Schuchardt --- MAINTAINERS | 1 + doc/arch/arm64.ffa.rst | 1 + test/dm/Makefile | 3 +- test/dm/ffa.c | 261 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 265 insertions(+), 1 deletion(-) create mode 100644 test/dm/ffa.c diff --git a/MAINTAINERS b/MAINTAINERS index 9d59ac09516..5a2a5467878 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -274,6 +274,7 @@ F: arch/sandbox/include/asm/sandbox_arm_ffa_priv.h F: doc/arch/arm64.ffa.rst F: drivers/firmware/arm-ffa/ F: include/arm_ffa.h +F: test/dm/ffa.c ARM FREESCALE IMX M: Stefano Babic diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst index 792898321a9..71606373f94 100644 --- a/doc/arch/arm64.ffa.rst +++ b/doc/arch/arm64.ffa.rst @@ -37,6 +37,7 @@ The U-Boot FF-A support provides the following parts: FF-A ABIs inspection methods. - An FF-A sandbox device driver for FF-A communication with the emulated Secure World. The driver leverages the FF-A Uclass to establish FF-A communication. +- Sandbox FF-A test cases. FF-A and SMC specifications ------------------------------------------- diff --git a/test/dm/Makefile b/test/dm/Makefile index 3799b1ae8fd..7ed00733c1a 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0+ # # Copyright (c) 2013 Google, Inc -# Copyright 2023 Arm Limited and/or its affiliates +# Copyright 2022-2023 Arm Limited and/or its affiliates obj-$(CONFIG_UT_DM) += test-dm.o @@ -92,6 +92,7 @@ obj-$(CONFIG_POWER_DOMAIN) += power-domain.o obj-$(CONFIG_ACPI_PMC) += pmc.o obj-$(CONFIG_DM_PMIC) += pmic.o obj-$(CONFIG_DM_PWM) += pwm.o +obj-$(CONFIG_ARM_FFA_TRANSPORT) += ffa.o obj-$(CONFIG_QFW) += qfw.o obj-$(CONFIG_RAM) += ram.o obj-y += regmap.o diff --git a/test/dm/ffa.c b/test/dm/ffa.c new file mode 100644 index 00000000000..6912666bb46 --- /dev/null +++ b/test/dm/ffa.c @@ -0,0 +1,261 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Functional tests for UCLASS_FFA class + * + * Copyright 2022-2023 Arm Limited and/or its affiliates + * + * Authors: + * Abdellatif El Khlifi + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Functional tests for the UCLASS_FFA */ + +static int check_fwk_version(struct ffa_priv *uc_priv, struct unit_test_state *uts) +{ + struct ffa_sandbox_data func_data; + u32 fwk_version = 0; + + func_data.data0 = &fwk_version; + func_data.data0_size = sizeof(fwk_version); + ut_assertok(sandbox_query_ffa_emul_state(FFA_VERSION, &func_data)); + ut_asserteq(uc_priv->fwk_version, fwk_version); + + return 0; +} + +static int check_endpoint_id(struct ffa_priv *uc_priv, struct unit_test_state *uts) +{ + ut_asserteq(0, uc_priv->id); + + return 0; +} + +static int check_rxtxbuf(struct ffa_priv *uc_priv, struct unit_test_state *uts) +{ + ut_assertnonnull(uc_priv->pair.rxbuf); + ut_assertnonnull(uc_priv->pair.txbuf); + + return 0; +} + +static int check_features(struct ffa_priv *uc_priv, struct unit_test_state *uts) +{ + ut_assert(uc_priv->pair.rxtx_min_pages == RXTX_4K || + uc_priv->pair.rxtx_min_pages == RXTX_16K || + uc_priv->pair.rxtx_min_pages == RXTX_64K); + + return 0; +} + +static int check_rxbuf_mapped_flag(u32 queried_func_id, + u8 rxbuf_mapped, + struct unit_test_state *uts) +{ + switch (queried_func_id) { + case FFA_RXTX_MAP: + ut_asserteq(1, rxbuf_mapped); + break; + case FFA_RXTX_UNMAP: + ut_asserteq(0, rxbuf_mapped); + break; + default: + ut_assert(false); + } + + return 0; +} + +static int check_rxbuf_release_flag(u8 rxbuf_owned, struct unit_test_state *uts) +{ + ut_asserteq(0, rxbuf_owned); + + return 0; +} + +static int test_ffa_msg_send_direct_req(u16 part_id, struct unit_test_state *uts) +{ + struct ffa_send_direct_data msg; + u8 cnt; + struct udevice *dev; + + ut_assertok(uclass_first_device_err(UCLASS_FFA, &dev)); + + ut_assertok(ffa_sync_send_receive(dev, part_id, &msg, 1)); + + for (cnt = 0; cnt < sizeof(struct ffa_send_direct_data) / sizeof(u64); cnt++) + ut_asserteq_64(-1UL, ((u64 *)&msg)[cnt]); + + return 0; +} + +static int test_partitions_and_comms(const char *service_uuid, + struct unit_test_state *uts) +{ + struct ffa_partition_desc *descs; + u32 count, i, j, valid_sps = 0; + struct udevice *dev; + struct ffa_sandbox_data func_data; + struct ffa_partitions *partitions; + + ut_assertok(uclass_first_device_err(UCLASS_FFA, &dev)); + + /* Get from the driver the count and information of the SPs matching the UUID */ + ut_assertok(ffa_partition_info_get(dev, service_uuid, &count, &descs)); + + /* Make sure the count is correct */ + ut_asserteq(SANDBOX_SP_COUNT_PER_VALID_SERVICE, count); + + /* SPs found , verify the partitions information */ + + func_data.data0 = &partitions; + func_data.data0_size = sizeof(struct ffa_partitions *); + ut_assertok(sandbox_query_ffa_emul_state(FFA_PARTITION_INFO_GET, &func_data)); + + for (i = 0; i < count ; i++) { + for (j = 0; + j < partitions->count; + j++) { + if (descs[i].info.id == + partitions->descs[j].info.id) { + valid_sps++; + ut_asserteq_mem(&descs[i], + &partitions->descs[j], + sizeof(struct ffa_partition_desc)); + /* Send and receive data from the current partition */ + test_ffa_msg_send_direct_req(descs[i].info.id, uts); + } + } + } + + /* Verify expected partitions found in the emulated secure world */ + ut_asserteq(SANDBOX_SP_COUNT_PER_VALID_SERVICE, valid_sps); + + return 0; +} + +static int dm_test_ffa_ack(struct unit_test_state *uts) +{ + struct ffa_priv *uc_priv; + struct ffa_sandbox_data func_data; + u8 rxbuf_flag = 0; + const char *svc1_uuid = SANDBOX_SERVICE1_UUID; + const char *svc2_uuid = SANDBOX_SERVICE2_UUID; + struct udevice *dev; + + /* Test probing the sandbox FF-A bus */ + ut_assertok(uclass_first_device_err(UCLASS_FFA, &dev)); + + /* Get a pointer to the sandbox FF-A bus private data */ + uc_priv = dev_get_uclass_priv(dev); + + /* Make sure the private data pointer is retrieved */ + ut_assertnonnull(uc_priv); + + /* Test FFA_VERSION */ + check_fwk_version(uc_priv, uts); + + /* Test FFA_ID_GET */ + check_endpoint_id(uc_priv, uts); + + /* Test FFA_FEATURES */ + check_features(uc_priv, uts); + + /* Test RX/TX buffers */ + check_rxtxbuf(uc_priv, uts); + + /* Test FFA_RXTX_MAP */ + func_data.data0 = &rxbuf_flag; + func_data.data0_size = sizeof(rxbuf_flag); + + rxbuf_flag = 0; + sandbox_query_ffa_emul_state(FFA_RXTX_MAP, &func_data); + check_rxbuf_mapped_flag(FFA_RXTX_MAP, rxbuf_flag, uts); + + /* FFA_PARTITION_INFO_GET / FFA_MSG_SEND_DIRECT_REQ */ + test_partitions_and_comms(svc1_uuid, uts); + + /* Test FFA_RX_RELEASE */ + rxbuf_flag = 1; + sandbox_query_ffa_emul_state(FFA_RX_RELEASE, &func_data); + check_rxbuf_release_flag(rxbuf_flag, uts); + + /* FFA_PARTITION_INFO_GET / FFA_MSG_SEND_DIRECT_REQ */ + test_partitions_and_comms(svc2_uuid, uts); + + /* Test FFA_RX_RELEASE */ + rxbuf_flag = 1; + ut_assertok(sandbox_query_ffa_emul_state(FFA_RX_RELEASE, &func_data)); + check_rxbuf_release_flag(rxbuf_flag, uts); + + return 0; +} + +DM_TEST(dm_test_ffa_ack, UT_TESTF_SCAN_FDT | UT_TESTF_CONSOLE_REC); + +static int dm_test_ffa_nack(struct unit_test_state *uts) +{ + struct ffa_priv *uc_priv; + const char *valid_svc_uuid = SANDBOX_SERVICE1_UUID; + const char *unvalid_svc_uuid = SANDBOX_SERVICE3_UUID; + const char *unvalid_svc_uuid_str = SANDBOX_SERVICE4_UUID; + struct ffa_send_direct_data msg; + int ret; + u32 count; + u16 part_id = 0; + struct udevice *dev; + struct ffa_partition_desc *descs = NULL; + + /* Test probing the sandbox FF-A bus */ + ut_assertok(uclass_first_device_err(UCLASS_FFA, &dev)); + + /* Get a pointer to the sandbox FF-A bus private data */ + uc_priv = dev_get_uclass_priv(dev); + + /* Make sure the private data pointer is retrieved */ + ut_assertnonnull(uc_priv); + + /* Query partitions count using invalid arguments */ + ret = ffa_partition_info_get(dev, NULL, NULL, NULL); + ut_asserteq(-EINVAL, ret); + ret = ffa_partition_info_get(dev, unvalid_svc_uuid, NULL, NULL); + ut_asserteq(-EINVAL, ret); + ret = ffa_partition_info_get(dev, unvalid_svc_uuid, &count, NULL); + ut_asserteq(-EINVAL, ret); + + /* Query partitions count using an invalid UUID string */ + ret = ffa_partition_info_get(dev, unvalid_svc_uuid_str, &count, &descs); + ut_asserteq(-EINVAL, ret); + + /* Query partitions count using an invalid UUID (no matching SP) */ + count = 0; + ret = ffa_partition_info_get(dev, unvalid_svc_uuid, &count, &descs); + ut_asserteq(0, count); + + /* Query partitions data using a valid UUID */ + count = 0; + ut_assertok(ffa_partition_info_get(dev, valid_svc_uuid, &count, &descs)); + /* Make sure partitions are detected */ + ut_asserteq(SANDBOX_SP_COUNT_PER_VALID_SERVICE, count); + ut_assertnonnull(descs); + + /* Send data to an invalid partition */ + ret = ffa_sync_send_receive(dev, part_id, &msg, 1); + ut_asserteq(-EINVAL, ret); + + /* Send data to a valid partition */ + part_id = uc_priv->partitions.descs[0].info.id; + ut_assertok(ffa_sync_send_receive(dev, part_id, &msg, 1)); + + return 0; +} + +DM_TEST(dm_test_ffa_nack, UT_TESTF_SCAN_FDT | UT_TESTF_CONSOLE_REC); From f16a48fec933829cfa3a81eb1dd531b924e3cde8 Mon Sep 17 00:00:00 2001 From: Abdellatif El Khlifi Date: Fri, 4 Aug 2023 14:33:43 +0100 Subject: [PATCH 7/9] arm_ffa: introduce armffa command Provide armffa command showcasing the use of the U-Boot FF-A support armffa is a command showcasing how to invoke FF-A operations. This provides a guidance to the client developers on how to call the FF-A bus interfaces. The command also allows to gather secure partitions information and ping these partitions. The command is also helpful in testing the communication with secure partitions. For more details please refer to the command documentation [1]. A Sandbox test is provided for the armffa command. [1]: doc/usage/cmd/armffa.rst Signed-off-by: Abdellatif El Khlifi Reviewed-by: Simon Glass Cc: Tom Rini Cc: Ilias Apalodimas Cc: Jens Wiklander Cc: Heinrich Schuchardt --- MAINTAINERS | 3 + cmd/Kconfig | 10 ++ cmd/Makefile | 1 + cmd/armffa.c | 202 +++++++++++++++++++++++++++++++ doc/arch/arm64.ffa.rst | 9 ++ doc/usage/cmd/armffa.rst | 94 ++++++++++++++ doc/usage/index.rst | 1 + drivers/firmware/arm-ffa/Kconfig | 1 + test/cmd/Makefile | 2 + test/cmd/armffa.c | 33 +++++ 10 files changed, 356 insertions(+) create mode 100644 cmd/armffa.c create mode 100644 doc/usage/cmd/armffa.rst create mode 100644 test/cmd/armffa.c diff --git a/MAINTAINERS b/MAINTAINERS index 5a2a5467878..bf851cffd6b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -271,9 +271,12 @@ M: Abdellatif El Khlifi S: Maintained F: arch/sandbox/include/asm/sandbox_arm_ffa.h F: arch/sandbox/include/asm/sandbox_arm_ffa_priv.h +F: cmd/armffa.c F: doc/arch/arm64.ffa.rst +F: doc/usage/cmd/armffa.rst F: drivers/firmware/arm-ffa/ F: include/arm_ffa.h +F: test/cmd/armffa.c F: test/dm/ffa.c ARM FREESCALE IMX diff --git a/cmd/Kconfig b/cmd/Kconfig index 7f65e3187a6..43ca10f69cc 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -952,6 +952,16 @@ endmenu menu "Device access commands" +config CMD_ARMFFA + bool "Arm FF-A test command" + depends on ARM_FFA_TRANSPORT + help + Provides a test command for the FF-A support + supported options: + - Listing the partition(s) info + - Sending a data pattern to the specified partition + - Displaying the arm_ffa device info + config CMD_ARMFLASH #depends on FLASH_CFI_DRIVER bool "armflash" diff --git a/cmd/Makefile b/cmd/Makefile index 9f8c0b058be..9bebf321c39 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -12,6 +12,7 @@ obj-y += panic.o obj-y += version.o # command +obj-$(CONFIG_CMD_ARMFFA) += armffa.o obj-$(CONFIG_CMD_2048) += 2048.o obj-$(CONFIG_CMD_ACPI) += acpi.o obj-$(CONFIG_CMD_ADDRMAP) += addrmap.o diff --git a/cmd/armffa.c b/cmd/armffa.c new file mode 100644 index 00000000000..7e6eafc03ad --- /dev/null +++ b/cmd/armffa.c @@ -0,0 +1,202 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2022-2023 Arm Limited and/or its affiliates + * + * Authors: + * Abdellatif El Khlifi + */ +#include +#include +#include +#include +#include +#include +#include + +/* Select the right physical address formatting according to the platform */ +#ifdef CONFIG_PHYS_64BIT +#define PhysAddrLength "ll" +#else +#define PhysAddrLength "" +#endif +#define PHYS_ADDR_LN "%" PhysAddrLength "x" + +/** + * ffa_get_dev() - Return the FF-A device + * @devp: pointer to the FF-A device + * + * Search for the FF-A device. + * + * Return: + * 0 on success. Otherwise, failure + */ +static int ffa_get_dev(struct udevice **devp) +{ + int ret; + + ret = uclass_first_device_err(UCLASS_FFA, devp); + if (ret) { + log_err("Cannot find FF-A bus device\n"); + return ret; + } + + return 0; +} + +/** + * do_ffa_getpart() - implementation of the getpart subcommand + * @cmdtp: Command Table + * @flag: flags + * @argc: number of arguments + * @argv: arguments + * + * Query a secure partition information. The secure partition UUID is provided + * as an argument. The function uses the arm_ffa driver + * partition_info_get operation which implements FFA_PARTITION_INFO_GET + * ABI to retrieve the data. The input UUID string is expected to be in big + * endian format. + * + * Return: + * + * CMD_RET_SUCCESS: on success, otherwise failure + */ +static int do_ffa_getpart(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + u32 count = 0; + int ret; + struct ffa_partition_desc *descs; + u32 i; + struct udevice *dev; + + if (argc != 2) { + log_err("Missing argument\n"); + return CMD_RET_USAGE; + } + + ret = ffa_get_dev(&dev); + if (ret) + return CMD_RET_FAILURE; + + /* Ask the driver to fill the buffer with the SPs info */ + + ret = ffa_partition_info_get(dev, argv[1], &count, &descs); + if (ret) { + log_err("Failure in querying partition(s) info (error code: %d)\n", ret); + return CMD_RET_FAILURE; + } + + /* SPs found , show the partition information */ + for (i = 0; i < count ; i++) { + log_info("Partition: id = %x , exec_ctxt %x , properties %x\n", + descs[i].info.id, + descs[i].info.exec_ctxt, + descs[i].info.properties); + } + + return CMD_RET_SUCCESS; +} + +/** + * do_ffa_ping() - implementation of the ping subcommand + * @cmdtp: Command Table + * @flag: flags + * @argc: number of arguments + * @argv: arguments + * + * Send data to a secure partition. The secure partition UUID is provided + * as an argument. Use the arm_ffa driver sync_send_receive operation + * which implements FFA_MSG_SEND_DIRECT_{REQ,RESP} ABIs to send/receive data. + * + * Return: + * + * CMD_RET_SUCCESS: on success, otherwise failure + */ +static int do_ffa_ping(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct ffa_send_direct_data msg = { + .data0 = 0xaaaaaaaa, + .data1 = 0xbbbbbbbb, + .data2 = 0xcccccccc, + .data3 = 0xdddddddd, + .data4 = 0xeeeeeeee, + }; + u16 part_id; + int ret; + struct udevice *dev; + + if (argc != 2) { + log_err("Missing argument\n"); + return CMD_RET_USAGE; + } + + part_id = strtoul(argv[1], NULL, 16); + if (!part_id) { + log_err("Partition ID can not be 0\n"); + return CMD_RET_USAGE; + } + + ret = ffa_get_dev(&dev); + if (ret) + return CMD_RET_FAILURE; + + ret = ffa_sync_send_receive(dev, part_id, &msg, 1); + if (!ret) { + u8 cnt; + + log_info("SP response:\n[LSB]\n"); + for (cnt = 0; + cnt < sizeof(struct ffa_send_direct_data) / sizeof(u64); + cnt++) + log_info("%llx\n", ((u64 *)&msg)[cnt]); + return CMD_RET_SUCCESS; + } + + log_err("Sending direct request error (%d)\n", ret); + return CMD_RET_FAILURE; +} + +/** + *do_ffa_devlist() - implementation of the devlist subcommand + * @cmdtp: [in] Command Table + * @flag: flags + * @argc: number of arguments + * @argv: arguments + * + * Query the device belonging to the UCLASS_FFA + * class. + * + * Return: + * + * CMD_RET_SUCCESS: on success, otherwise failure + */ +static int do_ffa_devlist(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct udevice *dev; + int ret; + + ret = ffa_get_dev(&dev); + if (ret) + return CMD_RET_FAILURE; + + log_info("device %s, addr " PHYS_ADDR_LN ", driver %s, ops " PHYS_ADDR_LN "\n", + dev->name, + map_to_sysmem(dev), + dev->driver->name, + map_to_sysmem(dev->driver->ops)); + + return CMD_RET_SUCCESS; +} + +static char armffa_help_text[] = + "getpart \n" + " - lists the partition(s) info\n" + "ping \n" + " - sends a data pattern to the specified partition\n" + "devlist\n" + " - displays information about the FF-A device/driver\n"; + +U_BOOT_CMD_WITH_SUBCMDS(armffa, "Arm FF-A test command", armffa_help_text, + U_BOOT_SUBCMD_MKENT(getpart, 2, 1, do_ffa_getpart), + U_BOOT_SUBCMD_MKENT(ping, 2, 1, do_ffa_ping), + U_BOOT_SUBCMD_MKENT(devlist, 1, 1, do_ffa_devlist)); diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst index 71606373f94..325fb80346e 100644 --- a/doc/arch/arm64.ffa.rst +++ b/doc/arch/arm64.ffa.rst @@ -120,6 +120,8 @@ At this stage the FF-A bus is registered with the DM and can be interacted with the DM APIs. Clients are able to probe then use the FF-A bus by calling uclass_first_device(). +Please refer to the armffa command implementation as an example of how to probe +and interact with the FF-A bus. When calling uclass_first_device(), the FF-A driver is probed and ends up calling ffa_do_probe() provided by the Uclass which does the following: @@ -219,6 +221,13 @@ Relationship between the sandbox emulator and the FF-A device ffa_emul 0 [ + ] sandbox_ffa_emul `-- arm-ffa-emul ffa 0 [ ] sandbox_arm_ffa `-- sandbox-arm-ffa +The armffa command +----------------------------------- + +armffa is a command showcasing how to use the FF-A bus and how to invoke the driver operations. + +Please refer the command documentation at :doc:`../usage/cmd/armffa` + Example of boot logs with FF-A enabled -------------------------------------- diff --git a/doc/usage/cmd/armffa.rst b/doc/usage/cmd/armffa.rst new file mode 100644 index 00000000000..13fa90c1290 --- /dev/null +++ b/doc/usage/cmd/armffa.rst @@ -0,0 +1,94 @@ +.. SPDX-License-Identifier: GPL-2.0+ +.. Copyright 2022-2023 Arm Limited and/or its affiliates + +armffa command +============== + +Synopsis +-------- + +:: + + armffa [sub-command] [arguments] + + sub-commands: + + getpart [partition UUID] + + lists the partition(s) info + + ping [partition ID] + + sends a data pattern to the specified partition + + devlist + + displays information about the FF-A device/driver + +Description +----------- + +armffa is a command showcasing how to use the FF-A bus and how to invoke its operations. + +This provides a guidance to the client developers on how to call the FF-A bus interfaces. + +The command also allows to gather secure partitions information and ping these partitions. + +The command is also helpful in testing the communication with secure partitions. + +Example +------- + +The following examples are run on Corstone-1000 platform. + +* ping + +:: + + corstone1000# armffa ping 0x8003 + SP response: + [LSB] + fffffffe + 0 + 0 + 0 + 0 + +* ping (failure case) + +:: + + corstone1000# armffa ping 0 + Sending direct request error (-22) + +* getpart + +:: + + corstone1000# armffa getpart 33d532ed-e699-0942-c09c-a798d9cd722d + Partition: id = 8003 , exec_ctxt 1 , properties 3 + +* getpart (failure case) + +:: + + corstone1000# armffa getpart 33d532ed-e699-0942-c09c-a798d9cd7221 + INVALID_PARAMETERS: Unrecognized UUID + Failure in querying partitions count (error code: -22) + +* devlist + +:: + + corstone1000# armffa devlist + device name arm_ffa, dev 00000000fdf41c30, driver name arm_ffa, ops 00000000fffc0e98 + +Configuration +------------- + +The command is available if CONFIG_CMD_ARMFFA=y and CONFIG_ARM_FFA_TRANSPORT=y. + +Return value +------------ + +The return value $? is 0 (true) on success, 1 (false) on failure. diff --git a/doc/usage/index.rst b/doc/usage/index.rst index 072db53614c..3326ec82fff 100644 --- a/doc/usage/index.rst +++ b/doc/usage/index.rst @@ -22,6 +22,7 @@ Shell commands cmd/acpi cmd/addrmap + cmd/armffa cmd/askenv cmd/base cmd/bdinfo diff --git a/drivers/firmware/arm-ffa/Kconfig b/drivers/firmware/arm-ffa/Kconfig index fc668c109d6..d75f8b53fd8 100644 --- a/drivers/firmware/arm-ffa/Kconfig +++ b/drivers/firmware/arm-ffa/Kconfig @@ -5,6 +5,7 @@ config ARM_FFA_TRANSPORT depends on DM && (ARM64 || SANDBOX) select ARM_SMCCC if !SANDBOX select ARM_SMCCC_FEATURES if !SANDBOX + imply CMD_ARMFFA select LIB_UUID select DEVRES help diff --git a/test/cmd/Makefile b/test/cmd/Makefile index a3cf983739e..6e3d7e919ef 100644 --- a/test/cmd/Makefile +++ b/test/cmd/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0+ # # Copyright (c) 2013 Google, Inc +# Copyright 2022-2023 Arm Limited and/or its affiliates ifdef CONFIG_HUSH_PARSER obj-$(CONFIG_CONSOLE_RECORD) += test_echo.o @@ -24,6 +25,7 @@ obj-$(CONFIG_CMD_SEAMA) += seama.o ifdef CONFIG_SANDBOX obj-$(CONFIG_CMD_READ) += rw.o obj-$(CONFIG_CMD_SETEXPR) += setexpr.o +obj-$(CONFIG_ARM_FFA_TRANSPORT) += armffa.o endif obj-$(CONFIG_CMD_TEMPERATURE) += temperature.o obj-$(CONFIG_CMD_WGET) += wget.o diff --git a/test/cmd/armffa.c b/test/cmd/armffa.c new file mode 100644 index 00000000000..9a44a397e8a --- /dev/null +++ b/test/cmd/armffa.c @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test for armffa command + * + * Copyright 2022-2023 Arm Limited and/or its affiliates + * + * Authors: + * Abdellatif El Khlifi + */ + +#include +#include +#include +#include +#include +#include + +/* Basic test of 'armffa' command */ +static int dm_test_armffa_cmd(struct unit_test_state *uts) +{ + /* armffa getpart */ + ut_assertok(run_command("armffa getpart " SANDBOX_SERVICE1_UUID, 0)); + + /* armffa ping */ + ut_assertok(run_commandf("armffa ping 0x%x", SANDBOX_SP1_ID)); + + /* armffa devlist */ + ut_assertok(run_command("armffa devlist", 0)); + + return 0; +} + +DM_TEST(dm_test_armffa_cmd, UT_TESTF_SCAN_FDT | UT_TESTF_CONSOLE_REC); From 218b062e8f20233181ecbcca1d627f5d44c4617d Mon Sep 17 00:00:00 2001 From: Abdellatif El Khlifi Date: Fri, 4 Aug 2023 14:33:44 +0100 Subject: [PATCH 8/9] arm_ffa: efi: introduce FF-A MM communication Add MM communication support using FF-A transport This feature allows accessing MM partitions services through EFI MM communication protocol. MM partitions such as StandAlonneMM or smm-gateway secure partitions which reside in secure world. An MM shared buffer and a door bell event are used to exchange the data. The data is used by EFI services such as GetVariable()/SetVariable() and copied from the communication buffer to the MM shared buffer. The secure partition is notified about availability of data in the MM shared buffer by an FF-A message (door bell). On such event, MM SP can read the data and updates the MM shared buffer with the response data. The response data is copied back to the communication buffer and consumed by the EFI subsystem. MM communication protocol supports FF-A 64-bit direct messaging. We tested the FF-A MM communication on the Corstone-1000 platform. We ran the UEFI SCT test suite containing EFI setVariable, getVariable and getNextVariable tests which involve FF-A MM communication and all tests are passing with the current changes. We made the SCT test reports (part of the ACS results) public following the latest Corstone-1000 platform software release. Please find the test reports at [1]. [1]: https://gitlab.arm.com/arm-reference-solutions/arm-reference-solutions-test-report/-/tree/master/embedded-a/corstone1000/CORSTONE1000-2023.06/acs_results_fpga.zip Signed-off-by: Abdellatif El Khlifi Tested-by: Gowtham Suresh Kumar Reviewed-by: Simon Glass Cc: Tom Rini Cc: Ilias Apalodimas Cc: Jens Wiklander Reviewed-by: Ilias Apalodimas Reviewed-by: Tom Rini --- include/mm_communication.h | 17 ++ lib/efi_loader/Kconfig | 39 ++++- lib/efi_loader/efi_variable_tee.c | 270 +++++++++++++++++++++++++++++- 3 files changed, 320 insertions(+), 6 deletions(-) diff --git a/include/mm_communication.h b/include/mm_communication.h index e65fbde60d0..f38f1a5344b 100644 --- a/include/mm_communication.h +++ b/include/mm_communication.h @@ -6,6 +6,9 @@ * Copyright (c) 2017, Intel Corporation. All rights reserved. * Copyright (C) 2020 Linaro Ltd. * Copyright (C) 2020 Linaro Ltd. + * Copyright 2022-2023 Arm Limited and/or its affiliates + * Authors: + * Abdellatif El Khlifi */ #ifndef _MM_COMMUNICATION_H_ @@ -13,6 +16,11 @@ #include +#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT) +/* MM service UUID string (big-endian format). This UUID is common across all MM SPs */ +#define MM_SP_UUID "33d532ed-e699-0942-c09c-a798d9cd722d" +#endif + /* * Interface to the pseudo Trusted Application (TA), which provides a * communication channel with the Standalone MM (Management Mode) @@ -248,4 +256,13 @@ struct smm_variable_var_check_property { u16 name[]; }; +#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT) +/* supported MM transports */ +enum mm_comms_select { + MM_COMMS_UNDEFINED, + MM_COMMS_FFA, + MM_COMMS_OPTEE +}; +#endif + #endif /* _MM_COMMUNICATION_H_ */ diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index a22e47616f7..9989e3f384e 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -55,13 +55,50 @@ config EFI_VARIABLE_FILE_STORE stored as file /ubootefi.var on the EFI system partition. config EFI_MM_COMM_TEE - bool "UEFI variables storage service via OP-TEE" + bool "UEFI variables storage service via the trusted world" depends on OPTEE help + Allowing access to the MM SP services (SPs such as StandAlonneMM, smm-gateway). + When using the u-boot OP-TEE driver, StandAlonneMM is supported. + When using the u-boot FF-A driver any MM SP is supported. + If OP-TEE is present and running StandAloneMM, dispatch all UEFI variable related operations to that. The application will verify, authenticate and store the variables on an RPMB. + When ARM_FFA_TRANSPORT is used, dispatch all UEFI variable related + operations to the MM SP running in the secure world. + A door bell mechanism is used to notify the SP when there is data in the shared + MM buffer. The data is copied by u-boot to the shared buffer before issuing + the door bell event. + +config FFA_SHARED_MM_BUF_SIZE + int "Memory size of the shared MM communication buffer" + depends on EFI_MM_COMM_TEE && ARM_FFA_TRANSPORT + help + This defines the size in bytes of the memory area reserved for the shared + buffer used for communication between the MM feature in U-Boot and + the MM SP in secure world. + The size of the memory region must be a multiple of the size of the maximum + translation granule size that is specified in the ID_AA64MMFR0_EL1 System register. + It is assumed that the MM SP knows the size of the shared MM communication buffer. + +config FFA_SHARED_MM_BUF_OFFSET + int "Data offset in the shared MM communication buffer" + depends on EFI_MM_COMM_TEE && ARM_FFA_TRANSPORT + help + This defines the offset in bytes of the data read or written to in the shared + buffer by the MM SP. + +config FFA_SHARED_MM_BUF_ADDR + hex "Define the address of the shared MM communication buffer" + depends on EFI_MM_COMM_TEE && ARM_FFA_TRANSPORT + help + This defines the address of the shared MM communication buffer + used for communication between the MM feature in U-Boot and + the MM SP in secure world. + It is assumed that the MM SP knows the address of the shared MM communication buffer. + config EFI_VARIABLE_NO_STORE bool "Don't persist non-volatile UEFI variables" help diff --git a/lib/efi_loader/efi_variable_tee.c b/lib/efi_loader/efi_variable_tee.c index dfef18435df..09d03c0eee7 100644 --- a/lib/efi_loader/efi_variable_tee.c +++ b/lib/efi_loader/efi_variable_tee.c @@ -4,16 +4,38 @@ * * Copyright (C) 2019 Linaro Ltd. * Copyright (C) 2019 Linaro Ltd. + * Copyright 2022-2023 Arm Limited and/or its affiliates + * + * Authors: + * Abdellatif El Khlifi */ #include +#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT) +#include +#endif +#include +#include #include #include #include #include -#include #include +#include #include +#include + +#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT) +/* MM return codes */ +#define MM_SUCCESS (0) +#define MM_NOT_SUPPORTED (-1) +#define MM_INVALID_PARAMETER (-2) +#define MM_DENIED (-3) +#define MM_NO_MEMORY (-5) + +static const char *mm_sp_svc_uuid = MM_SP_UUID; +static u16 mm_sp_id; +#endif extern struct efi_var_file __efi_runtime_data *efi_var_buf; static efi_uintn_t max_buffer_size; /* comm + var + func + data */ @@ -144,12 +166,238 @@ static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize) return ret; } +#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT) /** - * mm_communicate() - Adjust the cmonnucation buffer to StandAlonneMM and send + * ffa_notify_mm_sp() - Announce there is data in the shared buffer + * + * Notify the MM partition in the trusted world that + * data is available in the shared buffer. + * This is a blocking call during which trusted world has exclusive access + * to the MM shared buffer. + * + * Return: + * + * 0 on success + */ +static int ffa_notify_mm_sp(void) +{ + struct ffa_send_direct_data msg = {0}; + int ret; + int sp_event_ret; + struct udevice *dev; + + ret = uclass_first_device_err(UCLASS_FFA, &dev); + if (ret) { + log_err("EFI: Cannot find FF-A bus device, notify MM SP failure\n"); + return ret; + } + + msg.data0 = CONFIG_FFA_SHARED_MM_BUF_OFFSET; /* x3 */ + + ret = ffa_sync_send_receive(dev, mm_sp_id, &msg, 1); + if (ret) + return ret; + + sp_event_ret = msg.data0; /* x3 */ + + switch (sp_event_ret) { + case MM_SUCCESS: + ret = 0; + break; + case MM_NOT_SUPPORTED: + ret = -EINVAL; + break; + case MM_INVALID_PARAMETER: + ret = -EPERM; + break; + case MM_DENIED: + ret = -EACCES; + break; + case MM_NO_MEMORY: + ret = -EBUSY; + break; + default: + ret = -EACCES; + } + + return ret; +} + +/** + * ffa_discover_mm_sp_id() - Query the MM partition ID + * + * Use the FF-A driver to get the MM partition ID. + * If multiple partitions are found, use the first one. + * This is a boot time function. + * + * Return: + * + * 0 on success + */ +static int ffa_discover_mm_sp_id(void) +{ + u32 count = 0; + int ret; + struct ffa_partition_desc *descs; + struct udevice *dev; + + ret = uclass_first_device_err(UCLASS_FFA, &dev); + if (ret) { + log_err("EFI: Cannot find FF-A bus device, MM SP discovery failure\n"); + return ret; + } + + /* Ask the driver to fill the buffer with the SPs info */ + ret = ffa_partition_info_get(dev, mm_sp_svc_uuid, &count, &descs); + if (ret) { + log_err("EFI: Failure in querying SPs info (%d), MM SP discovery failure\n", ret); + return ret; + } + + /* MM SPs found , use the first one */ + + mm_sp_id = descs[0].info.id; + + log_info("EFI: MM partition ID 0x%x\n", mm_sp_id); + + return 0; +} + +/** + * ffa_mm_communicate() - Exchange EFI services data with the MM partition using FF-A + * @comm_buf: locally allocated communication buffer used for rx/tx + * @dsize: communication buffer size + * + * Issue a door bell event to notify the MM partition (SP) running in OP-TEE + * that there is data to read from the shared buffer. + * Communication with the MM SP is performed using FF-A transport. + * On the event, MM SP can read the data from the buffer and + * update the MM shared buffer with response data. + * The response data is copied back to the communication buffer. + * + * Return: + * + * EFI status code + */ +static efi_status_t ffa_mm_communicate(void *comm_buf, ulong comm_buf_size) +{ + ulong tx_data_size; + int ffa_ret; + efi_status_t efi_ret; + struct efi_mm_communicate_header *mm_hdr; + void *virt_shared_buf; + + if (!comm_buf) + return EFI_INVALID_PARAMETER; + + /* Discover MM partition ID at boot time */ + if (!mm_sp_id && ffa_discover_mm_sp_id()) { + log_err("EFI: Failure to discover MM SP ID at boot time, FF-A MM comms failure\n"); + return EFI_UNSUPPORTED; + } + + mm_hdr = (struct efi_mm_communicate_header *)comm_buf; + tx_data_size = mm_hdr->message_len + sizeof(efi_guid_t) + sizeof(size_t); + + if (comm_buf_size != tx_data_size || tx_data_size > CONFIG_FFA_SHARED_MM_BUF_SIZE) + return EFI_INVALID_PARAMETER; + + /* Copy the data to the shared buffer */ + + virt_shared_buf = map_sysmem((phys_addr_t)CONFIG_FFA_SHARED_MM_BUF_ADDR, 0); + memcpy(virt_shared_buf, comm_buf, tx_data_size); + + /* + * The secure world might have cache disabled for + * the device region used for shared buffer (which is the case for Optee). + * In this case, the secure world reads the data from DRAM. + * Let's flush the cache so the DRAM is updated with the latest data. + */ +#ifdef CONFIG_ARM64 + invalidate_dcache_all(); +#endif + + /* Announce there is data in the shared buffer */ + + ffa_ret = ffa_notify_mm_sp(); + + switch (ffa_ret) { + case 0: { + ulong rx_data_size; + /* Copy the MM SP response from the shared buffer to the communication buffer */ + rx_data_size = ((struct efi_mm_communicate_header *)virt_shared_buf)->message_len + + sizeof(efi_guid_t) + + sizeof(size_t); + + if (rx_data_size > comm_buf_size) { + efi_ret = EFI_OUT_OF_RESOURCES; + break; + } + + memcpy(comm_buf, virt_shared_buf, rx_data_size); + efi_ret = EFI_SUCCESS; + break; + } + case -EINVAL: + efi_ret = EFI_DEVICE_ERROR; + break; + case -EPERM: + efi_ret = EFI_INVALID_PARAMETER; + break; + case -EACCES: + efi_ret = EFI_ACCESS_DENIED; + break; + case -EBUSY: + efi_ret = EFI_OUT_OF_RESOURCES; + break; + default: + efi_ret = EFI_ACCESS_DENIED; + } + + unmap_sysmem(virt_shared_buf); + return efi_ret; +} + +/** + * get_mm_comms() - detect the available MM transport + * + * Make sure the FF-A bus is probed successfully + * which means FF-A communication with secure world works and ready + * for use. + * + * If FF-A bus is not ready, use OPTEE comms. + * + * Return: + * + * MM_COMMS_FFA or MM_COMMS_OPTEE + */ +static enum mm_comms_select get_mm_comms(void) +{ + struct udevice *dev; + int ret; + + ret = uclass_first_device_err(UCLASS_FFA, &dev); + if (ret) { + log_debug("EFI: Cannot find FF-A bus device, trying Optee comms\n"); + return MM_COMMS_OPTEE; + } + + return MM_COMMS_FFA; +} +#endif + +/** + * mm_communicate() - Adjust the communication buffer to the MM SP and send * it to OP-TEE * - * @comm_buf: locally allocted communcation buffer + * @comm_buf: locally allocated communication buffer * @dsize: buffer size + * + * The SP (also called partition) can be any MM SP such as StandAlonneMM or smm-gateway. + * The comm_buf format is the same for both partitions. + * When using the u-boot OP-TEE driver, StandAlonneMM is supported. + * When using the u-boot FF-A driver, any MM SP is supported. + * * Return: status code */ static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) @@ -157,12 +405,24 @@ static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) efi_status_t ret; struct efi_mm_communicate_header *mm_hdr; struct smm_variable_communicate_header *var_hdr; +#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT) + enum mm_comms_select mm_comms; +#endif dsize += MM_COMMUNICATE_HEADER_SIZE + MM_VARIABLE_COMMUNICATE_SIZE; mm_hdr = (struct efi_mm_communicate_header *)comm_buf; var_hdr = (struct smm_variable_communicate_header *)mm_hdr->data; - ret = optee_mm_communicate(comm_buf, dsize); +#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT) + mm_comms = get_mm_comms(); + if (mm_comms == MM_COMMS_FFA) + ret = ffa_mm_communicate(comm_buf, dsize); + else + ret = optee_mm_communicate(comm_buf, dsize); +#else + ret = optee_mm_communicate(comm_buf, dsize); +#endif + if (ret != EFI_SUCCESS) { log_err("%s failed!\n", __func__); return ret; @@ -697,7 +957,7 @@ void efi_variables_boot_exit_notify(void) ret = EFI_NOT_FOUND; if (ret != EFI_SUCCESS) - log_err("Unable to notify StMM for ExitBootServices\n"); + log_err("Unable to notify the MM partition for ExitBootServices\n"); free(comm_buf); /* From 13f3470adc18c5f71210a26e92cb86c375e7a35b Mon Sep 17 00:00:00 2001 From: Abdellatif El Khlifi Date: Fri, 4 Aug 2023 14:33:45 +0100 Subject: [PATCH 9/9] arm_ffa: efi: corstone1000: enable MM communication turn on EFI MM communication On Corstone-1000 platform MM communication between u-boot and the secure world (Optee) is done using the FF-A bus. Changes made are generated using savedefconfig. Signed-off-by: Abdellatif El Khlifi Cc: Tom Rini Cc: Simon Glass Cc: Ilias Apalodimas Cc: Jens Wiklander --- configs/corstone1000_defconfig | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/configs/corstone1000_defconfig b/configs/corstone1000_defconfig index c68e52b9fb2..3b5733b777d 100644 --- a/configs/corstone1000_defconfig +++ b/configs/corstone1000_defconfig @@ -42,6 +42,7 @@ CONFIG_OF_CONTROL=y CONFIG_VERSION_VARIABLE=y CONFIG_NET_RANDOM_ETHADDR=y CONFIG_REGMAP=y +CONFIG_ARM_FFA_TRANSPORT=y CONFIG_MISC=y # CONFIG_MMC is not set CONFIG_NVMXIP_QSPI=y @@ -54,9 +55,15 @@ CONFIG_DM_RTC=y CONFIG_RTC_EMULATION=y CONFIG_DM_SERIAL=y CONFIG_SYSRESET=y +CONFIG_TEE=y +CONFIG_OPTEE=y CONFIG_USB=y CONFIG_USB_ISP1760=y CONFIG_ERRNO_STR=y +CONFIG_EFI_MM_COMM_TEE=y +CONFIG_FFA_SHARED_MM_BUF_SIZE=4096 +CONFIG_FFA_SHARED_MM_BUF_OFFSET=0 +CONFIG_FFA_SHARED_MM_BUF_ADDR=0x02000000 CONFIG_EFI_CAPSULE_ON_DISK=y CONFIG_EFI_IGNORE_OSINDICATIONS=y CONFIG_FWU_MULTI_BANK_UPDATE=y