[PATCH 0/6] introduce Arm FF-A support

From: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
This patchset adds support for Arm FF-A (Arm Firmware Framework for Armv8-A v1.0).
FF-A support is generic by design and can be used by any Arm platform.
The features added are as follows:
1/ FF-A device driver 2/ armffa command 3/ FF-A Sandbox driver 4/ FF-A Sandbox test cases 5/ FF-A MM communication
The suggested design sees FF-A as a data bus allowing data exchange with the firmware running under TrustZone HW (such as Optee). The same approach was followed in the FF-A driver in Linux kernel (https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/driv...))
u-boot boards using FF-A can provide a device tree node in a <board>-u-boot.dtsi file. Since the node can not be hosted in Linux device tree, we suggest using u-boot device tree.
As a use case, we will send a commit enabling FF-A for the Corstone-1000 platform and providing the FF-A node in u-boot.dtsi files.
Corstone-1000: https://lore.kernel.org/u-boot/20220322104118.573537-1-rui.silva@linaro.org/
Cc: Tom Rini trini@konsulko.com
Abdellatif El Khlifi (6): arm_ffa: introduce Arm FF-A low-level driver arm_ffa: introduce armffa command arm_ffa: introduce the FF-A Sandbox driver arm_ffa: introduce Sandbox test cases for UCLASS_FFA arm_ffa: introduce armffa command Sandbox test arm_ffa: introduce FF-A MM communication
MAINTAINERS | 15 + arch/arm/cpu/armv8/cache.S | 16 + arch/arm/cpu/armv8/cache_v8.c | 3 +- arch/arm/cpu/armv8/smccc-call.S | 27 + arch/arm/lib/asm-offsets.c | 6 + arch/sandbox/dts/sandbox.dtsi | 10 + arch/sandbox/dts/test.dts | 10 + cmd/Kconfig | 10 + cmd/Makefile | 2 + cmd/armffa.c | 266 +++++ common/board_r.c | 7 + configs/sandbox64_defconfig | 2 + configs/sandbox_defconfig | 2 + doc/arch/sandbox.rst | 1 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/arm-ffa/Kconfig | 32 + drivers/arm-ffa/Makefile | 7 + drivers/arm-ffa/arm-ffa-uclass.c | 102 ++ drivers/arm-ffa/arm_ffa_prv.h | 200 ++++ drivers/arm-ffa/core.c | 1463 +++++++++++++++++++++++++ drivers/arm-ffa/sandbox.c | 735 +++++++++++++ drivers/arm-ffa/sandbox_arm_ffa_prv.h | 128 +++ include/arm_ffa.h | 197 ++++ include/arm_ffa_helper.h | 45 + include/dm/uclass-id.h | 1 + include/linux/arm-smccc.h | 28 +- include/mm_communication.h | 4 +- include/sandbox_arm_ffa.h | 31 + include/sandbox_arm_ffa_helper.h | 26 + lib/Kconfig | 1 + lib/Makefile | 1 + lib/arm-ffa/Kconfig | 6 + lib/arm-ffa/Makefile | 9 + lib/arm-ffa/arm_ffa_helper.c | 188 ++++ lib/arm-ffa/sandbox_arm_ffa_helper.c | 23 + lib/efi_loader/Kconfig | 14 +- lib/efi_loader/efi_boottime.c | 17 + lib/efi_loader/efi_variable_tee.c | 294 ++++- test/cmd/Makefile | 1 + test/cmd/armffa.c | 33 + test/cmd/armffa.h | 13 + test/dm/Makefile | 1 + test/dm/ffa.c | 424 +++++++ test/dm/ffa.h | 22 + 45 files changed, 4415 insertions(+), 11 deletions(-) create mode 100644 cmd/armffa.c create mode 100644 drivers/arm-ffa/Kconfig create mode 100644 drivers/arm-ffa/Makefile create mode 100644 drivers/arm-ffa/arm-ffa-uclass.c create mode 100644 drivers/arm-ffa/arm_ffa_prv.h create mode 100644 drivers/arm-ffa/core.c create mode 100644 drivers/arm-ffa/sandbox.c create mode 100644 drivers/arm-ffa/sandbox_arm_ffa_prv.h create mode 100644 include/arm_ffa.h create mode 100644 include/arm_ffa_helper.h create mode 100644 include/sandbox_arm_ffa.h create mode 100644 include/sandbox_arm_ffa_helper.h create mode 100644 lib/arm-ffa/Kconfig create mode 100644 lib/arm-ffa/Makefile create mode 100644 lib/arm-ffa/arm_ffa_helper.c create mode 100644 lib/arm-ffa/sandbox_arm_ffa_helper.c create mode 100644 test/cmd/armffa.c create mode 100644 test/cmd/armffa.h create mode 100644 test/dm/ffa.c create mode 100644 test/dm/ffa.h

From: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
Add the driver implementing Arm Firmware Framework for Armv8-A v1.0
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. This driver uses SMC32 calling convention.
The driver provides helper FF-A interfaces for user layers. These helper functions allow clients to pass data and select the FF-A function to use for the communication with secure world.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com --- MAINTAINERS | 8 + arch/arm/cpu/armv8/smccc-call.S | 27 + arch/arm/lib/asm-offsets.c | 6 + common/board_r.c | 7 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/arm-ffa/Kconfig | 25 + drivers/arm-ffa/Makefile | 6 + drivers/arm-ffa/arm-ffa-uclass.c | 65 ++ drivers/arm-ffa/arm_ffa_prv.h | 200 +++++ drivers/arm-ffa/core.c | 1427 ++++++++++++++++++++++++++++++ include/arm_ffa.h | 190 ++++ include/arm_ffa_helper.h | 45 + include/dm/uclass-id.h | 1 + include/linux/arm-smccc.h | 28 +- lib/Kconfig | 1 + lib/Makefile | 1 + lib/arm-ffa/Kconfig | 6 + lib/arm-ffa/Makefile | 8 + lib/arm-ffa/arm_ffa_helper.c | 188 ++++ lib/efi_loader/efi_boottime.c | 17 + 21 files changed, 2258 insertions(+), 1 deletion(-) create mode 100644 drivers/arm-ffa/Kconfig create mode 100644 drivers/arm-ffa/Makefile create mode 100644 drivers/arm-ffa/arm-ffa-uclass.c create mode 100644 drivers/arm-ffa/arm_ffa_prv.h create mode 100644 drivers/arm-ffa/core.c create mode 100644 include/arm_ffa.h create mode 100644 include/arm_ffa_helper.h create mode 100644 lib/arm-ffa/Kconfig create mode 100644 lib/arm-ffa/Makefile create mode 100644 lib/arm-ffa/arm_ffa_helper.c
diff --git a/MAINTAINERS b/MAINTAINERS index 74d5263fb1..c5b608eb60 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -232,6 +232,14 @@ F: board/CZ.NIC/ F: configs/turris_*_defconfig F: include/configs/turris_*.h
+ARM FF-A +M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com +S: Maintained +F: drivers/arm-ffa/ +F: include/arm_ffa.h +F: include/arm_ffa_helper.h +F: lib/arm-ffa/ + ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de M: Fabio Estevam festevam@gmail.com diff --git a/arch/arm/cpu/armv8/smccc-call.S b/arch/arm/cpu/armv8/smccc-call.S index dc92b28777..9a6aebf194 100644 --- a/arch/arm/cpu/armv8/smccc-call.S +++ b/arch/arm/cpu/armv8/smccc-call.S @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2015, Linaro Limited + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */ #include <linux/linkage.h> #include <linux/arm-smccc.h> @@ -45,3 +47,28 @@ ENDPROC(__arm_smccc_smc) ENTRY(__arm_smccc_hvc) SMCCC hvc ENDPROC(__arm_smccc_hvc) + +#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) + + .macro FFASMCCC instr + .cfi_startproc + \instr #0 + ldr x9, [sp] + stp x0, x1, [x9, #ARM_SMCCC_RES_X0_OFFS] + stp x2, x3, [x9, #ARM_SMCCC_RES_X2_OFFS] + stp x4, x5, [x9, #ARM_SMCCC_RES_X4_OFFS] + stp x6, x7, [x9, #ARM_SMCCC_RES_X6_OFFS] + ret + .cfi_endproc + .endm + +/* + * void arm_ffa_smccc_smc(unsigned long a0, unsigned long a1, unsigned long a2, + * unsigned long a3, unsigned long a4, unsigned long a5, + * unsigned long a6, unsigned long a7, struct arm_smccc_res *res) + */ +ENTRY(__arm_ffa_smccc_smc) + FFASMCCC smc +ENDPROC(__arm_ffa_smccc_smc) + +#endif diff --git a/arch/arm/lib/asm-offsets.c b/arch/arm/lib/asm-offsets.c index 22fd541f9a..02a4a42fe6 100644 --- a/arch/arm/lib/asm-offsets.c +++ b/arch/arm/lib/asm-offsets.c @@ -9,6 +9,8 @@ * generate asm statements containing #defines, * compile this file to assembler, and then extract the * #defines from the assembly-language output. + * + * (C) Copyright 2022 ARM Limited */
#include <common.h> @@ -115,6 +117,10 @@ int main(void) #ifdef CONFIG_ARM_SMCCC DEFINE(ARM_SMCCC_RES_X0_OFFS, offsetof(struct arm_smccc_res, a0)); DEFINE(ARM_SMCCC_RES_X2_OFFS, offsetof(struct arm_smccc_res, a2)); +#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) + DEFINE(ARM_SMCCC_RES_X4_OFFS, offsetof(struct arm_smccc_res, a4)); + DEFINE(ARM_SMCCC_RES_X6_OFFS, offsetof(struct arm_smccc_res, a6)); +#endif DEFINE(ARM_SMCCC_QUIRK_ID_OFFS, offsetof(struct arm_smccc_quirk, id)); DEFINE(ARM_SMCCC_QUIRK_STATE_OFFS, offsetof(struct arm_smccc_quirk, state)); #endif diff --git a/common/board_r.c b/common/board_r.c index b92c1bb0be..7866ec3ad5 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -62,6 +62,10 @@ #include <asm-generic/gpio.h> #include <efi_loader.h>
+#ifdef CONFIG_ARM_FFA_TRANSPORT +#include <arm_ffa_helper.h> +#endif + DECLARE_GLOBAL_DATA_PTR;
ulong monitor_flash_len; @@ -771,6 +775,9 @@ static init_fnc_t init_sequence_r[] = { INIT_FUNC_WATCHDOG_RESET initr_net, #endif +#ifdef CONFIG_ARM_FFA_TRANSPORT + ffa_helper_init_device, +#endif #ifdef CONFIG_POST initr_post, #endif diff --git a/drivers/Kconfig b/drivers/Kconfig index b26ca8cf70..e83c23789d 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -6,6 +6,8 @@ source "drivers/core/Kconfig"
source "drivers/adc/Kconfig"
+source "drivers/arm-ffa/Kconfig" + source "drivers/ata/Kconfig"
source "drivers/axi/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 4e7cf28440..6671d2a604 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -107,6 +107,7 @@ obj-y += iommu/ obj-y += smem/ obj-y += thermal/ obj-$(CONFIG_TEE) += tee/ +obj-$(CONFIG_ARM_FFA_TRANSPORT) += arm-ffa/ obj-y += axi/ obj-y += ufs/ obj-$(CONFIG_W1) += w1/ diff --git a/drivers/arm-ffa/Kconfig b/drivers/arm-ffa/Kconfig new file mode 100644 index 0000000000..e503dfaebf --- /dev/null +++ b/drivers/arm-ffa/Kconfig @@ -0,0 +1,25 @@ +# 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 if ARM64 + select LIB_UUID + select ARM_FFA_TRANSPORT_HELPERS + 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. + + This driver 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, the Secure World is considered as one + entity to communicate with. FF-A communication is handled by + one device and one instance. This device takes care of + all the interactions between Normal world and Secure World. diff --git a/drivers/arm-ffa/Makefile b/drivers/arm-ffa/Makefile new file mode 100644 index 0000000000..7bc9a336a9 --- /dev/null +++ b/drivers/arm-ffa/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2022 Abdellatif El Khlifi abdellatif.elkhlifi@arm.com +# + +obj-y += arm-ffa-uclass.o core.o diff --git a/drivers/arm-ffa/arm-ffa-uclass.c b/drivers/arm-ffa/arm-ffa-uclass.c new file mode 100644 index 0000000000..0bf661d397 --- /dev/null +++ b/drivers/arm-ffa/arm-ffa-uclass.c @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <dm.h> +#include <arm_ffa.h> +#include <errno.h> +#include <log.h> +#include <asm/global_data.h> + +DECLARE_GLOBAL_DATA_PTR; + +UCLASS_DRIVER(ffa) = { + .name = "ffa", + .id = UCLASS_FFA, +}; + +/** + * ffa_get_invoke_func - performs a call to the FF-A driver dispatcher + * @func_id: The FF-A function to be used + * @func_data: Pointer to the FF-A function arguments + * container structure. This also includes + * pointers to the returned data needed by + * clients. + * + * This runtime function passes the FF-A function ID and its arguments to + * the FF-A driver dispatcher. + * This function is called by the FF-A helper functions. + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +int __ffa_runtime ffa_get_invoke_func(u32 func_id, struct ffa_interface_data *func_data) +{ + if (!ffa_device_get_ops()->invoke_func) + return -EINVAL; + + return ffa_device_get_ops()->invoke_func(func_id, func_data); +} + +/** + * ffa_init_device - probes the arm_ffa device + * + * This boot time function makes sure the arm_ffa device is probed + * and ready for use. + * This function is called automatically at initcalls + * level (after u-boot relocation). + * + * Arm FF-A transport is implemented through a single u-boot + * device (arm_ffa). So, there is only one device belonging to UCLASS_FFA. + * All FF-A clients should use the arm_ffa device to use the FF-A + * transport. + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +int ffa_init_device(void) +{ + return ffa_get_device(); +} diff --git a/drivers/arm-ffa/arm_ffa_prv.h b/drivers/arm-ffa/arm_ffa_prv.h new file mode 100644 index 0000000000..c788d0bd53 --- /dev/null +++ b/drivers/arm-ffa/arm_ffa_prv.h @@ -0,0 +1,200 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __ARM_FFA_PRV_H +#define __ARM_FFA_PRV_H + +#include <arm_ffa.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <dm/read.h> + +/* + * This header is private. It is exclusively used by the FF-A driver + */ + +/* 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))) + +/* The FF-A SMC function prototype definition */ + +typedef void (*invoke_ffa_fn_t)(unsigned long a0, unsigned long a1, + unsigned long a2, unsigned long a3, unsigned long a4, + unsigned long a5, unsigned long a6, unsigned long a7, + struct arm_smccc_res *res); + +/** + * enum ffa_conduit - Arm FF-A conduits supported by the Arm FF-A driver + * Currently only SMC32 is supported. + */ +enum ffa_conduit { + FFA_CONDUIT_SMC = 0, +}; + +/** + * FFA_DECLARE_ARGS - FF-A functions local variables + * @a0-a7: local variables used to set registers x0-x7 + * @res: the structure hosting the FF-A function return data + * + * A helper macro for declaring local variables for the FF-A functions arguments. + * The x0-x7 registers are used to exchange data with the secure world. + * But, only the bottom 32-bit of thes registers contains the data. + */ +#define FFA_DECLARE_ARGS \ + unsigned long a0 = 0; \ + unsigned long a1 = 0; \ + unsigned long a2 = 0; \ + unsigned long a3 = 0; \ + unsigned long a4 = 0; \ + unsigned long a5 = 0; \ + unsigned long a6 = 0; \ + unsigned long a7 = 0; \ + struct arm_smccc_res res = {0} + +/* FF-A error codes */ +#define FFA_ERR_STAT_NOT_SUPPORTED (-1) +#define FFA_ERR_STAT_INVALID_PARAMETERS (-2) +#define FFA_ERR_STAT_NO_MEMORY (-3) +#define FFA_ERR_STAT_BUSY (-4) +#define FFA_ERR_STAT_INTERRUPTED (-5) +#define FFA_ERR_STAT_DENIED (-6) +#define FFA_ERR_STAT_RETRY (-7) +#define FFA_ERR_STAT_ABORTED (-8) + +/** + * struct ffa_features_desc - FF-A functions features + * @func_id: FF-A function + * @field1: features read from register w2 + * @field2: features read from register w3 + * + * Data structure describing the features of the FF-A functions queried by + * FFA_FEATURES + */ +struct ffa_features_desc { + u32 func_id; + u32 field1; + u32 field2; +}; + +/** + * enum ffa_rxtx_buf_sizes - minimum sizes supported + * for the RX/TX buffers + */ +enum ffa_rxtx_buf_sizes { + RXTX_4K, + RXTX_64K, + RXTX_16K +}; + +/* + * Number of the FF-A interfaces features descriptors + * currently only FFA_RXTX_MAP descriptor is supported + */ +#define FFA_FEATURE_DESC_CNT (1) + +/** + * struct ffa_pdata - platform data for the arm_ffa device + * @conduit: The FF-A conduit used + * + * Platform data structure read from the device tree + */ +struct ffa_pdata { + enum ffa_conduit conduit; +}; + +/** + * struct ffa_rxtxpair - structure hosting the RX/TX buffers virtual addresses + * @rxbuf: virtual address of the RX buffer + * @txbuf: virtual address of the TX buffer + * + * Data structure hosting 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 { + u64 rxbuf; /* virtual address */ + u64 txbuf; /* virtual address */ +}; + +/** + * struct ffa_partition_desc - the secure partition descriptor + * @info: partition information + * @UUID: UUID + * + * Each partition has its descriptor containing the partitions information and the UUID + */ +struct ffa_partition_desc { + struct ffa_partition_info info; + union ffa_partition_uuid UUID; +}; + +/** + * struct ffa_partitions - descriptors for all secure partitions + * @count: The number of partitions descriptors + * @descs The partitions descriptors table + * + * This data structure contains the partitions descriptors table + */ +struct ffa_partitions { + u32 count; + struct ffa_partition_desc *descs; /* virtual address */ +}; + +/** + * struct ffa_prvdata - the driver private data structure + * + * @dev: The arm_ffa device under u-boot driver model + * @fwk_version: FF-A framework version + * @id: u-boot endpoint ID + * @partitions: The partitions descriptors structure + * @pair: The RX/TX buffers pair + * @conduit: The selected conduit + * @invoke_ffa_fn: The function executing the FF-A function + * @features: Table of the FF-A functions having features + * + * The driver data structure hosting all resident data. + */ +struct ffa_prvdata { + struct udevice *dev; + u32 fwk_version; + u16 id; + struct ffa_partitions partitions; + struct ffa_rxtxpair pair; + enum ffa_conduit conduit; + invoke_ffa_fn_t invoke_ffa_fn; + struct ffa_features_desc features[FFA_FEATURE_DESC_CNT]; +}; + +#endif diff --git a/drivers/arm-ffa/core.c b/drivers/arm-ffa/core.c new file mode 100644 index 0000000000..9d0dab1d36 --- /dev/null +++ b/drivers/arm-ffa/core.c @@ -0,0 +1,1427 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include "arm_ffa_prv.h" +#include <asm/global_data.h> +#include <asm/io.h> +#include <common.h> +#include <dm.h> +#include <linux/errno.h> +#include <linux/sizes.h> +#include <log.h> +#include <malloc.h> +#include <mapmem.h> +#include <string.h> + +DECLARE_GLOBAL_DATA_PTR; + +/** + * The device private data structure containing all the resident + * data read from secure world + */ +struct ffa_prvdata __ffa_runtime_data ffa_priv_data = {0}; + +/* + * Driver functions + */ + +/** + * ffa_get_device - probes the arm_ffa device + * + * This boot time function makes sure the arm_ffa device is probed + * and ready for use. This is done using uclass_get_device. + * The arm_ffa driver belongs to UCLASS_FFA. + * This function should be called before using the driver. + * + * Arm FF-A transport is implemented through a single u-boot + * device (arm_ffa). So, there is only one device belonging to UCLASS_FFA. + * All FF-A clients should use the arm_ffa device to use the FF-A + * transport. + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +int ffa_get_device(void) +{ + int ret; + int devnum = 0; + + if (ffa_priv_data.dev) + return FFA_ERR_STAT_SUCCESS; + + /* + * searching and probing the device + */ + ret = uclass_get_device(UCLASS_FFA, devnum, &ffa_priv_data.dev); + if (ret) { + ffa_err("can not find the device"); + ffa_priv_data.dev = NULL; + return -ENODEV; + } + + return FFA_ERR_STAT_SUCCESS; +} + +/** + * ffa_get_version - FFA_VERSION handler function + * + * This is the boot time function that implements FFA_VERSION FF-A function + * to get from the secure world the FF-A framework version + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +static int ffa_get_version(void) +{ + u16 major, minor; + + FFA_DECLARE_ARGS; + + if (!ffa_priv_data.invoke_ffa_fn) + panic("[FFA] no private data found\n"); + + a0 = FFA_VERSION; + a1 = FFA_VERSION_1_0; + ffa_priv_data.invoke_ffa_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res); + + if (res.a0 == FFA_ERR_STAT_NOT_SUPPORTED) { + ffa_err("A Firmware Framework implementation does not exist"); + return -EOPNOTSUPP; + } + + major = GET_FFA_MAJOR_VERSION(res.a0); + minor = GET_FFA_MINOR_VERSION(res.a0); + + ffa_info("FF-A driver %d.%d\nFF-A framework %d.%d", + FFA_MAJOR_VERSION, FFA_MINOR_VERSION, major, minor); + + if ((major == FFA_MAJOR_VERSION && minor >= FFA_MINOR_VERSION)) { + ffa_info("Versions are compatible "); + + ffa_priv_data.fwk_version = res.a0; + + return FFA_ERR_STAT_SUCCESS; + } + + ffa_info("Versions are incompatible "); + return -EPROTONOSUPPORT; +} + +/** + * ffa_get_endpoint_id - FFA_ID_GET handler function + * + * This is the boot time function that implements FFA_ID_GET FF-A function + * to get from the secure world u-boot endpoint ID + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +static int ffa_get_endpoint_id(void) +{ + FFA_DECLARE_ARGS; + + if (!ffa_priv_data.invoke_ffa_fn) + panic("[FFA] no private data found\n"); + + a0 = FFA_ID_GET; + + ffa_priv_data.invoke_ffa_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res); + + switch (res.a0) { + case FFA_ERROR: + { + if (((int)res.a2) == FFA_ERR_STAT_NOT_SUPPORTED) { + ffa_err("This function is not implemented at this FF-A instance"); + return -EOPNOTSUPP; + } + + ffa_err("Undefined error code (%d)", ((int)res.a2)); + return -EINVAL; + } + case FFA_SUCCESS: + { + ffa_priv_data.id = GET_SELF_ENDPOINT_ID(res.a2); + ffa_info("endpoint ID is %u", ffa_priv_data.id); + + return FFA_ERR_STAT_SUCCESS; + } + default: + { + ffa_err("Undefined response function (0x%lx)", res.a0); + return -EINVAL; + } + } +} + +/** + * ffa_get_features_desc - returns the features descriptor of the specified + * FF-A function + * @func_id: the FF-A function which the features are to be retrieved + * + * This is a boot time function that searches the features descriptor of the + * specified FF-A function + * + * Return: + * + * When found, the address of the features descriptor is returned. Otherwise, NULL. + */ +static struct ffa_features_desc *ffa_get_features_desc(u32 func_id) +{ + u32 desc_idx; + + /* + * search for the descriptor of the selected FF-A interface + */ + for (desc_idx = 0; desc_idx < FFA_FEATURE_DESC_CNT ; desc_idx++) + if (ffa_priv_data.features[desc_idx].func_id == func_id) + return &ffa_priv_data.features[desc_idx]; + + return NULL; +} + +/** + * ffa_get_rxtx_map_features - FFA_FEATURES handler function with FFA_RXTX_MAP + * argument + * + * This is the boot time function that implements FFA_FEATURES FF-A function + * to retrieve the FFA_RXTX_MAP features + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +static int ffa_get_rxtx_map_features(void) +{ + FFA_DECLARE_ARGS; + + if (!ffa_priv_data.invoke_ffa_fn) + panic("[FFA] no private data found\n"); + + a0 = FFA_FEATURES; + a1 = FFA_RXTX_MAP; + + ffa_priv_data.invoke_ffa_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res); + + switch (res.a0) { + case FFA_ERROR: + { + if (((int)res.a2) == FFA_ERR_STAT_NOT_SUPPORTED) { + ffa_err("FFA_RXTX_MAP is not implemented at this FF-A instance"); + return -EOPNOTSUPP; + } + + ffa_err("Undefined error code (%d)", ((int)res.a2)); + return -EINVAL; + } + case FFA_SUCCESS: + { + u32 desc_idx; + + /* + * search for an empty descriptor + */ + for (desc_idx = 0; desc_idx < FFA_FEATURE_DESC_CNT ; desc_idx++) + if (!ffa_priv_data.features[desc_idx].func_id) { + /* + * populate the descriptor with + * the interface features data + */ + ffa_priv_data.features[desc_idx].func_id = + FFA_RXTX_MAP; + ffa_priv_data.features[desc_idx].field1 = + res.a2; + + ffa_info("FFA_RXTX_MAP features data 0x%lx", + res.a2); + + return FFA_ERR_STAT_SUCCESS; + } + + ffa_err("Cannot save FFA_RXTX_MAP features data. Descriptors table full"); + return -ENOBUFS; + } + default: + { + ffa_err("Undefined response function (0x%lx)", + res.a0); + return -EINVAL; + } + } +} + +/** + * ffa_get_rxtx_buffers_pages_cnt - reads from the features data descriptors + * the minimum number of pages in each of the RX/TX + * buffers + * @buf_4k_pages: Pointer to the minimum number of pages + * + * This is the boot time function that returns the minimum number of pages + * in each of the RX/TX buffers + * + * Return: + * + * buf_4k_pages points to the returned number of pages + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +static int ffa_get_rxtx_buffers_pages_cnt(size_t *buf_4k_pages) +{ + struct ffa_features_desc *desc = NULL; + + if (!buf_4k_pages) + return -EINVAL; + + desc = ffa_get_features_desc(FFA_RXTX_MAP); + if (!desc) + return -EINVAL; + + switch (desc->field1) { + case RXTX_4K: + *buf_4k_pages = 1; + break; + case RXTX_16K: + *buf_4k_pages = 4; + break; + case RXTX_64K: + *buf_4k_pages = 16; + break; + default: + ffa_err("RX/TX buffer size not supported"); + return -EINVAL; + } + + return FFA_ERR_STAT_SUCCESS; +} + +/** + * ffa_free_rxtx_buffers - frees the RX/TX buffers + * @buf_4k_pages: the minimum number of pages in each of the RX/TX + * buffers + * + * This is the boot time function used to free the RX/TX buffers + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +static int ffa_free_rxtx_buffers(size_t buf_4k_pages) +{ + efi_status_t free_rxbuf_ret, free_txbuf_ret; + + ffa_info("Freeing RX/TX buffers"); + + free_rxbuf_ret = efi_free_pages(ffa_priv_data.pair.rxbuf, buf_4k_pages); + free_txbuf_ret = efi_free_pages(ffa_priv_data.pair.txbuf, buf_4k_pages); + + if (free_rxbuf_ret != EFI_SUCCESS || free_txbuf_ret != EFI_SUCCESS) { + ffa_err("Failed to free RX/TX buffers (rx: %lu , tx: %lu)", + free_rxbuf_ret, + free_txbuf_ret); + return -EINVAL; + } + + ffa_priv_data.pair.rxbuf = 0; + ffa_priv_data.pair.txbuf = 0; + + return FFA_ERR_STAT_SUCCESS; +} + +/** + * ffa_alloc_rxtx_buffers - allocates the RX/TX buffers + * @buf_4k_pages: the minimum number of pages in each of the RX/TX + * buffers + * + * This is the boot time function used by ffa_map_rxtx_buffers to allocate + * the RX/TX buffers before mapping them + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +static int ffa_alloc_rxtx_buffers(size_t buf_4k_pages) +{ +#if CONFIG_IS_ENABLED(EFI_LOADER) + + efi_status_t efi_ret; + void *virt_txbuf; + void *virt_rxbuf; + + ffa_info("Using %lu 4KB page(s) for RX/TX buffers size", + buf_4k_pages); + + efi_ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES, + EFI_BOOT_SERVICES_DATA, + buf_4k_pages, + &ffa_priv_data.pair.rxbuf); + + if (efi_ret != EFI_SUCCESS) { + ffa_priv_data.pair.rxbuf = 0; + ffa_err("Failure to allocate RX buffer (EFI error: 0x%lx)", + efi_ret); + + return -ENOBUFS; + } + + ffa_info("RX buffer at virtual address 0x%llx", + ffa_priv_data.pair.rxbuf); + + virt_rxbuf = (void *)ffa_priv_data.pair.rxbuf; + + /* + * make sure the buffer is clean before use + */ + memset(virt_rxbuf, 0, buf_4k_pages * SZ_4K); + + efi_ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES, + EFI_RUNTIME_SERVICES_DATA, + buf_4k_pages, + &ffa_priv_data.pair.txbuf); + + if (efi_ret != EFI_SUCCESS) { + efi_free_pages(ffa_priv_data.pair.rxbuf, buf_4k_pages); + ffa_priv_data.pair.rxbuf = 0; + ffa_priv_data.pair.txbuf = 0; + ffa_err("Failure to allocate the TX buffer (EFI error: 0x%lx)" + , efi_ret); + + return -ENOBUFS; + } + + ffa_info("TX buffer at virtual address 0x%llx", + ffa_priv_data.pair.txbuf); + + virt_txbuf = (void *)ffa_priv_data.pair.txbuf; + + /* + * make sure the buffer is clean before use + */ + memset(virt_txbuf, 0, buf_4k_pages * SZ_4K); + + return FFA_ERR_STAT_SUCCESS; + +#else + return -ENOBUFS; +#endif +} + +/** + * ffa_map_rxtx_buffers - FFA_RXTX_MAP handler function + * @buf_4k_pages: the minimum number of pages in each of the RX/TX + * buffers + * + * This is the boot time function that implements FFA_RXTX_MAP FF-A function + * to map the RX/TX buffers + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +static int ffa_map_rxtx_buffers(size_t buf_4k_pages) +{ + int ret; + + FFA_DECLARE_ARGS; + + if (!ffa_priv_data.invoke_ffa_fn) + panic("[FFA] no private data found\n"); + + ret = ffa_alloc_rxtx_buffers(buf_4k_pages); + if (ret != FFA_ERR_STAT_SUCCESS) + return ret; + + a0 = FFA_RXTX_MAP; + a1 = ffa_priv_data.pair.txbuf; + a2 = ffa_priv_data.pair.rxbuf; + a3 = buf_4k_pages; + + ffa_priv_data.invoke_ffa_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res); + + switch (res.a0) { + case FFA_ERROR: + { + switch (((int)res.a2)) { + case FFA_ERR_STAT_INVALID_PARAMETERS: + ffa_err("One or more fields in input parameters is incorrectly encoded"); + ret = -EPERM; + break; + case FFA_ERR_STAT_NO_MEMORY: + ffa_err("Not enough memory"); + ret = -ENOMEM; + break; + case FFA_ERR_STAT_DENIED: + ffa_err("Buffer pair already registered"); + ret = -EACCES; + break; + case FFA_ERR_STAT_NOT_SUPPORTED: + ffa_err("This function is not implemented at this FF-A instance"); + ret = -EOPNOTSUPP; + break; + default: + ffa_err("Undefined error (%d)", + ((int)res.a2)); + ret = -EINVAL; + } + break; + } + case FFA_SUCCESS: + ffa_info("RX/TX buffers mapped"); + return FFA_ERR_STAT_SUCCESS; + default: + ffa_err("Undefined response function (0x%lx)", + res.a0); + ret = -EINVAL; + } + + ffa_free_rxtx_buffers(buf_4k_pages); + + return ret; +} + +/** + * ffa_unmap_rxtx_buffers - FFA_RXTX_UNMAP handler function + * + * This is the boot time function that implements FFA_RXTX_UNMAP FF-A function + * to unmap the RX/TX buffers + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +static int ffa_unmap_rxtx_buffers(void) +{ + FFA_DECLARE_ARGS; + + if (!ffa_priv_data.invoke_ffa_fn) + panic("[FFA] no private data found\n"); + + a0 = FFA_RXTX_UNMAP; + a1 = PREP_SELF_ENDPOINT_ID(ffa_priv_data.id); + + ffa_priv_data.invoke_ffa_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res); + + switch (res.a0) { + case FFA_ERROR: + { + if (((int)res.a2) == FFA_ERR_STAT_NOT_SUPPORTED) + panic("[FFA] FFA_RXTX_UNMAP is not implemented at this FF-A instance\n"); + else if (((int)res.a2) == FFA_ERR_STAT_INVALID_PARAMETERS) + panic("[FFA] There is no buffer pair registered on behalf of the caller\n"); + else + panic("[FFA] Undefined error (%d)\n", ((int)res.a2)); + } + case FFA_SUCCESS: + { + size_t buf_4k_pages = 0; + int ret; + + ret = ffa_get_rxtx_buffers_pages_cnt(&buf_4k_pages); + if (ret != FFA_ERR_STAT_SUCCESS) + panic("[FFA] RX/TX buffers unmapped but failure in getting pages count\n"); + + ret = ffa_free_rxtx_buffers(buf_4k_pages); + if (ret != FFA_ERR_STAT_SUCCESS) + panic("[FFA] RX/TX buffers unmapped but failure in freeing the memory\n"); + + ffa_info("RX/TX buffers unmapped and memory freed"); + + return FFA_ERR_STAT_SUCCESS; + } + default: + panic("[FFA] Undefined response function (0x%lx)", res.a0); + } +} + +/** + * ffa_release_rx_buffer - FFA_RX_RELEASE handler function + * + * This is the boot time function that invokes FFA_RX_RELEASE FF-A function + * to release the ownership of the RX buffer + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +static int ffa_release_rx_buffer(void) +{ + FFA_DECLARE_ARGS; + + if (!ffa_priv_data.invoke_ffa_fn) + panic("[FFA] no private data found\n"); + + a0 = FFA_RX_RELEASE; + + ffa_priv_data.invoke_ffa_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res); + + switch (res.a0) { + case FFA_ERROR: + { + if (((int)res.a2) == FFA_ERR_STAT_NOT_SUPPORTED) + panic("[FFA] FFA_RX_RELEASE is not implemented at this FF-A instance\n"); + else if (((int)res.a2) == FFA_ERR_STAT_DENIED) + panic("[FFA] Caller did not have ownership of the RX buffer\n"); + else + panic("[FFA] Undefined error (%d)\n", ((int)res.a2)); + } + case FFA_SUCCESS: + return FFA_ERR_STAT_SUCCESS; + + default: + panic("[FFA] Undefined response function (0x%lx)\n", res.a0); + } +} + +/** + * ffa_uuid_are_identical - checks whether two given UUIDs are identical + * @uuid1: first UUID + * @uuid2: second UUID + * + * This is a boot time function used by ffa_read_partitions_info to search + * for a UUID in the partitions descriptors table + * + * Return: + * + * 1 when UUIDs match. Otherwise, 0 + */ +int ffa_uuid_are_identical(const union ffa_partition_uuid *uuid1, + const union ffa_partition_uuid *uuid2) +{ + if (!uuid1 || !uuid2) + return 0; + + return (!memcmp(uuid1, uuid2, sizeof(union ffa_partition_uuid))); +} + +/** + * ffa_read_partitions_info - reads the data queried by FFA_PARTITION_INFO_GET + * and saves it in the private structure + * @count: The number of partitions queried + * @part_uuid: Pointer to the partition(s) UUID + * + * This is the boot time function that reads the partitions information + * returned by the FFA_PARTITION_INFO_GET and saves it in the private + * data structure. + * + * Return: + * + * The private data structure is updated with the partition(s) information + * FFA_ERR_STAT_SUCCESS is returned on success. Otherwise, failure + */ +static int ffa_read_partitions_info(u32 count, union ffa_partition_uuid *part_uuid) +{ + if (!count) { + ffa_err("No partition detected"); + return -ENODATA; + } + + ffa_info("Reading partitions data from the RX buffer"); + +#if CONFIG_IS_ENABLED(EFI_LOADER) + + if (!part_uuid) { + /* + * querying information of all partitions + */ + u64 data_pages; + u64 data_bytes; + efi_status_t efi_ret; + size_t buf_4k_pages = 0; + u32 desc_idx; + struct ffa_partition_info *parts_info; + int ret; + + data_bytes = count * sizeof(struct ffa_partition_desc); + data_pages = efi_size_in_pages(data_bytes); + + /* + * get the RX buffer size in pages + */ + ret = ffa_get_rxtx_buffers_pages_cnt(&buf_4k_pages); + if (ret != FFA_ERR_STAT_SUCCESS) { + ffa_err("Can not get the RX buffer size (error %d)", ret); + return ret; + } + + if (data_pages > buf_4k_pages) { + ffa_err("Partitions data size exceeds the RX buffer size:"); + ffa_err(" Sizes in pages: data %llu , RX buffer %lu ", + data_pages, + buf_4k_pages); + + return -ENOMEM; + } + + efi_ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES, + EFI_RUNTIME_SERVICES_DATA, + data_pages, + (u64 *)&ffa_priv_data.partitions.descs); + + if (efi_ret != EFI_SUCCESS) { + ffa_priv_data.partitions.descs = NULL; + + ffa_err("Cannot allocate partitions data buffer (EFI error 0x%lx)", + efi_ret); + + return -ENOBUFS; + } + + /* + * make sure the buffer is clean before use + */ + memset(ffa_priv_data.partitions.descs, 0, + data_pages * SZ_4K); + + parts_info = (struct ffa_partition_info *)ffa_priv_data.pair.rxbuf; + + for (desc_idx = 0 ; desc_idx < count ; desc_idx++) { + ffa_priv_data.partitions.descs[desc_idx].info = + parts_info[desc_idx]; + + ffa_info("Partition ID %x : info cached", + ffa_priv_data.partitions.descs[desc_idx].info.id); + } + + ffa_priv_data.partitions.count = count; + + ffa_info("%d partition(s) found and cached", count); + + } else { + u32 rx_desc_idx, cached_desc_idx; + struct ffa_partition_info *parts_info; + u8 desc_found; + + parts_info = (struct ffa_partition_info *)ffa_priv_data.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 < ffa_priv_data.partitions.count; + cached_desc_idx++) { + /* + * save the UUID + */ + if (ffa_priv_data.partitions.descs[cached_desc_idx].info.id == + parts_info[rx_desc_idx].id) { + ffa_priv_data.partitions.descs[cached_desc_idx].UUID = + *part_uuid; + + desc_found = 1; + break; + } + } + + if (!desc_found) + return -ENODATA; + } + } +#else +#warning "arm_ffa: reading FFA_PARTITION_INFO_GET data not implemented" +#endif + + return FFA_ERR_STAT_SUCCESS; +} + +/** + * ffa_query_partitions_info - invokes FFA_PARTITION_INFO_GET + * and saves partitions data + * @part_uuid: Pointer to the partition(s) UUID + * @pcount: Pointer to the number of partitions variable filled when querying + * + * This is the boot time function that executes the FFA_PARTITION_INFO_GET + * to query the partitions data. Then, it calls ffa_read_partitions_info + * to save the data in the private data structure. + * + * 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 + * FFA_ERR_STAT_SUCCESS is returned on success. Otherwise, failure + */ +static int ffa_query_partitions_info(union ffa_partition_uuid *part_uuid, + u32 *pcount) +{ + unsigned long a0 = 0; + union ffa_partition_uuid query_uuid = {0}; + unsigned long a5 = 0; + unsigned long a6 = 0; + unsigned long a7 = 0; + struct arm_smccc_res res = {0}; + + if (!ffa_priv_data.invoke_ffa_fn) + panic("[FFA] no private data found\n"); + + a0 = FFA_PARTITION_INFO_GET; + + /* + * 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; + } + + ffa_priv_data.invoke_ffa_fn(a0, query_uuid.words.a1, query_uuid.words.a2, + query_uuid.words.a3, query_uuid.words.a4, + a5, a6, a7, &res); + + switch (res.a0) { + case FFA_ERROR: + { + switch (((int)res.a2)) { + case FFA_ERR_STAT_INVALID_PARAMETERS: + ffa_err("Unrecognized UUID"); + return -EPERM; + case FFA_ERR_STAT_NO_MEMORY: + ffa_err("Results cannot fit in RX buffer of the caller"); + return -ENOMEM; + case FFA_ERR_STAT_DENIED: + ffa_err("Callee is not in a state to handle this request"); + return -EACCES; + case FFA_ERR_STAT_NOT_SUPPORTED: + ffa_err("This function is not implemented at this FF-A instance"); + return -EOPNOTSUPP; + case FFA_ERR_STAT_BUSY: + ffa_err("RX buffer of the caller is not free"); + return -EBUSY; + default: + ffa_err("Undefined error (%d)", ((int)res.a2)); + return -EINVAL; + } + } + case 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(res.a2, part_uuid); + if (ret) + ffa_err("Failed to read partition(s) data , error (%d)", ret); + } + + /* + * return the SP count + */ + if (part_uuid) { + if (!ret) + *pcount = res.a2; + else + *pcount = 0; + } + /* + * 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 secure world + */ + ret = ffa_release_rx_buffer(); + + if (!part_uuid && !res.a2) { + ffa_err("[FFA] no partition installed in the system"); + return -ENODEV; + } + + return ret; + } + default: + ffa_err("Undefined response function (0x%lx)", res.a0); + return -EINVAL; + } +} + +/** + * ffa_get_partitions_info - FFA_PARTITION_INFO_GET handler function + * @func_data: Pointer to the FF-A function arguments container structure. + * The passed arguments: + * Mode 1: When getting from the driver the number of + * secure partitions: + * @data0_size: UUID size + * @data0: pointer to the UUID (little endian) + * @data1_size: size of the number of partitions + * variable + * @data1: pointer to the number of partitions + * variable. The variable will be set + * by the driver + * Mode 2: When requesting the driver to return the + * partitions information: + * @data0_size: UUID size + * @data0: pointer to the UUID (little endian) + * @data1_size: size of the SPs information buffer + * @data1: pointer to SPs information buffer + * (allocated by the client). + * The buffer will be filled by the driver + * + * This is the boot time function that queries the secure partition data from + * the private data structure. If not found, it invokes 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 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(). + * + * When invoked through a client request, ffa_get_partitions_info should be + * called twice. First call is to get from the driver the number of secure + * partitions (SPs) associated to a particular UUID. + * Then, the caller (client) allocates the buffer to host the SPs data and + * issues a 2nd call. Then, the driver fills the SPs data in the pre-allocated + * buffer. + * + * To achieve the mechanism described above, ffa_get_partitions_info uses the + * following functions: + * ffa_read_partitions_info + * ffa_query_partitions_info + * + * Return: + * + * @data1: When pointing to the number of partitions variable, the number is + * set by the driver. + * When pointing to the partitions information buffer, the buffer will be + * filled by the driver. + * + * On success FFA_ERR_STAT_SUCCESS is returned. Otherwise, failure + */ +static int ffa_get_partitions_info(struct ffa_interface_data *func_data) +{ + /* + * fill_data: + * 0: return the SP count + * 1: fill SP data and return it to the caller + * -1: undefined mode + */ + int fill_data = -1; + u32 desc_idx, client_desc_idx; + union ffa_partition_uuid *part_uuid; + u32 client_desc_max_cnt; + u32 parts_found = 0; + + if (!func_data) { + ffa_err("No function data provided"); + return -EINVAL; + } + + if (!ffa_priv_data.partitions.count || !ffa_priv_data.partitions.descs) + panic("[FFA] No partition installed\n"); + + if (func_data->data0_size == sizeof(union ffa_partition_uuid) && + func_data->data0 && + func_data->data1_size == sizeof(u32) && + func_data->data1) { + /* + * data0 (in): pointer to UUID + * data1 (in): pointer to SP count + * Out: SP count returned in the count variable pointed by data1 + */ + + fill_data = 0; + + ffa_info("Preparing for checking partitions count"); + + } else if ((func_data->data0_size == sizeof(union ffa_partition_uuid)) && + func_data->data0 && + (func_data->data1_size >= sizeof(struct ffa_partition_info)) && + !(func_data->data1_size % sizeof(struct ffa_partition_info)) && + func_data->data1) { + /* + * data0 (in): pointer to UUID + * data1 (in): pointer to SPs descriptors buffer + * (created by the client) + * Out: SPs descriptors returned in the buffer + * pointed by data1 + */ + + fill_data = 1; + + client_desc_idx = 0; + + /* + * number of empty descriptors preallocated by the caller + */ + client_desc_max_cnt = + func_data->data1_size / sizeof(struct ffa_partition_info); + + ffa_info("Preparing for filling partitions info"); + + } else { + ffa_err("Invalid function arguments provided"); + return -EINVAL; + } + + part_uuid = (union ffa_partition_uuid *)func_data->data0; + + ffa_info("Searching partitions using the provided UUID"); + + /* + * search in the cached partitions + */ + for (desc_idx = 0; + desc_idx < ffa_priv_data.partitions.count; + desc_idx++) { + if (ffa_uuid_are_identical(&ffa_priv_data.partitions.descs[desc_idx].UUID, + part_uuid)) { + ffa_info("Partition ID %x matches the provided UUID", + ffa_priv_data.partitions.descs[desc_idx].info.id); + + parts_found++; + + if (fill_data) { + /* + * trying to fill the partition info in data1 + */ + + if (client_desc_idx < client_desc_max_cnt) { + ((struct ffa_partition_info *) + func_data->data1)[client_desc_idx++] = + ffa_priv_data.partitions.descs[desc_idx].info; + continue; + } + + ffa_err("Failed to fill the current descriptor client buffer full"); + return -ENOBUFS; + } + } + } + + if (!parts_found) { + int ret; + + ffa_info("No partition found. Querying framework ..."); + + ret = ffa_query_partitions_info(part_uuid, &parts_found); + + if (ret == FFA_ERR_STAT_SUCCESS) { + if (!fill_data) { + *((u32 *)func_data->data1) = parts_found; + + ffa_info("Number of partition(s) found matching the UUID: %d", + parts_found); + } else { + /* + * we want to read SPs info + */ + + /* + * If SPs data filled, retry searching SP info again + */ + if (parts_found) + ret = ffa_get_partitions_info(func_data); + else + ret = -ENODATA; + } + } + + return ret; + } + + /* partition(s) found */ + if (!fill_data) + *((u32 *)func_data->data1) = parts_found; + + return FFA_ERR_STAT_SUCCESS; +} + +/** + * ffa_cache_partitions_info - Queries and saves all secure partitions data + * + * This is a boot time function that invokes 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 the + * resident private data structure and we keep the UUID field empty + * (in FF-A 1.0 UUID is not provided by the partition descriptor) + * + * This function is called at the device probing level. + * ffa_cache_partitions_info uses ffa_query_partitions_info to get the data + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +static int ffa_cache_partitions_info(void) +{ + return ffa_query_partitions_info(NULL, NULL); +} + +/** + * ffa_msg_send_direct_req - FFA_MSG_SEND_DIRECT_{REQ,RESP} handler function + * @func_data: Pointer to the FF-A function arguments container structure. + * The passed arguments: + * @data0_size: partition ID size + * @data0: pointer to the partition ID + * @data1_size: exchanged data size + * @data1: pointer to the data buffer preallocated by + * the client (in/out) + * + * This is the runtime function that 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 20 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: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +static int __ffa_runtime ffa_msg_send_direct_req(struct ffa_interface_data + *func_data) +{ + u16 dst_part_id; + unsigned long a0 = 0; + unsigned long a1 = 0; + unsigned long a2 = 0; + struct ffa_send_direct_data *msg; + struct arm_smccc_res res = {0}; + + if (!ffa_priv_data.invoke_ffa_fn) + return -ENODEV; + + if (!func_data) + return -EINVAL; + + /* No partition installed */ + if (!ffa_priv_data.partitions.count || !ffa_priv_data.partitions.descs) + return -ENODEV; + + /* Undefined interface parameters */ + if (func_data->data0_size != sizeof(u16) || + !func_data->data0 || + func_data->data1_size != FFA_MSG_SEND_DIRECT_MAX_SIZE || + !func_data->data1) + return -EINVAL; + + dst_part_id = *((u16 *)func_data->data0); + msg = func_data->data1; + + a0 = FFA_MSG_SEND_DIRECT_REQ; + + a1 = PREP_SELF_ENDPOINT_ID(ffa_priv_data.id) | + PREP_PART_ENDPOINT_ID(dst_part_id); + + ffa_priv_data.invoke_ffa_fn(a0, a1, a2, + msg->a3, + msg->a4, + msg->a5, + msg->a6, + msg->a7, + &res); + + while (res.a0 == FFA_INTERRUPT) + ffa_priv_data.invoke_ffa_fn(FFA_RUN, res.a1, + 0, 0, 0, 0, 0, 0, + &res); + + switch (res.a0) { + case FFA_ERROR: + { + switch (((int)res.a2)) { + case FFA_ERR_STAT_INVALID_PARAMETERS: + /* Invalid endpoint ID or non-zero reserved register */ + return -EPERM; + case FFA_ERR_STAT_ABORTED: + /* Message target ran into unexpected error and has aborted */ + return -ECONNABORTED; + case FFA_ERR_STAT_DENIED: + /* Callee is not in a state to handle this request */ + return -EACCES; + case FFA_ERR_STAT_NOT_SUPPORTED: + /* This function is not implemented at this FF-A instance */ + return -EOPNOTSUPP; + case FFA_ERR_STAT_BUSY: + /* Message target is busy */ + return -EBUSY; + default: + /* Undefined error */ + return -ENXIO; + } + } + case FFA_SUCCESS: + + /* Message sent with no response */ + return FFA_ERR_STAT_SUCCESS; + + case FFA_MSG_SEND_DIRECT_RESP: + + /* + * Message sent with response + * extract the 32-bit wide return data + */ + msg->a3 = (u32)res.a3; + msg->a4 = (u32)res.a4; + msg->a5 = (u32)res.a5; + msg->a6 = (u32)res.a6; + msg->a7 = (u32)res.a7; + + return FFA_ERR_STAT_SUCCESS; + + default: + /* Undefined response function */ + return -ENOENT; + } +} + +/** + * invoke_ffa_drv_api - The driver dispatcher function + * @func_id: The FF-A function to be used + * @func_data: Pointer to the FF-A function arguments container + * structure. This also includes pointers to the + * returned data needed by clients. + * The dispatcher is a runtime function that selects the FF-A function handler + * based on the input FF-A function ID. + * The input arguments are passed to the handler function. + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +int __ffa_runtime invoke_ffa_drv_api(u32 func_id, + struct ffa_interface_data *func_data) +{ + if (!ffa_priv_data.dev) + return -ENODEV; + + switch (func_id) { + case FFA_PARTITION_INFO_GET: + return ffa_get_partitions_info(func_data); + case FFA_RXTX_UNMAP: + return ffa_unmap_rxtx_buffers(); + case FFA_MSG_SEND_DIRECT_REQ: + return ffa_msg_send_direct_req(func_data); + default: + /* Undefined FF-A interface */ + return -EINVAL; + } +} + +/** + * ffa_init_private_data - Initialization of the private data + * @dev: the arm_ffa device + * + * This boot time function reads data from the platform data structure + * and populates the private data structure + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +static int ffa_init_private_data(struct udevice *dev) +{ + struct ffa_pdata *pdata = dev_get_plat(dev); + + ffa_priv_data.conduit = pdata->conduit; + + if (ffa_priv_data.conduit == FFA_CONDUIT_SMC) { + ffa_priv_data.invoke_ffa_fn = arm_ffa_smccc_smc; + } else { + ffa_err("Undefined FF-A conduit (%d)", ffa_priv_data.conduit); + return -EINVAL; + } + + ffa_info("Conduit is %s", + ((ffa_priv_data.conduit == FFA_CONDUIT_SMC) ? + "SMC" : "NOT SUPPORTED")); + + return FFA_ERR_STAT_SUCCESS; +} + +/** + * ffa_bind - The driver bind function + * @dev: the arm_ffa device + * + * Called when the driver is bound with the device based on the DTB node. + * The private data structure is cleared before use. + * This makes sure that when the device added/removed multiple times the previous + * private structure is cleared. + * + * Return: + * + * FFA_ERR_STAT_SUCCESS + */ +static int ffa_bind(struct udevice *dev) +{ + memset(&ffa_priv_data, 0, sizeof(ffa_priv_data)); + + return FFA_ERR_STAT_SUCCESS; +} + +/** + * ffa_probe - The driver probe function + * @dev: the arm_ffa device + * + * Probing is done at boot time and triggered by the uclass device discovery. + * At probe level the following actions are done: + * - initialization of the driver private data structure + * - querying from secure world the FF-A framework version + * - querying from secure world the u-boot endpoint ID + * - querying from secure world the supported features of the specified FF-A calls + * - mapping the RX/TX buffers + * - querying from secure world all the partitions information + * + * All data queried from secure world is saved in the resident private data structure. + * + * The probe will fail if either FF-A framework is not detected or the + * FF-A requests are not behaving correctly. This ensures that the + * driver is not installed and its operations are not exported to the clients. + * However, once the driver is successfully probed and an FF-A anomaly is + * detected when clients invoke the driver operations, the driver cause + * u-boot to panic because the client would not know what to do in such conditions. + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +static int ffa_probe(struct udevice *dev) +{ + int ret; + size_t buf_4k_pages = 0; + + ret = ffa_init_private_data(dev); + + if (ret != FFA_ERR_STAT_SUCCESS) + return ret; + + ret = ffa_get_version(); + + if (ret != FFA_ERR_STAT_SUCCESS) + return ret; + + ret = ffa_get_endpoint_id(); + + if (ret != FFA_ERR_STAT_SUCCESS) + return ret; + + ret = ffa_get_rxtx_map_features(); + + if (ret != FFA_ERR_STAT_SUCCESS) + return ret; + + ret = ffa_get_rxtx_buffers_pages_cnt(&buf_4k_pages); + + if (ret != FFA_ERR_STAT_SUCCESS) + return ret; + + ret = ffa_map_rxtx_buffers(buf_4k_pages); + + if (ret != FFA_ERR_STAT_SUCCESS) + return ret; + + ret = ffa_cache_partitions_info(); + + if (ret != FFA_ERR_STAT_SUCCESS) { + ffa_free_rxtx_buffers(buf_4k_pages); + return ret; + } + + return FFA_ERR_STAT_SUCCESS; +} + +/** + * ffa_of_to_plat - Reads the device tree node + * @dev: the arm_ffa device + * + * This boot time function reads data from the device tree node and populates + * the platform data structure + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +static int ffa_of_to_plat(struct udevice *dev) +{ + struct ffa_pdata *pdata = dev_get_plat(dev); + const char *conduit = NULL; + + conduit = dev_read_string(dev, "method"); + + if (!conduit) { + ffa_err("Failure to read the conduit from device tree"); + return -EINVAL; + } + + if (strcmp("smc", conduit)) { + ffa_err("Unsupported conduit"); + return -EINVAL; + } + + pdata->conduit = FFA_CONDUIT_SMC; + + return FFA_ERR_STAT_SUCCESS; +} + +/** + * ffa_drv_ops - The driver operations runtime structure + * @invoke_func: The driver dispatcher + */ +struct ffa_ops __ffa_runtime_data ffa_drv_ops = { + .invoke_func = invoke_ffa_drv_api +}; + +/** + * ffa_device_get_ops - driver operations getter + * + * Return: + * This runtime function returns a pointer to the driver operations structure + */ +const struct ffa_ops * __ffa_runtime ffa_device_get_ops(void) +{ + return &ffa_drv_ops; +} + +/** + * Defining the device tree compatible string + */ + +static const struct udevice_id ffa_match_id[] = { + {"arm,ffa", 0}, + {}, +}; + +/** + * Declaring the arm_ffa driver under UCLASS_FFA + */ + +U_BOOT_DRIVER(arm_ffa) = { + .name = "arm_ffa", + .of_match = ffa_match_id, + .id = UCLASS_FFA, + .of_to_plat = ffa_of_to_plat, + .probe = ffa_probe, + .bind = ffa_bind, + .plat_auto = sizeof(struct ffa_pdata), +}; diff --git a/include/arm_ffa.h b/include/arm_ffa.h new file mode 100644 index 0000000000..5a2151f8e4 --- /dev/null +++ b/include/arm_ffa.h @@ -0,0 +1,190 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __ARM_FFA_H +#define __ARM_FFA_H + +#include <linux/arm-smccc.h> +#include <linux/printk.h> + +/* + * This header is public. It can be used by clients to access + * data structures and definitions they need + */ + +/* + * Macros for displaying logs + */ + +#define ffa_info(fmt, ...) pr_info("[FFA] " fmt "\n", ##__VA_ARGS__) +#define ffa_err(fmt, ...) pr_err("[FFA] " fmt "\n", ##__VA_ARGS__) + +/* + * The driver operations success error code + */ +#define FFA_ERR_STAT_SUCCESS (0) + +#if CONFIG_IS_ENABLED(EFI_LOADER) + +#include <efi_loader.h> + +/* + * __ffa_runtime_data and __ffa_runtime - controls whether data/code are + * available after calling the EFI ExitBootServices service. + * Data/code tagged with these keywords are resident (available at boot time and + * at runtime) + */ + +#define __ffa_runtime_data __efi_runtime_data +#define __ffa_runtime __efi_runtime + +#else + +#define __ffa_runtime_data +#define __ffa_runtime + +#endif + +/* + * 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_VERSION FFA_SMC_32(0x63) +#define FFA_ID_GET FFA_SMC_32(0x69) +#define FFA_FEATURES FFA_SMC_32(0x64) +#define FFA_PARTITION_INFO_GET FFA_SMC_32(0x68) +#define FFA_RXTX_MAP FFA_SMC_32(0x66) +#define FFA_RXTX_UNMAP FFA_SMC_32(0x67) +#define FFA_RX_RELEASE FFA_SMC_32(0x65) +#define FFA_MSG_SEND_DIRECT_REQ FFA_SMC_32(0x6F) +#define FFA_MSG_SEND_DIRECT_RESP FFA_SMC_32(0x70) +#define FFA_RUN FFA_SMC_32(0x6D) +#define FFA_ERROR FFA_SMC_32(0x60) +#define FFA_SUCCESS FFA_SMC_32(0x61) +#define FFA_INTERRUPT FFA_SMC_32(0x62) + +/* + * 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 __packed 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_send_direct_data - Data structure hosting the data + * used by FFA_MSG_SEND_DIRECT_{REQ,RESP} + * @a3-a7: Data read/written from/to w3-w7 registers + * + * Data structure containing the data to be sent by FFA_MSG_SEND_DIRECT_REQ + * or read from FFA_MSG_SEND_DIRECT_RESP + */ +struct __packed ffa_send_direct_data { + u32 a3; /* w3 */ + u32 a4; /* w4 */ + u32 a5; /* w5 */ + u32 a6; /* w6 */ + u32 a7; /* w7 */ +}; + +#define FFA_MSG_SEND_DIRECT_MAX_SIZE (sizeof(struct ffa_send_direct_data)) + +/* UUID data size */ +#define UUID_SIZE (16) + +/* + * union ffa_partition_uuid - Data union hosting the UUID + * transmitted by FFA_PARTITION_INFO_GET + * @words: data structure giving 32-bit words access to the UUID data + * @bytes: data structure giving byte access to the UUID data + * + * The structure holds little-endian UUID data. + */ +union ffa_partition_uuid { + struct __packed words { + u32 a1; /* w1 */ + u32 a2; /* w2 */ + u32 a3; /* w3 */ + u32 a4; /* w4 */ + } words; + u8 bytes[UUID_SIZE]; +}; + +/** + * struct ffa_interface_data - generic FF-A interface data structure used to exchange + * data between user layers and the driver + * @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 + * + * Using this structure user layers can pass various types of data with different sizes. + * The driver internal functions can detect the nature of this data, verfy compliance + * then execute the request when appropriate. + */ +struct ffa_interface_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 */ +}; + +/** + * struct ffa_ops - The driver operations structure + * @invoke_func: function pointer to the invoke function + * + * The data structure providing all the operations supported by the driver. + * This structure is resident. + */ +struct ffa_ops { + /* the driver dispatcher */ + int (*invoke_func)(u32 func_id, struct ffa_interface_data *func_data); +}; + +/** + * The device driver and the Uclass driver public functions + */ + +/** + * ffa_get_invoke_func - performs a call to the FF-A driver dispatcher + */ +int __ffa_runtime ffa_get_invoke_func(u32 func_id, + struct ffa_interface_data *func_data); + +/** + * ffa_device_get_ops - driver operations getter + */ +const struct ffa_ops * __ffa_runtime ffa_device_get_ops(void); + +/** + * ffa_get_device - probes the arm_ffa device + */ +int ffa_get_device(void); + +/** + * ffa_init_device - probes the arm_ffa device + */ +int ffa_init_device(void); +#endif diff --git a/include/arm_ffa_helper.h b/include/arm_ffa_helper.h new file mode 100644 index 0000000000..76f7a9fd29 --- /dev/null +++ b/include/arm_ffa_helper.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __ARM_FFA_HELPER_H +#define __ARM_FFA_HELPER_H + +#include <arm_ffa.h> + +/* + * This header is public. Including this header provides all data structures + * and definitions needed by clients to use the FF-A transport driver + * + * It also provides helper functions allowing to pass data and invoke FF-A functions + */ + +/** + * ffa_helper_get_partitions_info - Wrapper function for FFA_PARTITION_INFO_GET + */ +int ffa_helper_get_partitions_info(struct ffa_interface_data *func_data); + +/** + * ffa_helper_unmap_rxtx_buffers - Wrapper function for FFA_RXTX_UNMAP + */ +int ffa_helper_unmap_rxtx_buffers(void); + +/** + * ffa_helper_msg_send_direct_req - Wrapper function for + * FFA_MSG_SEND_DIRECT_{REQ,RESP} + */ +int __ffa_runtime ffa_helper_msg_send_direct_req(struct ffa_interface_data + *func_data); + +/** + * ffa_helper_init_device - Wrapper function for probing the arm_ffa device + */ +int ffa_helper_init_device(void); + +/** + * ffa_uuid_str_to_bin - Converts a big endian UUID string to a little endian buffer + */ +int ffa_uuid_str_to_bin(const char *uuid_str, unsigned char *uuid_bin); +#endif diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 0e26e1d138..a1181b8f48 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -52,6 +52,7 @@ enum uclass_id { UCLASS_EFI_MEDIA, /* Devices provided by UEFI firmware */ UCLASS_ETH, /* Ethernet device */ UCLASS_ETH_PHY, /* Ethernet PHY device */ + UCLASS_FFA, /* Arm Firmware Framework for Armv8-A */ UCLASS_FIRMWARE, /* Firmware */ UCLASS_FS_FIRMWARE_LOADER, /* Generic loader */ UCLASS_GPIO, /* Bank of general-purpose I/O pins */ diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h index 7f2be23394..d58c7c104b 100644 --- a/include/linux/arm-smccc.h +++ b/include/linux/arm-smccc.h @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2015, Linaro Limited + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */ #ifndef __LINUX_ARM_SMCCC_H #define __LINUX_ARM_SMCCC_H @@ -57,13 +59,17 @@ #include <linux/types.h> /** * struct arm_smccc_res - Result from SMC/HVC call - * @a0-a3 result values from registers 0 to 3 + * @a0-a7 result values from registers 0 to 7 */ struct arm_smccc_res { unsigned long a0; unsigned long a1; unsigned long a2; unsigned long a3; + unsigned long a4; + unsigned long a5; + unsigned long a6; + unsigned long a7; };
/** @@ -113,6 +119,26 @@ asmlinkage void __arm_smccc_hvc(unsigned long a0, unsigned long a1, unsigned long a5, unsigned long a6, unsigned long a7, struct arm_smccc_res *res, struct arm_smccc_quirk *quirk);
+#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) +/** + * __arm_ffa_smccc_smc() - make SMC calls used for FF-A transport + * @a0-a7: arguments passed in 64-bit registers x0 to x7 + * @res: result values from 64-bit registers x0 to x7 + * + * This function is used to make SMC calls following SMC32 Calling Convention. + * The content of the supplied parameters is copied to registers x0 to x7 prior + * to the SMC instruction. The SMC call return data is 32-bit data read from + * registers x0 tp x7. + */ +asmlinkage void __arm_ffa_smccc_smc(unsigned long a0, unsigned long a1, + unsigned long a2, unsigned long a3, unsigned long a4, + unsigned long a5, unsigned long a6, unsigned long a7, + struct arm_smccc_res *res); + +#define arm_ffa_smccc_smc __arm_ffa_smccc_smc + +#endif + #define arm_smccc_smc(...) __arm_smccc_smc(__VA_ARGS__, NULL)
#define arm_smccc_smc_quirk(...) __arm_smccc_smc(__VA_ARGS__) diff --git a/lib/Kconfig b/lib/Kconfig index 3c6fa99b1a..473821b882 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -810,6 +810,7 @@ config SMBIOS_PARSER source lib/efi/Kconfig source lib/efi_loader/Kconfig source lib/optee/Kconfig +source lib/arm-ffa/Kconfig
config TEST_FDTDEC bool "enable fdtdec test" diff --git a/lib/Makefile b/lib/Makefile index 11b03d1cbe..8e6fad6130 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_EFI) += efi/ obj-$(CONFIG_EFI_LOADER) += efi_driver/ obj-$(CONFIG_EFI_LOADER) += efi_loader/ obj-$(CONFIG_CMD_BOOTEFI_SELFTEST) += efi_selftest/ +obj-$(CONFIG_ARM_FFA_TRANSPORT_HELPERS) += arm-ffa/ obj-$(CONFIG_LZMA) += lzma/ obj-$(CONFIG_BZIP2) += bzip2/ obj-$(CONFIG_TIZEN) += tizen/ diff --git a/lib/arm-ffa/Kconfig b/lib/arm-ffa/Kconfig new file mode 100644 index 0000000000..79acbc5a8f --- /dev/null +++ b/lib/arm-ffa/Kconfig @@ -0,0 +1,6 @@ +config ARM_FFA_TRANSPORT_HELPERS + bool "Enable interface helpers for Arm Firmware Framework for Armv8-A" + depends on ARM_FFA_TRANSPORT + help + User layers call FF-A interfaces using helper functions which + pass the data and the FF-A function ID to the low level driver diff --git a/lib/arm-ffa/Makefile b/lib/arm-ffa/Makefile new file mode 100644 index 0000000000..cba625fde4 --- /dev/null +++ b/lib/arm-ffa/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2022 Abdellatif El Khlifi abdellatif.elkhlifi@arm.com +# + +# This file only gets included when CONFIG_ARM_FFA_TRANSPORT_HELPERS is set + +obj-y += arm_ffa_helper.o diff --git a/lib/arm-ffa/arm_ffa_helper.c b/lib/arm-ffa/arm_ffa_helper.c new file mode 100644 index 0000000000..67a3a4e9ab --- /dev/null +++ b/lib/arm-ffa/arm_ffa_helper.c @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <arm_ffa_helper.h> +#include <uuid.h> + +/** + * ffa_helper_get_partitions_info - Wrapper function for FFA_PARTITION_INFO_GET + * + * @func_data: Pointer to the FF-A function arguments container + * structure. + * The passed arguments: + * Mode 1: When getting from the driver the number of + * secure partitions: + * @data0_size: UUID size + * @data0: pointer to the UUID (little endian) + * @data1_size: size of the number of partitions + * variable + * @data1: pointer to the number of partitions + * variable. The variable will be set + * by the driver + * Mode 2: When requesting the driver to return the + * partitions information: + * @data0_size: UUID size + * @data0: pointer to the UUID (little endian) + * @data1_size: size of the SPs information buffer + * @data1: pointer to SPs information buffer + * (allocated by the client). + * The buffer will be filled by the driver + * + * This is the boot time function used by clients who wants to get from secure + * world the partition(s) information. + * + * 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 client should use + * ffa_helper_get_partitions_info to pass the UUID information to the driver + * which uses PARTITION_INFO_GET to obtain the partition(s) information. + * + * ffa_helper_get_partitions_info should be called twice. First call is to get + * from the driver the number of secure partitions (SPs) associated to a + * particular UUID. Then, the caller (client) allocates the buffer to host the + * SPs data and issues a 2nd call. Then, the driver fills the SPs data in the + * pre-allocated buffer. + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +int ffa_helper_get_partitions_info(struct ffa_interface_data *func_data) +{ + return ffa_get_invoke_func(FFA_PARTITION_INFO_GET, func_data); +} + +/** + * ffa_helper_unmap_rxtx_buffers - Wrapper function for FFA_RXTX_UNMAP + * + * This is the boot time function that allows clients to unmap the RX/TX + * buffers + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +int ffa_helper_unmap_rxtx_buffers(void) +{ + return ffa_get_invoke_func(FFA_RXTX_UNMAP, NULL); +} + +/** + * ffa_helper_msg_send_direct_req - Wrapper function for + * FFA_MSG_SEND_DIRECT_{REQ,RESP} + * @func_data: Pointer to the FF-A function arguments container structure. + * The passed arguments: + * @data0_size: partition ID size + * @data0: pointer to the partition ID + * @data1_size: exchanged data size + * @data1: pointer to the data buffer preallocated by the client (in/out) + * + * This is the runtime function that allows clients to send data to the secure + * world partitions. The arm_ffa driver uses FFA_MSG_SEND_DIRECT_REQ to send the + * data to the secure partition. The response from the secure partition is + * handled internally by the driver using FFA_MSG_SEND_DIRECT_RESP and returned + * to ffa_helper_msg_send_direct_req through @func_data + * + * The maximum size of the data that can be exchanged is 20 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} + * + * The client should pre-allocate a buffer pointed by @data1 which the size + * is sizeof(struct ffa_send_direct_data) + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +int __ffa_runtime ffa_helper_msg_send_direct_req(struct ffa_interface_data + *func_data) +{ + return ffa_get_invoke_func(FFA_MSG_SEND_DIRECT_REQ, func_data); +} + +/** + * ffa_helper_init_device - Wrapper function for probing the arm_ffa device + * + * This boot time function should be called to probe the arm_ffa device so + * it becomes ready for use. + * To achieve that, this function is called automatically at initcalls + * level (after u-boot relocation). + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +int ffa_helper_init_device(void) +{ + return ffa_init_device(); +} + +/** + * ffa_uuid_str_to_bin - Converts a big endian UUID string to a little endian buffer + * @uuid_str: UUID string in big endian format (36 bytes wide + '/0') + * @uuid_bin: preallocated 16 bytes UUID buffer in little endian format + * + * UUID binary format used by the FF-A framework (16 bytes): + * + * [LSB] 4B-2B-2B-2B-6B (little endian data fields) + * + * UUID string is 36 length of characters (36 bytes): + * + * xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + * be be be be be + * + * where x is a hexadecimal character. Fields are separated by '-'s. + * When converting to a binary UUID, these endianness rules apply: + * be: means the field in the string is considered a big endian hex number + * and should be converted to little endian binary format + * + * Return: + * + * uuid_bin filled with little endian UUID data + * On success 0 is returned. Otherwise, failure code. + */ +int ffa_uuid_str_to_bin(const char *uuid_str, unsigned char *uuid_bin) +{ + u16 tmp16 = 0; + u32 tmp32 = 0; + u64 tmp64 = 0; + + if (!uuid_str_valid(uuid_str) || !uuid_bin) + return -EINVAL; + + /* + * reverse bytes from big to little endian + */ + tmp32 = simple_strtoul(uuid_str, NULL, 16); + memcpy(uuid_bin, &tmp32, 4); + + /* + * reverse bytes from big to little endian + */ + tmp16 = simple_strtoul(uuid_str + 9, NULL, 16); + memcpy(uuid_bin + 4, &tmp16, 2); + + /* + * reverse bytes from big to little endian + */ + tmp16 = simple_strtoul(uuid_str + 14, NULL, 16); + memcpy(uuid_bin + 6, &tmp16, 2); + + /* + * reverse bytes from big to little endian + */ + tmp16 = simple_strtoul(uuid_str + 19, NULL, 16); + memcpy(uuid_bin + 8, &tmp16, 2); + + /* + * reverse bytes from big to little endian + */ + tmp64 = simple_strtoull(uuid_str + 24, NULL, 16); + memcpy(uuid_bin + 10, (char *)&tmp64, 6); + + return 0; +} diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 5bcb8253ed..cffa2c69d6 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -23,6 +23,10 @@ #include <asm/setjmp.h> #include <linux/libfdt_env.h>
+#if defined(CONFIG_ARM_FFA_TRANSPORT) +#include <arm_ffa_helper.h> +#endif + DECLARE_GLOBAL_DATA_PTR;
/* Task priority level */ @@ -2114,6 +2118,10 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, struct efi_event *evt, *next_event; efi_status_t ret = EFI_SUCCESS;
+#if defined(CONFIG_ARM_FFA_TRANSPORT) + int ffa_ret; +#endif + EFI_ENTRY("%p, %zx", image_handle, map_key);
/* Check that the caller has read the current memory map */ @@ -2174,6 +2182,15 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL); }
+#if defined(CONFIG_ARM_FFA_TRANSPORT) + /* unmap FF-A RX/TX buffers */ + ffa_ret = ffa_helper_unmap_rxtx_buffers(); + if (ffa_ret) + debug("[efi_boottime][ERROR]: can not unmap FF-A RX/TX buffers\n"); + else + debug("[efi_boottime][INFO]: FF-A RX/TX buffers unmapped\n"); +#endif + /* Patch out unsupported runtime function */ efi_runtime_detach();

From: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
Provide armffa command showcasing the use of the FF-A driver
The armffa command allows to query secure partitions data from the secure world and exchanging messages with the partitions.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com --- MAINTAINERS | 1 + cmd/Kconfig | 10 ++ cmd/Makefile | 2 + cmd/armffa.c | 266 ++++++++++++++++++++++++++++++++++++++++ drivers/arm-ffa/Kconfig | 1 + 5 files changed, 280 insertions(+) create mode 100644 cmd/armffa.c
diff --git a/MAINTAINERS b/MAINTAINERS index c5b608eb60..50ccd6a7ba 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -235,6 +235,7 @@ F: include/configs/turris_*.h ARM FF-A M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com S: Maintained +F: cmd/armffa.c F: drivers/arm-ffa/ F: include/arm_ffa.h F: include/arm_ffa_helper.h diff --git a/cmd/Kconfig b/cmd/Kconfig index 79219bcb74..de5bea1404 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -813,6 +813,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 Arm FF-A driver + 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 ede634d731..2905ce63cf 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -12,6 +12,8 @@ obj-y += panic.o obj-y += version.o
# command + +obj-$(CONFIG_CMD_ARMFFA) += armffa.o obj-$(CONFIG_CMD_ACPI) += acpi.o obj-$(CONFIG_CMD_ADDRMAP) += addrmap.o obj-$(CONFIG_CMD_AES) += aes.o diff --git a/cmd/armffa.c b/cmd/armffa.c new file mode 100644 index 0000000000..fcfc3a06bd --- /dev/null +++ b/cmd/armffa.c @@ -0,0 +1,266 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <arm_ffa_helper.h> +#include <asm/io.h> +#include <common.h> +#include <command.h> +#include <dm.h> +#include <mapmem.h> +#include <stdlib.h> + +/** + * do_ffa_get_singular_partition_info - implementation of the getpart subcommand + * @cmdtp: Command Table + * @flag: flags + * @argc: number of arguments + * @argv: arguments + * + * This function queries the secure partition information which the UUID is provided + * as an argument. The function uses the arm_ffa driver helper function + * 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_get_singular_partition_info(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct ffa_interface_data func_data = {0}; + u32 count = 0; + int ret; + union ffa_partition_uuid service_uuid = {0}; + struct ffa_partition_info *parts_info; + u32 info_idx; + + if (argc != 1) + return -EINVAL; + + if (ffa_uuid_str_to_bin(argv[0], (unsigned char *)&service_uuid)) { + ffa_err("Invalid UUID"); + return -EINVAL; + } + + /* + * get from the driver the count of the SPs matching the UUID + */ + func_data.data0_size = sizeof(service_uuid); + func_data.data0 = &service_uuid; + func_data.data1_size = sizeof(count); + func_data.data1 = &count; + + ret = ffa_helper_get_partitions_info(&func_data); + if (ret != FFA_ERR_STAT_SUCCESS) { + ffa_err("Failure in querying partitions count (error code: %d)", ret); + return ret; + } + + if (!count) { + ffa_info("No secure partition found"); + return ret; + } + + /* + * pre-allocate a buffer to be filled by the driver + * with ffa_partition_info structs + */ + + parts_info = calloc(count, sizeof(struct ffa_partition_info)); + if (!parts_info) + return -EINVAL; + + ffa_info("Pre-allocating %d partition(s) info structures", count); + + func_data.data1_size = count * sizeof(struct ffa_partition_info); + func_data.data1 = parts_info; + + /* + * ask the driver to fill the buffer with the SPs info + */ + ret = ffa_helper_get_partitions_info(&func_data); + if (ret != FFA_ERR_STAT_SUCCESS) { + ffa_err("Failure in querying partition(s) info (error code: %d)", ret); + free(parts_info); + return ret; + } + + /* + * SPs found , show the partition information + */ + for (info_idx = 0; info_idx < count ; info_idx++) { + ffa_info("Partition: id = 0x%x , exec_ctxt 0x%x , properties 0x%x", + parts_info[info_idx].id, + parts_info[info_idx].exec_ctxt, + parts_info[info_idx].properties); + } + + free(parts_info); + + return 0; +} + +/** + * do_ffa_msg_send_direct_req - implementation of the ping subcommand + * @cmdtp: Command Table + * @flag: flags + * @argc: number of arguments + * @argv: arguments + * + * This function sends data to the secure partition which the ID is provided + * as an argument. The function uses the arm_ffa driver helper function + * to send data. + * + * Return: + * + * CMD_RET_SUCCESS: on success, otherwise failure + */ +int do_ffa_msg_send_direct_req(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct ffa_interface_data func_data = {0}; + struct ffa_send_direct_data msg = {0}; + u32 pattern = 0xaabbccd0; + u16 part_id; + int ret; + + if (argc != 1) + return -EINVAL; + + errno = 0; + part_id = strtoul(argv[0], NULL, 16); + + if (errno) { + ffa_err("Invalid partition ID"); + return -EINVAL; + } + + /* + * telling the driver which partition to use + */ + func_data.data0_size = sizeof(part_id); + func_data.data0 = &part_id; + + /* + * filling the message data + */ + msg.a3 = ++pattern; + msg.a4 = ++pattern; + msg.a5 = ++pattern; + msg.a6 = ++pattern; + msg.a7 = ++pattern; + func_data.data1_size = sizeof(msg); + func_data.data1 = &msg; + + ret = ffa_helper_msg_send_direct_req(&func_data); + if (ret == FFA_ERR_STAT_SUCCESS) { + u8 cnt; + + ffa_info("SP response:\n[LSB]"); + for (cnt = 0; + cnt < sizeof(struct ffa_send_direct_data) / sizeof(u32); + cnt++) + ffa_info("0x%x", ((u32 *)&msg)[cnt]); + } else { + ffa_err("Sending direct request error (%d)", ret); + } + + return ret; +} + +/** + *do_ffa_dev_list - implementation of the devlist subcommand + * @cmdtp: [in] Command Table + * @flag: flags + * @argc: number of arguments + * @argv: arguments + * + * This function queries the devices belonging to the UCLASS_FFA + * class. Currently, one device is expected to show up: the arm_ffa device + * + * Return: + * + * CMD_RET_SUCCESS: on success, otherwise failure + */ +int do_ffa_dev_list(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct udevice *dev = NULL; + int i, ret; + + ffa_info("arm_ffa uclass entries:"); + + for (i = 0, ret = uclass_first_device(UCLASS_FFA, &dev); + dev; + ret = uclass_next_device(&dev), i++) { + if (ret) + break; + + ffa_info("entry %d - instance %08x, ops %08x, plat %08x", + i, + (u32)map_to_sysmem(dev), + (u32)map_to_sysmem(dev->driver->ops), + (u32)map_to_sysmem(dev_get_plat(dev))); + } + + return cmd_process_error(cmdtp, ret); +} + +static struct cmd_tbl armffa_commands[] = { + U_BOOT_CMD_MKENT(getpart, 1, 1, do_ffa_get_singular_partition_info, "", ""), + U_BOOT_CMD_MKENT(ping, 1, 1, do_ffa_msg_send_direct_req, "", ""), + U_BOOT_CMD_MKENT(devlist, 0, 1, do_ffa_dev_list, "", ""), +}; + +/** + * do_armffa - the armffa command main function + * @cmdtp: Command Table + * @flag: flags + * @argc: number of arguments + * @argv: arguments + * + * This function identifies which armffa subcommand to run. + * Then, it makes sure the arm_ffa device is probed and + * ready for use. + * Then, it runs the subcommand. + * + * Return: + * + * CMD_RET_SUCCESS: on success, otherwise failure + */ +static int do_armffa(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct cmd_tbl *armffa_cmd; + int ret; + + if (argc < 2) + return CMD_RET_USAGE; + + armffa_cmd = find_cmd_tbl(argv[1], armffa_commands, ARRAY_SIZE(armffa_commands)); + + argc -= 2; + argv += 2; + + if (!armffa_cmd || argc > armffa_cmd->maxargs) + return CMD_RET_USAGE; + + ret = ffa_helper_init_device(); + if (ret != FFA_ERR_STAT_SUCCESS) + return cmd_process_error(cmdtp, ret); + + ret = armffa_cmd->cmd(armffa_cmd, flag, argc, argv); + + return cmd_process_error(armffa_cmd, ret); +} + +U_BOOT_CMD(armffa, 4, 1, do_armffa, + "Arm FF-A operations test command", + "getpart <partition UUID>\n" + " - lists the partition(s) info\n" + "ping <partition ID>\n" + " - sends a data pattern to the specified partition\n" + "devlist\n" + " - displays the arm_ffa device info\n"); diff --git a/drivers/arm-ffa/Kconfig b/drivers/arm-ffa/Kconfig index e503dfaebf..7b7cfe26b1 100644 --- a/drivers/arm-ffa/Kconfig +++ b/drivers/arm-ffa/Kconfig @@ -4,6 +4,7 @@ config ARM_FFA_TRANSPORT bool "Enable Arm Firmware Framework for Armv8-A driver" depends on DM && ARM64 select ARM_SMCCC if ARM64 + select CMD_ARMFFA select LIB_UUID select ARM_FFA_TRANSPORT_HELPERS help

On 3/29/22 17:16, abdellatif.elkhlifi@arm.com wrote:
From: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
Provide armffa command showcasing the use of the FF-A driver
The armffa command allows to query secure partitions data from the secure world and exchanging messages with the partitions.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com
MAINTAINERS | 1 + cmd/Kconfig | 10 ++ cmd/Makefile | 2 + cmd/armffa.c | 266 ++++++++++++++++++++++++++++++++++++++++ drivers/arm-ffa/Kconfig | 1 +
We want to have all commands to be documented in /doc/usage/cmd/.
Could you, please, provide the missing /doc/usage/cmd/armffa.rst file.
Best regards
Heinrich
5 files changed, 280 insertions(+) create mode 100644 cmd/armffa.c
diff --git a/MAINTAINERS b/MAINTAINERS index c5b608eb60..50ccd6a7ba 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -235,6 +235,7 @@ F: include/configs/turris_*.h ARM FF-A M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com S: Maintained +F: cmd/armffa.c F: drivers/arm-ffa/ F: include/arm_ffa.h F: include/arm_ffa_helper.h diff --git a/cmd/Kconfig b/cmd/Kconfig index 79219bcb74..de5bea1404 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -813,6 +813,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 Arm FF-A driver
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 ede634d731..2905ce63cf 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -12,6 +12,8 @@ obj-y += panic.o obj-y += version.o
# command
+obj-$(CONFIG_CMD_ARMFFA) += armffa.o obj-$(CONFIG_CMD_ACPI) += acpi.o obj-$(CONFIG_CMD_ADDRMAP) += addrmap.o obj-$(CONFIG_CMD_AES) += aes.o diff --git a/cmd/armffa.c b/cmd/armffa.c new file mode 100644 index 0000000000..fcfc3a06bd --- /dev/null +++ b/cmd/armffa.c @@ -0,0 +1,266 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include <arm_ffa_helper.h> +#include <asm/io.h> +#include <common.h> +#include <command.h> +#include <dm.h> +#include <mapmem.h> +#include <stdlib.h>
+/**
- do_ffa_get_singular_partition_info - implementation of the getpart subcommand
- @cmdtp: Command Table
- @flag: flags
- @argc: number of arguments
- @argv: arguments
- This function queries the secure partition information which the UUID is provided
- as an argument. The function uses the arm_ffa driver helper function
- 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_get_singular_partition_info(struct cmd_tbl *cmdtp, int flag, int argc,
char *const argv[])
+{
- struct ffa_interface_data func_data = {0};
- u32 count = 0;
- int ret;
- union ffa_partition_uuid service_uuid = {0};
- struct ffa_partition_info *parts_info;
- u32 info_idx;
- if (argc != 1)
return -EINVAL;
- if (ffa_uuid_str_to_bin(argv[0], (unsigned char *)&service_uuid)) {
ffa_err("Invalid UUID");
return -EINVAL;
- }
- /*
* get from the driver the count of the SPs matching the UUID
*/
- func_data.data0_size = sizeof(service_uuid);
- func_data.data0 = &service_uuid;
- func_data.data1_size = sizeof(count);
- func_data.data1 = &count;
- ret = ffa_helper_get_partitions_info(&func_data);
- if (ret != FFA_ERR_STAT_SUCCESS) {
ffa_err("Failure in querying partitions count (error code: %d)", ret);
return ret;
- }
- if (!count) {
ffa_info("No secure partition found");
return ret;
- }
- /*
* pre-allocate a buffer to be filled by the driver
* with ffa_partition_info structs
*/
- parts_info = calloc(count, sizeof(struct ffa_partition_info));
- if (!parts_info)
return -EINVAL;
- ffa_info("Pre-allocating %d partition(s) info structures", count);
- func_data.data1_size = count * sizeof(struct ffa_partition_info);
- func_data.data1 = parts_info;
- /*
* ask the driver to fill the buffer with the SPs info
*/
- ret = ffa_helper_get_partitions_info(&func_data);
- if (ret != FFA_ERR_STAT_SUCCESS) {
ffa_err("Failure in querying partition(s) info (error code: %d)", ret);
free(parts_info);
return ret;
- }
- /*
* SPs found , show the partition information
*/
- for (info_idx = 0; info_idx < count ; info_idx++) {
ffa_info("Partition: id = 0x%x , exec_ctxt 0x%x , properties 0x%x",
parts_info[info_idx].id,
parts_info[info_idx].exec_ctxt,
parts_info[info_idx].properties);
- }
- free(parts_info);
- return 0;
+}
+/**
- do_ffa_msg_send_direct_req - implementation of the ping subcommand
- @cmdtp: Command Table
- @flag: flags
- @argc: number of arguments
- @argv: arguments
- This function sends data to the secure partition which the ID is provided
- as an argument. The function uses the arm_ffa driver helper function
- to send data.
- Return:
- CMD_RET_SUCCESS: on success, otherwise failure
- */
+int do_ffa_msg_send_direct_req(struct cmd_tbl *cmdtp, int flag, int argc,
char *const argv[])
+{
- struct ffa_interface_data func_data = {0};
- struct ffa_send_direct_data msg = {0};
- u32 pattern = 0xaabbccd0;
- u16 part_id;
- int ret;
- if (argc != 1)
return -EINVAL;
- errno = 0;
- part_id = strtoul(argv[0], NULL, 16);
- if (errno) {
ffa_err("Invalid partition ID");
return -EINVAL;
- }
- /*
* telling the driver which partition to use
*/
- func_data.data0_size = sizeof(part_id);
- func_data.data0 = &part_id;
- /*
* filling the message data
*/
- msg.a3 = ++pattern;
- msg.a4 = ++pattern;
- msg.a5 = ++pattern;
- msg.a6 = ++pattern;
- msg.a7 = ++pattern;
- func_data.data1_size = sizeof(msg);
- func_data.data1 = &msg;
- ret = ffa_helper_msg_send_direct_req(&func_data);
- if (ret == FFA_ERR_STAT_SUCCESS) {
u8 cnt;
ffa_info("SP response:\n[LSB]");
for (cnt = 0;
cnt < sizeof(struct ffa_send_direct_data) / sizeof(u32);
cnt++)
ffa_info("0x%x", ((u32 *)&msg)[cnt]);
- } else {
ffa_err("Sending direct request error (%d)", ret);
- }
- return ret;
+}
+/**
- *do_ffa_dev_list - implementation of the devlist subcommand
- @cmdtp: [in] Command Table
- @flag: flags
- @argc: number of arguments
- @argv: arguments
- This function queries the devices belonging to the UCLASS_FFA
- class. Currently, one device is expected to show up: the arm_ffa device
- Return:
- CMD_RET_SUCCESS: on success, otherwise failure
- */
+int do_ffa_dev_list(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{
- struct udevice *dev = NULL;
- int i, ret;
- ffa_info("arm_ffa uclass entries:");
- for (i = 0, ret = uclass_first_device(UCLASS_FFA, &dev);
dev;
ret = uclass_next_device(&dev), i++) {
if (ret)
break;
ffa_info("entry %d - instance %08x, ops %08x, plat %08x",
i,
(u32)map_to_sysmem(dev),
(u32)map_to_sysmem(dev->driver->ops),
(u32)map_to_sysmem(dev_get_plat(dev)));
- }
- return cmd_process_error(cmdtp, ret);
+}
+static struct cmd_tbl armffa_commands[] = {
- U_BOOT_CMD_MKENT(getpart, 1, 1, do_ffa_get_singular_partition_info, "", ""),
- U_BOOT_CMD_MKENT(ping, 1, 1, do_ffa_msg_send_direct_req, "", ""),
- U_BOOT_CMD_MKENT(devlist, 0, 1, do_ffa_dev_list, "", ""),
+};
+/**
- do_armffa - the armffa command main function
- @cmdtp: Command Table
- @flag: flags
- @argc: number of arguments
- @argv: arguments
- This function identifies which armffa subcommand to run.
- Then, it makes sure the arm_ffa device is probed and
- ready for use.
- Then, it runs the subcommand.
- Return:
- CMD_RET_SUCCESS: on success, otherwise failure
- */
+static int do_armffa(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{
- struct cmd_tbl *armffa_cmd;
- int ret;
- if (argc < 2)
return CMD_RET_USAGE;
- armffa_cmd = find_cmd_tbl(argv[1], armffa_commands, ARRAY_SIZE(armffa_commands));
- argc -= 2;
- argv += 2;
- if (!armffa_cmd || argc > armffa_cmd->maxargs)
return CMD_RET_USAGE;
- ret = ffa_helper_init_device();
- if (ret != FFA_ERR_STAT_SUCCESS)
return cmd_process_error(cmdtp, ret);
- ret = armffa_cmd->cmd(armffa_cmd, flag, argc, argv);
- return cmd_process_error(armffa_cmd, ret);
+}
+U_BOOT_CMD(armffa, 4, 1, do_armffa,
"Arm FF-A operations test command",
"getpart <partition UUID>\n"
" - lists the partition(s) info\n"
"ping <partition ID>\n"
" - sends a data pattern to the specified partition\n"
"devlist\n"
" - displays the arm_ffa device info\n");
diff --git a/drivers/arm-ffa/Kconfig b/drivers/arm-ffa/Kconfig index e503dfaebf..7b7cfe26b1 100644 --- a/drivers/arm-ffa/Kconfig +++ b/drivers/arm-ffa/Kconfig @@ -4,6 +4,7 @@ config ARM_FFA_TRANSPORT bool "Enable Arm Firmware Framework for Armv8-A driver" depends on DM && ARM64 select ARM_SMCCC if ARM64
- select CMD_ARMFFA select LIB_UUID select ARM_FFA_TRANSPORT_HELPERS help

Hi Heinrich,
On Thu, Jul 27, 2023 at 07:51:42AM +0200, Heinrich Schuchardt wrote:
On 3/29/22 17:16, abdellatif.elkhlifi@arm.com wrote:
From: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
Provide armffa command showcasing the use of the FF-A driver
The armffa command allows to query secure partitions data from the secure world and exchanging messages with the partitions.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com
MAINTAINERS | 1 + cmd/Kconfig | 10 ++ cmd/Makefile | 2 + cmd/armffa.c | 266 ++++++++++++++++++++++++++++++++++++++++ drivers/arm-ffa/Kconfig | 1 +
We want to have all commands to be documented in /doc/usage/cmd/.
Could you, please, provide the missing /doc/usage/cmd/armffa.rst file.
It's here [1].
[1]: https://lore.kernel.org/all/20230726094503.100497-6-abdellatif.elkhlifi@arm....
Cheers, Abdellatif

Hi Abdellatif,
On Thu, 27 Jul 2023 at 03:00, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
Hi Heinrich,
On Thu, Jul 27, 2023 at 07:51:42AM +0200, Heinrich Schuchardt wrote:
On 3/29/22 17:16, abdellatif.elkhlifi@arm.com wrote:
From: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
Provide armffa command showcasing the use of the FF-A driver
The armffa command allows to query secure partitions data from the secure world and exchanging messages with the partitions.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com
MAINTAINERS | 1 + cmd/Kconfig | 10 ++ cmd/Makefile | 2 + cmd/armffa.c | 266 ++++++++++++++++++++++++++++++++++++++++ drivers/arm-ffa/Kconfig | 1 +
We want to have all commands to be documented in /doc/usage/cmd/.
Could you, please, provide the missing /doc/usage/cmd/armffa.rst file.
It's here [1].
I'd suggest mentioning that in the commit message for this patch, or even putting the testin the same patch, along with the test for the command. I find it hard to keep track of this too.
Cheers, Abdellatif
Regards, Simon

Hi Simon, Heinrich,
On Thu, Jul 27, 2023 at 07:52:00PM -0600, Simon Glass wrote:
Hi Abdellatif,
On Thu, 27 Jul 2023 at 03:00, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
Hi Heinrich,
On Thu, Jul 27, 2023 at 07:51:42AM +0200, Heinrich Schuchardt wrote:
On 3/29/22 17:16, abdellatif.elkhlifi@arm.com wrote:
From: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
Provide armffa command showcasing the use of the FF-A driver
The armffa command allows to query secure partitions data from the secure world and exchanging messages with the partitions.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com
MAINTAINERS | 1 + cmd/Kconfig | 10 ++ cmd/Makefile | 2 + cmd/armffa.c | 266 ++++++++++++++++++++++++++++++++++++++++ drivers/arm-ffa/Kconfig | 1 +
We want to have all commands to be documented in /doc/usage/cmd/.
Could you, please, provide the missing /doc/usage/cmd/armffa.rst file.
It's here [1].
I'd suggest mentioning that in the commit message for this patch, or even putting the testin the same patch, along with the test for the command. I find it hard to keep track of this too.
Please refer to the commit provided by the latest version (v17).
In the commit message the path to doc/usage/cmd/armffa.rst is already given.
Please refer to the commit [A].
The commit log says: For more details please refer to the command documentation [1].
[A]: https://lore.kernel.org/all/20230727160712.81477-6-abdellatif.elkhlifi@arm.c...
Cheers Abdellatif
Cheers, Abdellatif
Regards, Simon

From: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
Provide a Sandbox driver to emulate the FF-A ABIs
The emulated ABIs are those supported by the FF-A core driver and according to FF-A specification v1.0.
The Sandbox driver provides operations allowing the test application to read the status of all the inspected ABIs and perform functional tests based on that.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com --- MAINTAINERS | 2 + arch/sandbox/dts/sandbox.dtsi | 10 + arch/sandbox/dts/test.dts | 10 + common/board_r.c | 2 +- configs/sandbox64_defconfig | 2 + configs/sandbox_defconfig | 2 + doc/arch/sandbox.rst | 1 + drivers/arm-ffa/Kconfig | 8 +- drivers/arm-ffa/Makefile | 1 + drivers/arm-ffa/arm-ffa-uclass.c | 51 +- drivers/arm-ffa/core.c | 94 +++- drivers/arm-ffa/sandbox.c | 735 ++++++++++++++++++++++++++ drivers/arm-ffa/sandbox_arm_ffa_prv.h | 128 +++++ include/arm_ffa.h | 9 +- include/sandbox_arm_ffa.h | 31 ++ include/sandbox_arm_ffa_helper.h | 26 + lib/arm-ffa/Makefile | 1 + lib/arm-ffa/arm_ffa_helper.c | 6 +- lib/arm-ffa/sandbox_arm_ffa_helper.c | 23 + lib/efi_loader/efi_boottime.c | 4 +- 20 files changed, 1102 insertions(+), 44 deletions(-) create mode 100644 drivers/arm-ffa/sandbox.c create mode 100644 drivers/arm-ffa/sandbox_arm_ffa_prv.h create mode 100644 include/sandbox_arm_ffa.h create mode 100644 include/sandbox_arm_ffa_helper.h create mode 100644 lib/arm-ffa/sandbox_arm_ffa_helper.c
diff --git a/MAINTAINERS b/MAINTAINERS index 50ccd6a7ba..84524d7caf 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -239,6 +239,8 @@ F: cmd/armffa.c F: drivers/arm-ffa/ F: include/arm_ffa.h F: include/arm_ffa_helper.h +F: include/sandbox_arm_ffa.h +F: include/sandbox_arm_ffa_helper.h F: lib/arm-ffa/
ARM FREESCALE IMX diff --git a/arch/sandbox/dts/sandbox.dtsi b/arch/sandbox/dts/sandbox.dtsi index 66b813faad..523ffcc27d 100644 --- a/arch/sandbox/dts/sandbox.dtsi +++ b/arch/sandbox/dts/sandbox.dtsi @@ -416,6 +416,16 @@ sandbox_tee { compatible = "sandbox,tee"; }; + + arm_ffa { + compatible = "arm,ffa"; + method = "smc"; + }; + + sandbox_arm_ffa { + compatible = "sandbox,ffa"; + method = "smc"; + }; };
&cros_ec { diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 3d206fdb3c..2c638af042 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -1598,6 +1598,16 @@ compatible = "sandbox,regmap_test"; }; }; + + arm_ffa { + compatible = "arm,ffa"; + method = "smc"; + }; + + sandbox_arm_ffa { + compatible = "sandbox,ffa"; + method = "smc"; + }; };
#include "sandbox_pmic.dtsi" diff --git a/common/board_r.c b/common/board_r.c index 7866ec3ad5..3b82f74bb0 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -775,7 +775,7 @@ static init_fnc_t init_sequence_r[] = { INIT_FUNC_WATCHDOG_RESET initr_net, #endif -#ifdef CONFIG_ARM_FFA_TRANSPORT +#if defined(CONFIG_ARM_FFA_TRANSPORT) && !defined(CONFIG_SANDBOX_FFA) ffa_helper_init_device, #endif #ifdef CONFIG_POST diff --git a/configs/sandbox64_defconfig b/configs/sandbox64_defconfig index 40d1422a37..301c6f320f 100644 --- a/configs/sandbox64_defconfig +++ b/configs/sandbox64_defconfig @@ -252,3 +252,5 @@ CONFIG_TEST_FDTDEC=y CONFIG_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y +CONFIG_ARM_FFA_TRANSPORT=y +CONFIG_SANDBOX_FFA=y \ No newline at end of file diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 0f43101ab5..a2f273056f 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -324,3 +324,5 @@ CONFIG_TEST_FDTDEC=y CONFIG_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y +CONFIG_ARM_FFA_TRANSPORT=y +CONFIG_SANDBOX_FFA=y \ No newline at end of file diff --git a/doc/arch/sandbox.rst b/doc/arch/sandbox.rst index f8804e1f41..7cb5ea307c 100644 --- a/doc/arch/sandbox.rst +++ b/doc/arch/sandbox.rst @@ -203,6 +203,7 @@ Supported Drivers
U-Boot sandbox supports these emulations:
+- Arm FF-A - Block devices - Chrome OS EC - GPIO diff --git a/drivers/arm-ffa/Kconfig b/drivers/arm-ffa/Kconfig index 7b7cfe26b1..61103daa42 100644 --- a/drivers/arm-ffa/Kconfig +++ b/drivers/arm-ffa/Kconfig @@ -2,7 +2,7 @@
config ARM_FFA_TRANSPORT bool "Enable Arm Firmware Framework for Armv8-A driver" - depends on DM && ARM64 + depends on DM && (ARM64 || SANDBOX) select ARM_SMCCC if ARM64 select CMD_ARMFFA select LIB_UUID @@ -24,3 +24,9 @@ config ARM_FFA_TRANSPORT entity to communicate with. FF-A communication is handled by one device and one instance. This device takes care of all the interactions between Normal world and Secure World. + +config SANDBOX_FFA + bool "FF-A Sandbox driver" + depends on ARM_FFA_TRANSPORT && SANDBOX + help + This emulates the FF-A handling under Sandbox and allows to test the FF-A driver diff --git a/drivers/arm-ffa/Makefile b/drivers/arm-ffa/Makefile index 7bc9a336a9..df1cfe6ef0 100644 --- a/drivers/arm-ffa/Makefile +++ b/drivers/arm-ffa/Makefile @@ -4,3 +4,4 @@ #
obj-y += arm-ffa-uclass.o core.o +obj-$(CONFIG_SANDBOX_FFA) += sandbox.o diff --git a/drivers/arm-ffa/arm-ffa-uclass.c b/drivers/arm-ffa/arm-ffa-uclass.c index 0bf661d397..a5945afb9d 100644 --- a/drivers/arm-ffa/arm-ffa-uclass.c +++ b/drivers/arm-ffa/arm-ffa-uclass.c @@ -11,6 +11,10 @@ #include <log.h> #include <asm/global_data.h>
+#if CONFIG_IS_ENABLED(SANDBOX_FFA) +#include <sandbox_arm_ffa.h> +#endif + DECLARE_GLOBAL_DATA_PTR;
UCLASS_DRIVER(ffa) = { @@ -42,18 +46,42 @@ int __ffa_runtime ffa_get_invoke_func(u32 func_id, struct ffa_interface_data *fu return ffa_device_get_ops()->invoke_func(func_id, func_data); }
+#if CONFIG_IS_ENABLED(SANDBOX_FFA) + /** - * ffa_init_device - probes the arm_ffa device + * sandbox_ffa_get_invoke_func - performs a call to the Sandbox FF-A driver dispatcher + * @func_id: The FF-A function to be queried + * @func_data: Pointer to the query arguments + * container structure. + * + * This function passes the FF-A function ID to be queried during sandbox test cases + * and its arguments to the Sandbox FF-A driver dispatcher. + * This function is called by the Sandbox FF-A helper function. + * + * Return: * - * This boot time function makes sure the arm_ffa device is probed + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +int sandbox_ffa_get_invoke_func(u32 func_id, struct ffa_interface_data *func_data) +{ + if (!sandbox_ffa_device_get_ops()->invoke_func) + return -EINVAL; + + return sandbox_ffa_device_get_ops()->invoke_func(func_id, func_data); +} +#endif + +/** + * ffa_init_device - probes the arm_ffa and sandbox_arm_ffa devices + * + * This boot time function makes sure the arm_ffa and sandbox_arm_ffa devices are probed * and ready for use. * This function is called automatically at initcalls * level (after u-boot relocation). * - * Arm FF-A transport is implemented through a single u-boot - * device (arm_ffa). So, there is only one device belonging to UCLASS_FFA. - * All FF-A clients should use the arm_ffa device to use the FF-A - * transport. + * Arm FF-A transport is implemented through arm_ffa u-boot device managing the FF-A + * communication. In Sandbox mode sandbox_arm_ffa is used to test arm_ffa driver. + * All FF-A clients should use the arm_ffa device to use the FF-A transport. * * Return: * @@ -61,5 +89,14 @@ int __ffa_runtime ffa_get_invoke_func(u32 func_id, struct ffa_interface_data *fu */ int ffa_init_device(void) { - return ffa_get_device(); + int ret; + + ret = ffa_get_device(); + +#if CONFIG_IS_ENABLED(SANDBOX_FFA) + if (ret == FFA_ERR_STAT_SUCCESS) + ret = sandbox_ffa_get_device(); +#endif + + return ret; } diff --git a/drivers/arm-ffa/core.c b/drivers/arm-ffa/core.c index 9d0dab1d36..a4d9c08482 100644 --- a/drivers/arm-ffa/core.c +++ b/drivers/arm-ffa/core.c @@ -5,6 +5,11 @@ */
#include "arm_ffa_prv.h" + +#if CONFIG_IS_ENABLED(SANDBOX_FFA) +#include "sandbox_arm_ffa_prv.h" +#endif + #include <asm/global_data.h> #include <asm/io.h> #include <common.h> @@ -82,8 +87,10 @@ static int ffa_get_version(void)
FFA_DECLARE_ARGS;
- if (!ffa_priv_data.invoke_ffa_fn) - panic("[FFA] no private data found\n"); + if (!ffa_priv_data.invoke_ffa_fn) { + ffa_panic("no private data found\n"); + return -ENODEV; + }
a0 = FFA_VERSION; a1 = FFA_VERSION_1_0; @@ -126,8 +133,10 @@ static int ffa_get_endpoint_id(void) { FFA_DECLARE_ARGS;
- if (!ffa_priv_data.invoke_ffa_fn) - panic("[FFA] no private data found\n"); + if (!ffa_priv_data.invoke_ffa_fn) { + ffa_panic("no private data found\n"); + return -ENODEV; + }
a0 = FFA_ID_GET;
@@ -200,8 +209,10 @@ static int ffa_get_rxtx_map_features(void) { FFA_DECLARE_ARGS;
- if (!ffa_priv_data.invoke_ffa_fn) - panic("[FFA] no private data found\n"); + if (!ffa_priv_data.invoke_ffa_fn) { + ffa_panic("no private data found\n"); + return -ENODEV; + }
a0 = FFA_FEATURES; a1 = FFA_RXTX_MAP; @@ -427,8 +438,10 @@ static int ffa_map_rxtx_buffers(size_t buf_4k_pages)
FFA_DECLARE_ARGS;
- if (!ffa_priv_data.invoke_ffa_fn) - panic("[FFA] no private data found\n"); + if (!ffa_priv_data.invoke_ffa_fn) { + ffa_panic("no private data found\n"); + return -ENODEV; + }
ret = ffa_alloc_rxtx_buffers(buf_4k_pages); if (ret != FFA_ERR_STAT_SUCCESS) @@ -496,8 +509,10 @@ static int ffa_unmap_rxtx_buffers(void) { FFA_DECLARE_ARGS;
- if (!ffa_priv_data.invoke_ffa_fn) - panic("[FFA] no private data found\n"); + if (!ffa_priv_data.invoke_ffa_fn) { + ffa_panic("no private data found\n"); + return -ENODEV; + }
a0 = FFA_RXTX_UNMAP; a1 = PREP_SELF_ENDPOINT_ID(ffa_priv_data.id); @@ -508,11 +523,12 @@ static int ffa_unmap_rxtx_buffers(void) case FFA_ERROR: { if (((int)res.a2) == FFA_ERR_STAT_NOT_SUPPORTED) - panic("[FFA] FFA_RXTX_UNMAP is not implemented at this FF-A instance\n"); + ffa_panic("FFA_RXTX_UNMAP is not implemented at this FF-A instance\n"); else if (((int)res.a2) == FFA_ERR_STAT_INVALID_PARAMETERS) - panic("[FFA] There is no buffer pair registered on behalf of the caller\n"); + ffa_panic("There is no buffer pair registered on behalf of the caller\n"); else - panic("[FFA] Undefined error (%d)\n", ((int)res.a2)); + ffa_panic("Undefined error (%d)\n", ((int)res.a2)); + return -ENODEV; } case FFA_SUCCESS: { @@ -520,19 +536,24 @@ static int ffa_unmap_rxtx_buffers(void) int ret;
ret = ffa_get_rxtx_buffers_pages_cnt(&buf_4k_pages); - if (ret != FFA_ERR_STAT_SUCCESS) - panic("[FFA] RX/TX buffers unmapped but failure in getting pages count\n"); + if (ret != FFA_ERR_STAT_SUCCESS) { + ffa_panic("RX/TX buffers unmapped but failure in getting pages count\n"); + return -ENODEV; + }
ret = ffa_free_rxtx_buffers(buf_4k_pages); - if (ret != FFA_ERR_STAT_SUCCESS) - panic("[FFA] RX/TX buffers unmapped but failure in freeing the memory\n"); + if (ret != FFA_ERR_STAT_SUCCESS) { + ffa_panic("RX/TX buffers unmapped but failure in freeing the memory\n"); + return -ENODEV; + }
ffa_info("RX/TX buffers unmapped and memory freed");
return FFA_ERR_STAT_SUCCESS; } default: - panic("[FFA] Undefined response function (0x%lx)", res.a0); + ffa_panic("Undefined response function (0x%lx)", res.a0); + return -ENODEV; } }
@@ -550,8 +571,10 @@ static int ffa_release_rx_buffer(void) { FFA_DECLARE_ARGS;
- if (!ffa_priv_data.invoke_ffa_fn) - panic("[FFA] no private data found\n"); + if (!ffa_priv_data.invoke_ffa_fn) { + ffa_panic("no private data found\n"); + return -ENODEV; + }
a0 = FFA_RX_RELEASE;
@@ -561,17 +584,19 @@ static int ffa_release_rx_buffer(void) case FFA_ERROR: { if (((int)res.a2) == FFA_ERR_STAT_NOT_SUPPORTED) - panic("[FFA] FFA_RX_RELEASE is not implemented at this FF-A instance\n"); + ffa_panic("FFA_RX_RELEASE is not implemented at this FF-A instance\n"); else if (((int)res.a2) == FFA_ERR_STAT_DENIED) - panic("[FFA] Caller did not have ownership of the RX buffer\n"); + ffa_panic("Caller did not have ownership of the RX buffer\n"); else - panic("[FFA] Undefined error (%d)\n", ((int)res.a2)); + ffa_panic("Undefined error (%d)\n", ((int)res.a2)); + return -ENODEV; } case FFA_SUCCESS: return FFA_ERR_STAT_SUCCESS;
default: - panic("[FFA] Undefined response function (0x%lx)\n", res.a0); + ffa_panic("Undefined response function (0x%lx)\n", res.a0); + return -ENODEV; } }
@@ -763,8 +788,10 @@ static int ffa_query_partitions_info(union ffa_partition_uuid *part_uuid, unsigned long a7 = 0; struct arm_smccc_res res = {0};
- if (!ffa_priv_data.invoke_ffa_fn) - panic("[FFA] no private data found\n"); + if (!ffa_priv_data.invoke_ffa_fn) { + ffa_panic("no private data found\n"); + return -ENODEV; + }
a0 = FFA_PARTITION_INFO_GET;
@@ -840,7 +867,7 @@ static int ffa_query_partitions_info(union ffa_partition_uuid *part_uuid, ret = ffa_release_rx_buffer();
if (!part_uuid && !res.a2) { - ffa_err("[FFA] no partition installed in the system"); + ffa_err("no partition installed in the system"); return -ENODEV; }
@@ -931,8 +958,10 @@ static int ffa_get_partitions_info(struct ffa_interface_data *func_data) return -EINVAL; }
- if (!ffa_priv_data.partitions.count || !ffa_priv_data.partitions.descs) - panic("[FFA] No partition installed\n"); + if (!ffa_priv_data.partitions.count || !ffa_priv_data.partitions.descs) { + ffa_panic("No partition installed\n"); + return -ENODEV; + }
if (func_data->data0_size == sizeof(union ffa_partition_uuid) && func_data->data0 && @@ -1170,6 +1199,7 @@ static int __ffa_runtime ffa_msg_send_direct_req(struct ffa_interface_data /* Undefined error */ return -ENXIO; } + return -ENODEV; } case FFA_SUCCESS:
@@ -1247,7 +1277,13 @@ static int ffa_init_private_data(struct udevice *dev) ffa_priv_data.conduit = pdata->conduit;
if (ffa_priv_data.conduit == FFA_CONDUIT_SMC) { +#if CONFIG_IS_ENABLED(SANDBOX_FFA) + ffa_priv_data.invoke_ffa_fn = sandbox_arm_ffa_smccc_smc; + ffa_info("Using SMC emulation"); +#else ffa_priv_data.invoke_ffa_fn = arm_ffa_smccc_smc; +#endif + } else { ffa_err("Undefined FF-A conduit (%d)", ffa_priv_data.conduit); return -EINVAL; diff --git a/drivers/arm-ffa/sandbox.c b/drivers/arm-ffa/sandbox.c new file mode 100644 index 0000000000..fcabd4b59b --- /dev/null +++ b/drivers/arm-ffa/sandbox.c @@ -0,0 +1,735 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include "sandbox_arm_ffa_prv.h" +#include <asm/global_data.h> +#include <common.h> +#include <dm.h> +#include <linux/errno.h> +#include <linux/sizes.h> +#include <mapmem.h> +#include <string.h> + +DECLARE_GLOBAL_DATA_PTR; + +/** + * The device private data structure containing all the emulated secure world data + */ +static struct sandbox_ffa_prvdata sandbox_ffa_priv_data = {0}; + +/* The partitions (SPs) table */ +static struct ffa_partition_desc sandbox_partitions[SANDBOX_PARTITIONS_CNT] = { + { + .info = { .id = 0x1245, .exec_ctxt = 0x5687, .properties = 0x89325621 }, + .UUID = { .bytes = {SANDBOX_SERVICE1_UUID_DATA}} + }, + { + .info = { .id = 0x9836, .exec_ctxt = 0x9587, .properties = 0x45325621 }, + .UUID = { .bytes = {SANDBOX_SERVICE2_UUID_DATA}} + }, + { + .info = { .id = 0x6452, .exec_ctxt = 0x7687, .properties = 0x23325621 }, + .UUID = { .bytes = {SANDBOX_SERVICE1_UUID_DATA}} + }, + { + .info = { .id = 0x7814, .exec_ctxt = 0x1487, .properties = 0x70325621 }, + .UUID = { .bytes = {SANDBOX_SERVICE2_UUID_DATA}} + } + +}; + +/* + * Driver functions + */ + +/** + * sandbox_ffa_get_device - probes the sandbox_arm_ffa device + * + * This function makes sure the sandbox_arm_ffa device is probed + * and ready for use. This is done using uclass_get_device. + * The arm_ffa driver belongs to UCLASS_FFA. + * This function should be called before using the driver. + * + * sandbox_arm_ffa depends on arm_ffa device. This dependency is + * handled by ffa_init_device function. arm_ffa is probed first then + * it probes sandbox_arm_ffa using sandbox_ffa_get_device. + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +int sandbox_ffa_get_device(void) +{ + int ret; + int devnum = 1; + + if (sandbox_ffa_priv_data.dev) + return FFA_ERR_STAT_SUCCESS; + + /* + * searching and probing the device + */ + ret = uclass_get_device(UCLASS_FFA, devnum, &sandbox_ffa_priv_data.dev); + if (ret) { + ffa_err("[Sandbox] Can not find the FF-A Sandbox device"); + sandbox_ffa_priv_data.dev = NULL; + return -ENODEV; + } + + return FFA_ERR_STAT_SUCCESS; +} + +/** + * sandbox_ffa_version - Emulated FFA_VERSION handler function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_VERSION FF-A function. + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_version) +{ + sandbox_ffa_priv_data.fwk_version = FFA_VERSION_1_0; + res->a0 = sandbox_ffa_priv_data.fwk_version; + + /* w1-w7 MBZ */ + memset(FFA_W1W7_MBZ_REG_START, 0, FFA_W1W7_MBZ_CNT * sizeof(unsigned long)); + + return FFA_ERR_STAT_SUCCESS; +} + +/** + * sandbox_ffa_id_get - Emulated FFA_ID_GET handler function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_ID_GET FF-A function. + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_id_get) +{ + res->a0 = FFA_SUCCESS; + res->a1 = 0; + + sandbox_ffa_priv_data.id = NS_PHYS_ENDPOINT_ID; + res->a2 = sandbox_ffa_priv_data.id; + + /* w3-w7 MBZ */ + memset(FFA_W3_MBZ_REG_START, 0, FFA_W3W7_MBZ_CNT * sizeof(unsigned long)); + + return FFA_ERR_STAT_SUCCESS; +} + +/** + * sandbox_ffa_features - Emulated FFA_FEATURES handler function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_FEATURES FF-A function. + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_features) +{ + switch (a1) { + case FFA_RXTX_MAP: + { + res->a0 = FFA_SUCCESS; + res->a2 = RXTX_BUFFERS_MIN_SIZE; + res->a3 = 0; + /* w4-w7 MBZ */ + memset(FFA_W4W7_MBZ_REG_START, + 0, FFA_W4W7_MBZ_CNT * sizeof(unsigned long)); + break; + } + default: + { + res->a0 = FFA_ERROR; + res->a2 = FFA_ERR_STAT_NOT_SUPPORTED; + /* w3-w7 MBZ */ + memset(FFA_W3_MBZ_REG_START, + 0, FFA_W3W7_MBZ_CNT * sizeof(unsigned long)); + ffa_err("[Sandbox] FF-A interface 0x%lx not implemented", a1); + } + } + + res->a1 = 0; + + return FFA_ERR_STAT_SUCCESS; +} + +/** + * sandbox_ffa_partition_info_get - Emulated FFA_PARTITION_INFO_GET handler function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_PARTITION_INFO_GET FF-A function. + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_partition_info_get) +{ + struct ffa_partition_info *rxbuf_desc_info = NULL; + u32 descs_cnt; + u32 descs_size_bytes; + + res->a0 = FFA_ERROR; + + if (!sandbox_ffa_priv_data.pair.rxbuf) { + res->a2 = FFA_ERR_STAT_DENIED; + goto cleanup; + } + + if (sandbox_ffa_priv_data.pair_info.rxbuf_owned) { + res->a2 = FFA_ERR_STAT_BUSY; + goto cleanup; + } + + if (!sandbox_ffa_priv_data.partitions.descs) { + sandbox_ffa_priv_data.partitions.descs = sandbox_partitions; + sandbox_ffa_priv_data.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 descriptors buffer size */ + if ((sandbox_ffa_priv_data.pair_info.rxtx_buf_size * SZ_4K) < descs_size_bytes) { + res->a2 = FFA_ERR_STAT_NO_MEMORY; + goto cleanup; + } + + rxbuf_desc_info = (struct ffa_partition_info *)sandbox_ffa_priv_data.pair.rxbuf; + + /* No UUID specified. Return the information of all partitions */ + if (!a1 && !a2 && !a3 && !a4) { + for (descs_cnt = 0 ; descs_cnt < SANDBOX_PARTITIONS_CNT ; descs_cnt++) + *(rxbuf_desc_info++) = + sandbox_ffa_priv_data.partitions.descs[descs_cnt].info; + + res->a0 = FFA_SUCCESS; + res->a2 = SANDBOX_PARTITIONS_CNT; + /* transfer ownership to the consumer: the non secure world */ + sandbox_ffa_priv_data.pair_info.rxbuf_owned = 1; + + goto cleanup; + } + + /* + * A UUID is specified. Return the information of all partitions matching + * the UUID + */ + + for (descs_cnt = 0 ; descs_cnt < SANDBOX_PARTITIONS_CNT ; descs_cnt++) + if (a1 == sandbox_ffa_priv_data.partitions.descs[descs_cnt].UUID.words.a1 && + a2 == sandbox_ffa_priv_data.partitions.descs[descs_cnt].UUID.words.a2 && + a3 == sandbox_ffa_priv_data.partitions.descs[descs_cnt].UUID.words.a3 && + a4 == sandbox_ffa_priv_data.partitions.descs[descs_cnt].UUID.words.a4) { + *(rxbuf_desc_info++) = + sandbox_ffa_priv_data.partitions.descs[descs_cnt].info; + } + + if (rxbuf_desc_info != ((struct ffa_partition_info *)sandbox_ffa_priv_data.pair.rxbuf)) { + res->a0 = FFA_SUCCESS; + /* store the partitions count */ + res->a2 = (unsigned long) + (rxbuf_desc_info - (struct ffa_partition_info *) + sandbox_ffa_priv_data.pair.rxbuf); + + /* transfer ownership to the consumer: the non secure world */ + sandbox_ffa_priv_data.pair_info.rxbuf_owned = 1; + } else { + res->a2 = FFA_ERR_STAT_INVALID_PARAMETERS; + } + +cleanup: + + ffa_err("[Sandbox] FFA_PARTITION_INFO_GET (%ld)", res->a2); + + res->a1 = 0; + + /* w3-w7 MBZ */ + memset(FFA_W3_MBZ_REG_START, 0, FFA_W3W7_MBZ_CNT * sizeof(unsigned long)); + + return FFA_ERR_STAT_SUCCESS; +} + +/** + * sandbox_ffa_rxtx_map - Emulated FFA_RXTX_MAP handler function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_RXTX_MAP FF-A function. + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_rxtx_map) +{ + res->a0 = FFA_ERROR; + + if (sandbox_ffa_priv_data.pair.txbuf && sandbox_ffa_priv_data.pair.rxbuf) { + res->a2 = FFA_ERR_STAT_DENIED; + goto feedback; + } + + if (a3 >= RXTX_BUFFERS_MIN_PAGES && a1 && a2) { + sandbox_ffa_priv_data.pair.txbuf = a1; + sandbox_ffa_priv_data.pair.rxbuf = a2; + sandbox_ffa_priv_data.pair_info.rxtx_buf_size = a3; + sandbox_ffa_priv_data.pair_info.rxbuf_mapped = 1; + res->a0 = FFA_SUCCESS; + res->a2 = 0; + goto feedback; + } + + if (!a1 || !a2) + res->a2 = FFA_ERR_STAT_INVALID_PARAMETERS; + else + res->a2 = FFA_ERR_STAT_NO_MEMORY; + + ffa_err("[Sandbox] error in FFA_RXTX_MAP arguments (%d)", (int)res->a2); + +feedback: + + res->a1 = 0; + + /* w3-w7 MBZ */ + memset(FFA_W3_MBZ_REG_START, + 0, FFA_W3W7_MBZ_CNT * sizeof(unsigned long)); + + return FFA_ERR_STAT_SUCCESS; +} + +/** + * sandbox_ffa_rxtx_unmap - Emulated FFA_RXTX_UNMAP handler function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_RXTX_UNMAP FF-A function. + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_rxtx_unmap) +{ + res->a0 = FFA_ERROR; + res->a2 = FFA_ERR_STAT_INVALID_PARAMETERS; + + if (GET_NS_PHYS_ENDPOINT_ID(a1) != sandbox_ffa_priv_data.id) + goto feedback; + + if (sandbox_ffa_priv_data.pair.txbuf && sandbox_ffa_priv_data.pair.rxbuf) { + sandbox_ffa_priv_data.pair.txbuf = 0; + sandbox_ffa_priv_data.pair.rxbuf = 0; + sandbox_ffa_priv_data.pair_info.rxtx_buf_size = 0; + sandbox_ffa_priv_data.pair_info.rxbuf_mapped = 0; + res->a0 = FFA_SUCCESS; + res->a2 = 0; + goto feedback; + } + + ffa_err("[Sandbox] No buffer pair registered on behalf of the caller"); + +feedback: + + res->a1 = 0; + + /* w3-w7 MBZ */ + memset(FFA_W3_MBZ_REG_START, + 0, FFA_W3W7_MBZ_CNT * sizeof(unsigned long)); + + return FFA_ERR_STAT_SUCCESS; +} + +/** + * sandbox_ffa_rx_release - Emulated FFA_RX_RELEASE handler function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_RX_RELEASE FF-A function. + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_rx_release) +{ + if (!sandbox_ffa_priv_data.pair_info.rxbuf_owned) { + res->a0 = FFA_ERROR; + res->a2 = FFA_ERR_STAT_DENIED; + } else { + sandbox_ffa_priv_data.pair_info.rxbuf_owned = 0; + res->a0 = FFA_SUCCESS; + res->a2 = 0; + } + + res->a1 = 0; + + /* w3-w7 MBZ */ + memset(FFA_W3_MBZ_REG_START, + 0, FFA_W3W7_MBZ_CNT * sizeof(unsigned long)); + + return FFA_ERR_STAT_SUCCESS; +} + +/** + * sandbox_ffa_sp_valid - Checks SP validity + * @part_id: partition ID to check + * + * This is the function searches the input ID in the descriptors table. + * + * Return: + * + * 1 on success (Partition found). Otherwise, failure + */ +int sandbox_ffa_sp_valid(u16 part_id) +{ + u32 descs_cnt; + + for (descs_cnt = 0 ; descs_cnt < SANDBOX_PARTITIONS_CNT ; descs_cnt++) + if (sandbox_ffa_priv_data.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 function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_MSG_SEND_DIRECT_{REQ,RESP} + * FF-A functions. + * + * Emulating interrupts is not supported. So, FFA_RUN and FFA_INTERRUPT are not supported. + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_msg_send_direct_req) +{ + u16 part_id; + + part_id = GET_DST_SP_ID(a1); + + if ((GET_NS_PHYS_ENDPOINT_ID(a1) != sandbox_ffa_priv_data.id) || + !sandbox_ffa_sp_valid(part_id) || + a2) { + res->a0 = FFA_ERROR; + res->a1 = 0; + res->a2 = FFA_ERR_STAT_INVALID_PARAMETERS; + + /* w3-w7 MBZ */ + memset(FFA_W3_MBZ_REG_START, + 0, FFA_W3W7_MBZ_CNT * sizeof(unsigned long)); + + return FFA_ERR_STAT_SUCCESS; + } + + res->a0 = FFA_MSG_SEND_DIRECT_RESP; + + res->a1 = PREP_SRC_SP_ID(part_id) | + PREP_NS_PHYS_ENDPOINT_ID(sandbox_ffa_priv_data.id); + + res->a2 = 0; + + /* + * return 0xff bytes as a response + */ + res->a3 = 0xffffffff; + res->a4 = 0xffffffff; + res->a5 = 0xffffffff; + res->a6 = 0xffffffff; + res->a7 = 0xffffffff; + + return FFA_ERR_STAT_SUCCESS; +} + +/** + * sandbox_ffa_get_prv_data - Returns the pointer to FF-A core pivate data + * @func_data: Pointer to the FF-A function arguments container structure + * + * This is the handler that returns the address of the FF-A core pivate data. + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +int sandbox_ffa_get_prv_data(struct ffa_interface_data *func_data) +{ + if (!func_data) + return -EINVAL; + + if (!func_data->data0 || func_data->data0_size != sizeof(struct ffa_prvdata **)) + return -EINVAL; + + if (!func_data->data1 || func_data->data1_size != sizeof(struct sandbox_ffa_prvdata **)) + return -EINVAL; + + *((struct ffa_prvdata **)func_data->data0) = &ffa_priv_data; + *((struct sandbox_ffa_prvdata **)func_data->data1) = &sandbox_ffa_priv_data; + + return FFA_ERR_STAT_SUCCESS; +} + +/** + * sandbox_ffa_get_rxbuf_flags - Reading the mapping/ownership flags + * @queried_func_id: The FF-A function to be queried + * @func_data: Pointer to the FF-A function arguments container structure + * + * This is the handler that queries the status flags of the following emulated ABIs: + * FFA_RXTX_MAP, FFA_RXTX_UNMAP, FFA_RX_RELEASE + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +int sandbox_ffa_get_rxbuf_flags(u32 queried_func_id, struct ffa_interface_data *func_data) +{ + 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) = sandbox_ffa_priv_data.pair_info.rxbuf_mapped; + return FFA_ERR_STAT_SUCCESS; + case FFA_RX_RELEASE: + *((u8 *)func_data->data0) = sandbox_ffa_priv_data.pair_info.rxbuf_owned; + return FFA_ERR_STAT_SUCCESS; + default: + ffa_err("[Sandbox] The querried FF-A interface flag (%d) undefined", + queried_func_id); + return -EINVAL; + } +} + +/** + * invoke_sandbox_ffa_drv_api - The driver dispatcher function + * @queried_func_id: The FF-A function to be queried + * @func_data: Pointer to the FF-A function arguments container structure + * + * The dispatcher function that selects the handler that queries the + * status of FF-A ABIs given in the input argument. + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +int invoke_sandbox_ffa_drv_api(u32 queried_func_id, struct ffa_interface_data *func_data) +{ + switch (queried_func_id) { + case FFA_VERSION: + case FFA_ID_GET: + case FFA_FEATURES: + return sandbox_ffa_get_prv_data(func_data); + case FFA_RXTX_MAP: + case FFA_RXTX_UNMAP: + case FFA_RX_RELEASE: + return sandbox_ffa_get_rxbuf_flags(queried_func_id, func_data); + default: + ffa_err("[Sandbox] The querried FF-A interface (%d) undefined", queried_func_id); + return -EINVAL; + } +} + +/** + * sandbox_ffa_init_prv_data_from_dtb - init private data fields from DTB + * @dev: the sandbox_arm_ffa device + * + * The value of the conduit field in the private data structure is read from the DTB. + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +static int sandbox_ffa_init_prv_data_from_dtb(struct udevice *dev) +{ + struct ffa_pdata *pdata = dev_get_plat(dev); + + sandbox_ffa_priv_data.conduit = pdata->conduit; + + if (sandbox_ffa_priv_data.conduit != FFA_CONDUIT_SMC) { + ffa_err("[Sandbox] Undefined FF-A conduit (%d)", sandbox_ffa_priv_data.conduit); + return -EINVAL; + } + + ffa_info("[Sandbox] Conduit is SMC"); + + return FFA_ERR_STAT_SUCCESS; +} + +/** + * sandbox_arm_ffa_smccc_smc - FF-A SMC call emulation + * @dev: the SMC arguments to be passed to the FF-A ABI + * + * Sandbox driver emulates the FF-A ABIs SMC call using this function. + * The emulated FF-A ABI is identified and invoked. + * FF-A emulation is based on the FF-A specification 1.0 + * + * Return: + * + * FFA_ERR_STAT_SUCCESS 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(unsigned long a0, unsigned long a1, + unsigned long a2, unsigned long a3, unsigned long a4, + unsigned long a5, unsigned long a6, unsigned long a7, + struct arm_smccc_res *res) +{ + int ret = FFA_ERR_STAT_SUCCESS; + + switch (a0) { + case FFA_VERSION: + ret = sandbox_ffa_version(a0, a1, a2, a3, a4, a5, a6, a7, res); + break; + case FFA_PARTITION_INFO_GET: + ret = sandbox_ffa_partition_info_get(a0, a1, a2, a3, a4, a5, a6, a7, res); + break; + case FFA_RXTX_UNMAP: + ret = sandbox_ffa_rxtx_unmap(a0, a1, a2, a3, a4, a5, a6, a7, res); + break; + case FFA_MSG_SEND_DIRECT_REQ: + ret = sandbox_ffa_msg_send_direct_req(a0, a1, a2, a3, a4, a5, a6, a7, res); + break; + case FFA_ID_GET: + ret = sandbox_ffa_id_get(a0, a1, a2, a3, a4, a5, a6, a7, res); + break; + case FFA_FEATURES: + ret = sandbox_ffa_features(a0, a1, a2, a3, a4, a5, a6, a7, res); + break; + case FFA_RXTX_MAP: + ret = sandbox_ffa_rxtx_map(a0, a1, a2, a3, a4, a5, a6, a7, res); + break; + case FFA_RX_RELEASE: + ret = sandbox_ffa_rx_release(a0, a1, a2, a3, a4, a5, a6, a7, res); + break; + default: + ffa_err("[Sandbox] Undefined FF-A interface (0x%x)", (unsigned int)a0); + } + + if (ret != FFA_ERR_STAT_SUCCESS) + ffa_err("[Sandbox] FF-A ABI internal failure (%d)", ret); +} + +/** + * sandbox_ffa_bind - The driver bind function + * @dev: the arm_ffa device + * + * Called when the driver is bound with the device based on the DTB node. + * The private data structure is cleared before use. + * This makes sure that when the device added/removed multiple times the previous + * private structure is cleared. + * + * Return: + * + * FFA_ERR_STAT_SUCCESS + */ +static int sandbox_ffa_bind(struct udevice *dev) +{ + memset(&sandbox_ffa_priv_data, 0, sizeof(sandbox_ffa_priv_data)); + + return FFA_ERR_STAT_SUCCESS; +} + +/** + * sandbox_ffa_probe - The driver probe function + * @dev: the sandbox_arm_ffa device + * + * Probing is triggered by the uclass device discovery. + * At probe level the device tree conduit in the private structure + * is read from the DTB. + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +static int sandbox_ffa_probe(struct udevice *dev) +{ + return sandbox_ffa_init_prv_data_from_dtb(dev); +} + +/** + * sandbox_ffa_of_to_plat - Reads the device tree node + * @dev: the sandbox_arm_ffa device + * + * This function reads data from the device tree node and populates + * the platform data structure + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +static int sandbox_ffa_of_to_plat(struct udevice *dev) +{ + struct ffa_pdata *pdata = dev_get_plat(dev); + const char *conduit; + + conduit = dev_read_string(dev, "method"); + + if (!conduit) { + ffa_err("[Sandbox] Failure to read the conduit from device tree"); + return -EINVAL; + } + + if (strcmp("smc", conduit)) { + ffa_err("[Sandbox] Unsupported conduit"); + return -EINVAL; + } + + pdata->conduit = FFA_CONDUIT_SMC; + + return FFA_ERR_STAT_SUCCESS; +} + +/** + * sandbox_ffa_drv_ops - The driver operations structure + * @invoke_func: The driver dispatcher + */ +struct ffa_ops sandbox_ffa_drv_ops = { + .invoke_func = invoke_sandbox_ffa_drv_api +}; + +/** + * sandbox_ffa_device_get_ops - driver operations getter + * + * Return: + * This function returns a pointer to the driver operations structure + */ +const struct ffa_ops *sandbox_ffa_device_get_ops(void) +{ + return &sandbox_ffa_drv_ops; +} + +/** + * Defining the device tree compatible string + */ +static const struct udevice_id sandbox_ffa_match_id[] = { + {"sandbox,ffa", 0}, + {}, +}; + +/** + * Declaring the sandbox_arm_ffa driver under UCLASS_FFA + */ +U_BOOT_DRIVER(sandbox_arm_ffa) = { + .name = "sandbox_arm_ffa", + .of_match = sandbox_ffa_match_id, + .id = UCLASS_FFA, + .of_to_plat = sandbox_ffa_of_to_plat, + .probe = sandbox_ffa_probe, + .bind = sandbox_ffa_bind, + .plat_auto = sizeof(struct ffa_pdata), +}; diff --git a/drivers/arm-ffa/sandbox_arm_ffa_prv.h b/drivers/arm-ffa/sandbox_arm_ffa_prv.h new file mode 100644 index 0000000000..9f5085f098 --- /dev/null +++ b/drivers/arm-ffa/sandbox_arm_ffa_prv.h @@ -0,0 +1,128 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __SANDBOX_ARM_FFA_PRV_H +#define __SANDBOX_ARM_FFA_PRV_H + +#include "arm_ffa_prv.h" + +/* + * This header is private. It is exclusively used by the Sandbox FF-A driver + */ + +/* 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 */ + +/* w1-w7 MBZ */ +#define FFA_W1W7_MBZ_CNT (7) +#define FFA_W1W7_MBZ_REG_START (&res->a1) + +/* w4-w7 MBZ */ +#define FFA_W4W7_MBZ_CNT (4) +#define FFA_W4W7_MBZ_REG_START (&res->a4) + +/* w3-w7 MBZ */ +#define FFA_W3W7_MBZ_CNT (5) +#define FFA_W3_MBZ_REG_START (&res->a3) + +/* secure partitions count */ +#define SANDBOX_PARTITIONS_CNT (4) + +/* service 1 UUID binary data (little-endian format) */ +#define SANDBOX_SERVICE1_UUID_DATA \ + 0xed, 0x32, 0xd5, 0x33, \ + 0x99, 0xe6, 0x42, 0x09, \ + 0x9c, 0xc0, 0x2d, 0x72, \ + 0xcd, 0xd9, 0x98, 0xa7 + +/* service 2 UUID binary data (little-endian format) */ +#define SANDBOX_SERVICE2_UUID_DATA \ + 0xab, 0xcd, 0xd5, 0x33, \ + 0x99, 0xe6, 0x42, 0x09, \ + 0x9c, 0xc0, 0x2d, 0x72, \ + 0xcd, 0xd9, 0x98, 0xa7 + +/** + * struct ffa_rxtxpair_info - structure hosting the RX/TX buffers flags + * @rxbuf_owned: RX buffer ownership flag (the owner is non secure world: the consumer) + * @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 as set by the FF-A core driver + * + * Data structure hosting 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_prvdata - the driver private data structure + * + * @dev: The arm_ffa device under u-boot driver model + * @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 + * @conduit: The selected conduit + * + * The driver data structure hosting all the emulated secure world data. + */ +struct sandbox_ffa_prvdata { + struct udevice *dev; + u32 fwk_version; + u16 id; + struct ffa_partitions partitions; + struct ffa_rxtxpair pair; + struct ffa_rxtxpair_info pair_info; + enum ffa_conduit conduit; +}; + +void sandbox_arm_ffa_smccc_smc(unsigned long a0, unsigned long a1, + unsigned long a2, unsigned long a3, unsigned long a4, + unsigned long a5, unsigned long a6, unsigned long a7, + struct arm_smccc_res *res); + +#define SANDBOX_SMC_FFA_ABI(ffabi) static int sandbox_##ffabi(unsigned long a0, unsigned long a1, \ + unsigned long a2, unsigned long a3, unsigned long a4, \ + unsigned long a5, unsigned long a6, unsigned long a7, \ + struct arm_smccc_res *res) + +/* The core FF-A private data structure to inspect */ +extern struct ffa_prvdata ffa_priv_data; + +#endif diff --git a/include/arm_ffa.h b/include/arm_ffa.h index 5a2151f8e4..bff565f2af 100644 --- a/include/arm_ffa.h +++ b/include/arm_ffa.h @@ -22,6 +22,13 @@ #define ffa_info(fmt, ...) pr_info("[FFA] " fmt "\n", ##__VA_ARGS__) #define ffa_err(fmt, ...) pr_err("[FFA] " fmt "\n", ##__VA_ARGS__)
+/* panic only on real HW. On sandbox mode return an error code */ +#if CONFIG_IS_ENABLED(SANDBOX_FFA) +#define ffa_panic(fmt, ...) ffa_err("[FFA] " fmt "\n", ##__VA_ARGS__) +#else +#define ffa_panic(fmt, ...) panic("[FFA] " fmt "\n", ##__VA_ARGS__) +#endif + /* * The driver operations success error code */ @@ -184,7 +191,7 @@ const struct ffa_ops * __ffa_runtime ffa_device_get_ops(void); int ffa_get_device(void);
/** - * ffa_init_device - probes the arm_ffa device + * ffa_init_device - probes the arm_ffa and arm_ffa devices */ int ffa_init_device(void); #endif diff --git a/include/sandbox_arm_ffa.h b/include/sandbox_arm_ffa.h new file mode 100644 index 0000000000..fc681018ff --- /dev/null +++ b/include/sandbox_arm_ffa.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __SANDBOX_ARM_FFA_H +#define __SANDBOX_ARM_FFA_H + +#include <arm_ffa.h> + +/** + * The device driver and the Uclass driver public functions + */ + +/** + * sandbox_ffa_get_invoke_func - performs a call to the Sandbox FF-A driver dispatcher + */ +int sandbox_ffa_get_invoke_func(u32 func_id, struct ffa_interface_data *func_data); + +/** + * sandbox_ffa_device_get_ops - driver operations getter + */ +const struct ffa_ops *sandbox_ffa_device_get_ops(void); + +/** + * sandbox_ffa_get_device - probes the sandbox_arm_ffa device + */ +int sandbox_ffa_get_device(void); + +#endif diff --git a/include/sandbox_arm_ffa_helper.h b/include/sandbox_arm_ffa_helper.h new file mode 100644 index 0000000000..f0fcd04536 --- /dev/null +++ b/include/sandbox_arm_ffa_helper.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __SANDBOX_ARM_FFA_HELPER_H +#define __SANDBOX_ARM_FFA_HELPER_H + +#include <arm_ffa_helper.h> +#include <sandbox_arm_ffa.h> +#include "../drivers/arm-ffa/sandbox_arm_ffa_prv.h" + +/* + * This header is public. Including this header provides all FF-A Sandbox data structures + * It also provides the helper function allowing to pass data and invoke Sandbox FF-A functions + * used for testing the FF-A core driver + */ + +/** + * sandbox_ffa_helper_query_core_state - Wrapper function for + * reading the FF-A core driver data + */ +int sandbox_ffa_helper_query_core_state(u32 queried_func_id, struct ffa_interface_data *func_data); + +#endif diff --git a/lib/arm-ffa/Makefile b/lib/arm-ffa/Makefile index cba625fde4..04159da8eb 100644 --- a/lib/arm-ffa/Makefile +++ b/lib/arm-ffa/Makefile @@ -6,3 +6,4 @@ # This file only gets included when CONFIG_ARM_FFA_TRANSPORT_HELPERS is set
obj-y += arm_ffa_helper.o +obj-$(CONFIG_SANDBOX_FFA) += sandbox_arm_ffa_helper.o diff --git a/lib/arm-ffa/arm_ffa_helper.c b/lib/arm-ffa/arm_ffa_helper.c index 67a3a4e9ab..32ee3b4486 100644 --- a/lib/arm-ffa/arm_ffa_helper.c +++ b/lib/arm-ffa/arm_ffa_helper.c @@ -105,10 +105,10 @@ int __ffa_runtime ffa_helper_msg_send_direct_req(struct ffa_interface_data }
/** - * ffa_helper_init_device - Wrapper function for probing the arm_ffa device + * ffa_helper_init_device - Wrapper function for probing the arm_ffa and sandbox_arm_ffa devices * - * This boot time function should be called to probe the arm_ffa device so - * it becomes ready for use. + * This boot time function should be called to probe the arm_ffa and sandbox_arm_ffa devices so + * they become ready for use. * To achieve that, this function is called automatically at initcalls * level (after u-boot relocation). * diff --git a/lib/arm-ffa/sandbox_arm_ffa_helper.c b/lib/arm-ffa/sandbox_arm_ffa_helper.c new file mode 100644 index 0000000000..7859f30fc7 --- /dev/null +++ b/lib/arm-ffa/sandbox_arm_ffa_helper.c @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <sandbox_arm_ffa_helper.h> + +/** + * sandbox_ffa_helper_query_core_state - Wrapper function for querying FF-A implementation + * + * A helper function used for querying the status of FF-A ABIs given in the input argument + * and the FF-A core driver. + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +int sandbox_ffa_helper_query_core_state(u32 queried_func_id, struct ffa_interface_data *func_data) +{ + return sandbox_ffa_get_invoke_func(queried_func_id, func_data); +} diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index cffa2c69d6..12fc28fd82 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -2118,7 +2118,7 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, struct efi_event *evt, *next_event; efi_status_t ret = EFI_SUCCESS;
-#if defined(CONFIG_ARM_FFA_TRANSPORT) +#if defined(CONFIG_ARM_FFA_TRANSPORT) && !defined(CONFIG_SANDBOX_FFA) int ffa_ret; #endif
@@ -2182,7 +2182,7 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL); }
-#if defined(CONFIG_ARM_FFA_TRANSPORT) +#if defined(CONFIG_ARM_FFA_TRANSPORT) && !defined(CONFIG_SANDBOX_FFA) /* unmap FF-A RX/TX buffers */ ffa_ret = ffa_helper_unmap_rxtx_buffers(); if (ffa_ret)

From: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
Add functional test cases for the FF-A core driver
These tests rely on the FF-A Sandbox driver which helps in inspecting the FF-A core driver.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com --- MAINTAINERS | 2 + test/dm/Makefile | 1 + test/dm/ffa.c | 424 +++++++++++++++++++++++++++++++++++++++++++++++ test/dm/ffa.h | 22 +++ 4 files changed, 449 insertions(+) create mode 100644 test/dm/ffa.c create mode 100644 test/dm/ffa.h
diff --git a/MAINTAINERS b/MAINTAINERS index 84524d7caf..52274b2fac 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -242,6 +242,8 @@ F: include/arm_ffa_helper.h F: include/sandbox_arm_ffa.h F: include/sandbox_arm_ffa_helper.h F: lib/arm-ffa/ +F: test/dm/ffa.c +F: test/dm/ffa.h
ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de diff --git a/test/dm/Makefile b/test/dm/Makefile index d46552fbf3..48eab1b1ff 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -79,6 +79,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_SANDBOX_FFA) += 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 0000000000..82875705c6 --- /dev/null +++ b/test/dm/ffa.c @@ -0,0 +1,424 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Functional tests for UCLASS_FFA class + * + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <console.h> +#include <dm.h> +#include <dm/test.h> +#include "ffa.h" +#include <test/test.h> +#include <test/ut.h> +#include <sandbox_arm_ffa_helper.h> +#include <string.h> + +/* Functional tests for the UCLASS_FFA */ + +int dm_test_ffa_log(struct unit_test_state *uts, char *msg) +{ + char cmd[LOG_CMD_SZ] = {0}; + + console_record_reset(); + + snprintf(cmd, LOG_CMD_SZ, "echo "%s"", msg); + run_command(cmd, 0); + + ut_assert_console_end(); + + return CMD_RET_SUCCESS; +} + +static int check_fwk_version(struct ffa_prvdata *prvdata, struct unit_test_state *uts) +{ + if (prvdata->fwk_version != SANDBOX_FWK_VERSION) { + char msg[LOG_MSG_SZ] = {0}; + + snprintf(msg, LOG_MSG_SZ, "[%s]: Error: prvdata->fwk_version = 0x%x", __func__, + prvdata->fwk_version); + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + return CMD_RET_SUCCESS; +} + +static int check_endpoint_id(struct ffa_prvdata *prvdata, struct unit_test_state *uts) +{ + if (prvdata->id) { + char msg[LOG_MSG_SZ] = {0}; + + snprintf(msg, LOG_MSG_SZ, "[%s]: Error: prvdata->id = 0x%x", __func__, prvdata->id); + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + return CMD_RET_SUCCESS; +} + +static int check_dev(struct ffa_prvdata *prvdata, struct unit_test_state *uts) +{ + if (!prvdata->dev) { + char msg[LOG_MSG_SZ] = {0}; + + snprintf(msg, LOG_MSG_SZ, "[%s]: Error: device = 0x%p", __func__, prvdata->dev); + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + return CMD_RET_SUCCESS; +} + +static int check_prvdata_null(struct ffa_prvdata *prvdata) +{ + u32 idx; + u8 *byte = (u8 *)prvdata; + + for (idx = 0 ; idx < sizeof(struct ffa_prvdata) ; idx++) + if (*(byte++)) + return CMD_RET_FAILURE; + + return CMD_RET_SUCCESS; +} + +static int check_rxtxbuf(struct ffa_prvdata *prvdata, struct unit_test_state *uts) +{ + if (!prvdata->pair.rxbuf && prvdata->pair.txbuf) { + char msg[LOG_MSG_SZ] = {0}; + + snprintf(msg, LOG_MSG_SZ, "[%s]: Error: rxbuf = 0x%llx txbuf = 0x%llx", __func__, + prvdata->pair.rxbuf, + prvdata->pair.txbuf); + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + return CMD_RET_SUCCESS; +} + +static int check_features(struct ffa_prvdata *prvdata, struct unit_test_state *uts) +{ + u32 desc_idx; + char msg[LOG_MSG_SZ] = {0}; + + snprintf(msg, + LOG_MSG_SZ, + "[%s]: Error: FFA_RXTX_MAP features not found", + __func__); + + for (desc_idx = 0; desc_idx < FFA_FEATURE_DESC_CNT ; desc_idx++) + if (prvdata->features[desc_idx].func_id == FFA_RXTX_MAP) { + if (prvdata->features[desc_idx].field1 != RXTX_4K && + prvdata->features[desc_idx].field1 != RXTX_16K && + prvdata->features[desc_idx].field1 != RXTX_64K) { + snprintf(msg, + LOG_MSG_SZ, + "[%s]: Error: FFA_RXTX_MAP features = 0x%x", + __func__, + prvdata->features[desc_idx].field1); + break; + } + return CMD_RET_SUCCESS; + } + + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; +} + +static int check_rxbuf_mapped_flag(u32 queried_func_id, + u8 rxbuf_mapped, + struct unit_test_state *uts) +{ + char msg[LOG_MSG_SZ] = {0}; + + switch (queried_func_id) { + case FFA_RXTX_MAP: + { + if (rxbuf_mapped) + return CMD_RET_SUCCESS; + break; + } + case FFA_RXTX_UNMAP: + { + if (!rxbuf_mapped) + return CMD_RET_SUCCESS; + break; + } + default: + return CMD_RET_FAILURE; + } + + snprintf(msg, LOG_MSG_SZ, "[%s]: Error: %s mapping issue", __func__, + (queried_func_id == FFA_RXTX_MAP ? "FFA_RXTX_MAP" : "FFA_RXTX_UNMAP")); + dm_test_ffa_log(uts, msg); + + return CMD_RET_FAILURE; +} + +static int check_rxbuf_release_flag(u8 rxbuf_owned, struct unit_test_state *uts) +{ + if (rxbuf_owned) { + char msg[LOG_MSG_SZ] = {0}; + + snprintf(msg, LOG_MSG_SZ, "[%s]: Error: RX buffer not released", __func__); + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + return CMD_RET_SUCCESS; +} + +static int test_ffa_msg_send_direct_req(u16 part_id, struct unit_test_state *uts) +{ + struct ffa_interface_data func_data = {0}; + struct ffa_send_direct_data msg = {0}; + u32 pattern = 0xaabbccdd; + u8 cnt; + + /* + * telling the driver which partition to use + */ + func_data.data0_size = sizeof(part_id); + func_data.data0 = &part_id; + + /* + * filling the message data + */ + msg.a3 = pattern; + msg.a4 = pattern; + msg.a5 = pattern; + msg.a6 = pattern; + msg.a7 = pattern; + func_data.data1_size = sizeof(msg); + func_data.data1 = &msg; + + ut_assertok(ffa_helper_msg_send_direct_req(&func_data)); + + for (cnt = 0; cnt < sizeof(struct ffa_send_direct_data) / sizeof(u32); cnt++) + ut_assertok(((u32 *)&msg)[cnt] != 0xffffffff); + + return CMD_RET_SUCCESS; +} + +static int test_partitions_and_comms(union ffa_partition_uuid *service_uuid, + struct sandbox_ffa_prvdata *sdx_prvdata, + struct unit_test_state *uts) +{ + struct ffa_interface_data func_data = {0}; + u32 count = 0; + struct ffa_partition_info *parts_info; + u32 info_idx, exp_info_idx; + int ret; + + /* + * get from the driver the count of the SPs matching the UUID + */ + func_data.data0_size = sizeof(*service_uuid); + func_data.data0 = service_uuid; + func_data.data1_size = sizeof(count); + func_data.data1 = &count; + + ut_assertok(ffa_helper_get_partitions_info(&func_data)); + + /* make sure partitions are detected */ + ut_assertok(!count); + + /* + * pre-allocate a buffer to be filled by the driver + * with ffa_partition_info structs + */ + + parts_info = calloc(count, sizeof(struct ffa_partition_info)); + ut_assertok(!parts_info); + + func_data.data1_size = count * sizeof(struct ffa_partition_info); + func_data.data1 = parts_info; + + /* + * ask the driver to fill the buffer with the SPs info + */ + ret = ffa_helper_get_partitions_info(&func_data); + if (ret != FFA_ERR_STAT_SUCCESS) { + free(parts_info); + ut_assertok(ret != FFA_ERR_STAT_SUCCESS); + } + + /* + * SPs found , verify the partitions information + */ + + ret = CMD_RET_FAILURE; + + for (info_idx = 0; info_idx < count ; info_idx++) { + for (exp_info_idx = 0; + exp_info_idx < sdx_prvdata->partitions.count; + exp_info_idx++) { + if (parts_info[info_idx].id == + sdx_prvdata->partitions.descs[exp_info_idx].info.id) { + ret = memcmp(&parts_info[info_idx], + &sdx_prvdata->partitions.descs[exp_info_idx] + .info, + sizeof(struct ffa_partition_info)); + if (ret) + free(parts_info); + ut_assertok(ret != 0); + /* send and receive data from the current partition */ + test_ffa_msg_send_direct_req(parts_info[info_idx].id, uts); + } + ret = CMD_RET_SUCCESS; + } + } + + free(parts_info); + + /* Verify expected partitions found in the emulated secure world*/ + ut_assertok(ret != CMD_RET_SUCCESS); + + return CMD_RET_SUCCESS; +} + +static int dm_test_ffa_ack(struct unit_test_state *uts) +{ + struct ffa_prvdata *prvdata = NULL; + struct sandbox_ffa_prvdata *sdx_prvdata = NULL; + struct ffa_interface_data func_data = {0}; + u8 rxbuf_flag = 0; + union ffa_partition_uuid svc1_uuid = { .bytes = {SANDBOX_SERVICE1_UUID_DATA}}; + union ffa_partition_uuid svc2_uuid = { .bytes = {SANDBOX_SERVICE2_UUID_DATA}}; + int ret; + + /* get a pointer to the FF-A core and sandbox drivers private data */ + func_data.data0 = &prvdata; + func_data.data0_size = sizeof(prvdata); + func_data.data1 = &sdx_prvdata; + func_data.data1_size = sizeof(sdx_prvdata); + + ut_assertok(sandbox_ffa_helper_query_core_state(FFA_VERSION, &func_data)); + + /* make sure the core private data is cleared before use */ + ut_assertok(check_prvdata_null(prvdata)); + + /* test probing FF-A devices */ + ut_assertok(ffa_helper_init_device()); + ut_assertok(check_dev(prvdata, uts)); + + /* test FFA_VERSION */ + ut_assertok(check_fwk_version(prvdata, uts)); + + /* test FFA_ID_GET */ + ut_assertok(check_endpoint_id(prvdata, uts)); + + /* test FFA_FEATURES */ + ut_assertok(check_features(prvdata, uts)); + + /* test core RX/TX buffers */ + ut_assertok(check_rxtxbuf(prvdata, uts)); + + /* test FFA_RXTX_MAP */ + func_data.data0 = &rxbuf_flag; + func_data.data0_size = sizeof(rxbuf_flag); + + rxbuf_flag = 0; + ut_assertok(sandbox_ffa_helper_query_core_state(FFA_RXTX_MAP, &func_data)); + ut_assertok(check_rxbuf_mapped_flag(FFA_RXTX_MAP, rxbuf_flag, uts)); + + /* FFA_PARTITION_INFO_GET / FFA_MSG_SEND_DIRECT_REQ */ + ret = test_partitions_and_comms(&svc1_uuid, sdx_prvdata, uts); + ut_assertok(ret != CMD_RET_SUCCESS); + + /* test FFA_RX_RELEASE */ + rxbuf_flag = 1; + ut_assertok(sandbox_ffa_helper_query_core_state(FFA_RX_RELEASE, &func_data)); + ut_assertok(check_rxbuf_release_flag(rxbuf_flag, uts)); + + /* FFA_PARTITION_INFO_GET / FFA_MSG_SEND_DIRECT_REQ */ + ret = test_partitions_and_comms(&svc2_uuid, sdx_prvdata, uts); + ut_assertok(ret != CMD_RET_SUCCESS); + + /* test FFA_RX_RELEASE */ + rxbuf_flag = 1; + ut_assertok(sandbox_ffa_helper_query_core_state(FFA_RX_RELEASE, &func_data)); + ut_assertok(check_rxbuf_release_flag(rxbuf_flag, uts)); + + /* test FFA_RXTX_UNMAP */ + ut_assertok(ffa_helper_unmap_rxtx_buffers()); + + rxbuf_flag = 1; + ut_assertok(sandbox_ffa_helper_query_core_state(FFA_RXTX_UNMAP, &func_data)); + ut_assertok(check_rxbuf_mapped_flag(FFA_RXTX_UNMAP, rxbuf_flag, uts)); + + return CMD_RET_SUCCESS; +} + +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_prvdata *prvdata = NULL; + struct sandbox_ffa_prvdata *sdx_prvdata = NULL; + + struct ffa_interface_data func_data = {0}; + union ffa_partition_uuid valid_svc_uuid = { .bytes = {SANDBOX_SERVICE1_UUID_DATA}}; + union ffa_partition_uuid unvalid_svc_uuid = { .bytes = {SANDBOX_SERVICE3_UUID_DATA}}; + struct ffa_send_direct_data msg = {0}; + int ret; + u32 count = 0; + u16 part_id = 0; + + /* get partitions info before probing the core driver */ + func_data.data0_size = sizeof(union ffa_partition_uuid); + func_data.data0 = &unvalid_svc_uuid; + func_data.data1_size = sizeof(count); + func_data.data1 = &count; + + ret = ffa_helper_get_partitions_info(&func_data); + ut_assertok(ret != -ENODEV); + + /* get a pointer to the FF-A core and sandbox drivers private data */ + func_data.data0 = &prvdata; + func_data.data0_size = sizeof(prvdata); + func_data.data1 = &sdx_prvdata; + func_data.data1_size = sizeof(sdx_prvdata); + ut_assertok(sandbox_ffa_helper_query_core_state(FFA_VERSION, &func_data)); + + /* probing FF-A devices */ + ut_assertok(ffa_helper_init_device()); + ut_assertok(check_dev(prvdata, uts)); + + /* query partition info using invalid arguments */ + ret = ffa_helper_get_partitions_info(&func_data); + ut_assertok(ret != -EINVAL); + + /* query partition info using an invalid UUID */ + func_data.data0_size = sizeof(union ffa_partition_uuid); + func_data.data0 = &unvalid_svc_uuid; + func_data.data1_size = sizeof(count); + func_data.data1 = &count; + + ret = ffa_helper_get_partitions_info(&func_data); + ut_assertok(ret != -EPERM); + + /* query partition info using a valid UUID */ + func_data.data0 = &valid_svc_uuid; + count = 0; + ret = ffa_helper_get_partitions_info(&func_data); + /* make sure partitions are detected */ + ut_assertok(ret != FFA_ERR_STAT_SUCCESS); + ut_assertok(!count); + + /* send data to an invalid partition */ + func_data.data0_size = sizeof(part_id); + func_data.data0 = &part_id; + func_data.data1_size = sizeof(msg); + func_data.data1 = &msg; + + ret = ffa_helper_msg_send_direct_req(&func_data); + ut_assertok(ret != -EPERM); + + /* send data to a valid partition */ + part_id = ffa_priv_data.partitions.descs[0].info.id; + ret = ffa_helper_msg_send_direct_req(&func_data); + ut_assertok(ret != FFA_ERR_STAT_SUCCESS); + + return CMD_RET_SUCCESS; +} + +DM_TEST(dm_test_ffa_nack, UT_TESTF_SCAN_FDT | UT_TESTF_CONSOLE_REC); diff --git a/test/dm/ffa.h b/test/dm/ffa.h new file mode 100644 index 0000000000..11c7980fcf --- /dev/null +++ b/test/dm/ffa.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __TEST_DM_FFA_H +#define __TEST_DM_FFA_H + +#define SANDBOX_FWK_VERSION (0x10000) + +#define LOG_MSG_SZ (100) +#define LOG_CMD_SZ (LOG_MSG_SZ * 2) + +/* service 3 UUID binary data (little-endian format) */ +#define SANDBOX_SERVICE3_UUID_DATA \ + 0xfd, 0x42, 0xd5, 0x33, \ + 0x19, 0xa6, 0x42, 0x09, \ + 0x9c, 0xc1, 0x2d, 0x72, \ + 0xcd, 0xd9, 0x98, 0x89 + +#endif /*__TEST_DM_FFA_H */

From: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
Add Sandbox test for the armffa command
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com --- MAINTAINERS | 2 ++ test/cmd/Makefile | 1 + test/cmd/armffa.c | 33 +++++++++++++++++++++++++++++++++ test/cmd/armffa.h | 13 +++++++++++++ 4 files changed, 49 insertions(+) create mode 100644 test/cmd/armffa.c create mode 100644 test/cmd/armffa.h
diff --git a/MAINTAINERS b/MAINTAINERS index 52274b2fac..9828095837 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -242,6 +242,8 @@ F: include/arm_ffa_helper.h F: include/sandbox_arm_ffa.h F: include/sandbox_arm_ffa_helper.h F: lib/arm-ffa/ +F: test/cmd/armffa.c +F: test/cmd/armffa.h F: test/dm/ffa.c F: test/dm/ffa.h
diff --git a/test/cmd/Makefile b/test/cmd/Makefile index a59adb1e6d..d9dc0809d6 100644 --- a/test/cmd/Makefile +++ b/test/cmd/Makefile @@ -11,3 +11,4 @@ obj-$(CONFIG_CMD_MEM_SEARCH) += mem_search.o obj-$(CONFIG_CMD_PINMUX) += pinmux.o obj-$(CONFIG_CMD_PWM) += pwm.o obj-$(CONFIG_CMD_SETEXPR) += setexpr.o +obj-$(CONFIG_SANDBOX_FFA) += armffa.o diff --git a/test/cmd/armffa.c b/test/cmd/armffa.c new file mode 100644 index 0000000000..d35032047b --- /dev/null +++ b/test/cmd/armffa.c @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test for armffa command + * + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include "armffa.h" +#include <arm_ffa_helper.h> +#include <dm.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +/* Basic test of 'armffa' command */ +static int dm_test_armffa_cmd(struct unit_test_state *uts) +{ + ut_assertok(ffa_helper_init_device()); + + /* armffa getpart <UUID> */ + ut_assertok(run_command("armffa getpart " SE_PROXY_PARTITION_UUID, 0)); + + /* armffa ping <ID> */ + ut_assertok(run_command("armffa ping " SE_PROXY_PARTITION_ID, 0)); + + /* armffa devlist */ + ut_assertok(run_command("armffa devlist", 0)); + + return CMD_RET_SUCCESS; +} + +DM_TEST(dm_test_armffa_cmd, UT_TESTF_SCAN_FDT | UT_TESTF_CONSOLE_REC); diff --git a/test/cmd/armffa.h b/test/cmd/armffa.h new file mode 100644 index 0000000000..630dc75e25 --- /dev/null +++ b/test/cmd/armffa.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __TEST_CMD_FFA_H +#define __TEST_CMD_FFA_H + +#define SE_PROXY_PARTITION_ID "0x1245" +#define SE_PROXY_PARTITION_UUID "33d532ed-e699-0942-c09c-a798d9cd722d" + +#endif /*__TEST_CMD_FFA_H */

From: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
Add MM communication support using FF-A transport
FF-A MM communication allows exchanging data with StandAlonneMM or smm-gateway secure partitions which run in OP-TEE.
An MM shared buffer and a door bell event are used to exchange this 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.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Signed-off-by: Gowtham Suresh Kumar gowtham.sureshkumar@arm.com Cc: Tom Rini trini@konsulko.com --- arch/arm/cpu/armv8/cache.S | 16 ++ arch/arm/cpu/armv8/cache_v8.c | 3 +- include/mm_communication.h | 4 +- lib/efi_loader/Kconfig | 14 +- lib/efi_loader/efi_variable_tee.c | 294 +++++++++++++++++++++++++++++- 5 files changed, 321 insertions(+), 10 deletions(-)
diff --git a/arch/arm/cpu/armv8/cache.S b/arch/arm/cpu/armv8/cache.S index d1cee23437..bdbe89e0c5 100644 --- a/arch/arm/cpu/armv8/cache.S +++ b/arch/arm/cpu/armv8/cache.S @@ -21,7 +21,11 @@ * x1: 0 clean & invalidate, 1 invalidate only * x2~x9: clobbered */ +#ifdef CONFIG_ARM_FFA_TRANSPORT +.pushsection .text.efi_runtime, "ax" +#else .pushsection .text.__asm_dcache_level, "ax" +#endif ENTRY(__asm_dcache_level) lsl x12, x0, #1 msr csselr_el1, x12 /* select cache level */ @@ -65,7 +69,11 @@ ENDPROC(__asm_dcache_level) * * flush or invalidate all data cache by SET/WAY. */ +#ifdef CONFIG_ARM_FFA_TRANSPORT +.pushsection .text.efi_runtime, "ax" +#else .pushsection .text.__asm_dcache_all, "ax" +#endif ENTRY(__asm_dcache_all) mov x1, x0 dsb sy @@ -109,7 +117,11 @@ ENTRY(__asm_flush_dcache_all) ENDPROC(__asm_flush_dcache_all) .popsection
+#ifdef CONFIG_ARM_FFA_TRANSPORT +.pushsection .text.efi_runtime, "ax" +#else .pushsection .text.__asm_invalidate_dcache_all, "ax" +#endif ENTRY(__asm_invalidate_dcache_all) mov x0, #0x1 b __asm_dcache_all @@ -182,7 +194,11 @@ ENTRY(__asm_invalidate_icache_all) ENDPROC(__asm_invalidate_icache_all) .popsection
+#ifdef CONFIG_ARM_FFA_TRANSPORT +.pushsection .text.efi_runtime, "ax" +#else .pushsection .text.__asm_invalidate_l3_dcache, "ax" +#endif WEAK(__asm_invalidate_l3_dcache) mov x0, #0 /* return status as success */ ret diff --git a/arch/arm/cpu/armv8/cache_v8.c b/arch/arm/cpu/armv8/cache_v8.c index 3de18c7675..187a4497a7 100644 --- a/arch/arm/cpu/armv8/cache_v8.c +++ b/arch/arm/cpu/armv8/cache_v8.c @@ -9,6 +9,7 @@
#include <common.h> #include <cpu_func.h> +#include <efi_loader.h> #include <hang.h> #include <log.h> #include <asm/cache.h> @@ -425,7 +426,7 @@ __weak void mmu_setup(void) /* * Performs a invalidation of the entire data cache at all levels */ -void invalidate_dcache_all(void) +void __efi_runtime invalidate_dcache_all(void) { __asm_invalidate_dcache_all(); __asm_invalidate_l3_dcache(); diff --git a/include/mm_communication.h b/include/mm_communication.h index e65fbde60d..bb99190956 100644 --- a/include/mm_communication.h +++ b/include/mm_communication.h @@ -123,7 +123,7 @@ struct __packed efi_mm_communicate_header { * * Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_HEADER. */ -struct smm_variable_communicate_header { +struct __packed smm_variable_communicate_header { efi_uintn_t function; efi_status_t ret_status; u8 data[]; @@ -145,7 +145,7 @@ struct smm_variable_communicate_header { * Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE. * */ -struct smm_variable_access { +struct __packed smm_variable_access { efi_guid_t guid; efi_uintn_t data_size; efi_uintn_t name_size; diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index 28657f50c9..0d69133595 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -55,13 +55,23 @@ 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" - depends on OPTEE + bool "UEFI variables storage service via the trusted world" + depends on OPTEE || ARM_FFA_TRANSPORT help + The MM SP (also called partition) can be StandAlonneMM or smm-gateway. + When using the u-boot OP-TEE driver, StandAlonneMM is supported. + When using the u-boot FF-A driver, StandAlonneMM and smm-gateway are 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 under Optee in the trusted 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. + endchoice
config EFI_VARIABLES_PRESEED diff --git a/lib/efi_loader/efi_variable_tee.c b/lib/efi_loader/efi_variable_tee.c index dfef18435d..3d01985662 100644 --- a/lib/efi_loader/efi_variable_tee.c +++ b/lib/efi_loader/efi_variable_tee.c @@ -15,6 +15,53 @@ #include <malloc.h> #include <mm_communication.h>
+#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) + +#include <arm_ffa_helper.h> +#include <cpu_func.h> +#include <mapmem.h> + +#ifndef FFA_SHARED_MM_BUFFER_SIZE + +#include <linux/sizes.h> +#define FFA_SHARED_MM_BUFFER_SIZE SZ_4K /* 4 KB */ + +#endif + +#ifndef FFA_SHARED_MM_BUFFER_ADDR + +/* + * shared buffer physical address used for communication between + * u-boot and the MM SP + */ +#define FFA_SHARED_MM_BUFFER_ADDR (0x023F8000) + +#endif + +/* MM return codes */ +#define MM_SUCCESS (0) + +#define ARM_SVC_ID_SP_EVENT_COMPLETE_AARCH64 (0xC4000061) +#define ARM_SVC_ID_SP_EVENT_COMPLETE ARM_SVC_ID_SP_EVENT_COMPLETE_AARCH64 + +#ifndef MM_SP_UUID_DATA + +/* MM SP UUID binary data (little-endian format) */ +#define MM_SP_UUID_DATA \ + 0xed, 0x32, 0xd5, 0x33, \ + 0x99, 0xe6, 0x42, 0x09, \ + 0x9c, 0xc0, 0x2d, 0x72, \ + 0xcd, 0xd9, 0x98, 0xa7 + +#endif + +/* MM_SP_UUID_DATA defined by the platform */ +union ffa_partition_uuid mm_sp_svc_uuid = {.bytes = {MM_SP_UUID_DATA}}; + +static __efi_runtime_data 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 */ static efi_uintn_t max_payload_size; /* func + data */ @@ -24,6 +71,7 @@ struct mm_connection { u32 session; };
+#if (IS_ENABLED(CONFIG_OPTEE)) /** * get_connection() - Retrieve OP-TEE session for a specific UUID. * @@ -143,16 +191,238 @@ static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize)
return ret; } +#endif + +#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) + +/** + * ffa_notify_mm_sp() - Announce there is data in the shared buffer + * + * Notifies 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 __efi_runtime ffa_notify_mm_sp(void) +{ + struct ffa_interface_data func_data = {0}; + struct ffa_send_direct_data msg = {0}; + int ret; + u32 sp_event_complete; + int sp_event_ret; + + func_data.data0_size = sizeof(mm_sp_id); + func_data.data0 = &mm_sp_id; + + msg.a3 = FFA_SHARED_MM_BUFFER_ADDR; + msg.a4 = FFA_SHARED_MM_BUFFER_SIZE; + func_data.data1_size = sizeof(msg); + func_data.data1 = &msg; + + ret = ffa_helper_msg_send_direct_req(&func_data); + if (ret != FFA_ERR_STAT_SUCCESS) + return ret; + + sp_event_complete = msg.a3; + sp_event_ret = (int)msg.a4; + + if (sp_event_complete == ARM_SVC_ID_SP_EVENT_COMPLETE && sp_event_ret == MM_SUCCESS) + return 0; + + /* + * Failure to notify the MM SP + */ + + return -EACCES; +} + +/** + * 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) +{ + struct ffa_interface_data func_data = {0}; + u32 count = 0; + int ret; + struct ffa_partition_info *parts_info; + + /* + * get from the driver the count of the SPs matching the UUID + */ + func_data.data0_size = sizeof(mm_sp_svc_uuid); + func_data.data0 = &mm_sp_svc_uuid; + func_data.data1_size = sizeof(count); + func_data.data1 = &count; + + ret = ffa_helper_get_partitions_info(&func_data); + if (ret != FFA_ERR_STAT_SUCCESS) { + log_err("EFI: Failure in querying partitions count (error code: %d)\n", ret); + return ret; + } + + if (!count) { + log_info("EFI: No MM partition found\n"); + return ret; + } + + /* + * pre-allocate a buffer to be filled by the driver + * with ffa_partition_info structs + */ + + parts_info = calloc(count, sizeof(struct ffa_partition_info)); + if (!parts_info) + return -EINVAL; + + log_info("EFI: Pre-allocating %d partition(s) info structures\n", count); + + func_data.data1_size = count * + sizeof(struct ffa_partition_info); + func_data.data1 = parts_info; + + /* + * ask the driver to fill the + * buffer with the SPs info + */ + ret = ffa_helper_get_partitions_info(&func_data); + if (ret != FFA_ERR_STAT_SUCCESS) { + log_err("EFI: Failure in querying partition(s) info (error code: %d)\n", ret); + free(parts_info); + return ret; + } + + /* + * MM SPs found , use the first one + */ + + mm_sp_id = parts_info[0].id; + + log_info("EFI: MM partition ID 0x%x\n", mm_sp_id); + + free(parts_info); + + return 0; +}
/** - * mm_communicate() - Adjust the cmonnucation buffer to StandAlonneMM and send + * 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 + * + * Issues 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 __efi_runtime ffa_mm_communicate(void *comm_buf, ulong comm_buf_size) +{ + ulong tx_data_size; + int ffa_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() != FFA_ERR_STAT_SUCCESS) { + log_err("EFI: Failure to discover MM partition ID at boot time\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 > FFA_SHARED_MM_BUFFER_SIZE) + return EFI_INVALID_PARAMETER; + + /* Copy the data to the shared buffer */ + + virt_shared_buf = (void *)map_sysmem((phys_addr_t)FFA_SHARED_MM_BUFFER_ADDR, 0); + efi_memcpy_runtime(virt_shared_buf, comm_buf, tx_data_size); + + /* + * The secure world has cache disabled for device region which we use for shared buffer. + * So, 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(); + if (ffa_ret) + unmap_sysmem(virt_shared_buf); + + 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_memcpy_runtime(comm_buf, virt_shared_buf, comm_buf_size); + unmap_sysmem(virt_shared_buf); + return EFI_BUFFER_TOO_SMALL; + } + + efi_memcpy_runtime(comm_buf, virt_shared_buf, rx_data_size); + unmap_sysmem(virt_shared_buf); + + return EFI_SUCCESS; + } + case -EINVAL: + return EFI_DEVICE_ERROR; + case -EPERM: + return EFI_INVALID_PARAMETER; + case -EACCES: + return EFI_ACCESS_DENIED; + case -EBUSY: + return EFI_OUT_OF_RESOURCES; + default: + return EFI_ACCESS_DENIED; + } +} +#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 MM SP (also called partition) can be 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, StandAlonneMM and smm-gateway are supported. + * * Return: status code */ -static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) +static efi_status_t __efi_runtime mm_communicate(u8 *comm_buf, efi_uintn_t dsize) { efi_status_t ret; struct efi_mm_communicate_header *mm_hdr; @@ -162,9 +432,16 @@ static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) mm_hdr = (struct efi_mm_communicate_header *)comm_buf; var_hdr = (struct smm_variable_communicate_header *)mm_hdr->data;
+ #if (IS_ENABLED(CONFIG_OPTEE)) ret = optee_mm_communicate(comm_buf, dsize); + #elif (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) + ret = ffa_mm_communicate(comm_buf, dsize); + #endif if (ret != EFI_SUCCESS) { - log_err("%s failed!\n", __func__); + /* No need for showing a log here. mm_communicate failure happens + * when getVariable() is called with data size set to 0. + * This is not expected so no log shown. + */ return ret; }
@@ -258,6 +535,13 @@ efi_status_t EFIAPI get_max_payload(efi_uintn_t *size) goto out; } *size = var_payload->size; + + #if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) + if (*size > FFA_SHARED_MM_BUFFER_SIZE) + *size = FFA_SHARED_MM_BUFFER_SIZE - MM_COMMUNICATE_HEADER_SIZE - + MM_VARIABLE_COMMUNICATE_SIZE; + #endif + /* * There seems to be a bug in EDK2 miscalculating the boundaries and * size checks, so deduct 2 more bytes to fulfill this requirement. Fix @@ -697,7 +981,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);
/*

Hi Abdellatif,
Can you please keep me cc'ed in future revisions?
On Tue, Mar 29, 2022 at 04:16:59PM +0100, abdellatif.elkhlifi@arm.com wrote:
From: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
Add MM communication support using FF-A transport
FF-A MM communication allows exchanging data with StandAlonneMM or smm-gateway secure partitions which run in OP-TEE.
An MM shared buffer and a door bell event are used to exchange this 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.
What I am missing from all this is a description on how to test this and what's needed in OP-TEE and EDK2.
Unless I am reading this wrong, there is a "new" Secure Partition that will handle both the efi variables (along with all the EFI rules you need to update those and the crypto checks you need for authenticated variables). But StMM includes the hardware drivers as well. How is that handled in the SP context?
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Signed-off-by: Gowtham Suresh Kumar gowtham.sureshkumar@arm.com Cc: Tom Rini trini@konsulko.com
arch/arm/cpu/armv8/cache.S | 16 ++ arch/arm/cpu/armv8/cache_v8.c | 3 +- include/mm_communication.h | 4 +- lib/efi_loader/Kconfig | 14 +- lib/efi_loader/efi_variable_tee.c | 294 +++++++++++++++++++++++++++++- 5 files changed, 321 insertions(+), 10 deletions(-)
diff --git a/arch/arm/cpu/armv8/cache.S b/arch/arm/cpu/armv8/cache.S index d1cee23437..bdbe89e0c5 100644 --- a/arch/arm/cpu/armv8/cache.S +++ b/arch/arm/cpu/armv8/cache.S @@ -21,7 +21,11 @@
- x1: 0 clean & invalidate, 1 invalidate only
- x2~x9: clobbered
*/ +#ifdef CONFIG_ARM_FFA_TRANSPORT +.pushsection .text.efi_runtime, "ax" +#else .pushsection .text.__asm_dcache_level, "ax" +#endif ENTRY(__asm_dcache_level) lsl x12, x0, #1 msr csselr_el1, x12 /* select cache level */ @@ -65,7 +69,11 @@ ENDPROC(__asm_dcache_level)
- flush or invalidate all data cache by SET/WAY.
*/ +#ifdef CONFIG_ARM_FFA_TRANSPORT +.pushsection .text.efi_runtime, "ax" +#else .pushsection .text.__asm_dcache_all, "ax" +#endif ENTRY(__asm_dcache_all) mov x1, x0 dsb sy @@ -109,7 +117,11 @@ ENTRY(__asm_flush_dcache_all) ENDPROC(__asm_flush_dcache_all) .popsection
+#ifdef CONFIG_ARM_FFA_TRANSPORT +.pushsection .text.efi_runtime, "ax" +#else .pushsection .text.__asm_invalidate_dcache_all, "ax" +#endif ENTRY(__asm_invalidate_dcache_all) mov x0, #0x1 b __asm_dcache_all @@ -182,7 +194,11 @@ ENTRY(__asm_invalidate_icache_all) ENDPROC(__asm_invalidate_icache_all) .popsection
+#ifdef CONFIG_ARM_FFA_TRANSPORT +.pushsection .text.efi_runtime, "ax" +#else .pushsection .text.__asm_invalidate_l3_dcache, "ax" +#endif WEAK(__asm_invalidate_l3_dcache) mov x0, #0 /* return status as success */ ret diff --git a/arch/arm/cpu/armv8/cache_v8.c b/arch/arm/cpu/armv8/cache_v8.c index 3de18c7675..187a4497a7 100644 --- a/arch/arm/cpu/armv8/cache_v8.c +++ b/arch/arm/cpu/armv8/cache_v8.c @@ -9,6 +9,7 @@
#include <common.h> #include <cpu_func.h> +#include <efi_loader.h> #include <hang.h> #include <log.h> #include <asm/cache.h> @@ -425,7 +426,7 @@ __weak void mmu_setup(void) /*
- Performs a invalidation of the entire data cache at all levels
*/ -void invalidate_dcache_all(void) +void __efi_runtime invalidate_dcache_all(void) { __asm_invalidate_dcache_all(); __asm_invalidate_l3_dcache(); diff --git a/include/mm_communication.h b/include/mm_communication.h index e65fbde60d..bb99190956 100644 --- a/include/mm_communication.h +++ b/include/mm_communication.h @@ -123,7 +123,7 @@ struct __packed efi_mm_communicate_header {
- Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_HEADER.
*/ -struct smm_variable_communicate_header { +struct __packed smm_variable_communicate_header {
Why is this converted to packed?
efi_uintn_t function; efi_status_t ret_status; u8 data[]; @@ -145,7 +145,7 @@ struct smm_variable_communicate_header {
- Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE.
*/ -struct smm_variable_access { +struct __packed smm_variable_access {
Ditto
efi_guid_t guid; efi_uintn_t data_size; efi_uintn_t name_size; diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index 28657f50c9..0d69133595 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -55,13 +55,23 @@ 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"
- depends on OPTEE
bool "UEFI variables storage service via the trusted world"
depends on OPTEE || ARM_FFA_TRANSPORT help
The MM SP (also called partition) can be StandAlonneMM or smm-gateway.
When using the u-boot OP-TEE driver, StandAlonneMM is supported.
When using the u-boot FF-A driver, StandAlonneMM and smm-gateway are 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 under Optee in the trusted 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.
The naming is a bit misleading imho. FF-A is the transport mechanism between the secure and non-secure world. You may as well have FF-A along with OP-TEE. That doesn't automatically mean the SP is used instead of StMM.
This code is complicated already, I don't think we want the additional ifdefery. Wouldn't it be better to have this discover to what kind of protocol (e.g FF-A vs SMC calling conventions) it talks to? SPs are discoverable, so we could reason about those as well without asking the user to understand Arm protocol internals.
endchoice
config EFI_VARIABLES_PRESEED diff --git a/lib/efi_loader/efi_variable_tee.c b/lib/efi_loader/efi_variable_tee.c index dfef18435d..3d01985662 100644 --- a/lib/efi_loader/efi_variable_tee.c +++ b/lib/efi_loader/efi_variable_tee.c @@ -15,6 +15,53 @@ #include <malloc.h> #include <mm_communication.h>
+#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
+#include <arm_ffa_helper.h> +#include <cpu_func.h> +#include <mapmem.h>
+#ifndef FFA_SHARED_MM_BUFFER_SIZE
+#include <linux/sizes.h> +#define FFA_SHARED_MM_BUFFER_SIZE SZ_4K /* 4 KB */
+#endif
+#ifndef FFA_SHARED_MM_BUFFER_ADDR
+/*
- shared buffer physical address used for communication between
- u-boot and the MM SP
- */
+#define FFA_SHARED_MM_BUFFER_ADDR (0x023F8000)
Can you explain what this is used for ?
+#endif
+/* MM return codes */ +#define MM_SUCCESS (0)
+#define ARM_SVC_ID_SP_EVENT_COMPLETE_AARCH64 (0xC4000061) +#define ARM_SVC_ID_SP_EVENT_COMPLETE ARM_SVC_ID_SP_EVENT_COMPLETE_AARCH64
+#ifndef MM_SP_UUID_DATA
+/* MM SP UUID binary data (little-endian format) */ +#define MM_SP_UUID_DATA \
- 0xed, 0x32, 0xd5, 0x33, \
- 0x99, 0xe6, 0x42, 0x09, \
- 0x9c, 0xc0, 0x2d, 0x72, \
- 0xcd, 0xd9, 0x98, 0xa7
[...]
- /* Announce there is data in the shared buffer */
- ffa_ret = ffa_notify_mm_sp();
- if (ffa_ret)
unmap_sysmem(virt_shared_buf);
There are several calls around on runtime code which call functions not marked as runtime. This will just crash when called.
- 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_memcpy_runtime(comm_buf, virt_shared_buf, comm_buf_size);
unmap_sysmem(virt_shared_buf);
return EFI_BUFFER_TOO_SMALL;
}
efi_memcpy_runtime(comm_buf, virt_shared_buf, rx_data_size);
unmap_sysmem(virt_shared_buf);
return EFI_SUCCESS;
- }
- case -EINVAL:
return EFI_DEVICE_ERROR;
- case -EPERM:
return EFI_INVALID_PARAMETER;
- case -EACCES:
return EFI_ACCESS_DENIED;
- case -EBUSY:
return EFI_OUT_OF_RESOURCES;
- default:
return EFI_ACCESS_DENIED;
- }
+} +#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 MM SP (also called partition) can be 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, StandAlonneMM and smm-gateway are supported.
*/
- Return: status code
-static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) +static efi_status_t __efi_runtime mm_communicate(u8 *comm_buf, efi_uintn_t dsize) { efi_status_t ret; struct efi_mm_communicate_header *mm_hdr; @@ -162,9 +432,16 @@ static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) mm_hdr = (struct efi_mm_communicate_header *)comm_buf; var_hdr = (struct smm_variable_communicate_header *)mm_hdr->data;
- #if (IS_ENABLED(CONFIG_OPTEE)) ret = optee_mm_communicate(comm_buf, dsize);
- #elif (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
- ret = ffa_mm_communicate(comm_buf, dsize);
- #endif if (ret != EFI_SUCCESS) {
log_err("%s failed!\n", __func__);
/* No need for showing a log here. mm_communicate failure happens
* when getVariable() is called with data size set to 0.
* This is not expected so no log shown.
*/
It can also fail on normal cases were the size is != 0
return ret;
}
@@ -258,6 +535,13 @@ efi_status_t EFIAPI get_max_payload(efi_uintn_t *size) goto out; } *size = var_payload->size;
- #if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
if (*size > FFA_SHARED_MM_BUFFER_SIZE)
*size = FFA_SHARED_MM_BUFFER_SIZE - MM_COMMUNICATE_HEADER_SIZE -
MM_VARIABLE_COMMUNICATE_SIZE;
- #endif
- /*
- There seems to be a bug in EDK2 miscalculating the boundaries and
- size checks, so deduct 2 more bytes to fulfill this requirement. Fix
@@ -697,7 +981,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);
/*
-- 2.17.1
Thanks /Ilias

On Thu, Apr 14, 2022 at 10:54:08PM +0300, Ilias Apalodimas wrote:
Hi Abdellatif,
Can you please keep me cc'ed in future revisions?
On Tue, Mar 29, 2022 at 04:16:59PM +0100, abdellatif.elkhlifi@arm.com wrote:
From: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
Add MM communication support using FF-A transport
FF-A MM communication allows exchanging data with StandAlonneMM or smm-gateway secure partitions which run in OP-TEE.
An MM shared buffer and a door bell event are used to exchange this 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.
What I am missing from all this is a description on how to test this and what's needed in OP-TEE and EDK2.
Unless I am reading this wrong, there is a "new" Secure Partition that will handle both the efi variables (along with all the EFI rules you need to update those and the crypto checks you need for authenticated variables). But StMM includes the hardware drivers as well. How is that handled in the SP context?
In the v4 patchset the MM SPs are discovered at runtime. All MM SPs have the same UUID and use the same communication protocol. From v4, FF-A MM communication in u-boot is not tied to any MM SP. Please check: https://lore.kernel.org/all/20220926101723.9965-10-abdellatif.elkhlifi@arm.c...
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Signed-off-by: Gowtham Suresh Kumar gowtham.sureshkumar@arm.com Cc: Tom Rini trini@konsulko.com
arch/arm/cpu/armv8/cache.S | 16 ++ arch/arm/cpu/armv8/cache_v8.c | 3 +- include/mm_communication.h | 4 +- lib/efi_loader/Kconfig | 14 +- lib/efi_loader/efi_variable_tee.c | 294 +++++++++++++++++++++++++++++- 5 files changed, 321 insertions(+), 10 deletions(-)
diff --git a/arch/arm/cpu/armv8/cache.S b/arch/arm/cpu/armv8/cache.S index d1cee23437..bdbe89e0c5 100644 --- a/arch/arm/cpu/armv8/cache.S +++ b/arch/arm/cpu/armv8/cache.S @@ -21,7 +21,11 @@
- x1: 0 clean & invalidate, 1 invalidate only
- x2~x9: clobbered
*/ +#ifdef CONFIG_ARM_FFA_TRANSPORT +.pushsection .text.efi_runtime, "ax" +#else .pushsection .text.__asm_dcache_level, "ax" +#endif ENTRY(__asm_dcache_level) lsl x12, x0, #1 msr csselr_el1, x12 /* select cache level */ @@ -65,7 +69,11 @@ ENDPROC(__asm_dcache_level)
- flush or invalidate all data cache by SET/WAY.
*/ +#ifdef CONFIG_ARM_FFA_TRANSPORT +.pushsection .text.efi_runtime, "ax" +#else .pushsection .text.__asm_dcache_all, "ax" +#endif ENTRY(__asm_dcache_all) mov x1, x0 dsb sy @@ -109,7 +117,11 @@ ENTRY(__asm_flush_dcache_all) ENDPROC(__asm_flush_dcache_all) .popsection
+#ifdef CONFIG_ARM_FFA_TRANSPORT +.pushsection .text.efi_runtime, "ax" +#else .pushsection .text.__asm_invalidate_dcache_all, "ax" +#endif ENTRY(__asm_invalidate_dcache_all) mov x0, #0x1 b __asm_dcache_all @@ -182,7 +194,11 @@ ENTRY(__asm_invalidate_icache_all) ENDPROC(__asm_invalidate_icache_all) .popsection
+#ifdef CONFIG_ARM_FFA_TRANSPORT +.pushsection .text.efi_runtime, "ax" +#else .pushsection .text.__asm_invalidate_l3_dcache, "ax" +#endif WEAK(__asm_invalidate_l3_dcache) mov x0, #0 /* return status as success */ ret diff --git a/arch/arm/cpu/armv8/cache_v8.c b/arch/arm/cpu/armv8/cache_v8.c index 3de18c7675..187a4497a7 100644 --- a/arch/arm/cpu/armv8/cache_v8.c +++ b/arch/arm/cpu/armv8/cache_v8.c @@ -9,6 +9,7 @@
#include <common.h> #include <cpu_func.h> +#include <efi_loader.h> #include <hang.h> #include <log.h> #include <asm/cache.h> @@ -425,7 +426,7 @@ __weak void mmu_setup(void) /*
- Performs a invalidation of the entire data cache at all levels
*/ -void invalidate_dcache_all(void) +void __efi_runtime invalidate_dcache_all(void) { __asm_invalidate_dcache_all(); __asm_invalidate_l3_dcache(); diff --git a/include/mm_communication.h b/include/mm_communication.h index e65fbde60d..bb99190956 100644 --- a/include/mm_communication.h +++ b/include/mm_communication.h @@ -123,7 +123,7 @@ struct __packed efi_mm_communicate_header {
- Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_HEADER.
*/ -struct smm_variable_communicate_header { +struct __packed smm_variable_communicate_header {
Why is this converted to packed?
no need to add the attribute packed for efi_mm_communicate_header and smm_variable_communicate_header structures because all fields sizes are multiple of 8 bytes. This has been addressed in v4 patchset. However, the smm_variable_access structure still needs to be packed because attr is u32 and a 4 bytes padding is added by the compiler which breaks the communication with the secure world.
efi_uintn_t function; efi_status_t ret_status; u8 data[]; @@ -145,7 +145,7 @@ struct smm_variable_communicate_header {
- Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE.
*/ -struct smm_variable_access { +struct __packed smm_variable_access {
Ditto
efi_guid_t guid; efi_uintn_t data_size; efi_uintn_t name_size; diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index 28657f50c9..0d69133595 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -55,13 +55,23 @@ 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"
- depends on OPTEE
bool "UEFI variables storage service via the trusted world"
depends on OPTEE || ARM_FFA_TRANSPORT help
The MM SP (also called partition) can be StandAlonneMM or smm-gateway.
When using the u-boot OP-TEE driver, StandAlonneMM is supported.
When using the u-boot FF-A driver, StandAlonneMM and smm-gateway are 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 under Optee in the trusted 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.
The naming is a bit misleading imho. FF-A is the transport mechanism between the secure and non-secure world. You may as well have FF-A along with OP-TEE. That doesn't automatically mean the SP is used instead of StMM.
This code is complicated already, I don't think we want the additional ifdefery. Wouldn't it be better to have this discover to what kind of protocol (e.g FF-A vs SMC calling conventions) it talks to? SPs are discoverable, so we could reason about those as well without asking the user to understand Arm protocol internals.
From patchset v4, when using the u-boot FF-A driver all MM SPs are supported
because the agreement is that they all use the same UUID. Please check: https://lore.kernel.org/all/20220926101723.9965-10-abdellatif.elkhlifi@arm.c...
endchoice
config EFI_VARIABLES_PRESEED diff --git a/lib/efi_loader/efi_variable_tee.c b/lib/efi_loader/efi_variable_tee.c index dfef18435d..3d01985662 100644 --- a/lib/efi_loader/efi_variable_tee.c +++ b/lib/efi_loader/efi_variable_tee.c @@ -15,6 +15,53 @@ #include <malloc.h> #include <mm_communication.h>
+#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
+#include <arm_ffa_helper.h> +#include <cpu_func.h> +#include <mapmem.h>
+#ifndef FFA_SHARED_MM_BUFFER_SIZE
+#include <linux/sizes.h> +#define FFA_SHARED_MM_BUFFER_SIZE SZ_4K /* 4 KB */
+#endif
+#ifndef FFA_SHARED_MM_BUFFER_ADDR
+/*
- shared buffer physical address used for communication between
- u-boot and the MM SP
- */
+#define FFA_SHARED_MM_BUFFER_ADDR (0x023F8000)
Can you explain what this is used for ?
FF-A MM communication is implemented by ffa_mm_communicate(). It allows exchanging EFI services data with the MM partition using FF-A transport. The mechanism is based on a shared buffer and a door bell event.
When u-boot wants to send data to the MM partition the data is written to the shared buffer and a door bell event is issued.
A door bell event means that there is data to read from the shared buffer.
The MM partition writes its response in the same shared buffer and u-boot reads the response from the shared buffer at ffa_mm_communicate() level.
The shared buffer address is specified by FFA_SHARED_MM_BUFFER_ADDR and should be defined by the board.
+#endif
+/* MM return codes */ +#define MM_SUCCESS (0)
+#define ARM_SVC_ID_SP_EVENT_COMPLETE_AARCH64 (0xC4000061) +#define ARM_SVC_ID_SP_EVENT_COMPLETE ARM_SVC_ID_SP_EVENT_COMPLETE_AARCH64
+#ifndef MM_SP_UUID_DATA
+/* MM SP UUID binary data (little-endian format) */ +#define MM_SP_UUID_DATA \
- 0xed, 0x32, 0xd5, 0x33, \
- 0x99, 0xe6, 0x42, 0x09, \
- 0x9c, 0xc0, 0x2d, 0x72, \
- 0xcd, 0xd9, 0x98, 0xa7
[...]
- /* Announce there is data in the shared buffer */
- ffa_ret = ffa_notify_mm_sp();
- if (ffa_ret)
unmap_sysmem(virt_shared_buf);
There are several calls around on runtime code which call functions not marked as runtime. This will just crash when called.
This has been addressed from v2 patchset. Please refer to the latest work on v4: https://lore.kernel.org/all/20220926101723.9965-10-abdellatif.elkhlifi@arm.c...
- 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_memcpy_runtime(comm_buf, virt_shared_buf, comm_buf_size);
unmap_sysmem(virt_shared_buf);
return EFI_BUFFER_TOO_SMALL;
}
efi_memcpy_runtime(comm_buf, virt_shared_buf, rx_data_size);
unmap_sysmem(virt_shared_buf);
return EFI_SUCCESS;
- }
- case -EINVAL:
return EFI_DEVICE_ERROR;
- case -EPERM:
return EFI_INVALID_PARAMETER;
- case -EACCES:
return EFI_ACCESS_DENIED;
- case -EBUSY:
return EFI_OUT_OF_RESOURCES;
- default:
return EFI_ACCESS_DENIED;
- }
+} +#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 MM SP (also called partition) can be 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, StandAlonneMM and smm-gateway are supported.
*/
- Return: status code
-static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) +static efi_status_t __efi_runtime mm_communicate(u8 *comm_buf, efi_uintn_t dsize) { efi_status_t ret; struct efi_mm_communicate_header *mm_hdr; @@ -162,9 +432,16 @@ static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) mm_hdr = (struct efi_mm_communicate_header *)comm_buf; var_hdr = (struct smm_variable_communicate_header *)mm_hdr->data;
- #if (IS_ENABLED(CONFIG_OPTEE)) ret = optee_mm_communicate(comm_buf, dsize);
- #elif (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
- ret = ffa_mm_communicate(comm_buf, dsize);
- #endif if (ret != EFI_SUCCESS) {
log_err("%s failed!\n", __func__);
/* No need for showing a log here. mm_communicate failure happens
* when getVariable() is called with data size set to 0.
* This is not expected so no log shown.
*/
It can also fail on normal cases were the size is != 0
This has been addressed on v4 patchset: https://lore.kernel.org/all/20220926101723.9965-10-abdellatif.elkhlifi@arm.c...
return ret;
}
@@ -258,6 +535,13 @@ efi_status_t EFIAPI get_max_payload(efi_uintn_t *size) goto out; } *size = var_payload->size;
- #if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
if (*size > FFA_SHARED_MM_BUFFER_SIZE)
*size = FFA_SHARED_MM_BUFFER_SIZE - MM_COMMUNICATE_HEADER_SIZE -
MM_VARIABLE_COMMUNICATE_SIZE;
- #endif
- /*
- There seems to be a bug in EDK2 miscalculating the boundaries and
- size checks, so deduct 2 more bytes to fulfill this requirement. Fix
@@ -697,7 +981,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);
/*
-- 2.17.1
Thanks /Ilias

Hello guys, gentle ping, any thoughts about these changes ?
On Tue, Mar 29, 2022 at 04:16:53PM +0100, abdellatif.elkhlifi@arm.com wrote:
From: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
This patchset adds support for Arm FF-A (Arm Firmware Framework for Armv8-A v1.0).
FF-A support is generic by design and can be used by any Arm platform.
The features added are as follows:
1/ FF-A device driver 2/ armffa command 3/ FF-A Sandbox driver 4/ FF-A Sandbox test cases 5/ FF-A MM communication
The suggested design sees FF-A as a data bus allowing data exchange with the firmware running under TrustZone HW (such as Optee). The same approach was followed in the FF-A driver in Linux kernel (https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/driv...))
u-boot boards using FF-A can provide a device tree node in a <board>-u-boot.dtsi file. Since the node can not be hosted in Linux device tree, we suggest using u-boot device tree.
As a use case, we will send a commit enabling FF-A for the Corstone-1000 platform and providing the FF-A node in u-boot.dtsi files.
Corstone-1000: https://lore.kernel.org/u-boot/20220322104118.573537-1-rui.silva@linaro.org/
Cc: Tom Rini trini@konsulko.com
Abdellatif El Khlifi (6): arm_ffa: introduce Arm FF-A low-level driver arm_ffa: introduce armffa command arm_ffa: introduce the FF-A Sandbox driver arm_ffa: introduce Sandbox test cases for UCLASS_FFA arm_ffa: introduce armffa command Sandbox test arm_ffa: introduce FF-A MM communication
MAINTAINERS | 15 + arch/arm/cpu/armv8/cache.S | 16 + arch/arm/cpu/armv8/cache_v8.c | 3 +- arch/arm/cpu/armv8/smccc-call.S | 27 + arch/arm/lib/asm-offsets.c | 6 + arch/sandbox/dts/sandbox.dtsi | 10 + arch/sandbox/dts/test.dts | 10 + cmd/Kconfig | 10 + cmd/Makefile | 2 + cmd/armffa.c | 266 +++++ common/board_r.c | 7 + configs/sandbox64_defconfig | 2 + configs/sandbox_defconfig | 2 + doc/arch/sandbox.rst | 1 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/arm-ffa/Kconfig | 32 + drivers/arm-ffa/Makefile | 7 + drivers/arm-ffa/arm-ffa-uclass.c | 102 ++ drivers/arm-ffa/arm_ffa_prv.h | 200 ++++ drivers/arm-ffa/core.c | 1463 +++++++++++++++++++++++++ drivers/arm-ffa/sandbox.c | 735 +++++++++++++ drivers/arm-ffa/sandbox_arm_ffa_prv.h | 128 +++ include/arm_ffa.h | 197 ++++ include/arm_ffa_helper.h | 45 + include/dm/uclass-id.h | 1 + include/linux/arm-smccc.h | 28 +- include/mm_communication.h | 4 +- include/sandbox_arm_ffa.h | 31 + include/sandbox_arm_ffa_helper.h | 26 + lib/Kconfig | 1 + lib/Makefile | 1 + lib/arm-ffa/Kconfig | 6 + lib/arm-ffa/Makefile | 9 + lib/arm-ffa/arm_ffa_helper.c | 188 ++++ lib/arm-ffa/sandbox_arm_ffa_helper.c | 23 + lib/efi_loader/Kconfig | 14 +- lib/efi_loader/efi_boottime.c | 17 + lib/efi_loader/efi_variable_tee.c | 294 ++++- test/cmd/Makefile | 1 + test/cmd/armffa.c | 33 + test/cmd/armffa.h | 13 + test/dm/Makefile | 1 + test/dm/ffa.c | 424 +++++++ test/dm/ffa.h | 22 + 45 files changed, 4415 insertions(+), 11 deletions(-) create mode 100644 cmd/armffa.c create mode 100644 drivers/arm-ffa/Kconfig create mode 100644 drivers/arm-ffa/Makefile create mode 100644 drivers/arm-ffa/arm-ffa-uclass.c create mode 100644 drivers/arm-ffa/arm_ffa_prv.h create mode 100644 drivers/arm-ffa/core.c create mode 100644 drivers/arm-ffa/sandbox.c create mode 100644 drivers/arm-ffa/sandbox_arm_ffa_prv.h create mode 100644 include/arm_ffa.h create mode 100644 include/arm_ffa_helper.h create mode 100644 include/sandbox_arm_ffa.h create mode 100644 include/sandbox_arm_ffa_helper.h create mode 100644 lib/arm-ffa/Kconfig create mode 100644 lib/arm-ffa/Makefile create mode 100644 lib/arm-ffa/arm_ffa_helper.c create mode 100644 lib/arm-ffa/sandbox_arm_ffa_helper.c create mode 100644 test/cmd/armffa.c create mode 100644 test/cmd/armffa.h create mode 100644 test/dm/ffa.c create mode 100644 test/dm/ffa.h
-- 2.17.1

On Tue, Mar 29, 2022 at 04:16:53PM +0100, abdellatif.elkhlifi@arm.com wrote:
From: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
This patchset adds support for Arm FF-A (Arm Firmware Framework for Armv8-A v1.0).
FF-A support is generic by design and can be used by any Arm platform.
The features added are as follows:
1/ FF-A device driver 2/ armffa command 3/ FF-A Sandbox driver 4/ FF-A Sandbox test cases 5/ FF-A MM communication
The suggested design sees FF-A as a data bus allowing data exchange with the firmware running under TrustZone HW (such as Optee). The same approach was followed in the FF-A driver in Linux kernel (https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/driv...))
u-boot boards using FF-A can provide a device tree node in a <board>-u-boot.dtsi file. Since the node can not be hosted in Linux device tree, we suggest using u-boot device tree.
Why can't the node be in the upstream tree? It should be, so that it can be shared between all users. Especially since there's in-Linux users?

On Wed, Apr 06, 2022 at 03:47:11PM -0400, Tom Rini wrote:
On Tue, Mar 29, 2022 at 04:16:53PM +0100, abdellatif.elkhlifi@arm.com wrote:
From: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
This patchset adds support for Arm FF-A (Arm Firmware Framework for Armv8-A v1.0).
FF-A support is generic by design and can be used by any Arm platform.
The features added are as follows:
1/ FF-A device driver 2/ armffa command 3/ FF-A Sandbox driver 4/ FF-A Sandbox test cases 5/ FF-A MM communication
The suggested design sees FF-A as a data bus allowing data exchange with the firmware running under TrustZone HW (such as Optee). The same approach was followed in the FF-A driver in Linux kernel (https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/driv...))
u-boot boards using FF-A can provide a device tree node in a <board>-u-boot.dtsi file. Since the node can not be hosted in Linux device tree, we suggest using u-boot device tree.
Why can't the node be in the upstream tree? It should be, so that it can be shared between all users. Especially since there's in-Linux users?
-- Tom
Linux already has an FF-A bus driver and doesn't use a device tree node for FF-A.
The Linux driver registers FF-A as a bus:
int arm_ffa_bus_init(void) { return bus_register(&ffa_bus_type); }
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/driv...
So, there is no user for the node in Linux. That's why we suggest hosting the node in the u-boot device tree (a u-boot.dtsi file)

On Thu, Apr 07, 2022 at 01:54:24PM +0100, Abdellatif El Khlifi wrote:
On Wed, Apr 06, 2022 at 03:47:11PM -0400, Tom Rini wrote:
On Tue, Mar 29, 2022 at 04:16:53PM +0100, abdellatif.elkhlifi@arm.com wrote:
From: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
This patchset adds support for Arm FF-A (Arm Firmware Framework for Armv8-A v1.0).
FF-A support is generic by design and can be used by any Arm platform.
The features added are as follows:
1/ FF-A device driver 2/ armffa command 3/ FF-A Sandbox driver 4/ FF-A Sandbox test cases 5/ FF-A MM communication
The suggested design sees FF-A as a data bus allowing data exchange with the firmware running under TrustZone HW (such as Optee). The same approach was followed in the FF-A driver in Linux kernel (https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/driv...))
u-boot boards using FF-A can provide a device tree node in a <board>-u-boot.dtsi file. Since the node can not be hosted in Linux device tree, we suggest using u-boot device tree.
Why can't the node be in the upstream tree? It should be, so that it can be shared between all users. Especially since there's in-Linux users?
-- Tom
Linux already has an FF-A bus driver and doesn't use a device tree node for FF-A.
The Linux driver registers FF-A as a bus:
int arm_ffa_bus_init(void) { return bus_register(&ffa_bus_type); }
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/driv...
So, there is no user for the node in Linux. That's why we suggest hosting the node in the u-boot device tree (a u-boot.dtsi file)
OK, but you can still push it upstream as it's not required to have an in tree user.

On Thu, Apr 07, 2022 at 08:58:11AM -0400, Tom Rini wrote:
On Thu, Apr 07, 2022 at 01:54:24PM +0100, Abdellatif El Khlifi wrote:
On Wed, Apr 06, 2022 at 03:47:11PM -0400, Tom Rini wrote:
On Tue, Mar 29, 2022 at 04:16:53PM +0100, abdellatif.elkhlifi@arm.com wrote:
From: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
This patchset adds support for Arm FF-A (Arm Firmware Framework for Armv8-A v1.0).
FF-A support is generic by design and can be used by any Arm platform.
The features added are as follows:
1/ FF-A device driver 2/ armffa command 3/ FF-A Sandbox driver 4/ FF-A Sandbox test cases 5/ FF-A MM communication
The suggested design sees FF-A as a data bus allowing data exchange with the firmware running under TrustZone HW (such as Optee). The same approach was followed in the FF-A driver in Linux kernel (https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/driv...))
u-boot boards using FF-A can provide a device tree node in a <board>-u-boot.dtsi file. Since the node can not be hosted in Linux device tree, we suggest using u-boot device tree.
Why can't the node be in the upstream tree? It should be, so that it can be shared between all users. Especially since there's in-Linux users?
-- Tom
Linux already has an FF-A bus driver and doesn't use a device tree node for FF-A.
The Linux driver registers FF-A as a bus:
int arm_ffa_bus_init(void) { return bus_register(&ffa_bus_type); }
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/driv...
So, there is no user for the node in Linux. That's why we suggest hosting the node in the u-boot device tree (a u-boot.dtsi file)
OK, but you can still push it upstream as it's not required to have an in tree user.
-- Tom
During the review of Corstone-1000 patchset, Rui Silva had a discussion with the Linux device tree maintainer (Rob Herring). Rob is not in favour of an FFA node in the kernel device tree. This is why we are including the FFA node in u-boot device tree (u-boot.dtsi files).
-- Abdellatif

On Tue, Apr 12, 2022 at 12:43:15PM +0100, Abdellatif El Khlifi wrote:
On Thu, Apr 07, 2022 at 08:58:11AM -0400, Tom Rini wrote:
On Thu, Apr 07, 2022 at 01:54:24PM +0100, Abdellatif El Khlifi wrote:
On Wed, Apr 06, 2022 at 03:47:11PM -0400, Tom Rini wrote:
On Tue, Mar 29, 2022 at 04:16:53PM +0100, abdellatif.elkhlifi@arm.com wrote:
From: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
This patchset adds support for Arm FF-A (Arm Firmware Framework for Armv8-A v1.0).
FF-A support is generic by design and can be used by any Arm platform.
The features added are as follows:
1/ FF-A device driver 2/ armffa command 3/ FF-A Sandbox driver 4/ FF-A Sandbox test cases 5/ FF-A MM communication
The suggested design sees FF-A as a data bus allowing data exchange with the firmware running under TrustZone HW (such as Optee). The same approach was followed in the FF-A driver in Linux kernel (https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/driv...))
u-boot boards using FF-A can provide a device tree node in a <board>-u-boot.dtsi file. Since the node can not be hosted in Linux device tree, we suggest using u-boot device tree.
Why can't the node be in the upstream tree? It should be, so that it can be shared between all users. Especially since there's in-Linux users?
-- Tom
Linux already has an FF-A bus driver and doesn't use a device tree node for FF-A.
The Linux driver registers FF-A as a bus:
int arm_ffa_bus_init(void) { return bus_register(&ffa_bus_type); }
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/driv...
So, there is no user for the node in Linux. That's why we suggest hosting the node in the u-boot device tree (a u-boot.dtsi file)
OK, but you can still push it upstream as it's not required to have an in tree user.
During the review of Corstone-1000 patchset, Rui Silva had a discussion with the Linux device tree maintainer (Rob Herring). Rob is not in favour of an FFA node in the kernel device tree. This is why we are including the FFA node in u-boot device tree (u-boot.dtsi files).
I'm a bit confused now, can you please link to the kernel thread? Or Rob, can you chime in here please?

On Tue, Apr 12, 2022 at 7:01 AM Tom Rini trini@konsulko.com wrote:
On Tue, Apr 12, 2022 at 12:43:15PM +0100, Abdellatif El Khlifi wrote:
On Thu, Apr 07, 2022 at 08:58:11AM -0400, Tom Rini wrote:
On Thu, Apr 07, 2022 at 01:54:24PM +0100, Abdellatif El Khlifi wrote:
On Wed, Apr 06, 2022 at 03:47:11PM -0400, Tom Rini wrote:
On Tue, Mar 29, 2022 at 04:16:53PM +0100, abdellatif.elkhlifi@arm.com wrote:
From: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
This patchset adds support for Arm FF-A (Arm Firmware Framework for Armv8-A v1.0).
FF-A support is generic by design and can be used by any Arm platform.
The features added are as follows:
1/ FF-A device driver 2/ armffa command 3/ FF-A Sandbox driver 4/ FF-A Sandbox test cases 5/ FF-A MM communication
The suggested design sees FF-A as a data bus allowing data exchange with the firmware running under TrustZone HW (such as Optee). The same approach was followed in the FF-A driver in Linux kernel (https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/driv...))
u-boot boards using FF-A can provide a device tree node in a <board>-u-boot.dtsi file. Since the node can not be hosted in Linux device tree, we suggest using u-boot device tree.
Why can't the node be in the upstream tree? It should be, so that it can be shared between all users. Especially since there's in-Linux users?
-- Tom
Linux already has an FF-A bus driver and doesn't use a device tree node for FF-A.
The Linux driver registers FF-A as a bus:
int arm_ffa_bus_init(void) { return bus_register(&ffa_bus_type); }
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/driv...
So, there is no user for the node in Linux. That's why we suggest hosting the node in the u-boot device tree (a u-boot.dtsi file)
OK, but you can still push it upstream as it's not required to have an in tree user.
During the review of Corstone-1000 patchset, Rui Silva had a discussion with the Linux device tree maintainer (Rob Herring). Rob is not in favour of an FFA node in the kernel device tree. This is why we are including the FFA node in u-boot device tree (u-boot.dtsi files).
Sigh. There is not a 'kernel device tree' and a 'u-boot device tree'. There is only 1. For SystemReadyIR compliance, that is a hard requirement.
I'm a bit confused now, can you please link to the kernel thread? Or Rob, can you chime in here please?
The FFA DT binding was rejected in favor of making FFA discoverable. The FFA spec was amended to address that. DT is only for what we failed to make discoverable. For hardware, we're stuck with it. We shouldn't repeat that for software interfaces.
Rob

On Tue, Apr 12, 2022 at 08:28:42AM -0500, Rob Herring wrote:
On Tue, Apr 12, 2022 at 7:01 AM Tom Rini trini@konsulko.com wrote:
On Tue, Apr 12, 2022 at 12:43:15PM +0100, Abdellatif El Khlifi wrote:
On Thu, Apr 07, 2022 at 08:58:11AM -0400, Tom Rini wrote:
On Thu, Apr 07, 2022 at 01:54:24PM +0100, Abdellatif El Khlifi wrote:
On Wed, Apr 06, 2022 at 03:47:11PM -0400, Tom Rini wrote:
On Tue, Mar 29, 2022 at 04:16:53PM +0100, abdellatif.elkhlifi@arm.com wrote: > From: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com > > This patchset adds support for Arm FF-A (Arm Firmware Framework for Armv8-A v1.0). > > FF-A support is generic by design and can be used by any Arm platform. > > The features added are as follows: > > 1/ FF-A device driver > 2/ armffa command > 3/ FF-A Sandbox driver > 4/ FF-A Sandbox test cases > 5/ FF-A MM communication > > > The suggested design sees FF-A as a data bus allowing data exchange with the firmware > running under TrustZone HW (such as Optee). The same approach was followed in the > FF-A driver in Linux kernel (https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/driv...)) > > u-boot boards using FF-A can provide a device tree node in a <board>-u-boot.dtsi file. > Since the node can not be hosted in Linux device tree, we suggest using u-boot device tree.
Why can't the node be in the upstream tree? It should be, so that it can be shared between all users. Especially since there's in-Linux users?
-- Tom
Linux already has an FF-A bus driver and doesn't use a device tree node for FF-A.
The Linux driver registers FF-A as a bus:
int arm_ffa_bus_init(void) { return bus_register(&ffa_bus_type); }
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/driv...
So, there is no user for the node in Linux. That's why we suggest hosting the node in the u-boot device tree (a u-boot.dtsi file)
OK, but you can still push it upstream as it's not required to have an in tree user.
During the review of Corstone-1000 patchset, Rui Silva had a discussion with the Linux device tree maintainer (Rob Herring). Rob is not in favour of an FFA node in the kernel device tree. This is why we are including the FFA node in u-boot device tree (u-boot.dtsi files).
Sigh. There is not a 'kernel device tree' and a 'u-boot device tree'. There is only 1. For SystemReadyIR compliance, that is a hard requirement.
I'm a bit confused now, can you please link to the kernel thread? Or Rob, can you chime in here please?
The FFA DT binding was rejected in favor of making FFA discoverable. The FFA spec was amended to address that. DT is only for what we failed to make discoverable. For hardware, we're stuck with it. We shouldn't repeat that for software interfaces.
Rob
Guys,
Since we can not add an FFA node in the device tree, we will make FFA a discoverable bus. So, we will manually create the udevice, binding it to the driver and probing it. Manually means directly calling device_bind and device_probe APIs.
Any thoughts about this approach ?
Abdellatif

On Wed, Apr 13, 2022 at 03:20:23PM +0100, Abdellatif El Khlifi wrote:
On Tue, Apr 12, 2022 at 08:28:42AM -0500, Rob Herring wrote:
On Tue, Apr 12, 2022 at 7:01 AM Tom Rini trini@konsulko.com wrote:
On Tue, Apr 12, 2022 at 12:43:15PM +0100, Abdellatif El Khlifi wrote:
On Thu, Apr 07, 2022 at 08:58:11AM -0400, Tom Rini wrote:
On Thu, Apr 07, 2022 at 01:54:24PM +0100, Abdellatif El Khlifi wrote:
On Wed, Apr 06, 2022 at 03:47:11PM -0400, Tom Rini wrote: > On Tue, Mar 29, 2022 at 04:16:53PM +0100, abdellatif.elkhlifi@arm.com wrote: > > From: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com > > > > This patchset adds support for Arm FF-A (Arm Firmware Framework for Armv8-A v1.0). > > > > FF-A support is generic by design and can be used by any Arm platform. > > > > The features added are as follows: > > > > 1/ FF-A device driver > > 2/ armffa command > > 3/ FF-A Sandbox driver > > 4/ FF-A Sandbox test cases > > 5/ FF-A MM communication > > > > > > The suggested design sees FF-A as a data bus allowing data exchange with the firmware > > running under TrustZone HW (such as Optee). The same approach was followed in the > > FF-A driver in Linux kernel (https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/driv...)) > > > > u-boot boards using FF-A can provide a device tree node in a <board>-u-boot.dtsi file. > > Since the node can not be hosted in Linux device tree, we suggest using u-boot device tree. > > Why can't the node be in the upstream tree? It should be, so that it > can be shared between all users. Especially since there's in-Linux > users? > > -- > Tom
Linux already has an FF-A bus driver and doesn't use a device tree node for FF-A.
The Linux driver registers FF-A as a bus:
int arm_ffa_bus_init(void) { return bus_register(&ffa_bus_type); }
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/driv...
So, there is no user for the node in Linux. That's why we suggest hosting the node in the u-boot device tree (a u-boot.dtsi file)
OK, but you can still push it upstream as it's not required to have an in tree user.
During the review of Corstone-1000 patchset, Rui Silva had a discussion with the Linux device tree maintainer (Rob Herring). Rob is not in favour of an FFA node in the kernel device tree. This is why we are including the FFA node in u-boot device tree (u-boot.dtsi files).
Sigh. There is not a 'kernel device tree' and a 'u-boot device tree'. There is only 1. For SystemReadyIR compliance, that is a hard requirement.
I'm a bit confused now, can you please link to the kernel thread? Or Rob, can you chime in here please?
The FFA DT binding was rejected in favor of making FFA discoverable. The FFA spec was amended to address that. DT is only for what we failed to make discoverable. For hardware, we're stuck with it. We shouldn't repeat that for software interfaces.
Rob
Guys,
Since we can not add an FFA node in the device tree, we will make FFA a discoverable bus. So, we will manually create the udevice, binding it to the driver and probing it. Manually means directly calling device_bind and device_probe APIs.
Any thoughts about this approach ?
How is it both discoverable and doesn't have a device tree node, in the kernel?

On Wed, Apr 13, 2022 at 12:46:07PM -0400, Tom Rini wrote:
On Wed, Apr 13, 2022 at 03:20:23PM +0100, Abdellatif El Khlifi wrote:
[...]
Since we can not add an FFA node in the device tree, we will make FFA a discoverable bus. So, we will manually create the udevice, binding it to the driver and probing it. Manually means directly calling device_bind and device_probe APIs.
Any thoughts about this approach ?
How is it both discoverable and doesn't have a device tree node, in the kernel?
I don't clearly understand your query here fully. Generally if some h/w is discoverable, then we don't need to have device node in the DT as it may result in conflicts.
In this we can query f/w to check if a particular feature is implemented or not before we initialise the FF-A bus. One we discover the partitions we add devices for each and the individual device and associated driver gets probed based on UUID.
I have neither seen the u-boot implementation nor fully understand how device/driver model differs from the kernel one to comment on details. I am happy to provide any more details if required.
-- Regards, Sudeep

From: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
This patchset adds support for Arm FF-A (Arm Firmware Framework for Armv8-A v1.0).
FF-A support is generic by design and can be used by any Arm platform.
The features added are as follows:
1/ FF-A bus driver 2/ armffa command 3/ FF-A Sandbox driver 4/ FF-A Sandbox test cases 5/ FF-A MM communication
The suggested design considers FF-A as a discoverable bus. 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 Secure World firmware runs under TrustZone HW (such as Optee). The same approach was followed in the FF-A driver in Linux kernel (https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/driv...))
Cc: Tom Rini trini@konsulko.com Cc: Rob Herring robh@kernel.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Achin Gupta achin.gupta@arm.com Cc: Simon Glass sjg@chromium.org Cc: Vishnu Banavath vishnu.banavath@arm.com
Abdellatif El Khlifi (6): arm_ffa: introduce Arm FF-A low-level driver arm_ffa: introduce armffa command arm_ffa: introduce the FF-A Sandbox driver arm_ffa: introduce Sandbox test cases for UCLASS_FFA arm_ffa: introduce armffa command Sandbox test arm_ffa: introduce FF-A MM communication
MAINTAINERS | 15 + arch/arm/cpu/armv8/cache.S | 16 + arch/arm/cpu/armv8/cache_v8.c | 3 +- arch/arm/cpu/armv8/smccc-call.S | 27 + arch/arm/lib/asm-offsets.c | 6 + cmd/Kconfig | 10 + cmd/Makefile | 2 + cmd/armffa.c | 266 +++++ common/board_r.c | 7 + configs/sandbox64_defconfig | 2 + configs/sandbox_defconfig | 2 + doc/arch/sandbox.rst | 1 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/arm-ffa/Kconfig | 34 + drivers/arm-ffa/Makefile | 7 + drivers/arm-ffa/arm-ffa-uclass.c | 103 ++ drivers/arm-ffa/arm_ffa_prv.h | 193 ++++ drivers/arm-ffa/core.c | 1384 +++++++++++++++++++++++++ drivers/arm-ffa/sandbox.c | 669 ++++++++++++ drivers/arm-ffa/sandbox_arm_ffa_prv.h | 131 +++ include/arm_ffa.h | 197 ++++ include/arm_ffa_helper.h | 45 + include/dm/uclass-id.h | 1 + include/linux/arm-smccc.h | 28 +- include/mm_communication.h | 4 +- include/sandbox_arm_ffa.h | 31 + include/sandbox_arm_ffa_helper.h | 26 + lib/Kconfig | 1 + lib/Makefile | 1 + lib/arm-ffa/Kconfig | 6 + lib/arm-ffa/Makefile | 9 + lib/arm-ffa/arm_ffa_helper.c | 188 ++++ lib/arm-ffa/sandbox_arm_ffa_helper.c | 23 + lib/efi_loader/Kconfig | 14 +- lib/efi_loader/efi_boottime.c | 17 + lib/efi_loader/efi_variable_tee.c | 281 ++++- test/cmd/Makefile | 1 + test/cmd/armffa.c | 33 + test/cmd/armffa.h | 13 + test/dm/Makefile | 1 + test/dm/ffa.c | 418 ++++++++ test/dm/ffa.h | 22 + 43 files changed, 4230 insertions(+), 11 deletions(-) create mode 100644 cmd/armffa.c create mode 100644 drivers/arm-ffa/Kconfig create mode 100644 drivers/arm-ffa/Makefile create mode 100644 drivers/arm-ffa/arm-ffa-uclass.c create mode 100644 drivers/arm-ffa/arm_ffa_prv.h create mode 100644 drivers/arm-ffa/core.c create mode 100644 drivers/arm-ffa/sandbox.c create mode 100644 drivers/arm-ffa/sandbox_arm_ffa_prv.h create mode 100644 include/arm_ffa.h create mode 100644 include/arm_ffa_helper.h create mode 100644 include/sandbox_arm_ffa.h create mode 100644 include/sandbox_arm_ffa_helper.h create mode 100644 lib/arm-ffa/Kconfig create mode 100644 lib/arm-ffa/Makefile create mode 100644 lib/arm-ffa/arm_ffa_helper.c create mode 100644 lib/arm-ffa/sandbox_arm_ffa_helper.c create mode 100644 test/cmd/armffa.c create mode 100644 test/cmd/armffa.h create mode 100644 test/dm/ffa.c create mode 100644 test/dm/ffa.h

From: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
Add the driver implementing Arm Firmware Framework for Armv8-A v1.0
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. This driver uses SMC32 calling convention.
In u-boot FF-A design, FF-A is considered as a discoverable bus. 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 provides helper FF-A interfaces for user layers. These helper functions allow clients to pass data and select the FF-A function to use for the communication with secure world.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com --- MAINTAINERS | 8 + arch/arm/cpu/armv8/smccc-call.S | 27 + arch/arm/lib/asm-offsets.c | 6 + common/board_r.c | 7 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/arm-ffa/Kconfig | 27 + drivers/arm-ffa/Makefile | 6 + drivers/arm-ffa/arm-ffa-uclass.c | 64 ++ drivers/arm-ffa/arm_ffa_prv.h | 193 +++++ drivers/arm-ffa/core.c | 1349 ++++++++++++++++++++++++++++++ include/arm_ffa.h | 190 +++++ include/arm_ffa_helper.h | 45 + include/dm/uclass-id.h | 1 + include/linux/arm-smccc.h | 28 +- lib/Kconfig | 1 + lib/Makefile | 1 + lib/arm-ffa/Kconfig | 6 + lib/arm-ffa/Makefile | 8 + lib/arm-ffa/arm_ffa_helper.c | 188 +++++ lib/efi_loader/efi_boottime.c | 17 + 21 files changed, 2174 insertions(+), 1 deletion(-) create mode 100644 drivers/arm-ffa/Kconfig create mode 100644 drivers/arm-ffa/Makefile create mode 100644 drivers/arm-ffa/arm-ffa-uclass.c create mode 100644 drivers/arm-ffa/arm_ffa_prv.h create mode 100644 drivers/arm-ffa/core.c create mode 100644 include/arm_ffa.h create mode 100644 include/arm_ffa_helper.h create mode 100644 lib/arm-ffa/Kconfig create mode 100644 lib/arm-ffa/Makefile create mode 100644 lib/arm-ffa/arm_ffa_helper.c
diff --git a/MAINTAINERS b/MAINTAINERS index aca97cd2a3..efa17206b8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -232,6 +232,14 @@ F: board/CZ.NIC/ F: configs/turris_*_defconfig F: include/configs/turris_*.h
+ARM FF-A +M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com +S: Maintained +F: drivers/arm-ffa/ +F: include/arm_ffa.h +F: include/arm_ffa_helper.h +F: lib/arm-ffa/ + ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de M: Fabio Estevam festevam@gmail.com diff --git a/arch/arm/cpu/armv8/smccc-call.S b/arch/arm/cpu/armv8/smccc-call.S index dc92b28777..9a6aebf194 100644 --- a/arch/arm/cpu/armv8/smccc-call.S +++ b/arch/arm/cpu/armv8/smccc-call.S @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2015, Linaro Limited + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */ #include <linux/linkage.h> #include <linux/arm-smccc.h> @@ -45,3 +47,28 @@ ENDPROC(__arm_smccc_smc) ENTRY(__arm_smccc_hvc) SMCCC hvc ENDPROC(__arm_smccc_hvc) + +#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) + + .macro FFASMCCC instr + .cfi_startproc + \instr #0 + ldr x9, [sp] + stp x0, x1, [x9, #ARM_SMCCC_RES_X0_OFFS] + stp x2, x3, [x9, #ARM_SMCCC_RES_X2_OFFS] + stp x4, x5, [x9, #ARM_SMCCC_RES_X4_OFFS] + stp x6, x7, [x9, #ARM_SMCCC_RES_X6_OFFS] + ret + .cfi_endproc + .endm + +/* + * void arm_ffa_smccc_smc(unsigned long a0, unsigned long a1, unsigned long a2, + * unsigned long a3, unsigned long a4, unsigned long a5, + * unsigned long a6, unsigned long a7, struct arm_smccc_res *res) + */ +ENTRY(__arm_ffa_smccc_smc) + FFASMCCC smc +ENDPROC(__arm_ffa_smccc_smc) + +#endif diff --git a/arch/arm/lib/asm-offsets.c b/arch/arm/lib/asm-offsets.c index 22fd541f9a..02a4a42fe6 100644 --- a/arch/arm/lib/asm-offsets.c +++ b/arch/arm/lib/asm-offsets.c @@ -9,6 +9,8 @@ * generate asm statements containing #defines, * compile this file to assembler, and then extract the * #defines from the assembly-language output. + * + * (C) Copyright 2022 ARM Limited */
#include <common.h> @@ -115,6 +117,10 @@ int main(void) #ifdef CONFIG_ARM_SMCCC DEFINE(ARM_SMCCC_RES_X0_OFFS, offsetof(struct arm_smccc_res, a0)); DEFINE(ARM_SMCCC_RES_X2_OFFS, offsetof(struct arm_smccc_res, a2)); +#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) + DEFINE(ARM_SMCCC_RES_X4_OFFS, offsetof(struct arm_smccc_res, a4)); + DEFINE(ARM_SMCCC_RES_X6_OFFS, offsetof(struct arm_smccc_res, a6)); +#endif DEFINE(ARM_SMCCC_QUIRK_ID_OFFS, offsetof(struct arm_smccc_quirk, id)); DEFINE(ARM_SMCCC_QUIRK_STATE_OFFS, offsetof(struct arm_smccc_quirk, state)); #endif diff --git a/common/board_r.c b/common/board_r.c index b92c1bb0be..bb5f1d0aa6 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -62,6 +62,10 @@ #include <asm-generic/gpio.h> #include <efi_loader.h>
+#ifdef CONFIG_ARM_FFA_TRANSPORT +#include <arm_ffa_helper.h> +#endif + DECLARE_GLOBAL_DATA_PTR;
ulong monitor_flash_len; @@ -771,6 +775,9 @@ static init_fnc_t init_sequence_r[] = { INIT_FUNC_WATCHDOG_RESET initr_net, #endif +#ifdef CONFIG_ARM_FFA_TRANSPORT + ffa_helper_bus_discover, +#endif #ifdef CONFIG_POST initr_post, #endif diff --git a/drivers/Kconfig b/drivers/Kconfig index b26ca8cf70..e83c23789d 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -6,6 +6,8 @@ source "drivers/core/Kconfig"
source "drivers/adc/Kconfig"
+source "drivers/arm-ffa/Kconfig" + source "drivers/ata/Kconfig"
source "drivers/axi/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 4e7cf28440..6671d2a604 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -107,6 +107,7 @@ obj-y += iommu/ obj-y += smem/ obj-y += thermal/ obj-$(CONFIG_TEE) += tee/ +obj-$(CONFIG_ARM_FFA_TRANSPORT) += arm-ffa/ obj-y += axi/ obj-y += ufs/ obj-$(CONFIG_W1) += w1/ diff --git a/drivers/arm-ffa/Kconfig b/drivers/arm-ffa/Kconfig new file mode 100644 index 0000000000..23815534c4 --- /dev/null +++ b/drivers/arm-ffa/Kconfig @@ -0,0 +1,27 @@ +# 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 if ARM64 + select LIB_UUID + select ARM_FFA_TRANSPORT_HELPERS + 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. + + This driver 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. + 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. diff --git a/drivers/arm-ffa/Makefile b/drivers/arm-ffa/Makefile new file mode 100644 index 0000000000..7bc9a336a9 --- /dev/null +++ b/drivers/arm-ffa/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2022 Abdellatif El Khlifi abdellatif.elkhlifi@arm.com +# + +obj-y += arm-ffa-uclass.o core.o diff --git a/drivers/arm-ffa/arm-ffa-uclass.c b/drivers/arm-ffa/arm-ffa-uclass.c new file mode 100644 index 0000000000..2439f87586 --- /dev/null +++ b/drivers/arm-ffa/arm-ffa-uclass.c @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <dm.h> +#include <arm_ffa.h> +#include <errno.h> +#include <log.h> +#include <asm/global_data.h> + +DECLARE_GLOBAL_DATA_PTR; + +UCLASS_DRIVER(ffa) = { + .name = "ffa", + .id = UCLASS_FFA, +}; + +/** + * ffa_get_invoke_func - performs a call to the FF-A driver dispatcher + * @func_id: The FF-A function to be used + * @func_data: Pointer to the FF-A function arguments + * container structure. This also includes + * pointers to the returned data needed by + * clients. + * + * This runtime function passes the FF-A function ID and its arguments to + * the FF-A driver dispatcher. + * This function is called by the FF-A helper functions. + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +int __ffa_runtime ffa_get_invoke_func(u32 func_id, struct ffa_interface_data *func_data) +{ + if (!ffa_device_get_ops()->invoke_func) + return -EINVAL; + + return ffa_device_get_ops()->invoke_func(func_id, func_data); +} + +/** + * ffa_bus_discover - discover FF-A bus and probe the arm_ffa device + * + * This boot time function makes sure the FF-A bus is discoverable. + * Then, the arm_ffa device is probed and ready to use. + * This function is called automatically at initcalls + * level (after u-boot relocation). + * + * Arm FF-A transport is implemented through arm_ffa u-boot device managing the FF-A + * communication. + * All FF-A clients should use the arm_ffa device to use the FF-A transport. + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +int ffa_bus_discover(void) +{ + return ffa_get_device(); +} diff --git a/drivers/arm-ffa/arm_ffa_prv.h b/drivers/arm-ffa/arm_ffa_prv.h new file mode 100644 index 0000000000..44f258addb --- /dev/null +++ b/drivers/arm-ffa/arm_ffa_prv.h @@ -0,0 +1,193 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __ARM_FFA_PRV_H +#define __ARM_FFA_PRV_H + +#include <arm_ffa.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <dm/read.h> + +/* + * This header is private. It is exclusively used by the FF-A driver + */ + +/* FF-A core driver name */ +#define FFA_DRV_NAME "arm_ffa" + +/* 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))) + +/* The FF-A SMC function prototype definition */ + +typedef void (*invoke_ffa_fn_t)(unsigned long a0, unsigned long a1, + unsigned long a2, unsigned long a3, unsigned long a4, + unsigned long a5, unsigned long a6, unsigned long a7, + struct arm_smccc_res *res); + +/** + * enum ffa_conduit - Arm FF-A conduits supported by the Arm FF-A driver + * Currently only SMC32 is supported. + */ +enum ffa_conduit { + FFA_CONDUIT_SMC = 0, +}; + +/** + * FFA_DECLARE_ARGS - FF-A functions local variables + * @a0-a7: local variables used to set registers x0-x7 + * @res: the structure hosting the FF-A function return data + * + * A helper macro for declaring local variables for the FF-A functions arguments. + * The x0-x7 registers are used to exchange data with the secure world. + * But, only the bottom 32-bit of thes registers contains the data. + */ +#define FFA_DECLARE_ARGS \ + unsigned long a0 = 0; \ + unsigned long a1 = 0; \ + unsigned long a2 = 0; \ + unsigned long a3 = 0; \ + unsigned long a4 = 0; \ + unsigned long a5 = 0; \ + unsigned long a6 = 0; \ + unsigned long a7 = 0; \ + struct arm_smccc_res res = {0} + +/* FF-A error codes */ +#define FFA_ERR_STAT_NOT_SUPPORTED (-1) +#define FFA_ERR_STAT_INVALID_PARAMETERS (-2) +#define FFA_ERR_STAT_NO_MEMORY (-3) +#define FFA_ERR_STAT_BUSY (-4) +#define FFA_ERR_STAT_INTERRUPTED (-5) +#define FFA_ERR_STAT_DENIED (-6) +#define FFA_ERR_STAT_RETRY (-7) +#define FFA_ERR_STAT_ABORTED (-8) + +/** + * struct ffa_features_desc - FF-A functions features + * @func_id: FF-A function + * @field1: features read from register w2 + * @field2: features read from register w3 + * + * Data structure describing the features of the FF-A functions queried by + * FFA_FEATURES + */ +struct ffa_features_desc { + u32 func_id; + u32 field1; + u32 field2; +}; + +/** + * enum ffa_rxtx_buf_sizes - minimum sizes supported + * for the RX/TX buffers + */ +enum ffa_rxtx_buf_sizes { + RXTX_4K, + RXTX_64K, + RXTX_16K +}; + +/* + * Number of the FF-A interfaces features descriptors + * currently only FFA_RXTX_MAP descriptor is supported + */ +#define FFA_FEATURE_DESC_CNT (1) + +/** + * struct ffa_rxtxpair - structure hosting the RX/TX buffers virtual addresses + * @rxbuf: virtual address of the RX buffer + * @txbuf: virtual address of the TX buffer + * + * Data structure hosting 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 { + u64 rxbuf; /* virtual address */ + u64 txbuf; /* virtual address */ +}; + +/** + * struct ffa_partition_desc - the secure partition descriptor + * @info: partition information + * @UUID: UUID + * + * Each partition has its descriptor containing the partitions information and the UUID + */ +struct ffa_partition_desc { + struct ffa_partition_info info; + union ffa_partition_uuid UUID; +}; + +/** + * struct ffa_partitions - descriptors for all secure partitions + * @count: The number of partitions descriptors + * @descs The partitions descriptors table + * + * This data structure contains the partitions descriptors table + */ +struct ffa_partitions { + u32 count; + struct ffa_partition_desc *descs; /* virtual address */ +}; + +/** + * struct ffa_prvdata - the driver private data structure + * + * @dev: The arm_ffa device under u-boot driver model + * @fwk_version: FF-A framework version + * @id: u-boot endpoint ID + * @partitions: The partitions descriptors structure + * @pair: The RX/TX buffers pair + * @conduit: The selected conduit + * @invoke_ffa_fn: The function executing the FF-A function + * @features: Table of the FF-A functions having features + * + * The driver data structure hosting all resident data. + */ +struct ffa_prvdata { + struct udevice *dev; + u32 fwk_version; + u16 id; + struct ffa_partitions partitions; + struct ffa_rxtxpair pair; + enum ffa_conduit conduit; + invoke_ffa_fn_t invoke_ffa_fn; + struct ffa_features_desc features[FFA_FEATURE_DESC_CNT]; +}; + +#endif diff --git a/drivers/arm-ffa/core.c b/drivers/arm-ffa/core.c new file mode 100644 index 0000000000..09e4eb753a --- /dev/null +++ b/drivers/arm-ffa/core.c @@ -0,0 +1,1349 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include "arm_ffa_prv.h" +#include <asm/global_data.h> +#include <asm/io.h> +#include <common.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> +#include <log.h> +#include <malloc.h> +#include <mapmem.h> +#include <string.h> + +DECLARE_GLOBAL_DATA_PTR; + +/** + * The device private data structure containing all the resident + * data read from secure world + */ +struct ffa_prvdata __ffa_runtime_data ffa_priv_data = {0}; + +/* + * Driver functions + */ + +/** + * ffa_get_device - create, bind and probe the arm_ffa device + * + * This boot time function makes sure the arm_ffa device is + * created, bound to this driver, probed and ready to use. + * Arm FF-A transport is implemented through a single u-boot + * device managing the FF-A bus (arm_ffa). + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +int ffa_get_device(void) +{ + int ret; + + if (ffa_priv_data.dev) + return FFA_ERR_STAT_SUCCESS; + + ret = device_bind(dm_root(), + DM_DRIVER_GET(arm_ffa), + FFA_DRV_NAME, + NULL, + ofnode_null(), + &ffa_priv_data.dev); + if (ret) { + ffa_priv_data.dev = NULL; + return ret; + } + + /* The FF-A bus discovery succeeds when probing is successful */ + ret = device_probe(ffa_priv_data.dev); + if (ret) { + ffa_err("can not probe the device"); + device_unbind(ffa_priv_data.dev); + ffa_priv_data.dev = NULL; + return ret; + } + + return FFA_ERR_STAT_SUCCESS; +} + +/** + * ffa_get_version - FFA_VERSION handler function + * + * This is the boot time function that implements FFA_VERSION FF-A function + * to get from the secure world the FF-A framework version + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +static int ffa_get_version(void) +{ + u16 major, minor; + + FFA_DECLARE_ARGS; + + if (!ffa_priv_data.invoke_ffa_fn) + panic("[FFA] no private data found\n"); + + a0 = FFA_VERSION; + a1 = FFA_VERSION_1_0; + ffa_priv_data.invoke_ffa_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res); + + if (res.a0 == FFA_ERR_STAT_NOT_SUPPORTED) { + ffa_err("A Firmware Framework implementation does not exist"); + return -EOPNOTSUPP; + } + + major = GET_FFA_MAJOR_VERSION(res.a0); + minor = GET_FFA_MINOR_VERSION(res.a0); + + ffa_info("FF-A driver %d.%d\nFF-A framework %d.%d", + FFA_MAJOR_VERSION, FFA_MINOR_VERSION, major, minor); + + if ((major == FFA_MAJOR_VERSION && minor >= FFA_MINOR_VERSION)) { + ffa_info("Versions are compatible "); + + ffa_priv_data.fwk_version = res.a0; + + return FFA_ERR_STAT_SUCCESS; + } + + ffa_info("Versions are incompatible "); + return -EPROTONOSUPPORT; +} + +/** + * ffa_get_endpoint_id - FFA_ID_GET handler function + * + * This is the boot time function that implements FFA_ID_GET FF-A function + * to get from the secure world u-boot endpoint ID + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +static int ffa_get_endpoint_id(void) +{ + FFA_DECLARE_ARGS; + + if (!ffa_priv_data.invoke_ffa_fn) + panic("[FFA] no private data found\n"); + + a0 = FFA_ID_GET; + + ffa_priv_data.invoke_ffa_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res); + + switch (res.a0) { + case FFA_ERROR: + { + if (((int)res.a2) == FFA_ERR_STAT_NOT_SUPPORTED) { + ffa_err("This function is not implemented at this FF-A instance"); + return -EOPNOTSUPP; + } + + ffa_err("Undefined error code (%d)", ((int)res.a2)); + return -EINVAL; + } + case FFA_SUCCESS: + { + ffa_priv_data.id = GET_SELF_ENDPOINT_ID(res.a2); + ffa_info("endpoint ID is %u", ffa_priv_data.id); + + return FFA_ERR_STAT_SUCCESS; + } + default: + { + ffa_err("Undefined response function (0x%lx)", res.a0); + return -EINVAL; + } + } +} + +/** + * ffa_get_features_desc - returns the features descriptor of the specified + * FF-A function + * @func_id: the FF-A function which the features are to be retrieved + * + * This is a boot time function that searches the features descriptor of the + * specified FF-A function + * + * Return: + * + * When found, the address of the features descriptor is returned. Otherwise, NULL. + */ +static struct ffa_features_desc *ffa_get_features_desc(u32 func_id) +{ + u32 desc_idx; + + /* + * search for the descriptor of the selected FF-A interface + */ + for (desc_idx = 0; desc_idx < FFA_FEATURE_DESC_CNT ; desc_idx++) + if (ffa_priv_data.features[desc_idx].func_id == func_id) + return &ffa_priv_data.features[desc_idx]; + + return NULL; +} + +/** + * ffa_get_rxtx_map_features - FFA_FEATURES handler function with FFA_RXTX_MAP + * argument + * + * This is the boot time function that implements FFA_FEATURES FF-A function + * to retrieve the FFA_RXTX_MAP features + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +static int ffa_get_rxtx_map_features(void) +{ + FFA_DECLARE_ARGS; + + if (!ffa_priv_data.invoke_ffa_fn) + panic("[FFA] no private data found\n"); + + a0 = FFA_FEATURES; + a1 = FFA_RXTX_MAP; + + ffa_priv_data.invoke_ffa_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res); + + switch (res.a0) { + case FFA_ERROR: + { + if (((int)res.a2) == FFA_ERR_STAT_NOT_SUPPORTED) { + ffa_err("FFA_RXTX_MAP is not implemented at this FF-A instance"); + return -EOPNOTSUPP; + } + + ffa_err("Undefined error code (%d)", ((int)res.a2)); + return -EINVAL; + } + case FFA_SUCCESS: + { + u32 desc_idx; + + /* + * search for an empty descriptor + */ + for (desc_idx = 0; desc_idx < FFA_FEATURE_DESC_CNT ; desc_idx++) + if (!ffa_priv_data.features[desc_idx].func_id) { + /* + * populate the descriptor with + * the interface features data + */ + ffa_priv_data.features[desc_idx].func_id = + FFA_RXTX_MAP; + ffa_priv_data.features[desc_idx].field1 = + res.a2; + + ffa_info("FFA_RXTX_MAP features data 0x%lx", + res.a2); + + return FFA_ERR_STAT_SUCCESS; + } + + ffa_err("Cannot save FFA_RXTX_MAP features data. Descriptors table full"); + return -ENOBUFS; + } + default: + { + ffa_err("Undefined response function (0x%lx)", + res.a0); + return -EINVAL; + } + } +} + +/** + * ffa_get_rxtx_buffers_pages_cnt - reads from the features data descriptors + * the minimum number of pages in each of the RX/TX + * buffers + * @buf_4k_pages: Pointer to the minimum number of pages + * + * This is the boot time function that returns the minimum number of pages + * in each of the RX/TX buffers + * + * Return: + * + * buf_4k_pages points to the returned number of pages + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +static int ffa_get_rxtx_buffers_pages_cnt(size_t *buf_4k_pages) +{ + struct ffa_features_desc *desc = NULL; + + if (!buf_4k_pages) + return -EINVAL; + + desc = ffa_get_features_desc(FFA_RXTX_MAP); + if (!desc) + return -EINVAL; + + switch (desc->field1) { + case RXTX_4K: + *buf_4k_pages = 1; + break; + case RXTX_16K: + *buf_4k_pages = 4; + break; + case RXTX_64K: + *buf_4k_pages = 16; + break; + default: + ffa_err("RX/TX buffer size not supported"); + return -EINVAL; + } + + return FFA_ERR_STAT_SUCCESS; +} + +/** + * ffa_free_rxtx_buffers - frees the RX/TX buffers + * @buf_4k_pages: the minimum number of pages in each of the RX/TX + * buffers + * + * This is the boot time function used to free the RX/TX buffers + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +static int ffa_free_rxtx_buffers(size_t buf_4k_pages) +{ + efi_status_t free_rxbuf_ret, free_txbuf_ret; + + ffa_info("Freeing RX/TX buffers"); + + free_rxbuf_ret = efi_free_pages(ffa_priv_data.pair.rxbuf, buf_4k_pages); + free_txbuf_ret = efi_free_pages(ffa_priv_data.pair.txbuf, buf_4k_pages); + + if (free_rxbuf_ret != EFI_SUCCESS || free_txbuf_ret != EFI_SUCCESS) { + ffa_err("Failed to free RX/TX buffers (rx: %lu , tx: %lu)", + free_rxbuf_ret, + free_txbuf_ret); + return -EINVAL; + } + + ffa_priv_data.pair.rxbuf = 0; + ffa_priv_data.pair.txbuf = 0; + + return FFA_ERR_STAT_SUCCESS; +} + +/** + * ffa_alloc_rxtx_buffers - allocates the RX/TX buffers + * @buf_4k_pages: the minimum number of pages in each of the RX/TX + * buffers + * + * This is the boot time function used by ffa_map_rxtx_buffers to allocate + * the RX/TX buffers before mapping them + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +static int ffa_alloc_rxtx_buffers(size_t buf_4k_pages) +{ +#if CONFIG_IS_ENABLED(EFI_LOADER) + + efi_status_t efi_ret; + void *virt_txbuf; + void *virt_rxbuf; + + ffa_info("Using %lu 4KB page(s) for RX/TX buffers size", + buf_4k_pages); + + efi_ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES, + EFI_BOOT_SERVICES_DATA, + buf_4k_pages, + &ffa_priv_data.pair.rxbuf); + + if (efi_ret != EFI_SUCCESS) { + ffa_priv_data.pair.rxbuf = 0; + ffa_err("Failure to allocate RX buffer (EFI error: 0x%lx)", + efi_ret); + + return -ENOBUFS; + } + + ffa_info("RX buffer at virtual address 0x%llx", + ffa_priv_data.pair.rxbuf); + + virt_rxbuf = (void *)ffa_priv_data.pair.rxbuf; + + /* + * make sure the buffer is clean before use + */ + memset(virt_rxbuf, 0, buf_4k_pages * SZ_4K); + + efi_ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES, + EFI_RUNTIME_SERVICES_DATA, + buf_4k_pages, + &ffa_priv_data.pair.txbuf); + + if (efi_ret != EFI_SUCCESS) { + efi_free_pages(ffa_priv_data.pair.rxbuf, buf_4k_pages); + ffa_priv_data.pair.rxbuf = 0; + ffa_priv_data.pair.txbuf = 0; + ffa_err("Failure to allocate the TX buffer (EFI error: 0x%lx)" + , efi_ret); + + return -ENOBUFS; + } + + ffa_info("TX buffer at virtual address 0x%llx", + ffa_priv_data.pair.txbuf); + + virt_txbuf = (void *)ffa_priv_data.pair.txbuf; + + /* + * make sure the buffer is clean before use + */ + memset(virt_txbuf, 0, buf_4k_pages * SZ_4K); + + return FFA_ERR_STAT_SUCCESS; + +#else + return -ENOBUFS; +#endif +} + +/** + * ffa_map_rxtx_buffers - FFA_RXTX_MAP handler function + * @buf_4k_pages: the minimum number of pages in each of the RX/TX + * buffers + * + * This is the boot time function that implements FFA_RXTX_MAP FF-A function + * to map the RX/TX buffers + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +static int ffa_map_rxtx_buffers(size_t buf_4k_pages) +{ + int ret; + + FFA_DECLARE_ARGS; + + if (!ffa_priv_data.invoke_ffa_fn) + panic("[FFA] no private data found\n"); + + ret = ffa_alloc_rxtx_buffers(buf_4k_pages); + if (ret != FFA_ERR_STAT_SUCCESS) + return ret; + + a0 = FFA_RXTX_MAP; + a1 = ffa_priv_data.pair.txbuf; + a2 = ffa_priv_data.pair.rxbuf; + a3 = buf_4k_pages; + + ffa_priv_data.invoke_ffa_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res); + + switch (res.a0) { + case FFA_ERROR: + { + switch (((int)res.a2)) { + case FFA_ERR_STAT_INVALID_PARAMETERS: + ffa_err("One or more fields in input parameters is incorrectly encoded"); + ret = -EPERM; + break; + case FFA_ERR_STAT_NO_MEMORY: + ffa_err("Not enough memory"); + ret = -ENOMEM; + break; + case FFA_ERR_STAT_DENIED: + ffa_err("Buffer pair already registered"); + ret = -EACCES; + break; + case FFA_ERR_STAT_NOT_SUPPORTED: + ffa_err("This function is not implemented at this FF-A instance"); + ret = -EOPNOTSUPP; + break; + default: + ffa_err("Undefined error (%d)", + ((int)res.a2)); + ret = -EINVAL; + } + break; + } + case FFA_SUCCESS: + ffa_info("RX/TX buffers mapped"); + return FFA_ERR_STAT_SUCCESS; + default: + ffa_err("Undefined response function (0x%lx)", + res.a0); + ret = -EINVAL; + } + + ffa_free_rxtx_buffers(buf_4k_pages); + + return ret; +} + +/** + * ffa_unmap_rxtx_buffers - FFA_RXTX_UNMAP handler function + * + * This is the boot time function that implements FFA_RXTX_UNMAP FF-A function + * to unmap the RX/TX buffers + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +static int ffa_unmap_rxtx_buffers(void) +{ + FFA_DECLARE_ARGS; + + if (!ffa_priv_data.invoke_ffa_fn) + panic("[FFA] no private data found\n"); + + a0 = FFA_RXTX_UNMAP; + a1 = PREP_SELF_ENDPOINT_ID(ffa_priv_data.id); + + ffa_priv_data.invoke_ffa_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res); + + switch (res.a0) { + case FFA_ERROR: + { + if (((int)res.a2) == FFA_ERR_STAT_NOT_SUPPORTED) + panic("[FFA] FFA_RXTX_UNMAP is not implemented at this FF-A instance\n"); + else if (((int)res.a2) == FFA_ERR_STAT_INVALID_PARAMETERS) + panic("[FFA] There is no buffer pair registered on behalf of the caller\n"); + else + panic("[FFA] Undefined error (%d)\n", ((int)res.a2)); + } + case FFA_SUCCESS: + { + size_t buf_4k_pages = 0; + int ret; + + ret = ffa_get_rxtx_buffers_pages_cnt(&buf_4k_pages); + if (ret != FFA_ERR_STAT_SUCCESS) + panic("[FFA] RX/TX buffers unmapped but failure in getting pages count\n"); + + ret = ffa_free_rxtx_buffers(buf_4k_pages); + if (ret != FFA_ERR_STAT_SUCCESS) + panic("[FFA] RX/TX buffers unmapped but failure in freeing the memory\n"); + + ffa_info("RX/TX buffers unmapped and memory freed"); + + return FFA_ERR_STAT_SUCCESS; + } + default: + panic("[FFA] Undefined response function (0x%lx)", res.a0); + } +} + +/** + * ffa_release_rx_buffer - FFA_RX_RELEASE handler function + * + * This is the boot time function that invokes FFA_RX_RELEASE FF-A function + * to release the ownership of the RX buffer + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +static int ffa_release_rx_buffer(void) +{ + FFA_DECLARE_ARGS; + + if (!ffa_priv_data.invoke_ffa_fn) + panic("[FFA] no private data found\n"); + + a0 = FFA_RX_RELEASE; + + ffa_priv_data.invoke_ffa_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res); + + switch (res.a0) { + case FFA_ERROR: + { + if (((int)res.a2) == FFA_ERR_STAT_NOT_SUPPORTED) + panic("[FFA] FFA_RX_RELEASE is not implemented at this FF-A instance\n"); + else if (((int)res.a2) == FFA_ERR_STAT_DENIED) + panic("[FFA] Caller did not have ownership of the RX buffer\n"); + else + panic("[FFA] Undefined error (%d)\n", ((int)res.a2)); + } + case FFA_SUCCESS: + return FFA_ERR_STAT_SUCCESS; + + default: + panic("[FFA] Undefined response function (0x%lx)\n", res.a0); + } +} + +/** + * ffa_uuid_are_identical - checks whether two given UUIDs are identical + * @uuid1: first UUID + * @uuid2: second UUID + * + * This is a boot time function used by ffa_read_partitions_info to search + * for a UUID in the partitions descriptors table + * + * Return: + * + * 1 when UUIDs match. Otherwise, 0 + */ +int ffa_uuid_are_identical(const union ffa_partition_uuid *uuid1, + const union ffa_partition_uuid *uuid2) +{ + if (!uuid1 || !uuid2) + return 0; + + return (!memcmp(uuid1, uuid2, sizeof(union ffa_partition_uuid))); +} + +/** + * ffa_read_partitions_info - reads the data queried by FFA_PARTITION_INFO_GET + * and saves it in the private structure + * @count: The number of partitions queried + * @part_uuid: Pointer to the partition(s) UUID + * + * This is the boot time function that reads the partitions information + * returned by the FFA_PARTITION_INFO_GET and saves it in the private + * data structure. + * + * Return: + * + * The private data structure is updated with the partition(s) information + * FFA_ERR_STAT_SUCCESS is returned on success. Otherwise, failure + */ +static int ffa_read_partitions_info(u32 count, union ffa_partition_uuid *part_uuid) +{ + if (!count) { + ffa_err("No partition detected"); + return -ENODATA; + } + + ffa_info("Reading partitions data from the RX buffer"); + +#if CONFIG_IS_ENABLED(EFI_LOADER) + + if (!part_uuid) { + /* + * querying information of all partitions + */ + u64 data_pages; + u64 data_bytes; + efi_status_t efi_ret; + size_t buf_4k_pages = 0; + u32 desc_idx; + struct ffa_partition_info *parts_info; + int ret; + + data_bytes = count * sizeof(struct ffa_partition_desc); + data_pages = efi_size_in_pages(data_bytes); + + /* + * get the RX buffer size in pages + */ + ret = ffa_get_rxtx_buffers_pages_cnt(&buf_4k_pages); + if (ret != FFA_ERR_STAT_SUCCESS) { + ffa_err("Can not get the RX buffer size (error %d)", ret); + return ret; + } + + if (data_pages > buf_4k_pages) { + ffa_err("Partitions data size exceeds the RX buffer size:"); + ffa_err(" Sizes in pages: data %llu , RX buffer %lu ", + data_pages, + buf_4k_pages); + + return -ENOMEM; + } + + efi_ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES, + EFI_RUNTIME_SERVICES_DATA, + data_pages, + (u64 *)&ffa_priv_data.partitions.descs); + + if (efi_ret != EFI_SUCCESS) { + ffa_priv_data.partitions.descs = NULL; + + ffa_err("Cannot allocate partitions data buffer (EFI error 0x%lx)", + efi_ret); + + return -ENOBUFS; + } + + /* + * make sure the buffer is clean before use + */ + memset(ffa_priv_data.partitions.descs, 0, + data_pages * SZ_4K); + + parts_info = (struct ffa_partition_info *)ffa_priv_data.pair.rxbuf; + + for (desc_idx = 0 ; desc_idx < count ; desc_idx++) { + ffa_priv_data.partitions.descs[desc_idx].info = + parts_info[desc_idx]; + + ffa_info("Partition ID %x : info cached", + ffa_priv_data.partitions.descs[desc_idx].info.id); + } + + ffa_priv_data.partitions.count = count; + + ffa_info("%d partition(s) found and cached", count); + + } else { + u32 rx_desc_idx, cached_desc_idx; + struct ffa_partition_info *parts_info; + u8 desc_found; + + parts_info = (struct ffa_partition_info *)ffa_priv_data.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 < ffa_priv_data.partitions.count; + cached_desc_idx++) { + /* + * save the UUID + */ + if (ffa_priv_data.partitions.descs[cached_desc_idx].info.id == + parts_info[rx_desc_idx].id) { + ffa_priv_data.partitions.descs[cached_desc_idx].UUID = + *part_uuid; + + desc_found = 1; + break; + } + } + + if (!desc_found) + return -ENODATA; + } + } +#else +#warning "arm_ffa: reading FFA_PARTITION_INFO_GET data not implemented" +#endif + + return FFA_ERR_STAT_SUCCESS; +} + +/** + * ffa_query_partitions_info - invokes FFA_PARTITION_INFO_GET + * and saves partitions data + * @part_uuid: Pointer to the partition(s) UUID + * @pcount: Pointer to the number of partitions variable filled when querying + * + * This is the boot time function that executes the FFA_PARTITION_INFO_GET + * to query the partitions data. Then, it calls ffa_read_partitions_info + * to save the data in the private data structure. + * + * 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 + * FFA_ERR_STAT_SUCCESS is returned on success. Otherwise, failure + */ +static int ffa_query_partitions_info(union ffa_partition_uuid *part_uuid, + u32 *pcount) +{ + unsigned long a0 = 0; + union ffa_partition_uuid query_uuid = {0}; + unsigned long a5 = 0; + unsigned long a6 = 0; + unsigned long a7 = 0; + struct arm_smccc_res res = {0}; + + if (!ffa_priv_data.invoke_ffa_fn) + panic("[FFA] no private data found\n"); + + a0 = FFA_PARTITION_INFO_GET; + + /* + * 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; + } + + ffa_priv_data.invoke_ffa_fn(a0, query_uuid.words.a1, query_uuid.words.a2, + query_uuid.words.a3, query_uuid.words.a4, + a5, a6, a7, &res); + + switch (res.a0) { + case FFA_ERROR: + { + switch (((int)res.a2)) { + case FFA_ERR_STAT_INVALID_PARAMETERS: + ffa_err("Unrecognized UUID"); + return -EPERM; + case FFA_ERR_STAT_NO_MEMORY: + ffa_err("Results cannot fit in RX buffer of the caller"); + return -ENOMEM; + case FFA_ERR_STAT_DENIED: + ffa_err("Callee is not in a state to handle this request"); + return -EACCES; + case FFA_ERR_STAT_NOT_SUPPORTED: + ffa_err("This function is not implemented at this FF-A instance"); + return -EOPNOTSUPP; + case FFA_ERR_STAT_BUSY: + ffa_err("RX buffer of the caller is not free"); + return -EBUSY; + default: + ffa_err("Undefined error (%d)", ((int)res.a2)); + return -EINVAL; + } + } + case 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(res.a2, part_uuid); + if (ret) + ffa_err("Failed to read partition(s) data , error (%d)", ret); + } + + /* + * return the SP count + */ + if (part_uuid) { + if (!ret) + *pcount = res.a2; + else + *pcount = 0; + } + /* + * 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 secure world + */ + ret = ffa_release_rx_buffer(); + + if (!part_uuid && !res.a2) { + ffa_err("[FFA] no partition installed in the system"); + return -ENODEV; + } + + return ret; + } + default: + ffa_err("Undefined response function (0x%lx)", res.a0); + return -EINVAL; + } +} + +/** + * ffa_get_partitions_info - FFA_PARTITION_INFO_GET handler function + * @func_data: Pointer to the FF-A function arguments container structure. + * The passed arguments: + * Mode 1: When getting from the driver the number of + * secure partitions: + * @data0_size: UUID size + * @data0: pointer to the UUID (little endian) + * @data1_size: size of the number of partitions + * variable + * @data1: pointer to the number of partitions + * variable. The variable will be set + * by the driver + * Mode 2: When requesting the driver to return the + * partitions information: + * @data0_size: UUID size + * @data0: pointer to the UUID (little endian) + * @data1_size: size of the SPs information buffer + * @data1: pointer to SPs information buffer + * (allocated by the client). + * The buffer will be filled by the driver + * + * This is the boot time function that queries the secure partition data from + * the private data structure. If not found, it invokes 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 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(). + * + * When invoked through a client request, ffa_get_partitions_info should be + * called twice. First call is to get from the driver the number of secure + * partitions (SPs) associated to a particular UUID. + * Then, the caller (client) allocates the buffer to host the SPs data and + * issues a 2nd call. Then, the driver fills the SPs data in the pre-allocated + * buffer. + * + * To achieve the mechanism described above, ffa_get_partitions_info uses the + * following functions: + * ffa_read_partitions_info + * ffa_query_partitions_info + * + * Return: + * + * @data1: When pointing to the number of partitions variable, the number is + * set by the driver. + * When pointing to the partitions information buffer, the buffer will be + * filled by the driver. + * + * On success FFA_ERR_STAT_SUCCESS is returned. Otherwise, failure + */ +static int ffa_get_partitions_info(struct ffa_interface_data *func_data) +{ + /* + * fill_data: + * 0: return the SP count + * 1: fill SP data and return it to the caller + * -1: undefined mode + */ + int fill_data = -1; + u32 desc_idx, client_desc_idx; + union ffa_partition_uuid *part_uuid; + u32 client_desc_max_cnt; + u32 parts_found = 0; + + if (!func_data) { + ffa_err("No function data provided"); + return -EINVAL; + } + + if (!ffa_priv_data.partitions.count || !ffa_priv_data.partitions.descs) + panic("[FFA] No partition installed\n"); + + if (func_data->data0_size == sizeof(union ffa_partition_uuid) && + func_data->data0 && + func_data->data1_size == sizeof(u32) && + func_data->data1) { + /* + * data0 (in): pointer to UUID + * data1 (in): pointer to SP count + * Out: SP count returned in the count variable pointed by data1 + */ + + fill_data = 0; + + ffa_info("Preparing for checking partitions count"); + + } else if ((func_data->data0_size == sizeof(union ffa_partition_uuid)) && + func_data->data0 && + (func_data->data1_size >= sizeof(struct ffa_partition_info)) && + !(func_data->data1_size % sizeof(struct ffa_partition_info)) && + func_data->data1) { + /* + * data0 (in): pointer to UUID + * data1 (in): pointer to SPs descriptors buffer + * (created by the client) + * Out: SPs descriptors returned in the buffer + * pointed by data1 + */ + + fill_data = 1; + + client_desc_idx = 0; + + /* + * number of empty descriptors preallocated by the caller + */ + client_desc_max_cnt = + func_data->data1_size / sizeof(struct ffa_partition_info); + + ffa_info("Preparing for filling partitions info"); + + } else { + ffa_err("Invalid function arguments provided"); + return -EINVAL; + } + + part_uuid = (union ffa_partition_uuid *)func_data->data0; + + ffa_info("Searching partitions using the provided UUID"); + + /* + * search in the cached partitions + */ + for (desc_idx = 0; + desc_idx < ffa_priv_data.partitions.count; + desc_idx++) { + if (ffa_uuid_are_identical(&ffa_priv_data.partitions.descs[desc_idx].UUID, + part_uuid)) { + ffa_info("Partition ID %x matches the provided UUID", + ffa_priv_data.partitions.descs[desc_idx].info.id); + + parts_found++; + + if (fill_data) { + /* + * trying to fill the partition info in data1 + */ + + if (client_desc_idx < client_desc_max_cnt) { + ((struct ffa_partition_info *) + func_data->data1)[client_desc_idx++] = + ffa_priv_data.partitions.descs[desc_idx].info; + continue; + } + + ffa_err("Failed to fill the current descriptor client buffer full"); + return -ENOBUFS; + } + } + } + + if (!parts_found) { + int ret; + + ffa_info("No partition found. Querying framework ..."); + + ret = ffa_query_partitions_info(part_uuid, &parts_found); + + if (ret == FFA_ERR_STAT_SUCCESS) { + if (!fill_data) { + *((u32 *)func_data->data1) = parts_found; + + ffa_info("Number of partition(s) found matching the UUID: %d", + parts_found); + } else { + /* + * we want to read SPs info + */ + + /* + * If SPs data filled, retry searching SP info again + */ + if (parts_found) + ret = ffa_get_partitions_info(func_data); + else + ret = -ENODATA; + } + } + + return ret; + } + + /* partition(s) found */ + if (!fill_data) + *((u32 *)func_data->data1) = parts_found; + + return FFA_ERR_STAT_SUCCESS; +} + +/** + * ffa_cache_partitions_info - Queries and saves all secure partitions data + * + * This is a boot time function that invokes 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 the + * resident private data structure and we keep the UUID field empty + * (in FF-A 1.0 UUID is not provided by the partition descriptor) + * + * This function is called at the device probing level. + * ffa_cache_partitions_info uses ffa_query_partitions_info to get the data + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +static int ffa_cache_partitions_info(void) +{ + return ffa_query_partitions_info(NULL, NULL); +} + +/** + * ffa_msg_send_direct_req - FFA_MSG_SEND_DIRECT_{REQ,RESP} handler function + * @func_data: Pointer to the FF-A function arguments container structure. + * The passed arguments: + * @data0_size: partition ID size + * @data0: pointer to the partition ID + * @data1_size: exchanged data size + * @data1: pointer to the data buffer preallocated by + * the client (in/out) + * + * This is the runtime function that 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 20 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: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +static int __ffa_runtime ffa_msg_send_direct_req(struct ffa_interface_data + *func_data) +{ + u16 dst_part_id; + unsigned long a0 = 0; + unsigned long a1 = 0; + unsigned long a2 = 0; + struct ffa_send_direct_data *msg; + struct arm_smccc_res res = {0}; + + if (!ffa_priv_data.invoke_ffa_fn) + return -ENODEV; + + if (!func_data) + return -EINVAL; + + /* No partition installed */ + if (!ffa_priv_data.partitions.count || !ffa_priv_data.partitions.descs) + return -ENODEV; + + /* Undefined interface parameters */ + if (func_data->data0_size != sizeof(u16) || + !func_data->data0 || + func_data->data1_size != FFA_MSG_SEND_DIRECT_MAX_SIZE || + !func_data->data1) + return -EINVAL; + + dst_part_id = *((u16 *)func_data->data0); + msg = func_data->data1; + + a0 = FFA_MSG_SEND_DIRECT_REQ; + + a1 = PREP_SELF_ENDPOINT_ID(ffa_priv_data.id) | + PREP_PART_ENDPOINT_ID(dst_part_id); + + ffa_priv_data.invoke_ffa_fn(a0, a1, a2, + msg->a3, + msg->a4, + msg->a5, + msg->a6, + msg->a7, + &res); + + while (res.a0 == FFA_INTERRUPT) + ffa_priv_data.invoke_ffa_fn(FFA_RUN, res.a1, + 0, 0, 0, 0, 0, 0, + &res); + + switch (res.a0) { + case FFA_ERROR: + { + switch (((int)res.a2)) { + case FFA_ERR_STAT_INVALID_PARAMETERS: + /* Invalid endpoint ID or non-zero reserved register */ + return -EPERM; + case FFA_ERR_STAT_ABORTED: + /* Message target ran into unexpected error and has aborted */ + return -ECONNABORTED; + case FFA_ERR_STAT_DENIED: + /* Callee is not in a state to handle this request */ + return -EACCES; + case FFA_ERR_STAT_NOT_SUPPORTED: + /* This function is not implemented at this FF-A instance */ + return -EOPNOTSUPP; + case FFA_ERR_STAT_BUSY: + /* Message target is busy */ + return -EBUSY; + default: + /* Undefined error */ + return -ENXIO; + } + } + case FFA_SUCCESS: + + /* Message sent with no response */ + return FFA_ERR_STAT_SUCCESS; + + case FFA_MSG_SEND_DIRECT_RESP: + + /* + * Message sent with response + * extract the 32-bit wide return data + */ + msg->a3 = (u32)res.a3; + msg->a4 = (u32)res.a4; + msg->a5 = (u32)res.a5; + msg->a6 = (u32)res.a6; + msg->a7 = (u32)res.a7; + + return FFA_ERR_STAT_SUCCESS; + + default: + /* Undefined response function */ + return -ENOENT; + } +} + +/** + * invoke_ffa_drv_api - The driver dispatcher function + * @func_id: The FF-A function to be used + * @func_data: Pointer to the FF-A function arguments container + * structure. This also includes pointers to the + * returned data needed by clients. + * The dispatcher is a runtime function that selects the FF-A function handler + * based on the input FF-A function ID. + * The input arguments are passed to the handler function. + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +int __ffa_runtime invoke_ffa_drv_api(u32 func_id, + struct ffa_interface_data *func_data) +{ + if (!ffa_priv_data.dev) + return -ENODEV; + + switch (func_id) { + case FFA_PARTITION_INFO_GET: + return ffa_get_partitions_info(func_data); + case FFA_RXTX_UNMAP: + return ffa_unmap_rxtx_buffers(); + case FFA_MSG_SEND_DIRECT_REQ: + return ffa_msg_send_direct_req(func_data); + default: + /* Undefined FF-A interface */ + return -EINVAL; + } +} + +/** + * ffa_set_conduit - Set the conduit + * + * This boot time function clears the private data structure and sets the conduit + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +static int ffa_set_conduit(void) +{ + ffa_priv_data.conduit = FFA_CONDUIT_SMC; + + ffa_priv_data.invoke_ffa_fn = arm_ffa_smccc_smc; + + ffa_info("Conduit is SMC"); + + return FFA_ERR_STAT_SUCCESS; +} + +/** + * ffa_probe - The driver probe function + * @dev: the arm_ffa device + * + * Probing is done at boot time and triggered by the uclass device discovery. + * At probe level the following actions are done: + * - setting the conduit + * - querying the FF-A framework version + * - querying from secure world the u-boot endpoint ID + * - querying from secure world the supported features of the specified FF-A calls + * - mapping the RX/TX buffers + * - querying from secure world all the partitions information + * + * All data queried from secure world is saved in the resident private data structure. + * + * The probe will fail if either FF-A framework is not detected or the + * FF-A requests are not behaving correctly. This ensures that the + * driver is not installed and its operations are not exported to the clients. + * However, once the driver is successfully probed and an FF-A anomaly is + * detected when clients invoke the driver operations, the driver cause + * u-boot to panic because the client would not know what to do in such conditions. + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +static int ffa_probe(struct udevice *dev) +{ + int ret; + size_t buf_4k_pages = 0; + + ret = ffa_set_conduit(); + if (ret != FFA_ERR_STAT_SUCCESS) + return ret; + + ret = ffa_get_version(); + if (ret != FFA_ERR_STAT_SUCCESS) + return ret; + + ret = ffa_get_endpoint_id(); + if (ret != FFA_ERR_STAT_SUCCESS) + return ret; + + ret = ffa_get_rxtx_map_features(); + if (ret != FFA_ERR_STAT_SUCCESS) + return ret; + + ret = ffa_get_rxtx_buffers_pages_cnt(&buf_4k_pages); + if (ret != FFA_ERR_STAT_SUCCESS) + return ret; + + ret = ffa_map_rxtx_buffers(buf_4k_pages); + if (ret != FFA_ERR_STAT_SUCCESS) + return ret; + + ret = ffa_cache_partitions_info(); + if (ret != FFA_ERR_STAT_SUCCESS) { + ffa_free_rxtx_buffers(buf_4k_pages); + return ret; + } + + return FFA_ERR_STAT_SUCCESS; +} + +/** + * ffa_drv_ops - The driver operations runtime structure + * @invoke_func: The driver dispatcher + */ +struct ffa_ops __ffa_runtime_data ffa_drv_ops = { + .invoke_func = invoke_ffa_drv_api +}; + +/** + * ffa_device_get_ops - driver operations getter + * + * Return: + * This runtime function returns a pointer to the driver operations structure + */ +const struct ffa_ops * __ffa_runtime ffa_device_get_ops(void) +{ + return &ffa_drv_ops; +} + +/** + * Declaring the arm_ffa driver under UCLASS_FFA + */ + +U_BOOT_DRIVER(arm_ffa) = { + .name = FFA_DRV_NAME, + .id = UCLASS_FFA, + .probe = ffa_probe, +}; diff --git a/include/arm_ffa.h b/include/arm_ffa.h new file mode 100644 index 0000000000..98db01ee72 --- /dev/null +++ b/include/arm_ffa.h @@ -0,0 +1,190 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __ARM_FFA_H +#define __ARM_FFA_H + +#include <linux/arm-smccc.h> +#include <linux/printk.h> + +/* + * This header is public. It can be used by clients to access + * data structures and definitions they need + */ + +/* + * Macros for displaying logs + */ + +#define ffa_info(fmt, ...) pr_info("[FFA] " fmt "\n", ##__VA_ARGS__) +#define ffa_err(fmt, ...) pr_err("[FFA] " fmt "\n", ##__VA_ARGS__) + +/* + * The driver operations success error code + */ +#define FFA_ERR_STAT_SUCCESS (0) + +#if CONFIG_IS_ENABLED(EFI_LOADER) + +#include <efi_loader.h> + +/* + * __ffa_runtime_data and __ffa_runtime - controls whether data/code are + * available after calling the EFI ExitBootServices service. + * Data/code tagged with these keywords are resident (available at boot time and + * at runtime) + */ + +#define __ffa_runtime_data __efi_runtime_data +#define __ffa_runtime __efi_runtime + +#else + +#define __ffa_runtime_data +#define __ffa_runtime + +#endif + +/* + * 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_VERSION FFA_SMC_32(0x63) +#define FFA_ID_GET FFA_SMC_32(0x69) +#define FFA_FEATURES FFA_SMC_32(0x64) +#define FFA_PARTITION_INFO_GET FFA_SMC_32(0x68) +#define FFA_RXTX_MAP FFA_SMC_32(0x66) +#define FFA_RXTX_UNMAP FFA_SMC_32(0x67) +#define FFA_RX_RELEASE FFA_SMC_32(0x65) +#define FFA_MSG_SEND_DIRECT_REQ FFA_SMC_32(0x6F) +#define FFA_MSG_SEND_DIRECT_RESP FFA_SMC_32(0x70) +#define FFA_RUN FFA_SMC_32(0x6D) +#define FFA_ERROR FFA_SMC_32(0x60) +#define FFA_SUCCESS FFA_SMC_32(0x61) +#define FFA_INTERRUPT FFA_SMC_32(0x62) + +/* + * 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 __packed 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_send_direct_data - Data structure hosting the data + * used by FFA_MSG_SEND_DIRECT_{REQ,RESP} + * @a3-a7: Data read/written from/to w3-w7 registers + * + * Data structure containing the data to be sent by FFA_MSG_SEND_DIRECT_REQ + * or read from FFA_MSG_SEND_DIRECT_RESP + */ +struct __packed ffa_send_direct_data { + u32 a3; /* w3 */ + u32 a4; /* w4 */ + u32 a5; /* w5 */ + u32 a6; /* w6 */ + u32 a7; /* w7 */ +}; + +#define FFA_MSG_SEND_DIRECT_MAX_SIZE (sizeof(struct ffa_send_direct_data)) + +/* UUID data size */ +#define UUID_SIZE (16) + +/* + * union ffa_partition_uuid - Data union hosting the UUID + * transmitted by FFA_PARTITION_INFO_GET + * @words: data structure giving 32-bit words access to the UUID data + * @bytes: data structure giving byte access to the UUID data + * + * The structure holds little-endian UUID data. + */ +union ffa_partition_uuid { + struct __packed words { + u32 a1; /* w1 */ + u32 a2; /* w2 */ + u32 a3; /* w3 */ + u32 a4; /* w4 */ + } words; + u8 bytes[UUID_SIZE]; +}; + +/** + * struct ffa_interface_data - generic FF-A interface data structure used to exchange + * data between user layers and the driver + * @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 + * + * Using this structure user layers can pass various types of data with different sizes. + * The driver internal functions can detect the nature of this data, verfy compliance + * then execute the request when appropriate. + */ +struct ffa_interface_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 */ +}; + +/** + * struct ffa_ops - The driver operations structure + * @invoke_func: function pointer to the invoke function + * + * The data structure providing all the operations supported by the driver. + * This structure is resident. + */ +struct ffa_ops { + /* the driver dispatcher */ + int (*invoke_func)(u32 func_id, struct ffa_interface_data *func_data); +}; + +/** + * The device driver and the Uclass driver public functions + */ + +/** + * ffa_get_invoke_func - performs a call to the FF-A driver dispatcher + */ +int __ffa_runtime ffa_get_invoke_func(u32 func_id, + struct ffa_interface_data *func_data); + +/** + * ffa_device_get_ops - driver operations getter + */ +const struct ffa_ops * __ffa_runtime ffa_device_get_ops(void); + +/** + * ffa_get_device - create, bind and probe the arm_ffa device + */ +int ffa_get_device(void); + +/** + * ffa_bus_discover - discover FF-A bus and probes the arm_ffa device + */ +int ffa_bus_discover(void); +#endif diff --git a/include/arm_ffa_helper.h b/include/arm_ffa_helper.h new file mode 100644 index 0000000000..1cf68748f0 --- /dev/null +++ b/include/arm_ffa_helper.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __ARM_FFA_HELPER_H +#define __ARM_FFA_HELPER_H + +#include <arm_ffa.h> + +/* + * This header is public. Including this header provides all data structures + * and definitions needed by clients to use the FF-A transport driver + * + * It also provides helper functions allowing to pass data and invoke FF-A functions + */ + +/** + * ffa_helper_get_partitions_info - Wrapper function for FFA_PARTITION_INFO_GET + */ +int ffa_helper_get_partitions_info(struct ffa_interface_data *func_data); + +/** + * ffa_helper_unmap_rxtx_buffers - Wrapper function for FFA_RXTX_UNMAP + */ +int ffa_helper_unmap_rxtx_buffers(void); + +/** + * ffa_helper_msg_send_direct_req - Wrapper function for + * FFA_MSG_SEND_DIRECT_{REQ,RESP} + */ +int __ffa_runtime ffa_helper_msg_send_direct_req(struct ffa_interface_data + *func_data); + +/** + * ffa_helper_bus_discover - Wrapper function for FF-A bus discovery + */ +int ffa_helper_bus_discover(void); + +/** + * ffa_uuid_str_to_bin - Converts a big endian UUID string to a little endian buffer + */ +int ffa_uuid_str_to_bin(const char *uuid_str, unsigned char *uuid_bin); +#endif diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 0e26e1d138..a1181b8f48 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -52,6 +52,7 @@ enum uclass_id { UCLASS_EFI_MEDIA, /* Devices provided by UEFI firmware */ UCLASS_ETH, /* Ethernet device */ UCLASS_ETH_PHY, /* Ethernet PHY device */ + UCLASS_FFA, /* Arm Firmware Framework for Armv8-A */ UCLASS_FIRMWARE, /* Firmware */ UCLASS_FS_FIRMWARE_LOADER, /* Generic loader */ UCLASS_GPIO, /* Bank of general-purpose I/O pins */ diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h index 7f2be23394..d58c7c104b 100644 --- a/include/linux/arm-smccc.h +++ b/include/linux/arm-smccc.h @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2015, Linaro Limited + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */ #ifndef __LINUX_ARM_SMCCC_H #define __LINUX_ARM_SMCCC_H @@ -57,13 +59,17 @@ #include <linux/types.h> /** * struct arm_smccc_res - Result from SMC/HVC call - * @a0-a3 result values from registers 0 to 3 + * @a0-a7 result values from registers 0 to 7 */ struct arm_smccc_res { unsigned long a0; unsigned long a1; unsigned long a2; unsigned long a3; + unsigned long a4; + unsigned long a5; + unsigned long a6; + unsigned long a7; };
/** @@ -113,6 +119,26 @@ asmlinkage void __arm_smccc_hvc(unsigned long a0, unsigned long a1, unsigned long a5, unsigned long a6, unsigned long a7, struct arm_smccc_res *res, struct arm_smccc_quirk *quirk);
+#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) +/** + * __arm_ffa_smccc_smc() - make SMC calls used for FF-A transport + * @a0-a7: arguments passed in 64-bit registers x0 to x7 + * @res: result values from 64-bit registers x0 to x7 + * + * This function is used to make SMC calls following SMC32 Calling Convention. + * The content of the supplied parameters is copied to registers x0 to x7 prior + * to the SMC instruction. The SMC call return data is 32-bit data read from + * registers x0 tp x7. + */ +asmlinkage void __arm_ffa_smccc_smc(unsigned long a0, unsigned long a1, + unsigned long a2, unsigned long a3, unsigned long a4, + unsigned long a5, unsigned long a6, unsigned long a7, + struct arm_smccc_res *res); + +#define arm_ffa_smccc_smc __arm_ffa_smccc_smc + +#endif + #define arm_smccc_smc(...) __arm_smccc_smc(__VA_ARGS__, NULL)
#define arm_smccc_smc_quirk(...) __arm_smccc_smc(__VA_ARGS__) diff --git a/lib/Kconfig b/lib/Kconfig index effe735365..9006e8d44c 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -845,6 +845,7 @@ config SMBIOS_PARSER source lib/efi/Kconfig source lib/efi_loader/Kconfig source lib/optee/Kconfig +source lib/arm-ffa/Kconfig
config TEST_FDTDEC bool "enable fdtdec test" diff --git a/lib/Makefile b/lib/Makefile index 13fe5fb7a4..36f67cd94e 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_EFI) += efi/ obj-$(CONFIG_EFI_LOADER) += efi_driver/ obj-$(CONFIG_EFI_LOADER) += efi_loader/ obj-$(CONFIG_CMD_BOOTEFI_SELFTEST) += efi_selftest/ +obj-$(CONFIG_ARM_FFA_TRANSPORT_HELPERS) += arm-ffa/ obj-$(CONFIG_LZMA) += lzma/ obj-$(CONFIG_BZIP2) += bzip2/ obj-$(CONFIG_TIZEN) += tizen/ diff --git a/lib/arm-ffa/Kconfig b/lib/arm-ffa/Kconfig new file mode 100644 index 0000000000..79acbc5a8f --- /dev/null +++ b/lib/arm-ffa/Kconfig @@ -0,0 +1,6 @@ +config ARM_FFA_TRANSPORT_HELPERS + bool "Enable interface helpers for Arm Firmware Framework for Armv8-A" + depends on ARM_FFA_TRANSPORT + help + User layers call FF-A interfaces using helper functions which + pass the data and the FF-A function ID to the low level driver diff --git a/lib/arm-ffa/Makefile b/lib/arm-ffa/Makefile new file mode 100644 index 0000000000..cba625fde4 --- /dev/null +++ b/lib/arm-ffa/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2022 Abdellatif El Khlifi abdellatif.elkhlifi@arm.com +# + +# This file only gets included when CONFIG_ARM_FFA_TRANSPORT_HELPERS is set + +obj-y += arm_ffa_helper.o diff --git a/lib/arm-ffa/arm_ffa_helper.c b/lib/arm-ffa/arm_ffa_helper.c new file mode 100644 index 0000000000..a291b000a7 --- /dev/null +++ b/lib/arm-ffa/arm_ffa_helper.c @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <arm_ffa_helper.h> +#include <uuid.h> + +/** + * ffa_helper_get_partitions_info - Wrapper function for FFA_PARTITION_INFO_GET + * + * @func_data: Pointer to the FF-A function arguments container + * structure. + * The passed arguments: + * Mode 1: When getting from the driver the number of + * secure partitions: + * @data0_size: UUID size + * @data0: pointer to the UUID (little endian) + * @data1_size: size of the number of partitions + * variable + * @data1: pointer to the number of partitions + * variable. The variable will be set + * by the driver + * Mode 2: When requesting the driver to return the + * partitions information: + * @data0_size: UUID size + * @data0: pointer to the UUID (little endian) + * @data1_size: size of the SPs information buffer + * @data1: pointer to SPs information buffer + * (allocated by the client). + * The buffer will be filled by the driver + * + * This is the boot time function used by clients who wants to get from secure + * world the partition(s) information. + * + * 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 client should use + * ffa_helper_get_partitions_info to pass the UUID information to the driver + * which uses PARTITION_INFO_GET to obtain the partition(s) information. + * + * ffa_helper_get_partitions_info should be called twice. First call is to get + * from the driver the number of secure partitions (SPs) associated to a + * particular UUID. Then, the caller (client) allocates the buffer to host the + * SPs data and issues a 2nd call. Then, the driver fills the SPs data in the + * pre-allocated buffer. + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +int ffa_helper_get_partitions_info(struct ffa_interface_data *func_data) +{ + return ffa_get_invoke_func(FFA_PARTITION_INFO_GET, func_data); +} + +/** + * ffa_helper_unmap_rxtx_buffers - Wrapper function for FFA_RXTX_UNMAP + * + * This is the boot time function that allows clients to unmap the RX/TX + * buffers + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +int ffa_helper_unmap_rxtx_buffers(void) +{ + return ffa_get_invoke_func(FFA_RXTX_UNMAP, NULL); +} + +/** + * ffa_helper_msg_send_direct_req - Wrapper function for + * FFA_MSG_SEND_DIRECT_{REQ,RESP} + * @func_data: Pointer to the FF-A function arguments container structure. + * The passed arguments: + * @data0_size: partition ID size + * @data0: pointer to the partition ID + * @data1_size: exchanged data size + * @data1: pointer to the data buffer preallocated by the client (in/out) + * + * This is the runtime function that allows clients to send data to the secure + * world partitions. The arm_ffa driver uses FFA_MSG_SEND_DIRECT_REQ to send the + * data to the secure partition. The response from the secure partition is + * handled internally by the driver using FFA_MSG_SEND_DIRECT_RESP and returned + * to ffa_helper_msg_send_direct_req through @func_data + * + * The maximum size of the data that can be exchanged is 20 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} + * + * The client should pre-allocate a buffer pointed by @data1 which the size + * is sizeof(struct ffa_send_direct_data) + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +int __ffa_runtime ffa_helper_msg_send_direct_req(struct ffa_interface_data + *func_data) +{ + return ffa_get_invoke_func(FFA_MSG_SEND_DIRECT_REQ, func_data); +} + +/** + * ffa_helper_bus_discover - Wrapper function for FF-A bus discovery + * + * This boot time function should be called to discover the FF-A bus and + * probe the FF-A device. + * To achieve that, this function is called automatically at initcalls + * level (after u-boot relocation). + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +int ffa_helper_bus_discover(void) +{ + return ffa_bus_discover(); +} + +/** + * ffa_uuid_str_to_bin - Converts a big endian UUID string to a little endian buffer + * @uuid_str: UUID string in big endian format (36 bytes wide + '/0') + * @uuid_bin: preallocated 16 bytes UUID buffer in little endian format + * + * UUID binary format used by the FF-A framework (16 bytes): + * + * [LSB] 4B-2B-2B-2B-6B (little endian data fields) + * + * UUID string is 36 length of characters (36 bytes): + * + * xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + * be be be be be + * + * where x is a hexadecimal character. Fields are separated by '-'s. + * When converting to a binary UUID, these endianness rules apply: + * be: means the field in the string is considered a big endian hex number + * and should be converted to little endian binary format + * + * Return: + * + * uuid_bin filled with little endian UUID data + * On success 0 is returned. Otherwise, failure code. + */ +int ffa_uuid_str_to_bin(const char *uuid_str, unsigned char *uuid_bin) +{ + u16 tmp16 = 0; + u32 tmp32 = 0; + u64 tmp64 = 0; + + if (!uuid_str_valid(uuid_str) || !uuid_bin) + return -EINVAL; + + /* + * reverse bytes from big to little endian + */ + tmp32 = simple_strtoul(uuid_str, NULL, 16); + memcpy(uuid_bin, &tmp32, 4); + + /* + * reverse bytes from big to little endian + */ + tmp16 = simple_strtoul(uuid_str + 9, NULL, 16); + memcpy(uuid_bin + 4, &tmp16, 2); + + /* + * reverse bytes from big to little endian + */ + tmp16 = simple_strtoul(uuid_str + 14, NULL, 16); + memcpy(uuid_bin + 6, &tmp16, 2); + + /* + * reverse bytes from big to little endian + */ + tmp16 = simple_strtoul(uuid_str + 19, NULL, 16); + memcpy(uuid_bin + 8, &tmp16, 2); + + /* + * reverse bytes from big to little endian + */ + tmp64 = simple_strtoull(uuid_str + 24, NULL, 16); + memcpy(uuid_bin + 10, (char *)&tmp64, 6); + + return 0; +} diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 5bcb8253ed..cffa2c69d6 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -23,6 +23,10 @@ #include <asm/setjmp.h> #include <linux/libfdt_env.h>
+#if defined(CONFIG_ARM_FFA_TRANSPORT) +#include <arm_ffa_helper.h> +#endif + DECLARE_GLOBAL_DATA_PTR;
/* Task priority level */ @@ -2114,6 +2118,10 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, struct efi_event *evt, *next_event; efi_status_t ret = EFI_SUCCESS;
+#if defined(CONFIG_ARM_FFA_TRANSPORT) + int ffa_ret; +#endif + EFI_ENTRY("%p, %zx", image_handle, map_key);
/* Check that the caller has read the current memory map */ @@ -2174,6 +2182,15 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL); }
+#if defined(CONFIG_ARM_FFA_TRANSPORT) + /* unmap FF-A RX/TX buffers */ + ffa_ret = ffa_helper_unmap_rxtx_buffers(); + if (ffa_ret) + debug("[efi_boottime][ERROR]: can not unmap FF-A RX/TX buffers\n"); + else + debug("[efi_boottime][INFO]: FF-A RX/TX buffers unmapped\n"); +#endif + /* Patch out unsupported runtime function */ efi_runtime_detach();

Hi Abdellatif On Fri, Apr 15, 2022 at 01:27:58PM +0100, abdellatif.elkhlifi@arm.com wrote:
From: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
Add the driver implementing Arm Firmware Framework for Armv8-A v1.0
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. This driver uses SMC32 calling convention.
In u-boot FF-A design, FF-A is considered as a discoverable bus. 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 provides helper FF-A interfaces for user layers. These helper functions allow clients to pass data and select the FF-A function to use for the communication with secure world.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com +++ b/arch/arm/cpu/armv8/smccc-call.S @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0 */ /*
- Copyright (c) 2015, Linaro Limited
- (C) Copyright 2022 ARM Limited
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#include <linux/linkage.h> #include <linux/arm-smccc.h> @@ -45,3 +47,28 @@ ENDPROC(__arm_smccc_smc) ENTRY(__arm_smccc_hvc) SMCCC hvc ENDPROC(__arm_smccc_hvc)
+#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
- .macro FFASMCCC instr
- .cfi_startproc
- \instr #0
- ldr x9, [sp]
- stp x0, x1, [x9, #ARM_SMCCC_RES_X0_OFFS]
- stp x2, x3, [x9, #ARM_SMCCC_RES_X2_OFFS]
- stp x4, x5, [x9, #ARM_SMCCC_RES_X4_OFFS]
- stp x6, x7, [x9, #ARM_SMCCC_RES_X6_OFFS]
- ret
- .cfi_endproc
- .endm
+/*
- void arm_ffa_smccc_smc(unsigned long a0, unsigned long a1, unsigned long a2,
unsigned long a3, unsigned long a4, unsigned long a5,
unsigned long a6, unsigned long a7, struct arm_smccc_res *res)
- */
+ENTRY(__arm_ffa_smccc_smc)
- FFASMCCC smc
+ENDPROC(__arm_ffa_smccc_smc)
+#endif diff --git a/arch/arm/lib/asm-offsets.c b/arch/arm/lib/asm-offsets.c index 22fd541f9a..02a4a42fe6 100644 --- a/arch/arm/lib/asm-offsets.c +++ b/arch/arm/lib/asm-offsets.c @@ -9,6 +9,8 @@
- generate asm statements containing #defines,
- compile this file to assembler, and then extract the
- #defines from the assembly-language output.
*/
- (C) Copyright 2022 ARM Limited
#include <common.h> @@ -115,6 +117,10 @@ int main(void) #ifdef CONFIG_ARM_SMCCC DEFINE(ARM_SMCCC_RES_X0_OFFS, offsetof(struct arm_smccc_res, a0)); DEFINE(ARM_SMCCC_RES_X2_OFFS, offsetof(struct arm_smccc_res, a2)); +#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
- DEFINE(ARM_SMCCC_RES_X4_OFFS, offsetof(struct arm_smccc_res, a4));
- DEFINE(ARM_SMCCC_RES_X6_OFFS, offsetof(struct arm_smccc_res, a6));
+#endif DEFINE(ARM_SMCCC_QUIRK_ID_OFFS, offsetof(struct arm_smccc_quirk, id)); DEFINE(ARM_SMCCC_QUIRK_STATE_OFFS, offsetof(struct arm_smccc_quirk, state)); #endif diff --git a/common/board_r.c b/common/board_r.c index b92c1bb0be..bb5f1d0aa6 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -62,6 +62,10 @@ #include <asm-generic/gpio.h> #include <efi_loader.h>
+#ifdef CONFIG_ARM_FFA_TRANSPORT +#include <arm_ffa_helper.h> +#endif
DECLARE_GLOBAL_DATA_PTR;
ulong monitor_flash_len; @@ -771,6 +775,9 @@ static init_fnc_t init_sequence_r[] = { INIT_FUNC_WATCHDOG_RESET initr_net, #endif +#ifdef CONFIG_ARM_FFA_TRANSPORT
- ffa_helper_bus_discover,
+#endif #ifdef CONFIG_POST initr_post, #endif diff --git a/drivers/Kconfig b/drivers/Kconfig index b26ca8cf70..e83c23789d 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -6,6 +6,8 @@ source "drivers/core/Kconfig"
source "drivers/adc/Kconfig"
+source "drivers/arm-ffa/Kconfig"
source "drivers/ata/Kconfig"
source "drivers/axi/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 4e7cf28440..6671d2a604 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -107,6 +107,7 @@ obj-y += iommu/ obj-y += smem/ obj-y += thermal/ obj-$(CONFIG_TEE) += tee/ +obj-$(CONFIG_ARM_FFA_TRANSPORT) += arm-ffa/ obj-y += axi/ obj-y += ufs/ obj-$(CONFIG_W1) += w1/ diff --git a/drivers/arm-ffa/Kconfig b/drivers/arm-ffa/Kconfig new file mode 100644 index 0000000000..23815534c4 --- /dev/null +++ b/drivers/arm-ffa/Kconfig @@ -0,0 +1,27 @@ +# 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 if ARM64
- select LIB_UUID
- select ARM_FFA_TRANSPORT_HELPERS
- 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.
This driver 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.
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.
diff --git a/drivers/arm-ffa/Makefile b/drivers/arm-ffa/Makefile new file mode 100644 index 0000000000..7bc9a336a9 --- /dev/null +++ b/drivers/arm-ffa/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2022 Abdellatif El Khlifi abdellatif.elkhlifi@arm.com +#
+obj-y += arm-ffa-uclass.o core.o diff --git a/drivers/arm-ffa/arm-ffa-uclass.c b/drivers/arm-ffa/arm-ffa-uclass.c new file mode 100644 index 0000000000..2439f87586 --- /dev/null +++ b/drivers/arm-ffa/arm-ffa-uclass.c @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include <common.h> +#include <dm.h> +#include <arm_ffa.h> +#include <errno.h> +#include <log.h> +#include <asm/global_data.h>
+DECLARE_GLOBAL_DATA_PTR;
+UCLASS_DRIVER(ffa) = {
- .name = "ffa",
- .id = UCLASS_FFA,
+};
+/**
- ffa_get_invoke_func - performs a call to the FF-A driver dispatcher
- @func_id: The FF-A function to be used
- @func_data: Pointer to the FF-A function arguments
container structure. This also includes
pointers to the returned data needed by
clients.
- This runtime function passes the FF-A function ID and its arguments to
- the FF-A driver dispatcher.
- This function is called by the FF-A helper functions.
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+int __ffa_runtime ffa_get_invoke_func(u32 func_id, struct ffa_interface_data *func_data) +{
- if (!ffa_device_get_ops()->invoke_func)
return -EINVAL;
- return ffa_device_get_ops()->invoke_func(func_id, func_data);
+}
+/**
- ffa_bus_discover - discover FF-A bus and probe the arm_ffa device
- This boot time function makes sure the FF-A bus is discoverable.
- Then, the arm_ffa device is probed and ready to use.
- This function is called automatically at initcalls
- level (after u-boot relocation).
- Arm FF-A transport is implemented through arm_ffa u-boot device managing the FF-A
- communication.
- All FF-A clients should use the arm_ffa device to use the FF-A transport.
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+int ffa_bus_discover(void) +{
- return ffa_get_device();
+} diff --git a/drivers/arm-ffa/arm_ffa_prv.h b/drivers/arm-ffa/arm_ffa_prv.h new file mode 100644 index 0000000000..44f258addb --- /dev/null +++ b/drivers/arm-ffa/arm_ffa_prv.h @@ -0,0 +1,193 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#ifndef __ARM_FFA_PRV_H +#define __ARM_FFA_PRV_H
+#include <arm_ffa.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <dm/read.h>
+/*
- This header is private. It is exclusively used by the FF-A driver
- */
+/* FF-A core driver name */ +#define FFA_DRV_NAME "arm_ffa"
+/* 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)))
+/* The FF-A SMC function prototype definition */
+typedef void (*invoke_ffa_fn_t)(unsigned long a0, unsigned long a1,
unsigned long a2, unsigned long a3, unsigned long a4,
unsigned long a5, unsigned long a6, unsigned long a7,
struct arm_smccc_res *res);
+/**
- enum ffa_conduit - Arm FF-A conduits supported by the Arm FF-A driver
- Currently only SMC32 is supported.
- */
+enum ffa_conduit {
- FFA_CONDUIT_SMC = 0,
+};
+/**
- FFA_DECLARE_ARGS - FF-A functions local variables
- @a0-a7: local variables used to set registers x0-x7
- @res: the structure hosting the FF-A function return data
- A helper macro for declaring local variables for the FF-A functions arguments.
- The x0-x7 registers are used to exchange data with the secure world.
- But, only the bottom 32-bit of thes registers contains the data.
- */
+#define FFA_DECLARE_ARGS \
- unsigned long a0 = 0; \
- unsigned long a1 = 0; \
- unsigned long a2 = 0; \
- unsigned long a3 = 0; \
- unsigned long a4 = 0; \
- unsigned long a5 = 0; \
- unsigned long a6 = 0; \
- unsigned long a7 = 0; \
- struct arm_smccc_res res = {0}
+/* FF-A error codes */ +#define FFA_ERR_STAT_NOT_SUPPORTED (-1) +#define FFA_ERR_STAT_INVALID_PARAMETERS (-2) +#define FFA_ERR_STAT_NO_MEMORY (-3) +#define FFA_ERR_STAT_BUSY (-4) +#define FFA_ERR_STAT_INTERRUPTED (-5) +#define FFA_ERR_STAT_DENIED (-6) +#define FFA_ERR_STAT_RETRY (-7) +#define FFA_ERR_STAT_ABORTED (-8)
+/**
- struct ffa_features_desc - FF-A functions features
- @func_id: FF-A function
- @field1: features read from register w2
- @field2: features read from register w3
- Data structure describing the features of the FF-A functions queried by
- FFA_FEATURES
- */
+struct ffa_features_desc {
- u32 func_id;
- u32 field1;
- u32 field2;
+};
+/**
- enum ffa_rxtx_buf_sizes - minimum sizes supported
- for the RX/TX buffers
- */
+enum ffa_rxtx_buf_sizes {
- RXTX_4K,
- RXTX_64K,
- RXTX_16K
+};
+/*
- Number of the FF-A interfaces features descriptors
- currently only FFA_RXTX_MAP descriptor is supported
- */
+#define FFA_FEATURE_DESC_CNT (1)
+/**
- struct ffa_rxtxpair - structure hosting the RX/TX buffers virtual addresses
- @rxbuf: virtual address of the RX buffer
- @txbuf: virtual address of the TX buffer
- Data structure hosting 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 {
- u64 rxbuf; /* virtual address */
- u64 txbuf; /* virtual address */
+};
+/**
- struct ffa_partition_desc - the secure partition descriptor
- @info: partition information
- @UUID: UUID
- Each partition has its descriptor containing the partitions information and the UUID
- */
+struct ffa_partition_desc {
- struct ffa_partition_info info;
- union ffa_partition_uuid UUID;
+};
+/**
- struct ffa_partitions - descriptors for all secure partitions
- @count: The number of partitions descriptors
- @descs The partitions descriptors table
- This data structure contains the partitions descriptors table
- */
+struct ffa_partitions {
- u32 count;
- struct ffa_partition_desc *descs; /* virtual address */
+};
+/**
- struct ffa_prvdata - the driver private data structure
- @dev: The arm_ffa device under u-boot driver model
- @fwk_version: FF-A framework version
- @id: u-boot endpoint ID
- @partitions: The partitions descriptors structure
- @pair: The RX/TX buffers pair
- @conduit: The selected conduit
- @invoke_ffa_fn: The function executing the FF-A function
- @features: Table of the FF-A functions having features
- The driver data structure hosting all resident data.
- */
+struct ffa_prvdata {
- struct udevice *dev;
- u32 fwk_version;
- u16 id;
- struct ffa_partitions partitions;
- struct ffa_rxtxpair pair;
- enum ffa_conduit conduit;
- invoke_ffa_fn_t invoke_ffa_fn;
- struct ffa_features_desc features[FFA_FEATURE_DESC_CNT];
+};
+#endif diff --git a/drivers/arm-ffa/core.c b/drivers/arm-ffa/core.c new file mode 100644 index 0000000000..09e4eb753a --- /dev/null +++ b/drivers/arm-ffa/core.c @@ -0,0 +1,1349 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include "arm_ffa_prv.h" +#include <asm/global_data.h> +#include <asm/io.h> +#include <common.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> +#include <log.h> +#include <malloc.h> +#include <mapmem.h> +#include <string.h>
+DECLARE_GLOBAL_DATA_PTR;
+/**
- The device private data structure containing all the resident
- data read from secure world
- */
+struct ffa_prvdata __ffa_runtime_data ffa_priv_data = {0};
+/*
- Driver functions
- */
+/**
- ffa_get_device - create, bind and probe the arm_ffa device
- This boot time function makes sure the arm_ffa device is
- created, bound to this driver, probed and ready to use.
- Arm FF-A transport is implemented through a single u-boot
- device managing the FF-A bus (arm_ffa).
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+int ffa_get_device(void) +{
- int ret;
- if (ffa_priv_data.dev)
return FFA_ERR_STAT_SUCCESS;
- ret = device_bind(dm_root(),
DM_DRIVER_GET(arm_ffa),
FFA_DRV_NAME,
NULL,
ofnode_null(),
&ffa_priv_data.dev);
- if (ret) {
ffa_priv_data.dev = NULL;
return ret;
- }
- /* The FF-A bus discovery succeeds when probing is successful */
- ret = device_probe(ffa_priv_data.dev);
- if (ret) {
ffa_err("can not probe the device");
device_unbind(ffa_priv_data.dev);
ffa_priv_data.dev = NULL;
return ret;
- }
- return FFA_ERR_STAT_SUCCESS;
+}
+/**
- ffa_get_version - FFA_VERSION handler function
- This is the boot time function that implements FFA_VERSION FF-A function
- to get from the secure world the FF-A framework version
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+static int ffa_get_version(void) +{
- u16 major, minor;
- FFA_DECLARE_ARGS;
- if (!ffa_priv_data.invoke_ffa_fn)
panic("[FFA] no private data found\n");
- a0 = FFA_VERSION;
- a1 = FFA_VERSION_1_0;
- ffa_priv_data.invoke_ffa_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res);
- if (res.a0 == FFA_ERR_STAT_NOT_SUPPORTED) {
ffa_err("A Firmware Framework implementation does not exist");
return -EOPNOTSUPP;
- }
- major = GET_FFA_MAJOR_VERSION(res.a0);
- minor = GET_FFA_MINOR_VERSION(res.a0);
- ffa_info("FF-A driver %d.%d\nFF-A framework %d.%d",
FFA_MAJOR_VERSION, FFA_MINOR_VERSION, major, minor);
- if ((major == FFA_MAJOR_VERSION && minor >= FFA_MINOR_VERSION)) {
ffa_info("Versions are compatible ");
ffa_priv_data.fwk_version = res.a0;
return FFA_ERR_STAT_SUCCESS;
- }
- ffa_info("Versions are incompatible ");
- return -EPROTONOSUPPORT;
+}
+/**
- ffa_get_endpoint_id - FFA_ID_GET handler function
- This is the boot time function that implements FFA_ID_GET FF-A function
- to get from the secure world u-boot endpoint ID
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+static int ffa_get_endpoint_id(void) +{
- FFA_DECLARE_ARGS;
- if (!ffa_priv_data.invoke_ffa_fn)
panic("[FFA] no private data found\n");
- a0 = FFA_ID_GET;
- ffa_priv_data.invoke_ffa_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res);
- switch (res.a0) {
- case FFA_ERROR:
- {
if (((int)res.a2) == FFA_ERR_STAT_NOT_SUPPORTED) {
ffa_err("This function is not implemented at this FF-A instance");
return -EOPNOTSUPP;
}
ffa_err("Undefined error code (%d)", ((int)res.a2));
return -EINVAL;
- }
- case FFA_SUCCESS:
- {
ffa_priv_data.id = GET_SELF_ENDPOINT_ID(res.a2);
ffa_info("endpoint ID is %u", ffa_priv_data.id);
return FFA_ERR_STAT_SUCCESS;
- }
- default:
- {
ffa_err("Undefined response function (0x%lx)", res.a0);
return -EINVAL;
- }
- }
+}
+/**
- ffa_get_features_desc - returns the features descriptor of the specified
FF-A function
- @func_id: the FF-A function which the features are to be retrieved
- This is a boot time function that searches the features descriptor of the
- specified FF-A function
- Return:
- When found, the address of the features descriptor is returned. Otherwise, NULL.
- */
+static struct ffa_features_desc *ffa_get_features_desc(u32 func_id) +{
- u32 desc_idx;
- /*
* search for the descriptor of the selected FF-A interface
*/
- for (desc_idx = 0; desc_idx < FFA_FEATURE_DESC_CNT ; desc_idx++)
if (ffa_priv_data.features[desc_idx].func_id == func_id)
return &ffa_priv_data.features[desc_idx];
- return NULL;
+}
+/**
- ffa_get_rxtx_map_features - FFA_FEATURES handler function with FFA_RXTX_MAP
argument
- This is the boot time function that implements FFA_FEATURES FF-A function
- to retrieve the FFA_RXTX_MAP features
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+static int ffa_get_rxtx_map_features(void) +{
- FFA_DECLARE_ARGS;
- if (!ffa_priv_data.invoke_ffa_fn)
panic("[FFA] no private data found\n");
- a0 = FFA_FEATURES;
- a1 = FFA_RXTX_MAP;
- ffa_priv_data.invoke_ffa_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res);
- switch (res.a0) {
- case FFA_ERROR:
- {
if (((int)res.a2) == FFA_ERR_STAT_NOT_SUPPORTED) {
ffa_err("FFA_RXTX_MAP is not implemented at this FF-A instance");
return -EOPNOTSUPP;
}
ffa_err("Undefined error code (%d)", ((int)res.a2));
return -EINVAL;
This happens quite a few times throughout the code. Why is -EINVAL used here instead of -FFA_ERR_STAT_INVALID_PARAMETERS for example?
We should either use FFA_* errors consistently of get rid of those entirely and stick to standard ones, but let's not mix and match
- }
- case FFA_SUCCESS:
- {
u32 desc_idx;
/*
* search for an empty descriptor
*/
for (desc_idx = 0; desc_idx < FFA_FEATURE_DESC_CNT ; desc_idx++)
if (!ffa_priv_data.features[desc_idx].func_id) {
/*
* populate the descriptor with
* the interface features data
*/
ffa_priv_data.features[desc_idx].func_id =
FFA_RXTX_MAP;
ffa_priv_data.features[desc_idx].field1 =
res.a2;
ffa_info("FFA_RXTX_MAP features data 0x%lx",
res.a2);
return FFA_ERR_STAT_SUCCESS;
}
ffa_err("Cannot save FFA_RXTX_MAP features data. Descriptors table full");
return -ENOBUFS;
Similarly -FFA_ERR_STAT_NO_MEMORY or something?
- }
- default:
- {
ffa_err("Undefined response function (0x%lx)",
res.a0);
return -EINVAL;
- }
- }
+}
+/**
- ffa_get_rxtx_buffers_pages_cnt - reads from the features data descriptors
the minimum number of pages in each of the RX/TX
buffers
- @buf_4k_pages: Pointer to the minimum number of pages
- This is the boot time function that returns the minimum number of pages
- in each of the RX/TX buffers
- Return:
- buf_4k_pages points to the returned number of pages
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+static int ffa_get_rxtx_buffers_pages_cnt(size_t *buf_4k_pages) +{
- struct ffa_features_desc *desc = NULL;
- if (!buf_4k_pages)
return -EINVAL;
- desc = ffa_get_features_desc(FFA_RXTX_MAP);
- if (!desc)
return -EINVAL;
- switch (desc->field1) {
- case RXTX_4K:
*buf_4k_pages = 1;
break;
- case RXTX_16K:
*buf_4k_pages = 4;
break;
- case RXTX_64K:
*buf_4k_pages = 16;
break;
- default:
ffa_err("RX/TX buffer size not supported");
return -EINVAL;
- }
- return FFA_ERR_STAT_SUCCESS;
+}
+/**
- ffa_free_rxtx_buffers - frees the RX/TX buffers
- @buf_4k_pages: the minimum number of pages in each of the RX/TX
buffers
- This is the boot time function used to free the RX/TX buffers
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+static int ffa_free_rxtx_buffers(size_t buf_4k_pages) +{
- efi_status_t free_rxbuf_ret, free_txbuf_ret;
- ffa_info("Freeing RX/TX buffers");
- free_rxbuf_ret = efi_free_pages(ffa_priv_data.pair.rxbuf, buf_4k_pages);
- free_txbuf_ret = efi_free_pages(ffa_priv_data.pair.txbuf, buf_4k_pages);
- if (free_rxbuf_ret != EFI_SUCCESS || free_txbuf_ret != EFI_SUCCESS) {
ffa_err("Failed to free RX/TX buffers (rx: %lu , tx: %lu)",
free_rxbuf_ret,
free_txbuf_ret);
return -EINVAL;
- }
- ffa_priv_data.pair.rxbuf = 0;
- ffa_priv_data.pair.txbuf = 0;
Should those be set to 0 regardless of the efi_free_pages() outcome? If not then you probably need to handle those one by one. As is it right now one failure to free a buffer means both of those won't be set
- return FFA_ERR_STAT_SUCCESS;
+}
+/**
- ffa_alloc_rxtx_buffers - allocates the RX/TX buffers
- @buf_4k_pages: the minimum number of pages in each of the RX/TX
buffers
- This is the boot time function used by ffa_map_rxtx_buffers to allocate
- the RX/TX buffers before mapping them
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+static int ffa_alloc_rxtx_buffers(size_t buf_4k_pages) +{ +#if CONFIG_IS_ENABLED(EFI_LOADER)
- efi_status_t efi_ret;
- void *virt_txbuf;
- void *virt_rxbuf;
- ffa_info("Using %lu 4KB page(s) for RX/TX buffers size",
buf_4k_pages);
- efi_ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
EFI_BOOT_SERVICES_DATA,
buf_4k_pages,
&ffa_priv_data.pair.rxbuf);
Why are we specifically limiting this to EFI? I understand using efi allocated memory for potential runtime operations, were we need the memory preserved, but why here?
There's also a side effect with this allocation. The EFI subsystem doesn't usually come up until u-boot is fully up and running. This call will force it to start way earlier
- if (efi_ret != EFI_SUCCESS) {
ffa_priv_data.pair.rxbuf = 0;
ffa_err("Failure to allocate RX buffer (EFI error: 0x%lx)",
efi_ret);
return -ENOBUFS;
- }
- ffa_info("RX buffer at virtual address 0x%llx",
ffa_priv_data.pair.rxbuf);
- virt_rxbuf = (void *)ffa_priv_data.pair.rxbuf;
- /*
* make sure the buffer is clean before use
*/
- memset(virt_rxbuf, 0, buf_4k_pages * SZ_4K);
- efi_ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
EFI_RUNTIME_SERVICES_DATA,
buf_4k_pages,
&ffa_priv_data.pair.txbuf);
- if (efi_ret != EFI_SUCCESS) {
efi_free_pages(ffa_priv_data.pair.rxbuf, buf_4k_pages);
ffa_priv_data.pair.rxbuf = 0;
ffa_priv_data.pair.txbuf = 0;
ffa_err("Failure to allocate the TX buffer (EFI error: 0x%lx)"
, efi_ret);
return -ENOBUFS;
- }
- ffa_info("TX buffer at virtual address 0x%llx",
ffa_priv_data.pair.txbuf);
- virt_txbuf = (void *)ffa_priv_data.pair.txbuf;
- /*
* make sure the buffer is clean before use
*/
- memset(virt_txbuf, 0, buf_4k_pages * SZ_4K);
- return FFA_ERR_STAT_SUCCESS;
+#else
- return -ENOBUFS;
+#endif +}
+/**
- ffa_map_rxtx_buffers - FFA_RXTX_MAP handler function
- @buf_4k_pages: the minimum number of pages in each of the RX/TX
buffers
- This is the boot time function that implements FFA_RXTX_MAP FF-A function
- to map the RX/TX buffers
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+static int ffa_map_rxtx_buffers(size_t buf_4k_pages) +{
- int ret;
- FFA_DECLARE_ARGS;
- if (!ffa_priv_data.invoke_ffa_fn)
panic("[FFA] no private data found\n");
- ret = ffa_alloc_rxtx_buffers(buf_4k_pages);
- if (ret != FFA_ERR_STAT_SUCCESS)
return ret;
- a0 = FFA_RXTX_MAP;
- a1 = ffa_priv_data.pair.txbuf;
- a2 = ffa_priv_data.pair.rxbuf;
- a3 = buf_4k_pages;
- ffa_priv_data.invoke_ffa_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res);
- switch (res.a0) {
- case FFA_ERROR:
- {
switch (((int)res.a2)) {
case FFA_ERR_STAT_INVALID_PARAMETERS:
ffa_err("One or more fields in input parameters is incorrectly encoded");
ret = -EPERM;
break;
case FFA_ERR_STAT_NO_MEMORY:
ffa_err("Not enough memory");
ret = -ENOMEM;
break;
case FFA_ERR_STAT_DENIED:
ffa_err("Buffer pair already registered");
ret = -EACCES;
break;
case FFA_ERR_STAT_NOT_SUPPORTED:
ffa_err("This function is not implemented at this FF-A instance");
ret = -EOPNOTSUPP;
break;
default:
ffa_err("Undefined error (%d)",
((int)res.a2));
ret = -EINVAL;
}
Can we have an array with string literals for the errors and do a lookup instead of nested case switches?
break;
- }
- case FFA_SUCCESS:
ffa_info("RX/TX buffers mapped");
return FFA_ERR_STAT_SUCCESS;
- default:
ffa_err("Undefined response function (0x%lx)",
res.a0);
ret = -EINVAL;
- }
- ffa_free_rxtx_buffers(buf_4k_pages);
- return ret;
+}
+/**
- ffa_unmap_rxtx_buffers - FFA_RXTX_UNMAP handler function
- This is the boot time function that implements FFA_RXTX_UNMAP FF-A function
- to unmap the RX/TX buffers
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+static int ffa_unmap_rxtx_buffers(void) +{
- FFA_DECLARE_ARGS;
- if (!ffa_priv_data.invoke_ffa_fn)
panic("[FFA] no private data found\n");
- a0 = FFA_RXTX_UNMAP;
- a1 = PREP_SELF_ENDPOINT_ID(ffa_priv_data.id);
- ffa_priv_data.invoke_ffa_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res);
- switch (res.a0) {
- case FFA_ERROR:
- {
if (((int)res.a2) == FFA_ERR_STAT_NOT_SUPPORTED)
panic("[FFA] FFA_RXTX_UNMAP is not implemented at this FF-A instance\n");
else if (((int)res.a2) == FFA_ERR_STAT_INVALID_PARAMETERS)
panic("[FFA] There is no buffer pair registered on behalf of the caller\n");
else
panic("[FFA] Undefined error (%d)\n", ((int)res.a2));
There's panics sprinkled around the code. Are all those cases really fatal?
- }
- case FFA_SUCCESS:
- {
size_t buf_4k_pages = 0;
int ret;
ret = ffa_get_rxtx_buffers_pages_cnt(&buf_4k_pages);
if (ret != FFA_ERR_STAT_SUCCESS)
panic("[FFA] RX/TX buffers unmapped but failure in getting pages count\n");
ret = ffa_free_rxtx_buffers(buf_4k_pages);
if (ret != FFA_ERR_STAT_SUCCESS)
panic("[FFA] RX/TX buffers unmapped but failure in freeing the memory\n");
ffa_info("RX/TX buffers unmapped and memory freed");
return FFA_ERR_STAT_SUCCESS;
- }
- default:
panic("[FFA] Undefined response function (0x%lx)", res.a0);
- }
+}
+/**
- ffa_release_rx_buffer - FFA_RX_RELEASE handler function
- This is the boot time function that invokes FFA_RX_RELEASE FF-A function
- to release the ownership of the RX buffer
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+static int ffa_release_rx_buffer(void) +{
- FFA_DECLARE_ARGS;
- if (!ffa_priv_data.invoke_ffa_fn)
panic("[FFA] no private data found\n");
- a0 = FFA_RX_RELEASE;
- ffa_priv_data.invoke_ffa_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res);
- switch (res.a0) {
- case FFA_ERROR:
- {
if (((int)res.a2) == FFA_ERR_STAT_NOT_SUPPORTED)
panic("[FFA] FFA_RX_RELEASE is not implemented at this FF-A instance\n");
else if (((int)res.a2) == FFA_ERR_STAT_DENIED)
panic("[FFA] Caller did not have ownership of the RX buffer\n");
else
panic("[FFA] Undefined error (%d)\n", ((int)res.a2));
- }
- case FFA_SUCCESS:
return FFA_ERR_STAT_SUCCESS;
- default:
panic("[FFA] Undefined response function (0x%lx)\n", res.a0);
- }
+}
+/**
- ffa_uuid_are_identical - checks whether two given UUIDs are identical
- @uuid1: first UUID
- @uuid2: second UUID
- This is a boot time function used by ffa_read_partitions_info to search
- for a UUID in the partitions descriptors table
- Return:
- 1 when UUIDs match. Otherwise, 0
- */
+int ffa_uuid_are_identical(const union ffa_partition_uuid *uuid1,
const union ffa_partition_uuid *uuid2)
+{
- if (!uuid1 || !uuid2)
return 0;
- return (!memcmp(uuid1, uuid2, sizeof(union ffa_partition_uuid)));
+}
+/**
- ffa_read_partitions_info - reads the data queried by FFA_PARTITION_INFO_GET
and saves it in the private structure
- @count: The number of partitions queried
- @part_uuid: Pointer to the partition(s) UUID
- This is the boot time function that reads the partitions information
- returned by the FFA_PARTITION_INFO_GET and saves it in the private
- data structure.
- Return:
- The private data structure is updated with the partition(s) information
- FFA_ERR_STAT_SUCCESS is returned on success. Otherwise, failure
- */
+static int ffa_read_partitions_info(u32 count, union ffa_partition_uuid *part_uuid) +{
- if (!count) {
ffa_err("No partition detected");
return -ENODATA;
- }
- ffa_info("Reading partitions data from the RX buffer");
+#if CONFIG_IS_ENABLED(EFI_LOADER)
- if (!part_uuid) {
/*
* querying information of all partitions
*/
u64 data_pages;
u64 data_bytes;
efi_status_t efi_ret;
size_t buf_4k_pages = 0;
u32 desc_idx;
struct ffa_partition_info *parts_info;
int ret;
data_bytes = count * sizeof(struct ffa_partition_desc);
data_pages = efi_size_in_pages(data_bytes);
/*
* get the RX buffer size in pages
*/
ret = ffa_get_rxtx_buffers_pages_cnt(&buf_4k_pages);
if (ret != FFA_ERR_STAT_SUCCESS) {
ffa_err("Can not get the RX buffer size (error %d)", ret);
return ret;
}
if (data_pages > buf_4k_pages) {
ffa_err("Partitions data size exceeds the RX buffer size:");
ffa_err(" Sizes in pages: data %llu , RX buffer %lu ",
data_pages,
buf_4k_pages);
return -ENOMEM;
}
efi_ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
EFI_RUNTIME_SERVICES_DATA,
data_pages,
(u64 *)&ffa_priv_data.partitions.descs);
Same questions here. Why are we using the EFI APIs to allocate buffers?
if (efi_ret != EFI_SUCCESS) {
ffa_priv_data.partitions.descs = NULL;
ffa_err("Cannot allocate partitions data buffer (EFI error 0x%lx)",
efi_ret);
return -ENOBUFS;
}
/*
* make sure the buffer is clean before use
*/
memset(ffa_priv_data.partitions.descs, 0,
data_pages * SZ_4K);
parts_info = (struct ffa_partition_info *)ffa_priv_data.pair.rxbuf;
for (desc_idx = 0 ; desc_idx < count ; desc_idx++) {
ffa_priv_data.partitions.descs[desc_idx].info =
parts_info[desc_idx];
ffa_info("Partition ID %x : info cached",
ffa_priv_data.partitions.descs[desc_idx].info.id);
}
ffa_priv_data.partitions.count = count;
ffa_info("%d partition(s) found and cached", count);
- } else {
u32 rx_desc_idx, cached_desc_idx;
struct ffa_partition_info *parts_info;
u8 desc_found;
parts_info = (struct ffa_partition_info *)ffa_priv_data.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 < ffa_priv_data.partitions.count;
cached_desc_idx++) {
/*
* save the UUID
*/
if (ffa_priv_data.partitions.descs[cached_desc_idx].info.id ==
parts_info[rx_desc_idx].id) {
ffa_priv_data.partitions.descs[cached_desc_idx].UUID =
*part_uuid;
desc_found = 1;
break;
}
}
if (!desc_found)
return -ENODATA;
}
- }
+#else +#warning "arm_ffa: reading FFA_PARTITION_INFO_GET data not implemented" +#endif
- return FFA_ERR_STAT_SUCCESS;
+}
+/**
- ffa_query_partitions_info - invokes FFA_PARTITION_INFO_GET
and saves partitions data
- @part_uuid: Pointer to the partition(s) UUID
- @pcount: Pointer to the number of partitions variable filled when querying
- This is the boot time function that executes the FFA_PARTITION_INFO_GET
- to query the partitions data. Then, it calls ffa_read_partitions_info
- to save the data in the private data structure.
- 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
- FFA_ERR_STAT_SUCCESS is returned on success. Otherwise, failure
- */
+static int ffa_query_partitions_info(union ffa_partition_uuid *part_uuid,
u32 *pcount)
+{
- unsigned long a0 = 0;
- union ffa_partition_uuid query_uuid = {0};
- unsigned long a5 = 0;
- unsigned long a6 = 0;
- unsigned long a7 = 0;
- struct arm_smccc_res res = {0};
- if (!ffa_priv_data.invoke_ffa_fn)
panic("[FFA] no private data found\n");
- a0 = FFA_PARTITION_INFO_GET;
- /*
* 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;
- }
- ffa_priv_data.invoke_ffa_fn(a0, query_uuid.words.a1, query_uuid.words.a2,
query_uuid.words.a3, query_uuid.words.a4,
a5, a6, a7, &res);
- switch (res.a0) {
- case FFA_ERROR:
- {
switch (((int)res.a2)) {
case FFA_ERR_STAT_INVALID_PARAMETERS:
ffa_err("Unrecognized UUID");
return -EPERM;
case FFA_ERR_STAT_NO_MEMORY:
ffa_err("Results cannot fit in RX buffer of the caller");
return -ENOMEM;
case FFA_ERR_STAT_DENIED:
ffa_err("Callee is not in a state to handle this request");
return -EACCES;
case FFA_ERR_STAT_NOT_SUPPORTED:
ffa_err("This function is not implemented at this FF-A instance");
return -EOPNOTSUPP;
case FFA_ERR_STAT_BUSY:
ffa_err("RX buffer of the caller is not free");
return -EBUSY;
default:
ffa_err("Undefined error (%d)", ((int)res.a2));
return -EINVAL;
}
- }
Same cases as above. Please map those in an array or something and just do a lookup to print an error
- case 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(res.a2, part_uuid);
if (ret)
ffa_err("Failed to read partition(s) data , error (%d)", ret);
}
/*
* return the SP count
*/
if (part_uuid) {
if (!ret)
*pcount = res.a2;
else
*pcount = 0;
}
If I am following the code correctly this can be called with (NULL, NULL), which means that the previous !pcount check won't apply. Is there any other check I am missing?
/*
* 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 secure world
*/
ret = ffa_release_rx_buffer();
if (!part_uuid && !res.a2) {
ffa_err("[FFA] no partition installed in the system");
return -ENODEV;
}
return ret;
- }
- default:
ffa_err("Undefined response function (0x%lx)", res.a0);
return -EINVAL;
- }
+}
+/**
[...]
Regards /Ilias

On Fri, May 13, 2022 at 05:23:19PM +0300, Ilias Apalodimas wrote:
Hi Abdellatif On Fri, Apr 15, 2022 at 01:27:58PM +0100, abdellatif.elkhlifi@arm.com wrote:
From: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
Add the driver implementing Arm Firmware Framework for Armv8-A v1.0
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. This driver uses SMC32 calling convention.
In u-boot FF-A design, FF-A is considered as a discoverable bus. 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 provides helper FF-A interfaces for user layers. These helper functions allow clients to pass data and select the FF-A function to use for the communication with secure world.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com +++ b/arch/arm/cpu/armv8/smccc-call.S @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0 */ /*
- Copyright (c) 2015, Linaro Limited
- (C) Copyright 2022 ARM Limited
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#include <linux/linkage.h> #include <linux/arm-smccc.h> @@ -45,3 +47,28 @@ ENDPROC(__arm_smccc_smc) ENTRY(__arm_smccc_hvc) SMCCC hvc ENDPROC(__arm_smccc_hvc)
+#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
- .macro FFASMCCC instr
- .cfi_startproc
- \instr #0
- ldr x9, [sp]
- stp x0, x1, [x9, #ARM_SMCCC_RES_X0_OFFS]
- stp x2, x3, [x9, #ARM_SMCCC_RES_X2_OFFS]
- stp x4, x5, [x9, #ARM_SMCCC_RES_X4_OFFS]
- stp x6, x7, [x9, #ARM_SMCCC_RES_X6_OFFS]
- ret
- .cfi_endproc
- .endm
+/*
- void arm_ffa_smccc_smc(unsigned long a0, unsigned long a1, unsigned long a2,
unsigned long a3, unsigned long a4, unsigned long a5,
unsigned long a6, unsigned long a7, struct arm_smccc_res *res)
- */
+ENTRY(__arm_ffa_smccc_smc)
- FFASMCCC smc
+ENDPROC(__arm_ffa_smccc_smc)
+#endif diff --git a/arch/arm/lib/asm-offsets.c b/arch/arm/lib/asm-offsets.c index 22fd541f9a..02a4a42fe6 100644 --- a/arch/arm/lib/asm-offsets.c +++ b/arch/arm/lib/asm-offsets.c @@ -9,6 +9,8 @@
- generate asm statements containing #defines,
- compile this file to assembler, and then extract the
- #defines from the assembly-language output.
*/
- (C) Copyright 2022 ARM Limited
#include <common.h> @@ -115,6 +117,10 @@ int main(void) #ifdef CONFIG_ARM_SMCCC DEFINE(ARM_SMCCC_RES_X0_OFFS, offsetof(struct arm_smccc_res, a0)); DEFINE(ARM_SMCCC_RES_X2_OFFS, offsetof(struct arm_smccc_res, a2)); +#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
- DEFINE(ARM_SMCCC_RES_X4_OFFS, offsetof(struct arm_smccc_res, a4));
- DEFINE(ARM_SMCCC_RES_X6_OFFS, offsetof(struct arm_smccc_res, a6));
+#endif DEFINE(ARM_SMCCC_QUIRK_ID_OFFS, offsetof(struct arm_smccc_quirk, id)); DEFINE(ARM_SMCCC_QUIRK_STATE_OFFS, offsetof(struct arm_smccc_quirk, state)); #endif diff --git a/common/board_r.c b/common/board_r.c index b92c1bb0be..bb5f1d0aa6 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -62,6 +62,10 @@ #include <asm-generic/gpio.h> #include <efi_loader.h>
+#ifdef CONFIG_ARM_FFA_TRANSPORT +#include <arm_ffa_helper.h> +#endif
DECLARE_GLOBAL_DATA_PTR;
ulong monitor_flash_len; @@ -771,6 +775,9 @@ static init_fnc_t init_sequence_r[] = { INIT_FUNC_WATCHDOG_RESET initr_net, #endif +#ifdef CONFIG_ARM_FFA_TRANSPORT
- ffa_helper_bus_discover,
+#endif #ifdef CONFIG_POST initr_post, #endif diff --git a/drivers/Kconfig b/drivers/Kconfig index b26ca8cf70..e83c23789d 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -6,6 +6,8 @@ source "drivers/core/Kconfig"
source "drivers/adc/Kconfig"
+source "drivers/arm-ffa/Kconfig"
source "drivers/ata/Kconfig"
source "drivers/axi/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 4e7cf28440..6671d2a604 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -107,6 +107,7 @@ obj-y += iommu/ obj-y += smem/ obj-y += thermal/ obj-$(CONFIG_TEE) += tee/ +obj-$(CONFIG_ARM_FFA_TRANSPORT) += arm-ffa/ obj-y += axi/ obj-y += ufs/ obj-$(CONFIG_W1) += w1/ diff --git a/drivers/arm-ffa/Kconfig b/drivers/arm-ffa/Kconfig new file mode 100644 index 0000000000..23815534c4 --- /dev/null +++ b/drivers/arm-ffa/Kconfig @@ -0,0 +1,27 @@ +# 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 if ARM64
- select LIB_UUID
- select ARM_FFA_TRANSPORT_HELPERS
- 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.
This driver 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.
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.
diff --git a/drivers/arm-ffa/Makefile b/drivers/arm-ffa/Makefile new file mode 100644 index 0000000000..7bc9a336a9 --- /dev/null +++ b/drivers/arm-ffa/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2022 Abdellatif El Khlifi abdellatif.elkhlifi@arm.com +#
+obj-y += arm-ffa-uclass.o core.o diff --git a/drivers/arm-ffa/arm-ffa-uclass.c b/drivers/arm-ffa/arm-ffa-uclass.c new file mode 100644 index 0000000000..2439f87586 --- /dev/null +++ b/drivers/arm-ffa/arm-ffa-uclass.c @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include <common.h> +#include <dm.h> +#include <arm_ffa.h> +#include <errno.h> +#include <log.h> +#include <asm/global_data.h>
+DECLARE_GLOBAL_DATA_PTR;
+UCLASS_DRIVER(ffa) = {
- .name = "ffa",
- .id = UCLASS_FFA,
+};
+/**
- ffa_get_invoke_func - performs a call to the FF-A driver dispatcher
- @func_id: The FF-A function to be used
- @func_data: Pointer to the FF-A function arguments
container structure. This also includes
pointers to the returned data needed by
clients.
- This runtime function passes the FF-A function ID and its arguments to
- the FF-A driver dispatcher.
- This function is called by the FF-A helper functions.
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+int __ffa_runtime ffa_get_invoke_func(u32 func_id, struct ffa_interface_data *func_data) +{
- if (!ffa_device_get_ops()->invoke_func)
return -EINVAL;
- return ffa_device_get_ops()->invoke_func(func_id, func_data);
+}
+/**
- ffa_bus_discover - discover FF-A bus and probe the arm_ffa device
- This boot time function makes sure the FF-A bus is discoverable.
- Then, the arm_ffa device is probed and ready to use.
- This function is called automatically at initcalls
- level (after u-boot relocation).
- Arm FF-A transport is implemented through arm_ffa u-boot device managing the FF-A
- communication.
- All FF-A clients should use the arm_ffa device to use the FF-A transport.
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+int ffa_bus_discover(void) +{
- return ffa_get_device();
+} diff --git a/drivers/arm-ffa/arm_ffa_prv.h b/drivers/arm-ffa/arm_ffa_prv.h new file mode 100644 index 0000000000..44f258addb --- /dev/null +++ b/drivers/arm-ffa/arm_ffa_prv.h @@ -0,0 +1,193 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#ifndef __ARM_FFA_PRV_H +#define __ARM_FFA_PRV_H
+#include <arm_ffa.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <dm/read.h>
+/*
- This header is private. It is exclusively used by the FF-A driver
- */
+/* FF-A core driver name */ +#define FFA_DRV_NAME "arm_ffa"
+/* 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)))
+/* The FF-A SMC function prototype definition */
+typedef void (*invoke_ffa_fn_t)(unsigned long a0, unsigned long a1,
unsigned long a2, unsigned long a3, unsigned long a4,
unsigned long a5, unsigned long a6, unsigned long a7,
struct arm_smccc_res *res);
+/**
- enum ffa_conduit - Arm FF-A conduits supported by the Arm FF-A driver
- Currently only SMC32 is supported.
- */
+enum ffa_conduit {
- FFA_CONDUIT_SMC = 0,
+};
+/**
- FFA_DECLARE_ARGS - FF-A functions local variables
- @a0-a7: local variables used to set registers x0-x7
- @res: the structure hosting the FF-A function return data
- A helper macro for declaring local variables for the FF-A functions arguments.
- The x0-x7 registers are used to exchange data with the secure world.
- But, only the bottom 32-bit of thes registers contains the data.
- */
+#define FFA_DECLARE_ARGS \
- unsigned long a0 = 0; \
- unsigned long a1 = 0; \
- unsigned long a2 = 0; \
- unsigned long a3 = 0; \
- unsigned long a4 = 0; \
- unsigned long a5 = 0; \
- unsigned long a6 = 0; \
- unsigned long a7 = 0; \
- struct arm_smccc_res res = {0}
+/* FF-A error codes */ +#define FFA_ERR_STAT_NOT_SUPPORTED (-1) +#define FFA_ERR_STAT_INVALID_PARAMETERS (-2) +#define FFA_ERR_STAT_NO_MEMORY (-3) +#define FFA_ERR_STAT_BUSY (-4) +#define FFA_ERR_STAT_INTERRUPTED (-5) +#define FFA_ERR_STAT_DENIED (-6) +#define FFA_ERR_STAT_RETRY (-7) +#define FFA_ERR_STAT_ABORTED (-8)
+/**
- struct ffa_features_desc - FF-A functions features
- @func_id: FF-A function
- @field1: features read from register w2
- @field2: features read from register w3
- Data structure describing the features of the FF-A functions queried by
- FFA_FEATURES
- */
+struct ffa_features_desc {
- u32 func_id;
- u32 field1;
- u32 field2;
+};
+/**
- enum ffa_rxtx_buf_sizes - minimum sizes supported
- for the RX/TX buffers
- */
+enum ffa_rxtx_buf_sizes {
- RXTX_4K,
- RXTX_64K,
- RXTX_16K
+};
+/*
- Number of the FF-A interfaces features descriptors
- currently only FFA_RXTX_MAP descriptor is supported
- */
+#define FFA_FEATURE_DESC_CNT (1)
+/**
- struct ffa_rxtxpair - structure hosting the RX/TX buffers virtual addresses
- @rxbuf: virtual address of the RX buffer
- @txbuf: virtual address of the TX buffer
- Data structure hosting 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 {
- u64 rxbuf; /* virtual address */
- u64 txbuf; /* virtual address */
+};
+/**
- struct ffa_partition_desc - the secure partition descriptor
- @info: partition information
- @UUID: UUID
- Each partition has its descriptor containing the partitions information and the UUID
- */
+struct ffa_partition_desc {
- struct ffa_partition_info info;
- union ffa_partition_uuid UUID;
+};
+/**
- struct ffa_partitions - descriptors for all secure partitions
- @count: The number of partitions descriptors
- @descs The partitions descriptors table
- This data structure contains the partitions descriptors table
- */
+struct ffa_partitions {
- u32 count;
- struct ffa_partition_desc *descs; /* virtual address */
+};
+/**
- struct ffa_prvdata - the driver private data structure
- @dev: The arm_ffa device under u-boot driver model
- @fwk_version: FF-A framework version
- @id: u-boot endpoint ID
- @partitions: The partitions descriptors structure
- @pair: The RX/TX buffers pair
- @conduit: The selected conduit
- @invoke_ffa_fn: The function executing the FF-A function
- @features: Table of the FF-A functions having features
- The driver data structure hosting all resident data.
- */
+struct ffa_prvdata {
- struct udevice *dev;
- u32 fwk_version;
- u16 id;
- struct ffa_partitions partitions;
- struct ffa_rxtxpair pair;
- enum ffa_conduit conduit;
- invoke_ffa_fn_t invoke_ffa_fn;
- struct ffa_features_desc features[FFA_FEATURE_DESC_CNT];
+};
+#endif diff --git a/drivers/arm-ffa/core.c b/drivers/arm-ffa/core.c new file mode 100644 index 0000000000..09e4eb753a --- /dev/null +++ b/drivers/arm-ffa/core.c @@ -0,0 +1,1349 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include "arm_ffa_prv.h" +#include <asm/global_data.h> +#include <asm/io.h> +#include <common.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> +#include <log.h> +#include <malloc.h> +#include <mapmem.h> +#include <string.h>
+DECLARE_GLOBAL_DATA_PTR;
+/**
- The device private data structure containing all the resident
- data read from secure world
- */
+struct ffa_prvdata __ffa_runtime_data ffa_priv_data = {0};
+/*
- Driver functions
- */
+/**
- ffa_get_device - create, bind and probe the arm_ffa device
- This boot time function makes sure the arm_ffa device is
- created, bound to this driver, probed and ready to use.
- Arm FF-A transport is implemented through a single u-boot
- device managing the FF-A bus (arm_ffa).
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+int ffa_get_device(void) +{
- int ret;
- if (ffa_priv_data.dev)
return FFA_ERR_STAT_SUCCESS;
- ret = device_bind(dm_root(),
DM_DRIVER_GET(arm_ffa),
FFA_DRV_NAME,
NULL,
ofnode_null(),
&ffa_priv_data.dev);
- if (ret) {
ffa_priv_data.dev = NULL;
return ret;
- }
- /* The FF-A bus discovery succeeds when probing is successful */
- ret = device_probe(ffa_priv_data.dev);
- if (ret) {
ffa_err("can not probe the device");
device_unbind(ffa_priv_data.dev);
ffa_priv_data.dev = NULL;
return ret;
- }
- return FFA_ERR_STAT_SUCCESS;
+}
+/**
- ffa_get_version - FFA_VERSION handler function
- This is the boot time function that implements FFA_VERSION FF-A function
- to get from the secure world the FF-A framework version
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+static int ffa_get_version(void) +{
- u16 major, minor;
- FFA_DECLARE_ARGS;
- if (!ffa_priv_data.invoke_ffa_fn)
panic("[FFA] no private data found\n");
- a0 = FFA_VERSION;
- a1 = FFA_VERSION_1_0;
- ffa_priv_data.invoke_ffa_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res);
- if (res.a0 == FFA_ERR_STAT_NOT_SUPPORTED) {
ffa_err("A Firmware Framework implementation does not exist");
return -EOPNOTSUPP;
- }
- major = GET_FFA_MAJOR_VERSION(res.a0);
- minor = GET_FFA_MINOR_VERSION(res.a0);
- ffa_info("FF-A driver %d.%d\nFF-A framework %d.%d",
FFA_MAJOR_VERSION, FFA_MINOR_VERSION, major, minor);
- if ((major == FFA_MAJOR_VERSION && minor >= FFA_MINOR_VERSION)) {
ffa_info("Versions are compatible ");
ffa_priv_data.fwk_version = res.a0;
return FFA_ERR_STAT_SUCCESS;
- }
- ffa_info("Versions are incompatible ");
- return -EPROTONOSUPPORT;
+}
+/**
- ffa_get_endpoint_id - FFA_ID_GET handler function
- This is the boot time function that implements FFA_ID_GET FF-A function
- to get from the secure world u-boot endpoint ID
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+static int ffa_get_endpoint_id(void) +{
- FFA_DECLARE_ARGS;
- if (!ffa_priv_data.invoke_ffa_fn)
panic("[FFA] no private data found\n");
- a0 = FFA_ID_GET;
- ffa_priv_data.invoke_ffa_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res);
- switch (res.a0) {
- case FFA_ERROR:
- {
if (((int)res.a2) == FFA_ERR_STAT_NOT_SUPPORTED) {
ffa_err("This function is not implemented at this FF-A instance");
return -EOPNOTSUPP;
}
ffa_err("Undefined error code (%d)", ((int)res.a2));
return -EINVAL;
- }
- case FFA_SUCCESS:
- {
ffa_priv_data.id = GET_SELF_ENDPOINT_ID(res.a2);
ffa_info("endpoint ID is %u", ffa_priv_data.id);
return FFA_ERR_STAT_SUCCESS;
- }
- default:
- {
ffa_err("Undefined response function (0x%lx)", res.a0);
return -EINVAL;
- }
- }
+}
+/**
- ffa_get_features_desc - returns the features descriptor of the specified
FF-A function
- @func_id: the FF-A function which the features are to be retrieved
- This is a boot time function that searches the features descriptor of the
- specified FF-A function
- Return:
- When found, the address of the features descriptor is returned. Otherwise, NULL.
- */
+static struct ffa_features_desc *ffa_get_features_desc(u32 func_id) +{
- u32 desc_idx;
- /*
* search for the descriptor of the selected FF-A interface
*/
- for (desc_idx = 0; desc_idx < FFA_FEATURE_DESC_CNT ; desc_idx++)
if (ffa_priv_data.features[desc_idx].func_id == func_id)
return &ffa_priv_data.features[desc_idx];
- return NULL;
+}
+/**
- ffa_get_rxtx_map_features - FFA_FEATURES handler function with FFA_RXTX_MAP
argument
- This is the boot time function that implements FFA_FEATURES FF-A function
- to retrieve the FFA_RXTX_MAP features
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+static int ffa_get_rxtx_map_features(void) +{
- FFA_DECLARE_ARGS;
- if (!ffa_priv_data.invoke_ffa_fn)
panic("[FFA] no private data found\n");
- a0 = FFA_FEATURES;
- a1 = FFA_RXTX_MAP;
- ffa_priv_data.invoke_ffa_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res);
- switch (res.a0) {
- case FFA_ERROR:
- {
if (((int)res.a2) == FFA_ERR_STAT_NOT_SUPPORTED) {
ffa_err("FFA_RXTX_MAP is not implemented at this FF-A instance");
return -EOPNOTSUPP;
}
ffa_err("Undefined error code (%d)", ((int)res.a2));
return -EINVAL;
This happens quite a few times throughout the code. Why is -EINVAL used here instead of -FFA_ERR_STAT_INVALID_PARAMETERS for example?
We should either use FFA_* errors consistently of get rid of those entirely and stick to standard ones, but let's not mix and match
Thanks for all your feedbacks. Error handling and panics removal have been addressed in v4 patchset. Now we use a mapping mechanism and no panics anymore. please check: https://lore.kernel.org/all/20220926101723.9965-5-abdellatif.elkhlifi@arm.co...
- }
- case FFA_SUCCESS:
- {
u32 desc_idx;
/*
* search for an empty descriptor
*/
for (desc_idx = 0; desc_idx < FFA_FEATURE_DESC_CNT ; desc_idx++)
if (!ffa_priv_data.features[desc_idx].func_id) {
/*
* populate the descriptor with
* the interface features data
*/
ffa_priv_data.features[desc_idx].func_id =
FFA_RXTX_MAP;
ffa_priv_data.features[desc_idx].field1 =
res.a2;
ffa_info("FFA_RXTX_MAP features data 0x%lx",
res.a2);
return FFA_ERR_STAT_SUCCESS;
}
ffa_err("Cannot save FFA_RXTX_MAP features data. Descriptors table full");
return -ENOBUFS;
Similarly -FFA_ERR_STAT_NO_MEMORY or something?
- }
- default:
- {
ffa_err("Undefined response function (0x%lx)",
res.a0);
return -EINVAL;
- }
- }
+}
+/**
- ffa_get_rxtx_buffers_pages_cnt - reads from the features data descriptors
the minimum number of pages in each of the RX/TX
buffers
- @buf_4k_pages: Pointer to the minimum number of pages
- This is the boot time function that returns the minimum number of pages
- in each of the RX/TX buffers
- Return:
- buf_4k_pages points to the returned number of pages
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+static int ffa_get_rxtx_buffers_pages_cnt(size_t *buf_4k_pages) +{
- struct ffa_features_desc *desc = NULL;
- if (!buf_4k_pages)
return -EINVAL;
- desc = ffa_get_features_desc(FFA_RXTX_MAP);
- if (!desc)
return -EINVAL;
- switch (desc->field1) {
- case RXTX_4K:
*buf_4k_pages = 1;
break;
- case RXTX_16K:
*buf_4k_pages = 4;
break;
- case RXTX_64K:
*buf_4k_pages = 16;
break;
- default:
ffa_err("RX/TX buffer size not supported");
return -EINVAL;
- }
- return FFA_ERR_STAT_SUCCESS;
+}
+/**
- ffa_free_rxtx_buffers - frees the RX/TX buffers
- @buf_4k_pages: the minimum number of pages in each of the RX/TX
buffers
- This is the boot time function used to free the RX/TX buffers
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+static int ffa_free_rxtx_buffers(size_t buf_4k_pages) +{
- efi_status_t free_rxbuf_ret, free_txbuf_ret;
- ffa_info("Freeing RX/TX buffers");
- free_rxbuf_ret = efi_free_pages(ffa_priv_data.pair.rxbuf, buf_4k_pages);
- free_txbuf_ret = efi_free_pages(ffa_priv_data.pair.txbuf, buf_4k_pages);
- if (free_rxbuf_ret != EFI_SUCCESS || free_txbuf_ret != EFI_SUCCESS) {
ffa_err("Failed to free RX/TX buffers (rx: %lu , tx: %lu)",
free_rxbuf_ret,
free_txbuf_ret);
return -EINVAL;
- }
- ffa_priv_data.pair.rxbuf = 0;
- ffa_priv_data.pair.txbuf = 0;
Should those be set to 0 regardless of the efi_free_pages() outcome? If not then you probably need to handle those one by one. As is it right now one failure to free a buffer means both of those won't be set
addressed in v4 patchset, please check: https://lore.kernel.org/all/20220926101723.9965-5-abdellatif.elkhlifi@arm.co...
- return FFA_ERR_STAT_SUCCESS;
+}
+/**
- ffa_alloc_rxtx_buffers - allocates the RX/TX buffers
- @buf_4k_pages: the minimum number of pages in each of the RX/TX
buffers
- This is the boot time function used by ffa_map_rxtx_buffers to allocate
- the RX/TX buffers before mapping them
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+static int ffa_alloc_rxtx_buffers(size_t buf_4k_pages) +{ +#if CONFIG_IS_ENABLED(EFI_LOADER)
- efi_status_t efi_ret;
- void *virt_txbuf;
- void *virt_rxbuf;
- ffa_info("Using %lu 4KB page(s) for RX/TX buffers size",
buf_4k_pages);
- efi_ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
EFI_BOOT_SERVICES_DATA,
buf_4k_pages,
&ffa_priv_data.pair.rxbuf);
Why are we specifically limiting this to EFI? I understand using efi allocated memory for potential runtime operations, were we need the memory preserved, but why here?
There's also a side effect with this allocation. The EFI subsystem doesn't usually come up until u-boot is fully up and running. This call will force it to start way earlier
In v4 the core driver is no longer dependent on EFI. The intent is to be able to compile it without EFI support. The use of EFI APIs has been dropped and replaced with generic u-boot APIs. When using EFI, FF-A core driver data needs to be copied to the EFI runtime section so it can be accessed at runtime. For that specific case, a new CONFIG_ARM_FFA_EFI_RUNTIME_MODE has been created to enable the feature of copying the data to the runtime section. The code that copies the data is provided by drivers/firmware/arm-ffa/efi_ffa_runtime_data_mgr.c When EFI is needed CONFIG_ARM_FFA_EFI_RUNTIME_MODE should be turned on. If no EFI needed the FF-A driver can still be used independently.
- if (efi_ret != EFI_SUCCESS) {
ffa_priv_data.pair.rxbuf = 0;
ffa_err("Failure to allocate RX buffer (EFI error: 0x%lx)",
efi_ret);
return -ENOBUFS;
- }
- ffa_info("RX buffer at virtual address 0x%llx",
ffa_priv_data.pair.rxbuf);
- virt_rxbuf = (void *)ffa_priv_data.pair.rxbuf;
- /*
* make sure the buffer is clean before use
*/
- memset(virt_rxbuf, 0, buf_4k_pages * SZ_4K);
- efi_ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
EFI_RUNTIME_SERVICES_DATA,
buf_4k_pages,
&ffa_priv_data.pair.txbuf);
- if (efi_ret != EFI_SUCCESS) {
efi_free_pages(ffa_priv_data.pair.rxbuf, buf_4k_pages);
ffa_priv_data.pair.rxbuf = 0;
ffa_priv_data.pair.txbuf = 0;
ffa_err("Failure to allocate the TX buffer (EFI error: 0x%lx)"
, efi_ret);
return -ENOBUFS;
- }
- ffa_info("TX buffer at virtual address 0x%llx",
ffa_priv_data.pair.txbuf);
- virt_txbuf = (void *)ffa_priv_data.pair.txbuf;
- /*
* make sure the buffer is clean before use
*/
- memset(virt_txbuf, 0, buf_4k_pages * SZ_4K);
- return FFA_ERR_STAT_SUCCESS;
+#else
- return -ENOBUFS;
+#endif +}
+/**
- ffa_map_rxtx_buffers - FFA_RXTX_MAP handler function
- @buf_4k_pages: the minimum number of pages in each of the RX/TX
buffers
- This is the boot time function that implements FFA_RXTX_MAP FF-A function
- to map the RX/TX buffers
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+static int ffa_map_rxtx_buffers(size_t buf_4k_pages) +{
- int ret;
- FFA_DECLARE_ARGS;
- if (!ffa_priv_data.invoke_ffa_fn)
panic("[FFA] no private data found\n");
- ret = ffa_alloc_rxtx_buffers(buf_4k_pages);
- if (ret != FFA_ERR_STAT_SUCCESS)
return ret;
- a0 = FFA_RXTX_MAP;
- a1 = ffa_priv_data.pair.txbuf;
- a2 = ffa_priv_data.pair.rxbuf;
- a3 = buf_4k_pages;
- ffa_priv_data.invoke_ffa_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res);
- switch (res.a0) {
- case FFA_ERROR:
- {
switch (((int)res.a2)) {
case FFA_ERR_STAT_INVALID_PARAMETERS:
ffa_err("One or more fields in input parameters is incorrectly encoded");
ret = -EPERM;
break;
case FFA_ERR_STAT_NO_MEMORY:
ffa_err("Not enough memory");
ret = -ENOMEM;
break;
case FFA_ERR_STAT_DENIED:
ffa_err("Buffer pair already registered");
ret = -EACCES;
break;
case FFA_ERR_STAT_NOT_SUPPORTED:
ffa_err("This function is not implemented at this FF-A instance");
ret = -EOPNOTSUPP;
break;
default:
ffa_err("Undefined error (%d)",
((int)res.a2));
ret = -EINVAL;
}
Can we have an array with string literals for the errors and do a lookup instead of nested case switches?
break;
- }
- case FFA_SUCCESS:
ffa_info("RX/TX buffers mapped");
return FFA_ERR_STAT_SUCCESS;
- default:
ffa_err("Undefined response function (0x%lx)",
res.a0);
ret = -EINVAL;
- }
- ffa_free_rxtx_buffers(buf_4k_pages);
- return ret;
+}
+/**
- ffa_unmap_rxtx_buffers - FFA_RXTX_UNMAP handler function
- This is the boot time function that implements FFA_RXTX_UNMAP FF-A function
- to unmap the RX/TX buffers
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+static int ffa_unmap_rxtx_buffers(void) +{
- FFA_DECLARE_ARGS;
- if (!ffa_priv_data.invoke_ffa_fn)
panic("[FFA] no private data found\n");
- a0 = FFA_RXTX_UNMAP;
- a1 = PREP_SELF_ENDPOINT_ID(ffa_priv_data.id);
- ffa_priv_data.invoke_ffa_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res);
- switch (res.a0) {
- case FFA_ERROR:
- {
if (((int)res.a2) == FFA_ERR_STAT_NOT_SUPPORTED)
panic("[FFA] FFA_RXTX_UNMAP is not implemented at this FF-A instance\n");
else if (((int)res.a2) == FFA_ERR_STAT_INVALID_PARAMETERS)
panic("[FFA] There is no buffer pair registered on behalf of the caller\n");
else
panic("[FFA] Undefined error (%d)\n", ((int)res.a2));
There's panics sprinkled around the code. Are all those cases really fatal?
- }
- case FFA_SUCCESS:
- {
size_t buf_4k_pages = 0;
int ret;
ret = ffa_get_rxtx_buffers_pages_cnt(&buf_4k_pages);
if (ret != FFA_ERR_STAT_SUCCESS)
panic("[FFA] RX/TX buffers unmapped but failure in getting pages count\n");
ret = ffa_free_rxtx_buffers(buf_4k_pages);
if (ret != FFA_ERR_STAT_SUCCESS)
panic("[FFA] RX/TX buffers unmapped but failure in freeing the memory\n");
ffa_info("RX/TX buffers unmapped and memory freed");
return FFA_ERR_STAT_SUCCESS;
- }
- default:
panic("[FFA] Undefined response function (0x%lx)", res.a0);
- }
+}
+/**
- ffa_release_rx_buffer - FFA_RX_RELEASE handler function
- This is the boot time function that invokes FFA_RX_RELEASE FF-A function
- to release the ownership of the RX buffer
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+static int ffa_release_rx_buffer(void) +{
- FFA_DECLARE_ARGS;
- if (!ffa_priv_data.invoke_ffa_fn)
panic("[FFA] no private data found\n");
- a0 = FFA_RX_RELEASE;
- ffa_priv_data.invoke_ffa_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res);
- switch (res.a0) {
- case FFA_ERROR:
- {
if (((int)res.a2) == FFA_ERR_STAT_NOT_SUPPORTED)
panic("[FFA] FFA_RX_RELEASE is not implemented at this FF-A instance\n");
else if (((int)res.a2) == FFA_ERR_STAT_DENIED)
panic("[FFA] Caller did not have ownership of the RX buffer\n");
else
panic("[FFA] Undefined error (%d)\n", ((int)res.a2));
- }
- case FFA_SUCCESS:
return FFA_ERR_STAT_SUCCESS;
- default:
panic("[FFA] Undefined response function (0x%lx)\n", res.a0);
- }
+}
+/**
- ffa_uuid_are_identical - checks whether two given UUIDs are identical
- @uuid1: first UUID
- @uuid2: second UUID
- This is a boot time function used by ffa_read_partitions_info to search
- for a UUID in the partitions descriptors table
- Return:
- 1 when UUIDs match. Otherwise, 0
- */
+int ffa_uuid_are_identical(const union ffa_partition_uuid *uuid1,
const union ffa_partition_uuid *uuid2)
+{
- if (!uuid1 || !uuid2)
return 0;
- return (!memcmp(uuid1, uuid2, sizeof(union ffa_partition_uuid)));
+}
+/**
- ffa_read_partitions_info - reads the data queried by FFA_PARTITION_INFO_GET
and saves it in the private structure
- @count: The number of partitions queried
- @part_uuid: Pointer to the partition(s) UUID
- This is the boot time function that reads the partitions information
- returned by the FFA_PARTITION_INFO_GET and saves it in the private
- data structure.
- Return:
- The private data structure is updated with the partition(s) information
- FFA_ERR_STAT_SUCCESS is returned on success. Otherwise, failure
- */
+static int ffa_read_partitions_info(u32 count, union ffa_partition_uuid *part_uuid) +{
- if (!count) {
ffa_err("No partition detected");
return -ENODATA;
- }
- ffa_info("Reading partitions data from the RX buffer");
+#if CONFIG_IS_ENABLED(EFI_LOADER)
- if (!part_uuid) {
/*
* querying information of all partitions
*/
u64 data_pages;
u64 data_bytes;
efi_status_t efi_ret;
size_t buf_4k_pages = 0;
u32 desc_idx;
struct ffa_partition_info *parts_info;
int ret;
data_bytes = count * sizeof(struct ffa_partition_desc);
data_pages = efi_size_in_pages(data_bytes);
/*
* get the RX buffer size in pages
*/
ret = ffa_get_rxtx_buffers_pages_cnt(&buf_4k_pages);
if (ret != FFA_ERR_STAT_SUCCESS) {
ffa_err("Can not get the RX buffer size (error %d)", ret);
return ret;
}
if (data_pages > buf_4k_pages) {
ffa_err("Partitions data size exceeds the RX buffer size:");
ffa_err(" Sizes in pages: data %llu , RX buffer %lu ",
data_pages,
buf_4k_pages);
return -ENOMEM;
}
efi_ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
EFI_RUNTIME_SERVICES_DATA,
data_pages,
(u64 *)&ffa_priv_data.partitions.descs);
Same questions here. Why are we using the EFI APIs to allocate buffers?
if (efi_ret != EFI_SUCCESS) {
ffa_priv_data.partitions.descs = NULL;
ffa_err("Cannot allocate partitions data buffer (EFI error 0x%lx)",
efi_ret);
return -ENOBUFS;
}
/*
* make sure the buffer is clean before use
*/
memset(ffa_priv_data.partitions.descs, 0,
data_pages * SZ_4K);
parts_info = (struct ffa_partition_info *)ffa_priv_data.pair.rxbuf;
for (desc_idx = 0 ; desc_idx < count ; desc_idx++) {
ffa_priv_data.partitions.descs[desc_idx].info =
parts_info[desc_idx];
ffa_info("Partition ID %x : info cached",
ffa_priv_data.partitions.descs[desc_idx].info.id);
}
ffa_priv_data.partitions.count = count;
ffa_info("%d partition(s) found and cached", count);
- } else {
u32 rx_desc_idx, cached_desc_idx;
struct ffa_partition_info *parts_info;
u8 desc_found;
parts_info = (struct ffa_partition_info *)ffa_priv_data.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 < ffa_priv_data.partitions.count;
cached_desc_idx++) {
/*
* save the UUID
*/
if (ffa_priv_data.partitions.descs[cached_desc_idx].info.id ==
parts_info[rx_desc_idx].id) {
ffa_priv_data.partitions.descs[cached_desc_idx].UUID =
*part_uuid;
desc_found = 1;
break;
}
}
if (!desc_found)
return -ENODATA;
}
- }
+#else +#warning "arm_ffa: reading FFA_PARTITION_INFO_GET data not implemented" +#endif
- return FFA_ERR_STAT_SUCCESS;
+}
+/**
- ffa_query_partitions_info - invokes FFA_PARTITION_INFO_GET
and saves partitions data
- @part_uuid: Pointer to the partition(s) UUID
- @pcount: Pointer to the number of partitions variable filled when querying
- This is the boot time function that executes the FFA_PARTITION_INFO_GET
- to query the partitions data. Then, it calls ffa_read_partitions_info
- to save the data in the private data structure.
- 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
- FFA_ERR_STAT_SUCCESS is returned on success. Otherwise, failure
- */
+static int ffa_query_partitions_info(union ffa_partition_uuid *part_uuid,
u32 *pcount)
+{
- unsigned long a0 = 0;
- union ffa_partition_uuid query_uuid = {0};
- unsigned long a5 = 0;
- unsigned long a6 = 0;
- unsigned long a7 = 0;
- struct arm_smccc_res res = {0};
- if (!ffa_priv_data.invoke_ffa_fn)
panic("[FFA] no private data found\n");
- a0 = FFA_PARTITION_INFO_GET;
- /*
* 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;
- }
- ffa_priv_data.invoke_ffa_fn(a0, query_uuid.words.a1, query_uuid.words.a2,
query_uuid.words.a3, query_uuid.words.a4,
a5, a6, a7, &res);
- switch (res.a0) {
- case FFA_ERROR:
- {
switch (((int)res.a2)) {
case FFA_ERR_STAT_INVALID_PARAMETERS:
ffa_err("Unrecognized UUID");
return -EPERM;
case FFA_ERR_STAT_NO_MEMORY:
ffa_err("Results cannot fit in RX buffer of the caller");
return -ENOMEM;
case FFA_ERR_STAT_DENIED:
ffa_err("Callee is not in a state to handle this request");
return -EACCES;
case FFA_ERR_STAT_NOT_SUPPORTED:
ffa_err("This function is not implemented at this FF-A instance");
return -EOPNOTSUPP;
case FFA_ERR_STAT_BUSY:
ffa_err("RX buffer of the caller is not free");
return -EBUSY;
default:
ffa_err("Undefined error (%d)", ((int)res.a2));
return -EINVAL;
}
- }
Same cases as above. Please map those in an array or something and just do a lookup to print an error
- case 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(res.a2, part_uuid);
if (ret)
ffa_err("Failed to read partition(s) data , error (%d)", ret);
}
/*
* return the SP count
*/
if (part_uuid) {
if (!ret)
*pcount = res.a2;
else
*pcount = 0;
}
If I am following the code correctly this can be called with (NULL, NULL), which means that the previous !pcount check won't apply. Is there any other check I am missing?
addressed in v4 patchset, please check: https://lore.kernel.org/all/20220926101723.9965-5-abdellatif.elkhlifi@arm.co...
/*
* 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 secure world
*/
ret = ffa_release_rx_buffer();
if (!part_uuid && !res.a2) {
ffa_err("[FFA] no partition installed in the system");
return -ENODEV;
}
return ret;
- }
- default:
ffa_err("Undefined response function (0x%lx)", res.a0);
return -EINVAL;
- }
+}
+/**
[...]
Regards /Ilias

On Fri, Apr 15, 2022 at 01:27:58PM +0100, abdellatif.elkhlifi@arm.com wrote:
From: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
Add the driver implementing Arm Firmware Framework for Armv8-A v1.0
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. This driver uses SMC32 calling convention.
How come only the SMC32 calling convention is used? What if you need 64-bit pointers for some reason (RXTX_MAP comes to mind) ? Doing the 64-bit calling convention at the same time can't be much more work.
In u-boot FF-A design, FF-A is considered as a discoverable bus. 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 provides helper FF-A interfaces for user layers. These helper functions allow clients to pass data and select the FF-A function to use for the communication with secure world.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
MAINTAINERS | 8 + arch/arm/cpu/armv8/smccc-call.S | 27 + arch/arm/lib/asm-offsets.c | 6 + common/board_r.c | 7 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/arm-ffa/Kconfig | 27 + drivers/arm-ffa/Makefile | 6 + drivers/arm-ffa/arm-ffa-uclass.c | 64 ++ drivers/arm-ffa/arm_ffa_prv.h | 193 +++++ drivers/arm-ffa/core.c | 1349 ++++++++++++++++++++++++++++++ include/arm_ffa.h | 190 +++++ include/arm_ffa_helper.h | 45 + include/dm/uclass-id.h | 1 + include/linux/arm-smccc.h | 28 +- lib/Kconfig | 1 + lib/Makefile | 1 + lib/arm-ffa/Kconfig | 6 + lib/arm-ffa/Makefile | 8 + lib/arm-ffa/arm_ffa_helper.c | 188 +++++ lib/efi_loader/efi_boottime.c | 17 + 21 files changed, 2174 insertions(+), 1 deletion(-) create mode 100644 drivers/arm-ffa/Kconfig create mode 100644 drivers/arm-ffa/Makefile create mode 100644 drivers/arm-ffa/arm-ffa-uclass.c create mode 100644 drivers/arm-ffa/arm_ffa_prv.h create mode 100644 drivers/arm-ffa/core.c create mode 100644 include/arm_ffa.h create mode 100644 include/arm_ffa_helper.h create mode 100644 lib/arm-ffa/Kconfig create mode 100644 lib/arm-ffa/Makefile create mode 100644 lib/arm-ffa/arm_ffa_helper.c
diff --git a/MAINTAINERS b/MAINTAINERS index aca97cd2a3..efa17206b8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -232,6 +232,14 @@ F: board/CZ.NIC/ F: configs/turris_*_defconfig F: include/configs/turris_*.h
+ARM FF-A +M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com +S: Maintained +F: drivers/arm-ffa/ +F: include/arm_ffa.h +F: include/arm_ffa_helper.h +F: lib/arm-ffa/
ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de M: Fabio Estevam festevam@gmail.com diff --git a/arch/arm/cpu/armv8/smccc-call.S b/arch/arm/cpu/armv8/smccc-call.S index dc92b28777..9a6aebf194 100644 --- a/arch/arm/cpu/armv8/smccc-call.S +++ b/arch/arm/cpu/armv8/smccc-call.S @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0 */ /*
- Copyright (c) 2015, Linaro Limited
- (C) Copyright 2022 ARM Limited
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#include <linux/linkage.h> #include <linux/arm-smccc.h> @@ -45,3 +47,28 @@ ENDPROC(__arm_smccc_smc) ENTRY(__arm_smccc_hvc) SMCCC hvc ENDPROC(__arm_smccc_hvc)
+#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
- .macro FFASMCCC instr
- .cfi_startproc
- \instr #0
- ldr x9, [sp]
- stp x0, x1, [x9, #ARM_SMCCC_RES_X0_OFFS]
- stp x2, x3, [x9, #ARM_SMCCC_RES_X2_OFFS]
- stp x4, x5, [x9, #ARM_SMCCC_RES_X4_OFFS]
- stp x6, x7, [x9, #ARM_SMCCC_RES_X6_OFFS]
- ret
- .cfi_endproc
- .endm
+/*
- void arm_ffa_smccc_smc(unsigned long a0, unsigned long a1, unsigned long a2,
unsigned long a3, unsigned long a4, unsigned long a5,
unsigned long a6, unsigned long a7, struct arm_smccc_res *res)
- */
+ENTRY(__arm_ffa_smccc_smc)
- FFASMCCC smc
+ENDPROC(__arm_ffa_smccc_smc)
+#endif diff --git a/arch/arm/lib/asm-offsets.c b/arch/arm/lib/asm-offsets.c index 22fd541f9a..02a4a42fe6 100644 --- a/arch/arm/lib/asm-offsets.c +++ b/arch/arm/lib/asm-offsets.c @@ -9,6 +9,8 @@
- generate asm statements containing #defines,
- compile this file to assembler, and then extract the
- #defines from the assembly-language output.
*/
- (C) Copyright 2022 ARM Limited
#include <common.h> @@ -115,6 +117,10 @@ int main(void) #ifdef CONFIG_ARM_SMCCC DEFINE(ARM_SMCCC_RES_X0_OFFS, offsetof(struct arm_smccc_res, a0)); DEFINE(ARM_SMCCC_RES_X2_OFFS, offsetof(struct arm_smccc_res, a2)); +#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
- DEFINE(ARM_SMCCC_RES_X4_OFFS, offsetof(struct arm_smccc_res, a4));
- DEFINE(ARM_SMCCC_RES_X6_OFFS, offsetof(struct arm_smccc_res, a6));
+#endif DEFINE(ARM_SMCCC_QUIRK_ID_OFFS, offsetof(struct arm_smccc_quirk, id)); DEFINE(ARM_SMCCC_QUIRK_STATE_OFFS, offsetof(struct arm_smccc_quirk, state)); #endif diff --git a/common/board_r.c b/common/board_r.c index b92c1bb0be..bb5f1d0aa6 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -62,6 +62,10 @@ #include <asm-generic/gpio.h> #include <efi_loader.h>
+#ifdef CONFIG_ARM_FFA_TRANSPORT +#include <arm_ffa_helper.h> +#endif
DECLARE_GLOBAL_DATA_PTR;
ulong monitor_flash_len; @@ -771,6 +775,9 @@ static init_fnc_t init_sequence_r[] = { INIT_FUNC_WATCHDOG_RESET initr_net, #endif +#ifdef CONFIG_ARM_FFA_TRANSPORT
- ffa_helper_bus_discover,
+#endif #ifdef CONFIG_POST initr_post, #endif diff --git a/drivers/Kconfig b/drivers/Kconfig index b26ca8cf70..e83c23789d 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -6,6 +6,8 @@ source "drivers/core/Kconfig"
source "drivers/adc/Kconfig"
+source "drivers/arm-ffa/Kconfig"
source "drivers/ata/Kconfig"
source "drivers/axi/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 4e7cf28440..6671d2a604 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -107,6 +107,7 @@ obj-y += iommu/ obj-y += smem/ obj-y += thermal/ obj-$(CONFIG_TEE) += tee/ +obj-$(CONFIG_ARM_FFA_TRANSPORT) += arm-ffa/ obj-y += axi/ obj-y += ufs/ obj-$(CONFIG_W1) += w1/ diff --git a/drivers/arm-ffa/Kconfig b/drivers/arm-ffa/Kconfig new file mode 100644 index 0000000000..23815534c4 --- /dev/null +++ b/drivers/arm-ffa/Kconfig @@ -0,0 +1,27 @@ +# 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 if ARM64
- select LIB_UUID
- select ARM_FFA_TRANSPORT_HELPERS
- 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.
This driver 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.
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.
diff --git a/drivers/arm-ffa/Makefile b/drivers/arm-ffa/Makefile new file mode 100644 index 0000000000..7bc9a336a9 --- /dev/null +++ b/drivers/arm-ffa/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2022 Abdellatif El Khlifi abdellatif.elkhlifi@arm.com +#
+obj-y += arm-ffa-uclass.o core.o diff --git a/drivers/arm-ffa/arm-ffa-uclass.c b/drivers/arm-ffa/arm-ffa-uclass.c new file mode 100644 index 0000000000..2439f87586 --- /dev/null +++ b/drivers/arm-ffa/arm-ffa-uclass.c @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include <common.h> +#include <dm.h> +#include <arm_ffa.h> +#include <errno.h> +#include <log.h> +#include <asm/global_data.h>
+DECLARE_GLOBAL_DATA_PTR;
+UCLASS_DRIVER(ffa) = {
- .name = "ffa",
- .id = UCLASS_FFA,
+};
+/**
- ffa_get_invoke_func - performs a call to the FF-A driver dispatcher
- @func_id: The FF-A function to be used
- @func_data: Pointer to the FF-A function arguments
container structure. This also includes
pointers to the returned data needed by
clients.
- This runtime function passes the FF-A function ID and its arguments to
- the FF-A driver dispatcher.
- This function is called by the FF-A helper functions.
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+int __ffa_runtime ffa_get_invoke_func(u32 func_id, struct ffa_interface_data *func_data) +{
- if (!ffa_device_get_ops()->invoke_func)
return -EINVAL;
- return ffa_device_get_ops()->invoke_func(func_id, func_data);
+}
+/**
- ffa_bus_discover - discover FF-A bus and probe the arm_ffa device
- This boot time function makes sure the FF-A bus is discoverable.
- Then, the arm_ffa device is probed and ready to use.
- This function is called automatically at initcalls
- level (after u-boot relocation).
- Arm FF-A transport is implemented through arm_ffa u-boot device managing the FF-A
- communication.
- All FF-A clients should use the arm_ffa device to use the FF-A transport.
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+int ffa_bus_discover(void) +{
- return ffa_get_device();
+} diff --git a/drivers/arm-ffa/arm_ffa_prv.h b/drivers/arm-ffa/arm_ffa_prv.h new file mode 100644 index 0000000000..44f258addb --- /dev/null +++ b/drivers/arm-ffa/arm_ffa_prv.h @@ -0,0 +1,193 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#ifndef __ARM_FFA_PRV_H +#define __ARM_FFA_PRV_H
+#include <arm_ffa.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <dm/read.h>
+/*
- This header is private. It is exclusively used by the FF-A driver
- */
+/* FF-A core driver name */ +#define FFA_DRV_NAME "arm_ffa"
+/* 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)))
+/* The FF-A SMC function prototype definition */
+typedef void (*invoke_ffa_fn_t)(unsigned long a0, unsigned long a1,
unsigned long a2, unsigned long a3, unsigned long a4,
unsigned long a5, unsigned long a6, unsigned long a7,
struct arm_smccc_res *res);
+/**
- enum ffa_conduit - Arm FF-A conduits supported by the Arm FF-A driver
- Currently only SMC32 is supported.
- */
+enum ffa_conduit {
- FFA_CONDUIT_SMC = 0,
+};
Why is this enum needed?
+/**
- FFA_DECLARE_ARGS - FF-A functions local variables
- @a0-a7: local variables used to set registers x0-x7
- @res: the structure hosting the FF-A function return data
- A helper macro for declaring local variables for the FF-A functions arguments.
- The x0-x7 registers are used to exchange data with the secure world.
- But, only the bottom 32-bit of thes registers contains the data.
- */
+#define FFA_DECLARE_ARGS \
- unsigned long a0 = 0; \
- unsigned long a1 = 0; \
- unsigned long a2 = 0; \
- unsigned long a3 = 0; \
- unsigned long a4 = 0; \
- unsigned long a5 = 0; \
- unsigned long a6 = 0; \
- unsigned long a7 = 0; \
- struct arm_smccc_res res = {0}
+/* FF-A error codes */ +#define FFA_ERR_STAT_NOT_SUPPORTED (-1) +#define FFA_ERR_STAT_INVALID_PARAMETERS (-2) +#define FFA_ERR_STAT_NO_MEMORY (-3) +#define FFA_ERR_STAT_BUSY (-4) +#define FFA_ERR_STAT_INTERRUPTED (-5) +#define FFA_ERR_STAT_DENIED (-6) +#define FFA_ERR_STAT_RETRY (-7) +#define FFA_ERR_STAT_ABORTED (-8)
+/**
- struct ffa_features_desc - FF-A functions features
- @func_id: FF-A function
- @field1: features read from register w2
- @field2: features read from register w3
- Data structure describing the features of the FF-A functions queried by
- FFA_FEATURES
- */
+struct ffa_features_desc {
- u32 func_id;
- u32 field1;
- u32 field2;
+};
+/**
- enum ffa_rxtx_buf_sizes - minimum sizes supported
- for the RX/TX buffers
- */
+enum ffa_rxtx_buf_sizes {
- RXTX_4K,
- RXTX_64K,
- RXTX_16K
+};
+/*
- Number of the FF-A interfaces features descriptors
- currently only FFA_RXTX_MAP descriptor is supported
- */
+#define FFA_FEATURE_DESC_CNT (1)
+/**
- struct ffa_rxtxpair - structure hosting the RX/TX buffers virtual addresses
- @rxbuf: virtual address of the RX buffer
- @txbuf: virtual address of the TX buffer
- Data structure hosting 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 {
- u64 rxbuf; /* virtual address */
- u64 txbuf; /* virtual address */
+};
+/**
- struct ffa_partition_desc - the secure partition descriptor
- @info: partition information
- @UUID: UUID
- Each partition has its descriptor containing the partitions information and the UUID
- */
+struct ffa_partition_desc {
- struct ffa_partition_info info;
- union ffa_partition_uuid UUID;
+};
+/**
- struct ffa_partitions - descriptors for all secure partitions
- @count: The number of partitions descriptors
- @descs The partitions descriptors table
- This data structure contains the partitions descriptors table
- */
+struct ffa_partitions {
- u32 count;
- struct ffa_partition_desc *descs; /* virtual address */
+};
+/**
- struct ffa_prvdata - the driver private data structure
- @dev: The arm_ffa device under u-boot driver model
- @fwk_version: FF-A framework version
- @id: u-boot endpoint ID
- @partitions: The partitions descriptors structure
- @pair: The RX/TX buffers pair
- @conduit: The selected conduit
- @invoke_ffa_fn: The function executing the FF-A function
- @features: Table of the FF-A functions having features
- The driver data structure hosting all resident data.
- */
+struct ffa_prvdata {
- struct udevice *dev;
- u32 fwk_version;
- u16 id;
- struct ffa_partitions partitions;
- struct ffa_rxtxpair pair;
- enum ffa_conduit conduit;
- invoke_ffa_fn_t invoke_ffa_fn;
- struct ffa_features_desc features[FFA_FEATURE_DESC_CNT];
+};
+#endif diff --git a/drivers/arm-ffa/core.c b/drivers/arm-ffa/core.c new file mode 100644 index 0000000000..09e4eb753a --- /dev/null +++ b/drivers/arm-ffa/core.c @@ -0,0 +1,1349 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include "arm_ffa_prv.h" +#include <asm/global_data.h> +#include <asm/io.h> +#include <common.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> +#include <log.h> +#include <malloc.h> +#include <mapmem.h> +#include <string.h>
+DECLARE_GLOBAL_DATA_PTR;
+/**
- The device private data structure containing all the resident
- data read from secure world
- */
+struct ffa_prvdata __ffa_runtime_data ffa_priv_data = {0};
+/*
- Driver functions
- */
+/**
- ffa_get_device - create, bind and probe the arm_ffa device
- This boot time function makes sure the arm_ffa device is
- created, bound to this driver, probed and ready to use.
- Arm FF-A transport is implemented through a single u-boot
- device managing the FF-A bus (arm_ffa).
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+int ffa_get_device(void) +{
- int ret;
- if (ffa_priv_data.dev)
return FFA_ERR_STAT_SUCCESS;
- ret = device_bind(dm_root(),
DM_DRIVER_GET(arm_ffa),
FFA_DRV_NAME,
NULL,
ofnode_null(),
&ffa_priv_data.dev);
- if (ret) {
ffa_priv_data.dev = NULL;
return ret;
- }
- /* The FF-A bus discovery succeeds when probing is successful */
- ret = device_probe(ffa_priv_data.dev);
- if (ret) {
ffa_err("can not probe the device");
device_unbind(ffa_priv_data.dev);
ffa_priv_data.dev = NULL;
return ret;
- }
- return FFA_ERR_STAT_SUCCESS;
+}
+/**
- ffa_get_version - FFA_VERSION handler function
- This is the boot time function that implements FFA_VERSION FF-A function
- to get from the secure world the FF-A framework version
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+static int ffa_get_version(void) +{
- u16 major, minor;
- FFA_DECLARE_ARGS;
- if (!ffa_priv_data.invoke_ffa_fn)
panic("[FFA] no private data found\n");
- a0 = FFA_VERSION;
- a1 = FFA_VERSION_1_0;
- ffa_priv_data.invoke_ffa_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res);
- if (res.a0 == FFA_ERR_STAT_NOT_SUPPORTED) {
ffa_err("A Firmware Framework implementation does not exist");
return -EOPNOTSUPP;
- }
- major = GET_FFA_MAJOR_VERSION(res.a0);
- minor = GET_FFA_MINOR_VERSION(res.a0);
- ffa_info("FF-A driver %d.%d\nFF-A framework %d.%d",
FFA_MAJOR_VERSION, FFA_MINOR_VERSION, major, minor);
- if ((major == FFA_MAJOR_VERSION && minor >= FFA_MINOR_VERSION)) {
ffa_info("Versions are compatible ");
ffa_priv_data.fwk_version = res.a0;
return FFA_ERR_STAT_SUCCESS;
- }
- ffa_info("Versions are incompatible ");
This is an error, perhaps better to use ffa_err() instead. And mention the versions in question also since the info level might be disabled.
- return -EPROTONOSUPPORT;
+}
+/**
- ffa_get_endpoint_id - FFA_ID_GET handler function
- This is the boot time function that implements FFA_ID_GET FF-A function
- to get from the secure world u-boot endpoint ID
This is to get the vm id from an eventual hypervisor, or 0 if there isn't any hypervisor.
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+static int ffa_get_endpoint_id(void) +{
- FFA_DECLARE_ARGS;
- if (!ffa_priv_data.invoke_ffa_fn)
panic("[FFA] no private data found\n");
- a0 = FFA_ID_GET;
- ffa_priv_data.invoke_ffa_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res);
- switch (res.a0) {
- case FFA_ERROR:
- {
if (((int)res.a2) == FFA_ERR_STAT_NOT_SUPPORTED) {
ffa_err("This function is not implemented at this FF-A instance");
return -EOPNOTSUPP;
}
ffa_err("Undefined error code (%d)", ((int)res.a2));
return -EINVAL;
- }
Are these extra braces really needed? Same below.
- case FFA_SUCCESS:
- {
ffa_priv_data.id = GET_SELF_ENDPOINT_ID(res.a2);
ffa_info("endpoint ID is %u", ffa_priv_data.id);
return FFA_ERR_STAT_SUCCESS;
- }
- default:
- {
ffa_err("Undefined response function (0x%lx)", res.a0);
return -EINVAL;
- }
- }
+}
+/**
- ffa_get_features_desc - returns the features descriptor of the specified
FF-A function
- @func_id: the FF-A function which the features are to be retrieved
- This is a boot time function that searches the features descriptor of the
- specified FF-A function
- Return:
- When found, the address of the features descriptor is returned. Otherwise, NULL.
- */
+static struct ffa_features_desc *ffa_get_features_desc(u32 func_id) +{
- u32 desc_idx;
- /*
* search for the descriptor of the selected FF-A interface
*/
- for (desc_idx = 0; desc_idx < FFA_FEATURE_DESC_CNT ; desc_idx++)
if (ffa_priv_data.features[desc_idx].func_id == func_id)
return &ffa_priv_data.features[desc_idx];
- return NULL;
+}
+/**
- ffa_get_rxtx_map_features - FFA_FEATURES handler function with FFA_RXTX_MAP
argument
- This is the boot time function that implements FFA_FEATURES FF-A function
- to retrieve the FFA_RXTX_MAP features
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+static int ffa_get_rxtx_map_features(void) +{
- FFA_DECLARE_ARGS;
- if (!ffa_priv_data.invoke_ffa_fn)
panic("[FFA] no private data found\n");
- a0 = FFA_FEATURES;
- a1 = FFA_RXTX_MAP;
- ffa_priv_data.invoke_ffa_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res);
- switch (res.a0) {
- case FFA_ERROR:
- {
if (((int)res.a2) == FFA_ERR_STAT_NOT_SUPPORTED) {
ffa_err("FFA_RXTX_MAP is not implemented at this FF-A instance");
return -EOPNOTSUPP;
}
ffa_err("Undefined error code (%d)", ((int)res.a2));
return -EINVAL;
- }
- case FFA_SUCCESS:
- {
u32 desc_idx;
/*
* search for an empty descriptor
*/
for (desc_idx = 0; desc_idx < FFA_FEATURE_DESC_CNT ; desc_idx++)
if (!ffa_priv_data.features[desc_idx].func_id) {
/*
* populate the descriptor with
* the interface features data
*/
ffa_priv_data.features[desc_idx].func_id =
FFA_RXTX_MAP;
ffa_priv_data.features[desc_idx].field1 =
res.a2;
ffa_info("FFA_RXTX_MAP features data 0x%lx",
res.a2);
This seems a bit more complicated than necessary. How about just save the block size in a separate fields in ffa_priv_data? That would also make it easier to understand what this value is without looking it up in the spec to remind oneself.
return FFA_ERR_STAT_SUCCESS;
}
ffa_err("Cannot save FFA_RXTX_MAP features data. Descriptors table full");
return -ENOBUFS;
- }
- default:
- {
ffa_err("Undefined response function (0x%lx)",
res.a0);
return -EINVAL;
- }
- }
+}
+/**
- ffa_get_rxtx_buffers_pages_cnt - reads from the features data descriptors
the minimum number of pages in each of the RX/TX
buffers
- @buf_4k_pages: Pointer to the minimum number of pages
- This is the boot time function that returns the minimum number of pages
- in each of the RX/TX buffers
- Return:
- buf_4k_pages points to the returned number of pages
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+static int ffa_get_rxtx_buffers_pages_cnt(size_t *buf_4k_pages) +{
- struct ffa_features_desc *desc = NULL;
- if (!buf_4k_pages)
return -EINVAL;
- desc = ffa_get_features_desc(FFA_RXTX_MAP);
- if (!desc)
return -EINVAL;
- switch (desc->field1) {
- case RXTX_4K:
*buf_4k_pages = 1;
break;
- case RXTX_16K:
*buf_4k_pages = 4;
break;
- case RXTX_64K:
*buf_4k_pages = 16;
break;
- default:
ffa_err("RX/TX buffer size not supported");
return -EINVAL;
- }
- return FFA_ERR_STAT_SUCCESS;
+}
+/**
- ffa_free_rxtx_buffers - frees the RX/TX buffers
- @buf_4k_pages: the minimum number of pages in each of the RX/TX
buffers
- This is the boot time function used to free the RX/TX buffers
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+static int ffa_free_rxtx_buffers(size_t buf_4k_pages) +{
- efi_status_t free_rxbuf_ret, free_txbuf_ret;
- ffa_info("Freeing RX/TX buffers");
- free_rxbuf_ret = efi_free_pages(ffa_priv_data.pair.rxbuf, buf_4k_pages);
- free_txbuf_ret = efi_free_pages(ffa_priv_data.pair.txbuf, buf_4k_pages);
- if (free_rxbuf_ret != EFI_SUCCESS || free_txbuf_ret != EFI_SUCCESS) {
ffa_err("Failed to free RX/TX buffers (rx: %lu , tx: %lu)",
free_rxbuf_ret,
free_txbuf_ret);
return -EINVAL;
- }
- ffa_priv_data.pair.rxbuf = 0;
- ffa_priv_data.pair.txbuf = 0;
- return FFA_ERR_STAT_SUCCESS;
+}
+/**
- ffa_alloc_rxtx_buffers - allocates the RX/TX buffers
- @buf_4k_pages: the minimum number of pages in each of the RX/TX
buffers
- This is the boot time function used by ffa_map_rxtx_buffers to allocate
- the RX/TX buffers before mapping them
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+static int ffa_alloc_rxtx_buffers(size_t buf_4k_pages) +{ +#if CONFIG_IS_ENABLED(EFI_LOADER)
- efi_status_t efi_ret;
- void *virt_txbuf;
- void *virt_rxbuf;
- ffa_info("Using %lu 4KB page(s) for RX/TX buffers size",
buf_4k_pages);
- efi_ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
EFI_BOOT_SERVICES_DATA,
buf_4k_pages,
&ffa_priv_data.pair.rxbuf);
- if (efi_ret != EFI_SUCCESS) {
ffa_priv_data.pair.rxbuf = 0;
ffa_err("Failure to allocate RX buffer (EFI error: 0x%lx)",
efi_ret);
return -ENOBUFS;
- }
- ffa_info("RX buffer at virtual address 0x%llx",
ffa_priv_data.pair.rxbuf);
- virt_rxbuf = (void *)ffa_priv_data.pair.rxbuf;
- /*
* make sure the buffer is clean before use
*/
- memset(virt_rxbuf, 0, buf_4k_pages * SZ_4K);
- efi_ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
EFI_RUNTIME_SERVICES_DATA,
buf_4k_pages,
&ffa_priv_data.pair.txbuf);
- if (efi_ret != EFI_SUCCESS) {
efi_free_pages(ffa_priv_data.pair.rxbuf, buf_4k_pages);
ffa_priv_data.pair.rxbuf = 0;
ffa_priv_data.pair.txbuf = 0;
ffa_err("Failure to allocate the TX buffer (EFI error: 0x%lx)"
, efi_ret);
return -ENOBUFS;
- }
- ffa_info("TX buffer at virtual address 0x%llx",
ffa_priv_data.pair.txbuf);
- virt_txbuf = (void *)ffa_priv_data.pair.txbuf;
- /*
* make sure the buffer is clean before use
*/
- memset(virt_txbuf, 0, buf_4k_pages * SZ_4K);
- return FFA_ERR_STAT_SUCCESS;
+#else
- return -ENOBUFS;
+#endif +}
+/**
- ffa_map_rxtx_buffers - FFA_RXTX_MAP handler function
- @buf_4k_pages: the minimum number of pages in each of the RX/TX
buffers
- This is the boot time function that implements FFA_RXTX_MAP FF-A function
- to map the RX/TX buffers
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+static int ffa_map_rxtx_buffers(size_t buf_4k_pages) +{
- int ret;
- FFA_DECLARE_ARGS;
- if (!ffa_priv_data.invoke_ffa_fn)
panic("[FFA] no private data found\n");
- ret = ffa_alloc_rxtx_buffers(buf_4k_pages);
- if (ret != FFA_ERR_STAT_SUCCESS)
return ret;
- a0 = FFA_RXTX_MAP;
- a1 = ffa_priv_data.pair.txbuf;
- a2 = ffa_priv_data.pair.rxbuf;
- a3 = buf_4k_pages;
- ffa_priv_data.invoke_ffa_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res);
- switch (res.a0) {
- case FFA_ERROR:
- {
switch (((int)res.a2)) {
case FFA_ERR_STAT_INVALID_PARAMETERS:
ffa_err("One or more fields in input parameters is incorrectly encoded");
ret = -EPERM;
break;
case FFA_ERR_STAT_NO_MEMORY:
ffa_err("Not enough memory");
ret = -ENOMEM;
break;
case FFA_ERR_STAT_DENIED:
ffa_err("Buffer pair already registered");
ret = -EACCES;
break;
case FFA_ERR_STAT_NOT_SUPPORTED:
ffa_err("This function is not implemented at this FF-A instance");
ret = -EOPNOTSUPP;
break;
default:
ffa_err("Undefined error (%d)",
((int)res.a2));
ret = -EINVAL;
}
How about lookup table like in the kernel instead? It seems that this kind of translation will be needed a few times.
break;
- }
- case FFA_SUCCESS:
ffa_info("RX/TX buffers mapped");
return FFA_ERR_STAT_SUCCESS;
- default:
ffa_err("Undefined response function (0x%lx)",
res.a0);
ret = -EINVAL;
- }
- ffa_free_rxtx_buffers(buf_4k_pages);
- return ret;
+}
+/**
- ffa_unmap_rxtx_buffers - FFA_RXTX_UNMAP handler function
- This is the boot time function that implements FFA_RXTX_UNMAP FF-A function
- to unmap the RX/TX buffers
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+static int ffa_unmap_rxtx_buffers(void) +{
- FFA_DECLARE_ARGS;
- if (!ffa_priv_data.invoke_ffa_fn)
panic("[FFA] no private data found\n");
- a0 = FFA_RXTX_UNMAP;
- a1 = PREP_SELF_ENDPOINT_ID(ffa_priv_data.id);
- ffa_priv_data.invoke_ffa_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res);
- switch (res.a0) {
- case FFA_ERROR:
- {
if (((int)res.a2) == FFA_ERR_STAT_NOT_SUPPORTED)
panic("[FFA] FFA_RXTX_UNMAP is not implemented at this FF-A instance\n");
else if (((int)res.a2) == FFA_ERR_STAT_INVALID_PARAMETERS)
panic("[FFA] There is no buffer pair registered on behalf of the caller\n");
else
panic("[FFA] Undefined error (%d)\n", ((int)res.a2));
- }
- case FFA_SUCCESS:
- {
size_t buf_4k_pages = 0;
int ret;
ret = ffa_get_rxtx_buffers_pages_cnt(&buf_4k_pages);
if (ret != FFA_ERR_STAT_SUCCESS)
panic("[FFA] RX/TX buffers unmapped but failure in getting pages count\n");
ret = ffa_free_rxtx_buffers(buf_4k_pages);
if (ret != FFA_ERR_STAT_SUCCESS)
panic("[FFA] RX/TX buffers unmapped but failure in freeing the memory\n");
ffa_info("RX/TX buffers unmapped and memory freed");
return FFA_ERR_STAT_SUCCESS;
- }
- default:
panic("[FFA] Undefined response function (0x%lx)", res.a0);
- }
+}
+/**
- ffa_release_rx_buffer - FFA_RX_RELEASE handler function
- This is the boot time function that invokes FFA_RX_RELEASE FF-A function
- to release the ownership of the RX buffer
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+static int ffa_release_rx_buffer(void) +{
- FFA_DECLARE_ARGS;
- if (!ffa_priv_data.invoke_ffa_fn)
panic("[FFA] no private data found\n");
- a0 = FFA_RX_RELEASE;
- ffa_priv_data.invoke_ffa_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res);
- switch (res.a0) {
- case FFA_ERROR:
- {
if (((int)res.a2) == FFA_ERR_STAT_NOT_SUPPORTED)
panic("[FFA] FFA_RX_RELEASE is not implemented at this FF-A instance\n");
else if (((int)res.a2) == FFA_ERR_STAT_DENIED)
panic("[FFA] Caller did not have ownership of the RX buffer\n");
else
panic("[FFA] Undefined error (%d)\n", ((int)res.a2));
- }
- case FFA_SUCCESS:
return FFA_ERR_STAT_SUCCESS;
- default:
panic("[FFA] Undefined response function (0x%lx)\n", res.a0);
- }
+}
+/**
- ffa_uuid_are_identical - checks whether two given UUIDs are identical
- @uuid1: first UUID
- @uuid2: second UUID
- This is a boot time function used by ffa_read_partitions_info to search
- for a UUID in the partitions descriptors table
- Return:
- 1 when UUIDs match. Otherwise, 0
- */
+int ffa_uuid_are_identical(const union ffa_partition_uuid *uuid1,
const union ffa_partition_uuid *uuid2)
+{
- if (!uuid1 || !uuid2)
return 0;
- return (!memcmp(uuid1, uuid2, sizeof(union ffa_partition_uuid)));
+}
+/**
- ffa_read_partitions_info - reads the data queried by FFA_PARTITION_INFO_GET
and saves it in the private structure
- @count: The number of partitions queried
- @part_uuid: Pointer to the partition(s) UUID
- This is the boot time function that reads the partitions information
- returned by the FFA_PARTITION_INFO_GET and saves it in the private
- data structure.
- Return:
- The private data structure is updated with the partition(s) information
- FFA_ERR_STAT_SUCCESS is returned on success. Otherwise, failure
- */
+static int ffa_read_partitions_info(u32 count, union ffa_partition_uuid *part_uuid) +{
- if (!count) {
ffa_err("No partition detected");
return -ENODATA;
- }
- ffa_info("Reading partitions data from the RX buffer");
+#if CONFIG_IS_ENABLED(EFI_LOADER)
- if (!part_uuid) {
/*
* querying information of all partitions
*/
u64 data_pages;
u64 data_bytes;
efi_status_t efi_ret;
size_t buf_4k_pages = 0;
u32 desc_idx;
struct ffa_partition_info *parts_info;
int ret;
data_bytes = count * sizeof(struct ffa_partition_desc);
data_pages = efi_size_in_pages(data_bytes);
/*
* get the RX buffer size in pages
*/
ret = ffa_get_rxtx_buffers_pages_cnt(&buf_4k_pages);
if (ret != FFA_ERR_STAT_SUCCESS) {
ffa_err("Can not get the RX buffer size (error %d)", ret);
return ret;
}
if (data_pages > buf_4k_pages) {
ffa_err("Partitions data size exceeds the RX buffer size:");
ffa_err(" Sizes in pages: data %llu , RX buffer %lu ",
data_pages,
buf_4k_pages);
return -ENOMEM;
}
efi_ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
EFI_RUNTIME_SERVICES_DATA,
data_pages,
(u64 *)&ffa_priv_data.partitions.descs);
if (efi_ret != EFI_SUCCESS) {
ffa_priv_data.partitions.descs = NULL;
ffa_err("Cannot allocate partitions data buffer (EFI error 0x%lx)",
efi_ret);
return -ENOBUFS;
}
/*
* make sure the buffer is clean before use
*/
memset(ffa_priv_data.partitions.descs, 0,
data_pages * SZ_4K);
parts_info = (struct ffa_partition_info *)ffa_priv_data.pair.rxbuf;
for (desc_idx = 0 ; desc_idx < count ; desc_idx++) {
ffa_priv_data.partitions.descs[desc_idx].info =
parts_info[desc_idx];
ffa_info("Partition ID %x : info cached",
ffa_priv_data.partitions.descs[desc_idx].info.id);
}
ffa_priv_data.partitions.count = count;
ffa_info("%d partition(s) found and cached", count);
- } else {
u32 rx_desc_idx, cached_desc_idx;
struct ffa_partition_info *parts_info;
u8 desc_found;
parts_info = (struct ffa_partition_info *)ffa_priv_data.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 < ffa_priv_data.partitions.count;
cached_desc_idx++) {
/*
* save the UUID
*/
if (ffa_priv_data.partitions.descs[cached_desc_idx].info.id ==
parts_info[rx_desc_idx].id) {
ffa_priv_data.partitions.descs[cached_desc_idx].UUID =
*part_uuid;
desc_found = 1;
break;
}
}
if (!desc_found)
return -ENODATA;
}
- }
+#else +#warning "arm_ffa: reading FFA_PARTITION_INFO_GET data not implemented" +#endif
- return FFA_ERR_STAT_SUCCESS;
+}
+/**
- ffa_query_partitions_info - invokes FFA_PARTITION_INFO_GET
and saves partitions data
Strange indentation.
- @part_uuid: Pointer to the partition(s) UUID
- @pcount: Pointer to the number of partitions variable filled when querying
- This is the boot time function that executes the FFA_PARTITION_INFO_GET
- to query the partitions data. Then, it calls ffa_read_partitions_info
- to save the data in the private data structure.
- 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
- FFA_ERR_STAT_SUCCESS is returned on success. Otherwise, failure
- */
+static int ffa_query_partitions_info(union ffa_partition_uuid *part_uuid,
u32 *pcount)
+{
- unsigned long a0 = 0;
- union ffa_partition_uuid query_uuid = {0};
- unsigned long a5 = 0;
- unsigned long a6 = 0;
- unsigned long a7 = 0;
- struct arm_smccc_res res = {0};
- if (!ffa_priv_data.invoke_ffa_fn)
panic("[FFA] no private data found\n");
- a0 = FFA_PARTITION_INFO_GET;
Why no initialize a0 with with value above instead?
- /*
* 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;
- }
- ffa_priv_data.invoke_ffa_fn(a0, query_uuid.words.a1, query_uuid.words.a2,
query_uuid.words.a3, query_uuid.words.a4,
a5, a6, a7, &res);
- switch (res.a0) {
- case FFA_ERROR:
- {
switch (((int)res.a2)) {
case FFA_ERR_STAT_INVALID_PARAMETERS:
ffa_err("Unrecognized UUID");
return -EPERM;
case FFA_ERR_STAT_NO_MEMORY:
ffa_err("Results cannot fit in RX buffer of the caller");
return -ENOMEM;
case FFA_ERR_STAT_DENIED:
ffa_err("Callee is not in a state to handle this request");
return -EACCES;
case FFA_ERR_STAT_NOT_SUPPORTED:
ffa_err("This function is not implemented at this FF-A instance");
return -EOPNOTSUPP;
case FFA_ERR_STAT_BUSY:
ffa_err("RX buffer of the caller is not free");
return -EBUSY;
default:
ffa_err("Undefined error (%d)", ((int)res.a2));
return -EINVAL;
}
- }
- case 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(res.a2, part_uuid);
if (ret)
ffa_err("Failed to read partition(s) data , error (%d)", ret);
}
/*
* return the SP count
*/
if (part_uuid) {
if (!ret)
*pcount = res.a2;
else
*pcount = 0;
}
/*
* 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 secure world
*/
if there's a hypervisor we're returning the buffer to the hypervisor instead. how about "... back to the SPM or hypervisor"?
ret = ffa_release_rx_buffer();
if (!part_uuid && !res.a2) {
ffa_err("[FFA] no partition installed in the system");
return -ENODEV;
}
return ret;
- }
- default:
ffa_err("Undefined response function (0x%lx)", res.a0);
return -EINVAL;
- }
+}
+/**
- ffa_get_partitions_info - FFA_PARTITION_INFO_GET handler function
- @func_data: Pointer to the FF-A function arguments container structure.
- The passed arguments:
- Mode 1: When getting from the driver the number of
- secure partitions:
- @data0_size: UUID size
- @data0: pointer to the UUID (little endian)
- @data1_size: size of the number of partitions
variable
- @data1: pointer to the number of partitions
variable. The variable will be set
by the driver
- Mode 2: When requesting the driver to return the
- partitions information:
- @data0_size: UUID size
- @data0: pointer to the UUID (little endian)
- @data1_size: size of the SPs information buffer
- @data1: pointer to SPs information buffer
(allocated by the client).
The buffer will be filled by the driver
- This is the boot time function that queries the secure partition data from
- the private data structure. If not found, it invokes 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 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().
- When invoked through a client request, ffa_get_partitions_info should be
- called twice. First call is to get from the driver the number of secure
- partitions (SPs) associated to a particular UUID.
- Then, the caller (client) allocates the buffer to host the SPs data and
- issues a 2nd call. Then, the driver fills the SPs data in the pre-allocated
- buffer.
- To achieve the mechanism described above, ffa_get_partitions_info uses the
- following functions:
ffa_read_partitions_info
ffa_query_partitions_info
- Return:
- @data1: When pointing to the number of partitions variable, the number is
- set by the driver.
- When pointing to the partitions information buffer, the buffer will be
- filled by the driver.
- On success FFA_ERR_STAT_SUCCESS is returned. Otherwise, failure
- */
+static int ffa_get_partitions_info(struct ffa_interface_data *func_data) +{
- /*
* fill_data:
* 0: return the SP count
* 1: fill SP data and return it to the caller
* -1: undefined mode
*/
- int fill_data = -1;
- u32 desc_idx, client_desc_idx;
- union ffa_partition_uuid *part_uuid;
- u32 client_desc_max_cnt;
- u32 parts_found = 0;
- if (!func_data) {
ffa_err("No function data provided");
return -EINVAL;
- }
- if (!ffa_priv_data.partitions.count || !ffa_priv_data.partitions.descs)
panic("[FFA] No partition installed\n");
- if (func_data->data0_size == sizeof(union ffa_partition_uuid) &&
func_data->data0 &&
func_data->data1_size == sizeof(u32) &&
func_data->data1) {
/*
* data0 (in): pointer to UUID
* data1 (in): pointer to SP count
* Out: SP count returned in the count variable pointed by data1
*/
fill_data = 0;
ffa_info("Preparing for checking partitions count");
- } else if ((func_data->data0_size == sizeof(union ffa_partition_uuid)) &&
func_data->data0 &&
(func_data->data1_size >= sizeof(struct ffa_partition_info)) &&
!(func_data->data1_size % sizeof(struct ffa_partition_info)) &&
func_data->data1) {
/*
* data0 (in): pointer to UUID
* data1 (in): pointer to SPs descriptors buffer
* (created by the client)
* Out: SPs descriptors returned in the buffer
* pointed by data1
*/
fill_data = 1;
client_desc_idx = 0;
/*
* number of empty descriptors preallocated by the caller
*/
client_desc_max_cnt =
func_data->data1_size / sizeof(struct ffa_partition_info);
ffa_info("Preparing for filling partitions info");
- } else {
ffa_err("Invalid function arguments provided");
return -EINVAL;
- }
- part_uuid = (union ffa_partition_uuid *)func_data->data0;
- ffa_info("Searching partitions using the provided UUID");
- /*
* search in the cached partitions
*/
- for (desc_idx = 0;
desc_idx < ffa_priv_data.partitions.count;
desc_idx++) {
if (ffa_uuid_are_identical(&ffa_priv_data.partitions.descs[desc_idx].UUID,
part_uuid)) {
ffa_info("Partition ID %x matches the provided UUID",
ffa_priv_data.partitions.descs[desc_idx].info.id);
parts_found++;
if (fill_data) {
/*
* trying to fill the partition info in data1
*/
if (client_desc_idx < client_desc_max_cnt) {
((struct ffa_partition_info *)
func_data->data1)[client_desc_idx++] =
ffa_priv_data.partitions.descs[desc_idx].info;
continue;
}
ffa_err("Failed to fill the current descriptor client buffer full");
return -ENOBUFS;
}
}
- }
- if (!parts_found) {
int ret;
ffa_info("No partition found. Querying framework ...");
ret = ffa_query_partitions_info(part_uuid, &parts_found);
if (ret == FFA_ERR_STAT_SUCCESS) {
if (!fill_data) {
*((u32 *)func_data->data1) = parts_found;
ffa_info("Number of partition(s) found matching the UUID: %d",
parts_found);
} else {
/*
* we want to read SPs info
*/
/*
* If SPs data filled, retry searching SP info again
*/
if (parts_found)
ret = ffa_get_partitions_info(func_data);
else
ret = -ENODATA;
}
}
return ret;
- }
- /* partition(s) found */
- if (!fill_data)
*((u32 *)func_data->data1) = parts_found;
- return FFA_ERR_STAT_SUCCESS;
+}
+/**
- ffa_cache_partitions_info - Queries and saves all secure partitions data
- This is a boot time function that invokes 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 the
- resident private data structure and we keep the UUID field empty
- (in FF-A 1.0 UUID is not provided by the partition descriptor)
- This function is called at the device probing level.
- ffa_cache_partitions_info uses ffa_query_partitions_info to get the data
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+static int ffa_cache_partitions_info(void) +{
- return ffa_query_partitions_info(NULL, NULL);
+}
+/**
- ffa_msg_send_direct_req - FFA_MSG_SEND_DIRECT_{REQ,RESP} handler function
- @func_data: Pointer to the FF-A function arguments container structure.
The passed arguments:
@data0_size: partition ID size
@data0: pointer to the partition ID
@data1_size: exchanged data size
@data1: pointer to the data buffer preallocated by
the client (in/out)
- This is the runtime function that 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 20 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:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
This API looks quite complicated. Wouldn't it be easier to let the caller supply the struct ffa_send_direct_data *msg pointer directly instead, like in the Linux kernel driver. The approach here prevents the compiler from helping with type checks etc.
+static int __ffa_runtime ffa_msg_send_direct_req(struct ffa_interface_data
*func_data)
+{
- u16 dst_part_id;
- unsigned long a0 = 0;
- unsigned long a1 = 0;
- unsigned long a2 = 0;
- struct ffa_send_direct_data *msg;
- struct arm_smccc_res res = {0};
- if (!ffa_priv_data.invoke_ffa_fn)
return -ENODEV;
- if (!func_data)
return -EINVAL;
- /* No partition installed */
- if (!ffa_priv_data.partitions.count || !ffa_priv_data.partitions.descs)
return -ENODEV;
- /* Undefined interface parameters */
- if (func_data->data0_size != sizeof(u16) ||
!func_data->data0 ||
func_data->data1_size != FFA_MSG_SEND_DIRECT_MAX_SIZE ||
FFA_MSG_SEND_DIRECT_MAX_SIZE? It's more like FFA_MSG_SEND_DIRECT_ONLY_SIZE.
!func_data->data1)
return -EINVAL;
- dst_part_id = *((u16 *)func_data->data0);
- msg = func_data->data1;
- a0 = FFA_MSG_SEND_DIRECT_REQ;
- a1 = PREP_SELF_ENDPOINT_ID(ffa_priv_data.id) |
PREP_PART_ENDPOINT_ID(dst_part_id);
- ffa_priv_data.invoke_ffa_fn(a0, a1, a2,
msg->a3,
msg->a4,
msg->a5,
msg->a6,
msg->a7,
&res);
- while (res.a0 == FFA_INTERRUPT)
ffa_priv_data.invoke_ffa_fn(FFA_RUN, res.a1,
0, 0, 0, 0, 0, 0,
&res);
- switch (res.a0) {
- case FFA_ERROR:
- {
switch (((int)res.a2)) {
case FFA_ERR_STAT_INVALID_PARAMETERS:
/* Invalid endpoint ID or non-zero reserved register */
return -EPERM;
case FFA_ERR_STAT_ABORTED:
/* Message target ran into unexpected error and has aborted */
return -ECONNABORTED;
case FFA_ERR_STAT_DENIED:
/* Callee is not in a state to handle this request */
return -EACCES;
case FFA_ERR_STAT_NOT_SUPPORTED:
/* This function is not implemented at this FF-A instance */
return -EOPNOTSUPP;
case FFA_ERR_STAT_BUSY:
/* Message target is busy */
return -EBUSY;
default:
/* Undefined error */
return -ENXIO;
}
- }
- case FFA_SUCCESS:
/* Message sent with no response */
return FFA_ERR_STAT_SUCCESS;
- case FFA_MSG_SEND_DIRECT_RESP:
/*
* Message sent with response
* extract the 32-bit wide return data
*/
msg->a3 = (u32)res.a3;
msg->a4 = (u32)res.a4;
msg->a5 = (u32)res.a5;
msg->a6 = (u32)res.a6;
msg->a7 = (u32)res.a7;
return FFA_ERR_STAT_SUCCESS;
- default:
/* Undefined response function */
return -ENOENT;
- }
+}
+/**
- invoke_ffa_drv_api - The driver dispatcher function
- @func_id: The FF-A function to be used
- @func_data: Pointer to the FF-A function arguments container
structure. This also includes pointers to the
returned data needed by clients.
- The dispatcher is a runtime function that selects the FF-A function handler
- based on the input FF-A function ID.
- The input arguments are passed to the handler function.
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+int __ffa_runtime invoke_ffa_drv_api(u32 func_id,
struct ffa_interface_data *func_data)
+{
- if (!ffa_priv_data.dev)
return -ENODEV;
I don't see the point with this funnel layer. It would in my opinion be easier to have one function pointer for each of the provided FF-A functions.
I wouldn't mind if the interface was a bit more like the interface provided in the Linux kernel, at least the parts we're going to support here. It would for sure help later when updating the OP-TEE driver to use this FF-A driver.
- switch (func_id) {
- case FFA_PARTITION_INFO_GET:
return ffa_get_partitions_info(func_data);
- case FFA_RXTX_UNMAP:
return ffa_unmap_rxtx_buffers();
- case FFA_MSG_SEND_DIRECT_REQ:
return ffa_msg_send_direct_req(func_data);
- default:
/* Undefined FF-A interface */
return -EINVAL;
- }
+}
+/**
- ffa_set_conduit - Set the conduit
- This boot time function clears the private data structure and sets the conduit
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+static int ffa_set_conduit(void) +{
- ffa_priv_data.conduit = FFA_CONDUIT_SMC;
- ffa_priv_data.invoke_ffa_fn = arm_ffa_smccc_smc;
- ffa_info("Conduit is SMC");
What if there's a hypervisor? Then HVC should be used instead.
- return FFA_ERR_STAT_SUCCESS;
+}
+/**
- ffa_probe - The driver probe function
- @dev: the arm_ffa device
- Probing is done at boot time and triggered by the uclass device discovery.
- At probe level the following actions are done:
- setting the conduit
- querying the FF-A framework version
- querying from secure world the u-boot endpoint ID
- querying from secure world the supported features of the specified FF-A calls
- mapping the RX/TX buffers
- querying from secure world all the partitions information
- All data queried from secure world is saved in the resident private data structure.
- The probe will fail if either FF-A framework is not detected or the
- FF-A requests are not behaving correctly. This ensures that the
- driver is not installed and its operations are not exported to the clients.
- However, once the driver is successfully probed and an FF-A anomaly is
- detected when clients invoke the driver operations, the driver cause
- u-boot to panic because the client would not know what to do in such conditions.
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+static int ffa_probe(struct udevice *dev) +{
- int ret;
- size_t buf_4k_pages = 0;
- ret = ffa_set_conduit();
- if (ret != FFA_ERR_STAT_SUCCESS)
return ret;
- ret = ffa_get_version();
- if (ret != FFA_ERR_STAT_SUCCESS)
return ret;
- ret = ffa_get_endpoint_id();
- if (ret != FFA_ERR_STAT_SUCCESS)
return ret;
- ret = ffa_get_rxtx_map_features();
- if (ret != FFA_ERR_STAT_SUCCESS)
return ret;
- ret = ffa_get_rxtx_buffers_pages_cnt(&buf_4k_pages);
- if (ret != FFA_ERR_STAT_SUCCESS)
return ret;
- ret = ffa_map_rxtx_buffers(buf_4k_pages);
This function only suceeds if compiled with EFI runtime support, so probing will always fail without EFI runtime support.
If compiled with EFI runtime support, how is this supposed to work with the kernel FF-A driver? The FF-A framework only supports one buffer pair per component and I don't see where the buffer pair is freed when U-Boot is done.
- if (ret != FFA_ERR_STAT_SUCCESS)
return ret;
- ret = ffa_cache_partitions_info();
- if (ret != FFA_ERR_STAT_SUCCESS) {
ffa_free_rxtx_buffers(buf_4k_pages);
return ret;
- }
- return FFA_ERR_STAT_SUCCESS;
+}
+/**
- ffa_drv_ops - The driver operations runtime structure
- @invoke_func: The driver dispatcher
- */
+struct ffa_ops __ffa_runtime_data ffa_drv_ops = {
- .invoke_func = invoke_ffa_drv_api
+};
+/**
- ffa_device_get_ops - driver operations getter
- Return:
- This runtime function returns a pointer to the driver operations structure
- */
+const struct ffa_ops * __ffa_runtime ffa_device_get_ops(void) +{
- return &ffa_drv_ops;
+}
+/**
- Declaring the arm_ffa driver under UCLASS_FFA
- */
+U_BOOT_DRIVER(arm_ffa) = {
- .name = FFA_DRV_NAME,
- .id = UCLASS_FFA,
- .probe = ffa_probe,
+}; diff --git a/include/arm_ffa.h b/include/arm_ffa.h new file mode 100644 index 0000000000..98db01ee72 --- /dev/null +++ b/include/arm_ffa.h @@ -0,0 +1,190 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#ifndef __ARM_FFA_H +#define __ARM_FFA_H
+#include <linux/arm-smccc.h> +#include <linux/printk.h>
+/*
- This header is public. It can be used by clients to access
- data structures and definitions they need
- */
+/*
- Macros for displaying logs
- */
+#define ffa_info(fmt, ...) pr_info("[FFA] " fmt "\n", ##__VA_ARGS__) +#define ffa_err(fmt, ...) pr_err("[FFA] " fmt "\n", ##__VA_ARGS__)
+/*
- The driver operations success error code
- */
+#define FFA_ERR_STAT_SUCCESS (0)
+#if CONFIG_IS_ENABLED(EFI_LOADER)
+#include <efi_loader.h>
+/*
- __ffa_runtime_data and __ffa_runtime - controls whether data/code are
- available after calling the EFI ExitBootServices service.
- Data/code tagged with these keywords are resident (available at boot time and
- at runtime)
- */
+#define __ffa_runtime_data __efi_runtime_data +#define __ffa_runtime __efi_runtime
+#else
+#define __ffa_runtime_data +#define __ffa_runtime
+#endif
+/*
- 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_VERSION FFA_SMC_32(0x63) +#define FFA_ID_GET FFA_SMC_32(0x69) +#define FFA_FEATURES FFA_SMC_32(0x64) +#define FFA_PARTITION_INFO_GET FFA_SMC_32(0x68)
Too much indentation, same below
+#define FFA_RXTX_MAP FFA_SMC_32(0x66) +#define FFA_RXTX_UNMAP FFA_SMC_32(0x67) +#define FFA_RX_RELEASE FFA_SMC_32(0x65) +#define FFA_MSG_SEND_DIRECT_REQ FFA_SMC_32(0x6F) +#define FFA_MSG_SEND_DIRECT_RESP FFA_SMC_32(0x70) +#define FFA_RUN FFA_SMC_32(0x6D) +#define FFA_ERROR FFA_SMC_32(0x60) +#define FFA_SUCCESS FFA_SMC_32(0x61) +#define FFA_INTERRUPT FFA_SMC_32(0x62)
+/*
- 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 __packed 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_send_direct_data - Data structure hosting the data
used by FFA_MSG_SEND_DIRECT_{REQ,RESP}
- @a3-a7: Data read/written from/to w3-w7 registers
- Data structure containing the data to be sent by FFA_MSG_SEND_DIRECT_REQ
- or read from FFA_MSG_SEND_DIRECT_RESP
- */
+struct __packed ffa_send_direct_data {
- u32 a3; /* w3 */
- u32 a4; /* w4 */
- u32 a5; /* w5 */
- u32 a6; /* w6 */
- u32 a7; /* w7 */
+};
+#define FFA_MSG_SEND_DIRECT_MAX_SIZE (sizeof(struct ffa_send_direct_data))
+/* UUID data size */ +#define UUID_SIZE (16)
+/*
- union ffa_partition_uuid - Data union hosting the UUID
transmitted by FFA_PARTITION_INFO_GET
- @words: data structure giving 32-bit words access to the UUID data
- @bytes: data structure giving byte access to the UUID data
- The structure holds little-endian UUID data.
- */
+union ffa_partition_uuid {
- struct __packed words {
- u32 a1; /* w1 */
- u32 a2; /* w2 */
- u32 a3; /* w3 */
- u32 a4; /* w4 */
- } words;
- u8 bytes[UUID_SIZE];
+};
+/**
- struct ffa_interface_data - generic FF-A interface data structure used to exchange
data between user layers and the driver
- @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
- Using this structure user layers can pass various types of data with different sizes.
- The driver internal functions can detect the nature of this data, verfy compliance
- then execute the request when appropriate.
- */
+struct ffa_interface_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 */
+};
+/**
- struct ffa_ops - The driver operations structure
- @invoke_func: function pointer to the invoke function
- The data structure providing all the operations supported by the driver.
- This structure is resident.
- */
+struct ffa_ops {
- /* the driver dispatcher */
- int (*invoke_func)(u32 func_id, struct ffa_interface_data *func_data);
+};
+/**
- The device driver and the Uclass driver public functions
- */
+/**
- ffa_get_invoke_func - performs a call to the FF-A driver dispatcher
- */
+int __ffa_runtime ffa_get_invoke_func(u32 func_id,
struct ffa_interface_data *func_data);
+/**
- ffa_device_get_ops - driver operations getter
- */
+const struct ffa_ops * __ffa_runtime ffa_device_get_ops(void);
+/**
- ffa_get_device - create, bind and probe the arm_ffa device
- */
+int ffa_get_device(void);
+/**
- ffa_bus_discover - discover FF-A bus and probes the arm_ffa device
- */
+int ffa_bus_discover(void); +#endif diff --git a/include/arm_ffa_helper.h b/include/arm_ffa_helper.h new file mode 100644 index 0000000000..1cf68748f0 --- /dev/null +++ b/include/arm_ffa_helper.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#ifndef __ARM_FFA_HELPER_H +#define __ARM_FFA_HELPER_H
+#include <arm_ffa.h>
+/*
- This header is public. Including this header provides all data structures
- and definitions needed by clients to use the FF-A transport driver
- It also provides helper functions allowing to pass data and invoke FF-A functions
- */
+/**
- ffa_helper_get_partitions_info - Wrapper function for FFA_PARTITION_INFO_GET
- */
+int ffa_helper_get_partitions_info(struct ffa_interface_data *func_data);
+/**
- ffa_helper_unmap_rxtx_buffers - Wrapper function for FFA_RXTX_UNMAP
- */
+int ffa_helper_unmap_rxtx_buffers(void);
+/**
- ffa_helper_msg_send_direct_req - Wrapper function for
FFA_MSG_SEND_DIRECT_{REQ,RESP}
- */
+int __ffa_runtime ffa_helper_msg_send_direct_req(struct ffa_interface_data
*func_data);
+/**
- ffa_helper_bus_discover - Wrapper function for FF-A bus discovery
- */
+int ffa_helper_bus_discover(void);
+/**
- ffa_uuid_str_to_bin - Converts a big endian UUID string to a little endian buffer
- */
+int ffa_uuid_str_to_bin(const char *uuid_str, unsigned char *uuid_bin); +#endif diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 0e26e1d138..a1181b8f48 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -52,6 +52,7 @@ enum uclass_id { UCLASS_EFI_MEDIA, /* Devices provided by UEFI firmware */ UCLASS_ETH, /* Ethernet device */ UCLASS_ETH_PHY, /* Ethernet PHY device */
- UCLASS_FFA, /* Arm Firmware Framework for Armv8-A */ UCLASS_FIRMWARE, /* Firmware */ UCLASS_FS_FIRMWARE_LOADER, /* Generic loader */ UCLASS_GPIO, /* Bank of general-purpose I/O pins */
diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h index 7f2be23394..d58c7c104b 100644 --- a/include/linux/arm-smccc.h +++ b/include/linux/arm-smccc.h @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0 */ /*
- Copyright (c) 2015, Linaro Limited
- (C) Copyright 2022 ARM Limited
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#ifndef __LINUX_ARM_SMCCC_H #define __LINUX_ARM_SMCCC_H @@ -57,13 +59,17 @@ #include <linux/types.h> /**
- struct arm_smccc_res - Result from SMC/HVC call
- @a0-a3 result values from registers 0 to 3
*/
- @a0-a7 result values from registers 0 to 7
struct arm_smccc_res { unsigned long a0; unsigned long a1; unsigned long a2; unsigned long a3;
- unsigned long a4;
- unsigned long a5;
- unsigned long a6;
- unsigned long a7;
};
/** @@ -113,6 +119,26 @@ asmlinkage void __arm_smccc_hvc(unsigned long a0, unsigned long a1, unsigned long a5, unsigned long a6, unsigned long a7, struct arm_smccc_res *res, struct arm_smccc_quirk *quirk);
+#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) +/**
- __arm_ffa_smccc_smc() - make SMC calls used for FF-A transport
- @a0-a7: arguments passed in 64-bit registers x0 to x7
- @res: result values from 64-bit registers x0 to x7
- This function is used to make SMC calls following SMC32 Calling Convention.
- The content of the supplied parameters is copied to registers x0 to x7 prior
- to the SMC instruction. The SMC call return data is 32-bit data read from
- registers x0 tp x7.
- */
+asmlinkage void __arm_ffa_smccc_smc(unsigned long a0, unsigned long a1,
unsigned long a2, unsigned long a3, unsigned long a4,
unsigned long a5, unsigned long a6, unsigned long a7,
struct arm_smccc_res *res);
+#define arm_ffa_smccc_smc __arm_ffa_smccc_smc
+#endif
How about leaving the FF-A bit out of this and only have generic SMCCC support? That would make it easier to bring in updates from Linux without reinventing the wheel. I would also be nice to know which version of the SMCCC we're using.
Cheers, Jens
#define arm_smccc_smc(...) __arm_smccc_smc(__VA_ARGS__, NULL)
#define arm_smccc_smc_quirk(...) __arm_smccc_smc(__VA_ARGS__) diff --git a/lib/Kconfig b/lib/Kconfig index effe735365..9006e8d44c 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -845,6 +845,7 @@ config SMBIOS_PARSER source lib/efi/Kconfig source lib/efi_loader/Kconfig source lib/optee/Kconfig +source lib/arm-ffa/Kconfig
config TEST_FDTDEC bool "enable fdtdec test" diff --git a/lib/Makefile b/lib/Makefile index 13fe5fb7a4..36f67cd94e 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_EFI) += efi/ obj-$(CONFIG_EFI_LOADER) += efi_driver/ obj-$(CONFIG_EFI_LOADER) += efi_loader/ obj-$(CONFIG_CMD_BOOTEFI_SELFTEST) += efi_selftest/ +obj-$(CONFIG_ARM_FFA_TRANSPORT_HELPERS) += arm-ffa/ obj-$(CONFIG_LZMA) += lzma/ obj-$(CONFIG_BZIP2) += bzip2/ obj-$(CONFIG_TIZEN) += tizen/ diff --git a/lib/arm-ffa/Kconfig b/lib/arm-ffa/Kconfig new file mode 100644 index 0000000000..79acbc5a8f --- /dev/null +++ b/lib/arm-ffa/Kconfig @@ -0,0 +1,6 @@ +config ARM_FFA_TRANSPORT_HELPERS
- bool "Enable interface helpers for Arm Firmware Framework for Armv8-A"
- depends on ARM_FFA_TRANSPORT
- help
User layers call FF-A interfaces using helper functions which
pass the data and the FF-A function ID to the low level driver
diff --git a/lib/arm-ffa/Makefile b/lib/arm-ffa/Makefile new file mode 100644 index 0000000000..cba625fde4 --- /dev/null +++ b/lib/arm-ffa/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2022 Abdellatif El Khlifi abdellatif.elkhlifi@arm.com +#
+# This file only gets included when CONFIG_ARM_FFA_TRANSPORT_HELPERS is set
+obj-y += arm_ffa_helper.o diff --git a/lib/arm-ffa/arm_ffa_helper.c b/lib/arm-ffa/arm_ffa_helper.c new file mode 100644 index 0000000000..a291b000a7 --- /dev/null +++ b/lib/arm-ffa/arm_ffa_helper.c @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include <common.h> +#include <arm_ffa_helper.h> +#include <uuid.h>
+/**
- ffa_helper_get_partitions_info - Wrapper function for FFA_PARTITION_INFO_GET
- @func_data: Pointer to the FF-A function arguments container
structure.
The passed arguments:
Mode 1: When getting from the driver the number of
secure partitions:
@data0_size: UUID size
@data0: pointer to the UUID (little endian)
@data1_size: size of the number of partitions
variable
@data1: pointer to the number of partitions
variable. The variable will be set
by the driver
Mode 2: When requesting the driver to return the
partitions information:
@data0_size: UUID size
@data0: pointer to the UUID (little endian)
@data1_size: size of the SPs information buffer
@data1: pointer to SPs information buffer
(allocated by the client).
The buffer will be filled by the driver
- This is the boot time function used by clients who wants to get from secure
- world the partition(s) information.
- 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 client should use
- ffa_helper_get_partitions_info to pass the UUID information to the driver
- which uses PARTITION_INFO_GET to obtain the partition(s) information.
- ffa_helper_get_partitions_info should be called twice. First call is to get
- from the driver the number of secure partitions (SPs) associated to a
- particular UUID. Then, the caller (client) allocates the buffer to host the
- SPs data and issues a 2nd call. Then, the driver fills the SPs data in the
- pre-allocated buffer.
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+int ffa_helper_get_partitions_info(struct ffa_interface_data *func_data) +{
- return ffa_get_invoke_func(FFA_PARTITION_INFO_GET, func_data);
+}
+/**
- ffa_helper_unmap_rxtx_buffers - Wrapper function for FFA_RXTX_UNMAP
- This is the boot time function that allows clients to unmap the RX/TX
- buffers
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+int ffa_helper_unmap_rxtx_buffers(void) +{
- return ffa_get_invoke_func(FFA_RXTX_UNMAP, NULL);
+}
+/**
- ffa_helper_msg_send_direct_req - Wrapper function for
FFA_MSG_SEND_DIRECT_{REQ,RESP}
- @func_data: Pointer to the FF-A function arguments container structure.
- The passed arguments:
- @data0_size: partition ID size
- @data0: pointer to the partition ID
- @data1_size: exchanged data size
- @data1: pointer to the data buffer preallocated by the client (in/out)
- This is the runtime function that allows clients to send data to the secure
- world partitions. The arm_ffa driver uses FFA_MSG_SEND_DIRECT_REQ to send the
- data to the secure partition. The response from the secure partition is
- handled internally by the driver using FFA_MSG_SEND_DIRECT_RESP and returned
- to ffa_helper_msg_send_direct_req through @func_data
- The maximum size of the data that can be exchanged is 20 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}
- The client should pre-allocate a buffer pointed by @data1 which the size
- is sizeof(struct ffa_send_direct_data)
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+int __ffa_runtime ffa_helper_msg_send_direct_req(struct ffa_interface_data
*func_data)
+{
- return ffa_get_invoke_func(FFA_MSG_SEND_DIRECT_REQ, func_data);
+}
+/**
- ffa_helper_bus_discover - Wrapper function for FF-A bus discovery
- This boot time function should be called to discover the FF-A bus and
- probe the FF-A device.
- To achieve that, this function is called automatically at initcalls
- level (after u-boot relocation).
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+int ffa_helper_bus_discover(void) +{
- return ffa_bus_discover();
+}
+/**
- ffa_uuid_str_to_bin - Converts a big endian UUID string to a little endian buffer
- @uuid_str: UUID string in big endian format (36 bytes wide + '/0')
- @uuid_bin: preallocated 16 bytes UUID buffer in little endian format
- UUID binary format used by the FF-A framework (16 bytes):
- [LSB] 4B-2B-2B-2B-6B (little endian data fields)
- UUID string is 36 length of characters (36 bytes):
- xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
- be be be be be
- where x is a hexadecimal character. Fields are separated by '-'s.
- When converting to a binary UUID, these endianness rules apply:
be: means the field in the string is considered a big endian hex number
and should be converted to little endian binary format
- Return:
- uuid_bin filled with little endian UUID data
- On success 0 is returned. Otherwise, failure code.
- */
+int ffa_uuid_str_to_bin(const char *uuid_str, unsigned char *uuid_bin) +{
- u16 tmp16 = 0;
- u32 tmp32 = 0;
- u64 tmp64 = 0;
- if (!uuid_str_valid(uuid_str) || !uuid_bin)
return -EINVAL;
- /*
* reverse bytes from big to little endian
*/
- tmp32 = simple_strtoul(uuid_str, NULL, 16);
- memcpy(uuid_bin, &tmp32, 4);
- /*
* reverse bytes from big to little endian
*/
- tmp16 = simple_strtoul(uuid_str + 9, NULL, 16);
- memcpy(uuid_bin + 4, &tmp16, 2);
- /*
* reverse bytes from big to little endian
*/
- tmp16 = simple_strtoul(uuid_str + 14, NULL, 16);
- memcpy(uuid_bin + 6, &tmp16, 2);
- /*
* reverse bytes from big to little endian
*/
- tmp16 = simple_strtoul(uuid_str + 19, NULL, 16);
- memcpy(uuid_bin + 8, &tmp16, 2);
- /*
* reverse bytes from big to little endian
*/
- tmp64 = simple_strtoull(uuid_str + 24, NULL, 16);
- memcpy(uuid_bin + 10, (char *)&tmp64, 6);
- return 0;
+} diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 5bcb8253ed..cffa2c69d6 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -23,6 +23,10 @@ #include <asm/setjmp.h> #include <linux/libfdt_env.h>
+#if defined(CONFIG_ARM_FFA_TRANSPORT) +#include <arm_ffa_helper.h> +#endif
DECLARE_GLOBAL_DATA_PTR;
/* Task priority level */ @@ -2114,6 +2118,10 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, struct efi_event *evt, *next_event; efi_status_t ret = EFI_SUCCESS;
+#if defined(CONFIG_ARM_FFA_TRANSPORT)
- int ffa_ret;
+#endif
EFI_ENTRY("%p, %zx", image_handle, map_key);
/* Check that the caller has read the current memory map */
@@ -2174,6 +2182,15 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL); }
+#if defined(CONFIG_ARM_FFA_TRANSPORT)
- /* unmap FF-A RX/TX buffers */
- ffa_ret = ffa_helper_unmap_rxtx_buffers();
- if (ffa_ret)
debug("[efi_boottime][ERROR]: can not unmap FF-A RX/TX buffers\n");
- else
debug("[efi_boottime][INFO]: FF-A RX/TX buffers unmapped\n");
+#endif
- /* Patch out unsupported runtime function */ efi_runtime_detach();
-- 2.17.1

On Fri, May 13, 2022 at 04:40:36PM +0200, Jens Wiklander wrote:
On Fri, Apr 15, 2022 at 01:27:58PM +0100, abdellatif.elkhlifi@arm.com wrote:
From: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
Add the driver implementing Arm Firmware Framework for Armv8-A v1.0
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. This driver uses SMC32 calling convention.
How come only the SMC32 calling convention is used? What if you need 64-bit pointers for some reason (RXTX_MAP comes to mind) ? Doing the 64-bit calling convention at the same time can't be much more work.
Thanks for the feedback. SMC64 support has been added in v3 patchset. Please refer to the latest work provided by v4: https://lore.kernel.org/all/20220926101723.9965-2-abdellatif.elkhlifi@arm.co...
In u-boot FF-A design, FF-A is considered as a discoverable bus. 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 provides helper FF-A interfaces for user layers. These helper functions allow clients to pass data and select the FF-A function to use for the communication with secure world.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
MAINTAINERS | 8 + arch/arm/cpu/armv8/smccc-call.S | 27 + arch/arm/lib/asm-offsets.c | 6 + common/board_r.c | 7 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/arm-ffa/Kconfig | 27 + drivers/arm-ffa/Makefile | 6 + drivers/arm-ffa/arm-ffa-uclass.c | 64 ++ drivers/arm-ffa/arm_ffa_prv.h | 193 +++++ drivers/arm-ffa/core.c | 1349 ++++++++++++++++++++++++++++++ include/arm_ffa.h | 190 +++++ include/arm_ffa_helper.h | 45 + include/dm/uclass-id.h | 1 + include/linux/arm-smccc.h | 28 +- lib/Kconfig | 1 + lib/Makefile | 1 + lib/arm-ffa/Kconfig | 6 + lib/arm-ffa/Makefile | 8 + lib/arm-ffa/arm_ffa_helper.c | 188 +++++ lib/efi_loader/efi_boottime.c | 17 + 21 files changed, 2174 insertions(+), 1 deletion(-) create mode 100644 drivers/arm-ffa/Kconfig create mode 100644 drivers/arm-ffa/Makefile create mode 100644 drivers/arm-ffa/arm-ffa-uclass.c create mode 100644 drivers/arm-ffa/arm_ffa_prv.h create mode 100644 drivers/arm-ffa/core.c create mode 100644 include/arm_ffa.h create mode 100644 include/arm_ffa_helper.h create mode 100644 lib/arm-ffa/Kconfig create mode 100644 lib/arm-ffa/Makefile create mode 100644 lib/arm-ffa/arm_ffa_helper.c
diff --git a/MAINTAINERS b/MAINTAINERS index aca97cd2a3..efa17206b8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -232,6 +232,14 @@ F: board/CZ.NIC/ F: configs/turris_*_defconfig F: include/configs/turris_*.h
+ARM FF-A +M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com +S: Maintained +F: drivers/arm-ffa/ +F: include/arm_ffa.h +F: include/arm_ffa_helper.h +F: lib/arm-ffa/
ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de M: Fabio Estevam festevam@gmail.com diff --git a/arch/arm/cpu/armv8/smccc-call.S b/arch/arm/cpu/armv8/smccc-call.S index dc92b28777..9a6aebf194 100644 --- a/arch/arm/cpu/armv8/smccc-call.S +++ b/arch/arm/cpu/armv8/smccc-call.S @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0 */ /*
- Copyright (c) 2015, Linaro Limited
- (C) Copyright 2022 ARM Limited
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#include <linux/linkage.h> #include <linux/arm-smccc.h> @@ -45,3 +47,28 @@ ENDPROC(__arm_smccc_smc) ENTRY(__arm_smccc_hvc) SMCCC hvc ENDPROC(__arm_smccc_hvc)
+#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
- .macro FFASMCCC instr
- .cfi_startproc
- \instr #0
- ldr x9, [sp]
- stp x0, x1, [x9, #ARM_SMCCC_RES_X0_OFFS]
- stp x2, x3, [x9, #ARM_SMCCC_RES_X2_OFFS]
- stp x4, x5, [x9, #ARM_SMCCC_RES_X4_OFFS]
- stp x6, x7, [x9, #ARM_SMCCC_RES_X6_OFFS]
- ret
- .cfi_endproc
- .endm
+/*
- void arm_ffa_smccc_smc(unsigned long a0, unsigned long a1, unsigned long a2,
unsigned long a3, unsigned long a4, unsigned long a5,
unsigned long a6, unsigned long a7, struct arm_smccc_res *res)
- */
+ENTRY(__arm_ffa_smccc_smc)
- FFASMCCC smc
+ENDPROC(__arm_ffa_smccc_smc)
+#endif diff --git a/arch/arm/lib/asm-offsets.c b/arch/arm/lib/asm-offsets.c index 22fd541f9a..02a4a42fe6 100644 --- a/arch/arm/lib/asm-offsets.c +++ b/arch/arm/lib/asm-offsets.c @@ -9,6 +9,8 @@
- generate asm statements containing #defines,
- compile this file to assembler, and then extract the
- #defines from the assembly-language output.
*/
- (C) Copyright 2022 ARM Limited
#include <common.h> @@ -115,6 +117,10 @@ int main(void) #ifdef CONFIG_ARM_SMCCC DEFINE(ARM_SMCCC_RES_X0_OFFS, offsetof(struct arm_smccc_res, a0)); DEFINE(ARM_SMCCC_RES_X2_OFFS, offsetof(struct arm_smccc_res, a2)); +#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
- DEFINE(ARM_SMCCC_RES_X4_OFFS, offsetof(struct arm_smccc_res, a4));
- DEFINE(ARM_SMCCC_RES_X6_OFFS, offsetof(struct arm_smccc_res, a6));
+#endif DEFINE(ARM_SMCCC_QUIRK_ID_OFFS, offsetof(struct arm_smccc_quirk, id)); DEFINE(ARM_SMCCC_QUIRK_STATE_OFFS, offsetof(struct arm_smccc_quirk, state)); #endif diff --git a/common/board_r.c b/common/board_r.c index b92c1bb0be..bb5f1d0aa6 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -62,6 +62,10 @@ #include <asm-generic/gpio.h> #include <efi_loader.h>
+#ifdef CONFIG_ARM_FFA_TRANSPORT +#include <arm_ffa_helper.h> +#endif
DECLARE_GLOBAL_DATA_PTR;
ulong monitor_flash_len; @@ -771,6 +775,9 @@ static init_fnc_t init_sequence_r[] = { INIT_FUNC_WATCHDOG_RESET initr_net, #endif +#ifdef CONFIG_ARM_FFA_TRANSPORT
- ffa_helper_bus_discover,
+#endif #ifdef CONFIG_POST initr_post, #endif diff --git a/drivers/Kconfig b/drivers/Kconfig index b26ca8cf70..e83c23789d 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -6,6 +6,8 @@ source "drivers/core/Kconfig"
source "drivers/adc/Kconfig"
+source "drivers/arm-ffa/Kconfig"
source "drivers/ata/Kconfig"
source "drivers/axi/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 4e7cf28440..6671d2a604 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -107,6 +107,7 @@ obj-y += iommu/ obj-y += smem/ obj-y += thermal/ obj-$(CONFIG_TEE) += tee/ +obj-$(CONFIG_ARM_FFA_TRANSPORT) += arm-ffa/ obj-y += axi/ obj-y += ufs/ obj-$(CONFIG_W1) += w1/ diff --git a/drivers/arm-ffa/Kconfig b/drivers/arm-ffa/Kconfig new file mode 100644 index 0000000000..23815534c4 --- /dev/null +++ b/drivers/arm-ffa/Kconfig @@ -0,0 +1,27 @@ +# 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 if ARM64
- select LIB_UUID
- select ARM_FFA_TRANSPORT_HELPERS
- 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.
This driver 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.
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.
diff --git a/drivers/arm-ffa/Makefile b/drivers/arm-ffa/Makefile new file mode 100644 index 0000000000..7bc9a336a9 --- /dev/null +++ b/drivers/arm-ffa/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2022 Abdellatif El Khlifi abdellatif.elkhlifi@arm.com +#
+obj-y += arm-ffa-uclass.o core.o diff --git a/drivers/arm-ffa/arm-ffa-uclass.c b/drivers/arm-ffa/arm-ffa-uclass.c new file mode 100644 index 0000000000..2439f87586 --- /dev/null +++ b/drivers/arm-ffa/arm-ffa-uclass.c @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include <common.h> +#include <dm.h> +#include <arm_ffa.h> +#include <errno.h> +#include <log.h> +#include <asm/global_data.h>
+DECLARE_GLOBAL_DATA_PTR;
+UCLASS_DRIVER(ffa) = {
- .name = "ffa",
- .id = UCLASS_FFA,
+};
+/**
- ffa_get_invoke_func - performs a call to the FF-A driver dispatcher
- @func_id: The FF-A function to be used
- @func_data: Pointer to the FF-A function arguments
container structure. This also includes
pointers to the returned data needed by
clients.
- This runtime function passes the FF-A function ID and its arguments to
- the FF-A driver dispatcher.
- This function is called by the FF-A helper functions.
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+int __ffa_runtime ffa_get_invoke_func(u32 func_id, struct ffa_interface_data *func_data) +{
- if (!ffa_device_get_ops()->invoke_func)
return -EINVAL;
- return ffa_device_get_ops()->invoke_func(func_id, func_data);
+}
+/**
- ffa_bus_discover - discover FF-A bus and probe the arm_ffa device
- This boot time function makes sure the FF-A bus is discoverable.
- Then, the arm_ffa device is probed and ready to use.
- This function is called automatically at initcalls
- level (after u-boot relocation).
- Arm FF-A transport is implemented through arm_ffa u-boot device managing the FF-A
- communication.
- All FF-A clients should use the arm_ffa device to use the FF-A transport.
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+int ffa_bus_discover(void) +{
- return ffa_get_device();
+} diff --git a/drivers/arm-ffa/arm_ffa_prv.h b/drivers/arm-ffa/arm_ffa_prv.h new file mode 100644 index 0000000000..44f258addb --- /dev/null +++ b/drivers/arm-ffa/arm_ffa_prv.h @@ -0,0 +1,193 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#ifndef __ARM_FFA_PRV_H +#define __ARM_FFA_PRV_H
+#include <arm_ffa.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <dm/read.h>
+/*
- This header is private. It is exclusively used by the FF-A driver
- */
+/* FF-A core driver name */ +#define FFA_DRV_NAME "arm_ffa"
+/* 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)))
+/* The FF-A SMC function prototype definition */
+typedef void (*invoke_ffa_fn_t)(unsigned long a0, unsigned long a1,
unsigned long a2, unsigned long a3, unsigned long a4,
unsigned long a5, unsigned long a6, unsigned long a7,
struct arm_smccc_res *res);
+/**
- enum ffa_conduit - Arm FF-A conduits supported by the Arm FF-A driver
- Currently only SMC32 is supported.
- */
+enum ffa_conduit {
- FFA_CONDUIT_SMC = 0,
+};
Why is this enum needed?
Removed in v4. Please check https://lore.kernel.org/all/20220926101723.9965-5-abdellatif.elkhlifi@arm.co...
+/**
- FFA_DECLARE_ARGS - FF-A functions local variables
- @a0-a7: local variables used to set registers x0-x7
- @res: the structure hosting the FF-A function return data
- A helper macro for declaring local variables for the FF-A functions arguments.
- The x0-x7 registers are used to exchange data with the secure world.
- But, only the bottom 32-bit of thes registers contains the data.
- */
+#define FFA_DECLARE_ARGS \
- unsigned long a0 = 0; \
- unsigned long a1 = 0; \
- unsigned long a2 = 0; \
- unsigned long a3 = 0; \
- unsigned long a4 = 0; \
- unsigned long a5 = 0; \
- unsigned long a6 = 0; \
- unsigned long a7 = 0; \
- struct arm_smccc_res res = {0}
+/* FF-A error codes */ +#define FFA_ERR_STAT_NOT_SUPPORTED (-1) +#define FFA_ERR_STAT_INVALID_PARAMETERS (-2) +#define FFA_ERR_STAT_NO_MEMORY (-3) +#define FFA_ERR_STAT_BUSY (-4) +#define FFA_ERR_STAT_INTERRUPTED (-5) +#define FFA_ERR_STAT_DENIED (-6) +#define FFA_ERR_STAT_RETRY (-7) +#define FFA_ERR_STAT_ABORTED (-8)
+/**
- struct ffa_features_desc - FF-A functions features
- @func_id: FF-A function
- @field1: features read from register w2
- @field2: features read from register w3
- Data structure describing the features of the FF-A functions queried by
- FFA_FEATURES
- */
+struct ffa_features_desc {
- u32 func_id;
- u32 field1;
- u32 field2;
+};
+/**
- enum ffa_rxtx_buf_sizes - minimum sizes supported
- for the RX/TX buffers
- */
+enum ffa_rxtx_buf_sizes {
- RXTX_4K,
- RXTX_64K,
- RXTX_16K
+};
+/*
- Number of the FF-A interfaces features descriptors
- currently only FFA_RXTX_MAP descriptor is supported
- */
+#define FFA_FEATURE_DESC_CNT (1)
+/**
- struct ffa_rxtxpair - structure hosting the RX/TX buffers virtual addresses
- @rxbuf: virtual address of the RX buffer
- @txbuf: virtual address of the TX buffer
- Data structure hosting 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 {
- u64 rxbuf; /* virtual address */
- u64 txbuf; /* virtual address */
+};
+/**
- struct ffa_partition_desc - the secure partition descriptor
- @info: partition information
- @UUID: UUID
- Each partition has its descriptor containing the partitions information and the UUID
- */
+struct ffa_partition_desc {
- struct ffa_partition_info info;
- union ffa_partition_uuid UUID;
+};
+/**
- struct ffa_partitions - descriptors for all secure partitions
- @count: The number of partitions descriptors
- @descs The partitions descriptors table
- This data structure contains the partitions descriptors table
- */
+struct ffa_partitions {
- u32 count;
- struct ffa_partition_desc *descs; /* virtual address */
+};
+/**
- struct ffa_prvdata - the driver private data structure
- @dev: The arm_ffa device under u-boot driver model
- @fwk_version: FF-A framework version
- @id: u-boot endpoint ID
- @partitions: The partitions descriptors structure
- @pair: The RX/TX buffers pair
- @conduit: The selected conduit
- @invoke_ffa_fn: The function executing the FF-A function
- @features: Table of the FF-A functions having features
- The driver data structure hosting all resident data.
- */
+struct ffa_prvdata {
- struct udevice *dev;
- u32 fwk_version;
- u16 id;
- struct ffa_partitions partitions;
- struct ffa_rxtxpair pair;
- enum ffa_conduit conduit;
- invoke_ffa_fn_t invoke_ffa_fn;
- struct ffa_features_desc features[FFA_FEATURE_DESC_CNT];
+};
+#endif diff --git a/drivers/arm-ffa/core.c b/drivers/arm-ffa/core.c new file mode 100644 index 0000000000..09e4eb753a --- /dev/null +++ b/drivers/arm-ffa/core.c @@ -0,0 +1,1349 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include "arm_ffa_prv.h" +#include <asm/global_data.h> +#include <asm/io.h> +#include <common.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> +#include <log.h> +#include <malloc.h> +#include <mapmem.h> +#include <string.h>
+DECLARE_GLOBAL_DATA_PTR;
+/**
- The device private data structure containing all the resident
- data read from secure world
- */
+struct ffa_prvdata __ffa_runtime_data ffa_priv_data = {0};
+/*
- Driver functions
- */
+/**
- ffa_get_device - create, bind and probe the arm_ffa device
- This boot time function makes sure the arm_ffa device is
- created, bound to this driver, probed and ready to use.
- Arm FF-A transport is implemented through a single u-boot
- device managing the FF-A bus (arm_ffa).
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+int ffa_get_device(void) +{
- int ret;
- if (ffa_priv_data.dev)
return FFA_ERR_STAT_SUCCESS;
- ret = device_bind(dm_root(),
DM_DRIVER_GET(arm_ffa),
FFA_DRV_NAME,
NULL,
ofnode_null(),
&ffa_priv_data.dev);
- if (ret) {
ffa_priv_data.dev = NULL;
return ret;
- }
- /* The FF-A bus discovery succeeds when probing is successful */
- ret = device_probe(ffa_priv_data.dev);
- if (ret) {
ffa_err("can not probe the device");
device_unbind(ffa_priv_data.dev);
ffa_priv_data.dev = NULL;
return ret;
- }
- return FFA_ERR_STAT_SUCCESS;
+}
+/**
- ffa_get_version - FFA_VERSION handler function
- This is the boot time function that implements FFA_VERSION FF-A function
- to get from the secure world the FF-A framework version
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+static int ffa_get_version(void) +{
- u16 major, minor;
- FFA_DECLARE_ARGS;
- if (!ffa_priv_data.invoke_ffa_fn)
panic("[FFA] no private data found\n");
- a0 = FFA_VERSION;
- a1 = FFA_VERSION_1_0;
- ffa_priv_data.invoke_ffa_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res);
- if (res.a0 == FFA_ERR_STAT_NOT_SUPPORTED) {
ffa_err("A Firmware Framework implementation does not exist");
return -EOPNOTSUPP;
- }
- major = GET_FFA_MAJOR_VERSION(res.a0);
- minor = GET_FFA_MINOR_VERSION(res.a0);
- ffa_info("FF-A driver %d.%d\nFF-A framework %d.%d",
FFA_MAJOR_VERSION, FFA_MINOR_VERSION, major, minor);
- if ((major == FFA_MAJOR_VERSION && minor >= FFA_MINOR_VERSION)) {
ffa_info("Versions are compatible ");
ffa_priv_data.fwk_version = res.a0;
return FFA_ERR_STAT_SUCCESS;
- }
- ffa_info("Versions are incompatible ");
This is an error, perhaps better to use ffa_err() instead. And mention the versions in question also since the info level might be disabled.
Done. Please refer to v4 for the latest updates on that part.
- return -EPROTONOSUPPORT;
+}
+/**
- ffa_get_endpoint_id - FFA_ID_GET handler function
- This is the boot time function that implements FFA_ID_GET FF-A function
- to get from the secure world u-boot endpoint ID
This is to get the vm id from an eventual hypervisor, or 0 if there isn't any hypervisor.
The FF-A 1.0 spec says: ID value 0 must be returned at the Non-secure physical FF-A instance. In our case the Non-secure physical FF-A instance is u-boot. The comment in the function header is trying to be as close as possible to what the spec is saying.
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+static int ffa_get_endpoint_id(void) +{
- FFA_DECLARE_ARGS;
- if (!ffa_priv_data.invoke_ffa_fn)
panic("[FFA] no private data found\n");
- a0 = FFA_ID_GET;
- ffa_priv_data.invoke_ffa_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res);
- switch (res.a0) {
- case FFA_ERROR:
- {
if (((int)res.a2) == FFA_ERR_STAT_NOT_SUPPORTED) {
ffa_err("This function is not implemented at this FF-A instance");
return -EOPNOTSUPP;
}
ffa_err("Undefined error code (%d)", ((int)res.a2));
return -EINVAL;
- }
Are these extra braces really needed? Same below.
error handling has been redesigned to use error mappings. Please refer to v4: https://lore.kernel.org/all/20220926101723.9965-5-abdellatif.elkhlifi@arm.co...
- case FFA_SUCCESS:
- {
ffa_priv_data.id = GET_SELF_ENDPOINT_ID(res.a2);
ffa_info("endpoint ID is %u", ffa_priv_data.id);
return FFA_ERR_STAT_SUCCESS;
- }
- default:
- {
ffa_err("Undefined response function (0x%lx)", res.a0);
return -EINVAL;
- }
- }
+}
+/**
- ffa_get_features_desc - returns the features descriptor of the specified
FF-A function
- @func_id: the FF-A function which the features are to be retrieved
- This is a boot time function that searches the features descriptor of the
- specified FF-A function
- Return:
- When found, the address of the features descriptor is returned. Otherwise, NULL.
- */
+static struct ffa_features_desc *ffa_get_features_desc(u32 func_id) +{
- u32 desc_idx;
- /*
* search for the descriptor of the selected FF-A interface
*/
- for (desc_idx = 0; desc_idx < FFA_FEATURE_DESC_CNT ; desc_idx++)
if (ffa_priv_data.features[desc_idx].func_id == func_id)
return &ffa_priv_data.features[desc_idx];
- return NULL;
+}
+/**
- ffa_get_rxtx_map_features - FFA_FEATURES handler function with FFA_RXTX_MAP
argument
- This is the boot time function that implements FFA_FEATURES FF-A function
- to retrieve the FFA_RXTX_MAP features
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+static int ffa_get_rxtx_map_features(void) +{
- FFA_DECLARE_ARGS;
- if (!ffa_priv_data.invoke_ffa_fn)
panic("[FFA] no private data found\n");
- a0 = FFA_FEATURES;
- a1 = FFA_RXTX_MAP;
- ffa_priv_data.invoke_ffa_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res);
- switch (res.a0) {
- case FFA_ERROR:
- {
if (((int)res.a2) == FFA_ERR_STAT_NOT_SUPPORTED) {
ffa_err("FFA_RXTX_MAP is not implemented at this FF-A instance");
return -EOPNOTSUPP;
}
ffa_err("Undefined error code (%d)", ((int)res.a2));
return -EINVAL;
- }
- case FFA_SUCCESS:
- {
u32 desc_idx;
/*
* search for an empty descriptor
*/
for (desc_idx = 0; desc_idx < FFA_FEATURE_DESC_CNT ; desc_idx++)
if (!ffa_priv_data.features[desc_idx].func_id) {
/*
* populate the descriptor with
* the interface features data
*/
ffa_priv_data.features[desc_idx].func_id =
FFA_RXTX_MAP;
ffa_priv_data.features[desc_idx].field1 =
res.a2;
ffa_info("FFA_RXTX_MAP features data 0x%lx",
res.a2);
This seems a bit more complicated than necessary. How about just save the block size in a separate fields in ffa_priv_data? That would also make it easier to understand what this value is without looking it up in the spec to remind oneself.
Done in v4. rxtx_min_pages field has been added in the ffa_rxtxpair structure and replaces the older logic.
return FFA_ERR_STAT_SUCCESS;
}
ffa_err("Cannot save FFA_RXTX_MAP features data. Descriptors table full");
return -ENOBUFS;
- }
- default:
- {
ffa_err("Undefined response function (0x%lx)",
res.a0);
return -EINVAL;
- }
- }
+}
+/**
- ffa_get_rxtx_buffers_pages_cnt - reads from the features data descriptors
the minimum number of pages in each of the RX/TX
buffers
- @buf_4k_pages: Pointer to the minimum number of pages
- This is the boot time function that returns the minimum number of pages
- in each of the RX/TX buffers
- Return:
- buf_4k_pages points to the returned number of pages
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+static int ffa_get_rxtx_buffers_pages_cnt(size_t *buf_4k_pages) +{
- struct ffa_features_desc *desc = NULL;
- if (!buf_4k_pages)
return -EINVAL;
- desc = ffa_get_features_desc(FFA_RXTX_MAP);
- if (!desc)
return -EINVAL;
- switch (desc->field1) {
- case RXTX_4K:
*buf_4k_pages = 1;
break;
- case RXTX_16K:
*buf_4k_pages = 4;
break;
- case RXTX_64K:
*buf_4k_pages = 16;
break;
- default:
ffa_err("RX/TX buffer size not supported");
return -EINVAL;
- }
- return FFA_ERR_STAT_SUCCESS;
+}
+/**
- ffa_free_rxtx_buffers - frees the RX/TX buffers
- @buf_4k_pages: the minimum number of pages in each of the RX/TX
buffers
- This is the boot time function used to free the RX/TX buffers
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+static int ffa_free_rxtx_buffers(size_t buf_4k_pages) +{
- efi_status_t free_rxbuf_ret, free_txbuf_ret;
- ffa_info("Freeing RX/TX buffers");
- free_rxbuf_ret = efi_free_pages(ffa_priv_data.pair.rxbuf, buf_4k_pages);
- free_txbuf_ret = efi_free_pages(ffa_priv_data.pair.txbuf, buf_4k_pages);
- if (free_rxbuf_ret != EFI_SUCCESS || free_txbuf_ret != EFI_SUCCESS) {
ffa_err("Failed to free RX/TX buffers (rx: %lu , tx: %lu)",
free_rxbuf_ret,
free_txbuf_ret);
return -EINVAL;
- }
- ffa_priv_data.pair.rxbuf = 0;
- ffa_priv_data.pair.txbuf = 0;
- return FFA_ERR_STAT_SUCCESS;
+}
+/**
- ffa_alloc_rxtx_buffers - allocates the RX/TX buffers
- @buf_4k_pages: the minimum number of pages in each of the RX/TX
buffers
- This is the boot time function used by ffa_map_rxtx_buffers to allocate
- the RX/TX buffers before mapping them
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+static int ffa_alloc_rxtx_buffers(size_t buf_4k_pages) +{ +#if CONFIG_IS_ENABLED(EFI_LOADER)
- efi_status_t efi_ret;
- void *virt_txbuf;
- void *virt_rxbuf;
- ffa_info("Using %lu 4KB page(s) for RX/TX buffers size",
buf_4k_pages);
- efi_ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
EFI_BOOT_SERVICES_DATA,
buf_4k_pages,
&ffa_priv_data.pair.rxbuf);
- if (efi_ret != EFI_SUCCESS) {
ffa_priv_data.pair.rxbuf = 0;
ffa_err("Failure to allocate RX buffer (EFI error: 0x%lx)",
efi_ret);
return -ENOBUFS;
- }
- ffa_info("RX buffer at virtual address 0x%llx",
ffa_priv_data.pair.rxbuf);
- virt_rxbuf = (void *)ffa_priv_data.pair.rxbuf;
- /*
* make sure the buffer is clean before use
*/
- memset(virt_rxbuf, 0, buf_4k_pages * SZ_4K);
- efi_ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
EFI_RUNTIME_SERVICES_DATA,
buf_4k_pages,
&ffa_priv_data.pair.txbuf);
- if (efi_ret != EFI_SUCCESS) {
efi_free_pages(ffa_priv_data.pair.rxbuf, buf_4k_pages);
ffa_priv_data.pair.rxbuf = 0;
ffa_priv_data.pair.txbuf = 0;
ffa_err("Failure to allocate the TX buffer (EFI error: 0x%lx)"
, efi_ret);
return -ENOBUFS;
- }
- ffa_info("TX buffer at virtual address 0x%llx",
ffa_priv_data.pair.txbuf);
- virt_txbuf = (void *)ffa_priv_data.pair.txbuf;
- /*
* make sure the buffer is clean before use
*/
- memset(virt_txbuf, 0, buf_4k_pages * SZ_4K);
- return FFA_ERR_STAT_SUCCESS;
+#else
- return -ENOBUFS;
+#endif +}
+/**
- ffa_map_rxtx_buffers - FFA_RXTX_MAP handler function
- @buf_4k_pages: the minimum number of pages in each of the RX/TX
buffers
- This is the boot time function that implements FFA_RXTX_MAP FF-A function
- to map the RX/TX buffers
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+static int ffa_map_rxtx_buffers(size_t buf_4k_pages) +{
- int ret;
- FFA_DECLARE_ARGS;
- if (!ffa_priv_data.invoke_ffa_fn)
panic("[FFA] no private data found\n");
- ret = ffa_alloc_rxtx_buffers(buf_4k_pages);
- if (ret != FFA_ERR_STAT_SUCCESS)
return ret;
- a0 = FFA_RXTX_MAP;
- a1 = ffa_priv_data.pair.txbuf;
- a2 = ffa_priv_data.pair.rxbuf;
- a3 = buf_4k_pages;
- ffa_priv_data.invoke_ffa_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res);
- switch (res.a0) {
- case FFA_ERROR:
- {
switch (((int)res.a2)) {
case FFA_ERR_STAT_INVALID_PARAMETERS:
ffa_err("One or more fields in input parameters is incorrectly encoded");
ret = -EPERM;
break;
case FFA_ERR_STAT_NO_MEMORY:
ffa_err("Not enough memory");
ret = -ENOMEM;
break;
case FFA_ERR_STAT_DENIED:
ffa_err("Buffer pair already registered");
ret = -EACCES;
break;
case FFA_ERR_STAT_NOT_SUPPORTED:
ffa_err("This function is not implemented at this FF-A instance");
ret = -EOPNOTSUPP;
break;
default:
ffa_err("Undefined error (%d)",
((int)res.a2));
ret = -EINVAL;
}
How about lookup table like in the kernel instead? It seems that this kind of translation will be needed a few times.
Done in v4.
break;
- }
- case FFA_SUCCESS:
ffa_info("RX/TX buffers mapped");
return FFA_ERR_STAT_SUCCESS;
- default:
ffa_err("Undefined response function (0x%lx)",
res.a0);
ret = -EINVAL;
- }
- ffa_free_rxtx_buffers(buf_4k_pages);
- return ret;
+}
+/**
- ffa_unmap_rxtx_buffers - FFA_RXTX_UNMAP handler function
- This is the boot time function that implements FFA_RXTX_UNMAP FF-A function
- to unmap the RX/TX buffers
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+static int ffa_unmap_rxtx_buffers(void) +{
- FFA_DECLARE_ARGS;
- if (!ffa_priv_data.invoke_ffa_fn)
panic("[FFA] no private data found\n");
- a0 = FFA_RXTX_UNMAP;
- a1 = PREP_SELF_ENDPOINT_ID(ffa_priv_data.id);
- ffa_priv_data.invoke_ffa_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res);
- switch (res.a0) {
- case FFA_ERROR:
- {
if (((int)res.a2) == FFA_ERR_STAT_NOT_SUPPORTED)
panic("[FFA] FFA_RXTX_UNMAP is not implemented at this FF-A instance\n");
else if (((int)res.a2) == FFA_ERR_STAT_INVALID_PARAMETERS)
panic("[FFA] There is no buffer pair registered on behalf of the caller\n");
else
panic("[FFA] Undefined error (%d)\n", ((int)res.a2));
- }
- case FFA_SUCCESS:
- {
size_t buf_4k_pages = 0;
int ret;
ret = ffa_get_rxtx_buffers_pages_cnt(&buf_4k_pages);
if (ret != FFA_ERR_STAT_SUCCESS)
panic("[FFA] RX/TX buffers unmapped but failure in getting pages count\n");
ret = ffa_free_rxtx_buffers(buf_4k_pages);
if (ret != FFA_ERR_STAT_SUCCESS)
panic("[FFA] RX/TX buffers unmapped but failure in freeing the memory\n");
ffa_info("RX/TX buffers unmapped and memory freed");
return FFA_ERR_STAT_SUCCESS;
- }
- default:
panic("[FFA] Undefined response function (0x%lx)", res.a0);
- }
+}
+/**
- ffa_release_rx_buffer - FFA_RX_RELEASE handler function
- This is the boot time function that invokes FFA_RX_RELEASE FF-A function
- to release the ownership of the RX buffer
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+static int ffa_release_rx_buffer(void) +{
- FFA_DECLARE_ARGS;
- if (!ffa_priv_data.invoke_ffa_fn)
panic("[FFA] no private data found\n");
- a0 = FFA_RX_RELEASE;
- ffa_priv_data.invoke_ffa_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res);
- switch (res.a0) {
- case FFA_ERROR:
- {
if (((int)res.a2) == FFA_ERR_STAT_NOT_SUPPORTED)
panic("[FFA] FFA_RX_RELEASE is not implemented at this FF-A instance\n");
else if (((int)res.a2) == FFA_ERR_STAT_DENIED)
panic("[FFA] Caller did not have ownership of the RX buffer\n");
else
panic("[FFA] Undefined error (%d)\n", ((int)res.a2));
- }
- case FFA_SUCCESS:
return FFA_ERR_STAT_SUCCESS;
- default:
panic("[FFA] Undefined response function (0x%lx)\n", res.a0);
- }
+}
+/**
- ffa_uuid_are_identical - checks whether two given UUIDs are identical
- @uuid1: first UUID
- @uuid2: second UUID
- This is a boot time function used by ffa_read_partitions_info to search
- for a UUID in the partitions descriptors table
- Return:
- 1 when UUIDs match. Otherwise, 0
- */
+int ffa_uuid_are_identical(const union ffa_partition_uuid *uuid1,
const union ffa_partition_uuid *uuid2)
+{
- if (!uuid1 || !uuid2)
return 0;
- return (!memcmp(uuid1, uuid2, sizeof(union ffa_partition_uuid)));
+}
+/**
- ffa_read_partitions_info - reads the data queried by FFA_PARTITION_INFO_GET
and saves it in the private structure
- @count: The number of partitions queried
- @part_uuid: Pointer to the partition(s) UUID
- This is the boot time function that reads the partitions information
- returned by the FFA_PARTITION_INFO_GET and saves it in the private
- data structure.
- Return:
- The private data structure is updated with the partition(s) information
- FFA_ERR_STAT_SUCCESS is returned on success. Otherwise, failure
- */
+static int ffa_read_partitions_info(u32 count, union ffa_partition_uuid *part_uuid) +{
- if (!count) {
ffa_err("No partition detected");
return -ENODATA;
- }
- ffa_info("Reading partitions data from the RX buffer");
+#if CONFIG_IS_ENABLED(EFI_LOADER)
- if (!part_uuid) {
/*
* querying information of all partitions
*/
u64 data_pages;
u64 data_bytes;
efi_status_t efi_ret;
size_t buf_4k_pages = 0;
u32 desc_idx;
struct ffa_partition_info *parts_info;
int ret;
data_bytes = count * sizeof(struct ffa_partition_desc);
data_pages = efi_size_in_pages(data_bytes);
/*
* get the RX buffer size in pages
*/
ret = ffa_get_rxtx_buffers_pages_cnt(&buf_4k_pages);
if (ret != FFA_ERR_STAT_SUCCESS) {
ffa_err("Can not get the RX buffer size (error %d)", ret);
return ret;
}
if (data_pages > buf_4k_pages) {
ffa_err("Partitions data size exceeds the RX buffer size:");
ffa_err(" Sizes in pages: data %llu , RX buffer %lu ",
data_pages,
buf_4k_pages);
return -ENOMEM;
}
efi_ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
EFI_RUNTIME_SERVICES_DATA,
data_pages,
(u64 *)&ffa_priv_data.partitions.descs);
if (efi_ret != EFI_SUCCESS) {
ffa_priv_data.partitions.descs = NULL;
ffa_err("Cannot allocate partitions data buffer (EFI error 0x%lx)",
efi_ret);
return -ENOBUFS;
}
/*
* make sure the buffer is clean before use
*/
memset(ffa_priv_data.partitions.descs, 0,
data_pages * SZ_4K);
parts_info = (struct ffa_partition_info *)ffa_priv_data.pair.rxbuf;
for (desc_idx = 0 ; desc_idx < count ; desc_idx++) {
ffa_priv_data.partitions.descs[desc_idx].info =
parts_info[desc_idx];
ffa_info("Partition ID %x : info cached",
ffa_priv_data.partitions.descs[desc_idx].info.id);
}
ffa_priv_data.partitions.count = count;
ffa_info("%d partition(s) found and cached", count);
- } else {
u32 rx_desc_idx, cached_desc_idx;
struct ffa_partition_info *parts_info;
u8 desc_found;
parts_info = (struct ffa_partition_info *)ffa_priv_data.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 < ffa_priv_data.partitions.count;
cached_desc_idx++) {
/*
* save the UUID
*/
if (ffa_priv_data.partitions.descs[cached_desc_idx].info.id ==
parts_info[rx_desc_idx].id) {
ffa_priv_data.partitions.descs[cached_desc_idx].UUID =
*part_uuid;
desc_found = 1;
break;
}
}
if (!desc_found)
return -ENODATA;
}
- }
+#else +#warning "arm_ffa: reading FFA_PARTITION_INFO_GET data not implemented" +#endif
- return FFA_ERR_STAT_SUCCESS;
+}
+/**
- ffa_query_partitions_info - invokes FFA_PARTITION_INFO_GET
and saves partitions data
Strange indentation.
- @part_uuid: Pointer to the partition(s) UUID
- @pcount: Pointer to the number of partitions variable filled when querying
- This is the boot time function that executes the FFA_PARTITION_INFO_GET
- to query the partitions data. Then, it calls ffa_read_partitions_info
- to save the data in the private data structure.
- 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
- FFA_ERR_STAT_SUCCESS is returned on success. Otherwise, failure
- */
+static int ffa_query_partitions_info(union ffa_partition_uuid *part_uuid,
u32 *pcount)
+{
- unsigned long a0 = 0;
- union ffa_partition_uuid query_uuid = {0};
- unsigned long a5 = 0;
- unsigned long a6 = 0;
- unsigned long a7 = 0;
- struct arm_smccc_res res = {0};
- if (!ffa_priv_data.invoke_ffa_fn)
panic("[FFA] no private data found\n");
- a0 = FFA_PARTITION_INFO_GET;
Why no initialize a0 with with value above instead?
following the new interface design a0 is set at the invoke_ffa_fn callback call. Please refer to v4 for the latest update on that.
- /*
* 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;
- }
- ffa_priv_data.invoke_ffa_fn(a0, query_uuid.words.a1, query_uuid.words.a2,
query_uuid.words.a3, query_uuid.words.a4,
a5, a6, a7, &res);
- switch (res.a0) {
- case FFA_ERROR:
- {
switch (((int)res.a2)) {
case FFA_ERR_STAT_INVALID_PARAMETERS:
ffa_err("Unrecognized UUID");
return -EPERM;
case FFA_ERR_STAT_NO_MEMORY:
ffa_err("Results cannot fit in RX buffer of the caller");
return -ENOMEM;
case FFA_ERR_STAT_DENIED:
ffa_err("Callee is not in a state to handle this request");
return -EACCES;
case FFA_ERR_STAT_NOT_SUPPORTED:
ffa_err("This function is not implemented at this FF-A instance");
return -EOPNOTSUPP;
case FFA_ERR_STAT_BUSY:
ffa_err("RX buffer of the caller is not free");
return -EBUSY;
default:
ffa_err("Undefined error (%d)", ((int)res.a2));
return -EINVAL;
}
- }
- case 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(res.a2, part_uuid);
if (ret)
ffa_err("Failed to read partition(s) data , error (%d)", ret);
}
/*
* return the SP count
*/
if (part_uuid) {
if (!ret)
*pcount = res.a2;
else
*pcount = 0;
}
/*
* 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 secure world
*/
if there's a hypervisor we're returning the buffer to the hypervisor instead. how about "... back to the SPM or hypervisor"?
Done
ret = ffa_release_rx_buffer();
if (!part_uuid && !res.a2) {
ffa_err("[FFA] no partition installed in the system");
return -ENODEV;
}
return ret;
- }
- default:
ffa_err("Undefined response function (0x%lx)", res.a0);
return -EINVAL;
- }
+}
+/**
- ffa_get_partitions_info - FFA_PARTITION_INFO_GET handler function
- @func_data: Pointer to the FF-A function arguments container structure.
- The passed arguments:
- Mode 1: When getting from the driver the number of
- secure partitions:
- @data0_size: UUID size
- @data0: pointer to the UUID (little endian)
- @data1_size: size of the number of partitions
variable
- @data1: pointer to the number of partitions
variable. The variable will be set
by the driver
- Mode 2: When requesting the driver to return the
- partitions information:
- @data0_size: UUID size
- @data0: pointer to the UUID (little endian)
- @data1_size: size of the SPs information buffer
- @data1: pointer to SPs information buffer
(allocated by the client).
The buffer will be filled by the driver
- This is the boot time function that queries the secure partition data from
- the private data structure. If not found, it invokes 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 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().
- When invoked through a client request, ffa_get_partitions_info should be
- called twice. First call is to get from the driver the number of secure
- partitions (SPs) associated to a particular UUID.
- Then, the caller (client) allocates the buffer to host the SPs data and
- issues a 2nd call. Then, the driver fills the SPs data in the pre-allocated
- buffer.
- To achieve the mechanism described above, ffa_get_partitions_info uses the
- following functions:
ffa_read_partitions_info
ffa_query_partitions_info
- Return:
- @data1: When pointing to the number of partitions variable, the number is
- set by the driver.
- When pointing to the partitions information buffer, the buffer will be
- filled by the driver.
- On success FFA_ERR_STAT_SUCCESS is returned. Otherwise, failure
- */
+static int ffa_get_partitions_info(struct ffa_interface_data *func_data) +{
- /*
* fill_data:
* 0: return the SP count
* 1: fill SP data and return it to the caller
* -1: undefined mode
*/
- int fill_data = -1;
- u32 desc_idx, client_desc_idx;
- union ffa_partition_uuid *part_uuid;
- u32 client_desc_max_cnt;
- u32 parts_found = 0;
- if (!func_data) {
ffa_err("No function data provided");
return -EINVAL;
- }
- if (!ffa_priv_data.partitions.count || !ffa_priv_data.partitions.descs)
panic("[FFA] No partition installed\n");
- if (func_data->data0_size == sizeof(union ffa_partition_uuid) &&
func_data->data0 &&
func_data->data1_size == sizeof(u32) &&
func_data->data1) {
/*
* data0 (in): pointer to UUID
* data1 (in): pointer to SP count
* Out: SP count returned in the count variable pointed by data1
*/
fill_data = 0;
ffa_info("Preparing for checking partitions count");
- } else if ((func_data->data0_size == sizeof(union ffa_partition_uuid)) &&
func_data->data0 &&
(func_data->data1_size >= sizeof(struct ffa_partition_info)) &&
!(func_data->data1_size % sizeof(struct ffa_partition_info)) &&
func_data->data1) {
/*
* data0 (in): pointer to UUID
* data1 (in): pointer to SPs descriptors buffer
* (created by the client)
* Out: SPs descriptors returned in the buffer
* pointed by data1
*/
fill_data = 1;
client_desc_idx = 0;
/*
* number of empty descriptors preallocated by the caller
*/
client_desc_max_cnt =
func_data->data1_size / sizeof(struct ffa_partition_info);
ffa_info("Preparing for filling partitions info");
- } else {
ffa_err("Invalid function arguments provided");
return -EINVAL;
- }
- part_uuid = (union ffa_partition_uuid *)func_data->data0;
- ffa_info("Searching partitions using the provided UUID");
- /*
* search in the cached partitions
*/
- for (desc_idx = 0;
desc_idx < ffa_priv_data.partitions.count;
desc_idx++) {
if (ffa_uuid_are_identical(&ffa_priv_data.partitions.descs[desc_idx].UUID,
part_uuid)) {
ffa_info("Partition ID %x matches the provided UUID",
ffa_priv_data.partitions.descs[desc_idx].info.id);
parts_found++;
if (fill_data) {
/*
* trying to fill the partition info in data1
*/
if (client_desc_idx < client_desc_max_cnt) {
((struct ffa_partition_info *)
func_data->data1)[client_desc_idx++] =
ffa_priv_data.partitions.descs[desc_idx].info;
continue;
}
ffa_err("Failed to fill the current descriptor client buffer full");
return -ENOBUFS;
}
}
- }
- if (!parts_found) {
int ret;
ffa_info("No partition found. Querying framework ...");
ret = ffa_query_partitions_info(part_uuid, &parts_found);
if (ret == FFA_ERR_STAT_SUCCESS) {
if (!fill_data) {
*((u32 *)func_data->data1) = parts_found;
ffa_info("Number of partition(s) found matching the UUID: %d",
parts_found);
} else {
/*
* we want to read SPs info
*/
/*
* If SPs data filled, retry searching SP info again
*/
if (parts_found)
ret = ffa_get_partitions_info(func_data);
else
ret = -ENODATA;
}
}
return ret;
- }
- /* partition(s) found */
- if (!fill_data)
*((u32 *)func_data->data1) = parts_found;
- return FFA_ERR_STAT_SUCCESS;
+}
+/**
- ffa_cache_partitions_info - Queries and saves all secure partitions data
- This is a boot time function that invokes 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 the
- resident private data structure and we keep the UUID field empty
- (in FF-A 1.0 UUID is not provided by the partition descriptor)
- This function is called at the device probing level.
- ffa_cache_partitions_info uses ffa_query_partitions_info to get the data
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+static int ffa_cache_partitions_info(void) +{
- return ffa_query_partitions_info(NULL, NULL);
+}
+/**
- ffa_msg_send_direct_req - FFA_MSG_SEND_DIRECT_{REQ,RESP} handler function
- @func_data: Pointer to the FF-A function arguments container structure.
The passed arguments:
@data0_size: partition ID size
@data0: pointer to the partition ID
@data1_size: exchanged data size
@data1: pointer to the data buffer preallocated by
the client (in/out)
- This is the runtime function that 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 20 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:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
This API looks quite complicated. Wouldn't it be easier to let the caller supply the struct ffa_send_direct_data *msg pointer directly instead, like in the Linux kernel driver. The approach here prevents the compiler from helping with type checks etc.
This has been addressed following the new interface design and alignment with the kernel driver. Please refer to v4 for the latest updates on that.
+static int __ffa_runtime ffa_msg_send_direct_req(struct ffa_interface_data
*func_data)
+{
- u16 dst_part_id;
- unsigned long a0 = 0;
- unsigned long a1 = 0;
- unsigned long a2 = 0;
- struct ffa_send_direct_data *msg;
- struct arm_smccc_res res = {0};
- if (!ffa_priv_data.invoke_ffa_fn)
return -ENODEV;
- if (!func_data)
return -EINVAL;
- /* No partition installed */
- if (!ffa_priv_data.partitions.count || !ffa_priv_data.partitions.descs)
return -ENODEV;
- /* Undefined interface parameters */
- if (func_data->data0_size != sizeof(u16) ||
!func_data->data0 ||
func_data->data1_size != FFA_MSG_SEND_DIRECT_MAX_SIZE ||
FFA_MSG_SEND_DIRECT_MAX_SIZE? It's more like FFA_MSG_SEND_DIRECT_ONLY_SIZE.
!func_data->data1)
return -EINVAL;
- dst_part_id = *((u16 *)func_data->data0);
- msg = func_data->data1;
- a0 = FFA_MSG_SEND_DIRECT_REQ;
- a1 = PREP_SELF_ENDPOINT_ID(ffa_priv_data.id) |
PREP_PART_ENDPOINT_ID(dst_part_id);
- ffa_priv_data.invoke_ffa_fn(a0, a1, a2,
msg->a3,
msg->a4,
msg->a5,
msg->a6,
msg->a7,
&res);
- while (res.a0 == FFA_INTERRUPT)
ffa_priv_data.invoke_ffa_fn(FFA_RUN, res.a1,
0, 0, 0, 0, 0, 0,
&res);
- switch (res.a0) {
- case FFA_ERROR:
- {
switch (((int)res.a2)) {
case FFA_ERR_STAT_INVALID_PARAMETERS:
/* Invalid endpoint ID or non-zero reserved register */
return -EPERM;
case FFA_ERR_STAT_ABORTED:
/* Message target ran into unexpected error and has aborted */
return -ECONNABORTED;
case FFA_ERR_STAT_DENIED:
/* Callee is not in a state to handle this request */
return -EACCES;
case FFA_ERR_STAT_NOT_SUPPORTED:
/* This function is not implemented at this FF-A instance */
return -EOPNOTSUPP;
case FFA_ERR_STAT_BUSY:
/* Message target is busy */
return -EBUSY;
default:
/* Undefined error */
return -ENXIO;
}
- }
- case FFA_SUCCESS:
/* Message sent with no response */
return FFA_ERR_STAT_SUCCESS;
- case FFA_MSG_SEND_DIRECT_RESP:
/*
* Message sent with response
* extract the 32-bit wide return data
*/
msg->a3 = (u32)res.a3;
msg->a4 = (u32)res.a4;
msg->a5 = (u32)res.a5;
msg->a6 = (u32)res.a6;
msg->a7 = (u32)res.a7;
return FFA_ERR_STAT_SUCCESS;
- default:
/* Undefined response function */
return -ENOENT;
- }
+}
+/**
- invoke_ffa_drv_api - The driver dispatcher function
- @func_id: The FF-A function to be used
- @func_data: Pointer to the FF-A function arguments container
structure. This also includes pointers to the
returned data needed by clients.
- The dispatcher is a runtime function that selects the FF-A function handler
- based on the input FF-A function ID.
- The input arguments are passed to the handler function.
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+int __ffa_runtime invoke_ffa_drv_api(u32 func_id,
struct ffa_interface_data *func_data)
+{
- if (!ffa_priv_data.dev)
return -ENODEV;
I don't see the point with this funnel layer. It would in my opinion be easier to have one function pointer for each of the provided FF-A functions.
I wouldn't mind if the interface was a bit more like the interface provided in the Linux kernel, at least the parts we're going to support here. It would for sure help later when updating the OP-TEE driver to use this FF-A driver.
This has been addressed following the new interface design and alignment with the kernel driver. Please refer to v4 for the latest updates on that. ffa_bus_ops has been introduced and contains all the exported callbacks.
- switch (func_id) {
- case FFA_PARTITION_INFO_GET:
return ffa_get_partitions_info(func_data);
- case FFA_RXTX_UNMAP:
return ffa_unmap_rxtx_buffers();
- case FFA_MSG_SEND_DIRECT_REQ:
return ffa_msg_send_direct_req(func_data);
- default:
/* Undefined FF-A interface */
return -EINVAL;
- }
+}
+/**
- ffa_set_conduit - Set the conduit
- This boot time function clears the private data structure and sets the conduit
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+static int ffa_set_conduit(void) +{
- ffa_priv_data.conduit = FFA_CONDUIT_SMC;
- ffa_priv_data.invoke_ffa_fn = arm_ffa_smccc_smc;
- ffa_info("Conduit is SMC");
What if there's a hypervisor? Then HVC should be used instead.
We are only supporting SMC at this stage
- return FFA_ERR_STAT_SUCCESS;
+}
+/**
- ffa_probe - The driver probe function
- @dev: the arm_ffa device
- Probing is done at boot time and triggered by the uclass device discovery.
- At probe level the following actions are done:
- setting the conduit
- querying the FF-A framework version
- querying from secure world the u-boot endpoint ID
- querying from secure world the supported features of the specified FF-A calls
- mapping the RX/TX buffers
- querying from secure world all the partitions information
- All data queried from secure world is saved in the resident private data structure.
- The probe will fail if either FF-A framework is not detected or the
- FF-A requests are not behaving correctly. This ensures that the
- driver is not installed and its operations are not exported to the clients.
- However, once the driver is successfully probed and an FF-A anomaly is
- detected when clients invoke the driver operations, the driver cause
- u-boot to panic because the client would not know what to do in such conditions.
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+static int ffa_probe(struct udevice *dev) +{
- int ret;
- size_t buf_4k_pages = 0;
- ret = ffa_set_conduit();
- if (ret != FFA_ERR_STAT_SUCCESS)
return ret;
- ret = ffa_get_version();
- if (ret != FFA_ERR_STAT_SUCCESS)
return ret;
- ret = ffa_get_endpoint_id();
- if (ret != FFA_ERR_STAT_SUCCESS)
return ret;
- ret = ffa_get_rxtx_map_features();
- if (ret != FFA_ERR_STAT_SUCCESS)
return ret;
- ret = ffa_get_rxtx_buffers_pages_cnt(&buf_4k_pages);
- if (ret != FFA_ERR_STAT_SUCCESS)
return ret;
- ret = ffa_map_rxtx_buffers(buf_4k_pages);
This function only suceeds if compiled with EFI runtime support, so probing will always fail without EFI runtime support.
If compiled with EFI runtime support, how is this supposed to work with the kernel FF-A driver? The FF-A framework only supports one buffer pair per component and I don't see where the buffer pair is freed when U-Boot is done.
From patchset v3, FF-A core driver has been decoupled from EFI. It can
be compiled without EFI support. Please refer to v4 for the latest work on that. Freeing the RX/TX buffers is done in 2 locations.
1/ When RX/TX buffers are unmapped at ExitBootServices(). At EFI runtime RX/TX buffers created by u-boot do not exist anymore. 2/ When the arm_ffa device is removed (please see ffa_remove function)
- if (ret != FFA_ERR_STAT_SUCCESS)
return ret;
- ret = ffa_cache_partitions_info();
- if (ret != FFA_ERR_STAT_SUCCESS) {
ffa_free_rxtx_buffers(buf_4k_pages);
return ret;
- }
- return FFA_ERR_STAT_SUCCESS;
+}
+/**
- ffa_drv_ops - The driver operations runtime structure
- @invoke_func: The driver dispatcher
- */
+struct ffa_ops __ffa_runtime_data ffa_drv_ops = {
- .invoke_func = invoke_ffa_drv_api
+};
+/**
- ffa_device_get_ops - driver operations getter
- Return:
- This runtime function returns a pointer to the driver operations structure
- */
+const struct ffa_ops * __ffa_runtime ffa_device_get_ops(void) +{
- return &ffa_drv_ops;
+}
+/**
- Declaring the arm_ffa driver under UCLASS_FFA
- */
+U_BOOT_DRIVER(arm_ffa) = {
- .name = FFA_DRV_NAME,
- .id = UCLASS_FFA,
- .probe = ffa_probe,
+}; diff --git a/include/arm_ffa.h b/include/arm_ffa.h new file mode 100644 index 0000000000..98db01ee72 --- /dev/null +++ b/include/arm_ffa.h @@ -0,0 +1,190 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#ifndef __ARM_FFA_H +#define __ARM_FFA_H
+#include <linux/arm-smccc.h> +#include <linux/printk.h>
+/*
- This header is public. It can be used by clients to access
- data structures and definitions they need
- */
+/*
- Macros for displaying logs
- */
+#define ffa_info(fmt, ...) pr_info("[FFA] " fmt "\n", ##__VA_ARGS__) +#define ffa_err(fmt, ...) pr_err("[FFA] " fmt "\n", ##__VA_ARGS__)
+/*
- The driver operations success error code
- */
+#define FFA_ERR_STAT_SUCCESS (0)
+#if CONFIG_IS_ENABLED(EFI_LOADER)
+#include <efi_loader.h>
+/*
- __ffa_runtime_data and __ffa_runtime - controls whether data/code are
- available after calling the EFI ExitBootServices service.
- Data/code tagged with these keywords are resident (available at boot time and
- at runtime)
- */
+#define __ffa_runtime_data __efi_runtime_data +#define __ffa_runtime __efi_runtime
+#else
+#define __ffa_runtime_data +#define __ffa_runtime
+#endif
+/*
- 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_VERSION FFA_SMC_32(0x63) +#define FFA_ID_GET FFA_SMC_32(0x69) +#define FFA_FEATURES FFA_SMC_32(0x64) +#define FFA_PARTITION_INFO_GET FFA_SMC_32(0x68)
Too much indentation, same below
+#define FFA_RXTX_MAP FFA_SMC_32(0x66) +#define FFA_RXTX_UNMAP FFA_SMC_32(0x67) +#define FFA_RX_RELEASE FFA_SMC_32(0x65) +#define FFA_MSG_SEND_DIRECT_REQ FFA_SMC_32(0x6F) +#define FFA_MSG_SEND_DIRECT_RESP FFA_SMC_32(0x70) +#define FFA_RUN FFA_SMC_32(0x6D) +#define FFA_ERROR FFA_SMC_32(0x60) +#define FFA_SUCCESS FFA_SMC_32(0x61) +#define FFA_INTERRUPT FFA_SMC_32(0x62)
+/*
- 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 __packed 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_send_direct_data - Data structure hosting the data
used by FFA_MSG_SEND_DIRECT_{REQ,RESP}
- @a3-a7: Data read/written from/to w3-w7 registers
- Data structure containing the data to be sent by FFA_MSG_SEND_DIRECT_REQ
- or read from FFA_MSG_SEND_DIRECT_RESP
- */
+struct __packed ffa_send_direct_data {
- u32 a3; /* w3 */
- u32 a4; /* w4 */
- u32 a5; /* w5 */
- u32 a6; /* w6 */
- u32 a7; /* w7 */
+};
+#define FFA_MSG_SEND_DIRECT_MAX_SIZE (sizeof(struct ffa_send_direct_data))
+/* UUID data size */ +#define UUID_SIZE (16)
+/*
- union ffa_partition_uuid - Data union hosting the UUID
transmitted by FFA_PARTITION_INFO_GET
- @words: data structure giving 32-bit words access to the UUID data
- @bytes: data structure giving byte access to the UUID data
- The structure holds little-endian UUID data.
- */
+union ffa_partition_uuid {
- struct __packed words {
- u32 a1; /* w1 */
- u32 a2; /* w2 */
- u32 a3; /* w3 */
- u32 a4; /* w4 */
- } words;
- u8 bytes[UUID_SIZE];
+};
+/**
- struct ffa_interface_data - generic FF-A interface data structure used to exchange
data between user layers and the driver
- @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
- Using this structure user layers can pass various types of data with different sizes.
- The driver internal functions can detect the nature of this data, verfy compliance
- then execute the request when appropriate.
- */
+struct ffa_interface_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 */
+};
+/**
- struct ffa_ops - The driver operations structure
- @invoke_func: function pointer to the invoke function
- The data structure providing all the operations supported by the driver.
- This structure is resident.
- */
+struct ffa_ops {
- /* the driver dispatcher */
- int (*invoke_func)(u32 func_id, struct ffa_interface_data *func_data);
+};
+/**
- The device driver and the Uclass driver public functions
- */
+/**
- ffa_get_invoke_func - performs a call to the FF-A driver dispatcher
- */
+int __ffa_runtime ffa_get_invoke_func(u32 func_id,
struct ffa_interface_data *func_data);
+/**
- ffa_device_get_ops - driver operations getter
- */
+const struct ffa_ops * __ffa_runtime ffa_device_get_ops(void);
+/**
- ffa_get_device - create, bind and probe the arm_ffa device
- */
+int ffa_get_device(void);
+/**
- ffa_bus_discover - discover FF-A bus and probes the arm_ffa device
- */
+int ffa_bus_discover(void); +#endif diff --git a/include/arm_ffa_helper.h b/include/arm_ffa_helper.h new file mode 100644 index 0000000000..1cf68748f0 --- /dev/null +++ b/include/arm_ffa_helper.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#ifndef __ARM_FFA_HELPER_H +#define __ARM_FFA_HELPER_H
+#include <arm_ffa.h>
+/*
- This header is public. Including this header provides all data structures
- and definitions needed by clients to use the FF-A transport driver
- It also provides helper functions allowing to pass data and invoke FF-A functions
- */
+/**
- ffa_helper_get_partitions_info - Wrapper function for FFA_PARTITION_INFO_GET
- */
+int ffa_helper_get_partitions_info(struct ffa_interface_data *func_data);
+/**
- ffa_helper_unmap_rxtx_buffers - Wrapper function for FFA_RXTX_UNMAP
- */
+int ffa_helper_unmap_rxtx_buffers(void);
+/**
- ffa_helper_msg_send_direct_req - Wrapper function for
FFA_MSG_SEND_DIRECT_{REQ,RESP}
- */
+int __ffa_runtime ffa_helper_msg_send_direct_req(struct ffa_interface_data
*func_data);
+/**
- ffa_helper_bus_discover - Wrapper function for FF-A bus discovery
- */
+int ffa_helper_bus_discover(void);
+/**
- ffa_uuid_str_to_bin - Converts a big endian UUID string to a little endian buffer
- */
+int ffa_uuid_str_to_bin(const char *uuid_str, unsigned char *uuid_bin); +#endif diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 0e26e1d138..a1181b8f48 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -52,6 +52,7 @@ enum uclass_id { UCLASS_EFI_MEDIA, /* Devices provided by UEFI firmware */ UCLASS_ETH, /* Ethernet device */ UCLASS_ETH_PHY, /* Ethernet PHY device */
- UCLASS_FFA, /* Arm Firmware Framework for Armv8-A */ UCLASS_FIRMWARE, /* Firmware */ UCLASS_FS_FIRMWARE_LOADER, /* Generic loader */ UCLASS_GPIO, /* Bank of general-purpose I/O pins */
diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h index 7f2be23394..d58c7c104b 100644 --- a/include/linux/arm-smccc.h +++ b/include/linux/arm-smccc.h @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0 */ /*
- Copyright (c) 2015, Linaro Limited
- (C) Copyright 2022 ARM Limited
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#ifndef __LINUX_ARM_SMCCC_H #define __LINUX_ARM_SMCCC_H @@ -57,13 +59,17 @@ #include <linux/types.h> /**
- struct arm_smccc_res - Result from SMC/HVC call
- @a0-a3 result values from registers 0 to 3
*/
- @a0-a7 result values from registers 0 to 7
struct arm_smccc_res { unsigned long a0; unsigned long a1; unsigned long a2; unsigned long a3;
- unsigned long a4;
- unsigned long a5;
- unsigned long a6;
- unsigned long a7;
};
/** @@ -113,6 +119,26 @@ asmlinkage void __arm_smccc_hvc(unsigned long a0, unsigned long a1, unsigned long a5, unsigned long a6, unsigned long a7, struct arm_smccc_res *res, struct arm_smccc_quirk *quirk);
+#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) +/**
- __arm_ffa_smccc_smc() - make SMC calls used for FF-A transport
- @a0-a7: arguments passed in 64-bit registers x0 to x7
- @res: result values from 64-bit registers x0 to x7
- This function is used to make SMC calls following SMC32 Calling Convention.
- The content of the supplied parameters is copied to registers x0 to x7 prior
- to the SMC instruction. The SMC call return data is 32-bit data read from
- registers x0 tp x7.
- */
+asmlinkage void __arm_ffa_smccc_smc(unsigned long a0, unsigned long a1,
unsigned long a2, unsigned long a3, unsigned long a4,
unsigned long a5, unsigned long a6, unsigned long a7,
struct arm_smccc_res *res);
+#define arm_ffa_smccc_smc __arm_ffa_smccc_smc
+#endif
How about leaving the FF-A bit out of this and only have generic SMCCC support? That would make it easier to bring in updates from Linux without reinventing the wheel. I would also be nice to know which version of the SMCCC we're using.
Done through this patch: https://lore.kernel.org/all/20220926101723.9965-2-abdellatif.elkhlifi@arm.co...
Cheers, Jens
#define arm_smccc_smc(...) __arm_smccc_smc(__VA_ARGS__, NULL)
#define arm_smccc_smc_quirk(...) __arm_smccc_smc(__VA_ARGS__) diff --git a/lib/Kconfig b/lib/Kconfig index effe735365..9006e8d44c 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -845,6 +845,7 @@ config SMBIOS_PARSER source lib/efi/Kconfig source lib/efi_loader/Kconfig source lib/optee/Kconfig +source lib/arm-ffa/Kconfig
config TEST_FDTDEC bool "enable fdtdec test" diff --git a/lib/Makefile b/lib/Makefile index 13fe5fb7a4..36f67cd94e 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_EFI) += efi/ obj-$(CONFIG_EFI_LOADER) += efi_driver/ obj-$(CONFIG_EFI_LOADER) += efi_loader/ obj-$(CONFIG_CMD_BOOTEFI_SELFTEST) += efi_selftest/ +obj-$(CONFIG_ARM_FFA_TRANSPORT_HELPERS) += arm-ffa/ obj-$(CONFIG_LZMA) += lzma/ obj-$(CONFIG_BZIP2) += bzip2/ obj-$(CONFIG_TIZEN) += tizen/ diff --git a/lib/arm-ffa/Kconfig b/lib/arm-ffa/Kconfig new file mode 100644 index 0000000000..79acbc5a8f --- /dev/null +++ b/lib/arm-ffa/Kconfig @@ -0,0 +1,6 @@ +config ARM_FFA_TRANSPORT_HELPERS
- bool "Enable interface helpers for Arm Firmware Framework for Armv8-A"
- depends on ARM_FFA_TRANSPORT
- help
User layers call FF-A interfaces using helper functions which
pass the data and the FF-A function ID to the low level driver
diff --git a/lib/arm-ffa/Makefile b/lib/arm-ffa/Makefile new file mode 100644 index 0000000000..cba625fde4 --- /dev/null +++ b/lib/arm-ffa/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2022 Abdellatif El Khlifi abdellatif.elkhlifi@arm.com +#
+# This file only gets included when CONFIG_ARM_FFA_TRANSPORT_HELPERS is set
+obj-y += arm_ffa_helper.o diff --git a/lib/arm-ffa/arm_ffa_helper.c b/lib/arm-ffa/arm_ffa_helper.c new file mode 100644 index 0000000000..a291b000a7 --- /dev/null +++ b/lib/arm-ffa/arm_ffa_helper.c @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include <common.h> +#include <arm_ffa_helper.h> +#include <uuid.h>
+/**
- ffa_helper_get_partitions_info - Wrapper function for FFA_PARTITION_INFO_GET
- @func_data: Pointer to the FF-A function arguments container
structure.
The passed arguments:
Mode 1: When getting from the driver the number of
secure partitions:
@data0_size: UUID size
@data0: pointer to the UUID (little endian)
@data1_size: size of the number of partitions
variable
@data1: pointer to the number of partitions
variable. The variable will be set
by the driver
Mode 2: When requesting the driver to return the
partitions information:
@data0_size: UUID size
@data0: pointer to the UUID (little endian)
@data1_size: size of the SPs information buffer
@data1: pointer to SPs information buffer
(allocated by the client).
The buffer will be filled by the driver
- This is the boot time function used by clients who wants to get from secure
- world the partition(s) information.
- 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 client should use
- ffa_helper_get_partitions_info to pass the UUID information to the driver
- which uses PARTITION_INFO_GET to obtain the partition(s) information.
- ffa_helper_get_partitions_info should be called twice. First call is to get
- from the driver the number of secure partitions (SPs) associated to a
- particular UUID. Then, the caller (client) allocates the buffer to host the
- SPs data and issues a 2nd call. Then, the driver fills the SPs data in the
- pre-allocated buffer.
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+int ffa_helper_get_partitions_info(struct ffa_interface_data *func_data) +{
- return ffa_get_invoke_func(FFA_PARTITION_INFO_GET, func_data);
+}
+/**
- ffa_helper_unmap_rxtx_buffers - Wrapper function for FFA_RXTX_UNMAP
- This is the boot time function that allows clients to unmap the RX/TX
- buffers
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+int ffa_helper_unmap_rxtx_buffers(void) +{
- return ffa_get_invoke_func(FFA_RXTX_UNMAP, NULL);
+}
+/**
- ffa_helper_msg_send_direct_req - Wrapper function for
FFA_MSG_SEND_DIRECT_{REQ,RESP}
- @func_data: Pointer to the FF-A function arguments container structure.
- The passed arguments:
- @data0_size: partition ID size
- @data0: pointer to the partition ID
- @data1_size: exchanged data size
- @data1: pointer to the data buffer preallocated by the client (in/out)
- This is the runtime function that allows clients to send data to the secure
- world partitions. The arm_ffa driver uses FFA_MSG_SEND_DIRECT_REQ to send the
- data to the secure partition. The response from the secure partition is
- handled internally by the driver using FFA_MSG_SEND_DIRECT_RESP and returned
- to ffa_helper_msg_send_direct_req through @func_data
- The maximum size of the data that can be exchanged is 20 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}
- The client should pre-allocate a buffer pointed by @data1 which the size
- is sizeof(struct ffa_send_direct_data)
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+int __ffa_runtime ffa_helper_msg_send_direct_req(struct ffa_interface_data
*func_data)
+{
- return ffa_get_invoke_func(FFA_MSG_SEND_DIRECT_REQ, func_data);
+}
+/**
- ffa_helper_bus_discover - Wrapper function for FF-A bus discovery
- This boot time function should be called to discover the FF-A bus and
- probe the FF-A device.
- To achieve that, this function is called automatically at initcalls
- level (after u-boot relocation).
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+int ffa_helper_bus_discover(void) +{
- return ffa_bus_discover();
+}
+/**
- ffa_uuid_str_to_bin - Converts a big endian UUID string to a little endian buffer
- @uuid_str: UUID string in big endian format (36 bytes wide + '/0')
- @uuid_bin: preallocated 16 bytes UUID buffer in little endian format
- UUID binary format used by the FF-A framework (16 bytes):
- [LSB] 4B-2B-2B-2B-6B (little endian data fields)
- UUID string is 36 length of characters (36 bytes):
- xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
- be be be be be
- where x is a hexadecimal character. Fields are separated by '-'s.
- When converting to a binary UUID, these endianness rules apply:
be: means the field in the string is considered a big endian hex number
and should be converted to little endian binary format
- Return:
- uuid_bin filled with little endian UUID data
- On success 0 is returned. Otherwise, failure code.
- */
+int ffa_uuid_str_to_bin(const char *uuid_str, unsigned char *uuid_bin) +{
- u16 tmp16 = 0;
- u32 tmp32 = 0;
- u64 tmp64 = 0;
- if (!uuid_str_valid(uuid_str) || !uuid_bin)
return -EINVAL;
- /*
* reverse bytes from big to little endian
*/
- tmp32 = simple_strtoul(uuid_str, NULL, 16);
- memcpy(uuid_bin, &tmp32, 4);
- /*
* reverse bytes from big to little endian
*/
- tmp16 = simple_strtoul(uuid_str + 9, NULL, 16);
- memcpy(uuid_bin + 4, &tmp16, 2);
- /*
* reverse bytes from big to little endian
*/
- tmp16 = simple_strtoul(uuid_str + 14, NULL, 16);
- memcpy(uuid_bin + 6, &tmp16, 2);
- /*
* reverse bytes from big to little endian
*/
- tmp16 = simple_strtoul(uuid_str + 19, NULL, 16);
- memcpy(uuid_bin + 8, &tmp16, 2);
- /*
* reverse bytes from big to little endian
*/
- tmp64 = simple_strtoull(uuid_str + 24, NULL, 16);
- memcpy(uuid_bin + 10, (char *)&tmp64, 6);
- return 0;
+} diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 5bcb8253ed..cffa2c69d6 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -23,6 +23,10 @@ #include <asm/setjmp.h> #include <linux/libfdt_env.h>
+#if defined(CONFIG_ARM_FFA_TRANSPORT) +#include <arm_ffa_helper.h> +#endif
DECLARE_GLOBAL_DATA_PTR;
/* Task priority level */ @@ -2114,6 +2118,10 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, struct efi_event *evt, *next_event; efi_status_t ret = EFI_SUCCESS;
+#if defined(CONFIG_ARM_FFA_TRANSPORT)
- int ffa_ret;
+#endif
EFI_ENTRY("%p, %zx", image_handle, map_key);
/* Check that the caller has read the current memory map */
@@ -2174,6 +2182,15 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL); }
+#if defined(CONFIG_ARM_FFA_TRANSPORT)
- /* unmap FF-A RX/TX buffers */
- ffa_ret = ffa_helper_unmap_rxtx_buffers();
- if (ffa_ret)
debug("[efi_boottime][ERROR]: can not unmap FF-A RX/TX buffers\n");
- else
debug("[efi_boottime][INFO]: FF-A RX/TX buffers unmapped\n");
+#endif
- /* Patch out unsupported runtime function */ efi_runtime_detach();
-- 2.17.1

From: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
Provide armffa command showcasing the use of the FF-A driver
The armffa command allows to query secure partitions data from the secure world and exchanging messages with the partitions.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com --- MAINTAINERS | 1 + cmd/Kconfig | 10 ++ cmd/Makefile | 2 + cmd/armffa.c | 266 ++++++++++++++++++++++++++++++++++++++++ drivers/arm-ffa/Kconfig | 1 + 5 files changed, 280 insertions(+) create mode 100644 cmd/armffa.c
diff --git a/MAINTAINERS b/MAINTAINERS index efa17206b8..ca2e13b9ec 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -235,6 +235,7 @@ F: include/configs/turris_*.h ARM FF-A M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com S: Maintained +F: cmd/armffa.c F: drivers/arm-ffa/ F: include/arm_ffa.h F: include/arm_ffa_helper.h diff --git a/cmd/Kconfig b/cmd/Kconfig index 516c6bbe2d..0d16e8cb14 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -823,6 +823,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 Arm FF-A driver + 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 ede634d731..2905ce63cf 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -12,6 +12,8 @@ obj-y += panic.o obj-y += version.o
# command + +obj-$(CONFIG_CMD_ARMFFA) += armffa.o obj-$(CONFIG_CMD_ACPI) += acpi.o obj-$(CONFIG_CMD_ADDRMAP) += addrmap.o obj-$(CONFIG_CMD_AES) += aes.o diff --git a/cmd/armffa.c b/cmd/armffa.c new file mode 100644 index 0000000000..4b74bf5f31 --- /dev/null +++ b/cmd/armffa.c @@ -0,0 +1,266 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <arm_ffa_helper.h> +#include <asm/io.h> +#include <common.h> +#include <command.h> +#include <dm.h> +#include <mapmem.h> +#include <stdlib.h> + +/** + * do_ffa_get_singular_partition_info - implementation of the getpart subcommand + * @cmdtp: Command Table + * @flag: flags + * @argc: number of arguments + * @argv: arguments + * + * This function queries the secure partition information which the UUID is provided + * as an argument. The function uses the arm_ffa driver helper function + * 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_get_singular_partition_info(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct ffa_interface_data func_data = {0}; + u32 count = 0; + int ret; + union ffa_partition_uuid service_uuid = {0}; + struct ffa_partition_info *parts_info; + u32 info_idx; + + if (argc != 1) + return -EINVAL; + + if (ffa_uuid_str_to_bin(argv[0], (unsigned char *)&service_uuid)) { + ffa_err("Invalid UUID"); + return -EINVAL; + } + + /* + * get from the driver the count of the SPs matching the UUID + */ + func_data.data0_size = sizeof(service_uuid); + func_data.data0 = &service_uuid; + func_data.data1_size = sizeof(count); + func_data.data1 = &count; + + ret = ffa_helper_get_partitions_info(&func_data); + if (ret != FFA_ERR_STAT_SUCCESS) { + ffa_err("Failure in querying partitions count (error code: %d)", ret); + return ret; + } + + if (!count) { + ffa_info("No secure partition found"); + return ret; + } + + /* + * pre-allocate a buffer to be filled by the driver + * with ffa_partition_info structs + */ + + parts_info = calloc(count, sizeof(struct ffa_partition_info)); + if (!parts_info) + return -EINVAL; + + ffa_info("Pre-allocating %d partition(s) info structures", count); + + func_data.data1_size = count * sizeof(struct ffa_partition_info); + func_data.data1 = parts_info; + + /* + * ask the driver to fill the buffer with the SPs info + */ + ret = ffa_helper_get_partitions_info(&func_data); + if (ret != FFA_ERR_STAT_SUCCESS) { + ffa_err("Failure in querying partition(s) info (error code: %d)", ret); + free(parts_info); + return ret; + } + + /* + * SPs found , show the partition information + */ + for (info_idx = 0; info_idx < count ; info_idx++) { + ffa_info("Partition: id = 0x%x , exec_ctxt 0x%x , properties 0x%x", + parts_info[info_idx].id, + parts_info[info_idx].exec_ctxt, + parts_info[info_idx].properties); + } + + free(parts_info); + + return 0; +} + +/** + * do_ffa_msg_send_direct_req - implementation of the ping subcommand + * @cmdtp: Command Table + * @flag: flags + * @argc: number of arguments + * @argv: arguments + * + * This function sends data to the secure partition which the ID is provided + * as an argument. The function uses the arm_ffa driver helper function + * to send data. + * + * Return: + * + * CMD_RET_SUCCESS: on success, otherwise failure + */ +int do_ffa_msg_send_direct_req(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct ffa_interface_data func_data = {0}; + struct ffa_send_direct_data msg = {0}; + u32 pattern = 0xaabbccd0; + u16 part_id; + int ret; + + if (argc != 1) + return -EINVAL; + + errno = 0; + part_id = strtoul(argv[0], NULL, 16); + + if (errno) { + ffa_err("Invalid partition ID"); + return -EINVAL; + } + + /* + * telling the driver which partition to use + */ + func_data.data0_size = sizeof(part_id); + func_data.data0 = &part_id; + + /* + * filling the message data + */ + msg.a3 = ++pattern; + msg.a4 = ++pattern; + msg.a5 = ++pattern; + msg.a6 = ++pattern; + msg.a7 = ++pattern; + func_data.data1_size = sizeof(msg); + func_data.data1 = &msg; + + ret = ffa_helper_msg_send_direct_req(&func_data); + if (ret == FFA_ERR_STAT_SUCCESS) { + u8 cnt; + + ffa_info("SP response:\n[LSB]"); + for (cnt = 0; + cnt < sizeof(struct ffa_send_direct_data) / sizeof(u32); + cnt++) + ffa_info("0x%x", ((u32 *)&msg)[cnt]); + } else { + ffa_err("Sending direct request error (%d)", ret); + } + + return ret; +} + +/** + *do_ffa_dev_list - implementation of the devlist subcommand + * @cmdtp: [in] Command Table + * @flag: flags + * @argc: number of arguments + * @argv: arguments + * + * This function queries the devices belonging to the UCLASS_FFA + * class. Currently, one device is expected to show up: the arm_ffa device + * + * Return: + * + * CMD_RET_SUCCESS: on success, otherwise failure + */ +int do_ffa_dev_list(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct udevice *dev = NULL; + int i, ret; + + ffa_info("arm_ffa uclass entries:"); + + for (i = 0, ret = uclass_first_device(UCLASS_FFA, &dev); + dev; + ret = uclass_next_device(&dev), i++) { + if (ret) + break; + + ffa_info("entry %d - instance %08x, ops %08x, plat %08x", + i, + (u32)map_to_sysmem(dev), + (u32)map_to_sysmem(dev->driver->ops), + (u32)map_to_sysmem(dev_get_plat(dev))); + } + + return cmd_process_error(cmdtp, ret); +} + +static struct cmd_tbl armffa_commands[] = { + U_BOOT_CMD_MKENT(getpart, 1, 1, do_ffa_get_singular_partition_info, "", ""), + U_BOOT_CMD_MKENT(ping, 1, 1, do_ffa_msg_send_direct_req, "", ""), + U_BOOT_CMD_MKENT(devlist, 0, 1, do_ffa_dev_list, "", ""), +}; + +/** + * do_armffa - the armffa command main function + * @cmdtp: Command Table + * @flag: flags + * @argc: number of arguments + * @argv: arguments + * + * This function identifies which armffa subcommand to run. + * Then, it makes sure the arm_ffa device is probed and + * ready for use. + * Then, it runs the subcommand. + * + * Return: + * + * CMD_RET_SUCCESS: on success, otherwise failure + */ +static int do_armffa(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct cmd_tbl *armffa_cmd; + int ret; + + if (argc < 2) + return CMD_RET_USAGE; + + armffa_cmd = find_cmd_tbl(argv[1], armffa_commands, ARRAY_SIZE(armffa_commands)); + + argc -= 2; + argv += 2; + + if (!armffa_cmd || argc > armffa_cmd->maxargs) + return CMD_RET_USAGE; + + ret = ffa_helper_bus_discover(); + if (ret != FFA_ERR_STAT_SUCCESS) + return cmd_process_error(cmdtp, ret); + + ret = armffa_cmd->cmd(armffa_cmd, flag, argc, argv); + + return cmd_process_error(armffa_cmd, ret); +} + +U_BOOT_CMD(armffa, 4, 1, do_armffa, + "Arm FF-A operations test command", + "getpart <partition UUID>\n" + " - lists the partition(s) info\n" + "ping <partition ID>\n" + " - sends a data pattern to the specified partition\n" + "devlist\n" + " - displays the arm_ffa device info\n"); diff --git a/drivers/arm-ffa/Kconfig b/drivers/arm-ffa/Kconfig index 23815534c4..fa2b2fecfb 100644 --- a/drivers/arm-ffa/Kconfig +++ b/drivers/arm-ffa/Kconfig @@ -4,6 +4,7 @@ config ARM_FFA_TRANSPORT bool "Enable Arm Firmware Framework for Armv8-A driver" depends on DM && ARM64 select ARM_SMCCC if ARM64 + select CMD_ARMFFA select LIB_UUID select ARM_FFA_TRANSPORT_HELPERS help

From: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
Provide a Sandbox driver to emulate the FF-A ABIs
The emulated ABIs are those supported by the FF-A core driver and according to FF-A specification v1.0.
The Sandbox driver provides operations allowing the test application to read the status of all the inspected ABIs and perform functional tests based on that.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com --- MAINTAINERS | 2 + common/board_r.c | 2 +- configs/sandbox64_defconfig | 2 + configs/sandbox_defconfig | 2 + doc/arch/sandbox.rst | 1 + drivers/arm-ffa/Kconfig | 8 +- drivers/arm-ffa/Makefile | 1 + drivers/arm-ffa/arm-ffa-uclass.c | 47 +- drivers/arm-ffa/core.c | 97 ++-- drivers/arm-ffa/sandbox.c | 669 ++++++++++++++++++++++++++ drivers/arm-ffa/sandbox_arm_ffa_prv.h | 131 +++++ include/arm_ffa.h | 9 +- include/sandbox_arm_ffa.h | 31 ++ include/sandbox_arm_ffa_helper.h | 26 + lib/arm-ffa/Makefile | 1 + lib/arm-ffa/arm_ffa_helper.c | 2 +- lib/arm-ffa/sandbox_arm_ffa_helper.c | 23 + lib/efi_loader/efi_boottime.c | 4 +- 18 files changed, 1017 insertions(+), 41 deletions(-) create mode 100644 drivers/arm-ffa/sandbox.c create mode 100644 drivers/arm-ffa/sandbox_arm_ffa_prv.h create mode 100644 include/sandbox_arm_ffa.h create mode 100644 include/sandbox_arm_ffa_helper.h create mode 100644 lib/arm-ffa/sandbox_arm_ffa_helper.c
diff --git a/MAINTAINERS b/MAINTAINERS index ca2e13b9ec..7c439ed1fd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -239,6 +239,8 @@ F: cmd/armffa.c F: drivers/arm-ffa/ F: include/arm_ffa.h F: include/arm_ffa_helper.h +F: include/sandbox_arm_ffa.h +F: include/sandbox_arm_ffa_helper.h F: lib/arm-ffa/
ARM FREESCALE IMX diff --git a/common/board_r.c b/common/board_r.c index bb5f1d0aa6..a03b5254b7 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -775,7 +775,7 @@ static init_fnc_t init_sequence_r[] = { INIT_FUNC_WATCHDOG_RESET initr_net, #endif -#ifdef CONFIG_ARM_FFA_TRANSPORT +#if defined(CONFIG_ARM_FFA_TRANSPORT) && !defined(CONFIG_SANDBOX_FFA) ffa_helper_bus_discover, #endif #ifdef CONFIG_POST diff --git a/configs/sandbox64_defconfig b/configs/sandbox64_defconfig index 4fbe148074..d6b3a137e3 100644 --- a/configs/sandbox64_defconfig +++ b/configs/sandbox64_defconfig @@ -252,3 +252,5 @@ CONFIG_TEST_FDTDEC=y CONFIG_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y +CONFIG_ARM_FFA_TRANSPORT=y +CONFIG_SANDBOX_FFA=y \ No newline at end of file diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 1826cf0195..161ff221bf 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -326,3 +326,5 @@ CONFIG_TEST_FDTDEC=y CONFIG_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y +CONFIG_ARM_FFA_TRANSPORT=y +CONFIG_SANDBOX_FFA=y \ No newline at end of file diff --git a/doc/arch/sandbox.rst b/doc/arch/sandbox.rst index f8804e1f41..7cb5ea307c 100644 --- a/doc/arch/sandbox.rst +++ b/doc/arch/sandbox.rst @@ -203,6 +203,7 @@ Supported Drivers
U-Boot sandbox supports these emulations:
+- Arm FF-A - Block devices - Chrome OS EC - GPIO diff --git a/drivers/arm-ffa/Kconfig b/drivers/arm-ffa/Kconfig index fa2b2fecfb..5774ae6496 100644 --- a/drivers/arm-ffa/Kconfig +++ b/drivers/arm-ffa/Kconfig @@ -2,7 +2,7 @@
config ARM_FFA_TRANSPORT bool "Enable Arm Firmware Framework for Armv8-A driver" - depends on DM && ARM64 + depends on DM && (ARM64 || SANDBOX) select ARM_SMCCC if ARM64 select CMD_ARMFFA select LIB_UUID @@ -26,3 +26,9 @@ config ARM_FFA_TRANSPORT 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. + +config SANDBOX_FFA + bool "FF-A Sandbox driver" + depends on ARM_FFA_TRANSPORT && SANDBOX + help + This emulates the FF-A handling under Sandbox and allows to test the FF-A driver diff --git a/drivers/arm-ffa/Makefile b/drivers/arm-ffa/Makefile index 7bc9a336a9..df1cfe6ef0 100644 --- a/drivers/arm-ffa/Makefile +++ b/drivers/arm-ffa/Makefile @@ -4,3 +4,4 @@ #
obj-y += arm-ffa-uclass.o core.o +obj-$(CONFIG_SANDBOX_FFA) += sandbox.o diff --git a/drivers/arm-ffa/arm-ffa-uclass.c b/drivers/arm-ffa/arm-ffa-uclass.c index 2439f87586..418aa06dd6 100644 --- a/drivers/arm-ffa/arm-ffa-uclass.c +++ b/drivers/arm-ffa/arm-ffa-uclass.c @@ -11,6 +11,10 @@ #include <log.h> #include <asm/global_data.h>
+#if CONFIG_IS_ENABLED(SANDBOX_FFA) +#include <sandbox_arm_ffa.h> +#endif + DECLARE_GLOBAL_DATA_PTR;
UCLASS_DRIVER(ffa) = { @@ -42,16 +46,42 @@ int __ffa_runtime ffa_get_invoke_func(u32 func_id, struct ffa_interface_data *fu return ffa_device_get_ops()->invoke_func(func_id, func_data); }
+#if CONFIG_IS_ENABLED(SANDBOX_FFA) + +/** + * sandbox_ffa_get_invoke_func - performs a call to the Sandbox FF-A driver dispatcher + * @func_id: The FF-A function to be queried + * @func_data: Pointer to the query arguments + * container structure. + * + * This function passes the FF-A function ID to be queried during sandbox test cases + * and its arguments to the Sandbox FF-A driver dispatcher. + * This function is called by the Sandbox FF-A helper function. + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +int sandbox_ffa_get_invoke_func(u32 func_id, struct ffa_interface_data *func_data) +{ + if (!sandbox_ffa_device_get_ops()->invoke_func) + return -EINVAL; + + return sandbox_ffa_device_get_ops()->invoke_func(func_id, func_data); +} +#endif + /** - * ffa_bus_discover - discover FF-A bus and probe the arm_ffa device + * ffa_bus_discover - discover FF-A bus and probe arm_ffa and sandbox_arm_ffa devices * * This boot time function makes sure the FF-A bus is discoverable. - * Then, the arm_ffa device is probed and ready to use. + * Then, the arm_ffa and sandbox_arm_ffa devices are ready to use. + * * This function is called automatically at initcalls * level (after u-boot relocation). * * Arm FF-A transport is implemented through arm_ffa u-boot device managing the FF-A - * communication. + * communication. In Sandbox mode sandbox_arm_ffa is used to test arm_ffa driver. * All FF-A clients should use the arm_ffa device to use the FF-A transport. * * Return: @@ -60,5 +90,14 @@ int __ffa_runtime ffa_get_invoke_func(u32 func_id, struct ffa_interface_data *fu */ int ffa_bus_discover(void) { - return ffa_get_device(); + int ret; + + ret = ffa_get_device(); + +#if CONFIG_IS_ENABLED(SANDBOX_FFA) + if (ret == FFA_ERR_STAT_SUCCESS) + ret = sandbox_ffa_get_device(); +#endif + + return ret; } diff --git a/drivers/arm-ffa/core.c b/drivers/arm-ffa/core.c index 09e4eb753a..246abc1473 100644 --- a/drivers/arm-ffa/core.c +++ b/drivers/arm-ffa/core.c @@ -5,6 +5,11 @@ */
#include "arm_ffa_prv.h" + +#if CONFIG_IS_ENABLED(SANDBOX_FFA) +#include "sandbox_arm_ffa_prv.h" +#endif + #include <asm/global_data.h> #include <asm/io.h> #include <common.h> @@ -88,8 +93,10 @@ static int ffa_get_version(void)
FFA_DECLARE_ARGS;
- if (!ffa_priv_data.invoke_ffa_fn) - panic("[FFA] no private data found\n"); + if (!ffa_priv_data.invoke_ffa_fn) { + ffa_panic("no private data found\n"); + return -ENODEV; + }
a0 = FFA_VERSION; a1 = FFA_VERSION_1_0; @@ -132,8 +139,10 @@ static int ffa_get_endpoint_id(void) { FFA_DECLARE_ARGS;
- if (!ffa_priv_data.invoke_ffa_fn) - panic("[FFA] no private data found\n"); + if (!ffa_priv_data.invoke_ffa_fn) { + ffa_panic("no private data found\n"); + return -ENODEV; + }
a0 = FFA_ID_GET;
@@ -206,8 +215,10 @@ static int ffa_get_rxtx_map_features(void) { FFA_DECLARE_ARGS;
- if (!ffa_priv_data.invoke_ffa_fn) - panic("[FFA] no private data found\n"); + if (!ffa_priv_data.invoke_ffa_fn) { + ffa_panic("no private data found\n"); + return -ENODEV; + }
a0 = FFA_FEATURES; a1 = FFA_RXTX_MAP; @@ -433,8 +444,10 @@ static int ffa_map_rxtx_buffers(size_t buf_4k_pages)
FFA_DECLARE_ARGS;
- if (!ffa_priv_data.invoke_ffa_fn) - panic("[FFA] no private data found\n"); + if (!ffa_priv_data.invoke_ffa_fn) { + ffa_panic("no private data found\n"); + return -ENODEV; + }
ret = ffa_alloc_rxtx_buffers(buf_4k_pages); if (ret != FFA_ERR_STAT_SUCCESS) @@ -502,8 +515,10 @@ static int ffa_unmap_rxtx_buffers(void) { FFA_DECLARE_ARGS;
- if (!ffa_priv_data.invoke_ffa_fn) - panic("[FFA] no private data found\n"); + if (!ffa_priv_data.invoke_ffa_fn) { + ffa_panic("no private data found\n"); + return -ENODEV; + }
a0 = FFA_RXTX_UNMAP; a1 = PREP_SELF_ENDPOINT_ID(ffa_priv_data.id); @@ -514,11 +529,12 @@ static int ffa_unmap_rxtx_buffers(void) case FFA_ERROR: { if (((int)res.a2) == FFA_ERR_STAT_NOT_SUPPORTED) - panic("[FFA] FFA_RXTX_UNMAP is not implemented at this FF-A instance\n"); + ffa_panic("FFA_RXTX_UNMAP is not implemented at this FF-A instance\n"); else if (((int)res.a2) == FFA_ERR_STAT_INVALID_PARAMETERS) - panic("[FFA] There is no buffer pair registered on behalf of the caller\n"); + ffa_panic("There is no buffer pair registered on behalf of the caller\n"); else - panic("[FFA] Undefined error (%d)\n", ((int)res.a2)); + ffa_panic("Undefined error (%d)\n", ((int)res.a2)); + return -ENODEV; } case FFA_SUCCESS: { @@ -526,19 +542,24 @@ static int ffa_unmap_rxtx_buffers(void) int ret;
ret = ffa_get_rxtx_buffers_pages_cnt(&buf_4k_pages); - if (ret != FFA_ERR_STAT_SUCCESS) - panic("[FFA] RX/TX buffers unmapped but failure in getting pages count\n"); + if (ret != FFA_ERR_STAT_SUCCESS) { + ffa_panic("RX/TX buffers unmapped but failure in getting pages count\n"); + return -ENODEV; + }
ret = ffa_free_rxtx_buffers(buf_4k_pages); - if (ret != FFA_ERR_STAT_SUCCESS) - panic("[FFA] RX/TX buffers unmapped but failure in freeing the memory\n"); + if (ret != FFA_ERR_STAT_SUCCESS) { + ffa_panic("RX/TX buffers unmapped but failure in freeing the memory\n"); + return -ENODEV; + }
ffa_info("RX/TX buffers unmapped and memory freed");
return FFA_ERR_STAT_SUCCESS; } default: - panic("[FFA] Undefined response function (0x%lx)", res.a0); + ffa_panic("Undefined response function (0x%lx)", res.a0); + return -ENODEV; } }
@@ -556,8 +577,10 @@ static int ffa_release_rx_buffer(void) { FFA_DECLARE_ARGS;
- if (!ffa_priv_data.invoke_ffa_fn) - panic("[FFA] no private data found\n"); + if (!ffa_priv_data.invoke_ffa_fn) { + ffa_panic("no private data found\n"); + return -ENODEV; + }
a0 = FFA_RX_RELEASE;
@@ -567,17 +590,19 @@ static int ffa_release_rx_buffer(void) case FFA_ERROR: { if (((int)res.a2) == FFA_ERR_STAT_NOT_SUPPORTED) - panic("[FFA] FFA_RX_RELEASE is not implemented at this FF-A instance\n"); + ffa_panic("FFA_RX_RELEASE is not implemented at this FF-A instance\n"); else if (((int)res.a2) == FFA_ERR_STAT_DENIED) - panic("[FFA] Caller did not have ownership of the RX buffer\n"); + ffa_panic("Caller did not have ownership of the RX buffer\n"); else - panic("[FFA] Undefined error (%d)\n", ((int)res.a2)); + ffa_panic("Undefined error (%d)\n", ((int)res.a2)); + return -ENODEV; } case FFA_SUCCESS: return FFA_ERR_STAT_SUCCESS;
default: - panic("[FFA] Undefined response function (0x%lx)\n", res.a0); + ffa_panic("Undefined response function (0x%lx)\n", res.a0); + return -ENODEV; } }
@@ -769,8 +794,10 @@ static int ffa_query_partitions_info(union ffa_partition_uuid *part_uuid, unsigned long a7 = 0; struct arm_smccc_res res = {0};
- if (!ffa_priv_data.invoke_ffa_fn) - panic("[FFA] no private data found\n"); + if (!ffa_priv_data.invoke_ffa_fn) { + ffa_panic("no private data found\n"); + return -ENODEV; + }
a0 = FFA_PARTITION_INFO_GET;
@@ -846,7 +873,7 @@ static int ffa_query_partitions_info(union ffa_partition_uuid *part_uuid, ret = ffa_release_rx_buffer();
if (!part_uuid && !res.a2) { - ffa_err("[FFA] no partition installed in the system"); + ffa_err("no partition installed in the system"); return -ENODEV; }
@@ -937,8 +964,10 @@ static int ffa_get_partitions_info(struct ffa_interface_data *func_data) return -EINVAL; }
- if (!ffa_priv_data.partitions.count || !ffa_priv_data.partitions.descs) - panic("[FFA] No partition installed\n"); + if (!ffa_priv_data.partitions.count || !ffa_priv_data.partitions.descs) { + ffa_panic("No partition installed\n"); + return -ENODEV; + }
if (func_data->data0_size == sizeof(union ffa_partition_uuid) && func_data->data0 && @@ -1176,6 +1205,7 @@ static int __ffa_runtime ffa_msg_send_direct_req(struct ffa_interface_data /* Undefined error */ return -ENXIO; } + return -ENODEV; } case FFA_SUCCESS:
@@ -1238,7 +1268,7 @@ int __ffa_runtime invoke_ffa_drv_api(u32 func_id, /** * ffa_set_conduit - Set the conduit * - * This boot time function clears the private data structure and sets the conduit + * This boot time function sets the conduit * * Return: * @@ -1248,7 +1278,12 @@ static int ffa_set_conduit(void) { ffa_priv_data.conduit = FFA_CONDUIT_SMC;
- ffa_priv_data.invoke_ffa_fn = arm_ffa_smccc_smc; +#if CONFIG_IS_ENABLED(SANDBOX_FFA) + ffa_priv_data.invoke_ffa_fn = sandbox_arm_ffa_smccc_smc; + ffa_info("Using SMC emulation"); +#else + ffa_priv_data.invoke_ffa_fn = arm_ffa_smccc_smc; +#endif
ffa_info("Conduit is SMC");
diff --git a/drivers/arm-ffa/sandbox.c b/drivers/arm-ffa/sandbox.c new file mode 100644 index 0000000000..d4a953fc3b --- /dev/null +++ b/drivers/arm-ffa/sandbox.c @@ -0,0 +1,669 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include "sandbox_arm_ffa_prv.h" +#include <asm/global_data.h> +#include <common.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> +#include <mapmem.h> +#include <string.h> + +DECLARE_GLOBAL_DATA_PTR; + +/** + * The device private data structure containing all the emulated secure world data + */ +static struct sandbox_ffa_prvdata sandbox_ffa_priv_data = {0}; + +/* The partitions (SPs) table */ +static struct ffa_partition_desc sandbox_partitions[SANDBOX_PARTITIONS_CNT] = { + { + .info = { .id = 0x1245, .exec_ctxt = 0x5687, .properties = 0x89325621 }, + .UUID = { .bytes = {SANDBOX_SERVICE1_UUID_DATA}} + }, + { + .info = { .id = 0x9836, .exec_ctxt = 0x9587, .properties = 0x45325621 }, + .UUID = { .bytes = {SANDBOX_SERVICE2_UUID_DATA}} + }, + { + .info = { .id = 0x6452, .exec_ctxt = 0x7687, .properties = 0x23325621 }, + .UUID = { .bytes = {SANDBOX_SERVICE1_UUID_DATA}} + }, + { + .info = { .id = 0x7814, .exec_ctxt = 0x1487, .properties = 0x70325621 }, + .UUID = { .bytes = {SANDBOX_SERVICE2_UUID_DATA}} + } + +}; + +/* + * Driver functions + */ + +/** + * sandbox_ffa_get_device - probes the sandbox_arm_ffa device + * + * This function makes sure the sandbox_arm_ffa device is probed + * This function makes sure the sandbox_arm_ffa device is + * created, bound to this driver, probed and ready to use. + * + * sandbox_arm_ffa depends on arm_ffa device. This dependency is + * handled by ffa_bus_discover function. arm_ffa is probed first then + * sandbox_arm_ffa. + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +int sandbox_ffa_get_device(void) +{ + int ret; + + if (sandbox_ffa_priv_data.dev) + return FFA_ERR_STAT_SUCCESS; + + ret = device_bind(dm_root(), + DM_DRIVER_GET(sandbox_arm_ffa), + FFA_SANDBOX_DRV_NAME, + NULL, + ofnode_null(), + &sandbox_ffa_priv_data.dev); + if (ret) { + sandbox_ffa_priv_data.dev = NULL; + return ret; + } + + ret = device_probe(sandbox_ffa_priv_data.dev); + if (ret) { + ffa_err("[Sandbox] can not probe the device"); + device_unbind(sandbox_ffa_priv_data.dev); + sandbox_ffa_priv_data.dev = NULL; + return ret; + } + + return FFA_ERR_STAT_SUCCESS; +} + +/** + * sandbox_ffa_version - Emulated FFA_VERSION handler function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_VERSION FF-A function. + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_version) +{ + sandbox_ffa_priv_data.fwk_version = FFA_VERSION_1_0; + res->a0 = sandbox_ffa_priv_data.fwk_version; + + /* w1-w7 MBZ */ + memset(FFA_W1W7_MBZ_REG_START, 0, FFA_W1W7_MBZ_CNT * sizeof(unsigned long)); + + return FFA_ERR_STAT_SUCCESS; +} + +/** + * sandbox_ffa_id_get - Emulated FFA_ID_GET handler function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_ID_GET FF-A function. + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_id_get) +{ + res->a0 = FFA_SUCCESS; + res->a1 = 0; + + sandbox_ffa_priv_data.id = NS_PHYS_ENDPOINT_ID; + res->a2 = sandbox_ffa_priv_data.id; + + /* w3-w7 MBZ */ + memset(FFA_W3_MBZ_REG_START, 0, FFA_W3W7_MBZ_CNT * sizeof(unsigned long)); + + return FFA_ERR_STAT_SUCCESS; +} + +/** + * sandbox_ffa_features - Emulated FFA_FEATURES handler function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_FEATURES FF-A function. + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_features) +{ + switch (a1) { + case FFA_RXTX_MAP: + { + res->a0 = FFA_SUCCESS; + res->a2 = RXTX_BUFFERS_MIN_SIZE; + res->a3 = 0; + /* w4-w7 MBZ */ + memset(FFA_W4W7_MBZ_REG_START, + 0, FFA_W4W7_MBZ_CNT * sizeof(unsigned long)); + break; + } + default: + { + res->a0 = FFA_ERROR; + res->a2 = FFA_ERR_STAT_NOT_SUPPORTED; + /* w3-w7 MBZ */ + memset(FFA_W3_MBZ_REG_START, + 0, FFA_W3W7_MBZ_CNT * sizeof(unsigned long)); + ffa_err("[Sandbox] FF-A interface 0x%lx not implemented", a1); + } + } + + res->a1 = 0; + + return FFA_ERR_STAT_SUCCESS; +} + +/** + * sandbox_ffa_partition_info_get - Emulated FFA_PARTITION_INFO_GET handler function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_PARTITION_INFO_GET FF-A function. + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_partition_info_get) +{ + struct ffa_partition_info *rxbuf_desc_info = NULL; + u32 descs_cnt; + u32 descs_size_bytes; + + res->a0 = FFA_ERROR; + + if (!sandbox_ffa_priv_data.pair.rxbuf) { + res->a2 = FFA_ERR_STAT_DENIED; + goto cleanup; + } + + if (sandbox_ffa_priv_data.pair_info.rxbuf_owned) { + res->a2 = FFA_ERR_STAT_BUSY; + goto cleanup; + } + + if (!sandbox_ffa_priv_data.partitions.descs) { + sandbox_ffa_priv_data.partitions.descs = sandbox_partitions; + sandbox_ffa_priv_data.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 descriptors buffer size */ + if ((sandbox_ffa_priv_data.pair_info.rxtx_buf_size * SZ_4K) < descs_size_bytes) { + res->a2 = FFA_ERR_STAT_NO_MEMORY; + goto cleanup; + } + + rxbuf_desc_info = (struct ffa_partition_info *)sandbox_ffa_priv_data.pair.rxbuf; + + /* No UUID specified. Return the information of all partitions */ + if (!a1 && !a2 && !a3 && !a4) { + for (descs_cnt = 0 ; descs_cnt < SANDBOX_PARTITIONS_CNT ; descs_cnt++) + *(rxbuf_desc_info++) = + sandbox_ffa_priv_data.partitions.descs[descs_cnt].info; + + res->a0 = FFA_SUCCESS; + res->a2 = SANDBOX_PARTITIONS_CNT; + /* transfer ownership to the consumer: the non secure world */ + sandbox_ffa_priv_data.pair_info.rxbuf_owned = 1; + + goto cleanup; + } + + /* + * A UUID is specified. Return the information of all partitions matching + * the UUID + */ + + for (descs_cnt = 0 ; descs_cnt < SANDBOX_PARTITIONS_CNT ; descs_cnt++) + if (a1 == sandbox_ffa_priv_data.partitions.descs[descs_cnt].UUID.words.a1 && + a2 == sandbox_ffa_priv_data.partitions.descs[descs_cnt].UUID.words.a2 && + a3 == sandbox_ffa_priv_data.partitions.descs[descs_cnt].UUID.words.a3 && + a4 == sandbox_ffa_priv_data.partitions.descs[descs_cnt].UUID.words.a4) { + *(rxbuf_desc_info++) = + sandbox_ffa_priv_data.partitions.descs[descs_cnt].info; + } + + if (rxbuf_desc_info != ((struct ffa_partition_info *)sandbox_ffa_priv_data.pair.rxbuf)) { + res->a0 = FFA_SUCCESS; + /* store the partitions count */ + res->a2 = (unsigned long) + (rxbuf_desc_info - (struct ffa_partition_info *) + sandbox_ffa_priv_data.pair.rxbuf); + + /* transfer ownership to the consumer: the non secure world */ + sandbox_ffa_priv_data.pair_info.rxbuf_owned = 1; + } else { + res->a2 = FFA_ERR_STAT_INVALID_PARAMETERS; + } + +cleanup: + + ffa_err("[Sandbox] FFA_PARTITION_INFO_GET (%ld)", res->a2); + + res->a1 = 0; + + /* w3-w7 MBZ */ + memset(FFA_W3_MBZ_REG_START, 0, FFA_W3W7_MBZ_CNT * sizeof(unsigned long)); + + return FFA_ERR_STAT_SUCCESS; +} + +/** + * sandbox_ffa_rxtx_map - Emulated FFA_RXTX_MAP handler function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_RXTX_MAP FF-A function. + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_rxtx_map) +{ + res->a0 = FFA_ERROR; + + if (sandbox_ffa_priv_data.pair.txbuf && sandbox_ffa_priv_data.pair.rxbuf) { + res->a2 = FFA_ERR_STAT_DENIED; + goto feedback; + } + + if (a3 >= RXTX_BUFFERS_MIN_PAGES && a1 && a2) { + sandbox_ffa_priv_data.pair.txbuf = a1; + sandbox_ffa_priv_data.pair.rxbuf = a2; + sandbox_ffa_priv_data.pair_info.rxtx_buf_size = a3; + sandbox_ffa_priv_data.pair_info.rxbuf_mapped = 1; + res->a0 = FFA_SUCCESS; + res->a2 = 0; + goto feedback; + } + + if (!a1 || !a2) + res->a2 = FFA_ERR_STAT_INVALID_PARAMETERS; + else + res->a2 = FFA_ERR_STAT_NO_MEMORY; + + ffa_err("[Sandbox] error in FFA_RXTX_MAP arguments (%d)", (int)res->a2); + +feedback: + + res->a1 = 0; + + /* w3-w7 MBZ */ + memset(FFA_W3_MBZ_REG_START, + 0, FFA_W3W7_MBZ_CNT * sizeof(unsigned long)); + + return FFA_ERR_STAT_SUCCESS; +} + +/** + * sandbox_ffa_rxtx_unmap - Emulated FFA_RXTX_UNMAP handler function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_RXTX_UNMAP FF-A function. + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_rxtx_unmap) +{ + res->a0 = FFA_ERROR; + res->a2 = FFA_ERR_STAT_INVALID_PARAMETERS; + + if (GET_NS_PHYS_ENDPOINT_ID(a1) != sandbox_ffa_priv_data.id) + goto feedback; + + if (sandbox_ffa_priv_data.pair.txbuf && sandbox_ffa_priv_data.pair.rxbuf) { + sandbox_ffa_priv_data.pair.txbuf = 0; + sandbox_ffa_priv_data.pair.rxbuf = 0; + sandbox_ffa_priv_data.pair_info.rxtx_buf_size = 0; + sandbox_ffa_priv_data.pair_info.rxbuf_mapped = 0; + res->a0 = FFA_SUCCESS; + res->a2 = 0; + goto feedback; + } + + ffa_err("[Sandbox] No buffer pair registered on behalf of the caller"); + +feedback: + + res->a1 = 0; + + /* w3-w7 MBZ */ + memset(FFA_W3_MBZ_REG_START, + 0, FFA_W3W7_MBZ_CNT * sizeof(unsigned long)); + + return FFA_ERR_STAT_SUCCESS; +} + +/** + * sandbox_ffa_rx_release - Emulated FFA_RX_RELEASE handler function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_RX_RELEASE FF-A function. + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_rx_release) +{ + if (!sandbox_ffa_priv_data.pair_info.rxbuf_owned) { + res->a0 = FFA_ERROR; + res->a2 = FFA_ERR_STAT_DENIED; + } else { + sandbox_ffa_priv_data.pair_info.rxbuf_owned = 0; + res->a0 = FFA_SUCCESS; + res->a2 = 0; + } + + res->a1 = 0; + + /* w3-w7 MBZ */ + memset(FFA_W3_MBZ_REG_START, + 0, FFA_W3W7_MBZ_CNT * sizeof(unsigned long)); + + return FFA_ERR_STAT_SUCCESS; +} + +/** + * sandbox_ffa_sp_valid - Checks SP validity + * @part_id: partition ID to check + * + * This is the function searches the input ID in the descriptors table. + * + * Return: + * + * 1 on success (Partition found). Otherwise, failure + */ +int sandbox_ffa_sp_valid(u16 part_id) +{ + u32 descs_cnt; + + for (descs_cnt = 0 ; descs_cnt < SANDBOX_PARTITIONS_CNT ; descs_cnt++) + if (sandbox_ffa_priv_data.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 function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_MSG_SEND_DIRECT_{REQ,RESP} + * FF-A functions. + * + * Emulating interrupts is not supported. So, FFA_RUN and FFA_INTERRUPT are not supported. + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_msg_send_direct_req) +{ + u16 part_id; + + part_id = GET_DST_SP_ID(a1); + + if ((GET_NS_PHYS_ENDPOINT_ID(a1) != sandbox_ffa_priv_data.id) || + !sandbox_ffa_sp_valid(part_id) || + a2) { + res->a0 = FFA_ERROR; + res->a1 = 0; + res->a2 = FFA_ERR_STAT_INVALID_PARAMETERS; + + /* w3-w7 MBZ */ + memset(FFA_W3_MBZ_REG_START, + 0, FFA_W3W7_MBZ_CNT * sizeof(unsigned long)); + + return FFA_ERR_STAT_SUCCESS; + } + + res->a0 = FFA_MSG_SEND_DIRECT_RESP; + + res->a1 = PREP_SRC_SP_ID(part_id) | + PREP_NS_PHYS_ENDPOINT_ID(sandbox_ffa_priv_data.id); + + res->a2 = 0; + + /* + * return 0xff bytes as a response + */ + res->a3 = 0xffffffff; + res->a4 = 0xffffffff; + res->a5 = 0xffffffff; + res->a6 = 0xffffffff; + res->a7 = 0xffffffff; + + return FFA_ERR_STAT_SUCCESS; +} + +/** + * sandbox_ffa_get_prv_data - Returns the pointer to FF-A core pivate data + * @func_data: Pointer to the FF-A function arguments container structure + * + * This is the handler that returns the address of the FF-A core pivate data. + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +int sandbox_ffa_get_prv_data(struct ffa_interface_data *func_data) +{ + if (!func_data) + return -EINVAL; + + if (!func_data->data0 || func_data->data0_size != sizeof(struct ffa_prvdata **)) + return -EINVAL; + + if (!func_data->data1 || func_data->data1_size != sizeof(struct sandbox_ffa_prvdata **)) + return -EINVAL; + + *((struct ffa_prvdata **)func_data->data0) = &ffa_priv_data; + *((struct sandbox_ffa_prvdata **)func_data->data1) = &sandbox_ffa_priv_data; + + return FFA_ERR_STAT_SUCCESS; +} + +/** + * sandbox_ffa_get_rxbuf_flags - Reading the mapping/ownership flags + * @queried_func_id: The FF-A function to be queried + * @func_data: Pointer to the FF-A function arguments container structure + * + * This is the handler that queries the status flags of the following emulated ABIs: + * FFA_RXTX_MAP, FFA_RXTX_UNMAP, FFA_RX_RELEASE + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +int sandbox_ffa_get_rxbuf_flags(u32 queried_func_id, struct ffa_interface_data *func_data) +{ + 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) = sandbox_ffa_priv_data.pair_info.rxbuf_mapped; + return FFA_ERR_STAT_SUCCESS; + case FFA_RX_RELEASE: + *((u8 *)func_data->data0) = sandbox_ffa_priv_data.pair_info.rxbuf_owned; + return FFA_ERR_STAT_SUCCESS; + default: + ffa_err("[Sandbox] The querried FF-A interface flag (%d) undefined", + queried_func_id); + return -EINVAL; + } +} + +/** + * invoke_sandbox_ffa_drv_api - The driver dispatcher function + * @queried_func_id: The FF-A function to be queried + * @func_data: Pointer to the FF-A function arguments container structure + * + * The dispatcher function that selects the handler that queries the + * status of FF-A ABIs given in the input argument. + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +int invoke_sandbox_ffa_drv_api(u32 queried_func_id, struct ffa_interface_data *func_data) +{ + switch (queried_func_id) { + case FFA_VERSION: + case FFA_ID_GET: + case FFA_FEATURES: + return sandbox_ffa_get_prv_data(func_data); + case FFA_RXTX_MAP: + case FFA_RXTX_UNMAP: + case FFA_RX_RELEASE: + return sandbox_ffa_get_rxbuf_flags(queried_func_id, func_data); + default: + ffa_err("[Sandbox] The querried FF-A interface (%d) undefined", queried_func_id); + return -EINVAL; + } +} + +/** + * sandbox_ffa_set_conduit - Set the conduit used by Sandbox + * + * Sets the conduit in the private data structure + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +static int sandbox_ffa_set_conduit(void) +{ + sandbox_ffa_priv_data.conduit = FFA_CONDUIT_SMC; + + ffa_info("[Sandbox] Conduit is SMC"); + + return FFA_ERR_STAT_SUCCESS; +} + +/** + * sandbox_arm_ffa_smccc_smc - FF-A SMC call emulation + * @dev: the SMC arguments to be passed to the FF-A ABI + * + * Sandbox driver emulates the FF-A ABIs SMC call using this function. + * The emulated FF-A ABI is identified and invoked. + * FF-A emulation is based on the FF-A specification 1.0 + * + * Return: + * + * FFA_ERR_STAT_SUCCESS 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(unsigned long a0, unsigned long a1, + unsigned long a2, unsigned long a3, unsigned long a4, + unsigned long a5, unsigned long a6, unsigned long a7, + struct arm_smccc_res *res) +{ + int ret = FFA_ERR_STAT_SUCCESS; + + switch (a0) { + case FFA_VERSION: + ret = sandbox_ffa_version(a0, a1, a2, a3, a4, a5, a6, a7, res); + break; + case FFA_PARTITION_INFO_GET: + ret = sandbox_ffa_partition_info_get(a0, a1, a2, a3, a4, a5, a6, a7, res); + break; + case FFA_RXTX_UNMAP: + ret = sandbox_ffa_rxtx_unmap(a0, a1, a2, a3, a4, a5, a6, a7, res); + break; + case FFA_MSG_SEND_DIRECT_REQ: + ret = sandbox_ffa_msg_send_direct_req(a0, a1, a2, a3, a4, a5, a6, a7, res); + break; + case FFA_ID_GET: + ret = sandbox_ffa_id_get(a0, a1, a2, a3, a4, a5, a6, a7, res); + break; + case FFA_FEATURES: + ret = sandbox_ffa_features(a0, a1, a2, a3, a4, a5, a6, a7, res); + break; + case FFA_RXTX_MAP: + ret = sandbox_ffa_rxtx_map(a0, a1, a2, a3, a4, a5, a6, a7, res); + break; + case FFA_RX_RELEASE: + ret = sandbox_ffa_rx_release(a0, a1, a2, a3, a4, a5, a6, a7, res); + break; + default: + ffa_err("[Sandbox] Undefined FF-A interface (0x%x)", (unsigned int)a0); + } + + if (ret != FFA_ERR_STAT_SUCCESS) + ffa_err("[Sandbox] FF-A ABI internal failure (%d)", ret); +} + +/** + * sandbox_ffa_probe - The driver probe function + * @dev: the sandbox_arm_ffa device + * + * At probe level the conduit is set + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +static int sandbox_ffa_probe(struct udevice *dev) +{ + return sandbox_ffa_set_conduit(); +} + +/** + * sandbox_ffa_drv_ops - The driver operations structure + * @invoke_func: The driver dispatcher + */ +struct ffa_ops sandbox_ffa_drv_ops = { + .invoke_func = invoke_sandbox_ffa_drv_api +}; + +/** + * sandbox_ffa_device_get_ops - driver operations getter + * + * Return: + * This function returns a pointer to the driver operations structure + */ +const struct ffa_ops *sandbox_ffa_device_get_ops(void) +{ + return &sandbox_ffa_drv_ops; +} + +/** + * Declaring the sandbox_arm_ffa driver under UCLASS_FFA + */ +U_BOOT_DRIVER(sandbox_arm_ffa) = { + .name = FFA_SANDBOX_DRV_NAME, + .id = UCLASS_FFA, + .probe = sandbox_ffa_probe, +}; diff --git a/drivers/arm-ffa/sandbox_arm_ffa_prv.h b/drivers/arm-ffa/sandbox_arm_ffa_prv.h new file mode 100644 index 0000000000..56b8d50a96 --- /dev/null +++ b/drivers/arm-ffa/sandbox_arm_ffa_prv.h @@ -0,0 +1,131 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __SANDBOX_ARM_FFA_PRV_H +#define __SANDBOX_ARM_FFA_PRV_H + +#include "arm_ffa_prv.h" + +/* + * This header is private. It is exclusively used by the Sandbox FF-A driver + */ + +/* FF-A core driver name */ +#define FFA_SANDBOX_DRV_NAME "sandbox_arm_ffa" + +/* 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 */ + +/* w1-w7 MBZ */ +#define FFA_W1W7_MBZ_CNT (7) +#define FFA_W1W7_MBZ_REG_START (&res->a1) + +/* w4-w7 MBZ */ +#define FFA_W4W7_MBZ_CNT (4) +#define FFA_W4W7_MBZ_REG_START (&res->a4) + +/* w3-w7 MBZ */ +#define FFA_W3W7_MBZ_CNT (5) +#define FFA_W3_MBZ_REG_START (&res->a3) + +/* secure partitions count */ +#define SANDBOX_PARTITIONS_CNT (4) + +/* service 1 UUID binary data (little-endian format) */ +#define SANDBOX_SERVICE1_UUID_DATA \ + 0xed, 0x32, 0xd5, 0x33, \ + 0x99, 0xe6, 0x42, 0x09, \ + 0x9c, 0xc0, 0x2d, 0x72, \ + 0xcd, 0xd9, 0x98, 0xa7 + +/* service 2 UUID binary data (little-endian format) */ +#define SANDBOX_SERVICE2_UUID_DATA \ + 0xab, 0xcd, 0xd5, 0x33, \ + 0x99, 0xe6, 0x42, 0x09, \ + 0x9c, 0xc0, 0x2d, 0x72, \ + 0xcd, 0xd9, 0x98, 0xa7 + +/** + * struct ffa_rxtxpair_info - structure hosting the RX/TX buffers flags + * @rxbuf_owned: RX buffer ownership flag (the owner is non secure world: the consumer) + * @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 as set by the FF-A core driver + * + * Data structure hosting 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_prvdata - the driver private data structure + * + * @dev: The arm_ffa device under u-boot driver model + * @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 + * @conduit: The selected conduit + * + * The driver data structure hosting all the emulated secure world data. + */ +struct sandbox_ffa_prvdata { + struct udevice *dev; + u32 fwk_version; + u16 id; + struct ffa_partitions partitions; + struct ffa_rxtxpair pair; + struct ffa_rxtxpair_info pair_info; + enum ffa_conduit conduit; +}; + +void sandbox_arm_ffa_smccc_smc(unsigned long a0, unsigned long a1, + unsigned long a2, unsigned long a3, unsigned long a4, + unsigned long a5, unsigned long a6, unsigned long a7, + struct arm_smccc_res *res); + +#define SANDBOX_SMC_FFA_ABI(ffabi) static int sandbox_##ffabi(unsigned long a0, unsigned long a1, \ + unsigned long a2, unsigned long a3, unsigned long a4, \ + unsigned long a5, unsigned long a6, unsigned long a7, \ + struct arm_smccc_res *res) + +/* The core FF-A private data structure to inspect */ +extern struct ffa_prvdata ffa_priv_data; + +#endif diff --git a/include/arm_ffa.h b/include/arm_ffa.h index 98db01ee72..c400de4a70 100644 --- a/include/arm_ffa.h +++ b/include/arm_ffa.h @@ -22,6 +22,13 @@ #define ffa_info(fmt, ...) pr_info("[FFA] " fmt "\n", ##__VA_ARGS__) #define ffa_err(fmt, ...) pr_err("[FFA] " fmt "\n", ##__VA_ARGS__)
+/* panic only on real HW. On sandbox mode return an error code */ +#if CONFIG_IS_ENABLED(SANDBOX_FFA) +#define ffa_panic(fmt, ...) ffa_err("[FFA] " fmt "\n", ##__VA_ARGS__) +#else +#define ffa_panic(fmt, ...) panic("[FFA] " fmt "\n", ##__VA_ARGS__) +#endif + /* * The driver operations success error code */ @@ -184,7 +191,7 @@ const struct ffa_ops * __ffa_runtime ffa_device_get_ops(void); int ffa_get_device(void);
/** - * ffa_bus_discover - discover FF-A bus and probes the arm_ffa device + * ffa_bus_discover - discover FF-A bus and probes the arm_ffa and sandbox_arm_ffa devices */ int ffa_bus_discover(void); #endif diff --git a/include/sandbox_arm_ffa.h b/include/sandbox_arm_ffa.h new file mode 100644 index 0000000000..aa7a3454b9 --- /dev/null +++ b/include/sandbox_arm_ffa.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __SANDBOX_ARM_FFA_H +#define __SANDBOX_ARM_FFA_H + +#include <arm_ffa.h> + +/** + * The device driver and the Uclass driver public functions + */ + +/** + * sandbox_ffa_get_invoke_func - performs a call to the Sandbox FF-A driver dispatcher + */ +int sandbox_ffa_get_invoke_func(u32 func_id, struct ffa_interface_data *func_data); + +/** + * sandbox_ffa_device_get_ops - driver operations getter + */ +const struct ffa_ops *sandbox_ffa_device_get_ops(void); + +/** + * sandbox_ffa_get_device - create, bind and probe the sandbox_arm_ffa device + */ +int sandbox_ffa_get_device(void); + +#endif diff --git a/include/sandbox_arm_ffa_helper.h b/include/sandbox_arm_ffa_helper.h new file mode 100644 index 0000000000..f0fcd04536 --- /dev/null +++ b/include/sandbox_arm_ffa_helper.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __SANDBOX_ARM_FFA_HELPER_H +#define __SANDBOX_ARM_FFA_HELPER_H + +#include <arm_ffa_helper.h> +#include <sandbox_arm_ffa.h> +#include "../drivers/arm-ffa/sandbox_arm_ffa_prv.h" + +/* + * This header is public. Including this header provides all FF-A Sandbox data structures + * It also provides the helper function allowing to pass data and invoke Sandbox FF-A functions + * used for testing the FF-A core driver + */ + +/** + * sandbox_ffa_helper_query_core_state - Wrapper function for + * reading the FF-A core driver data + */ +int sandbox_ffa_helper_query_core_state(u32 queried_func_id, struct ffa_interface_data *func_data); + +#endif diff --git a/lib/arm-ffa/Makefile b/lib/arm-ffa/Makefile index cba625fde4..04159da8eb 100644 --- a/lib/arm-ffa/Makefile +++ b/lib/arm-ffa/Makefile @@ -6,3 +6,4 @@ # This file only gets included when CONFIG_ARM_FFA_TRANSPORT_HELPERS is set
obj-y += arm_ffa_helper.o +obj-$(CONFIG_SANDBOX_FFA) += sandbox_arm_ffa_helper.o diff --git a/lib/arm-ffa/arm_ffa_helper.c b/lib/arm-ffa/arm_ffa_helper.c index a291b000a7..3ee6ee304f 100644 --- a/lib/arm-ffa/arm_ffa_helper.c +++ b/lib/arm-ffa/arm_ffa_helper.c @@ -108,7 +108,7 @@ int __ffa_runtime ffa_helper_msg_send_direct_req(struct ffa_interface_data * ffa_helper_bus_discover - Wrapper function for FF-A bus discovery * * This boot time function should be called to discover the FF-A bus and - * probe the FF-A device. + * probe the arm_ffa and sandbox_arm_ffa devices. * To achieve that, this function is called automatically at initcalls * level (after u-boot relocation). * diff --git a/lib/arm-ffa/sandbox_arm_ffa_helper.c b/lib/arm-ffa/sandbox_arm_ffa_helper.c new file mode 100644 index 0000000000..7859f30fc7 --- /dev/null +++ b/lib/arm-ffa/sandbox_arm_ffa_helper.c @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <sandbox_arm_ffa_helper.h> + +/** + * sandbox_ffa_helper_query_core_state - Wrapper function for querying FF-A implementation + * + * A helper function used for querying the status of FF-A ABIs given in the input argument + * and the FF-A core driver. + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +int sandbox_ffa_helper_query_core_state(u32 queried_func_id, struct ffa_interface_data *func_data) +{ + return sandbox_ffa_get_invoke_func(queried_func_id, func_data); +} diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index cffa2c69d6..12fc28fd82 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -2118,7 +2118,7 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, struct efi_event *evt, *next_event; efi_status_t ret = EFI_SUCCESS;
-#if defined(CONFIG_ARM_FFA_TRANSPORT) +#if defined(CONFIG_ARM_FFA_TRANSPORT) && !defined(CONFIG_SANDBOX_FFA) int ffa_ret; #endif
@@ -2182,7 +2182,7 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL); }
-#if defined(CONFIG_ARM_FFA_TRANSPORT) +#if defined(CONFIG_ARM_FFA_TRANSPORT) && !defined(CONFIG_SANDBOX_FFA) /* unmap FF-A RX/TX buffers */ ffa_ret = ffa_helper_unmap_rxtx_buffers(); if (ffa_ret)

From: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
Add functional test cases for the FF-A core driver
These tests rely on the FF-A Sandbox driver which helps in inspecting the FF-A core driver.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com --- MAINTAINERS | 2 + test/dm/Makefile | 1 + test/dm/ffa.c | 418 +++++++++++++++++++++++++++++++++++++++++++++++ test/dm/ffa.h | 22 +++ 4 files changed, 443 insertions(+) create mode 100644 test/dm/ffa.c create mode 100644 test/dm/ffa.h
diff --git a/MAINTAINERS b/MAINTAINERS index 7c439ed1fd..74b7ed9388 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -242,6 +242,8 @@ F: include/arm_ffa_helper.h F: include/sandbox_arm_ffa.h F: include/sandbox_arm_ffa_helper.h F: lib/arm-ffa/ +F: test/dm/ffa.c +F: test/dm/ffa.h
ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de diff --git a/test/dm/Makefile b/test/dm/Makefile index d46552fbf3..48eab1b1ff 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -79,6 +79,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_SANDBOX_FFA) += 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 0000000000..85b71bd808 --- /dev/null +++ b/test/dm/ffa.c @@ -0,0 +1,418 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Functional tests for UCLASS_FFA class + * + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <console.h> +#include <dm.h> +#include <dm/test.h> +#include "ffa.h" +#include <test/test.h> +#include <test/ut.h> +#include <sandbox_arm_ffa_helper.h> +#include <string.h> + +/* Functional tests for the UCLASS_FFA */ + +static int dm_test_ffa_log(struct unit_test_state *uts, char *msg) +{ + char cmd[LOG_CMD_SZ] = {0}; + + console_record_reset(); + + snprintf(cmd, LOG_CMD_SZ, "echo "%s"", msg); + run_command(cmd, 0); + + ut_assert_console_end(); + + return CMD_RET_SUCCESS; +} + +static int check_fwk_version(struct ffa_prvdata *prvdata, struct unit_test_state *uts) +{ + if (prvdata->fwk_version != SANDBOX_FWK_VERSION) { + char msg[LOG_MSG_SZ] = {0}; + + snprintf(msg, LOG_MSG_SZ, "[%s]: Error: prvdata->fwk_version = 0x%x", __func__, + prvdata->fwk_version); + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + return CMD_RET_SUCCESS; +} + +static int check_endpoint_id(struct ffa_prvdata *prvdata, struct unit_test_state *uts) +{ + if (prvdata->id) { + char msg[LOG_MSG_SZ] = {0}; + + snprintf(msg, LOG_MSG_SZ, "[%s]: Error: prvdata->id = 0x%x", __func__, prvdata->id); + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + return CMD_RET_SUCCESS; +} + +static int check_dev(struct ffa_prvdata *prvdata, struct unit_test_state *uts) +{ + if (!prvdata->dev) { + char msg[LOG_MSG_SZ] = {0}; + + snprintf(msg, LOG_MSG_SZ, "[%s]: Error: device = 0x%p", __func__, prvdata->dev); + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + return CMD_RET_SUCCESS; +} + +static int check_rxtxbuf(struct ffa_prvdata *prvdata, struct unit_test_state *uts) +{ + if (!prvdata->pair.rxbuf && prvdata->pair.txbuf) { + char msg[LOG_MSG_SZ] = {0}; + + snprintf(msg, LOG_MSG_SZ, "[%s]: Error: rxbuf = 0x%llx txbuf = 0x%llx", __func__, + prvdata->pair.rxbuf, + prvdata->pair.txbuf); + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + return CMD_RET_SUCCESS; +} + +static int check_features(struct ffa_prvdata *prvdata, struct unit_test_state *uts) +{ + u32 desc_idx; + char msg[LOG_MSG_SZ] = {0}; + + snprintf(msg, + LOG_MSG_SZ, + "[%s]: Error: FFA_RXTX_MAP features not found", + __func__); + + for (desc_idx = 0; desc_idx < FFA_FEATURE_DESC_CNT ; desc_idx++) + if (prvdata->features[desc_idx].func_id == FFA_RXTX_MAP) { + if (prvdata->features[desc_idx].field1 != RXTX_4K && + prvdata->features[desc_idx].field1 != RXTX_16K && + prvdata->features[desc_idx].field1 != RXTX_64K) { + snprintf(msg, + LOG_MSG_SZ, + "[%s]: Error: FFA_RXTX_MAP features = 0x%x", + __func__, + prvdata->features[desc_idx].field1); + break; + } + return CMD_RET_SUCCESS; + } + + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; +} + +static int check_rxbuf_mapped_flag(u32 queried_func_id, + u8 rxbuf_mapped, + struct unit_test_state *uts) +{ + char msg[LOG_MSG_SZ] = {0}; + + switch (queried_func_id) { + case FFA_RXTX_MAP: + { + if (rxbuf_mapped) + return CMD_RET_SUCCESS; + break; + } + case FFA_RXTX_UNMAP: + { + if (!rxbuf_mapped) + return CMD_RET_SUCCESS; + break; + } + default: + return CMD_RET_FAILURE; + } + + snprintf(msg, LOG_MSG_SZ, "[%s]: Error: %s mapping issue", __func__, + (queried_func_id == FFA_RXTX_MAP ? "FFA_RXTX_MAP" : "FFA_RXTX_UNMAP")); + dm_test_ffa_log(uts, msg); + + return CMD_RET_FAILURE; +} + +static int check_rxbuf_release_flag(u8 rxbuf_owned, struct unit_test_state *uts) +{ + if (rxbuf_owned) { + char msg[LOG_MSG_SZ] = {0}; + + snprintf(msg, LOG_MSG_SZ, "[%s]: Error: RX buffer not released", __func__); + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + return CMD_RET_SUCCESS; +} + +static int test_ffa_msg_send_direct_req(u16 part_id, struct unit_test_state *uts) +{ + struct ffa_interface_data func_data = {0}; + struct ffa_send_direct_data msg = {0}; + u32 pattern = 0xaabbccdd; + u8 cnt; + + /* + * telling the driver which partition to use + */ + func_data.data0_size = sizeof(part_id); + func_data.data0 = &part_id; + + /* + * filling the message data + */ + msg.a3 = pattern; + msg.a4 = pattern; + msg.a5 = pattern; + msg.a6 = pattern; + msg.a7 = pattern; + func_data.data1_size = sizeof(msg); + func_data.data1 = &msg; + + ut_assertok(ffa_helper_msg_send_direct_req(&func_data)); + + for (cnt = 0; cnt < sizeof(struct ffa_send_direct_data) / sizeof(u32); cnt++) + ut_assertok(((u32 *)&msg)[cnt] != 0xffffffff); + + return CMD_RET_SUCCESS; +} + +static int test_partitions_and_comms(union ffa_partition_uuid *service_uuid, + struct sandbox_ffa_prvdata *sdx_prvdata, + struct unit_test_state *uts) +{ + struct ffa_interface_data func_data = {0}; + u32 count = 0; + struct ffa_partition_info *parts_info; + u32 info_idx, exp_info_idx; + int ret; + + /* + * get from the driver the count of the SPs matching the UUID + */ + func_data.data0_size = sizeof(*service_uuid); + func_data.data0 = service_uuid; + func_data.data1_size = sizeof(count); + func_data.data1 = &count; + + ut_assertok(ffa_helper_get_partitions_info(&func_data)); + + /* make sure partitions are detected */ + ut_assertok(!count); + + /* + * pre-allocate a buffer to be filled by the driver + * with ffa_partition_info structs + */ + + parts_info = calloc(count, sizeof(struct ffa_partition_info)); + ut_assertok(!parts_info); + + func_data.data1_size = count * sizeof(struct ffa_partition_info); + func_data.data1 = parts_info; + + /* + * ask the driver to fill the buffer with the SPs info + */ + ret = ffa_helper_get_partitions_info(&func_data); + if (ret != FFA_ERR_STAT_SUCCESS) { + free(parts_info); + ut_assertok(ret != FFA_ERR_STAT_SUCCESS); + } + + /* + * SPs found , verify the partitions information + */ + + ret = CMD_RET_FAILURE; + + for (info_idx = 0; info_idx < count ; info_idx++) { + for (exp_info_idx = 0; + exp_info_idx < sdx_prvdata->partitions.count; + exp_info_idx++) { + if (parts_info[info_idx].id == + sdx_prvdata->partitions.descs[exp_info_idx].info.id) { + ret = memcmp(&parts_info[info_idx], + &sdx_prvdata->partitions.descs[exp_info_idx] + .info, + sizeof(struct ffa_partition_info)); + if (ret) + free(parts_info); + ut_assertok(ret != 0); + /* send and receive data from the current partition */ + test_ffa_msg_send_direct_req(parts_info[info_idx].id, uts); + } + ret = CMD_RET_SUCCESS; + } + } + + free(parts_info); + + /* Verify expected partitions found in the emulated secure world*/ + ut_assertok(ret != CMD_RET_SUCCESS); + + return CMD_RET_SUCCESS; +} + +static int dm_test_ffa_ack(struct unit_test_state *uts) +{ + struct ffa_prvdata *prvdata = NULL; + struct sandbox_ffa_prvdata *sdx_prvdata = NULL; + struct ffa_interface_data func_data = {0}; + u8 rxbuf_flag = 0; + union ffa_partition_uuid svc1_uuid = { .bytes = {SANDBOX_SERVICE1_UUID_DATA}}; + union ffa_partition_uuid svc2_uuid = { .bytes = {SANDBOX_SERVICE2_UUID_DATA}}; + int ret; + + /* get a pointer to the FF-A core and sandbox drivers private data */ + func_data.data0 = &prvdata; + func_data.data0_size = sizeof(prvdata); + func_data.data1 = &sdx_prvdata; + func_data.data1_size = sizeof(sdx_prvdata); + + ut_assertok(sandbox_ffa_helper_query_core_state(FFA_VERSION, &func_data)); + + /* clear private data before the new test */ + memset(prvdata, 0, sizeof(struct ffa_prvdata)); + memset(sdx_prvdata, 0, sizeof(struct sandbox_ffa_prvdata)); + + /* test probing FF-A devices */ + ut_assertok(ffa_helper_bus_discover()); + ut_assertok(check_dev(prvdata, uts)); + + /* test FFA_VERSION */ + ut_assertok(check_fwk_version(prvdata, uts)); + + /* test FFA_ID_GET */ + ut_assertok(check_endpoint_id(prvdata, uts)); + + /* test FFA_FEATURES */ + ut_assertok(check_features(prvdata, uts)); + + /* test core RX/TX buffers */ + ut_assertok(check_rxtxbuf(prvdata, uts)); + + /* test FFA_RXTX_MAP */ + func_data.data0 = &rxbuf_flag; + func_data.data0_size = sizeof(rxbuf_flag); + + rxbuf_flag = 0; + ut_assertok(sandbox_ffa_helper_query_core_state(FFA_RXTX_MAP, &func_data)); + ut_assertok(check_rxbuf_mapped_flag(FFA_RXTX_MAP, rxbuf_flag, uts)); + + /* FFA_PARTITION_INFO_GET / FFA_MSG_SEND_DIRECT_REQ */ + ret = test_partitions_and_comms(&svc1_uuid, sdx_prvdata, uts); + ut_assertok(ret != CMD_RET_SUCCESS); + + /* test FFA_RX_RELEASE */ + rxbuf_flag = 1; + ut_assertok(sandbox_ffa_helper_query_core_state(FFA_RX_RELEASE, &func_data)); + ut_assertok(check_rxbuf_release_flag(rxbuf_flag, uts)); + + /* FFA_PARTITION_INFO_GET / FFA_MSG_SEND_DIRECT_REQ */ + ret = test_partitions_and_comms(&svc2_uuid, sdx_prvdata, uts); + ut_assertok(ret != CMD_RET_SUCCESS); + + /* test FFA_RX_RELEASE */ + rxbuf_flag = 1; + ut_assertok(sandbox_ffa_helper_query_core_state(FFA_RX_RELEASE, &func_data)); + ut_assertok(check_rxbuf_release_flag(rxbuf_flag, uts)); + + /* test FFA_RXTX_UNMAP */ + ut_assertok(ffa_helper_unmap_rxtx_buffers()); + + rxbuf_flag = 1; + ut_assertok(sandbox_ffa_helper_query_core_state(FFA_RXTX_UNMAP, &func_data)); + ut_assertok(check_rxbuf_mapped_flag(FFA_RXTX_UNMAP, rxbuf_flag, uts)); + + return CMD_RET_SUCCESS; +} + +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_prvdata *prvdata = NULL; + struct sandbox_ffa_prvdata *sdx_prvdata = NULL; + + struct ffa_interface_data func_data = {0}; + union ffa_partition_uuid valid_svc_uuid = { .bytes = {SANDBOX_SERVICE1_UUID_DATA}}; + union ffa_partition_uuid unvalid_svc_uuid = { .bytes = {SANDBOX_SERVICE3_UUID_DATA}}; + struct ffa_send_direct_data msg = {0}; + int ret; + u32 count = 0; + u16 part_id = 0; + + /* get a pointer to the FF-A core and sandbox drivers private data */ + func_data.data0 = &prvdata; + func_data.data0_size = sizeof(prvdata); + func_data.data1 = &sdx_prvdata; + func_data.data1_size = sizeof(sdx_prvdata); + ut_assertok(sandbox_ffa_helper_query_core_state(FFA_VERSION, &func_data)); + + /* clear private data before the new test */ + memset(prvdata, 0, sizeof(struct ffa_prvdata)); + memset(sdx_prvdata, 0, sizeof(struct sandbox_ffa_prvdata)); + + /* get partitions info before probing the core driver */ + func_data.data0_size = sizeof(union ffa_partition_uuid); + func_data.data0 = &unvalid_svc_uuid; + func_data.data1_size = sizeof(count); + func_data.data1 = &count; + + ret = ffa_helper_get_partitions_info(&func_data); + ut_assertok(ret != -ENODEV); + + /* probing FF-A devices */ + ut_assertok(ffa_helper_bus_discover()); + ut_assertok(check_dev(prvdata, uts)); + + /* query partition info using invalid arguments */ + func_data.data0_size = 0; + ret = ffa_helper_get_partitions_info(&func_data); + ut_assertok(ret != -EINVAL); + + /* query partition info using an invalid UUID */ + func_data.data0_size = sizeof(union ffa_partition_uuid); + func_data.data0 = &unvalid_svc_uuid; + func_data.data1_size = sizeof(count); + func_data.data1 = &count; + + ret = ffa_helper_get_partitions_info(&func_data); + ut_assertok(ret != -EPERM); + + /* query partition info using a valid UUID */ + func_data.data0 = &valid_svc_uuid; + count = 0; + ret = ffa_helper_get_partitions_info(&func_data); + /* make sure partitions are detected */ + ut_assertok(ret != FFA_ERR_STAT_SUCCESS); + ut_assertok(!count); + + /* send data to an invalid partition */ + func_data.data0_size = sizeof(part_id); + func_data.data0 = &part_id; + func_data.data1_size = sizeof(msg); + func_data.data1 = &msg; + + ret = ffa_helper_msg_send_direct_req(&func_data); + ut_assertok(ret != -EPERM); + + /* send data to a valid partition */ + part_id = ffa_priv_data.partitions.descs[0].info.id; + ret = ffa_helper_msg_send_direct_req(&func_data); + ut_assertok(ret != FFA_ERR_STAT_SUCCESS); + + return CMD_RET_SUCCESS; +} + +DM_TEST(dm_test_ffa_nack, UT_TESTF_SCAN_FDT | UT_TESTF_CONSOLE_REC); diff --git a/test/dm/ffa.h b/test/dm/ffa.h new file mode 100644 index 0000000000..11c7980fcf --- /dev/null +++ b/test/dm/ffa.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __TEST_DM_FFA_H +#define __TEST_DM_FFA_H + +#define SANDBOX_FWK_VERSION (0x10000) + +#define LOG_MSG_SZ (100) +#define LOG_CMD_SZ (LOG_MSG_SZ * 2) + +/* service 3 UUID binary data (little-endian format) */ +#define SANDBOX_SERVICE3_UUID_DATA \ + 0xfd, 0x42, 0xd5, 0x33, \ + 0x19, 0xa6, 0x42, 0x09, \ + 0x9c, 0xc1, 0x2d, 0x72, \ + 0xcd, 0xd9, 0x98, 0x89 + +#endif /*__TEST_DM_FFA_H */

From: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
Add Sandbox test for the armffa command
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com --- MAINTAINERS | 2 ++ test/cmd/Makefile | 1 + test/cmd/armffa.c | 33 +++++++++++++++++++++++++++++++++ test/cmd/armffa.h | 13 +++++++++++++ 4 files changed, 49 insertions(+) create mode 100644 test/cmd/armffa.c create mode 100644 test/cmd/armffa.h
diff --git a/MAINTAINERS b/MAINTAINERS index 74b7ed9388..eb9c3886e6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -242,6 +242,8 @@ F: include/arm_ffa_helper.h F: include/sandbox_arm_ffa.h F: include/sandbox_arm_ffa_helper.h F: lib/arm-ffa/ +F: test/cmd/armffa.c +F: test/cmd/armffa.h F: test/dm/ffa.c F: test/dm/ffa.h
diff --git a/test/cmd/Makefile b/test/cmd/Makefile index a59adb1e6d..d9dc0809d6 100644 --- a/test/cmd/Makefile +++ b/test/cmd/Makefile @@ -11,3 +11,4 @@ obj-$(CONFIG_CMD_MEM_SEARCH) += mem_search.o obj-$(CONFIG_CMD_PINMUX) += pinmux.o obj-$(CONFIG_CMD_PWM) += pwm.o obj-$(CONFIG_CMD_SETEXPR) += setexpr.o +obj-$(CONFIG_SANDBOX_FFA) += armffa.o diff --git a/test/cmd/armffa.c b/test/cmd/armffa.c new file mode 100644 index 0000000000..fc41571cf0 --- /dev/null +++ b/test/cmd/armffa.c @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test for armffa command + * + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include "armffa.h" +#include <arm_ffa_helper.h> +#include <dm.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +/* Basic test of 'armffa' command */ +static int dm_test_armffa_cmd(struct unit_test_state *uts) +{ + ut_assertok(ffa_helper_bus_discover()); + + /* armffa getpart <UUID> */ + ut_assertok(run_command("armffa getpart " SE_PROXY_PARTITION_UUID, 0)); + + /* armffa ping <ID> */ + ut_assertok(run_command("armffa ping " SE_PROXY_PARTITION_ID, 0)); + + /* armffa devlist */ + ut_assertok(run_command("armffa devlist", 0)); + + return CMD_RET_SUCCESS; +} + +DM_TEST(dm_test_armffa_cmd, UT_TESTF_SCAN_FDT | UT_TESTF_CONSOLE_REC); diff --git a/test/cmd/armffa.h b/test/cmd/armffa.h new file mode 100644 index 0000000000..630dc75e25 --- /dev/null +++ b/test/cmd/armffa.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __TEST_CMD_FFA_H +#define __TEST_CMD_FFA_H + +#define SE_PROXY_PARTITION_ID "0x1245" +#define SE_PROXY_PARTITION_UUID "33d532ed-e699-0942-c09c-a798d9cd722d" + +#endif /*__TEST_CMD_FFA_H */

From: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
Add MM communication support using FF-A transport
FF-A MM communication allows exchanging data with StandAlonneMM or smm-gateway secure partitions which run in OP-TEE.
An MM shared buffer and a door bell event are used to exchange this 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.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Signed-off-by: Gowtham Suresh Kumar gowtham.sureshkumar@arm.com --- arch/arm/cpu/armv8/cache.S | 16 ++ arch/arm/cpu/armv8/cache_v8.c | 3 +- include/mm_communication.h | 4 +- lib/efi_loader/Kconfig | 14 +- lib/efi_loader/efi_variable_tee.c | 281 +++++++++++++++++++++++++++++- 5 files changed, 308 insertions(+), 10 deletions(-)
diff --git a/arch/arm/cpu/armv8/cache.S b/arch/arm/cpu/armv8/cache.S index d1cee23437..bdbe89e0c5 100644 --- a/arch/arm/cpu/armv8/cache.S +++ b/arch/arm/cpu/armv8/cache.S @@ -21,7 +21,11 @@ * x1: 0 clean & invalidate, 1 invalidate only * x2~x9: clobbered */ +#ifdef CONFIG_ARM_FFA_TRANSPORT +.pushsection .text.efi_runtime, "ax" +#else .pushsection .text.__asm_dcache_level, "ax" +#endif ENTRY(__asm_dcache_level) lsl x12, x0, #1 msr csselr_el1, x12 /* select cache level */ @@ -65,7 +69,11 @@ ENDPROC(__asm_dcache_level) * * flush or invalidate all data cache by SET/WAY. */ +#ifdef CONFIG_ARM_FFA_TRANSPORT +.pushsection .text.efi_runtime, "ax" +#else .pushsection .text.__asm_dcache_all, "ax" +#endif ENTRY(__asm_dcache_all) mov x1, x0 dsb sy @@ -109,7 +117,11 @@ ENTRY(__asm_flush_dcache_all) ENDPROC(__asm_flush_dcache_all) .popsection
+#ifdef CONFIG_ARM_FFA_TRANSPORT +.pushsection .text.efi_runtime, "ax" +#else .pushsection .text.__asm_invalidate_dcache_all, "ax" +#endif ENTRY(__asm_invalidate_dcache_all) mov x0, #0x1 b __asm_dcache_all @@ -182,7 +194,11 @@ ENTRY(__asm_invalidate_icache_all) ENDPROC(__asm_invalidate_icache_all) .popsection
+#ifdef CONFIG_ARM_FFA_TRANSPORT +.pushsection .text.efi_runtime, "ax" +#else .pushsection .text.__asm_invalidate_l3_dcache, "ax" +#endif WEAK(__asm_invalidate_l3_dcache) mov x0, #0 /* return status as success */ ret diff --git a/arch/arm/cpu/armv8/cache_v8.c b/arch/arm/cpu/armv8/cache_v8.c index 3de18c7675..187a4497a7 100644 --- a/arch/arm/cpu/armv8/cache_v8.c +++ b/arch/arm/cpu/armv8/cache_v8.c @@ -9,6 +9,7 @@
#include <common.h> #include <cpu_func.h> +#include <efi_loader.h> #include <hang.h> #include <log.h> #include <asm/cache.h> @@ -425,7 +426,7 @@ __weak void mmu_setup(void) /* * Performs a invalidation of the entire data cache at all levels */ -void invalidate_dcache_all(void) +void __efi_runtime invalidate_dcache_all(void) { __asm_invalidate_dcache_all(); __asm_invalidate_l3_dcache(); diff --git a/include/mm_communication.h b/include/mm_communication.h index e65fbde60d..bb99190956 100644 --- a/include/mm_communication.h +++ b/include/mm_communication.h @@ -123,7 +123,7 @@ struct __packed efi_mm_communicate_header { * * Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_HEADER. */ -struct smm_variable_communicate_header { +struct __packed smm_variable_communicate_header { efi_uintn_t function; efi_status_t ret_status; u8 data[]; @@ -145,7 +145,7 @@ struct smm_variable_communicate_header { * Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE. * */ -struct smm_variable_access { +struct __packed smm_variable_access { efi_guid_t guid; efi_uintn_t data_size; efi_uintn_t name_size; diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index 28657f50c9..0d69133595 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -55,13 +55,23 @@ 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" - depends on OPTEE + bool "UEFI variables storage service via the trusted world" + depends on OPTEE || ARM_FFA_TRANSPORT help + The MM SP (also called partition) can be StandAlonneMM or smm-gateway. + When using the u-boot OP-TEE driver, StandAlonneMM is supported. + When using the u-boot FF-A driver, StandAlonneMM and smm-gateway are 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 under Optee in the trusted 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. + endchoice
config EFI_VARIABLES_PRESEED diff --git a/lib/efi_loader/efi_variable_tee.c b/lib/efi_loader/efi_variable_tee.c index dfef18435d..915ccdafc2 100644 --- a/lib/efi_loader/efi_variable_tee.c +++ b/lib/efi_loader/efi_variable_tee.c @@ -15,6 +15,40 @@ #include <malloc.h> #include <mm_communication.h>
+#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) + +#include <arm_ffa_helper.h> +#include <cpu_func.h> +#include <mapmem.h> + +#ifndef FFA_SHARED_MM_BUFFER_SIZE +#warning "FFA_SHARED_MM_BUFFER_SIZE must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_SIZE 0 +#endif + +#ifndef FFA_SHARED_MM_BUFFER_ADDR +#warning "FFA_SHARED_MM_BUFFER_ADDR must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_ADDR 0 +#endif + +/* MM return codes */ +#define MM_SUCCESS (0) + +#define ARM_SVC_ID_SP_EVENT_COMPLETE_AARCH64 (0xC4000061) +#define ARM_SVC_ID_SP_EVENT_COMPLETE ARM_SVC_ID_SP_EVENT_COMPLETE_AARCH64 + +#ifndef MM_SP_UUID_DATA +#warning "MM_SP_UUID_DATA must be defined in include/configs/<board>.h" +#define MM_SP_UUID_DATA 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +#endif + +/* MM_SP_UUID_DATA defined by the platform */ +union ffa_partition_uuid mm_sp_svc_uuid = {.bytes = {MM_SP_UUID_DATA}}; + +static __efi_runtime_data 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 */ static efi_uintn_t max_payload_size; /* func + data */ @@ -24,6 +58,7 @@ struct mm_connection { u32 session; };
+#if (IS_ENABLED(CONFIG_OPTEE)) /** * get_connection() - Retrieve OP-TEE session for a specific UUID. * @@ -143,16 +178,238 @@ static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize)
return ret; } +#endif + +#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) + +/** + * ffa_notify_mm_sp() - Announce there is data in the shared buffer + * + * Notifies 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 __efi_runtime ffa_notify_mm_sp(void) +{ + struct ffa_interface_data func_data = {0}; + struct ffa_send_direct_data msg = {0}; + int ret; + u32 sp_event_complete; + int sp_event_ret; + + func_data.data0_size = sizeof(mm_sp_id); + func_data.data0 = &mm_sp_id; + + msg.a3 = FFA_SHARED_MM_BUFFER_ADDR; + msg.a4 = FFA_SHARED_MM_BUFFER_SIZE; + func_data.data1_size = sizeof(msg); + func_data.data1 = &msg; + + ret = ffa_helper_msg_send_direct_req(&func_data); + if (ret != FFA_ERR_STAT_SUCCESS) + return ret; + + sp_event_complete = msg.a3; + sp_event_ret = (int)msg.a4; + + if (sp_event_complete == ARM_SVC_ID_SP_EVENT_COMPLETE && sp_event_ret == MM_SUCCESS) + return 0; + + /* + * Failure to notify the MM SP + */ + + return -EACCES; +} + +/** + * 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) +{ + struct ffa_interface_data func_data = {0}; + u32 count = 0; + int ret; + struct ffa_partition_info *parts_info; + + /* + * get from the driver the count of the SPs matching the UUID + */ + func_data.data0_size = sizeof(mm_sp_svc_uuid); + func_data.data0 = &mm_sp_svc_uuid; + func_data.data1_size = sizeof(count); + func_data.data1 = &count; + + ret = ffa_helper_get_partitions_info(&func_data); + if (ret != FFA_ERR_STAT_SUCCESS) { + log_err("EFI: Failure in querying partitions count (error code: %d)\n", ret); + return ret; + } + + if (!count) { + log_info("EFI: No MM partition found\n"); + return ret; + } + + /* + * pre-allocate a buffer to be filled by the driver + * with ffa_partition_info structs + */ + + parts_info = calloc(count, sizeof(struct ffa_partition_info)); + if (!parts_info) + return -EINVAL; + + log_info("EFI: Pre-allocating %d partition(s) info structures\n", count); + + func_data.data1_size = count * + sizeof(struct ffa_partition_info); + func_data.data1 = parts_info; + + /* + * ask the driver to fill the + * buffer with the SPs info + */ + ret = ffa_helper_get_partitions_info(&func_data); + if (ret != FFA_ERR_STAT_SUCCESS) { + log_err("EFI: Failure in querying partition(s) info (error code: %d)\n", ret); + free(parts_info); + return ret; + } + + /* + * MM SPs found , use the first one + */ + + mm_sp_id = parts_info[0].id; + + log_info("EFI: MM partition ID 0x%x\n", mm_sp_id); + + free(parts_info); + + return 0; +}
/** - * mm_communicate() - Adjust the cmonnucation buffer to StandAlonneMM and send + * 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 + * + * Issues 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 __efi_runtime ffa_mm_communicate(void *comm_buf, ulong comm_buf_size) +{ + ulong tx_data_size; + int ffa_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() != FFA_ERR_STAT_SUCCESS) { + log_err("EFI: Failure to discover MM partition ID at boot time\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 > FFA_SHARED_MM_BUFFER_SIZE) + return EFI_INVALID_PARAMETER; + + /* Copy the data to the shared buffer */ + + virt_shared_buf = (void *)map_sysmem((phys_addr_t)FFA_SHARED_MM_BUFFER_ADDR, 0); + efi_memcpy_runtime(virt_shared_buf, comm_buf, tx_data_size); + + /* + * The secure world has cache disabled for device region which we use for shared buffer. + * So, 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(); + if (ffa_ret) + unmap_sysmem(virt_shared_buf); + + 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_memcpy_runtime(comm_buf, virt_shared_buf, comm_buf_size); + unmap_sysmem(virt_shared_buf); + return EFI_BUFFER_TOO_SMALL; + } + + efi_memcpy_runtime(comm_buf, virt_shared_buf, rx_data_size); + unmap_sysmem(virt_shared_buf); + + return EFI_SUCCESS; + } + case -EINVAL: + return EFI_DEVICE_ERROR; + case -EPERM: + return EFI_INVALID_PARAMETER; + case -EACCES: + return EFI_ACCESS_DENIED; + case -EBUSY: + return EFI_OUT_OF_RESOURCES; + default: + return EFI_ACCESS_DENIED; + } +} +#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 MM SP (also called partition) can be 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, StandAlonneMM and smm-gateway are supported. + * * Return: status code */ -static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) +static efi_status_t __efi_runtime mm_communicate(u8 *comm_buf, efi_uintn_t dsize) { efi_status_t ret; struct efi_mm_communicate_header *mm_hdr; @@ -162,9 +419,16 @@ static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) mm_hdr = (struct efi_mm_communicate_header *)comm_buf; var_hdr = (struct smm_variable_communicate_header *)mm_hdr->data;
+ #if (IS_ENABLED(CONFIG_OPTEE)) ret = optee_mm_communicate(comm_buf, dsize); + #elif (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) + ret = ffa_mm_communicate(comm_buf, dsize); + #endif if (ret != EFI_SUCCESS) { - log_err("%s failed!\n", __func__); + /* No need for showing a log here. mm_communicate failure happens + * when getVariable() is called with data size set to 0. + * This is not expected so no log shown. + */ return ret; }
@@ -258,6 +522,13 @@ efi_status_t EFIAPI get_max_payload(efi_uintn_t *size) goto out; } *size = var_payload->size; + + #if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) + if (*size > FFA_SHARED_MM_BUFFER_SIZE) + *size = FFA_SHARED_MM_BUFFER_SIZE - MM_COMMUNICATE_HEADER_SIZE - + MM_VARIABLE_COMMUNICATE_SIZE; + #endif + /* * There seems to be a bug in EDK2 miscalculating the boundaries and * size checks, so deduct 2 more bytes to fulfill this requirement. Fix @@ -697,7 +968,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);
/*

On Fri, Apr 15, 2022 at 01:27:57PM +0100, abdellatif.elkhlifi@arm.com wrote:
From: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
This patchset adds support for Arm FF-A (Arm Firmware Framework for Armv8-A v1.0).
FF-A support is generic by design and can be used by any Arm platform.
The features added are as follows:
1/ FF-A bus driver 2/ armffa command 3/ FF-A Sandbox driver 4/ FF-A Sandbox test cases 5/ FF-A MM communication
The suggested design considers FF-A as a discoverable bus. 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 Secure World firmware runs under TrustZone HW (such as Optee). The same approach was followed in the FF-A driver in Linux kernel (https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/driv...))
Cc: Tom Rini trini@konsulko.com Cc: Rob Herring robh@kernel.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Achin Gupta achin.gupta@arm.com Cc: Simon Glass sjg@chromium.org Cc: Vishnu Banavath vishnu.banavath@arm.com
I don't see a changelog compared with v1. Are any of the comments there addressed? Thanks.

On Fri, Apr 15, 2022 at 11:43:25AM -0400, Tom Rini wrote:
On Fri, Apr 15, 2022 at 01:27:57PM +0100, abdellatif.elkhlifi@arm.com wrote:
From: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
This patchset adds support for Arm FF-A (Arm Firmware Framework for Armv8-A v1.0).
FF-A support is generic by design and can be used by any Arm platform.
The features added are as follows:
1/ FF-A bus driver 2/ armffa command 3/ FF-A Sandbox driver 4/ FF-A Sandbox test cases 5/ FF-A MM communication
The suggested design considers FF-A as a discoverable bus. 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 Secure World firmware runs under TrustZone HW (such as Optee). The same approach was followed in the FF-A driver in Linux kernel (https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/driv...))
Cc: Tom Rini trini@konsulko.com Cc: Rob Herring robh@kernel.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Achin Gupta achin.gupta@arm.com Cc: Simon Glass sjg@chromium.org Cc: Vishnu Banavath vishnu.banavath@arm.com
I don't see a changelog compared with v1. Are any of the comments there addressed? Thanks.
-- Tom
Thanks Tom. I'll add a changelog for future versions.
The current Changelog:
Changelog: =============== v2 (15 Apr 2022) * Make FF-A bus discoverable (remove device tree support, bind and probe FF-A device using device_bind and device_probe)
v1 (29 Mar 2022) * FF-A bus driver with device tree support * armffa command * FF-A Sandbox driver * FF-A Sandbox test cases * FF-A MM communication

On Mon, May 09, 2022 at 11:55:14AM +0100, Abdellatif El Khlifi wrote:
On Fri, Apr 15, 2022 at 11:43:25AM -0400, Tom Rini wrote:
On Fri, Apr 15, 2022 at 01:27:57PM +0100, abdellatif.elkhlifi@arm.com wrote:
From: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
This patchset adds support for Arm FF-A (Arm Firmware Framework for Armv8-A v1.0).
FF-A support is generic by design and can be used by any Arm platform.
The features added are as follows:
1/ FF-A bus driver 2/ armffa command 3/ FF-A Sandbox driver 4/ FF-A Sandbox test cases 5/ FF-A MM communication
The suggested design considers FF-A as a discoverable bus. 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 Secure World firmware runs under TrustZone HW (such as Optee). The same approach was followed in the FF-A driver in Linux kernel (https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/driv...))
Cc: Tom Rini trini@konsulko.com Cc: Rob Herring robh@kernel.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Achin Gupta achin.gupta@arm.com Cc: Simon Glass sjg@chromium.org Cc: Vishnu Banavath vishnu.banavath@arm.com
I don't see a changelog compared with v1. Are any of the comments there addressed? Thanks.
-- Tom
Thanks Tom. I'll add a changelog for future versions.
The current Changelog:
Changelog:
v2 (15 Apr 2022)
- Make FF-A bus discoverable (remove device tree support, bind and probe FF-A device using device_bind and device_probe)
v1 (29 Mar 2022)
- FF-A bus driver with device tree support
- armffa command
- FF-A Sandbox driver
- FF-A Sandbox test cases
- FF-A MM communication
Hello guys, gentle ping, any feedback about the implementation of FF-A as a discoverable bus ?

Hi Abdellatif,
On Thu, May 12, 2022 at 03:04:38PM +0100, Abdellatif El Khlifi wrote:
On Mon, May 09, 2022 at 11:55:14AM +0100, Abdellatif El Khlifi wrote:
On Fri, Apr 15, 2022 at 11:43:25AM -0400, Tom Rini wrote:
On Fri, Apr 15, 2022 at 01:27:57PM +0100, abdellatif.elkhlifi@arm.com wrote:
From: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
This patchset adds support for Arm FF-A (Arm Firmware Framework for Armv8-A v1.0).
FF-A support is generic by design and can be used by any Arm platform.
The features added are as follows:
1/ FF-A bus driver 2/ armffa command 3/ FF-A Sandbox driver 4/ FF-A Sandbox test cases 5/ FF-A MM communication
The suggested design considers FF-A as a discoverable bus. 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 Secure World firmware runs under TrustZone HW (such as Optee). The same approach was followed in the FF-A driver in Linux kernel (https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/driv...))
Cc: Tom Rini trini@konsulko.com Cc: Rob Herring robh@kernel.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Achin Gupta achin.gupta@arm.com Cc: Simon Glass sjg@chromium.org Cc: Vishnu Banavath vishnu.banavath@arm.com
I don't see a changelog compared with v1. Are any of the comments there addressed? Thanks.
-- Tom
Thanks Tom. I'll add a changelog for future versions.
The current Changelog:
Changelog:
v2 (15 Apr 2022)
- Make FF-A bus discoverable (remove device tree support, bind and probe FF-A device using device_bind and device_probe)
v1 (29 Mar 2022)
- FF-A bus driver with device tree support
- armffa command
- FF-A Sandbox driver
- FF-A Sandbox test cases
- FF-A MM communication
Hello guys, gentle ping, any feedback about the implementation of FF-A as a discoverable bus ?
Apologies for the late reply, I was on vacation. I'll try having a closer look on patch #1-5 in the upcoming days. Can you take a look at my comments on patch #6 and reply?
Thanks /Ilias

This new version of the patchset provides improvements to the FF-A driver design.
The FF-A driver is a discoverable bus and brings these new features:
* support for the 64-bit x0-x17 registers in SMC arguments passing * align with the FF-A kernel driver interfaces * make the driver EFI independent
This patchset will be followed by further updates (work in progress):
* discover MM partitions at runtime * align with the FF-A kernel driver error handling * align Sandbox driver and tests with the new interfaces
Changelog: ===============
v3:
* port x0-x17 registers support from linux kernel as defined by SMCCCv1.2 * align the interfaces of the u-boot FF-A driver with those in the linux FF-A driver * remove the FF-A helper layer * make the u-boot FF-A driver independent from EFI * provide an optional config that enables copying the driver data to EFI runtime section at ExitBootServices service * use 64-bit version of FFA_RXTX_MAP, FFA_MSG_SEND_DIRECT_{REQ, RESP} * update armffa command with the new driver interfaces
v2 [2]:
* make FF-A bus discoverable using device_{bind, probe} APIs * remove device tree support
v1 [1]:
* introduce FF-A bus driver with device tree support * introduce armffa command * introduce FF-A Sandbox driver * add FF-A Sandbox test cases * introduce FF-A MM communication
Cheers, Abdellatif
[1]: https://lore.kernel.org/all/20220329151659.16894-1-abdellatif.elkhlifi@arm.c... [2]: https://lore.kernel.org/all/20220415122803.16666-1-abdellatif.elkhlifi@arm.c...
Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Achin Gupta achin.gupta@arm.com Cc: Vishnu Banavath vishnu.banavath@arm.com Cc: Xueliang Zhong Xueliang.Zhong@arm.com
Abdellatif El Khlifi (4): arm64: smccc: add Xn registers support used by SMC calls arm64: smccc: clear the Xn registers after SMC calls arm_ffa: introduce Arm FF-A low-level driver arm_ffa: introduce armffa command
MAINTAINERS | 7 + arch/arm/cpu/armv8/smccc-call.S | 73 ++ arch/arm/lib/asm-offsets.c | 13 + cmd/Kconfig | 10 + cmd/Makefile | 2 + cmd/armffa.c | 246 ++++ common/board_r.c | 7 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/arm-ffa/Kconfig | 34 + drivers/arm-ffa/Makefile | 7 + drivers/arm-ffa/arm-ffa-uclass.c | 16 + drivers/arm-ffa/arm_ffa_prv.h | 219 ++++ drivers/arm-ffa/core.c | 1338 ++++++++++++++++++++ drivers/arm-ffa/efi_ffa_runtime_data_mgr.c | 94 ++ include/arm_ffa.h | 132 ++ include/dm/uclass-id.h | 1 + include/linux/arm-smccc.h | 43 + include/uuid.h | 8 + lib/efi_loader/efi_boottime.c | 17 + lib/uuid.c | 65 + 21 files changed, 2335 insertions(+) create mode 100644 cmd/armffa.c create mode 100644 drivers/arm-ffa/Kconfig create mode 100644 drivers/arm-ffa/Makefile create mode 100644 drivers/arm-ffa/arm-ffa-uclass.c create mode 100644 drivers/arm-ffa/arm_ffa_prv.h create mode 100644 drivers/arm-ffa/core.c create mode 100644 drivers/arm-ffa/efi_ffa_runtime_data_mgr.c create mode 100644 include/arm_ffa.h

use x0-x17 registers in the SMC32/SMC64 calls according to SMCCCv1.2
Signed-off-by: Sudeep Holla sudeep.holla@arm.com Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org --- arch/arm/cpu/armv8/smccc-call.S | 53 +++++++++++++++++++++++++++++++++ arch/arm/lib/asm-offsets.c | 13 ++++++++ include/linux/arm-smccc.h | 43 ++++++++++++++++++++++++++ 3 files changed, 109 insertions(+)
diff --git a/arch/arm/cpu/armv8/smccc-call.S b/arch/arm/cpu/armv8/smccc-call.S index dc92b28777..ec6f299bc9 100644 --- a/arch/arm/cpu/armv8/smccc-call.S +++ b/arch/arm/cpu/armv8/smccc-call.S @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2015, Linaro Limited + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */ #include <linux/linkage.h> #include <linux/arm-smccc.h> @@ -45,3 +47,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 22fd541f9a..b6bd1b32b0 100644 --- a/arch/arm/lib/asm-offsets.c +++ b/arch/arm/lib/asm-offsets.c @@ -9,6 +9,8 @@ * generate asm statements containing #defines, * compile this file to assembler, and then extract the * #defines from the assembly-language output. + * + * (C) Copyright 2022 ARM Limited */
#include <common.h> @@ -117,6 +119,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 e1d09884a1..9105031d55 100644 --- a/include/linux/arm-smccc.h +++ b/include/linux/arm-smccc.h @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2015, Linaro Limited + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */ #ifndef __LINUX_ARM_SMCCC_H #define __LINUX_ARM_SMCCC_H @@ -70,6 +72,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

On Mon, Aug 01, 2022 at 06:20:50PM +0100, Abdellatif El Khlifi wrote:
use x0-x17 registers in the SMC32/SMC64 calls according to SMCCCv1.2
Signed-off-by: Sudeep Holla sudeep.holla@arm.com
Please drop my signed-off as I didn't. I am seeing this patch on the list for the first time and AFAIK I haven't posted any previous version of the patch that you are picking up.
IIUC, you are importing this code from the Linux kernel tree which I assume is fine. You can just mention the same and must not add anyone's sign-off without consulting them.
OTH, if you cherry-pick the commit as is and use it in u-boot tree(e.g. DTS patches), then you can retain them. That is my understanding. Happy to be corrected if my assumptions are not correct here.
-- Regards, Sudeep

On Mon, Aug 01, 2022 at 07:41:16PM +0100, Sudeep Holla wrote:
On Mon, Aug 01, 2022 at 06:20:50PM +0100, Abdellatif El Khlifi wrote:
use x0-x17 registers in the SMC32/SMC64 calls according to SMCCCv1.2
Signed-off-by: Sudeep Holla sudeep.holla@arm.com
Please drop my signed-off as I didn't. I am seeing this patch on the list for the first time and AFAIK I haven't posted any previous version of the patch that you are picking up.
IIUC, you are importing this code from the Linux kernel tree which I assume is fine. You can just mention the same and must not add anyone's sign-off without consulting them.
OTH, if you cherry-pick the commit as is and use it in u-boot tree(e.g. DTS patches), then you can retain them. That is my understanding. Happy to be corrected if my assumptions are not correct here.
-- Regards, Sudeep
No worries. I'll remove the signed-off. I just wanted to give credit to the original author :)
For the FF-A driver in u-boot, only the SMC part of the kernel patch is needed. No need for HVC support at the moment. The SMC part has been tested and proven to work. Hence, this patch.
I'll rename this commit to:
arm64: smccc: add support for SMCCCv1.2 x0-x17 registers
I'll also state in the commit description that this work is inspired from the following kernel commit:
arm64: smccc: Add support for SMCCCv1.2 extended input/output registers
You're welcome to provide review comments.
Cheers, Abdellatif

set to zero the x0-x17 registers
As per the SMCCC v1.2 spec, unused result and scratch registers can leak information after an SMC call. We can mitigate against this risk by returning zero in each register.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org --- arch/arm/cpu/armv8/smccc-call.S | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+)
diff --git a/arch/arm/cpu/armv8/smccc-call.S b/arch/arm/cpu/armv8/smccc-call.S index ec6f299bc9..8ac3e461e4 100644 --- a/arch/arm/cpu/armv8/smccc-call.S +++ b/arch/arm/cpu/armv8/smccc-call.S @@ -84,6 +84,26 @@ ENDPROC(__arm_smccc_hvc) stp x14, x15, [x19, #ARM_SMCCC_1_2_REGS_X14_OFFS] stp x16, x17, [x19, #ARM_SMCCC_1_2_REGS_X16_OFFS]
+ /* x0-x17 registers can leak information after an SMC or HVC call. Let's clear them */ + mov x0, xzr + mov x1, xzr + mov x2, xzr + mov x3, xzr + mov x4, xzr + mov x5, xzr + mov x6, xzr + mov x7, xzr + mov x8, xzr + mov x9, xzr + mov x10, xzr + mov x11, xzr + mov x12, xzr + mov x13, xzr + mov x14, xzr + mov x15, xzr + mov x16, xzr + mov x17, xzr + /* Restore original x19 */ ldp xzr, x19, [sp], #16 ret

On Mon, Aug 1, 2022 at 7:21 PM Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
set to zero the x0-x17 registers
As per the SMCCC v1.2 spec, unused result and scratch registers can leak information after an SMC call. We can mitigate against this risk by returning zero in each register.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
arch/arm/cpu/armv8/smccc-call.S | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+)
diff --git a/arch/arm/cpu/armv8/smccc-call.S b/arch/arm/cpu/armv8/smccc-call.S index ec6f299bc9..8ac3e461e4 100644 --- a/arch/arm/cpu/armv8/smccc-call.S +++ b/arch/arm/cpu/armv8/smccc-call.S @@ -84,6 +84,26 @@ ENDPROC(__arm_smccc_hvc) stp x14, x15, [x19, #ARM_SMCCC_1_2_REGS_X14_OFFS] stp x16, x17, [x19, #ARM_SMCCC_1_2_REGS_X16_OFFS]
/* x0-x17 registers can leak information after an SMC or HVC call. Let's clear them */
mov x0, xzr
mov x1, xzr
mov x2, xzr
mov x3, xzr
mov x4, xzr
mov x5, xzr
mov x6, xzr
mov x7, xzr
mov x8, xzr
mov x9, xzr
mov x10, xzr
mov x11, xzr
mov x12, xzr
mov x13, xzr
mov x14, xzr
mov x15, xzr
mov x16, xzr
mov x17, xzr
Is this information leakage worse than the information leakage from an ordinary C function? My point is, is this needed?
Thanks, Jens
/* Restore original x19 */ ldp xzr, x19, [sp], #16 ret
-- 2.17.1

On Tue, Aug 16, 2022 at 01:48:31PM +0200, Jens Wiklander wrote:
On Mon, Aug 1, 2022 at 7:21 PM Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
set to zero the x0-x17 registers
As per the SMCCC v1.2 spec, unused result and scratch registers can leak information after an SMC call. We can mitigate against this risk by returning zero in each register.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
arch/arm/cpu/armv8/smccc-call.S | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+)
diff --git a/arch/arm/cpu/armv8/smccc-call.S b/arch/arm/cpu/armv8/smccc-call.S index ec6f299bc9..8ac3e461e4 100644 --- a/arch/arm/cpu/armv8/smccc-call.S +++ b/arch/arm/cpu/armv8/smccc-call.S @@ -84,6 +84,26 @@ ENDPROC(__arm_smccc_hvc) stp x14, x15, [x19, #ARM_SMCCC_1_2_REGS_X14_OFFS] stp x16, x17, [x19, #ARM_SMCCC_1_2_REGS_X16_OFFS]
/* x0-x17 registers can leak information after an SMC or HVC call. Let's clear them */
mov x0, xzr
mov x1, xzr
mov x2, xzr
mov x3, xzr
mov x4, xzr
mov x5, xzr
mov x6, xzr
mov x7, xzr
mov x8, xzr
mov x9, xzr
mov x10, xzr
mov x11, xzr
mov x12, xzr
mov x13, xzr
mov x14, xzr
mov x15, xzr
mov x16, xzr
mov x17, xzr
Is this information leakage worse than the information leakage from an ordinary C function? My point is, is this needed?
The leakage we are referring to is data leakage across exception levels. The intent is to prevent lower exception levels (EL1/EL0) to read the data exchanged at EL2.
The linux kernel clears the general purpose registers before switching to EL0. As far as I know u-boot doesn't.
So, the code above makes sure the registers are cleared. An improved version of this has been releases in this patch: https://lore.kernel.org/all/20220926101723.9965-3-abdellatif.elkhlifi@arm.co...
Thanks, Jens
/* Restore original x19 */ ldp xzr, x19, [sp], #16 ret
-- 2.17.1

Add the driver implementing Arm Firmware Framework for Armv8-A v1.0
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.
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.
32-bit version of the ABIs is supported and 64-bit version of FFA_RXTX_MAP and FFA_MSG_SEND_DIRECT_{REQ, RESP}.
In u-boot FF-A design, FF-A is considered as a discoverable bus. 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:
- partition_info_get - sync_send_receive - rxtx_unmap
This implementation provides an optional feature to copy the driver data to EFI runtime area.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org --- MAINTAINERS | 6 + common/board_r.c | 7 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/arm-ffa/Kconfig | 33 + drivers/arm-ffa/Makefile | 7 + drivers/arm-ffa/arm-ffa-uclass.c | 16 + drivers/arm-ffa/arm_ffa_prv.h | 219 ++++ drivers/arm-ffa/core.c | 1338 ++++++++++++++++++++ drivers/arm-ffa/efi_ffa_runtime_data_mgr.c | 94 ++ include/arm_ffa.h | 132 ++ include/dm/uclass-id.h | 1 + include/uuid.h | 8 + lib/efi_loader/efi_boottime.c | 17 + lib/uuid.c | 65 + 15 files changed, 1946 insertions(+) create mode 100644 drivers/arm-ffa/Kconfig create mode 100644 drivers/arm-ffa/Makefile create mode 100644 drivers/arm-ffa/arm-ffa-uclass.c create mode 100644 drivers/arm-ffa/arm_ffa_prv.h create mode 100644 drivers/arm-ffa/core.c create mode 100644 drivers/arm-ffa/efi_ffa_runtime_data_mgr.c create mode 100644 include/arm_ffa.h
diff --git a/MAINTAINERS b/MAINTAINERS index f371d864f2..93a57f4df2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -248,6 +248,12 @@ 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 abdellatif.elkhlifi@arm.com +S: Maintained +F: drivers/arm-ffa/ +F: include/arm_ffa.h + ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de M: Fabio Estevam festevam@gmail.com diff --git a/common/board_r.c b/common/board_r.c index ed29069d2d..2d3f359b96 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -63,6 +63,10 @@ #include <efi_loader.h> #include <relocate.h>
+#ifdef CONFIG_ARM_FFA_TRANSPORT +#include <arm_ffa.h> +#endif + DECLARE_GLOBAL_DATA_PTR;
ulong monitor_flash_len; @@ -767,6 +771,9 @@ static init_fnc_t init_sequence_r[] = { INIT_FUNC_WATCHDOG_RESET initr_net, #endif +#ifdef CONFIG_ARM_FFA_TRANSPORT + ffa_bus_discover, +#endif #ifdef CONFIG_POST initr_post, #endif diff --git a/drivers/Kconfig b/drivers/Kconfig index 8b6fead351..60b5fd4d9d 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -6,6 +6,8 @@ source "drivers/core/Kconfig"
source "drivers/adc/Kconfig"
+source "drivers/arm-ffa/Kconfig" + source "drivers/ata/Kconfig"
source "drivers/axi/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index eba9940231..dfde371381 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -110,6 +110,7 @@ obj-y += iommu/ obj-y += smem/ obj-y += thermal/ obj-$(CONFIG_TEE) += tee/ +obj-$(CONFIG_ARM_FFA_TRANSPORT) += arm-ffa/ obj-y += axi/ obj-y += ufs/ obj-$(CONFIG_W1) += w1/ diff --git a/drivers/arm-ffa/Kconfig b/drivers/arm-ffa/Kconfig new file mode 100644 index 0000000000..882ffde90f --- /dev/null +++ b/drivers/arm-ffa/Kconfig @@ -0,0 +1,33 @@ +# 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 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. + + This driver 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. + 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. + +config ARM_FFA_EFI_RUNTIME_MODE + bool "Enable EFI runtime support for FF-A data and code " + depends on ARM_FFA_TRANSPORT && EFI_LOADER + help + Allows FF-A driver data structures and code to be accessible at EFI runtime diff --git a/drivers/arm-ffa/Makefile b/drivers/arm-ffa/Makefile new file mode 100644 index 0000000000..0b9b0a61b4 --- /dev/null +++ b/drivers/arm-ffa/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2022 Abdellatif El Khlifi abdellatif.elkhlifi@arm.com +# + +obj-y += arm-ffa-uclass.o core.o +obj-$(CONFIG_ARM_FFA_EFI_RUNTIME_MODE) += efi_ffa_runtime_data_mgr.o diff --git a/drivers/arm-ffa/arm-ffa-uclass.c b/drivers/arm-ffa/arm-ffa-uclass.c new file mode 100644 index 0000000000..7d9695d289 --- /dev/null +++ b/drivers/arm-ffa/arm-ffa-uclass.c @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <dm.h> +#include <asm/global_data.h> + +DECLARE_GLOBAL_DATA_PTR; + +UCLASS_DRIVER(ffa) = { + .name = "ffa", + .id = UCLASS_FFA, +}; diff --git a/drivers/arm-ffa/arm_ffa_prv.h b/drivers/arm-ffa/arm_ffa_prv.h new file mode 100644 index 0000000000..1133c02400 --- /dev/null +++ b/drivers/arm-ffa/arm_ffa_prv.h @@ -0,0 +1,219 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __ARM_FFA_PRV_H +#define __ARM_FFA_PRV_H + +#include <arm_ffa.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/arm-smccc.h> + +/* + * This header is private. It is exclusively used by the FF-A driver + */ + +/* FF-A core driver name */ +#define FFA_DRV_NAME "arm_ffa" + +/* 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)) + +#define FFA_VERSION FFA_SMC_32(0x63) +#define FFA_ID_GET FFA_SMC_32(0x69) +#define FFA_FEATURES FFA_SMC_32(0x64) +#define FFA_PARTITION_INFO_GET FFA_SMC_32(0x68) +#define FFA_RXTX_UNMAP FFA_SMC_32(0x67) +#define FFA_RX_RELEASE FFA_SMC_32(0x65) +#define FFA_RUN FFA_SMC_32(0x6D) +#define FFA_ERROR FFA_SMC_32(0x60) +#define FFA_SUCCESS FFA_SMC_32(0x61) +#define FFA_INTERRUPT FFA_SMC_32(0x62) +#define FFA_RXTX_MAP FFA_SMC_64(0x66) +#define FFA_MSG_SEND_DIRECT_REQ FFA_SMC_64(0x6F) +#define FFA_MSG_SEND_DIRECT_RESP FFA_SMC_64(0x70) + +/* The FF-A SMC function definitions */ + +typedef struct arm_smccc_1_2_regs ffa_value_t; +typedef void (*invoke_ffa_fn_t)(ffa_value_t args, ffa_value_t *res); + +/* FF-A error codes */ +#define FFA_ERR_STAT_NOT_SUPPORTED (-1) +#define FFA_ERR_STAT_INVALID_PARAMETERS (-2) +#define FFA_ERR_STAT_NO_MEMORY (-3) +#define FFA_ERR_STAT_BUSY (-4) +#define FFA_ERR_STAT_INTERRUPTED (-5) +#define FFA_ERR_STAT_DENIED (-6) +#define FFA_ERR_STAT_RETRY (-7) +#define FFA_ERR_STAT_ABORTED (-8) + +/* UUID data size */ +#define UUID_SIZE (16) + +/* + * union ffa_partition_uuid - Data union hosting the UUID + * transmitted by FFA_PARTITION_INFO_GET + * @words: data structure giving 32-bit words access to the UUID data + * @bytes: data structure giving byte access to the UUID data + * + * The structure holds little-endian UUID data. + */ +union ffa_partition_uuid { + struct __packed words { + u32 a1; /* w1 */ + u32 a2; /* w2 */ + u32 a3; /* w3 */ + u32 a4; /* w4 */ + } words; + u8 bytes[UUID_SIZE]; +}; + +/** + * struct ffa_features_desc - FF-A functions features + * @func_id: FF-A function + * @field1: features read from register w2 + * @field2: features read from register w3 + * + * Data structure describing the features of the FF-A functions queried by + * FFA_FEATURES + */ +struct ffa_features_desc { + u32 func_id; + u32 field1; + u64 field2; +}; + +/** + * enum ffa_rxtx_buf_sizes - minimum sizes supported + * for the RX/TX buffers + */ +enum ffa_rxtx_buf_sizes { + RXTX_4K, + RXTX_64K, + RXTX_16K +}; + +/* + * Number of the FF-A interfaces features descriptors + * currently only FFA_RXTX_MAP descriptor is supported + */ +#define FFA_FEATURE_DESC_CNT (1) + +/** + * struct ffa_rxtxpair - structure hosting the RX/TX buffers virtual addresses + * @rxbuf: virtual address of the RX buffer + * @txbuf: virtual address of the TX buffer + * + * Data structure hosting 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 { + u64 rxbuf; /* virtual address */ + u64 txbuf; /* virtual address */ +}; + +/** + * struct ffa_partition_desc - the secure partition descriptor + * @info: partition information + * @UUID: UUID + * + * Each partition has its descriptor containing the partitions information and the UUID + */ +struct ffa_partition_desc { + struct ffa_partition_info info; + union ffa_partition_uuid UUID; +}; + +/** + * struct ffa_partitions - descriptors for all secure partitions + * @count: The number of partitions descriptors + * @descs The partitions descriptors table + * + * This data structure contains the partitions descriptors table + */ +struct ffa_partitions { + u32 count; + struct ffa_partition_desc *descs; /* virtual address */ +}; + +/** + * struct ffa_prvdata - the driver private data structure + * + * @dev: The arm_ffa device under u-boot driver model + * @ffa_ops: The driver operations structure + * @fwk_version: FF-A framework version + * @id: u-boot endpoint ID + * @partitions: The partitions descriptors structure + * @pair: The RX/TX buffers pair + * @invoke_ffa_fn: The function executing the FF-A function + * @features: Table of the FF-A functions having features + * + * The driver data structure hosting all resident data. + */ +struct ffa_prvdata { + struct udevice *dev; + struct ffa_bus_ops ffa_ops; + u32 fwk_version; + u16 id; + struct ffa_partitions partitions; + struct ffa_rxtxpair pair; + invoke_ffa_fn_t invoke_ffa_fn; + struct ffa_features_desc features[FFA_FEATURE_DESC_CNT]; +}; + +/** + * ffa_device_get - create, bind and probe the arm_ffa device + */ +int ffa_device_get(void); + +/** + * ffa_bus_prvdata_get - bus driver private data getter + */ +struct ffa_prvdata **ffa_bus_prvdata_get(void); + +#endif diff --git a/drivers/arm-ffa/core.c b/drivers/arm-ffa/core.c new file mode 100644 index 0000000000..40d140fc3e --- /dev/null +++ b/drivers/arm-ffa/core.c @@ -0,0 +1,1338 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include "arm_ffa_prv.h" +#include <asm/global_data.h> +#include <common.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <dm/devres.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> +#include <log.h> +#include <malloc.h> +#include <string.h> +#include <uuid.h> + +DECLARE_GLOBAL_DATA_PTR; + +/** + * The device private data structure containing all the resident + * data read from secure world + */ +__ffa_runtime_data struct ffa_prvdata *ffa_priv_data; + +/* + * Driver functions + */ + +/** + * ffa_remove_device - removes the arm_ffa device + * @dev: the device to be removed + * + * This boot time function makes sure the arm_ffa device is removed + * No need to free the kmalloced data when the device is destroyed. + * It's automatically done by devm management. + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +int ffa_remove_device(struct udevice *dev) +{ + int ret; + + if (!dev) + return -ENODEV; + + ret = device_remove(dev, DM_REMOVE_NORMAL); + if (ret) { + ffa_err("Unable to remove. err:%d\n", ret); + return ret; + } + + ret = device_unbind(dev); + if (ret) { + ffa_err("Unable to unbind. err:%d\n", ret); + return ret; + } + + return FFA_ERR_STAT_SUCCESS; +} + +/** + * ffa_device_get - create, bind and probe the arm_ffa device + * + * This boot time function makes sure the arm_ffa device is + * created, bound to this driver, probed and ready to use. + * Arm FF-A transport is implemented through a single u-boot + * device managing the FF-A bus (arm_ffa). + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +int ffa_device_get(void) +{ + int ret; + struct udevice *dev = NULL; + + if (ffa_priv_data && ffa_priv_data->dev) + return FFA_ERR_STAT_SUCCESS; + + ret = device_bind(dm_root(), + DM_DRIVER_GET(arm_ffa), + FFA_DRV_NAME, + NULL, + ofnode_null(), + &dev); + if (ret) + return ret; + + /* The FF-A bus discovery succeeds when probing is successful */ + ret = device_probe(dev); + if (ret) { + ffa_err("arm_ffa device probing failed"); + ffa_remove_device(dev); + return ret; + } + + return FFA_ERR_STAT_SUCCESS; +} + +/** + * ffa_get_version - FFA_VERSION handler function + * + * This is the boot time function that implements FFA_VERSION FF-A function + * to get from the secure world the FF-A framework version + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +static int ffa_get_version(void) +{ + u16 major, minor; + ffa_value_t res = {0}; + + if (!ffa_priv_data->invoke_ffa_fn) + panic("[FFA] no private data found\n"); + + ffa_priv_data->invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_VERSION, + .a1 = FFA_VERSION_1_0, .a2 = 0, .a3 = 0, .a4 = 0, .a5 = 0, .a6 = 0, .a7 = 0, + }, &res); + + if (((int)res.a0) == FFA_ERR_STAT_NOT_SUPPORTED) { + ffa_err("A Firmware Framework implementation does not exist"); + return -EOPNOTSUPP; + } + + major = GET_FFA_MAJOR_VERSION((u32)res.a0); + minor = GET_FFA_MINOR_VERSION((u32)res.a0); + + ffa_info("FF-A driver %d.%d\nFF-A framework %d.%d", + FFA_MAJOR_VERSION, FFA_MINOR_VERSION, major, minor); + + if ((major == FFA_MAJOR_VERSION && minor >= FFA_MINOR_VERSION)) { + ffa_info("Versions are compatible "); + + ffa_priv_data->fwk_version = (u32)res.a0; + + return FFA_ERR_STAT_SUCCESS; + } + + ffa_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 + * + * This is the boot time function that implements FFA_ID_GET FF-A function + * to get from the secure world u-boot endpoint ID + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +static int ffa_get_endpoint_id(void) +{ + ffa_value_t res = {0}; + + if (!ffa_priv_data->invoke_ffa_fn) + panic("[FFA] no private data found\n"); + + ffa_priv_data->invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_ID_GET, + .a1 = 0, .a2 = 0, .a3 = 0, .a4 = 0, .a5 = 0, .a6 = 0, .a7 = 0, + }, &res); + + switch (res.a0) { + case FFA_ERROR: + + if (((int)res.a2) == FFA_ERR_STAT_NOT_SUPPORTED) { + ffa_err("This function is not implemented at this FF-A instance"); + return -EOPNOTSUPP; + } + + ffa_err("Undefined error code (%d)", ((int)res.a2)); + return -EINVAL; + + case FFA_SUCCESS: + + ffa_priv_data->id = GET_SELF_ENDPOINT_ID((u32)res.a2); + ffa_info("endpoint ID is %u", ffa_priv_data->id); + + return FFA_ERR_STAT_SUCCESS; + + default: + + ffa_err("Undefined response function (0x%lx)", res.a0); + return -EINVAL; + } +} + +/** + * ffa_get_features_desc - returns the features descriptor of the specified + * FF-A function + * @func_id: the FF-A function which the features are to be retrieved + * + * This is a boot time function that searches the features descriptor of the + * specified FF-A function + * + * Return: + * + * When found, the address of the features descriptor is returned. Otherwise, NULL. + */ +static struct ffa_features_desc *ffa_get_features_desc(u32 func_id) +{ + u32 desc_idx; + + /* + * search for the descriptor of the selected FF-A interface + */ + for (desc_idx = 0; desc_idx < FFA_FEATURE_DESC_CNT ; desc_idx++) + if (ffa_priv_data->features[desc_idx].func_id == func_id) + return &ffa_priv_data->features[desc_idx]; + + return NULL; +} + +/** + * ffa_get_rxtx_map_features - FFA_FEATURES handler function with FFA_RXTX_MAP + * argument + * + * This is the boot time function that implements FFA_FEATURES FF-A function + * to retrieve the FFA_RXTX_MAP features + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +static int ffa_get_rxtx_map_features(void) +{ + ffa_value_t res = {0}; + + if (!ffa_priv_data->invoke_ffa_fn) + panic("[FFA] no private data found\n"); + + ffa_priv_data->invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_FEATURES, + .a1 = FFA_RXTX_MAP, + .a2 = 0, .a3 = 0, .a4 = 0, .a5 = 0, .a6 = 0, .a7 = 0, + }, &res); + + switch (res.a0) { + case FFA_ERROR: + { + if (((int)res.a2) == FFA_ERR_STAT_NOT_SUPPORTED) { + ffa_err("FFA_RXTX_MAP is not implemented at this FF-A instance"); + return -EOPNOTSUPP; + } + + ffa_err("Undefined error code (%d)", ((int)res.a2)); + return -EINVAL; + } + case FFA_SUCCESS: + { + u32 desc_idx; + + /* + * search for an empty descriptor + */ + for (desc_idx = 0; desc_idx < FFA_FEATURE_DESC_CNT ; desc_idx++) + if (!ffa_priv_data->features[desc_idx].func_id) { + /* + * populate the descriptor with + * the interface features data + */ + ffa_priv_data->features[desc_idx].func_id = + FFA_RXTX_MAP; + ffa_priv_data->features[desc_idx].field1 = + (u32)res.a2; + + ffa_info("FFA_RXTX_MAP features data 0x%x", + ffa_priv_data->features[desc_idx].field1); + + return FFA_ERR_STAT_SUCCESS; + } + + ffa_err("Cannot save FFA_RXTX_MAP features data. Descriptors table full"); + return -ENOBUFS; + } + default: + { + ffa_err("Undefined response function (0x%lx)", + res.a0); + return -EINVAL; + } + } +} + +/** + * ffa_get_rxtx_buffers_pages_cnt - reads from the features data descriptors + * the minimum number of pages in each of the RX/TX + * buffers + * @buf_4k_pages: Pointer to the minimum number of pages + * + * This is the boot time function that returns the minimum number of pages + * in each of the RX/TX buffers + * + * Return: + * + * buf_4k_pages points to the returned number of pages + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +static int ffa_get_rxtx_buffers_pages_cnt(size_t *buf_4k_pages) +{ + struct ffa_features_desc *desc = NULL; + + if (!buf_4k_pages) + return -EINVAL; + + desc = ffa_get_features_desc(FFA_RXTX_MAP); + if (!desc) + return -EINVAL; + + switch (desc->field1) { + case RXTX_4K: + *buf_4k_pages = 1; + break; + case RXTX_16K: + *buf_4k_pages = 4; + break; + case RXTX_64K: + *buf_4k_pages = 16; + break; + default: + ffa_err("RX/TX buffer size not supported"); + return -EINVAL; + } + + return FFA_ERR_STAT_SUCCESS; +} + +/** + * ffa_free_rxtx_buffers - frees the RX/TX buffers + * @buf_4k_pages: the minimum number of pages in each of the RX/TX + * buffers + * + * This is the boot time function used to free the RX/TX buffers + * + */ +static void ffa_free_rxtx_buffers(void) +{ + ffa_info("Freeing RX/TX buffers"); + + if (ffa_priv_data->pair.rxbuf) { + free((void *)ffa_priv_data->pair.rxbuf); + ffa_priv_data->pair.rxbuf = 0; + } + + if (ffa_priv_data->pair.txbuf) { + free((void *)ffa_priv_data->pair.txbuf); + ffa_priv_data->pair.txbuf = 0; + } +} + +/** + * ffa_alloc_rxtx_buffers - allocates the RX/TX buffers + * @buf_4k_pages: the minimum number of pages in each of the RX/TX + * buffers + * + * This is the boot time function 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. + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +static int ffa_alloc_rxtx_buffers(size_t buf_4k_pages) +{ + u64 bytes; + + ffa_info("Using %lu 4KB page(s) for RX/TX buffers size", buf_4k_pages); + + bytes = buf_4k_pages * SZ_4K; + + /* RX/TX buffers addresses should be PAGE_SIZE aligned */ + + ffa_priv_data->pair.rxbuf = (u64)memalign(PAGE_SIZE, bytes); + if (!ffa_priv_data->pair.rxbuf) { + ffa_err("Failure to allocate RX buffer"); + return -ENOBUFS; + } + + ffa_info("RX buffer at virtual address 0x%llx", ffa_priv_data->pair.rxbuf); + + ffa_priv_data->pair.txbuf = (u64)memalign(PAGE_SIZE, bytes); + if (!ffa_priv_data->pair.txbuf) { + free((void *)ffa_priv_data->pair.rxbuf); + ffa_priv_data->pair.rxbuf = 0; + ffa_err("Failure to allocate the TX buffer"); + return -ENOBUFS; + } + + ffa_info("TX buffer at virtual address 0x%llx", ffa_priv_data->pair.txbuf); + + /* + * make sure the buffers are cleared before use + */ + memset((void *)ffa_priv_data->pair.rxbuf, 0, bytes); + memset((void *)ffa_priv_data->pair.txbuf, 0, bytes); + + return FFA_ERR_STAT_SUCCESS; +} + +/** + * ffa_map_rxtx_buffers - FFA_RXTX_MAP handler function + * @buf_4k_pages: the minimum number of pages in each of the RX/TX + * buffers + * + * This is the boot time function that implements FFA_RXTX_MAP FF-A function + * to map the RX/TX buffers + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +static int ffa_map_rxtx_buffers(size_t buf_4k_pages) +{ + int ret; + ffa_value_t res = {0}; + + if (!ffa_priv_data->invoke_ffa_fn) + panic("[FFA] no private data found\n"); + + ret = ffa_alloc_rxtx_buffers(buf_4k_pages); + if (ret != FFA_ERR_STAT_SUCCESS) + 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 + */ + + ffa_priv_data->invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_RXTX_MAP, + .a1 = ffa_priv_data->pair.txbuf, + .a2 = ffa_priv_data->pair.rxbuf, + .a3 = buf_4k_pages, + .a4 = 0, .a5 = 0, .a6 = 0, .a7 = 0, + }, &res); + + switch (res.a0) { + case FFA_ERROR: + { + switch (((int)res.a2)) { + case FFA_ERR_STAT_INVALID_PARAMETERS: + ffa_err("One or more fields in input parameters is incorrectly encoded"); + ret = -EPERM; + break; + case FFA_ERR_STAT_NO_MEMORY: + ffa_err("Not enough memory"); + ret = -ENOMEM; + break; + case FFA_ERR_STAT_DENIED: + ffa_err("Buffer pair already registered"); + ret = -EACCES; + break; + case FFA_ERR_STAT_NOT_SUPPORTED: + ffa_err("This function is not implemented at this FF-A instance"); + ret = -EOPNOTSUPP; + break; + default: + ffa_err("Undefined error (%d)", + ((int)res.a2)); + ret = -EINVAL; + } + break; + } + case FFA_SUCCESS: + ffa_info("RX/TX buffers mapped"); + return FFA_ERR_STAT_SUCCESS; + default: + ffa_err("Undefined response function (0x%lx)", + res.a0); + ret = -EINVAL; + } + + ffa_free_rxtx_buffers(); + + return ret; +} + +/** + * ffa_unmap_rxtx_buffers - FFA_RXTX_UNMAP handler function + * + * This is the boot time function that implements FFA_RXTX_UNMAP FF-A function + * to unmap the RX/TX buffers + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +static int ffa_unmap_rxtx_buffers(void) +{ + ffa_value_t res = {0}; + + if (!ffa_priv_data->invoke_ffa_fn) + panic("[FFA] no private data found\n"); + + ffa_priv_data->invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_RXTX_UNMAP, + .a1 = PREP_SELF_ENDPOINT_ID(ffa_priv_data->id), + .a2 = 0, .a3 = 0, .a4 = 0, .a5 = 0, .a6 = 0, .a7 = 0, + }, &res); + + switch (res.a0) { + case FFA_ERROR: + { + if (((int)res.a2) == FFA_ERR_STAT_NOT_SUPPORTED) + panic("[FFA] FFA_RXTX_UNMAP is not implemented at this FF-A instance\n"); + else if (((int)res.a2) == FFA_ERR_STAT_INVALID_PARAMETERS) + panic("[FFA] There is no buffer pair registered on behalf of the caller\n"); + else + panic("[FFA] Undefined error (%d)\n", ((int)res.a2)); + } + case FFA_SUCCESS: + { + ffa_free_rxtx_buffers(); + + return FFA_ERR_STAT_SUCCESS; + } + default: + panic("[FFA] Undefined response function (0x%lx)", res.a0); + return -ENODEV; + } +} + +/** + * ffa_release_rx_buffer - FFA_RX_RELEASE handler function + * + * This is the boot time function that invokes FFA_RX_RELEASE FF-A function + * to release the ownership of the RX buffer + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +static int ffa_release_rx_buffer(void) +{ + ffa_value_t res = {0}; + + if (!ffa_priv_data->invoke_ffa_fn) + panic("[FFA] no private data found\n"); + + ffa_priv_data->invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_RX_RELEASE, + .a1 = 0, .a2 = 0, .a3 = 0, .a4 = 0, .a5 = 0, .a6 = 0, .a7 = 0, + }, &res); + + switch (res.a0) { + case FFA_ERROR: + { + if (((int)res.a2) == FFA_ERR_STAT_NOT_SUPPORTED) + panic("[FFA] FFA_RX_RELEASE is not implemented at this FF-A instance\n"); + else if (((int)res.a2) == FFA_ERR_STAT_DENIED) + panic("[FFA] Caller did not have ownership of the RX buffer\n"); + else + panic("[FFA] Undefined error (%d)\n", ((int)res.a2)); + } + case FFA_SUCCESS: + return FFA_ERR_STAT_SUCCESS; + + default: + panic("[FFA] Undefined response function (0x%lx)\n", res.a0); + return -ENODEV; + } +} + +/** + * ffa_uuid_are_identical - checks whether two given UUIDs are identical + * @uuid1: first UUID + * @uuid2: second UUID + * + * This is a boot time function used by ffa_read_partitions_info to search + * for a UUID in the partitions descriptors table + * + * Return: + * + * 1 when UUIDs match. Otherwise, 0 + */ +int ffa_uuid_are_identical(const union ffa_partition_uuid *uuid1, + const union ffa_partition_uuid *uuid2) +{ + if (!uuid1 || !uuid2) + return 0; + + return (!memcmp(uuid1, uuid2, sizeof(union ffa_partition_uuid))); +} + +/** + * ffa_read_partitions_info - reads the data queried by FFA_PARTITION_INFO_GET + * and saves it in the private structure + * @count: The number of partitions queried + * @part_uuid: Pointer to the partition(s) UUID + * + * This is the boot time function that reads the partitions information + * returned by the FFA_PARTITION_INFO_GET and saves it in the private + * data structure. + * + * Return: + * + * The private data structure is updated with the partition(s) information + * FFA_ERR_STAT_SUCCESS is returned on success. Otherwise, failure + */ +static int ffa_read_partitions_info(u32 count, union ffa_partition_uuid *part_uuid) +{ + if (!count) { + ffa_err("No partition detected"); + return -ENODATA; + } + + ffa_info("Reading partitions data from the RX buffer"); + + if (!part_uuid) { + /* + * querying information of all partitions + */ + u64 buf_bytes; + u64 data_bytes; + size_t buf_4k_pages = 0; + u32 desc_idx; + struct ffa_partition_info *parts_info; + int ret; + + data_bytes = count * sizeof(struct ffa_partition_desc); + + /* + * get the RX buffer size in pages + */ + ret = ffa_get_rxtx_buffers_pages_cnt(&buf_4k_pages); + if (ret != FFA_ERR_STAT_SUCCESS) { + ffa_err("Can not get the RX buffer size (error %d)", ret); + return ret; + } + + buf_bytes = buf_4k_pages * SZ_4K; + + if (data_bytes > buf_bytes) { + ffa_err("Partitions data size exceeds the RX buffer size:"); + ffa_err(" Sizes in bytes: data %llu , RX buffer %llu ", + data_bytes, + buf_bytes); + + return -ENOMEM; + } + + ffa_priv_data->partitions.descs = (struct ffa_partition_desc *) + devm_kmalloc(ffa_priv_data->dev, data_bytes, __GFP_ZERO); + if (!ffa_priv_data->partitions.descs) { + ffa_err("Cannot allocate partitions data buffer"); + return -ENOMEM; + } + + parts_info = (struct ffa_partition_info *)ffa_priv_data->pair.rxbuf; + + for (desc_idx = 0 ; desc_idx < count ; desc_idx++) { + ffa_priv_data->partitions.descs[desc_idx].info = + parts_info[desc_idx]; + + ffa_info("Partition ID %x : info cached", + ffa_priv_data->partitions.descs[desc_idx].info.id); + } + + ffa_priv_data->partitions.count = count; + + ffa_info("%d partition(s) found and cached", count); + + } else { + u32 rx_desc_idx, cached_desc_idx; + struct ffa_partition_info *parts_info; + u8 desc_found; + + parts_info = (struct ffa_partition_info *)ffa_priv_data->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 < ffa_priv_data->partitions.count; + cached_desc_idx++) { + /* + * save the UUID + */ + if (ffa_priv_data->partitions.descs[cached_desc_idx].info.id == + parts_info[rx_desc_idx].id) { + ffa_priv_data->partitions.descs[cached_desc_idx].UUID = + *part_uuid; + + desc_found = 1; + break; + } + } + + if (!desc_found) + return -ENODATA; + } + } + + return FFA_ERR_STAT_SUCCESS; +} + +/** + * ffa_query_partitions_info - invokes FFA_PARTITION_INFO_GET and saves partitions data + * + * @part_uuid: Pointer to the partition(s) UUID + * @pcount: Pointer to the number of partitions variable filled when querying + * + * This is the boot time function that executes the FFA_PARTITION_INFO_GET + * to query the partitions data. Then, it calls ffa_read_partitions_info + * to save the data in the private data structure. + * + * 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 + * FFA_ERR_STAT_SUCCESS is returned on success. Otherwise, failure + */ +static int ffa_query_partitions_info(union ffa_partition_uuid *part_uuid, + u32 *pcount) +{ + union ffa_partition_uuid query_uuid = {0}; + ffa_value_t res = {0}; + + if (!ffa_priv_data->invoke_ffa_fn) + panic("[FFA] no private data found\n"); + + /* + * 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; + } + + ffa_priv_data->invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_PARTITION_INFO_GET, + .a1 = query_uuid.words.a1, + .a2 = query_uuid.words.a2, + .a3 = query_uuid.words.a3, + .a4 = query_uuid.words.a4, + .a5 = 0, + .a6 = 0, + .a7 = 0, + }, &res); + + switch (res.a0) { + case FFA_ERROR: + { + switch (((int)res.a2)) { + case FFA_ERR_STAT_INVALID_PARAMETERS: + ffa_err("Unrecognized UUID"); + return -EPERM; + case FFA_ERR_STAT_NO_MEMORY: + ffa_err("Results cannot fit in RX buffer of the caller"); + return -ENOMEM; + case FFA_ERR_STAT_DENIED: + ffa_err("Callee is not in a state to handle this request"); + return -EACCES; + case FFA_ERR_STAT_NOT_SUPPORTED: + ffa_err("This function is not implemented at this FF-A instance"); + return -EOPNOTSUPP; + case FFA_ERR_STAT_BUSY: + ffa_err("RX buffer of the caller is not free"); + return -EBUSY; + default: + ffa_err("Undefined error (%d)", ((int)res.a2)); + return -EINVAL; + } + } + case 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((u32)res.a2, part_uuid); + if (ret) + ffa_err("Failed to read partition(s) data , error (%d)", ret); + } + + /* + * return the SP count + */ + if (part_uuid) { + if (!ret) + *pcount = (u32)res.a2; + else + *pcount = 0; + } + /* + * 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(); + + if (!part_uuid && !res.a2) { + ffa_err("[FFA] no partition installed in the system"); + return -ENODEV; + } + + return ret; + } + default: + ffa_err("Undefined response function (0x%lx)", res.a0); + return -EINVAL; + } +} + +/** + * ffa_get_partitions_info - FFA_PARTITION_INFO_GET handler function + * + * The passed arguments: + * Mode 1: When getting from the driver the number of + * secure partitions: + * @uuid_str: pointer to the UUID string + * @parts_size: pointer to the variable that contains the number of partitions + * The variable will be set by the driver + * @buffer: NULL + * + * Mode 2: When requesting the driver to return the + * partitions information: + * @uuid_str: pointer to the UUID string + * @parts_size: pointer to the size of the SPs information buffer in bytes + * @buffer: pointer to SPs information buffer + * (allocated by the client). + * The buffer will be filled by the driver + * + * This is the boot time function that queries the secure partition data from + * the private data structure. If not found, it invokes 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 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(). + * + * When invoked through a client request, ffa_get_partitions_info should be + * called twice. First call is to get from the driver the number of secure + * partitions (SPs) associated to a particular UUID. + * Then, the caller (client) allocates the buffer to host the SPs data and + * issues a 2nd call. Then, the driver fills the SPs data in the pre-allocated + * buffer. + * + * To achieve the mechanism described above, ffa_get_partitions_info uses the + * following functions: + * ffa_read_partitions_info + * ffa_query_partitions_info + * + * Return: + * + * @parts_size: When pointing to the number of partitions variable, the number is + * set by the driver. + * When pointing to the partitions information buffer size, the buffer will be + * filled by the driver. + * + * On success FFA_ERR_STAT_SUCCESS is returned. Otherwise, failure + */ +static int ffa_get_partitions_info(const char *uuid_str, + u32 *parts_size, struct ffa_partition_info *buffer) +{ + /* + * fill_data: + * 0: return the SP count + * 1: fill SP data and return it to the caller + * -1: undefined mode + */ + int fill_data = -1; + u32 desc_idx, client_desc_idx; + union ffa_partition_uuid part_uuid = {0}; + u32 client_desc_max_cnt; + u32 parts_found = 0; + + if (!ffa_priv_data->partitions.count || !ffa_priv_data->partitions.descs) + panic("[FFA] No partition installed\n"); + + if (!uuid_str) { + ffa_err("No UUID provided"); + return -EINVAL; + } + + if (!parts_size) { + ffa_err("No size/count provided"); + return -EINVAL; + } + + if (ffa_uuid_str_to_bin(uuid_str, (unsigned char *)&part_uuid)) { + ffa_err("Invalid UUID"); + return -EINVAL; + } + + if (!buffer) { + /* Mode 1: getting the number of secure partitions */ + + fill_data = 0; + + ffa_info("Preparing for checking partitions count"); + + } else if ((*parts_size >= sizeof(struct ffa_partition_info)) && + !(*parts_size % sizeof(struct ffa_partition_info))) { + /* Mode 2: retrieving the partitions information */ + + fill_data = 1; + + client_desc_idx = 0; + + /* + * number of empty descriptors preallocated by the caller + */ + client_desc_max_cnt = *parts_size / sizeof(struct ffa_partition_info); + + ffa_info("Preparing for filling partitions info"); + + } else { + ffa_err("Invalid function arguments provided"); + return -EINVAL; + } + + ffa_info("Searching partitions using the provided UUID"); + + /* + * search in the cached partitions + */ + for (desc_idx = 0; + desc_idx < ffa_priv_data->partitions.count; + desc_idx++) { + if (ffa_uuid_are_identical(&ffa_priv_data->partitions.descs[desc_idx].UUID, + &part_uuid)) { + ffa_info("Partition ID %x matches the provided UUID", + ffa_priv_data->partitions.descs[desc_idx].info.id); + + parts_found++; + + if (fill_data) { + /* + * trying to fill the partition info in the input buffer + */ + + if (client_desc_idx < client_desc_max_cnt) { + buffer[client_desc_idx++] = + ffa_priv_data->partitions.descs[desc_idx].info; + continue; + } + + ffa_err("Failed to fill the current descriptor client buffer full"); + return -ENOBUFS; + } + } + } + + if (!parts_found) { + int ret; + + ffa_info("No partition found. Querying framework ..."); + + ret = ffa_query_partitions_info(&part_uuid, &parts_found); + + if (ret == FFA_ERR_STAT_SUCCESS) { + if (!fill_data) { + *parts_size = parts_found; + + ffa_info("Number of partition(s) found matching the UUID: %d", + parts_found); + } else { + /* + * If SPs data detected, they are already in the private data + * structure, retry searching SP data again to return them + * to the caller + */ + if (parts_found) + ret = ffa_get_partitions_info(uuid_str, parts_size, buffer); + else + ret = -ENODATA; + } + } + + return ret; + } + + /* partition(s) found */ + if (!fill_data) + *parts_size = parts_found; + + return FFA_ERR_STAT_SUCCESS; +} + +/** + * ffa_cache_partitions_info - Queries and saves all secure partitions data + * + * This is a boot time function that invokes 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 the + * resident private data structure and we keep the UUID field empty + * (in FF-A 1.0 UUID is not provided by the partition descriptor) + * + * This function is called at the device probing level. + * ffa_cache_partitions_info uses ffa_query_partitions_info to get the data + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +static int ffa_cache_partitions_info(void) +{ + return ffa_query_partitions_info(NULL, NULL); +} + +/** + * ffa_msg_send_direct_req - FFA_MSG_SEND_DIRECT_{REQ,RESP} handler function + * @dst_part_id: destination partition ID + * @msg: pointer to the message data preallocated by the client (in/out) + * + * This is the runtime function that 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: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +static int __ffa_runtime ffa_msg_send_direct_req(u16 dst_part_id, struct ffa_send_direct_data *msg) +{ + ffa_value_t res = {0}; + + if (!ffa_priv_data->invoke_ffa_fn) + return -ENODEV; + + /* No partition installed */ + if (!ffa_priv_data->partitions.count || !ffa_priv_data->partitions.descs) + return -ENODEV; + + ffa_priv_data->invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_MSG_SEND_DIRECT_REQ, + .a1 = PREP_SELF_ENDPOINT_ID(ffa_priv_data->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_INTERRUPT) + ffa_priv_data->invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_RUN, + .a1 = res.a1, .a2 = 0, .a3 = 0, .a4 = 0, .a5 = 0, .a6 = 0, .a7 = 0, + }, &res); + + switch (res.a0) { + case FFA_ERROR: + { + switch (((int)res.a2)) { + case FFA_ERR_STAT_INVALID_PARAMETERS: + /* Invalid endpoint ID or non-zero reserved register */ + return -EPERM; + case FFA_ERR_STAT_ABORTED: + /* Message target ran into unexpected error and has aborted */ + return -ECONNABORTED; + case FFA_ERR_STAT_DENIED: + /* Callee is not in a state to handle this request */ + return -EACCES; + case FFA_ERR_STAT_NOT_SUPPORTED: + /* This function is not implemented at this FF-A instance */ + return -EOPNOTSUPP; + case FFA_ERR_STAT_BUSY: + /* Message target is busy */ + return -EBUSY; + default: + /* Undefined error */ + return -ENXIO; + } + } + case FFA_SUCCESS: + + /* Message sent with no response */ + return FFA_ERR_STAT_SUCCESS; + + case FFA_MSG_SEND_DIRECT_RESP: + /* + * 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 FFA_ERR_STAT_SUCCESS; + + default: + /* Undefined response function */ + return -ENOENT; + } +} + +/** + * __arm_ffa_fn_smc - SMC wrapper + * + * Calls low level SMC function + * + * Return: void + */ +void __ffa_runtime __arm_ffa_fn_smc(ffa_value_t args, ffa_value_t *res) +{ + arm_smccc_1_2_smc(&args, res); +} + +/** + * ffa_set_conduit - Set the conduit + * + * This boot time function clears the private data structure and sets the conduit + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +static int ffa_set_conduit(void) +{ + ffa_priv_data->invoke_ffa_fn = __arm_ffa_fn_smc; + + ffa_info("Conduit is SMC"); + + return FFA_ERR_STAT_SUCCESS; +} + +/** + * ffa_set_bus_ops - Set the bus driver operations + * + * Setting the driver callbacks. + * + */ +static void ffa_set_bus_ops(void) +{ + ffa_priv_data->ffa_ops.partition_info_get = ffa_get_partitions_info; + ffa_priv_data->ffa_ops.sync_send_receive = ffa_msg_send_direct_req; + ffa_priv_data->ffa_ops.rxtx_unmap = ffa_unmap_rxtx_buffers; +} + +/** + * ffa_alloc_prvdata - allocate the driver main data structure + * @dev: the arm_ffa device + * + * This boot time function creates the main data structure embedding all the driver data. + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +static int ffa_alloc_prvdata(struct udevice *dev) +{ + if (!dev) + return -ENODEV; + + /* The device is registered with the DM. Let's create the driver main data structure*/ + + ffa_priv_data = devm_kmalloc(dev, sizeof(struct ffa_prvdata), __GFP_ZERO); + if (!ffa_priv_data) { + ffa_err("can not allocate the driver main data structure"); + return -ENOMEM; + } + + ffa_priv_data->dev = dev; + + return FFA_ERR_STAT_SUCCESS; +} + +/** + * ffa_probe - The driver probe function + * @dev: the arm_ffa device + * + * Probing is done at boot time and triggered by the uclass device discovery. + * At probe level the following actions are done: + * - setting the conduit + * - querying the FF-A framework version + * - querying from secure world the u-boot endpoint ID + * - querying from secure world the supported features of the specified FF-A calls + * - mapping the RX/TX buffers + * - querying from secure world all the partitions information + * + * All data queried from secure world is saved in the resident private data structure. + * + * The probe will fail if either FF-A framework is not detected or the + * FF-A requests are not behaving correctly. This ensures that the + * driver is not installed and its operations are not exported to the clients. + * However, once the driver is successfully probed and an FF-A anomaly is + * detected when clients invoke the driver operations, the driver cause + * u-boot to panic because the client would not know what to do in such conditions. + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +static int ffa_probe(struct udevice *dev) +{ + int ret; + size_t buf_4k_pages = 0; + + ret = ffa_alloc_prvdata(dev); + if (ret != FFA_ERR_STAT_SUCCESS) + return ret; + + ffa_set_bus_ops(); + + ret = ffa_set_conduit(); + if (ret != FFA_ERR_STAT_SUCCESS) + return ret; + + ret = ffa_get_version(); + if (ret != FFA_ERR_STAT_SUCCESS) + return ret; + + ret = ffa_get_endpoint_id(); + if (ret != FFA_ERR_STAT_SUCCESS) + return ret; + + ret = ffa_get_rxtx_map_features(); + if (ret != FFA_ERR_STAT_SUCCESS) + return ret; + + ret = ffa_get_rxtx_buffers_pages_cnt(&buf_4k_pages); + if (ret != FFA_ERR_STAT_SUCCESS) + return ret; + + ret = ffa_map_rxtx_buffers(buf_4k_pages); + if (ret != FFA_ERR_STAT_SUCCESS) + return ret; + + ret = ffa_cache_partitions_info(); + if (ret != FFA_ERR_STAT_SUCCESS) { + ffa_free_rxtx_buffers(); + return ret; + } + + return FFA_ERR_STAT_SUCCESS; +} + +/** + * ffa_bus_ops_get - bus driver operations getter + * + * Return: + * This runtime function returns a pointer to the driver operations structure + */ +const struct ffa_bus_ops * __ffa_runtime ffa_bus_ops_get(void) +{ + return &ffa_priv_data->ffa_ops; +} + +/** + * ffa_bus_prvdata_get - bus driver private data getter + * + * Return: + * This boot time function returns a pointer to the main private data structure + */ +struct ffa_prvdata **ffa_bus_prvdata_get(void) +{ + return &ffa_priv_data; +} + +/** + * ffa_bus_discover - discover FF-A bus and probe the arm_ffa device + * + * This boot time function makes sure the FF-A bus is discoverable. + * Then, the arm_ffa device is probed and ready to use. + * This function is called automatically at initcalls + * level (after u-boot relocation). + * + * Arm FF-A transport is implemented through arm_ffa u-boot device managing the FF-A + * communication. + * All FF-A clients should use the arm_ffa device to use the FF-A transport. + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +int ffa_bus_discover(void) +{ + return ffa_device_get(); +} + +/** + * Declaring the arm_ffa driver under UCLASS_FFA + */ + +U_BOOT_DRIVER(arm_ffa) = { + .name = FFA_DRV_NAME, + .id = UCLASS_FFA, + .probe = ffa_probe, +}; diff --git a/drivers/arm-ffa/efi_ffa_runtime_data_mgr.c b/drivers/arm-ffa/efi_ffa_runtime_data_mgr.c new file mode 100644 index 0000000000..942601a7ba --- /dev/null +++ b/drivers/arm-ffa/efi_ffa_runtime_data_mgr.c @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include "arm_ffa_prv.h" + +/** + * ffa_copy_runtime_data - copy the private data structure to the runtime area + * + * This boot time function copies the arm_ffa driver data structures including + * partitions data to the EFI runtime data section. + * + * Return: + * + * FFA_ERR_STAT_SUCCESS on success. Otherwise, failure + */ +efi_status_t ffa_copy_runtime_data(void) +{ + efi_status_t efi_ret; + efi_uintn_t prvdata_pages; + efi_uintn_t descs_pages; + struct ffa_prvdata **prvdata = NULL; /* Pointer to the current structure */ + struct ffa_prvdata *runtime_prvdata = NULL; /* Pointer to the structure runtime copy */ + u64 runtime_descs = 0; + + prvdata = ffa_bus_prvdata_get(); + + printf("INFO: EFI: FFA: prv data area at 0x%llx\n", (u64)prvdata); + + /* allocate private data runtime area */ + + prvdata_pages = efi_size_in_pages(sizeof(struct ffa_prvdata)); + efi_ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES, + EFI_RUNTIME_SERVICES_DATA, + prvdata_pages, + (u64 *)&runtime_prvdata); + + if (efi_ret != EFI_SUCCESS) { + printf("ERROR: EFI: FFA: allocating runtime data (err: 0x%lx, addr 0x%llx)\n", + efi_ret, (u64)runtime_prvdata); + + return efi_ret; + } + + printf("INFO: EFI: FFA: runtime data area at 0x%llx\n", (u64)runtime_prvdata); + + if (!runtime_prvdata) + return EFI_INVALID_PARAMETER; + + /* allocate the partition data runtime area */ + + descs_pages = efi_size_in_pages((*prvdata)->partitions.count * + sizeof(struct ffa_partition_desc)); + efi_ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES, + EFI_RUNTIME_SERVICES_DATA, + descs_pages, + &runtime_descs); + + if (efi_ret != EFI_SUCCESS) { + printf("ERROR: EFI: FFA: allocating runtime SPs data (err: 0x%lx, addr 0x%llx)\n", + efi_ret, runtime_descs); + + efi_free_pages((u64)runtime_prvdata, prvdata_pages); + + return efi_ret; + } + + printf("INFO: EFI: FFA: SPs runtime area at 0x%llx\n", (u64)runtime_descs); + + if (!runtime_descs) + return EFI_INVALID_PARAMETER; + + *runtime_prvdata = **prvdata; + + runtime_prvdata->dev = NULL; + runtime_prvdata->ffa_ops.partition_info_get = NULL; + runtime_prvdata->ffa_ops.rxtx_unmap = NULL; + runtime_prvdata->partitions.descs = (struct ffa_partition_desc *)runtime_descs; + runtime_prvdata->pair.rxbuf = 0; + runtime_prvdata->pair.txbuf = 0; + + /* + * Update the private data structure pointer in the driver + * no need to free the old structure. devm takes care of that + */ + *prvdata = runtime_prvdata; + + printf("INFO: EFI: FFA: runtime prv data now at 0x%llx , SPs count %d\n", + (u64)*prvdata, (*prvdata)->partitions.count); + + return FFA_ERR_STAT_SUCCESS; +} diff --git a/include/arm_ffa.h b/include/arm_ffa.h new file mode 100644 index 0000000000..ee9ce2d99d --- /dev/null +++ b/include/arm_ffa.h @@ -0,0 +1,132 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __ARM_FFA_H +#define __ARM_FFA_H + +#include <linux/printk.h> + +/* + * This header is public. It can be used by clients to access + * data structures and definitions they need + */ + +/* + * Macros for displaying logs + */ + +#define ffa_info(fmt, ...) pr_info("[FFA] " fmt "\n", ##__VA_ARGS__) +#define ffa_err(fmt, ...) pr_err("[FFA] " fmt "\n", ##__VA_ARGS__) + +/* + * The driver operations success error code + */ +#define FFA_ERR_STAT_SUCCESS (0) + +/* + * 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 __packed 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_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 __packed ffa_send_direct_data { + unsigned long data0; /* w3/x3 */ + unsigned long data1; /* w4/x4 */ + unsigned long data2; /* w5/x5 */ + unsigned long data3; /* w6/x6 */ + unsigned long data4; /* w7/x7 */ +}; + +#if CONFIG_IS_ENABLED(ARM_FFA_EFI_RUNTIME_MODE) + +#include <efi_loader.h> + +/* + * __ffa_runtime - controls whether functions are + * available after calling the EFI ExitBootServices service. + * Functions tagged with these keywords are resident (available at boot time and + * at runtime) + */ + +#define __ffa_runtime_data __efi_runtime_data +#define __ffa_runtime __efi_runtime + +#else + +/* + * The FF-A driver is independent from EFI + */ + +#define __ffa_runtime_data +#define __ffa_runtime + +#endif + +/** + * struct ffa_bus_ops - The driver operations structure + * @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)(const char *uuid_str, + u32 *parts_size, struct ffa_partition_info *buffer); + int (*sync_send_receive)(u16 dst_part_id, struct ffa_send_direct_data *msg); + int (*rxtx_unmap)(void); +}; + +/** + * The device driver and the Uclass driver public functions + */ + +/** + * ffa_bus_ops_get - driver operations getter + */ +const struct ffa_bus_ops * __ffa_runtime ffa_bus_ops_get(void); + +/** + * ffa_bus_discover - discover FF-A bus and probes the arm_ffa device + */ +int ffa_bus_discover(void); + +#if CONFIG_IS_ENABLED(ARM_FFA_EFI_RUNTIME_MODE) + +/** + * ffa_copy_runtime_data - copy the private data structure and the SPs data to the runtime area + */ +efi_status_t ffa_copy_runtime_data(void); + +#endif + +#endif diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index a432e43871..6eebbe9c7d 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -55,6 +55,7 @@ enum uclass_id { UCLASS_EFI_MEDIA, /* Devices provided by UEFI firmware */ UCLASS_ETH, /* Ethernet device */ UCLASS_ETH_PHY, /* Ethernet PHY device */ + UCLASS_FFA, /* Arm Firmware Framework for Armv8-A */ UCLASS_FIRMWARE, /* Firmware */ UCLASS_FUZZING_ENGINE, /* Fuzzing engine */ UCLASS_FS_FIRMWARE_LOADER, /* Generic loader */ diff --git a/include/uuid.h b/include/uuid.h index 4a4883d3b5..789f8e0f15 100644 --- a/include/uuid.h +++ b/include/uuid.h @@ -44,4 +44,12 @@ 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); + +#ifdef CONFIG_ARM_FFA_TRANSPORT +/** + * ffa_uuid_str_to_bin - Converts a big endian UUID string to a little endian buffer + */ +int ffa_uuid_str_to_bin(const char *uuid_str, unsigned char *uuid_bin); +#endif + #endif diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 4da64b5d29..e02bb445f5 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -23,6 +23,10 @@ #include <asm/setjmp.h> #include <linux/libfdt_env.h>
+#if defined(CONFIG_ARM_FFA_TRANSPORT) +#include <arm_ffa.h> +#endif + DECLARE_GLOBAL_DATA_PTR;
/* Task priority level */ @@ -2113,6 +2117,10 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, struct efi_event *evt, *next_event; efi_status_t ret = EFI_SUCCESS;
+#if defined(CONFIG_ARM_FFA_TRANSPORT) + int ffa_ret; +#endif + EFI_ENTRY("%p, %zx", image_handle, map_key);
/* Check that the caller has read the current memory map */ @@ -2173,6 +2181,15 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL); }
+#if defined(CONFIG_ARM_FFA_TRANSPORT) + /* unmap FF-A RX/TX buffers */ + ffa_ret = ffa_bus_ops_get()->rxtx_unmap(); + if (ffa_ret) + debug("[efi_boottime][ERROR]: can not unmap FF-A RX/TX buffers\n"); + else + debug("[efi_boottime][INFO]: FF-A RX/TX buffers unmapped\n"); +#endif + /* Patch out unsupported runtime function */ efi_runtime_detach();
diff --git a/lib/uuid.c b/lib/uuid.c index 284f8113ff..50b3e61d59 100644 --- a/lib/uuid.c +++ b/lib/uuid.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0+ /* * Copyright 2011 Calxeda, Inc. + * Copyright 2022 ARM Limited */
#include <common.h> @@ -342,6 +343,70 @@ int uuid_str_to_bin(const char *uuid_str, unsigned char *uuid_bin, return 0; }
+#ifdef CONFIG_ARM_FFA_TRANSPORT +/** + * ffa_uuid_str_to_bin - Converts a big endian UUID string to a little endian buffer + * @uuid_str: UUID string in big endian format (36 bytes wide + '/0') + * @uuid_bin: preallocated 16 bytes UUID buffer in little endian format + * + * UUID string is 36 characters (36 bytes): + * + * xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + * be be be be be + * + * where x is a hexadecimal character. Fields are separated by '-'s. + * When converting to a binary UUID, these endianness rules apply: + * be: means the field in the string is considered a big endian hex number + * and should be converted to little endian binary format + * + * Return: + * + * uuid_bin filled with little endian UUID data + * On success 0 is returned. Otherwise, failure code. + */ +int ffa_uuid_str_to_bin(const char *uuid_str, unsigned char *uuid_bin) +{ + u16 tmp16 = 0; + u32 tmp32 = 0; + u64 tmp64 = 0; + + if (!uuid_str_valid(uuid_str) || !uuid_bin) + return -EINVAL; + + /* + * reverse bytes from big to little endian + */ + tmp32 = simple_strtoul(uuid_str, NULL, 16); + memcpy(uuid_bin, &tmp32, 4); + + /* + * reverse bytes from big to little endian + */ + tmp16 = simple_strtoul(uuid_str + 9, NULL, 16); + memcpy(uuid_bin + 4, &tmp16, 2); + + /* + * reverse bytes from big to little endian + */ + tmp16 = simple_strtoul(uuid_str + 14, NULL, 16); + memcpy(uuid_bin + 6, &tmp16, 2); + + /* + * reverse bytes from big to little endian + */ + tmp16 = simple_strtoul(uuid_str + 19, NULL, 16); + memcpy(uuid_bin + 8, &tmp16, 2); + + /* + * reverse bytes from big to little endian + */ + tmp64 = simple_strtoull(uuid_str + 24, NULL, 16); + memcpy(uuid_bin + 10, (char *)&tmp64, 6); + + return 0; +} +#endif + /* * uuid_bin_to_str() - convert big endian binary data to string UUID or GUID. *

Hi Abdellatif,
On Mon, 1 Aug 2022 at 20:21, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
Add the driver implementing Arm Firmware Framework for Armv8-A v1.0
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.
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.
32-bit version of the ABIs is supported and 64-bit version of FFA_RXTX_MAP and FFA_MSG_SEND_DIRECT_{REQ, RESP}.
In u-boot FF-A design, FF-A is considered as a discoverable bus. 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:
- partition_info_get
- sync_send_receive
- rxtx_unmap
This implementation provides an optional feature to copy the driver data to EFI runtime area.
[...]
+config ARM_FFA_TRANSPORT
bool "Enable Arm Firmware Framework for Armv8-A driver"
depends on DM && ARM64
select ARM_SMCCC
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.
This driver 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.
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.
+config ARM_FFA_EFI_RUNTIME_MODE
bool "Enable EFI runtime support for FF-A data and code "
depends on ARM_FFA_TRANSPORT && EFI_LOADER
help
Allows FF-A driver data structures and code to be accessible at EFI runtime
Is there a reason we want to opt-in on that? What prevents it from always being there?
[...]
+/* 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))
+#define FFA_VERSION FFA_SMC_32(0x63) +#define FFA_ID_GET FFA_SMC_32(0x69) +#define FFA_FEATURES FFA_SMC_32(0x64) +#define FFA_PARTITION_INFO_GET FFA_SMC_32(0x68) +#define FFA_RXTX_UNMAP FFA_SMC_32(0x67) +#define FFA_RX_RELEASE FFA_SMC_32(0x65) +#define FFA_RUN FFA_SMC_32(0x6D) +#define FFA_ERROR FFA_SMC_32(0x60) +#define FFA_SUCCESS FFA_SMC_32(0x61) +#define FFA_INTERRUPT FFA_SMC_32(0x62) +#define FFA_RXTX_MAP FFA_SMC_64(0x66) +#define FFA_MSG_SEND_DIRECT_REQ FFA_SMC_64(0x6F) +#define FFA_MSG_SEND_DIRECT_RESP FFA_SMC_64(0x70)
+/* The FF-A SMC function definitions */
+typedef struct arm_smccc_1_2_regs ffa_value_t; +typedef void (*invoke_ffa_fn_t)(ffa_value_t args, ffa_value_t *res);
+/* FF-A error codes */ +#define FFA_ERR_STAT_NOT_SUPPORTED (-1) +#define FFA_ERR_STAT_INVALID_PARAMETERS (-2) +#define FFA_ERR_STAT_NO_MEMORY (-3) +#define FFA_ERR_STAT_BUSY (-4) +#define FFA_ERR_STAT_INTERRUPTED (-5) +#define FFA_ERR_STAT_DENIED (-6) +#define FFA_ERR_STAT_RETRY (-7) +#define FFA_ERR_STAT_ABORTED (-8)
+/* UUID data size */ +#define UUID_SIZE (16)
Please drop () on these
+/*
- union ffa_partition_uuid - Data union hosting the UUID
transmitted by FFA_PARTITION_INFO_GET
- @words: data structure giving 32-bit words access to the UUID data
- @bytes: data structure giving byte access to the UUID data
- The structure holds little-endian UUID data.
- */
+union ffa_partition_uuid {
struct __packed words {
u32 a1; /* w1 */
u32 a2; /* w2 */
u32 a3; /* w3 */
u32 a4; /* w4 */
} words;
u8 bytes[UUID_SIZE];
+};
is the bytes field used anywhere?
[...]
- Each partition has its descriptor containing the partitions information and the UUID
- */
+struct ffa_partition_desc {
struct ffa_partition_info info;
union ffa_partition_uuid UUID;
lower case please and perhaps a better name e.g partition_uuid
+};
+/**
- struct ffa_partitions - descriptors for all secure partitions
- @count: The number of partitions descriptors
- @descs The partitions descriptors table
- This data structure contains the partitions descriptors table
- */
+struct ffa_partitions {
u32 count;
struct ffa_partition_desc *descs; /* virtual address */
+};
+/**
- struct ffa_prvdata - the driver private data structure
- @dev: The arm_ffa device under u-boot driver model
- @ffa_ops: The driver operations structure
- @fwk_version: FF-A framework version
- @id: u-boot endpoint ID
- @partitions: The partitions descriptors structure
- @pair: The RX/TX buffers pair
- @invoke_ffa_fn: The function executing the FF-A function
- @features: Table of the FF-A functions having features
- The driver data structure hosting all resident data.
- */
+struct ffa_prvdata {
struct udevice *dev;
struct ffa_bus_ops ffa_ops;
u32 fwk_version;
u16 id;
struct ffa_partitions partitions;
struct ffa_rxtxpair pair;
invoke_ffa_fn_t invoke_ffa_fn;
struct ffa_features_desc features[FFA_FEATURE_DESC_CNT];
+};
+/**
- ffa_device_get - create, bind and probe the arm_ffa device
- */
+int ffa_device_get(void);
+/**
- ffa_bus_prvdata_get - bus driver private data getter
- */
+struct ffa_prvdata **ffa_bus_prvdata_get(void);
+#endif diff --git a/drivers/arm-ffa/core.c b/drivers/arm-ffa/core.c new file mode 100644 index 0000000000..40d140fc3e --- /dev/null +++ b/drivers/arm-ffa/core.c @@ -0,0 +1,1338 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include "arm_ffa_prv.h" +#include <asm/global_data.h> +#include <common.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <dm/devres.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> +#include <log.h> +#include <malloc.h> +#include <string.h> +#include <uuid.h>
+DECLARE_GLOBAL_DATA_PTR;
+/**
- The device private data structure containing all the resident
- data read from secure world
- */
+__ffa_runtime_data struct ffa_prvdata *ffa_priv_data;
I think it's better if we just keep this as efi_runtime, especially since the rest of the declarations you use are marked as efi_*
[...]
+/**
- ffa_device_get - create, bind and probe the arm_ffa device
- This boot time function makes sure the arm_ffa device is
- created, bound to this driver, probed and ready to use.
- Arm FF-A transport is implemented through a single u-boot
- device managing the FF-A bus (arm_ffa).
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+int ffa_device_get(void) +{
int ret;
struct udevice *dev = NULL;
if (ffa_priv_data && ffa_priv_data->dev)
return FFA_ERR_STAT_SUCCESS;
ret = device_bind(dm_root(),
DM_DRIVER_GET(arm_ffa),
FFA_DRV_NAME,
NULL,
ofnode_null(),
&dev);
if (ret)
return ret;
/* The FF-A bus discovery succeeds when probing is successful */
ret = device_probe(dev);
if (ret) {
ffa_err("arm_ffa device probing failed");
ffa_remove_device(dev);
return ret;
}
return FFA_ERR_STAT_SUCCESS;
The return values in most of the functions are confusing. I think you should just get rid of FFA_ERR_STAT_SUCCESS and just return 0
[...]
if (!ffa_priv_data->invoke_ffa_fn)
panic("[FFA] no private data found\n");
Get rid of all the panicking please. We've discussed this on a previous email. Isn't an error message enough?
[...]
- ffa_map_rxtx_buffers - FFA_RXTX_MAP handler function
- @buf_4k_pages: the minimum number of pages in each of the RX/TX
buffers
- This is the boot time function that implements FFA_RXTX_MAP FF-A function
- to map the RX/TX buffers
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+static int ffa_map_rxtx_buffers(size_t buf_4k_pages) +{
int ret;
ffa_value_t res = {0};
if (!ffa_priv_data->invoke_ffa_fn)
panic("[FFA] no private data found\n");
ret = ffa_alloc_rxtx_buffers(buf_4k_pages);
if (ret != FFA_ERR_STAT_SUCCESS)
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
*/
ffa_priv_data->invoke_ffa_fn((ffa_value_t){
.a0 = FFA_RXTX_MAP,
.a1 = ffa_priv_data->pair.txbuf,
.a2 = ffa_priv_data->pair.rxbuf,
.a3 = buf_4k_pages,
.a4 = 0, .a5 = 0, .a6 = 0, .a7 = 0,
}, &res);
switch (res.a0) {
case FFA_ERROR:
{
There are comments in v2 that are ignored throughout the patchset. Please check the remarks in v2 before sending a new patchset
switch (((int)res.a2)) {
case FFA_ERR_STAT_INVALID_PARAMETERS:
ffa_err("One or more fields in input parameters is incorrectly encoded");
ret = -EPERM;
break;
case FFA_ERR_STAT_NO_MEMORY:
ffa_err("Not enough memory");
ret = -ENOMEM;
break;
case FFA_ERR_STAT_DENIED:
ffa_err("Buffer pair already registered");
ret = -EACCES;
break;
case FFA_ERR_STAT_NOT_SUPPORTED:
ffa_err("This function is not implemented at this FF-A instance");
ret = -EOPNOTSUPP;
break;
default:
ffa_err("Undefined error (%d)",
((int)res.a2));
ret = -EINVAL;
}
break;
}
case FFA_SUCCESS:
ffa_info("RX/TX buffers mapped");
return FFA_ERR_STAT_SUCCESS;
default:
ffa_err("Undefined response function (0x%lx)",
res.a0);
ret = -EINVAL;
}
ffa_free_rxtx_buffers();
return ret;
+}
+/**
[...]
Regards /Ilias
+/**
- Declaring the arm_ffa driver under UCLASS_FFA
- */
+U_BOOT_DRIVER(arm_ffa) = {
.name = FFA_DRV_NAME,
.id = UCLASS_FFA,
.probe = ffa_probe,
+}; diff --git a/drivers/arm-ffa/efi_ffa_runtime_data_mgr.c b/drivers/arm-ffa/efi_ffa_runtime_data_mgr.c new file mode 100644 index 0000000000..942601a7ba --- /dev/null +++ b/drivers/arm-ffa/efi_ffa_runtime_data_mgr.c @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include "arm_ffa_prv.h"
+/**
- ffa_copy_runtime_data - copy the private data structure to the runtime area
- This boot time function copies the arm_ffa driver data structures including
- partitions data to the EFI runtime data section.
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+efi_status_t ffa_copy_runtime_data(void) +{
efi_status_t efi_ret;
efi_uintn_t prvdata_pages;
efi_uintn_t descs_pages;
struct ffa_prvdata **prvdata = NULL; /* Pointer to the current structure */
struct ffa_prvdata *runtime_prvdata = NULL; /* Pointer to the structure runtime copy */
u64 runtime_descs = 0;
prvdata = ffa_bus_prvdata_get();
printf("INFO: EFI: FFA: prv data area at 0x%llx\n", (u64)prvdata);
/* allocate private data runtime area */
prvdata_pages = efi_size_in_pages(sizeof(struct ffa_prvdata));
efi_ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
EFI_RUNTIME_SERVICES_DATA,
prvdata_pages,
(u64 *)&runtime_prvdata);
if (efi_ret != EFI_SUCCESS) {
printf("ERROR: EFI: FFA: allocating runtime data (err: 0x%lx, addr 0x%llx)\n",
efi_ret, (u64)runtime_prvdata);
return efi_ret;
}
printf("INFO: EFI: FFA: runtime data area at 0x%llx\n", (u64)runtime_prvdata);
if (!runtime_prvdata)
return EFI_INVALID_PARAMETER;
/* allocate the partition data runtime area */
descs_pages = efi_size_in_pages((*prvdata)->partitions.count *
sizeof(struct ffa_partition_desc));
efi_ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
EFI_RUNTIME_SERVICES_DATA,
descs_pages,
&runtime_descs);
if (efi_ret != EFI_SUCCESS) {
printf("ERROR: EFI: FFA: allocating runtime SPs data (err: 0x%lx, addr 0x%llx)\n",
efi_ret, runtime_descs);
efi_free_pages((u64)runtime_prvdata, prvdata_pages);
return efi_ret;
}
printf("INFO: EFI: FFA: SPs runtime area at 0x%llx\n", (u64)runtime_descs);
if (!runtime_descs)
return EFI_INVALID_PARAMETER;
*runtime_prvdata = **prvdata;
runtime_prvdata->dev = NULL;
runtime_prvdata->ffa_ops.partition_info_get = NULL;
runtime_prvdata->ffa_ops.rxtx_unmap = NULL;
runtime_prvdata->partitions.descs = (struct ffa_partition_desc *)runtime_descs;
runtime_prvdata->pair.rxbuf = 0;
runtime_prvdata->pair.txbuf = 0;
/*
* Update the private data structure pointer in the driver
* no need to free the old structure. devm takes care of that
*/
*prvdata = runtime_prvdata;
printf("INFO: EFI: FFA: runtime prv data now at 0x%llx , SPs count %d\n",
(u64)*prvdata, (*prvdata)->partitions.count);
return FFA_ERR_STAT_SUCCESS;
+} diff --git a/include/arm_ffa.h b/include/arm_ffa.h new file mode 100644 index 0000000000..ee9ce2d99d --- /dev/null +++ b/include/arm_ffa.h @@ -0,0 +1,132 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#ifndef __ARM_FFA_H +#define __ARM_FFA_H
+#include <linux/printk.h>
+/*
- This header is public. It can be used by clients to access
- data structures and definitions they need
- */
+/*
- Macros for displaying logs
- */
+#define ffa_info(fmt, ...) pr_info("[FFA] " fmt "\n", ##__VA_ARGS__) +#define ffa_err(fmt, ...) pr_err("[FFA] " fmt "\n", ##__VA_ARGS__)
+/*
- The driver operations success error code
- */
+#define FFA_ERR_STAT_SUCCESS (0)
+/*
- 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 __packed 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_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 __packed ffa_send_direct_data {
unsigned long data0; /* w3/x3 */
unsigned long data1; /* w4/x4 */
unsigned long data2; /* w5/x5 */
unsigned long data3; /* w6/x6 */
unsigned long data4; /* w7/x7 */
+};
+#if CONFIG_IS_ENABLED(ARM_FFA_EFI_RUNTIME_MODE)
+#include <efi_loader.h>
+/*
- __ffa_runtime - controls whether functions are
- available after calling the EFI ExitBootServices service.
- Functions tagged with these keywords are resident (available at boot time and
- at runtime)
- */
+#define __ffa_runtime_data __efi_runtime_data +#define __ffa_runtime __efi_runtime
+#else
+/*
- The FF-A driver is independent from EFI
- */
+#define __ffa_runtime_data +#define __ffa_runtime
+#endif
+/**
- struct ffa_bus_ops - The driver operations structure
- @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)(const char *uuid_str,
u32 *parts_size, struct ffa_partition_info *buffer);
int (*sync_send_receive)(u16 dst_part_id, struct ffa_send_direct_data *msg);
int (*rxtx_unmap)(void);
+};
+/**
- The device driver and the Uclass driver public functions
- */
+/**
- ffa_bus_ops_get - driver operations getter
- */
+const struct ffa_bus_ops * __ffa_runtime ffa_bus_ops_get(void);
+/**
- ffa_bus_discover - discover FF-A bus and probes the arm_ffa device
- */
+int ffa_bus_discover(void);
+#if CONFIG_IS_ENABLED(ARM_FFA_EFI_RUNTIME_MODE)
+/**
- ffa_copy_runtime_data - copy the private data structure and the SPs data to the runtime area
- */
+efi_status_t ffa_copy_runtime_data(void);
+#endif
+#endif diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index a432e43871..6eebbe9c7d 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -55,6 +55,7 @@ enum uclass_id { UCLASS_EFI_MEDIA, /* Devices provided by UEFI firmware */ UCLASS_ETH, /* Ethernet device */ UCLASS_ETH_PHY, /* Ethernet PHY device */
UCLASS_FFA, /* Arm Firmware Framework for Armv8-A */ UCLASS_FIRMWARE, /* Firmware */ UCLASS_FUZZING_ENGINE, /* Fuzzing engine */ UCLASS_FS_FIRMWARE_LOADER, /* Generic loader */
diff --git a/include/uuid.h b/include/uuid.h index 4a4883d3b5..789f8e0f15 100644 --- a/include/uuid.h +++ b/include/uuid.h @@ -44,4 +44,12 @@ 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);
+#ifdef CONFIG_ARM_FFA_TRANSPORT +/**
- ffa_uuid_str_to_bin - Converts a big endian UUID string to a little endian buffer
- */
+int ffa_uuid_str_to_bin(const char *uuid_str, unsigned char *uuid_bin); +#endif
#endif diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 4da64b5d29..e02bb445f5 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -23,6 +23,10 @@ #include <asm/setjmp.h> #include <linux/libfdt_env.h>
+#if defined(CONFIG_ARM_FFA_TRANSPORT) +#include <arm_ffa.h> +#endif
DECLARE_GLOBAL_DATA_PTR;
/* Task priority level */ @@ -2113,6 +2117,10 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, struct efi_event *evt, *next_event; efi_status_t ret = EFI_SUCCESS;
+#if defined(CONFIG_ARM_FFA_TRANSPORT)
int ffa_ret;
+#endif
EFI_ENTRY("%p, %zx", image_handle, map_key); /* Check that the caller has read the current memory map */
@@ -2173,6 +2181,15 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL); }
+#if defined(CONFIG_ARM_FFA_TRANSPORT)
/* unmap FF-A RX/TX buffers */
ffa_ret = ffa_bus_ops_get()->rxtx_unmap();
if (ffa_ret)
debug("[efi_boottime][ERROR]: can not unmap FF-A RX/TX buffers\n");
else
debug("[efi_boottime][INFO]: FF-A RX/TX buffers unmapped\n");
+#endif
/* Patch out unsupported runtime function */ efi_runtime_detach();
diff --git a/lib/uuid.c b/lib/uuid.c index 284f8113ff..50b3e61d59 100644 --- a/lib/uuid.c +++ b/lib/uuid.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0+ /*
- Copyright 2011 Calxeda, Inc.
*/
- Copyright 2022 ARM Limited
#include <common.h> @@ -342,6 +343,70 @@ int uuid_str_to_bin(const char *uuid_str, unsigned char *uuid_bin, return 0; }
+#ifdef CONFIG_ARM_FFA_TRANSPORT +/**
- ffa_uuid_str_to_bin - Converts a big endian UUID string to a little endian buffer
- @uuid_str: UUID string in big endian format (36 bytes wide + '/0')
- @uuid_bin: preallocated 16 bytes UUID buffer in little endian format
- UUID string is 36 characters (36 bytes):
- xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
- be be be be be
- where x is a hexadecimal character. Fields are separated by '-'s.
- When converting to a binary UUID, these endianness rules apply:
be: means the field in the string is considered a big endian hex number
and should be converted to little endian binary format
- Return:
- uuid_bin filled with little endian UUID data
- On success 0 is returned. Otherwise, failure code.
- */
+int ffa_uuid_str_to_bin(const char *uuid_str, unsigned char *uuid_bin) +{
u16 tmp16 = 0;
u32 tmp32 = 0;
u64 tmp64 = 0;
if (!uuid_str_valid(uuid_str) || !uuid_bin)
return -EINVAL;
/*
* reverse bytes from big to little endian
*/
tmp32 = simple_strtoul(uuid_str, NULL, 16);
memcpy(uuid_bin, &tmp32, 4);
/*
* reverse bytes from big to little endian
*/
tmp16 = simple_strtoul(uuid_str + 9, NULL, 16);
memcpy(uuid_bin + 4, &tmp16, 2);
/*
* reverse bytes from big to little endian
*/
tmp16 = simple_strtoul(uuid_str + 14, NULL, 16);
memcpy(uuid_bin + 6, &tmp16, 2);
/*
* reverse bytes from big to little endian
*/
tmp16 = simple_strtoul(uuid_str + 19, NULL, 16);
memcpy(uuid_bin + 8, &tmp16, 2);
/*
* reverse bytes from big to little endian
*/
tmp64 = simple_strtoull(uuid_str + 24, NULL, 16);
memcpy(uuid_bin + 10, (char *)&tmp64, 6);
return 0;
+} +#endif
/*
- uuid_bin_to_str() - convert big endian binary data to string UUID or GUID.
-- 2.17.1

On Fri, Aug 12, 2022 at 10:39:23AM +0300, Ilias Apalodimas wrote:
Hi Abdellatif,
On Mon, 1 Aug 2022 at 20:21, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
Add the driver implementing Arm Firmware Framework for Armv8-A v1.0
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.
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.
32-bit version of the ABIs is supported and 64-bit version of FFA_RXTX_MAP and FFA_MSG_SEND_DIRECT_{REQ, RESP}.
In u-boot FF-A design, FF-A is considered as a discoverable bus. 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:
- partition_info_get
- sync_send_receive
- rxtx_unmap
This implementation provides an optional feature to copy the driver data to EFI runtime area.
[...]
+config ARM_FFA_TRANSPORT
bool "Enable Arm Firmware Framework for Armv8-A driver"
depends on DM && ARM64
select ARM_SMCCC
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.
This driver 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.
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.
+config ARM_FFA_EFI_RUNTIME_MODE
bool "Enable EFI runtime support for FF-A data and code "
depends on ARM_FFA_TRANSPORT && EFI_LOADER
help
Allows FF-A driver data structures and code to be accessible at EFI runtime
Is there a reason we want to opt-in on that? What prevents it from always being there?
Yes, we agreed that FF-A driver needs to be independent from EFI. This is reflected in the v4 patchset. The intent is to be able to compile the core driver without EFI support. When using EFI, FF-A core driver data needs to be copied to the EFI runtime section so it can be accessed at runtime. For that specific case, CONFIG_ARM_FFA_EFI_RUNTIME_MODE has been created to enable the feature of copying the data to the runtime section. The code that copies the data is provided by drivers/firmware/arm-ffa/efi_ffa_runtime_data_mgr.c When EFI is needed CONFIG_ARM_FFA_EFI_RUNTIME_MODE should be turned on. If no EFI needed the FF-A driver can still be used independently.
[...]
+/* 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))
+#define FFA_VERSION FFA_SMC_32(0x63) +#define FFA_ID_GET FFA_SMC_32(0x69) +#define FFA_FEATURES FFA_SMC_32(0x64) +#define FFA_PARTITION_INFO_GET FFA_SMC_32(0x68) +#define FFA_RXTX_UNMAP FFA_SMC_32(0x67) +#define FFA_RX_RELEASE FFA_SMC_32(0x65) +#define FFA_RUN FFA_SMC_32(0x6D) +#define FFA_ERROR FFA_SMC_32(0x60) +#define FFA_SUCCESS FFA_SMC_32(0x61) +#define FFA_INTERRUPT FFA_SMC_32(0x62) +#define FFA_RXTX_MAP FFA_SMC_64(0x66) +#define FFA_MSG_SEND_DIRECT_REQ FFA_SMC_64(0x6F) +#define FFA_MSG_SEND_DIRECT_RESP FFA_SMC_64(0x70)
+/* The FF-A SMC function definitions */
+typedef struct arm_smccc_1_2_regs ffa_value_t; +typedef void (*invoke_ffa_fn_t)(ffa_value_t args, ffa_value_t *res);
+/* FF-A error codes */ +#define FFA_ERR_STAT_NOT_SUPPORTED (-1) +#define FFA_ERR_STAT_INVALID_PARAMETERS (-2) +#define FFA_ERR_STAT_NO_MEMORY (-3) +#define FFA_ERR_STAT_BUSY (-4) +#define FFA_ERR_STAT_INTERRUPTED (-5) +#define FFA_ERR_STAT_DENIED (-6) +#define FFA_ERR_STAT_RETRY (-7) +#define FFA_ERR_STAT_ABORTED (-8)
+/* UUID data size */ +#define UUID_SIZE (16)
Please drop () on these
+/*
- union ffa_partition_uuid - Data union hosting the UUID
transmitted by FFA_PARTITION_INFO_GET
- @words: data structure giving 32-bit words access to the UUID data
- @bytes: data structure giving byte access to the UUID data
- The structure holds little-endian UUID data.
- */
+union ffa_partition_uuid {
struct __packed words {
u32 a1; /* w1 */
u32 a2; /* w2 */
u32 a3; /* w3 */
u32 a4; /* w4 */
} words;
u8 bytes[UUID_SIZE];
+};
is the bytes field used anywhere?
removed in v4. Please see https://lore.kernel.org/all/20220926101723.9965-5-abdellatif.elkhlifi@arm.co...
[...]
- Each partition has its descriptor containing the partitions information and the UUID
- */
+struct ffa_partition_desc {
struct ffa_partition_info info;
union ffa_partition_uuid UUID;
lower case please and perhaps a better name e.g partition_uuid
Done in v4.
+};
+/**
- struct ffa_partitions - descriptors for all secure partitions
- @count: The number of partitions descriptors
- @descs The partitions descriptors table
- This data structure contains the partitions descriptors table
- */
+struct ffa_partitions {
u32 count;
struct ffa_partition_desc *descs; /* virtual address */
+};
+/**
- struct ffa_prvdata - the driver private data structure
- @dev: The arm_ffa device under u-boot driver model
- @ffa_ops: The driver operations structure
- @fwk_version: FF-A framework version
- @id: u-boot endpoint ID
- @partitions: The partitions descriptors structure
- @pair: The RX/TX buffers pair
- @invoke_ffa_fn: The function executing the FF-A function
- @features: Table of the FF-A functions having features
- The driver data structure hosting all resident data.
- */
+struct ffa_prvdata {
struct udevice *dev;
struct ffa_bus_ops ffa_ops;
u32 fwk_version;
u16 id;
struct ffa_partitions partitions;
struct ffa_rxtxpair pair;
invoke_ffa_fn_t invoke_ffa_fn;
struct ffa_features_desc features[FFA_FEATURE_DESC_CNT];
+};
+/**
- ffa_device_get - create, bind and probe the arm_ffa device
- */
+int ffa_device_get(void);
+/**
- ffa_bus_prvdata_get - bus driver private data getter
- */
+struct ffa_prvdata **ffa_bus_prvdata_get(void);
+#endif diff --git a/drivers/arm-ffa/core.c b/drivers/arm-ffa/core.c new file mode 100644 index 0000000000..40d140fc3e --- /dev/null +++ b/drivers/arm-ffa/core.c @@ -0,0 +1,1338 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include "arm_ffa_prv.h" +#include <asm/global_data.h> +#include <common.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <dm/devres.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> +#include <log.h> +#include <malloc.h> +#include <string.h> +#include <uuid.h>
+DECLARE_GLOBAL_DATA_PTR;
+/**
- The device private data structure containing all the resident
- data read from secure world
- */
+__ffa_runtime_data struct ffa_prvdata *ffa_priv_data;
I think it's better if we just keep this as efi_runtime, especially since the rest of the declarations you use are marked as efi_*
In v4 the core driver is no longer dependent on EFI. The intent is to be able to compile it without EFI support. So, we can't use __efi_runtime* in the FFA driver when EFI is off.
__ffa_runtime* keywords are created in include/arm_ffa.h
Based on wether CONFIG_ARM_FFA_EFI_RUNTIME_MODE is on or off __ffa_runtime* are set to __efi_runtime* or kept empty if the config is off.
[...]
+/**
- ffa_device_get - create, bind and probe the arm_ffa device
- This boot time function makes sure the arm_ffa device is
- created, bound to this driver, probed and ready to use.
- Arm FF-A transport is implemented through a single u-boot
- device managing the FF-A bus (arm_ffa).
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+int ffa_device_get(void) +{
int ret;
struct udevice *dev = NULL;
if (ffa_priv_data && ffa_priv_data->dev)
return FFA_ERR_STAT_SUCCESS;
ret = device_bind(dm_root(),
DM_DRIVER_GET(arm_ffa),
FFA_DRV_NAME,
NULL,
ofnode_null(),
&dev);
if (ret)
return ret;
/* The FF-A bus discovery succeeds when probing is successful */
ret = device_probe(dev);
if (ret) {
ffa_err("arm_ffa device probing failed");
ffa_remove_device(dev);
return ret;
}
return FFA_ERR_STAT_SUCCESS;
The return values in most of the functions are confusing. I think you should just get rid of FFA_ERR_STAT_SUCCESS and just return 0
Error handling and panics have been addressed in v4 patchset. As stated in the v3 cover letter, error handling and panics will be addressed in v4.
Please refer to this patch for the latest work: https://lore.kernel.org/all/20220926101723.9965-5-abdellatif.elkhlifi@arm.co...
[...]
if (!ffa_priv_data->invoke_ffa_fn)
panic("[FFA] no private data found\n");
Get rid of all the panicking please. We've discussed this on a previous email. Isn't an error message enough?
Error handling and panics have been addressed in v4 patchset. As stated in the v3 cover letter, error handling and panics will be addressed in v4.
Please refer to this patch for the latest work: https://lore.kernel.org/all/20220926101723.9965-5-abdellatif.elkhlifi@arm.co...
[...]
- ffa_map_rxtx_buffers - FFA_RXTX_MAP handler function
- @buf_4k_pages: the minimum number of pages in each of the RX/TX
buffers
- This is the boot time function that implements FFA_RXTX_MAP FF-A function
- to map the RX/TX buffers
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+static int ffa_map_rxtx_buffers(size_t buf_4k_pages) +{
int ret;
ffa_value_t res = {0};
if (!ffa_priv_data->invoke_ffa_fn)
panic("[FFA] no private data found\n");
ret = ffa_alloc_rxtx_buffers(buf_4k_pages);
if (ret != FFA_ERR_STAT_SUCCESS)
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
*/
ffa_priv_data->invoke_ffa_fn((ffa_value_t){
.a0 = FFA_RXTX_MAP,
.a1 = ffa_priv_data->pair.txbuf,
.a2 = ffa_priv_data->pair.rxbuf,
.a3 = buf_4k_pages,
.a4 = 0, .a5 = 0, .a6 = 0, .a7 = 0,
}, &res);
switch (res.a0) {
case FFA_ERROR:
{
There are comments in v2 that are ignored throughout the patchset. Please check the remarks in v2 before sending a new patchset
Error handling and panics have been addressed in v4 patchset. As stated in the v3 cover letter, error handling and panics will be addressed in v4.
Please refer to this patch for the latest work: https://lore.kernel.org/all/20220926101723.9965-5-abdellatif.elkhlifi@arm.co...
switch (((int)res.a2)) {
case FFA_ERR_STAT_INVALID_PARAMETERS:
ffa_err("One or more fields in input parameters is incorrectly encoded");
ret = -EPERM;
break;
case FFA_ERR_STAT_NO_MEMORY:
ffa_err("Not enough memory");
ret = -ENOMEM;
break;
case FFA_ERR_STAT_DENIED:
ffa_err("Buffer pair already registered");
ret = -EACCES;
break;
case FFA_ERR_STAT_NOT_SUPPORTED:
ffa_err("This function is not implemented at this FF-A instance");
ret = -EOPNOTSUPP;
break;
default:
ffa_err("Undefined error (%d)",
((int)res.a2));
ret = -EINVAL;
}
break;
}
case FFA_SUCCESS:
ffa_info("RX/TX buffers mapped");
return FFA_ERR_STAT_SUCCESS;
default:
ffa_err("Undefined response function (0x%lx)",
res.a0);
ret = -EINVAL;
}
ffa_free_rxtx_buffers();
return ret;
+}
+/**
[...]
Regards /Ilias
+/**
- Declaring the arm_ffa driver under UCLASS_FFA
- */
+U_BOOT_DRIVER(arm_ffa) = {
.name = FFA_DRV_NAME,
.id = UCLASS_FFA,
.probe = ffa_probe,
+}; diff --git a/drivers/arm-ffa/efi_ffa_runtime_data_mgr.c b/drivers/arm-ffa/efi_ffa_runtime_data_mgr.c new file mode 100644 index 0000000000..942601a7ba --- /dev/null +++ b/drivers/arm-ffa/efi_ffa_runtime_data_mgr.c @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include "arm_ffa_prv.h"
+/**
- ffa_copy_runtime_data - copy the private data structure to the runtime area
- This boot time function copies the arm_ffa driver data structures including
- partitions data to the EFI runtime data section.
- Return:
- FFA_ERR_STAT_SUCCESS on success. Otherwise, failure
- */
+efi_status_t ffa_copy_runtime_data(void) +{
efi_status_t efi_ret;
efi_uintn_t prvdata_pages;
efi_uintn_t descs_pages;
struct ffa_prvdata **prvdata = NULL; /* Pointer to the current structure */
struct ffa_prvdata *runtime_prvdata = NULL; /* Pointer to the structure runtime copy */
u64 runtime_descs = 0;
prvdata = ffa_bus_prvdata_get();
printf("INFO: EFI: FFA: prv data area at 0x%llx\n", (u64)prvdata);
/* allocate private data runtime area */
prvdata_pages = efi_size_in_pages(sizeof(struct ffa_prvdata));
efi_ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
EFI_RUNTIME_SERVICES_DATA,
prvdata_pages,
(u64 *)&runtime_prvdata);
if (efi_ret != EFI_SUCCESS) {
printf("ERROR: EFI: FFA: allocating runtime data (err: 0x%lx, addr 0x%llx)\n",
efi_ret, (u64)runtime_prvdata);
return efi_ret;
}
printf("INFO: EFI: FFA: runtime data area at 0x%llx\n", (u64)runtime_prvdata);
if (!runtime_prvdata)
return EFI_INVALID_PARAMETER;
/* allocate the partition data runtime area */
descs_pages = efi_size_in_pages((*prvdata)->partitions.count *
sizeof(struct ffa_partition_desc));
efi_ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
EFI_RUNTIME_SERVICES_DATA,
descs_pages,
&runtime_descs);
if (efi_ret != EFI_SUCCESS) {
printf("ERROR: EFI: FFA: allocating runtime SPs data (err: 0x%lx, addr 0x%llx)\n",
efi_ret, runtime_descs);
efi_free_pages((u64)runtime_prvdata, prvdata_pages);
return efi_ret;
}
printf("INFO: EFI: FFA: SPs runtime area at 0x%llx\n", (u64)runtime_descs);
if (!runtime_descs)
return EFI_INVALID_PARAMETER;
*runtime_prvdata = **prvdata;
runtime_prvdata->dev = NULL;
runtime_prvdata->ffa_ops.partition_info_get = NULL;
runtime_prvdata->ffa_ops.rxtx_unmap = NULL;
runtime_prvdata->partitions.descs = (struct ffa_partition_desc *)runtime_descs;
runtime_prvdata->pair.rxbuf = 0;
runtime_prvdata->pair.txbuf = 0;
/*
* Update the private data structure pointer in the driver
* no need to free the old structure. devm takes care of that
*/
*prvdata = runtime_prvdata;
printf("INFO: EFI: FFA: runtime prv data now at 0x%llx , SPs count %d\n",
(u64)*prvdata, (*prvdata)->partitions.count);
return FFA_ERR_STAT_SUCCESS;
+} diff --git a/include/arm_ffa.h b/include/arm_ffa.h new file mode 100644 index 0000000000..ee9ce2d99d --- /dev/null +++ b/include/arm_ffa.h @@ -0,0 +1,132 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#ifndef __ARM_FFA_H +#define __ARM_FFA_H
+#include <linux/printk.h>
+/*
- This header is public. It can be used by clients to access
- data structures and definitions they need
- */
+/*
- Macros for displaying logs
- */
+#define ffa_info(fmt, ...) pr_info("[FFA] " fmt "\n", ##__VA_ARGS__) +#define ffa_err(fmt, ...) pr_err("[FFA] " fmt "\n", ##__VA_ARGS__)
+/*
- The driver operations success error code
- */
+#define FFA_ERR_STAT_SUCCESS (0)
+/*
- 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 __packed 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_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 __packed ffa_send_direct_data {
unsigned long data0; /* w3/x3 */
unsigned long data1; /* w4/x4 */
unsigned long data2; /* w5/x5 */
unsigned long data3; /* w6/x6 */
unsigned long data4; /* w7/x7 */
+};
+#if CONFIG_IS_ENABLED(ARM_FFA_EFI_RUNTIME_MODE)
+#include <efi_loader.h>
+/*
- __ffa_runtime - controls whether functions are
- available after calling the EFI ExitBootServices service.
- Functions tagged with these keywords are resident (available at boot time and
- at runtime)
- */
+#define __ffa_runtime_data __efi_runtime_data +#define __ffa_runtime __efi_runtime
+#else
+/*
- The FF-A driver is independent from EFI
- */
+#define __ffa_runtime_data +#define __ffa_runtime
+#endif
+/**
- struct ffa_bus_ops - The driver operations structure
- @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)(const char *uuid_str,
u32 *parts_size, struct ffa_partition_info *buffer);
int (*sync_send_receive)(u16 dst_part_id, struct ffa_send_direct_data *msg);
int (*rxtx_unmap)(void);
+};
+/**
- The device driver and the Uclass driver public functions
- */
+/**
- ffa_bus_ops_get - driver operations getter
- */
+const struct ffa_bus_ops * __ffa_runtime ffa_bus_ops_get(void);
+/**
- ffa_bus_discover - discover FF-A bus and probes the arm_ffa device
- */
+int ffa_bus_discover(void);
+#if CONFIG_IS_ENABLED(ARM_FFA_EFI_RUNTIME_MODE)
+/**
- ffa_copy_runtime_data - copy the private data structure and the SPs data to the runtime area
- */
+efi_status_t ffa_copy_runtime_data(void);
+#endif
+#endif diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index a432e43871..6eebbe9c7d 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -55,6 +55,7 @@ enum uclass_id { UCLASS_EFI_MEDIA, /* Devices provided by UEFI firmware */ UCLASS_ETH, /* Ethernet device */ UCLASS_ETH_PHY, /* Ethernet PHY device */
UCLASS_FFA, /* Arm Firmware Framework for Armv8-A */ UCLASS_FIRMWARE, /* Firmware */ UCLASS_FUZZING_ENGINE, /* Fuzzing engine */ UCLASS_FS_FIRMWARE_LOADER, /* Generic loader */
diff --git a/include/uuid.h b/include/uuid.h index 4a4883d3b5..789f8e0f15 100644 --- a/include/uuid.h +++ b/include/uuid.h @@ -44,4 +44,12 @@ 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);
+#ifdef CONFIG_ARM_FFA_TRANSPORT +/**
- ffa_uuid_str_to_bin - Converts a big endian UUID string to a little endian buffer
- */
+int ffa_uuid_str_to_bin(const char *uuid_str, unsigned char *uuid_bin); +#endif
#endif diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 4da64b5d29..e02bb445f5 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -23,6 +23,10 @@ #include <asm/setjmp.h> #include <linux/libfdt_env.h>
+#if defined(CONFIG_ARM_FFA_TRANSPORT) +#include <arm_ffa.h> +#endif
DECLARE_GLOBAL_DATA_PTR;
/* Task priority level */ @@ -2113,6 +2117,10 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, struct efi_event *evt, *next_event; efi_status_t ret = EFI_SUCCESS;
+#if defined(CONFIG_ARM_FFA_TRANSPORT)
int ffa_ret;
+#endif
EFI_ENTRY("%p, %zx", image_handle, map_key); /* Check that the caller has read the current memory map */
@@ -2173,6 +2181,15 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL); }
+#if defined(CONFIG_ARM_FFA_TRANSPORT)
/* unmap FF-A RX/TX buffers */
ffa_ret = ffa_bus_ops_get()->rxtx_unmap();
if (ffa_ret)
debug("[efi_boottime][ERROR]: can not unmap FF-A RX/TX buffers\n");
else
debug("[efi_boottime][INFO]: FF-A RX/TX buffers unmapped\n");
+#endif
/* Patch out unsupported runtime function */ efi_runtime_detach();
diff --git a/lib/uuid.c b/lib/uuid.c index 284f8113ff..50b3e61d59 100644 --- a/lib/uuid.c +++ b/lib/uuid.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0+ /*
- Copyright 2011 Calxeda, Inc.
*/
- Copyright 2022 ARM Limited
#include <common.h> @@ -342,6 +343,70 @@ int uuid_str_to_bin(const char *uuid_str, unsigned char *uuid_bin, return 0; }
+#ifdef CONFIG_ARM_FFA_TRANSPORT +/**
- ffa_uuid_str_to_bin - Converts a big endian UUID string to a little endian buffer
- @uuid_str: UUID string in big endian format (36 bytes wide + '/0')
- @uuid_bin: preallocated 16 bytes UUID buffer in little endian format
- UUID string is 36 characters (36 bytes):
- xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
- be be be be be
- where x is a hexadecimal character. Fields are separated by '-'s.
- When converting to a binary UUID, these endianness rules apply:
be: means the field in the string is considered a big endian hex number
and should be converted to little endian binary format
- Return:
- uuid_bin filled with little endian UUID data
- On success 0 is returned. Otherwise, failure code.
- */
+int ffa_uuid_str_to_bin(const char *uuid_str, unsigned char *uuid_bin) +{
u16 tmp16 = 0;
u32 tmp32 = 0;
u64 tmp64 = 0;
if (!uuid_str_valid(uuid_str) || !uuid_bin)
return -EINVAL;
/*
* reverse bytes from big to little endian
*/
tmp32 = simple_strtoul(uuid_str, NULL, 16);
memcpy(uuid_bin, &tmp32, 4);
/*
* reverse bytes from big to little endian
*/
tmp16 = simple_strtoul(uuid_str + 9, NULL, 16);
memcpy(uuid_bin + 4, &tmp16, 2);
/*
* reverse bytes from big to little endian
*/
tmp16 = simple_strtoul(uuid_str + 14, NULL, 16);
memcpy(uuid_bin + 6, &tmp16, 2);
/*
* reverse bytes from big to little endian
*/
tmp16 = simple_strtoul(uuid_str + 19, NULL, 16);
memcpy(uuid_bin + 8, &tmp16, 2);
/*
* reverse bytes from big to little endian
*/
tmp64 = simple_strtoull(uuid_str + 24, NULL, 16);
memcpy(uuid_bin + 10, (char *)&tmp64, 6);
return 0;
+} +#endif
/*
- uuid_bin_to_str() - convert big endian binary data to string UUID or GUID.
-- 2.17.1

Hi Abdellatif,
On Mon, 1 Aug 2022 at 11:21, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
Add the driver implementing Arm Firmware Framework for Armv8-A v1.0
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.
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.
32-bit version of the ABIs is supported and 64-bit version of FFA_RXTX_MAP and FFA_MSG_SEND_DIRECT_{REQ, RESP}.
In u-boot FF-A design, FF-A is considered as a discoverable bus. 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:
- partition_info_get
- sync_send_receive
- rxtx_unmap
This implementation provides an optional feature to copy the driver data to EFI runtime area.
This is v2 but I don't see a change list?
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
MAINTAINERS | 6 + common/board_r.c | 7 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/arm-ffa/Kconfig | 33 + drivers/arm-ffa/Makefile | 7 + drivers/arm-ffa/arm-ffa-uclass.c | 16 + drivers/arm-ffa/arm_ffa_prv.h | 219 ++++ drivers/arm-ffa/core.c | 1338 ++++++++++++++++++++ drivers/arm-ffa/efi_ffa_runtime_data_mgr.c | 94 ++ include/arm_ffa.h | 132 ++ include/dm/uclass-id.h | 1 + include/uuid.h | 8 + lib/efi_loader/efi_boottime.c | 17 + lib/uuid.c | 65 + 15 files changed, 1946 insertions(+) create mode 100644 drivers/arm-ffa/Kconfig create mode 100644 drivers/arm-ffa/Makefile create mode 100644 drivers/arm-ffa/arm-ffa-uclass.c create mode 100644 drivers/arm-ffa/arm_ffa_prv.h create mode 100644 drivers/arm-ffa/core.c create mode 100644 drivers/arm-ffa/efi_ffa_runtime_data_mgr.c create mode 100644 include/arm_ffa.h
I think this should be in an existing directory, or perhaps drivers/firmware.
Regards, Simon

On Fri, Aug 12, 2022 at 08:21:02PM -0600, Simon Glass wrote:
Hi Abdellatif,
On Mon, 1 Aug 2022 at 11:21, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
Add the driver implementing Arm Firmware Framework for Armv8-A v1.0
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.
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.
32-bit version of the ABIs is supported and 64-bit version of FFA_RXTX_MAP and FFA_MSG_SEND_DIRECT_{REQ, RESP}.
In u-boot FF-A design, FF-A is considered as a discoverable bus. 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:
- partition_info_get
- sync_send_receive
- rxtx_unmap
This implementation provides an optional feature to copy the driver data to EFI runtime area.
This is v2 but I don't see a change list?
Sorry for not including that in the cover letter. Here is the changelog for v2 [1].
From v3 upwards the changelog is included in the cover letter.
[1] https://lore.kernel.org/all/20220509105514.GA4201@e121910.cambridge.arm.com/
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
MAINTAINERS | 6 + common/board_r.c | 7 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/arm-ffa/Kconfig | 33 + drivers/arm-ffa/Makefile | 7 + drivers/arm-ffa/arm-ffa-uclass.c | 16 + drivers/arm-ffa/arm_ffa_prv.h | 219 ++++ drivers/arm-ffa/core.c | 1338 ++++++++++++++++++++ drivers/arm-ffa/efi_ffa_runtime_data_mgr.c | 94 ++ include/arm_ffa.h | 132 ++ include/dm/uclass-id.h | 1 + include/uuid.h | 8 + lib/efi_loader/efi_boottime.c | 17 + lib/uuid.c | 65 + 15 files changed, 1946 insertions(+) create mode 100644 drivers/arm-ffa/Kconfig create mode 100644 drivers/arm-ffa/Makefile create mode 100644 drivers/arm-ffa/arm-ffa-uclass.c create mode 100644 drivers/arm-ffa/arm_ffa_prv.h create mode 100644 drivers/arm-ffa/core.c create mode 100644 drivers/arm-ffa/efi_ffa_runtime_data_mgr.c create mode 100644 include/arm_ffa.h
I think this should be in an existing directory, or perhaps drivers/firmware.
Done in v4 (under drivers/firmware).
Regards, Simon

Hi,
On Mon, 1 Aug 2022 at 11:21, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
Add the driver implementing Arm Firmware Framework for Armv8-A v1.0
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.
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.
32-bit version of the ABIs is supported and 64-bit version of FFA_RXTX_MAP and FFA_MSG_SEND_DIRECT_{REQ, RESP}.
In u-boot FF-A design, FF-A is considered as a discoverable bus. 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:
- partition_info_get
- sync_send_receive
- rxtx_unmap
This implementation provides an optional feature to copy the driver data to EFI runtime area.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
MAINTAINERS | 6 + common/board_r.c | 7 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/arm-ffa/Kconfig | 33 + drivers/arm-ffa/Makefile | 7 + drivers/arm-ffa/arm-ffa-uclass.c | 16 + drivers/arm-ffa/arm_ffa_prv.h | 219 ++++ drivers/arm-ffa/core.c | 1338 ++++++++++++++++++++ drivers/arm-ffa/efi_ffa_runtime_data_mgr.c | 94 ++ include/arm_ffa.h | 132 ++ include/dm/uclass-id.h | 1 + include/uuid.h | 8 + lib/efi_loader/efi_boottime.c | 17 + lib/uuid.c | 65 + 15 files changed, 1946 insertions(+) create mode 100644 drivers/arm-ffa/Kconfig create mode 100644 drivers/arm-ffa/Makefile create mode 100644 drivers/arm-ffa/arm-ffa-uclass.c create mode 100644 drivers/arm-ffa/arm_ffa_prv.h create mode 100644 drivers/arm-ffa/core.c create mode 100644 drivers/arm-ffa/.c create mode 100644 include/arm_ffa.h
Please add something to doc/ so people know what this is.
Since you are adding a new uclass you need a sandbox driver and tests.
The driver appears to have no operations, but there is a bus_ops. The ops should go in the driver, I suspect, and should pass the device as the first arg.
Can FFA_ERR_STAT_SUCCESS be 0 so you don't have to sprinkle the code with it?
Why is it using EFI things? Can this driver only be used with UEFI? I hope not, if it is an official way of updating firmware.
Please don't add more things to board_r.c - we are trying to remove this init over time. If it is a device it should be probed as needed.
Is there a device tree binding?
Also should this go in drivers/misc instead of creating a whole new subdir?
Regards, Simon

On Tue, Nov 15, 2022 at 08:24:24AM -0700, Simon Glass wrote:
Hi,
On Mon, 1 Aug 2022 at 11:21, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
Add the driver implementing Arm Firmware Framework for Armv8-A v1.0
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.
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.
32-bit version of the ABIs is supported and 64-bit version of FFA_RXTX_MAP and FFA_MSG_SEND_DIRECT_{REQ, RESP}.
In u-boot FF-A design, FF-A is considered as a discoverable bus. 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:
- partition_info_get
- sync_send_receive
- rxtx_unmap
This implementation provides an optional feature to copy the driver data to EFI runtime area.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
MAINTAINERS | 6 + common/board_r.c | 7 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/arm-ffa/Kconfig | 33 + drivers/arm-ffa/Makefile | 7 + drivers/arm-ffa/arm-ffa-uclass.c | 16 + drivers/arm-ffa/arm_ffa_prv.h | 219 ++++ drivers/arm-ffa/core.c | 1338 ++++++++++++++++++++ drivers/arm-ffa/efi_ffa_runtime_data_mgr.c | 94 ++ include/arm_ffa.h | 132 ++ include/dm/uclass-id.h | 1 + include/uuid.h | 8 + lib/efi_loader/efi_boottime.c | 17 + lib/uuid.c | 65 + 15 files changed, 1946 insertions(+) create mode 100644 drivers/arm-ffa/Kconfig create mode 100644 drivers/arm-ffa/Makefile create mode 100644 drivers/arm-ffa/arm-ffa-uclass.c create mode 100644 drivers/arm-ffa/arm_ffa_prv.h create mode 100644 drivers/arm-ffa/core.c create mode 100644 drivers/arm-ffa/.c create mode 100644 include/arm_ffa.h
Please add something to doc/ so people know what this is.
Since you are adding a new uclass you need a sandbox driver and tests.
The driver appears to have no operations, but there is a bus_ops. The ops should go in the driver, I suspect, and should pass the device as the first arg.
Can FFA_ERR_STAT_SUCCESS be 0 so you don't have to sprinkle the code with it?
Why is it using EFI things? Can this driver only be used with UEFI? I hope not, if it is an official way of updating firmware.
Please don't add more things to board_r.c - we are trying to remove this init over time. If it is a device it should be probed as needed.
Is there a device tree binding?
Also should this go in drivers/misc instead of creating a whole new subdir?
Hi Simon, thanks for reviewing.
All the above comments have already been addressed in the new versions of the patchset. Please refer to the latest version v7 [1].
By the way I'd like to highlight the following:
- The FF-A driver documentation is at doc/arch/arm64.ffa.rst, please refer to it since it provides helpful details about the FF-A support in U-Boot - The patchset comes with Sandbox driver and tests [2] - The driver has operations defined in struct ffa_bus_ops (include/arm_ffa.h). ffa_bus_ops_get() gets the ops. All these are in the driver (drivers/firmware/arm-ffa/core.c) - The FF-A bus has only 1 device. No multiple instances. So passing the device doesn't make sense in our case - FFA_ERR_STAT_SUCCESS has been removed and replaced with 0 - The driver is independent from EFI and can be compiled without EFI - FF-A bus discovery has been removed from the initcall level (board_r.c) Discovery is done on demand. Clients can call ffa_bus_discover() when they want to use the FF-A bus. As an example of how clients initiate discovery please refer to the FF-A MM comms client [3]. - As done in the Linux kernel, the FF-A bus doesn't have a device tree binding since there is no peripheral associated with FF-A. At the early stages of this patchset, we double checked with the device tree maintainer and the decision was no device tree for FF-A - The links below are from the U-Boot mailing list mirror in lore.kernel.org
Cheers.
[1]: https://lore.kernel.org/all/20221107192055.21669-1-abdellatif.elkhlifi@arm.c... [2]: https://lore.kernel.org/all/20221107192055.21669-7-abdellatif.elkhlifi@arm.c... https://lore.kernel.org/all/20221107192055.21669-8-abdellatif.elkhlifi@arm.c... https://lore.kernel.org/all/20221107192055.21669-9-abdellatif.elkhlifi@arm.c... [3]: https://lore.kernel.org/all/20221107192055.21669-10-abdellatif.elkhlifi@arm....
Regards, Simon

Hi,
On Wed, 16 Nov 2022 at 06:03, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
On Tue, Nov 15, 2022 at 08:24:24AM -0700, Simon Glass wrote:
Hi,
On Mon, 1 Aug 2022 at 11:21, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
Add the driver implementing Arm Firmware Framework for Armv8-A v1.0
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.
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.
32-bit version of the ABIs is supported and 64-bit version of FFA_RXTX_MAP and FFA_MSG_SEND_DIRECT_{REQ, RESP}.
In u-boot FF-A design, FF-A is considered as a discoverable bus. 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:
- partition_info_get
- sync_send_receive
- rxtx_unmap
This implementation provides an optional feature to copy the driver data to EFI runtime area.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
MAINTAINERS | 6 + common/board_r.c | 7 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/arm-ffa/Kconfig | 33 + drivers/arm-ffa/Makefile | 7 + drivers/arm-ffa/arm-ffa-uclass.c | 16 + drivers/arm-ffa/arm_ffa_prv.h | 219 ++++ drivers/arm-ffa/core.c | 1338 ++++++++++++++++++++ drivers/arm-ffa/efi_ffa_runtime_data_mgr.c | 94 ++ include/arm_ffa.h | 132 ++ include/dm/uclass-id.h | 1 + include/uuid.h | 8 + lib/efi_loader/efi_boottime.c | 17 + lib/uuid.c | 65 + 15 files changed, 1946 insertions(+) create mode 100644 drivers/arm-ffa/Kconfig create mode 100644 drivers/arm-ffa/Makefile create mode 100644 drivers/arm-ffa/arm-ffa-uclass.c create mode 100644 drivers/arm-ffa/arm_ffa_prv.h create mode 100644 drivers/arm-ffa/core.c create mode 100644 drivers/arm-ffa/.c create mode 100644 include/arm_ffa.h
Please add something to doc/ so people know what this is.
Since you are adding a new uclass you need a sandbox driver and tests.
The driver appears to have no operations, but there is a bus_ops. The ops should go in the driver, I suspect, and should pass the device as the first arg.
Can FFA_ERR_STAT_SUCCESS be 0 so you don't have to sprinkle the code with it?
Why is it using EFI things? Can this driver only be used with UEFI? I hope not, if it is an official way of updating firmware.
Please don't add more things to board_r.c - we are trying to remove this init over time. If it is a device it should be probed as needed.
Is there a device tree binding?
Also should this go in drivers/misc instead of creating a whole new subdir?
Hi Simon, thanks for reviewing.
All the above comments have already been addressed in the new versions of the patchset. Please refer to the latest version v7 [1].
By the way I'd like to highlight the following:
- The FF-A driver documentation is at doc/arch/arm64.ffa.rst, please refer to it since it provides helpful details about the FF-A support in U-Boot
OK
- The patchset comes with Sandbox driver and tests [2]
OK I suppose I saw that previously and forgot.
- The driver has operations defined in struct ffa_bus_ops (include/arm_ffa.h). ffa_bus_ops_get() gets the ops. All these are in the driver (drivers/firmware/arm-ffa/core.c)
Can you please push a tree somewhere so I can look?
- The FF-A bus has only 1 device. No multiple instances. So passing the device doesn't make sense in our case
It must still pass the device.
- FFA_ERR_STAT_SUCCESS has been removed and replaced with 0
OK
- The driver is independent from EFI and can be compiled without EFI
Oh, so what is ffa_copy_runtime_data() for?
- FF-A bus discovery has been removed from the initcall level (board_r.c)
Good.
Discovery is done on demand. Clients can call ffa_bus_discover() when they want to use the FF-A bus. As an example of how clients initiate discovery please refer to the FF-A MM comms client [3].
Is there a command to do this?
- As done in the Linux kernel, the FF-A bus doesn't have a device tree binding since there is no peripheral associated with FF-A. At the early stages of this patchset, we double checked with the device tree maintainer and the decision was no device tree for FF-A
Sorry, but you must add one.
- The links below are from the U-Boot mailing list mirror in lore.kernel.org
Regards, Simon
Cheers.
https://lore.kernel.org/all/20221107192055.21669-8-abdellatif.elkhlifi@arm.com/ https://lore.kernel.org/all/20221107192055.21669-9-abdellatif.elkhlifi@arm.com/
Regards, Simon

On Fri, Nov 18, 2022 at 01:50:26PM -0700, Simon Glass wrote:
Hi,
On Wed, 16 Nov 2022 at 06:03, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
On Tue, Nov 15, 2022 at 08:24:24AM -0700, Simon Glass wrote:
Hi,
On Mon, 1 Aug 2022 at 11:21, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
Add the driver implementing Arm Firmware Framework for Armv8-A v1.0
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.
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.
32-bit version of the ABIs is supported and 64-bit version of FFA_RXTX_MAP and FFA_MSG_SEND_DIRECT_{REQ, RESP}.
In u-boot FF-A design, FF-A is considered as a discoverable bus. 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:
- partition_info_get
- sync_send_receive
- rxtx_unmap
This implementation provides an optional feature to copy the driver data to EFI runtime area.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
MAINTAINERS | 6 + common/board_r.c | 7 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/arm-ffa/Kconfig | 33 + drivers/arm-ffa/Makefile | 7 + drivers/arm-ffa/arm-ffa-uclass.c | 16 + drivers/arm-ffa/arm_ffa_prv.h | 219 ++++ drivers/arm-ffa/core.c | 1338 ++++++++++++++++++++ drivers/arm-ffa/efi_ffa_runtime_data_mgr.c | 94 ++ include/arm_ffa.h | 132 ++ include/dm/uclass-id.h | 1 + include/uuid.h | 8 + lib/efi_loader/efi_boottime.c | 17 + lib/uuid.c | 65 + 15 files changed, 1946 insertions(+) create mode 100644 drivers/arm-ffa/Kconfig create mode 100644 drivers/arm-ffa/Makefile create mode 100644 drivers/arm-ffa/arm-ffa-uclass.c create mode 100644 drivers/arm-ffa/arm_ffa_prv.h create mode 100644 drivers/arm-ffa/core.c create mode 100644 drivers/arm-ffa/.c create mode 100644 include/arm_ffa.h
Please add something to doc/ so people know what this is.
Since you are adding a new uclass you need a sandbox driver and tests.
The driver appears to have no operations, but there is a bus_ops. The ops should go in the driver, I suspect, and should pass the device as the first arg.
Can FFA_ERR_STAT_SUCCESS be 0 so you don't have to sprinkle the code with it?
Why is it using EFI things? Can this driver only be used with UEFI? I hope not, if it is an official way of updating firmware.
Please don't add more things to board_r.c - we are trying to remove this init over time. If it is a device it should be probed as needed.
Is there a device tree binding?
Also should this go in drivers/misc instead of creating a whole new subdir?
Hi Simon, thanks for reviewing.
All the above comments have already been addressed in the new versions of the patchset. Please refer to the latest version v7 [1].
By the way I'd like to highlight the following:
- The FF-A driver documentation is at doc/arch/arm64.ffa.rst, please refer to it since it provides helpful details about the FF-A support in U-Boot
OK
- The patchset comes with Sandbox driver and tests [2]
OK I suppose I saw that previously and forgot.
- The driver has operations defined in struct ffa_bus_ops (include/arm_ffa.h). ffa_bus_ops_get() gets the ops. All these are in the driver (drivers/firmware/arm-ffa/core.c)
Can you please push a tree somewhere so I can look?
OK, I'll share the link to a public branch so you can refer to once it's created.
- The FF-A bus has only 1 device. No multiple instances. So passing the device doesn't make sense in our case
It must still pass the device.
I added this feature in patchset v8. Please refer to the latest patchset when reviewing [1].
[1]: https://lore.kernel.org/all/20221122131751.22747-1-abdellatif.elkhlifi@arm.c...
- FFA_ERR_STAT_SUCCESS has been removed and replaced with 0
OK
- The driver is independent from EFI and can be compiled without EFI
Oh, so what is ffa_copy_runtime_data() for?
This has already been dropped in newer versions of the pachset. Could you please refer to the latest ? (v8)
- FF-A bus discovery has been removed from the initcall level (board_r.c)
Good.
Discovery is done on demand. Clients can call ffa_bus_discover() when they want to use the FF-A bus. As an example of how clients initiate discovery please refer to the FF-A MM comms client [3].
Is there a command to do this?
If you mean whether there is a command to showcase this, yes.
I already created the armffa command as an example of how to use the FF-A bus and how clients can discover it.
Please refer to this commit [1].
[1]: https://lore.kernel.org/all/20221122131751.22747-6-abdellatif.elkhlifi@arm.c...
- As done in the Linux kernel, the FF-A bus doesn't have a device tree binding since there is no peripheral associated with FF-A. At the early stages of this patchset, we double checked with the device tree maintainer and the decision was no device tree for FF-A
Sorry, but you must add one.
We had a discussion on this on April 2022 with Rob Herring. We decided to do the same as the FF-A kernel driver: no device tree, we perform a manual bus discovery.
Rob Herring stated the following:
- There is not a 'kernel device tree' and a 'u-boot device tree'. There is only 1 - The FFA DT binding was rejected in favor of making FFA discoverable. The FFA spec was amended to address that. DT is only for what we failed to make discoverable. For hardware, we're stuck with it. We shouldn't repeat that for software interfaces.
For more details please refer to [1].
[1]: https://lore.kernel.org/all/CAL_Jsq+dwHqwDdALTAwU-VYW2=2kYCyHpv7mUP0zR04CwH1...
- The links below are from the U-Boot mailing list mirror in lore.kernel.org
Regards, Simon
Cheers.
https://lore.kernel.org/all/20221107192055.21669-8-abdellatif.elkhlifi@arm.com/ https://lore.kernel.org/all/20221107192055.21669-9-abdellatif.elkhlifi@arm.com/
Regards, Simon

Hi Abdellatif,
On Tue, 22 Nov 2022 at 06:50, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
On Fri, Nov 18, 2022 at 01:50:26PM -0700, Simon Glass wrote:
Hi,
On Wed, 16 Nov 2022 at 06:03, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
On Tue, Nov 15, 2022 at 08:24:24AM -0700, Simon Glass wrote:
Hi,
On Mon, 1 Aug 2022 at 11:21, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
Add the driver implementing Arm Firmware Framework for Armv8-A v1.0
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.
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.
32-bit version of the ABIs is supported and 64-bit version of FFA_RXTX_MAP and FFA_MSG_SEND_DIRECT_{REQ, RESP}.
In u-boot FF-A design, FF-A is considered as a discoverable bus. 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:
- partition_info_get
- sync_send_receive
- rxtx_unmap
This implementation provides an optional feature to copy the driver data to EFI runtime area.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
MAINTAINERS | 6 + common/board_r.c | 7 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/arm-ffa/Kconfig | 33 + drivers/arm-ffa/Makefile | 7 + drivers/arm-ffa/arm-ffa-uclass.c | 16 + drivers/arm-ffa/arm_ffa_prv.h | 219 ++++ drivers/arm-ffa/core.c | 1338 ++++++++++++++++++++ drivers/arm-ffa/efi_ffa_runtime_data_mgr.c | 94 ++ include/arm_ffa.h | 132 ++ include/dm/uclass-id.h | 1 + include/uuid.h | 8 + lib/efi_loader/efi_boottime.c | 17 + lib/uuid.c | 65 + 15 files changed, 1946 insertions(+) create mode 100644 drivers/arm-ffa/Kconfig create mode 100644 drivers/arm-ffa/Makefile create mode 100644 drivers/arm-ffa/arm-ffa-uclass.c create mode 100644 drivers/arm-ffa/arm_ffa_prv.h create mode 100644 drivers/arm-ffa/core.c create mode 100644 drivers/arm-ffa/.c create mode 100644 include/arm_ffa.h
[..]
OK
- The driver is independent from EFI and can be compiled without EFI
Oh, so what is ffa_copy_runtime_data() for?
This has already been dropped in newer versions of the pachset. Could you please refer to the latest ? (v8)
OK I will take a look at the latest.
- FF-A bus discovery has been removed from the initcall level (board_r.c)
Good.
Discovery is done on demand. Clients can call ffa_bus_discover() when they want to use the FF-A bus. As an example of how clients initiate discovery please refer to the FF-A MM comms client [3].
Is there a command to do this?
If you mean whether there is a command to showcase this, yes.
I already created the armffa command as an example of how to use the FF-A bus and how clients can discover it.
Please refer to this commit [1].
- As done in the Linux kernel, the FF-A bus doesn't have a device tree binding since there is no peripheral associated with FF-A. At the early stages of this patchset, we double checked with the device tree maintainer and the decision was no device tree for FF-A
Sorry, but you must add one.
We had a discussion on this on April 2022 with Rob Herring. We decided to do the same as the FF-A kernel driver: no device tree, we perform a manual bus discovery.
Rob Herring stated the following:
- There is not a 'kernel device tree' and a 'u-boot device tree'. There is only 1
- The FFA DT binding was rejected in favor of making FFA discoverable. The FFA spec was amended to address that. DT is only for what we failed to make discoverable. For hardware, we're stuck with it. We shouldn't repeat that for software interfaces.
OK. Rob and I will need to discuss this, but in the meantime, we should hold off on this series.
I am certainly not saying there should be two device trees. But there needs to be a binding for this thing.
For more details please refer to [1].
- The links below are from the U-Boot mailing list mirror in lore.kernel.org
Regards, Simon
Cheers.
https://lore.kernel.org/all/20221107192055.21669-8-abdellatif.elkhlifi@arm.com/ https://lore.kernel.org/all/20221107192055.21669-9-abdellatif.elkhlifi@arm.com/
Regards, Simon
Regards, Simon

Provide armffa command showcasing the use of the FF-A driver
The armffa command allows to query secure partitions data from the secure world and exchanging messages with the partitions.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org --- MAINTAINERS | 1 + cmd/Kconfig | 10 ++ cmd/Makefile | 2 + cmd/armffa.c | 246 ++++++++++++++++++++++++++++++++++++++++ drivers/arm-ffa/Kconfig | 1 + 5 files changed, 260 insertions(+) create mode 100644 cmd/armffa.c
diff --git a/MAINTAINERS b/MAINTAINERS index 93a57f4df2..b0fbc027c2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -251,6 +251,7 @@ F: configs/cortina_presidio-asic-pnand_defconfig ARM FF-A M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com S: Maintained +F: cmd/armffa.c F: drivers/arm-ffa/ F: include/arm_ffa.h
diff --git a/cmd/Kconfig b/cmd/Kconfig index a8260aa170..22f85188ff 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -902,6 +902,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 Arm FF-A driver + 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 5e43a1e022..e40f52f1e4 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -12,6 +12,8 @@ obj-y += panic.o obj-y += version.o
# command + +obj-$(CONFIG_CMD_ARMFFA) += armffa.o obj-$(CONFIG_CMD_ACPI) += acpi.o obj-$(CONFIG_CMD_ADDRMAP) += addrmap.o obj-$(CONFIG_CMD_AES) += aes.o diff --git a/cmd/armffa.c b/cmd/armffa.c new file mode 100644 index 0000000000..cb8a3d40aa --- /dev/null +++ b/cmd/armffa.c @@ -0,0 +1,246 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <arm_ffa.h> +#include <asm/io.h> +#include <common.h> +#include <command.h> +#include <dm.h> +#include <mapmem.h> +#include <stdlib.h> + +/** + * do_ffa_get_singular_partition_info - implementation of the getpart subcommand + * @cmdtp: Command Table + * @flag: flags + * @argc: number of arguments + * @argv: arguments + * + * This function queries the secure partition information which the UUID is provided + * as an argument. The function uses the arm_ffa driver partition_info_get operation + * 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_get_singular_partition_info(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + u32 count = 0, size = 0; + int ret; + struct ffa_partition_info *parts_info; + u32 info_idx; + + if (argc != 1) + return -EINVAL; + + /* Mode 1: getting the number of secure partitions */ + ret = ffa_bus_ops_get()->partition_info_get(argv[0], &count, NULL); + if (ret != FFA_ERR_STAT_SUCCESS) { + ffa_err("Failure in querying partitions count (error code: %d)", ret); + return ret; + } + + if (!count) { + ffa_info("No secure partition found"); + return ret; + } + + /* + * pre-allocate a buffer to be filled by the driver + * with ffa_partition_info structs + */ + + ffa_info("Pre-allocating %d partition(s) info structures", count); + + parts_info = calloc(count, sizeof(struct ffa_partition_info)); + if (!parts_info) + return -EINVAL; + + size = count * sizeof(struct ffa_partition_info); + + /* + * ask the driver to fill the buffer with the SPs info + */ + + ret = ffa_bus_ops_get()->partition_info_get(argv[0], &size, parts_info); + if (ret != FFA_ERR_STAT_SUCCESS) { + ffa_err("Failure in querying partition(s) info (error code: %d)", ret); + free(parts_info); + return ret; + } + + /* + * SPs found , show the partition information + */ + for (info_idx = 0; info_idx < count ; info_idx++) { + ffa_info("Partition: id = 0x%x , exec_ctxt 0x%x , properties 0x%x", + parts_info[info_idx].id, + parts_info[info_idx].exec_ctxt, + parts_info[info_idx].properties); + } + + free(parts_info); + + return 0; +} + +/** + * do_ffa_msg_send_direct_req - implementation of the ping subcommand + * @cmdtp: Command Table + * @flag: flags + * @argc: number of arguments + * @argv: arguments + * + * This function sends data to the secure partition which the ID is provided + * as an argument. The function uses the arm_ffa driver sync_send_receive operation + * to send data. + * + * Return: + * + * CMD_RET_SUCCESS: on success, otherwise failure + */ +int do_ffa_msg_send_direct_req(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct ffa_send_direct_data msg = {0}; + u64 pattern = 0xaabbccddaabbccd0; + u16 part_id; + int ret; + + if (argc != 1) + return -EINVAL; + + errno = 0; + part_id = strtoul(argv[0], NULL, 16); + + if (errno) { + ffa_err("Invalid partition ID"); + return -EINVAL; + } + + /* + * filling the message data + */ + msg.data0 = ++pattern; + msg.data1 = ++pattern; + msg.data2 = ++pattern; + msg.data3 = ++pattern; + msg.data4 = ++pattern; + + ret = ffa_bus_ops_get()->sync_send_receive(part_id, &msg); + if (ret == FFA_ERR_STAT_SUCCESS) { + u8 cnt; + + ffa_info("SP response:\n[LSB]"); + for (cnt = 0; + cnt < sizeof(struct ffa_send_direct_data) / sizeof(u64); + cnt++) + ffa_info("0x%llx", ((u64 *)&msg)[cnt]); + } else { + ffa_err("Sending direct request error (%d)", ret); + } + + return ret; +} + +/** + *do_ffa_dev_list - implementation of the devlist subcommand + * @cmdtp: [in] Command Table + * @flag: flags + * @argc: number of arguments + * @argv: arguments + * + * This function queries the devices belonging to the UCLASS_FFA + * class. Currently, one device is expected to show up: the arm_ffa device + * + * Return: + * + * CMD_RET_SUCCESS: on success, otherwise failure + */ +int do_ffa_dev_list(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct udevice *dev = NULL; + int i, ret; + + ffa_info("arm_ffa uclass entries:"); + + for (i = 0, ret = uclass_first_device(UCLASS_FFA, &dev); + dev; + ret = uclass_next_device(&dev), i++) { + if (ret) + break; + + ffa_info("entry %d - instance %08x, ops %08x, plat %08x", + i, + (u32)map_to_sysmem(dev), + (u32)map_to_sysmem(dev->driver->ops), + (u32)map_to_sysmem(dev_get_plat(dev))); + } + + return cmd_process_error(cmdtp, ret); +} + +static struct cmd_tbl armffa_commands[] = { + U_BOOT_CMD_MKENT(getpart, 1, 1, do_ffa_get_singular_partition_info, "", ""), + U_BOOT_CMD_MKENT(ping, 1, 1, do_ffa_msg_send_direct_req, "", ""), + U_BOOT_CMD_MKENT(devlist, 0, 1, do_ffa_dev_list, "", ""), +}; + +/** + * do_armffa - the armffa command main function + * @cmdtp: Command Table + * @flag: flags + * @argc: number of arguments + * @argv: arguments + * + * This function identifies which armffa subcommand to run. + * Then, it makes sure the arm_ffa device is probed and + * ready for use. + * Then, it runs the subcommand. + * + * Return: + * + * CMD_RET_SUCCESS: on success, otherwise failure + */ +static int do_armffa(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct cmd_tbl *armffa_cmd; + int ret; + + if (argc < 2) + return CMD_RET_USAGE; + + armffa_cmd = find_cmd_tbl(argv[1], armffa_commands, ARRAY_SIZE(armffa_commands)); + + argc -= 2; + argv += 2; + + if (!armffa_cmd || argc > armffa_cmd->maxargs) + return CMD_RET_USAGE; + + ret = ffa_bus_discover(); + if (ret != FFA_ERR_STAT_SUCCESS) + return cmd_process_error(cmdtp, ret); + + if (!ffa_bus_ops_get()) + return -EINVAL; + + ret = armffa_cmd->cmd(armffa_cmd, flag, argc, argv); + + return cmd_process_error(armffa_cmd, ret); +} + +U_BOOT_CMD(armffa, 4, 1, do_armffa, + "Arm FF-A operations test command", + "getpart <partition UUID>\n" + " - lists the partition(s) info\n" + "ping <partition ID>\n" + " - sends a data pattern to the specified partition\n" + "devlist\n" + " - displays the arm_ffa device info\n"); diff --git a/drivers/arm-ffa/Kconfig b/drivers/arm-ffa/Kconfig index 882ffde90f..25816b479b 100644 --- a/drivers/arm-ffa/Kconfig +++ b/drivers/arm-ffa/Kconfig @@ -4,6 +4,7 @@ config ARM_FFA_TRANSPORT bool "Enable Arm Firmware Framework for Armv8-A driver" depends on DM && ARM64 select ARM_SMCCC + select CMD_ARMFFA select LIB_UUID select DEVRES help

This new version of the patchset provides improvements to the FF-A support and updates for the Sandbox test cases.
Please refer to the changelog below for a list of the major changes made.
Please refer to the changelog in the commit logs for a detailed view of the changes including the minor changes.
All previous review comments have been addressed.
I'll reply to the individual review comments after the submission of this v4 patchset.
Please find at [4] an example of the expected boot logs when enabling FF-A support for a platform. In this example the platform is Corstone1000. But it can be any Arm 64-bit platform.
Changelog of the major changes: ===========================
v4:
* add FF-A support README (doc/README.ffa.drv) * improving error handling by mapping the FF-A errors to standard errors and logs * replacing panics with an error log * align sandbox driver and tests with the new FF-A driver interfaces and new way of error handling * use the new FF-A driver interfaces for MM communication * discover MM partitions at runtime * copy FF-A driver private data to EFI runtime section at ExitBootServices() * moving the FF-A driver work to drivers/firmware/arm-ffa * improving features discovery in FFA_FEATURES * add remove/bind functions to the FF-A core device * improve how the driver behaves when bus discovery is done more than once * move clearing x0-x17 registers code into a new macro like done in the linux kernel * enable EFI MM communication for the Corstone1000 platform
v3: [3]
* port x0-x17 registers support from linux kernel as defined by SMCCCv1.2 * align the interfaces of the u-boot FF-A driver with those in the linux FF-A driver * remove the FF-A helper layer * make the u-boot FF-A driver independent from EFI * provide an optional config that enables copying the driver data to EFI runtime section at ExitBootServices service * use 64-bit version of FFA_RXTX_MAP, FFA_MSG_SEND_DIRECT_{REQ, RESP} * update armffa command with the new driver interfaces
v2 [2]:
* make FF-A bus discoverable using device_{bind, probe} APIs * remove device tree support
v1 [1]:
* introduce FF-A bus driver with device tree support * introduce armffa command * introduce FF-A Sandbox driver * add FF-A Sandbox test cases * introduce FF-A MM communication
Cheers, Abdellatif
[1]: https://lore.kernel.org/all/20220329151659.16894-1-abdellatif.elkhlifi@arm.c... [2]: https://lore.kernel.org/all/20220415122803.16666-1-abdellatif.elkhlifi@arm.c... [3]: https://lore.kernel.org/all/20220801172053.20163-1-abdellatif.elkhlifi@arm.c... [4]: example of boot logs when enabling FF-A
``` U-Boot 2022.07 (Jul 11 2022 - 13:42:58 +0000) corstone1000 aarch64 ... [FFA] Conduit is SMC [FFA] FF-A driver 1.0 FF-A framework 1.0 [FFA] Versions are compatible [FFA] endpoint ID is 0 [FFA] Using 1 4KB page(s) for RX/TX buffers size [FFA] RX buffer at virtual address 0xfdf18000 [FFA] TX buffer at virtual address 0xfdf1a000 [FFA] RX/TX buffers mapped [FFA] Reading partitions data from the RX buffer [FFA] Partition ID 8001 : info cached [FFA] Partition ID 8002 : info cached [FFA] Partition ID 8003 : info cached [FFA] 3 partition(s) found and cached Hit any key to stop autoboot: 0 ... [FFA] Preparing for checking partitions count [FFA] Searching partitions using the provided UUID [FFA] No partition found. Querying framework ... [FFA] Reading partitions data from the RX buffer [FFA] Number of partition(s) found matching the UUID: 1 EFI: Pre-allocating 1 partition(s) info structures [FFA] Preparing for filling partitions info [FFA] Searching partitions using the provided UUID [FFA] Partition ID 8003 matches the provided UUID EFI: MM partition ID 0x8003 ... EFI stub: Booting Linux Kernel... EFI stub: Using DTB from configuration table EFI stub: Exiting boot services... [FFA] Freeing RX/TX buffers INFO: EFI: FFA: prv data area at 0xfff2f768 INFO: EFI: FFA: runtime data area at 0xfcef4000 INFO: EFI: FFA: SPs runtime area at 0xfcef3000 INFO: EFI: FFA: runtime prv data now at 0xfcef4000 , SPs count 3 INFO: EFI: FFA: runtime data copied Booting Linux on physical CPU 0x0000000000 [0x410fd040] ```
Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Achin Gupta achin.gupta@arm.com Cc: Vishnu Banavath vishnu.banavath@arm.com Cc: Xueliang Zhong Xueliang.Zhong@arm.com
Abdellatif El Khlifi (10): arm64: smccc: add support for SMCCCv1.2 x0-x17 registers arm64: smccc: clear the Xn registers after SMC calls lib: uuid: introduce be_uuid_str_to_le_bin function arm_ffa: introduce Arm FF-A low-level driver arm_ffa: introduce armffa command arm_ffa: introduce the FF-A Sandbox driver arm_ffa: introduce Sandbox test cases for UCLASS_FFA arm_ffa: introduce armffa command Sandbox test arm_ffa: introduce FF-A MM communication arm_ffa: corstone1000: enable EFI MM communication
MAINTAINERS | 11 + arch/arm/cpu/armv8/cache.S | 19 + arch/arm/cpu/armv8/cache_v8.c | 6 +- arch/arm/cpu/armv8/smccc-call.S | 62 + arch/arm/lib/asm-offsets.c | 14 + cmd/Kconfig | 10 + cmd/Makefile | 2 + cmd/armffa.c | 242 +++ common/board_r.c | 9 + configs/corstone1000_defconfig | 4 + configs/sandbox64_defconfig | 2 + configs/sandbox_defconfig | 2 + doc/README.ffa.drv | 160 ++ doc/arch/sandbox.rst | 1 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/firmware/arm-ffa/Kconfig | 46 + drivers/firmware/arm-ffa/Makefile | 8 + drivers/firmware/arm-ffa/arm-ffa-uclass.c | 16 + drivers/firmware/arm-ffa/arm_ffa_prv.h | 201 +++ drivers/firmware/arm-ffa/core.c | 1358 +++++++++++++++++ .../arm-ffa/efi_ffa_runtime_data_mgr.c | 94 ++ drivers/firmware/arm-ffa/sandbox.c | 659 ++++++++ .../firmware/arm-ffa/sandbox_arm_ffa_prv.h | 144 ++ include/arm_ffa.h | 127 ++ include/configs/corstone1000.h | 9 + include/dm/uclass-id.h | 4 + include/linux/arm-smccc.h | 43 + include/mm_communication.h | 9 +- include/sandbox_arm_ffa.h | 91 ++ include/uuid.h | 8 + lib/efi_loader/Kconfig | 14 +- lib/efi_loader/efi_boottime.c | 22 + lib/efi_loader/efi_variable_tee.c | 263 +++- lib/uuid.c | 64 + test/cmd/Makefile | 2 + test/cmd/armffa.c | 40 + test/dm/Makefile | 2 + test/dm/ffa.c | 394 +++++ 39 files changed, 4156 insertions(+), 9 deletions(-) create mode 100644 cmd/armffa.c create mode 100644 doc/README.ffa.drv 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_prv.h create mode 100644 drivers/firmware/arm-ffa/core.c create mode 100644 drivers/firmware/arm-ffa/efi_ffa_runtime_data_mgr.c create mode 100644 drivers/firmware/arm-ffa/sandbox.c create mode 100644 drivers/firmware/arm-ffa/sandbox_arm_ffa_prv.h create mode 100644 include/arm_ffa.h create mode 100644 include/sandbox_arm_ffa.h create mode 100644 test/cmd/armffa.c create mode 100644 test/dm/ffa.c

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=
Changelog: ===============
v4:
* rename the commit title and improve description new commit title: the current
v3:
* port x0-x17 registers support from linux kernel as defined by SMCCCv1.2 commit title: arm64: smccc: add Xn registers support used by SMC calls
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org --- arch/arm/cpu/armv8/smccc-call.S | 53 +++++++++++++++++++++++++++++++++ arch/arm/lib/asm-offsets.c | 14 +++++++++ include/linux/arm-smccc.h | 43 ++++++++++++++++++++++++++ 3 files changed, 110 insertions(+)
diff --git a/arch/arm/cpu/armv8/smccc-call.S b/arch/arm/cpu/armv8/smccc-call.S index dc92b28777..ec6f299bc9 100644 --- a/arch/arm/cpu/armv8/smccc-call.S +++ b/arch/arm/cpu/armv8/smccc-call.S @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2015, Linaro Limited + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */ #include <linux/linkage.h> #include <linux/arm-smccc.h> @@ -45,3 +47,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 22fd541f9a..1bc2d90faa 100644 --- a/arch/arm/lib/asm-offsets.c +++ b/arch/arm/lib/asm-offsets.c @@ -9,6 +9,9 @@ * generate asm statements containing #defines, * compile this file to assembler, and then extract the * #defines from the assembly-language output. + * + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> @@ -117,6 +120,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 e1d09884a1..9105031d55 100644 --- a/include/linux/arm-smccc.h +++ b/include/linux/arm-smccc.h @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2015, Linaro Limited + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */ #ifndef __LINUX_ARM_SMCCC_H #define __LINUX_ARM_SMCCC_H @@ -70,6 +72,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

set to zero the x0-x17 registers
As per the SMCCC v1.2 spec, unused result and scratch registers can leak information after an SMC call. We can mitigate against this risk by returning zero in each register.
The leakage we are referring to is data leakage across exception levels. The intent is to prevent lower exception levels (EL1/EL0) from reading the SMC data exchanged at EL2.
Changelog: ===============
v4:
* move the clearing code into a new macro: clear_gp_regs
v3:
* clear the Xn registers after SMC calls
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org --- arch/arm/cpu/armv8/smccc-call.S | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/arch/arm/cpu/armv8/smccc-call.S b/arch/arm/cpu/armv8/smccc-call.S index ec6f299bc9..32f3eb8eeb 100644 --- a/arch/arm/cpu/armv8/smccc-call.S +++ b/arch/arm/cpu/armv8/smccc-call.S @@ -50,6 +50,12 @@ ENDPROC(__arm_smccc_hvc)
#ifdef CONFIG_ARM64
+ .macro clear_gp_regs + .irp n,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17 + mov x\n, xzr + .endr + .endm + .macro SMCCC_1_2 instr /* Save `res` and free a GPR that won't be clobbered */ stp x1, x19, [sp, #-16]! @@ -84,6 +90,9 @@ ENDPROC(__arm_smccc_hvc) stp x14, x15, [x19, #ARM_SMCCC_1_2_REGS_X14_OFFS] stp x16, x17, [x19, #ARM_SMCCC_1_2_REGS_X16_OFFS]
+ /* x0-x17 registers can leak information after an SMC or HVC call. Let's clear them */ + clear_gp_regs + /* Restore original x19 */ ldp xzr, x19, [sp], #16 ret

convert big endian UUID string to little endian buffer
Changelog: ===============
v4:
* rename ffa_uuid_str_to_bin to be_uuid_str_to_le_bin and put in a standalone commit (the current)
v3:
* introduce ffa_uuid_str_to_bin (provided by arm_ffa: introduce Arm FF-A low-level driver)
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org --- include/uuid.h | 8 +++++++ lib/uuid.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+)
diff --git a/include/uuid.h b/include/uuid.h index 4a4883d3b5..ad3af350f9 100644 --- a/include/uuid.h +++ b/include/uuid.h @@ -2,6 +2,8 @@ /* * Copyright (C) 2014 Samsung Electronics * Przemyslaw Marczak p.marczak@samsung.com + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */ #ifndef __UUID_H__ #define __UUID_H__ @@ -44,4 +46,10 @@ 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); + +/** + * be_uuid_str_to_le_bin - Converts a big endian UUID string to a little endian buffer + */ +int be_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 465e1ac38f..15a9ab49d5 100644 --- a/lib/uuid.c +++ b/lib/uuid.c @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-2.0+ /* * Copyright 2011 Calxeda, Inc. + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> @@ -346,6 +348,68 @@ int uuid_str_to_bin(const char *uuid_str, unsigned char *uuid_bin, return 0; }
+/** + * be_uuid_str_to_le_bin - Converts a big endian UUID string to a little endian buffer + * @uuid_str: UUID string in big endian format (36 bytes wide + '/0') + * @uuid_bin: preallocated 16 bytes UUID buffer in little endian format + * + * UUID string is 36 characters (36 bytes): + * + * xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + * be be be be be + * + * where x is a hexadecimal character. Fields are separated by '-'s. + * When converting to a binary UUID, these endianness rules apply: + * be: means the field in the string is considered a big endian hex number + * and should be converted to little endian binary format + * + * Return: + * + * uuid_bin filled with little endian UUID data + * On success 0 is returned. Otherwise, failure code. + */ +int be_uuid_str_to_le_bin(const char *uuid_str, unsigned char *uuid_bin) +{ + u16 tmp16 = 0; + u32 tmp32 = 0; + u64 tmp64 = 0; + + if (!uuid_str_valid(uuid_str) || !uuid_bin) + return -EINVAL; + + /* + * reverse bytes from big to little endian + */ + tmp32 = simple_strtoul(uuid_str, NULL, 16); + memcpy(uuid_bin, &tmp32, 4); + + /* + * reverse bytes from big to little endian + */ + tmp16 = simple_strtoul(uuid_str + 9, NULL, 16); + memcpy(uuid_bin + 4, &tmp16, 2); + + /* + * reverse bytes from big to little endian + */ + tmp16 = simple_strtoul(uuid_str + 14, NULL, 16); + memcpy(uuid_bin + 6, &tmp16, 2); + + /* + * reverse bytes from big to little endian + */ + tmp16 = simple_strtoul(uuid_str + 19, NULL, 16); + memcpy(uuid_bin + 8, &tmp16, 2); + + /* + * reverse bytes from big to little endian + */ + tmp64 = simple_strtoull(uuid_str + 24, NULL, 16); + memcpy(uuid_bin + 10, (char *)&tmp64, 6); + + return 0; +} + /* * uuid_bin_to_str() - convert big endian binary data to string UUID or GUID. *

Add the driver 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.
32-bit version of the ABIs is supported and 64-bit version of FFA_RXTX_MAP and FFA_MSG_SEND_DIRECT_{REQ, RESP}.
In u-boot FF-A design, FF-A is considered as a discoverable bus. 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:
- partition_info_get - sync_send_receive - rxtx_unmap
This implementation provides an optional feature to copy the driver data to EFI runtime area.
[1]: https://developer.arm.com/documentation/den0077/latest/
Changelog: ===============
v4:
* add doc/README.ffa.drv * moving the FF-A driver work to drivers/firmware/arm-ffa * use less #ifdefs in lib/efi_loader/efi_boottime.c and replace #if defined by #if CONFIG_IS_ENABLED * improving error handling by mapping the FF-A errors to standard errors and logs * replacing panics with an error log and returning an error code * improving features discovery in FFA_FEATURES by introducing rxtx_min_pages private data field * add ffa_remove and ffa_bind functions * improve how the driver behaves when bus discovery is done more than once
v3:
* align the interfaces of the u-boot FF-A driver with those in the linux FF-A driver * remove the FF-A helper layer * make the u-boot FF-A driver independent from EFI * provide an optional config that enables copying the driver data to EFI runtime section at ExitBootServices service * use 64-bit version of FFA_RXTX_MAP, FFA_MSG_SEND_DIRECT_{REQ, RESP}
v2:
* make FF-A bus discoverable using device_{bind, probe} APIs * remove device tree support
v1:
* introduce FF-A bus driver with device tree support
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org --- MAINTAINERS | 7 + common/board_r.c | 9 + doc/README.ffa.drv | 160 ++ drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/firmware/arm-ffa/Kconfig | 39 + drivers/firmware/arm-ffa/Makefile | 7 + drivers/firmware/arm-ffa/arm-ffa-uclass.c | 16 + drivers/firmware/arm-ffa/arm_ffa_prv.h | 196 +++ drivers/firmware/arm-ffa/core.c | 1344 +++++++++++++++++ .../arm-ffa/efi_ffa_runtime_data_mgr.c | 94 ++ include/arm_ffa.h | 127 ++ include/dm/uclass-id.h | 4 + lib/efi_loader/efi_boottime.c | 15 + 14 files changed, 2021 insertions(+) create mode 100644 doc/README.ffa.drv 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_prv.h create mode 100644 drivers/firmware/arm-ffa/core.c create mode 100644 drivers/firmware/arm-ffa/efi_ffa_runtime_data_mgr.c create mode 100644 include/arm_ffa.h
diff --git a/MAINTAINERS b/MAINTAINERS index 83346183ee..02b84d5074 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -248,6 +248,13 @@ 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 abdellatif.elkhlifi@arm.com +S: Maintained +F: doc/README.ffa.drv +F: drivers/firmware/arm-ffa/ +F: include/arm_ffa.h + ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de M: Fabio Estevam festevam@gmail.com diff --git a/common/board_r.c b/common/board_r.c index 56eb60fa27..8c99faddfd 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -7,6 +7,8 @@ * (C) Copyright 2002 * Sysgo Real-Time Solutions, GmbH <www.elinos.com> * Marius Groeger mgroeger@sysgo.de + * + * (C) Copyright 2022 ARM Limited */
#include <common.h> @@ -66,6 +68,10 @@ #include <efi_loader.h> #include <relocate.h>
+#ifdef CONFIG_ARM_FFA_TRANSPORT +#include <arm_ffa.h> +#endif + DECLARE_GLOBAL_DATA_PTR;
ulong monitor_flash_len; @@ -770,6 +776,9 @@ static init_fnc_t init_sequence_r[] = { INIT_FUNC_WATCHDOG_RESET initr_net, #endif +#ifdef CONFIG_ARM_FFA_TRANSPORT + ffa_bus_discover, +#endif #ifdef CONFIG_POST initr_post, #endif diff --git a/doc/README.ffa.drv b/doc/README.ffa.drv new file mode 100644 index 0000000000..1c0a33deb8 --- /dev/null +++ b/doc/README.ffa.drv @@ -0,0 +1,160 @@ +Arm FF-A Driver +==================== + +Introduction +-------------------- + +FF-A stands for Firmware Framework for Arm A-profile processors. + +FF-A specifies interfaces that enable a pair of software sandboxes to communicate with each other. A sandbox aka partition could +be a VM in the Normal or Secure world, an application in S-EL0, or a Trusted OS in S-EL1. + +This FF-A driver implements the interfaces to communicate with partitions in the Secure world aka Secure partitions (SPs). + +The driver 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. + +FF-A driver 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. + +FF-A and SMC specifications +------------------------------------------- + +The current implementation of the driver relies on FF-A specification v1.0 and uses SMC32 calling convention. + +The driver has been tested with Optee OS which supports SMC32 for most of the SMC ABIs. + +For more details please refer to: https://developer.arm.com/documentation/den0077/a/?lang=en + +The FF-A driver uses 64-bit registers as per SMCCCv1.2 specification. + +For more details please refer to: https://documentation-service.arm.com/static/5f8edaeff86e16515cdbe4c6?token= + +Supported hardware +-------------------------------- + +Aarch64 plaforms + +Configuration +---------------------- + +CONFIG_ARM_FFA_TRANSPORT + Enables the FF-A bus driver. Turn this on if you want to use FF-A communication. + +CONFIG_ARM_FFA_EFI_RUNTIME_MODE + Optional config that enables EFI runtime support for FF-A data and code. + ffa_copy_runtime_data allows to copy the FF-A driver data structures to EFI runtime data section. + Turning the config on makes ffa_copy_runtime_data available for use and the driver code placed at EFI runtime code section. + Call ffa_copy_runtime_data at the event on which you want the FF-A data to be copied (example: at ExitBootServices). + +CONFIG_SANDBOX_FFA + Enables FF-A Sandbox driver. This emulates the FF-A ABIs handling under Sandbox and provides + functional tests for FF-A. + +FF-A ABIs under the hood +--------------------------------------- + +Invoking an FF-A ABI involves providing to the secure world/hypervisor the expected arguments from the ABI. + +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 giving back to the u-boot FF-A driver (non secure world). + +The driver reads the response and processes it accordingly. + +This methodology applies to all the FF-A ABIs in the driver. + +FF-A bus discovery in u-boot +------------------------------------------- + +When CONFIG_ARM_FFA_TRANSPORT is enabled, the FF-A bus is automatically discovered at initcall level (after u-boot relocation). + +The function that triggers the discovery process is ffa_bus_discover. + +ffa_bus_discover creates, binds and probes the arm_ffa device using device_{bind, probe} APIs. + +When the device is probed, ffa_probe is called which tries to communicate with the secure world or hypervisor. + +The FF-A bus is usable when these checks succeed: + +- querying the FF-A framework version +- querying from secure world the u-boot endpoint ID +- querying from secure world the supported features of the specified FF-A calls +- mapping the RX/TX buffers +- querying from secure world all the partitions information + +Probing fails when any of these operations fail. The FF-A bus discovery succeeds when probing is successful. + +When discovery fails the arm_ffa device is destroyed. + +The bus driver layer +------------------------------ + +The driver comes on top of the SMCCC layer and is implemented in drivers/firmware/arm-ffa/core.c + +The driver provides the following features: + +- 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 + +- 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, +the driver takes care of how to transport that to the secure world/hypervisor using FF-A. + +- The driver provides callbacks to be used by clients to access the FF-A bus: + +partition_info_get +sync_send_receive +rxtx_unmap + +- FF-A bus discovery at initcalls level (after u-boot relocation). The bus is up and running if the FF-A framework is responsive and compatible with the driver. + +- When EFI is enabled, unmap the RX/TX buffers at ExitBootServices() level. + +- When CONFIG_ARM_FFA_EFI_RUNTIME_MODE enabled, ffa_copy_runtime_data function is available for use. + +Using armffa command +----------------------------------- + +armffa is a command showcasing how to use the FF-A driver and how to invoke its operations. + +This provides a guidance to the client developers on how to call the FF-A bus interfaces. + +Usage: + +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 the arm_ffa device info diff --git a/drivers/Kconfig b/drivers/Kconfig index 8b6fead351..b06b1ae481 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -6,6 +6,8 @@ source "drivers/core/Kconfig"
source "drivers/adc/Kconfig"
+source "drivers/firmware/arm-ffa/Kconfig" + source "drivers/ata/Kconfig"
source "drivers/axi/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index eba9940231..c3bfad94ac 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -110,6 +110,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/arm-ffa/Kconfig b/drivers/firmware/arm-ffa/Kconfig new file mode 100644 index 0000000000..aceb61cf49 --- /dev/null +++ b/drivers/firmware/arm-ffa/Kconfig @@ -0,0 +1,39 @@ +# 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 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. + + This driver 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. + 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. + + For more details about the FF-A driver, please refer to doc/README.ffa.drv + +config ARM_FFA_EFI_RUNTIME_MODE + bool "Enable EFI runtime support for FF-A data and code" + depends on ARM_FFA_TRANSPORT && EFI_LOADER + help + Allows FF-A driver data structures and code to be accessible at EFI runtime. + FF-A data is copied by ffa_copy_runtime_data function. + The driver Code needed at runtime is placed at EFI runtime code section. + Turning this on makes ffa_copy_runtime_data available for use and the driver + code placed at EFI runtime code section. diff --git a/drivers/firmware/arm-ffa/Makefile b/drivers/firmware/arm-ffa/Makefile new file mode 100644 index 0000000000..0b9b0a61b4 --- /dev/null +++ b/drivers/firmware/arm-ffa/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2022 Abdellatif El Khlifi abdellatif.elkhlifi@arm.com +# + +obj-y += arm-ffa-uclass.o core.o +obj-$(CONFIG_ARM_FFA_EFI_RUNTIME_MODE) += efi_ffa_runtime_data_mgr.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 0000000000..7d9695d289 --- /dev/null +++ b/drivers/firmware/arm-ffa/arm-ffa-uclass.c @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <dm.h> +#include <asm/global_data.h> + +DECLARE_GLOBAL_DATA_PTR; + +UCLASS_DRIVER(ffa) = { + .name = "ffa", + .id = UCLASS_FFA, +}; diff --git a/drivers/firmware/arm-ffa/arm_ffa_prv.h b/drivers/firmware/arm-ffa/arm_ffa_prv.h new file mode 100644 index 0000000000..7bc90f7f66 --- /dev/null +++ b/drivers/firmware/arm-ffa/arm_ffa_prv.h @@ -0,0 +1,196 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __ARM_FFA_PRV_H +#define __ARM_FFA_PRV_H + +#include <arm_ffa.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/arm-smccc.h> + +/* + * This header is private. It is exclusively used by the FF-A driver + */ + +/* FF-A core driver name */ +#define FFA_DRV_NAME "arm_ffa" + +/* 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*/ +}; + +/* number of the errors supported by the FF-A specification */ +#define MAX_NUMBER_FFA_ERR 9 + +/* 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) + +/* The FF-A SMC function definitions */ + +typedef struct arm_smccc_1_2_regs ffa_value_t; +typedef void (*invoke_ffa_fn_t)(ffa_value_t args, ffa_value_t *res); + +/* + * 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 */ +}; + +/** + * 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 - structure hosting 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 + * + * Data structure hosting 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 { + u64 rxbuf; /* virtual address */ + u64 txbuf; /* virtual address */ + size_t rxtx_min_pages; /* minimum number of pages in each of the RX/TX buffers */ +}; + +/** + * 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_partitions - descriptors for all secure partitions + * @count: The number of partitions descriptors + * @descs The partitions descriptors table + * + * This data structure contains the partitions descriptors table + */ +struct ffa_partitions { + u32 count; + struct ffa_partition_desc *descs; /* virtual address */ +}; + +/** + * struct ffa_prvdata - the driver private data structure + * + * @dev: The arm_ffa device under u-boot driver model + * @ffa_ops: The driver operations structure + * @fwk_version: FF-A framework version + * @id: u-boot endpoint ID + * @partitions: The partitions descriptors structure + * @pair: The RX/TX buffers pair + * @invoke_ffa_fn: The function executing the FF-A function + * + * The driver data structure hosting all resident data. + */ +struct ffa_prvdata { + struct udevice *dev; + struct ffa_bus_ops ffa_ops; + u32 fwk_version; + u16 id; + struct ffa_partitions partitions; + struct ffa_rxtxpair pair; + invoke_ffa_fn_t invoke_ffa_fn; +}; + +/** + * ffa_device_get - create, bind and probe the arm_ffa device + */ +int ffa_device_get(void); + +/** + * ffa_bus_prvdata_get - bus driver private data getter + */ +struct ffa_prvdata **ffa_bus_prvdata_get(void); + +#endif diff --git a/drivers/firmware/arm-ffa/core.c b/drivers/firmware/arm-ffa/core.c new file mode 100644 index 0000000000..41c7b96e68 --- /dev/null +++ b/drivers/firmware/arm-ffa/core.c @@ -0,0 +1,1344 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include "arm_ffa_prv.h" +#include <asm/global_data.h> +#include <common.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <dm/devres.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> +#include <log.h> +#include <malloc.h> +#include <string.h> +#include <uuid.h> + +DECLARE_GLOBAL_DATA_PTR; + +/** + * The device private data structure containing all the resident + * data read from secure world + */ +__ffa_runtime_data struct ffa_prvdata *ffa_priv_data; + +/* Error mapping declarations */ + +__ffa_runtime_data int ffa_to_std_errmap[MAX_NUMBER_FFA_ERR] = { + 0, + -EOPNOTSUPP, /* NOT_SUPPORTED */ + -EINVAL, /* INVALID_PARAMETERS */ + -ENOMEM, /* NO_MEMORY */ + -EBUSY, /* BUSY */ + -EINTR, /* INTERRUPTED */ + -EACCES, /* DENIED */ + -EAGAIN, /* RETRY */ + -ECANCELED, /* ABORTED */ +}; + +struct ffa_abi_errmap err_msg_map[FFA_ERRMAP_COUNT] = { + [FFA_ID_TO_ERRMAP_ID(FFA_VERSION)] = { + { + "", + "NOT_SUPPORTED: A Firmware Framework implementation does not exist", + "", /* INVALID_PARAMETERS */ + "", /* NO_MEMORY */ + "", /* BUSY */ + "", /* INTERRUPTED */ + "", /* DENIED */ + "", /* RETRY */ + "", /* ABORTED */ + }, + }, + [FFA_ID_TO_ERRMAP_ID(FFA_ID_GET)] = { + { + "", + "NOT_SUPPORTED: This function is not implemented at this FF-A instance", + "", /* INVALID_PARAMETERS */ + "", /* NO_MEMORY */ + "", /* BUSY */ + "", /* INTERRUPTED */ + "", /* DENIED */ + "", /* RETRY */ + "", /* ABORTED */ + }, + }, + [FFA_ID_TO_ERRMAP_ID(FFA_FEATURES)] = { + { + "", + "NOT_SUPPORTED: FFA_RXTX_MAP is not implemented at this FF-A instance", + "", /* INVALID_PARAMETERS */ + "", /* NO_MEMORY */ + "", /* BUSY */ + "", /* INTERRUPTED */ + "", /* DENIED */ + "", /* RETRY */ + "", /* ABORTED */ + }, + }, + [FFA_ID_TO_ERRMAP_ID(FFA_PARTITION_INFO_GET)] = { + { + "", + "NOT_SUPPORTED: This function is not implemented at this FF-A instance", + "INVALID_PARAMETERS: Unrecognized UUID", + "NO_MEMORY: Results cannot fit in RX buffer of the caller", + "BUSY: RX buffer of the caller is not free", + "", /* INTERRUPTED */ + "DENIED: Callee is not in a state to handle this request", + "", /* RETRY */ + "", /* ABORTED */ + }, + }, + [FFA_ID_TO_ERRMAP_ID(FFA_RXTX_UNMAP)] = { + { + "", + "NOT_SUPPORTED: FFA_RXTX_UNMAP is not implemented at this FF-A instance", + "INVALID_PARAMETERS: No buffer pair registered on behalf of the caller", + "", /* NO_MEMORY */ + "", /* BUSY */ + "", /* INTERRUPTED */ + "", /* DENIED */ + "", /* RETRY */ + "", /* ABORTED */ + }, + }, + [FFA_ID_TO_ERRMAP_ID(FFA_RX_RELEASE)] = { + { + "", + "NOT_SUPPORTED: FFA_RX_RELEASE is not implemented at this FF-A instance", + "", /* INVALID_PARAMETERS */ + "", /* NO_MEMORY */ + "", /* BUSY */ + "", /* INTERRUPTED */ + "DENIED: Caller did not have ownership of the RX buffer", + "", /* RETRY */ + "", /* ABORTED */ + }, + }, + [FFA_ID_TO_ERRMAP_ID(FFA_RXTX_MAP)] = { + { + "", + "NOT_SUPPORTED: This function is not implemented at this FF-A instance", + "INVALID_PARAMETERS: Field(s) in input parameters incorrectly encoded", + "NO_MEMORY: Not enough memory", + "", /* BUSY */ + "", /* INTERRUPTED */ + "DENIED: Buffer pair already registered", + "", /* RETRY */ + "", /* ABORTED */ + }, + }, +}; + +/** + * ffa_to_std_errno - convert FF-A error code to standard error code + * @ffa_errno: Error code returned by the FF-A ABI + * + * This runtime function maps 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 + */ +__ffa_runtime 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 + * + * This boot time function maps 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 + */ +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_msg_map[abi_idx].err_str[err_idx]) + return -EINVAL; + + ffa_err("%s", err_msg_map[abi_idx].err_str[err_idx]); + + return 0; +} + +/* + * Driver core functions + */ + +/** + * ffa_remove_device - removes the arm_ffa device + * @dev: the device to be removed + * + * This boot time function makes sure the arm_ffa device is removed + * No need to free the kmalloced data when the device is destroyed. + * It's automatically done by devm management by + * device_remove() -> device_free() -> devres_release_probe(). + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int ffa_remove_device(struct udevice *dev) +{ + int ret; + + if (!dev) { + ffa_err("no udevice found"); + return -ENODEV; + } + + ret = device_remove(dev, DM_REMOVE_NORMAL); + if (ret) { + ffa_err("unable to remove. err:%d\n", ret); + return ret; + } + + ffa_info("device removed and freed"); + + ret = device_unbind(dev); + if (ret) { + ffa_err("unable to unbind. err:%d\n", ret); + return ret; + } + + ffa_info("device unbound"); + + return 0; +} + +/** + * ffa_device_get - create, bind and probe the arm_ffa device + * + * This boot time function makes sure the arm_ffa device is + * created, bound to this driver, probed and ready to use. + * Arm FF-A transport is implemented through a single u-boot + * device managing the FF-A bus (arm_ffa). + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int ffa_device_get(void) +{ + int ret; + struct udevice *dev = NULL; + + ret = device_bind(dm_root(), + DM_DRIVER_GET(arm_ffa), + FFA_DRV_NAME, + NULL, + ofnode_null(), + &dev); + if (ret) + return ret; + + /* The FF-A bus discovery succeeds when probing is successful */ + ret = device_probe(dev); + if (ret) { + ffa_err("arm_ffa device probing failed"); + ffa_remove_device(dev); + return ret; + } + + return 0; +} + +/** + * ffa_get_version - FFA_VERSION handler function + * + * This is the boot time function that implements FFA_VERSION FF-A function + * to get from the secure world the FF-A framework version + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_get_version(void) +{ + u16 major, minor; + ffa_value_t res = {0}; + int ffa_errno; + + ffa_priv_data->invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_32(FFA_VERSION), + .a1 = FFA_VERSION_1_0, .a2 = 0, .a3 = 0, .a4 = 0, .a5 = 0, .a6 = 0, .a7 = 0, + }, &res); + + ffa_errno = (int)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((u32)res.a0); + minor = GET_FFA_MINOR_VERSION((u32)res.a0); + + ffa_info("FF-A driver %d.%d\nFF-A framework %d.%d", + FFA_MAJOR_VERSION, FFA_MINOR_VERSION, major, minor); + + if ((major == FFA_MAJOR_VERSION && minor >= FFA_MINOR_VERSION)) { + ffa_info("Versions are compatible "); + + ffa_priv_data->fwk_version = (u32)res.a0; + + return 0; + } + + ffa_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 + * + * This is the boot time function that implements 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(void) +{ + ffa_value_t res = {0}; + int ffa_errno; + + ffa_priv_data->invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_32(FFA_ID_GET), + .a1 = 0, .a2 = 0, .a3 = 0, .a4 = 0, .a5 = 0, .a6 = 0, .a7 = 0, + }, &res); + + if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) { + ffa_priv_data->id = GET_SELF_ENDPOINT_ID((u32)res.a2); + ffa_info("endpoint ID is %u", ffa_priv_data->id); + + return 0; + } + + ffa_errno = (int)res.a2; + + ffa_print_error_log(FFA_ID_GET, ffa_errno); + + return ffa_to_std_errno(ffa_errno); +} + +/** + * ffa_set_rxtx_buffers_pages_cnt - sets the minimum number of pages in each of the RX/TX buffers + * @prop_field: properties field obtained from FFA_FEATURES ABI + * + * This boot time function sets the minimum number of pages + * in each of the RX/TX buffers in the private data structure + * + * Return: + * + * buf_4k_pages points to the returned number of pages + * 0 on success. Otherwise, failure + */ +static int ffa_set_rxtx_buffers_pages_cnt(u32 prop_field) +{ + if (!ffa_priv_data) + return -EINVAL; + + switch (prop_field) { + case RXTX_4K: + ffa_priv_data->pair.rxtx_min_pages = 1; + break; + case RXTX_16K: + ffa_priv_data->pair.rxtx_min_pages = 4; + break; + case RXTX_64K: + ffa_priv_data->pair.rxtx_min_pages = 16; + break; + default: + ffa_err("RX/TX buffer size not supported"); + return -EINVAL; + } + + return 0; +} + +/** + * ffa_get_rxtx_map_features - FFA_FEATURES handler function with FFA_RXTX_MAP argument + * + * This is the boot time function that implements 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(void) +{ + ffa_value_t res = {0}; + int ffa_errno; + + ffa_priv_data->invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_32(FFA_FEATURES), + .a1 = FFA_SMC_64(FFA_RXTX_MAP), + .a2 = 0, .a3 = 0, .a4 = 0, .a5 = 0, .a6 = 0, .a7 = 0, + }, &res); + + if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) + return ffa_set_rxtx_buffers_pages_cnt((u32)res.a2); + + ffa_errno = (int)res.a2; + ffa_print_error_log(FFA_FEATURES, ffa_errno); + + return ffa_to_std_errno(ffa_errno); +} + +/** + * ffa_free_rxtx_buffers - frees the RX/TX buffers + * + * This is the boot time function used to free the RX/TX buffers + * + */ +static void ffa_free_rxtx_buffers(void) +{ + ffa_info("Freeing RX/TX buffers"); + + if (ffa_priv_data->pair.rxbuf) { + free((void *)ffa_priv_data->pair.rxbuf); + ffa_priv_data->pair.rxbuf = 0; + } + + if (ffa_priv_data->pair.txbuf) { + free((void *)ffa_priv_data->pair.txbuf); + ffa_priv_data->pair.txbuf = 0; + } +} + +/** + * ffa_alloc_rxtx_buffers - allocates the RX/TX buffers + * + * This is the boot time function 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. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_alloc_rxtx_buffers(void) +{ + u64 bytes; + + ffa_info("Using %lu 4KB page(s) for RX/TX buffers size", + ffa_priv_data->pair.rxtx_min_pages); + + bytes = ffa_priv_data->pair.rxtx_min_pages * SZ_4K; + + /* RX/TX buffers addresses should be PAGE_SIZE aligned */ + + ffa_priv_data->pair.rxbuf = (u64)memalign(PAGE_SIZE, bytes); + if (!ffa_priv_data->pair.rxbuf) { + ffa_err("failure to allocate RX buffer"); + return -ENOBUFS; + } + + ffa_info("RX buffer at virtual address 0x%llx", ffa_priv_data->pair.rxbuf); + + ffa_priv_data->pair.txbuf = (u64)memalign(PAGE_SIZE, bytes); + if (!ffa_priv_data->pair.txbuf) { + free((void *)ffa_priv_data->pair.rxbuf); + ffa_priv_data->pair.rxbuf = 0; + ffa_err("failure to allocate the TX buffer"); + return -ENOBUFS; + } + + ffa_info("TX buffer at virtual address 0x%llx", ffa_priv_data->pair.txbuf); + + /* + * make sure the buffers are cleared before use + */ + memset((void *)ffa_priv_data->pair.rxbuf, 0, bytes); + memset((void *)ffa_priv_data->pair.txbuf, 0, bytes); + + return 0; +} + +/** + * ffa_map_rxtx_buffers - FFA_RXTX_MAP handler function + * + * This is the boot time function that implements FFA_RXTX_MAP FF-A function + * to map the RX/TX buffers + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_map_rxtx_buffers(void) +{ + int ret; + ffa_value_t res = {0}; + int ffa_errno; + + ret = ffa_alloc_rxtx_buffers(); + 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 + */ + + ffa_priv_data->invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_64(FFA_RXTX_MAP), + .a1 = ffa_priv_data->pair.txbuf, + .a2 = ffa_priv_data->pair.rxbuf, + .a3 = ffa_priv_data->pair.rxtx_min_pages, + .a4 = 0, .a5 = 0, .a6 = 0, .a7 = 0, + }, &res); + + if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) { + ffa_info("RX/TX buffers mapped"); + return 0; + } + + ffa_errno = (int)res.a2; + ffa_print_error_log(FFA_RXTX_MAP, ffa_errno); + + ffa_free_rxtx_buffers(); + + return ffa_to_std_errno(ffa_errno); +} + +/** + * ffa_unmap_rxtx_buffers - FFA_RXTX_UNMAP handler function + * + * This is the boot time function that implements FFA_RXTX_UNMAP FF-A function + * to unmap the RX/TX buffers + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_unmap_rxtx_buffers(void) +{ + ffa_value_t res = {0}; + int ffa_errno; + + ffa_priv_data->invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_32(FFA_RXTX_UNMAP), + .a1 = PREP_SELF_ENDPOINT_ID(ffa_priv_data->id), + .a2 = 0, .a3 = 0, .a4 = 0, .a5 = 0, .a6 = 0, .a7 = 0, + }, &res); + + if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) { + ffa_free_rxtx_buffers(); + return 0; + } + + ffa_errno = (int)res.a2; + ffa_print_error_log(FFA_RXTX_UNMAP, ffa_errno); + + return ffa_to_std_errno(ffa_errno); +} + +/** + * ffa_release_rx_buffer - FFA_RX_RELEASE handler function + * + * This is the boot time function that invokes 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(void) +{ + ffa_value_t res = {0}; + int ffa_errno; + + ffa_priv_data->invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_32(FFA_RX_RELEASE), + .a1 = 0, .a2 = 0, .a3 = 0, .a4 = 0, .a5 = 0, .a6 = 0, .a7 = 0, + }, &res); + + if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) + return 0; + + ffa_errno = (int)res.a2; + ffa_print_error_log(FFA_RX_RELEASE, ffa_errno); + + return ffa_to_std_errno(ffa_errno); +} + +/** + * ffa_uuid_are_identical - checks whether two given UUIDs are identical + * @uuid1: first UUID + * @uuid2: second UUID + * + * This is a boot time function used by ffa_read_partitions_info to search + * for a UUID in the partitions descriptors table + * + * Return: + * + * 1 when UUIDs match. Otherwise, 0 + */ +int 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 - reads the data queried by FFA_PARTITION_INFO_GET + * and saves it in the private structure + * @count: The number of partitions queried + * @part_uuid: Pointer to the partition(s) UUID + * + * This is the boot time function that reads the partitions information + * returned by the FFA_PARTITION_INFO_GET and saves it in the private + * data structure. + * + * Return: + * + * The private data structure is updated with the partition(s) information + * 0 is returned on success. Otherwise, failure + */ +static int ffa_read_partitions_info(u32 count, struct ffa_partition_uuid *part_uuid) +{ + if (!count) { + ffa_err("no partition detected"); + return -ENODATA; + } + + ffa_info("Reading partitions data from the RX buffer"); + + 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 = ffa_priv_data->pair.rxtx_min_pages * SZ_4K; + + if (data_bytes > buf_bytes) { + ffa_err("partitions data size exceeds the RX buffer size:"); + ffa_err(" sizes in bytes: data %llu , RX buffer %llu ", + data_bytes, + buf_bytes); + + return -ENOMEM; + } + + ffa_priv_data->partitions.descs = (struct ffa_partition_desc *) + devm_kmalloc(ffa_priv_data->dev, data_bytes, __GFP_ZERO); + if (!ffa_priv_data->partitions.descs) { + ffa_err("cannot allocate partitions data buffer"); + return -ENOMEM; + } + + parts_info = (struct ffa_partition_info *)ffa_priv_data->pair.rxbuf; + + for (desc_idx = 0 ; desc_idx < count ; desc_idx++) { + ffa_priv_data->partitions.descs[desc_idx].info = + parts_info[desc_idx]; + + ffa_info("Partition ID %x : info cached", + ffa_priv_data->partitions.descs[desc_idx].info.id); + } + + ffa_priv_data->partitions.count = count; + + ffa_info("%d partition(s) found and cached", count); + + } else { + u32 rx_desc_idx, cached_desc_idx; + struct ffa_partition_info *parts_info; + u8 desc_found; + + parts_info = (struct ffa_partition_info *)ffa_priv_data->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 < ffa_priv_data->partitions.count; + cached_desc_idx++) { + /* + * save the UUID + */ + if (ffa_priv_data->partitions.descs[cached_desc_idx].info.id == + parts_info[rx_desc_idx].id) { + ffa_priv_data->partitions.descs[cached_desc_idx].sp_uuid = + *part_uuid; + + desc_found = 1; + break; + } + } + + if (!desc_found) + return -ENODATA; + } + } + + return 0; +} + +/** + * ffa_query_partitions_info - invokes FFA_PARTITION_INFO_GET and saves partitions data + * + * @part_uuid: Pointer to the partition(s) UUID + * @pcount: Pointer to the number of partitions variable filled when querying + * + * This is the boot time function that executes the FFA_PARTITION_INFO_GET + * to query the partitions data. Then, it calls ffa_read_partitions_info + * to save the data in the private data structure. + * + * 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 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; + } + + ffa_priv_data->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, + .a5 = 0, + .a6 = 0, + .a7 = 0, + }, &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((u32)res.a2, part_uuid); + if (ret) { + ffa_err("failed to read partition(s) data , error (%d)", ret); + ffa_release_rx_buffer(); + 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(); + + return ret; + } + + ffa_errno = (int)res.a2; + ffa_print_error_log(FFA_PARTITION_INFO_GET, ffa_errno); + + return ffa_to_std_errno(ffa_errno); +} + +/** + * ffa_get_partitions_info - FFA_PARTITION_INFO_GET handler function + * + * The passed arguments: + * Mode 1: When getting from the driver the number of + * secure partitions: + * @uuid_str: pointer to the UUID string + * @parts_size: pointer to the variable that contains the number of partitions + * The variable will be set by the driver + * @buffer: NULL + * + * Mode 2: When requesting the driver to return the + * partitions information: + * @uuid_str: pointer to the UUID string + * @parts_size: pointer to the size of the SPs information buffer in bytes + * @buffer: pointer to SPs information buffer + * (allocated by the client). + * The buffer will be filled by the driver + * + * This is the boot time function that queries the secure partition data from + * the private data structure. If not found, it invokes 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 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(). + * + * When invoked through a client request, ffa_get_partitions_info should be + * called twice. First call is to get from the driver the number of secure + * partitions (SPs) associated to a particular UUID. + * Then, the caller (client) allocates the buffer to host the SPs data and + * issues a 2nd call. Then, the driver fills the SPs data in the pre-allocated + * buffer. + * + * To achieve the mechanism described above, ffa_get_partitions_info uses the + * following functions: + * ffa_read_partitions_info + * ffa_query_partitions_info + * + * Return: + * + * @parts_size: When pointing to the number of partitions variable, the number is + * set by the driver. + * When pointing to the partitions information buffer size, the buffer will be + * filled by the driver. + * + * On success 0 is returned. Otherwise, failure + */ +static int ffa_get_partitions_info(const char *uuid_str, + u32 *parts_size, struct ffa_partition_info *buffer) +{ + /* + * fill_data: + * 0: return the SP count + * 1: fill SP data and return it to the caller + * -1: undefined mode + */ + int fill_data = -1; + u32 desc_idx, client_desc_idx; + struct ffa_partition_uuid part_uuid = {0}; + u32 client_desc_max_cnt; + u32 parts_found = 0; + + if (!ffa_priv_data->partitions.count || !ffa_priv_data->partitions.descs) { + ffa_err("no partition installed"); + return -EINVAL; + } + + if (!uuid_str) { + ffa_err("no UUID provided"); + return -EINVAL; + } + + if (!parts_size) { + ffa_err("no size/count provided"); + return -EINVAL; + } + + if (be_uuid_str_to_le_bin(uuid_str, (unsigned char *)&part_uuid)) { + ffa_err("invalid UUID"); + return -EINVAL; + } + + if (!buffer) { + /* Mode 1: getting the number of secure partitions */ + + fill_data = 0; + + ffa_info("Preparing for checking partitions count"); + + } else if ((*parts_size >= sizeof(struct ffa_partition_info)) && + !(*parts_size % sizeof(struct ffa_partition_info))) { + /* Mode 2: retrieving the partitions information */ + + fill_data = 1; + + client_desc_idx = 0; + + /* + * number of empty descriptors preallocated by the caller + */ + client_desc_max_cnt = *parts_size / sizeof(struct ffa_partition_info); + + ffa_info("Preparing for filling partitions info"); + + } else { + ffa_err("invalid function arguments provided"); + return -EINVAL; + } + + ffa_info("Searching partitions using the provided UUID"); + + /* + * search in the cached partitions + */ + for (desc_idx = 0; + desc_idx < ffa_priv_data->partitions.count; + desc_idx++) { + if (ffa_uuid_are_identical(&ffa_priv_data->partitions.descs[desc_idx].sp_uuid, + &part_uuid)) { + ffa_info("Partition ID %x matches the provided UUID", + ffa_priv_data->partitions.descs[desc_idx].info.id); + + parts_found++; + + if (fill_data) { + /* + * trying to fill the partition info in the input buffer + */ + + if (client_desc_idx < client_desc_max_cnt) { + buffer[client_desc_idx++] = + ffa_priv_data->partitions.descs[desc_idx].info; + continue; + } + + ffa_err("failed to fill the current descriptor client buffer full"); + return -ENOBUFS; + } + } + } + + if (!parts_found) { + int ret; + + ffa_info("No partition found. Querying framework ..."); + + ret = ffa_query_partitions_info(&part_uuid, &parts_found); + + if (ret == 0) { + if (!fill_data) { + *parts_size = parts_found; + + ffa_info("Number of partition(s) found matching the UUID: %d", + parts_found); + } else { + /* + * If SPs data detected, they are already in the private data + * structure, retry searching SP data again to return them + * to the caller + */ + if (parts_found) + ret = ffa_get_partitions_info(uuid_str, parts_size, buffer); + else + ret = -ENODATA; + } + } + + return ret; + } + + /* partition(s) found */ + if (!fill_data) + *parts_size = parts_found; + + return 0; +} + +/** + * ffa_cache_partitions_info - Queries and saves all secure partitions data + * + * This is a boot time function that invokes 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 the + * resident private data structure and we keep the UUID field empty + * (in FF-A 1.0 UUID is not provided by the partition descriptor) + * + * This function is 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(void) +{ + return ffa_query_partitions_info(NULL, NULL); +} + +/** + * ffa_msg_send_direct_req - FFA_MSG_SEND_DIRECT_{REQ,RESP} handler function + * @dst_part_id: destination partition ID + * @msg: pointer to the message data preallocated by the client (in/out) + * + * This is the runtime function that 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 + */ +static int __ffa_runtime ffa_msg_send_direct_req(u16 dst_part_id, struct ffa_send_direct_data *msg) +{ + ffa_value_t res = {0}; + int ffa_errno; + + if (!ffa_priv_data || !ffa_priv_data->invoke_ffa_fn) + return -EINVAL; + + /* No partition installed */ + if (!ffa_priv_data->partitions.count || !ffa_priv_data->partitions.descs) + return -ENODEV; + + ffa_priv_data->invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_64(FFA_MSG_SEND_DIRECT_REQ), + .a1 = PREP_SELF_ENDPOINT_ID(ffa_priv_data->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)) + ffa_priv_data->invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_32(FFA_RUN), + .a1 = res.a1, .a2 = 0, .a3 = 0, .a4 = 0, .a5 = 0, .a6 = 0, .a7 = 0, + }, &res); + + if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) { + /* Message sent with no response */ + return 0; + } + + if (res.a0 == FFA_SMC_64(FFA_MSG_SEND_DIRECT_RESP)) { + /* + * 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 = (int)res.a2; + return ffa_to_std_errno(ffa_errno); +} + +/** + * __arm_ffa_fn_smc - 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 + * + * Return: void + */ +void __ffa_runtime __arm_ffa_fn_smc(ffa_value_t args, ffa_value_t *res) +{ + arm_smccc_1_2_smc(&args, res); +} + +/** + * ffa_set_smc_conduit - Set the SMC conduit + * + * This boot time function selects the SMC conduit by setting the driver invoke function + * to SMC assembly function + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_set_smc_conduit(void) +{ + ffa_priv_data->invoke_ffa_fn = __arm_ffa_fn_smc; + + if (!ffa_priv_data->invoke_ffa_fn) { + ffa_err("failure to set the invoke function"); + return -EINVAL; + } + + ffa_info("Conduit is SMC"); + + return 0; +} + +/** + * ffa_set_bus_ops - Set the bus driver operations + * + * Setting the driver callbacks. + * + */ +static void ffa_set_bus_ops(void) +{ + ffa_priv_data->ffa_ops.partition_info_get = ffa_get_partitions_info; + ffa_priv_data->ffa_ops.sync_send_receive = ffa_msg_send_direct_req; + ffa_priv_data->ffa_ops.rxtx_unmap = ffa_unmap_rxtx_buffers; +} + +/** + * ffa_alloc_prvdata - allocate the driver main data structure and sets the device + * @dev: the arm_ffa device + * + * This boot time function creates the main data structure embedding all the driver data. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_alloc_prvdata(struct udevice *dev) +{ + if (!dev) { + ffa_err("no udevice found"); + return -ENODEV; + } + + /* The device is registered with the DM. Let's create the driver main data structure*/ + + ffa_priv_data = devm_kmalloc(dev, sizeof(struct ffa_prvdata), __GFP_ZERO); + if (!ffa_priv_data) { + ffa_err("can not allocate the driver main data structure"); + return -ENOMEM; + } + + ffa_priv_data->dev = dev; + + return 0; +} + +/** + * ffa_probe - The driver probe function + * @dev: the arm_ffa device + * + * Probing is done at boot time and triggered by the uclass device discovery. + * At probe level the following actions are done: + * - setting the conduit + * - querying the FF-A framework version + * - 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 the resident private data structure. + * + * The probe will fail if either FF-A framework is not detected or the + * FF-A requests are not behaving correctly. This ensures that the + * driver is not installed and its operations are not exported to the clients. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_probe(struct udevice *dev) +{ + int ret; + + ret = ffa_alloc_prvdata(dev); + if (ret != 0) + return ret; + + ffa_set_bus_ops(); + + ret = ffa_set_smc_conduit(); + if (ret != 0) + return ret; + + ret = ffa_get_version(); + if (ret != 0) + return ret; + + ret = ffa_get_endpoint_id(); + if (ret != 0) + return ret; + + ret = ffa_get_rxtx_map_features(); + if (ret != 0) + return ret; + + ret = ffa_map_rxtx_buffers(); + if (ret != 0) + return ret; + + ret = ffa_cache_partitions_info(); + if (ret != 0) { + ffa_free_rxtx_buffers(); + return ret; + } + + return 0; +} + +/** + * ffa_remove - The driver remove function + * @dev: the arm_ffa device + * When the device is about to be removed , unmap the RX/TX buffers and free the memory + * Return: + * + * 0 on success. + */ +static int ffa_remove(struct udevice *dev) +{ + ffa_info("removing the device"); + + ffa_unmap_rxtx_buffers(); + + if (ffa_priv_data->pair.rxbuf || ffa_priv_data->pair.txbuf) + ffa_free_rxtx_buffers(); + + return 0; +} + +/** + * ffa_unbind - The driver unbind function + * @dev: the arm_ffa device + * After the device is removed and memory freed the device is unbound + * Return: + * + * 0 on success. + */ +static int ffa_unbind(struct udevice *dev) +{ + ffa_info("unbinding the device , private data already released"); + + ffa_priv_data = NULL; + + return 0; +} + +/** + * ffa_bus_ops_get - bus driver operations getter + * + * Return: + * This runtime function returns a pointer to the driver operations structure + */ +const struct ffa_bus_ops * __ffa_runtime ffa_bus_ops_get(void) +{ + return &ffa_priv_data->ffa_ops; +} + +/** + * ffa_bus_prvdata_get - bus driver private data getter + * + * Return: + * This boot time function returns a pointer to the main private data structure + */ +struct ffa_prvdata **ffa_bus_prvdata_get(void) +{ + return &ffa_priv_data; +} + +/** + * ffa_bus_discover - discover FF-A bus and probe the arm_ffa device + * + * This boot time function makes sure the FF-A bus is discoverable. + * Then, the arm_ffa device is probed and ready to use. + * This function is called automatically at initcalls + * level (after u-boot relocation). + * + * When the bus was already discovered successfully the discovery will not run again. + * + * Arm FF-A transport is implemented through arm_ffa u-boot device managing the FF-A + * communication. + * All FF-A clients should use the arm_ffa device to use the FF-A transport. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int ffa_bus_discover(void) +{ + int ret = 0; + + if (!ffa_priv_data) + ret = ffa_device_get(); + + return ret; +} + +/** + * Declaring the arm_ffa driver under UCLASS_FFA + */ + +U_BOOT_DRIVER(arm_ffa) = { + .name = FFA_DRV_NAME, + .id = UCLASS_FFA, + .probe = ffa_probe, + .remove = ffa_remove, + .unbind = ffa_unbind, +}; diff --git a/drivers/firmware/arm-ffa/efi_ffa_runtime_data_mgr.c b/drivers/firmware/arm-ffa/efi_ffa_runtime_data_mgr.c new file mode 100644 index 0000000000..c76cf2147b --- /dev/null +++ b/drivers/firmware/arm-ffa/efi_ffa_runtime_data_mgr.c @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include "arm_ffa_prv.h" + +/** + * ffa_copy_runtime_data - copy the private data structure to the runtime area + * + * This boot time function copies the arm_ffa driver data structures including + * partitions data to the EFI runtime data section. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +efi_status_t ffa_copy_runtime_data(void) +{ + efi_status_t efi_ret; + efi_uintn_t prvdata_pages; + efi_uintn_t descs_pages; + struct ffa_prvdata **prvdata = NULL; /* Pointer to the current structure */ + struct ffa_prvdata *runtime_prvdata = NULL; /* Pointer to the structure runtime copy */ + u64 runtime_descs = 0; + + prvdata = ffa_bus_prvdata_get(); + + printf("INFO: EFI: FFA: prv data area at 0x%llx\n", (u64)prvdata); + + /* allocate private data runtime area */ + + prvdata_pages = efi_size_in_pages(sizeof(struct ffa_prvdata)); + efi_ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES, + EFI_RUNTIME_SERVICES_DATA, + prvdata_pages, + (u64 *)&runtime_prvdata); + + if (efi_ret != EFI_SUCCESS) { + printf("ERROR: EFI: FFA: allocating runtime data (err: 0x%lx, addr 0x%llx)\n", + efi_ret, (u64)runtime_prvdata); + + return efi_ret; + } + + printf("INFO: EFI: FFA: runtime data area at 0x%llx\n", (u64)runtime_prvdata); + + if (!runtime_prvdata) + return EFI_INVALID_PARAMETER; + + /* allocate the partition data runtime area */ + + descs_pages = efi_size_in_pages((*prvdata)->partitions.count * + sizeof(struct ffa_partition_desc)); + efi_ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES, + EFI_RUNTIME_SERVICES_DATA, + descs_pages, + &runtime_descs); + + if (efi_ret != EFI_SUCCESS) { + printf("ERROR: EFI: FFA: allocating runtime SPs data (err: 0x%lx, addr 0x%llx)\n", + efi_ret, runtime_descs); + + efi_free_pages((u64)runtime_prvdata, prvdata_pages); + + return efi_ret; + } + + printf("INFO: EFI: FFA: SPs runtime area at 0x%llx\n", (u64)runtime_descs); + + if (!runtime_descs) + return EFI_INVALID_PARAMETER; + + *runtime_prvdata = **prvdata; + + runtime_prvdata->dev = NULL; + runtime_prvdata->ffa_ops.partition_info_get = NULL; + runtime_prvdata->ffa_ops.rxtx_unmap = NULL; + runtime_prvdata->partitions.descs = (struct ffa_partition_desc *)runtime_descs; + runtime_prvdata->pair.rxbuf = 0; + runtime_prvdata->pair.txbuf = 0; + + /* + * Update the private data structure pointer in the driver + * no need to free the old structure. devm takes care of that + */ + *prvdata = runtime_prvdata; + + printf("INFO: EFI: FFA: runtime prv data now at 0x%llx , SPs count %d\n", + (u64)*prvdata, (*prvdata)->partitions.count); + + return 0; +} diff --git a/include/arm_ffa.h b/include/arm_ffa.h new file mode 100644 index 0000000000..f17b100497 --- /dev/null +++ b/include/arm_ffa.h @@ -0,0 +1,127 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __ARM_FFA_H +#define __ARM_FFA_H + +#include <linux/printk.h> + +/* + * This header is public. It can be used by clients to access + * data structures and definitions they need + */ + +/* + * Macros for displaying logs + */ + +#define ffa_info(fmt, ...) pr_info("[FFA] " fmt "\n", ##__VA_ARGS__) +#define ffa_err(fmt, ...) pr_err("[FFA] " fmt "\n", ##__VA_ARGS__) + +/* + * 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 __packed 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_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 __packed ffa_send_direct_data { + unsigned long data0; /* w3/x3 */ + unsigned long data1; /* w4/x4 */ + unsigned long data2; /* w5/x5 */ + unsigned long data3; /* w6/x6 */ + unsigned long data4; /* w7/x7 */ +}; + +#if CONFIG_IS_ENABLED(ARM_FFA_EFI_RUNTIME_MODE) + +#include <efi_loader.h> + +/* + * __ffa_runtime - controls whether functions are + * available after calling the EFI ExitBootServices service. + * Functions tagged with these keywords are resident (available at boot time and + * at runtime) + */ + +#define __ffa_runtime_data __efi_runtime_data +#define __ffa_runtime __efi_runtime + +#else + +/* + * The FF-A driver is independent from EFI + */ + +#define __ffa_runtime_data +#define __ffa_runtime + +#endif + +/** + * struct ffa_bus_ops - The driver operations structure + * @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)(const char *uuid_str, + u32 *parts_size, struct ffa_partition_info *buffer); + int (*sync_send_receive)(u16 dst_part_id, struct ffa_send_direct_data *msg); + int (*rxtx_unmap)(void); +}; + +/** + * The device driver and the Uclass driver public functions + */ + +/** + * ffa_bus_ops_get - driver operations getter + */ +const struct ffa_bus_ops * __ffa_runtime ffa_bus_ops_get(void); + +/** + * ffa_bus_discover - discover FF-A bus and probes the arm_ffa device + */ +int ffa_bus_discover(void); + +#if CONFIG_IS_ENABLED(ARM_FFA_EFI_RUNTIME_MODE) + +/** + * ffa_copy_runtime_data - copy the private data structure and the SPs data to the runtime area + */ +efi_status_t ffa_copy_runtime_data(void); + +#endif + +#endif diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index a432e43871..5dd698b7a9 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -4,6 +4,9 @@ * * (C) Copyright 2012 * Pavel Herrmann morpheus.ibis@gmail.com + * + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#ifndef _DM_UCLASS_ID_H @@ -55,6 +58,7 @@ enum uclass_id { UCLASS_EFI_MEDIA, /* Devices provided by UEFI firmware */ UCLASS_ETH, /* Ethernet device */ UCLASS_ETH_PHY, /* Ethernet PHY device */ + UCLASS_FFA, /* Arm Firmware Framework for Armv8-A */ UCLASS_FIRMWARE, /* Firmware */ UCLASS_FUZZING_ENGINE, /* Fuzzing engine */ UCLASS_FS_FIRMWARE_LOADER, /* Generic loader */ diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 6f7333638a..af0b0f3db1 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -3,6 +3,9 @@ * EFI application boot time services * * Copyright (c) 2016 Alexander Graf + * + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> @@ -23,6 +26,10 @@ #include <asm/setjmp.h> #include <linux/libfdt_env.h>
+#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT) +#include <arm_ffa.h> +#endif + DECLARE_GLOBAL_DATA_PTR;
/* Task priority level */ @@ -2178,6 +2185,14 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL); }
+#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT) + /* unmap FF-A RX/TX buffers */ + if (ffa_bus_ops_get()->rxtx_unmap()) + debug("[efi_boottime][ERROR]: can not unmap FF-A RX/TX buffers\n"); + else + debug("[efi_boottime][INFO]: FF-A RX/TX buffers unmapped\n"); +#endif + /* Patch out unsupported runtime function */ efi_runtime_detach();

Provide armffa command showcasing the use of the FF-A driver
The armffa command allows to query secure partitions data from the secure world and exchanging messages with the partitions.
Changelog: ===============
v4:
* remove pattern data in do_ffa_msg_send_direct_req
v3:
* use the new driver interfaces (partition_info_get, sync_send_receive) in armffa command
v2:
* replace use of ffa_helper_init_device function by ffa_helper_bus_discover
v1:
* introduce armffa command
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org --- MAINTAINERS | 1 + cmd/Kconfig | 10 ++ cmd/Makefile | 2 + cmd/armffa.c | 242 +++++++++++++++++++++++++++++++ drivers/firmware/arm-ffa/Kconfig | 1 + 5 files changed, 256 insertions(+) create mode 100644 cmd/armffa.c
diff --git a/MAINTAINERS b/MAINTAINERS index 02b84d5074..fd3b2c4263 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -251,6 +251,7 @@ F: configs/cortina_presidio-asic-pnand_defconfig ARM FF-A M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com S: Maintained +F: cmd/armffa.c F: doc/README.ffa.drv F: drivers/firmware/arm-ffa/ F: include/arm_ffa.h diff --git a/cmd/Kconfig b/cmd/Kconfig index 0e0be94f41..013d4c3da7 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -912,6 +912,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 Arm FF-A driver + 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 6e87522b62..979c6d59df 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -12,6 +12,8 @@ obj-y += panic.o obj-y += version.o
# command + +obj-$(CONFIG_CMD_ARMFFA) += armffa.o obj-$(CONFIG_CMD_ACPI) += acpi.o obj-$(CONFIG_CMD_ADDRMAP) += addrmap.o obj-$(CONFIG_CMD_AES) += aes.o diff --git a/cmd/armffa.c b/cmd/armffa.c new file mode 100644 index 0000000000..9b56e8a830 --- /dev/null +++ b/cmd/armffa.c @@ -0,0 +1,242 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <arm_ffa.h> +#include <asm/io.h> +#include <common.h> +#include <command.h> +#include <dm.h> +#include <mapmem.h> +#include <stdlib.h> + +/** + * do_ffa_get_singular_partition_info - implementation of the getpart subcommand + * @cmdtp: Command Table + * @flag: flags + * @argc: number of arguments + * @argv: arguments + * + * This function queries the secure partition information which the UUID is provided + * as an argument. The function uses the arm_ffa driver partition_info_get operation + * 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_get_singular_partition_info(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + u32 count = 0, size = 0; + int ret; + struct ffa_partition_info *parts_info; + u32 info_idx; + + if (argc != 1) + return -EINVAL; + + /* Mode 1: getting the number of secure partitions */ + ret = ffa_bus_ops_get()->partition_info_get(argv[0], &count, NULL); + if (ret != 0) { + ffa_err("Failure in querying partitions count (error code: %d)", ret); + return ret; + } + + if (!count) { + ffa_info("No secure partition found"); + return ret; + } + + /* + * pre-allocate a buffer to be filled by the driver + * with ffa_partition_info structs + */ + + ffa_info("Pre-allocating %d partition(s) info structures", count); + + parts_info = calloc(count, sizeof(struct ffa_partition_info)); + if (!parts_info) + return -EINVAL; + + size = count * sizeof(struct ffa_partition_info); + + /* + * ask the driver to fill the buffer with the SPs info + */ + + ret = ffa_bus_ops_get()->partition_info_get(argv[0], &size, parts_info); + if (ret != 0) { + ffa_err("Failure in querying partition(s) info (error code: %d)", ret); + free(parts_info); + return ret; + } + + /* + * SPs found , show the partition information + */ + for (info_idx = 0; info_idx < count ; info_idx++) { + ffa_info("Partition: id = 0x%x , exec_ctxt 0x%x , properties 0x%x", + parts_info[info_idx].id, + parts_info[info_idx].exec_ctxt, + parts_info[info_idx].properties); + } + + free(parts_info); + + return 0; +} + +/** + * do_ffa_msg_send_direct_req - implementation of the ping subcommand + * @cmdtp: Command Table + * @flag: flags + * @argc: number of arguments + * @argv: arguments + * + * This function sends data to the secure partition which the ID is provided + * as an argument. The function uses the arm_ffa driver sync_send_receive operation + * to send data. + * + * Return: + * + * CMD_RET_SUCCESS: on success, otherwise failure + */ +int do_ffa_msg_send_direct_req(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; + + if (argc != 1) + return -EINVAL; + + errno = 0; + part_id = strtoul(argv[0], NULL, 16); + + if (errno) { + ffa_err("Invalid partition ID"); + return -EINVAL; + } + + ret = ffa_bus_ops_get()->sync_send_receive(part_id, &msg); + if (ret == 0) { + u8 cnt; + + ffa_info("SP response:\n[LSB]"); + for (cnt = 0; + cnt < sizeof(struct ffa_send_direct_data) / sizeof(u64); + cnt++) + ffa_info("0x%llx", ((u64 *)&msg)[cnt]); + } else { + ffa_err("Sending direct request error (%d)", ret); + } + + return ret; +} + +/** + *do_ffa_dev_list - implementation of the devlist subcommand + * @cmdtp: [in] Command Table + * @flag: flags + * @argc: number of arguments + * @argv: arguments + * + * This function queries the devices belonging to the UCLASS_FFA + * class. Currently, one device is expected to show up: the arm_ffa device + * + * Return: + * + * CMD_RET_SUCCESS: on success, otherwise failure + */ +int do_ffa_dev_list(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct udevice *dev = NULL; + int i, ret; + + ffa_info("arm_ffa uclass entries:"); + + for (i = 0, ret = uclass_first_device(UCLASS_FFA, &dev); + dev; + ret = uclass_next_device(&dev), i++) { + if (ret) + break; + + ffa_info("entry %d - instance %08x, ops %08x, plat %08x", + i, + (u32)map_to_sysmem(dev), + (u32)map_to_sysmem(dev->driver->ops), + (u32)map_to_sysmem(dev_get_plat(dev))); + } + + return cmd_process_error(cmdtp, ret); +} + +static struct cmd_tbl armffa_commands[] = { + U_BOOT_CMD_MKENT(getpart, 1, 1, do_ffa_get_singular_partition_info, "", ""), + U_BOOT_CMD_MKENT(ping, 1, 1, do_ffa_msg_send_direct_req, "", ""), + U_BOOT_CMD_MKENT(devlist, 0, 1, do_ffa_dev_list, "", ""), +}; + +/** + * do_armffa - the armffa command main function + * @cmdtp: Command Table + * @flag: flags + * @argc: number of arguments + * @argv: arguments + * + * This function identifies which armffa subcommand to run. + * Then, it makes sure the arm_ffa device is probed and + * ready for use. + * Then, it runs the subcommand. + * + * Return: + * + * CMD_RET_SUCCESS: on success, otherwise failure + */ +static int do_armffa(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct cmd_tbl *armffa_cmd; + int ret; + + if (argc < 2) + return CMD_RET_USAGE; + + armffa_cmd = find_cmd_tbl(argv[1], armffa_commands, ARRAY_SIZE(armffa_commands)); + + argc -= 2; + argv += 2; + + if (!armffa_cmd || argc > armffa_cmd->maxargs) + return CMD_RET_USAGE; + + ret = ffa_bus_discover(); + if (ret != 0) + return cmd_process_error(cmdtp, ret); + + if (!ffa_bus_ops_get()) + return -EINVAL; + + ret = armffa_cmd->cmd(armffa_cmd, flag, argc, argv); + + return cmd_process_error(armffa_cmd, ret); +} + +U_BOOT_CMD(armffa, 4, 1, do_armffa, + "Arm FF-A operations test command", + "getpart <partition UUID>\n" + " - lists the partition(s) info\n" + "ping <partition ID>\n" + " - sends a data pattern to the specified partition\n" + "devlist\n" + " - displays the arm_ffa device info\n"); diff --git a/drivers/firmware/arm-ffa/Kconfig b/drivers/firmware/arm-ffa/Kconfig index aceb61cf49..40b467b0a5 100644 --- a/drivers/firmware/arm-ffa/Kconfig +++ b/drivers/firmware/arm-ffa/Kconfig @@ -4,6 +4,7 @@ config ARM_FFA_TRANSPORT bool "Enable Arm Firmware Framework for Armv8-A driver" depends on DM && ARM64 select ARM_SMCCC + select CMD_ARMFFA select LIB_UUID select DEVRES help

Provide a Sandbox driver to emulate the FF-A ABIs
The emulated ABIs are those supported by the FF-A core driver and according to FF-A specification v1.0.
The Sandbox driver provides operations allowing the test application to read the status of all the inspected ABIs and perform functional tests based on that.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog: ===============
v4: align sandbox driver with the new FF-A driver interfaces and new way of error handling
v1: introduce the sandbox driver --- MAINTAINERS | 1 + common/board_r.c | 2 +- configs/sandbox64_defconfig | 2 + configs/sandbox_defconfig | 2 + doc/arch/sandbox.rst | 1 + drivers/firmware/arm-ffa/Kconfig | 10 +- drivers/firmware/arm-ffa/Makefile | 1 + drivers/firmware/arm-ffa/arm_ffa_prv.h | 15 +- drivers/firmware/arm-ffa/core.c | 24 +- drivers/firmware/arm-ffa/sandbox.c | 659 ++++++++++++++++++ .../firmware/arm-ffa/sandbox_arm_ffa_prv.h | 144 ++++ include/arm_ffa.h | 2 +- include/sandbox_arm_ffa.h | 91 +++ lib/efi_loader/efi_boottime.c | 2 +- 14 files changed, 941 insertions(+), 15 deletions(-) create mode 100644 drivers/firmware/arm-ffa/sandbox.c create mode 100644 drivers/firmware/arm-ffa/sandbox_arm_ffa_prv.h create mode 100644 include/sandbox_arm_ffa.h
diff --git a/MAINTAINERS b/MAINTAINERS index fd3b2c4263..6f01002e34 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -255,6 +255,7 @@ F: cmd/armffa.c F: doc/README.ffa.drv F: drivers/firmware/arm-ffa/ F: include/arm_ffa.h +F: include/sandbox_arm_ffa.h
ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de diff --git a/common/board_r.c b/common/board_r.c index 8c99faddfd..7f1eae65df 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -776,7 +776,7 @@ static init_fnc_t init_sequence_r[] = { INIT_FUNC_WATCHDOG_RESET initr_net, #endif -#ifdef CONFIG_ARM_FFA_TRANSPORT +#if defined(CONFIG_ARM_FFA_TRANSPORT) && !defined(CONFIG_SANDBOX_FFA) ffa_bus_discover, #endif #ifdef CONFIG_POST diff --git a/configs/sandbox64_defconfig b/configs/sandbox64_defconfig index 290d1506c2..36e6448968 100644 --- a/configs/sandbox64_defconfig +++ b/configs/sandbox64_defconfig @@ -248,3 +248,5 @@ CONFIG_TEST_FDTDEC=y CONFIG_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y +CONFIG_ARM_FFA_TRANSPORT=y +CONFIG_SANDBOX_FFA=y \ No newline at end of file diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index ab5d3f19bf..8bf3848788 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -328,3 +328,5 @@ CONFIG_TEST_FDTDEC=y CONFIG_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y +CONFIG_ARM_FFA_TRANSPORT=y +CONFIG_SANDBOX_FFA=y \ No newline at end of file diff --git a/doc/arch/sandbox.rst b/doc/arch/sandbox.rst index 068d4a3be4..5d7e1b2c48 100644 --- a/doc/arch/sandbox.rst +++ b/doc/arch/sandbox.rst @@ -203,6 +203,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 40b467b0a5..263481de96 100644 --- a/drivers/firmware/arm-ffa/Kconfig +++ b/drivers/firmware/arm-ffa/Kconfig @@ -2,8 +2,8 @@
config ARM_FFA_TRANSPORT bool "Enable Arm Firmware Framework for Armv8-A driver" - depends on DM && ARM64 - select ARM_SMCCC + depends on DM && (ARM64 || SANDBOX) + select ARM_SMCCC if !SANDBOX select CMD_ARMFFA select LIB_UUID select DEVRES @@ -38,3 +38,9 @@ config ARM_FFA_EFI_RUNTIME_MODE The driver Code needed at runtime is placed at EFI runtime code section. Turning this on makes ffa_copy_runtime_data available for use and the driver code placed at EFI runtime code section. + +config SANDBOX_FFA + bool "FF-A Sandbox driver" + depends on ARM_FFA_TRANSPORT && SANDBOX + help + This emulates the FF-A handling under Sandbox and allows to test the FF-A driver diff --git a/drivers/firmware/arm-ffa/Makefile b/drivers/firmware/arm-ffa/Makefile index 0b9b0a61b4..d50060b836 100644 --- a/drivers/firmware/arm-ffa/Makefile +++ b/drivers/firmware/arm-ffa/Makefile @@ -5,3 +5,4 @@
obj-y += arm-ffa-uclass.o core.o obj-$(CONFIG_ARM_FFA_EFI_RUNTIME_MODE) += efi_ffa_runtime_data_mgr.o +obj-$(CONFIG_SANDBOX_FFA) += sandbox.o diff --git a/drivers/firmware/arm-ffa/arm_ffa_prv.h b/drivers/firmware/arm-ffa/arm_ffa_prv.h index 7bc90f7f66..3e0d4c112c 100644 --- a/drivers/firmware/arm-ffa/arm_ffa_prv.h +++ b/drivers/firmware/arm-ffa/arm_ffa_prv.h @@ -19,6 +19,16 @@ /* FF-A core driver name */ #define FFA_DRV_NAME "arm_ffa"
+/* The FF-A SMC function definitions */ + +#if CONFIG_IS_ENABLED(SANDBOX_FFA) +#include "sandbox_arm_ffa.h" +#else +typedef struct arm_smccc_1_2_regs ffa_value_t; +#endif + +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) @@ -94,11 +104,6 @@ struct ffa_abi_errmap { #define FFA_ERRMAP_COUNT (FFA_LAST_ID - FFA_FIRST_ID + 1) #define FFA_ID_TO_ERRMAP_ID(ffa_id) ((ffa_id) - FFA_FIRST_ID)
-/* The FF-A SMC function definitions */ - -typedef struct arm_smccc_1_2_regs ffa_value_t; -typedef void (*invoke_ffa_fn_t)(ffa_value_t args, ffa_value_t *res); - /* * struct ffa_partition_uuid - 16 bytes UUID transmitted by FFA_PARTITION_INFO_GET * @a1-4: 32-bit words access to the UUID data diff --git a/drivers/firmware/arm-ffa/core.c b/drivers/firmware/arm-ffa/core.c index 41c7b96e68..caba10caae 100644 --- a/drivers/firmware/arm-ffa/core.c +++ b/drivers/firmware/arm-ffa/core.c @@ -1101,6 +1101,7 @@ static int __ffa_runtime ffa_msg_send_direct_req(u16 dst_part_id, struct ffa_sen return ffa_to_std_errno(ffa_errno); }
+#if !CONFIG_IS_ENABLED(SANDBOX_FFA) /** * __arm_ffa_fn_smc - SMC wrapper * @args: FF-A ABI arguments to be copied to Xn registers @@ -1114,6 +1115,7 @@ void __ffa_runtime __arm_ffa_fn_smc(ffa_value_t args, ffa_value_t *res) { arm_smccc_1_2_smc(&args, res); } +#endif
/** * ffa_set_smc_conduit - Set the SMC conduit @@ -1127,7 +1129,12 @@ void __ffa_runtime __arm_ffa_fn_smc(ffa_value_t args, ffa_value_t *res) */ static int ffa_set_smc_conduit(void) { - ffa_priv_data->invoke_ffa_fn = __arm_ffa_fn_smc; +#if CONFIG_IS_ENABLED(SANDBOX_FFA) + ffa_priv_data->invoke_ffa_fn = sandbox_arm_ffa_smccc_smc; + ffa_info("Using SMC emulation"); +#else + ffa_priv_data->invoke_ffa_fn = __arm_ffa_fn_smc; +#endif
if (!ffa_priv_data->invoke_ffa_fn) { ffa_err("failure to set the invoke function"); @@ -1304,17 +1311,18 @@ struct ffa_prvdata **ffa_bus_prvdata_get(void) }
/** - * ffa_bus_discover - discover FF-A bus and probe the arm_ffa device + * ffa_bus_discover - discover FF-A bus and probe arm_ffa and sandbox_arm_ffa devices * * This boot time function makes sure the FF-A bus is discoverable. - * Then, the arm_ffa device is probed and ready to use. + * Then, the arm_ffa and sandbox_arm_ffa devices are ready to use. + * * This function is called automatically at initcalls * level (after u-boot relocation). * * When the bus was already discovered successfully the discovery will not run again. * * Arm FF-A transport is implemented through arm_ffa u-boot device managing the FF-A - * communication. + * communication. In Sandbox mode sandbox_arm_ffa is used to test arm_ffa driver. * All FF-A clients should use the arm_ffa device to use the FF-A transport. * * Return: @@ -1325,9 +1333,15 @@ int ffa_bus_discover(void) { int ret = 0;
- if (!ffa_priv_data) + if (!ffa_priv_data) { ret = ffa_device_get();
+#if CONFIG_IS_ENABLED(SANDBOX_FFA) + if (ret == 0) + ret = sandbox_ffa_device_get(); +#endif + } + return ret; }
diff --git a/drivers/firmware/arm-ffa/sandbox.c b/drivers/firmware/arm-ffa/sandbox.c new file mode 100644 index 0000000000..16e1fdc809 --- /dev/null +++ b/drivers/firmware/arm-ffa/sandbox.c @@ -0,0 +1,659 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include "sandbox_arm_ffa_prv.h" +#include <asm/global_data.h> +#include <common.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> +#include <mapmem.h> +#include <string.h> + +DECLARE_GLOBAL_DATA_PTR; + +/** + * The device private data structure containing all the emulated secure world data + */ +static struct sandbox_ffa_prvdata sandbox_ffa_priv_data = {0}; + +/* 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_SP2_ID, .exec_ctxt = 0x9587, .properties = 0x45325621 }, + .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_SP3_ID, .exec_ctxt = 0x7687, .properties = 0x23325621 }, + .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, + } + } + +}; + +/* + * Driver functions + */ + +/** + * sandbox_ffa_get_device - probes the sandbox_arm_ffa device + * + * This function makes sure the sandbox_arm_ffa device is probed + * This function makes sure the sandbox_arm_ffa device is + * created, bound to this driver, probed and ready to use. + * + * sandbox_arm_ffa depends on arm_ffa device. This dependency is + * handled by ffa_bus_discover function. arm_ffa is probed first then + * sandbox_arm_ffa. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int sandbox_ffa_device_get(void) +{ + int ret; + + if (sandbox_ffa_priv_data.dev) + return 0; + + ret = device_bind(dm_root(), + DM_DRIVER_GET(sandbox_arm_ffa), + FFA_SANDBOX_DRV_NAME, + NULL, + ofnode_null(), + &sandbox_ffa_priv_data.dev); + if (ret) { + sandbox_ffa_priv_data.dev = NULL; + return ret; + } + + ret = device_probe(sandbox_ffa_priv_data.dev); + if (ret) { + ffa_err("[Sandbox] can not probe the device"); + device_unbind(sandbox_ffa_priv_data.dev); + sandbox_ffa_priv_data.dev = NULL; + return ret; + } + + return 0; +} + +/** + * sandbox_ffa_version - Emulated FFA_VERSION handler function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_VERSION FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_version) +{ + sandbox_ffa_priv_data.fwk_version = FFA_VERSION_1_0; + res->a0 = sandbox_ffa_priv_data.fwk_version; + + /* x1-x7 MBZ */ + memset(FFA_X1X7_MBZ_REG_START, 0, FFA_X1X7_MBZ_CNT * sizeof(unsigned long)); + + return 0; +} + +/** + * sandbox_ffa_id_get - Emulated FFA_ID_GET handler function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_ID_GET FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_id_get) +{ + res->a0 = FFA_SMC_32(FFA_SUCCESS); + res->a1 = 0; + + sandbox_ffa_priv_data.id = NS_PHYS_ENDPOINT_ID; + res->a2 = sandbox_ffa_priv_data.id; + + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, 0, FFA_X3X7_MBZ_CNT * sizeof(unsigned long)); + + return 0; +} + +/** + * sandbox_ffa_features - Emulated FFA_FEATURES handler function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_FEATURES FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_features) +{ + 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(unsigned long)); + } else { + res->a0 = FFA_SMC_32(FFA_ERROR); + res->a2 = FFA_ERR_STAT_NOT_SUPPORTED; + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, + 0, FFA_X3X7_MBZ_CNT * sizeof(unsigned long)); + ffa_err("[Sandbox] FF-A interface 0x%lx not implemented", pargs->a1); + } + + res->a1 = 0; + + return 0; +} + +/** + * sandbox_ffa_partition_info_get - Emulated FFA_PARTITION_INFO_GET handler function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_PARTITION_INFO_GET FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_partition_info_get) +{ + struct ffa_partition_info *rxbuf_desc_info = NULL; + u32 descs_cnt; + u32 descs_size_bytes; + + res->a0 = FFA_SMC_32(FFA_ERROR); + + if (!sandbox_ffa_priv_data.pair.rxbuf) { + res->a2 = FFA_ERR_STAT_DENIED; + goto cleanup; + } + + if (sandbox_ffa_priv_data.pair_info.rxbuf_owned) { + res->a2 = FFA_ERR_STAT_BUSY; + goto cleanup; + } + + if (!sandbox_ffa_priv_data.partitions.descs) { + sandbox_ffa_priv_data.partitions.descs = sandbox_partitions; + sandbox_ffa_priv_data.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 descriptors buffer size */ + if ((sandbox_ffa_priv_data.pair_info.rxtx_buf_size * SZ_4K) < descs_size_bytes) { + res->a2 = FFA_ERR_STAT_NO_MEMORY; + goto cleanup; + } + + rxbuf_desc_info = (struct ffa_partition_info *)sandbox_ffa_priv_data.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++) = + sandbox_ffa_priv_data.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 */ + sandbox_ffa_priv_data.pair_info.rxbuf_owned = 1; + + goto cleanup; + } + + /* + * A UUID is specified. Return the information of all partitions matching + * the UUID + */ + + for (descs_cnt = 0 ; descs_cnt < SANDBOX_PARTITIONS_CNT ; descs_cnt++) + if (pargs->a1 == sandbox_ffa_priv_data.partitions.descs[descs_cnt].sp_uuid.a1 && + pargs->a2 == sandbox_ffa_priv_data.partitions.descs[descs_cnt].sp_uuid.a2 && + pargs->a3 == sandbox_ffa_priv_data.partitions.descs[descs_cnt].sp_uuid.a3 && + pargs->a4 == sandbox_ffa_priv_data.partitions.descs[descs_cnt].sp_uuid.a4) { + *(rxbuf_desc_info++) = + sandbox_ffa_priv_data.partitions.descs[descs_cnt].info; + } + + if (rxbuf_desc_info != ((struct ffa_partition_info *)sandbox_ffa_priv_data.pair.rxbuf)) { + res->a0 = FFA_SMC_32(FFA_SUCCESS); + /* store the partitions count */ + res->a2 = (unsigned long) + (rxbuf_desc_info - (struct ffa_partition_info *) + sandbox_ffa_priv_data.pair.rxbuf); + + /* transfer ownership to the consumer: the non secure world */ + sandbox_ffa_priv_data.pair_info.rxbuf_owned = 1; + } else { + /* Unrecognized UUID */ + res->a2 = FFA_ERR_STAT_INVALID_PARAMETERS; + } + +cleanup: + + ffa_err("[Sandbox] FFA_PARTITION_INFO_GET (%ld)", res->a2); + + res->a1 = 0; + + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, 0, FFA_X3X7_MBZ_CNT * sizeof(unsigned long)); + + return 0; +} + +/** + * sandbox_ffa_rxtx_map - Emulated FFA_RXTX_MAP handler function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_RXTX_MAP FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_rxtx_map) +{ + res->a0 = FFA_SMC_32(FFA_ERROR); + + if (sandbox_ffa_priv_data.pair.txbuf && sandbox_ffa_priv_data.pair.rxbuf) { + res->a2 = FFA_ERR_STAT_DENIED; + goto feedback; + } + + if (pargs->a3 >= RXTX_BUFFERS_MIN_PAGES && pargs->a1 && pargs->a2) { + sandbox_ffa_priv_data.pair.txbuf = pargs->a1; + sandbox_ffa_priv_data.pair.rxbuf = pargs->a2; + sandbox_ffa_priv_data.pair_info.rxtx_buf_size = pargs->a3; + sandbox_ffa_priv_data.pair_info.rxbuf_mapped = 1; + res->a0 = FFA_SMC_32(FFA_SUCCESS); + res->a2 = 0; + goto feedback; + } + + if (!pargs->a1 || !pargs->a2) + res->a2 = FFA_ERR_STAT_INVALID_PARAMETERS; + else + res->a2 = FFA_ERR_STAT_NO_MEMORY; + + ffa_err("[Sandbox] error in FFA_RXTX_MAP arguments (%d)", (int)res->a2); + +feedback: + + res->a1 = 0; + + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, + 0, FFA_X3X7_MBZ_CNT * sizeof(unsigned long)); + + return 0; +} + +/** + * sandbox_ffa_rxtx_unmap - Emulated FFA_RXTX_UNMAP handler function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_RXTX_UNMAP FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_rxtx_unmap) +{ + res->a0 = FFA_SMC_32(FFA_ERROR); + res->a2 = FFA_ERR_STAT_INVALID_PARAMETERS; + + if (GET_NS_PHYS_ENDPOINT_ID(pargs->a1) != sandbox_ffa_priv_data.id) + goto feedback; + + if (sandbox_ffa_priv_data.pair.txbuf && sandbox_ffa_priv_data.pair.rxbuf) { + sandbox_ffa_priv_data.pair.txbuf = 0; + sandbox_ffa_priv_data.pair.rxbuf = 0; + sandbox_ffa_priv_data.pair_info.rxtx_buf_size = 0; + sandbox_ffa_priv_data.pair_info.rxbuf_mapped = 0; + res->a0 = FFA_SMC_32(FFA_SUCCESS); + res->a2 = 0; + goto feedback; + } + + ffa_err("[Sandbox] No buffer pair registered on behalf of the caller"); + +feedback: + + res->a1 = 0; + + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, + 0, FFA_X3X7_MBZ_CNT * sizeof(unsigned long)); + + return 0; +} + +/** + * sandbox_ffa_rx_release - Emulated FFA_RX_RELEASE handler function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_RX_RELEASE FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_rx_release) +{ + if (!sandbox_ffa_priv_data.pair_info.rxbuf_owned) { + res->a0 = FFA_SMC_32(FFA_ERROR); + res->a2 = FFA_ERR_STAT_DENIED; + } else { + sandbox_ffa_priv_data.pair_info.rxbuf_owned = 0; + res->a0 = FFA_SMC_32(FFA_SUCCESS); + res->a2 = 0; + } + + res->a1 = 0; + + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, + 0, FFA_X3X7_MBZ_CNT * sizeof(unsigned long)); + + return 0; +} + +/** + * sandbox_ffa_sp_valid - Checks SP validity + * @part_id: partition ID to check + * + * This is the function searches the input ID in the descriptors table. + * + * Return: + * + * 1 on success (Partition found). Otherwise, failure + */ +static int sandbox_ffa_sp_valid(u16 part_id) +{ + u32 descs_cnt; + + for (descs_cnt = 0 ; descs_cnt < SANDBOX_PARTITIONS_CNT ; descs_cnt++) + if (sandbox_ffa_priv_data.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 function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_MSG_SEND_DIRECT_{REQ,RESP} + * FF-A functions. + * + * 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 + */ +SANDBOX_SMC_FFA_ABI(ffa_msg_send_direct_req) +{ + u16 part_id; + + part_id = GET_DST_SP_ID(pargs->a1); + + if ((GET_NS_PHYS_ENDPOINT_ID(pargs->a1) != sandbox_ffa_priv_data.id) || + !sandbox_ffa_sp_valid(part_id) || + pargs->a2) { + res->a0 = FFA_SMC_32(FFA_ERROR); + res->a1 = 0; + res->a2 = FFA_ERR_STAT_INVALID_PARAMETERS; + + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, + 0, FFA_X3X7_MBZ_CNT * sizeof(unsigned long)); + + return 0; + } + + res->a0 = FFA_SMC_64(FFA_MSG_SEND_DIRECT_RESP); + + res->a1 = PREP_SRC_SP_ID(part_id) | + PREP_NS_PHYS_ENDPOINT_ID(sandbox_ffa_priv_data.id); + + res->a2 = 0; + + /* + * return 0xff bytes as a response + */ + res->a3 = 0xffffffffffffffff; + res->a4 = 0xffffffffffffffff; + res->a5 = 0xffffffffffffffff; + res->a6 = 0xffffffffffffffff; + res->a7 = 0xffffffffffffffff; + + return 0; +} + +/** + * sandbox_ffa_get_prv_data - Returns the pointer to FF-A core pivate data + * @func_data: Pointer to the FF-A function arguments container structure + * + * This is the handler that returns the address of the FF-A core pivate data. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int sandbox_ffa_get_prv_data(struct ffa_sandbox_data *func_data) +{ + if (!func_data) + return -EINVAL; + + if (!func_data->data0 || func_data->data0_size != sizeof(struct ffa_prvdata **)) + return -EINVAL; + + if (!func_data->data1 || func_data->data1_size != sizeof(struct sandbox_ffa_prvdata **)) + return -EINVAL; + + *((struct ffa_prvdata **)func_data->data0) = *(ffa_bus_prvdata_get()); + *((struct sandbox_ffa_prvdata **)func_data->data1) = &sandbox_ffa_priv_data; + + return 0; +} + +/** + * sandbox_ffa_get_rxbuf_flags - Reading the mapping/ownership flags + * @queried_func_id: The FF-A function to be queried + * @func_data: Pointer to the FF-A function arguments container structure + * + * This is the handler that queries 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(u32 queried_func_id, struct ffa_sandbox_data *func_data) +{ + 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) = sandbox_ffa_priv_data.pair_info.rxbuf_mapped; + return 0; + case FFA_RX_RELEASE: + *((u8 *)func_data->data0) = sandbox_ffa_priv_data.pair_info.rxbuf_owned; + return 0; + default: + ffa_err("[Sandbox] The querried FF-A interface flag (%d) undefined", + queried_func_id); + return -EINVAL; + } +} + +/** + * sandbox_ffa_query_core_state - The driver dispatcher function + * @queried_func_id: The FF-A function to be queried + * @func_data: Pointer to the FF-A function arguments container structure + * + * Queries the status of FF-A ABI specified in the input argument. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int sandbox_ffa_query_core_state(u32 queried_func_id, struct ffa_sandbox_data *func_data) +{ + switch (queried_func_id) { + case FFA_VERSION: + case FFA_ID_GET: + case FFA_FEATURES: + return sandbox_ffa_get_prv_data(func_data); + case FFA_RXTX_MAP: + case FFA_RXTX_UNMAP: + case FFA_RX_RELEASE: + return sandbox_ffa_get_rxbuf_flags(queried_func_id, func_data); + default: + ffa_err("[Sandbox] The querried FF-A interface (%d) undefined", 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 + * + * Sandbox driver emulates the FF-A ABIs SMC call using this function. + * 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; + + switch (args.a0) { + case FFA_SMC_32(FFA_VERSION): + ret = sandbox_ffa_version(&args, res); + break; + case FFA_SMC_32(FFA_PARTITION_INFO_GET): + ret = sandbox_ffa_partition_info_get(&args, res); + break; + case FFA_SMC_32(FFA_RXTX_UNMAP): + ret = sandbox_ffa_rxtx_unmap(&args, res); + break; + case FFA_SMC_64(FFA_MSG_SEND_DIRECT_REQ): + ret = sandbox_ffa_msg_send_direct_req(&args, res); + break; + case FFA_SMC_32(FFA_ID_GET): + ret = sandbox_ffa_id_get(&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(&args, res); + break; + case FFA_SMC_32(FFA_RX_RELEASE): + ret = sandbox_ffa_rx_release(&args, res); + break; + default: + ffa_err("[Sandbox] Undefined FF-A interface (0x%lx)", args.a0); + } + + if (ret != 0) + ffa_err("[Sandbox] FF-A ABI internal failure (%d)", ret); +} + +/** + * sandbox_ffa_probe - The driver probe function + * @dev: the sandbox_arm_ffa device + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int sandbox_ffa_probe(struct udevice *dev) +{ + return 0; +} + +/** + * sandbox_ffa_remove - The driver remove function + * @dev: the sandbox_arm_ffa device + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int sandbox_ffa_remove(struct udevice *dev) +{ + ffa_info("[Sandbox] removing the device"); + memset(&sandbox_ffa_priv_data, 0, sizeof(sandbox_ffa_priv_data)); + return 0; +} + +/** + * Declaring the sandbox_arm_ffa driver under UCLASS_FFA + */ +U_BOOT_DRIVER(sandbox_arm_ffa) = { + .name = FFA_SANDBOX_DRV_NAME, + .id = UCLASS_FFA, + .probe = sandbox_ffa_probe, + .remove = sandbox_ffa_remove, +}; diff --git a/drivers/firmware/arm-ffa/sandbox_arm_ffa_prv.h b/drivers/firmware/arm-ffa/sandbox_arm_ffa_prv.h new file mode 100644 index 0000000000..4db57f5092 --- /dev/null +++ b/drivers/firmware/arm-ffa/sandbox_arm_ffa_prv.h @@ -0,0 +1,144 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __SANDBOX_ARM_FFA_PRV_H +#define __SANDBOX_ARM_FFA_PRV_H + +#include "arm_ffa_prv.h" +#include <sandbox_arm_ffa.h> + +/* + * This header is private. It is exclusively used by the Sandbox FF-A driver + */ + +/* FF-A core driver name */ +#define FFA_SANDBOX_DRV_NAME "sandbox_arm_ffa" + +/* FF-A ABIs internal error codes (as defined by the spec) */ + +#define FFA_ERR_STAT_NOT_SUPPORTED -1 +#define FFA_ERR_STAT_INVALID_PARAMETERS -2 +#define FFA_ERR_STAT_NO_MEMORY -3 +#define FFA_ERR_STAT_BUSY -4 +#define FFA_ERR_STAT_DENIED -6 + +/* Providing Arm SMCCC declarations to sandbox */ + +#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)) + +/* 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) + +/* secure partitions count */ +#define SANDBOX_PARTITIONS_CNT (4) + +/* 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: the consumer) + * @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 as set by the FF-A core driver + * + * Data structure hosting 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_prvdata - the driver private data structure + * + * @dev: The arm_ffa device under u-boot driver model + * @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 + * @conduit: The selected conduit + * + * The driver data structure hosting all the emulated secure world data. + */ +struct sandbox_ffa_prvdata { + struct udevice *dev; + u32 fwk_version; + u16 id; + struct ffa_partitions partitions; + struct ffa_rxtxpair pair; + struct ffa_rxtxpair_info pair_info; +}; + +#define SANDBOX_SMC_FFA_ABI(ffabi) static int sandbox_##ffabi(ffa_value_t *pargs, ffa_value_t *res) + +#endif diff --git a/include/arm_ffa.h b/include/arm_ffa.h index f17b100497..665413a0c5 100644 --- a/include/arm_ffa.h +++ b/include/arm_ffa.h @@ -111,7 +111,7 @@ struct ffa_bus_ops { const struct ffa_bus_ops * __ffa_runtime ffa_bus_ops_get(void);
/** - * ffa_bus_discover - discover FF-A bus and probes the arm_ffa device + * ffa_bus_discover - discover FF-A bus and probes the arm_ffa and sandbox_arm_ffa devices */ int ffa_bus_discover(void);
diff --git a/include/sandbox_arm_ffa.h b/include/sandbox_arm_ffa.h new file mode 100644 index 0000000000..d5df16f282 --- /dev/null +++ b/include/sandbox_arm_ffa.h @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __SANDBOX_ARM_FFA_H +#define __SANDBOX_ARM_FFA_H + +#include <arm_ffa.h> + +/** + * struct sandbox_smccc_1_2_regs - Arguments for or Results from emulated SMC call + * @a0-a17 argument values from registers 0 to 17 + */ +struct sandbox_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; +}; + +typedef struct sandbox_smccc_1_2_regs ffa_value_t; + +/* UUIDs of services supported by the sandbox driver */ +#define SANDBOX_SERVICE1_UUID "ed32d533-4209-99e6-2d72-cdd998a79cc0" +#define SANDBOX_SERVICE2_UUID "ed32d544-4209-99e6-2d72-cdd998a79cc0" +#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" + +#define SANDBOX_SP_COUNT_PER_VALID_SERVICE 2 + +/** + * struct ffa_sandbox_data - generic data structure used to exchange + * data between test cases and the sandbox driver + * @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 + * + * Using this structure sandbox test cases can pass various types of data with different sizes. + */ +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 driver public functions + */ + +/** + * sandbox_ffa_query_core_state - Queries the status of FF-A ABIs + */ +int sandbox_ffa_query_core_state(u32 queried_func_id, struct ffa_sandbox_data *func_data); + +/** + * sandbox_ffa_get_device - create, bind and probe the sandbox_arm_ffa device + */ +int sandbox_ffa_device_get(void); + +/** + * sandbox_arm_ffa_smccc_smc - FF-A SMC call emulation + */ +void sandbox_arm_ffa_smccc_smc(ffa_value_t args, ffa_value_t *res); + +#endif diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index af0b0f3db1..d404343a7d 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -2185,7 +2185,7 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL); }
-#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT) +#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT) && !CONFIG_IS_ENABLED(SANDBOX_FFA) /* unmap FF-A RX/TX buffers */ if (ffa_bus_ops_get()->rxtx_unmap()) debug("[efi_boottime][ERROR]: can not unmap FF-A RX/TX buffers\n");

Add functional test cases for the FF-A core driver
These tests rely on the FF-A Sandbox driver which helps in inspecting the FF-A core driver.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog: ===============
v4: align sandbox tests with the new FF-A driver interfaces and new way of error handling
v1: introduce sandbox tests --- MAINTAINERS | 1 + test/dm/Makefile | 2 + test/dm/ffa.c | 394 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 397 insertions(+) create mode 100644 test/dm/ffa.c
diff --git a/MAINTAINERS b/MAINTAINERS index 6f01002e34..8c0dfff5f8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -256,6 +256,7 @@ F: doc/README.ffa.drv F: drivers/firmware/arm-ffa/ F: include/arm_ffa.h F: include/sandbox_arm_ffa.h +F: test/dm/ffa.c
ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de diff --git a/test/dm/Makefile b/test/dm/Makefile index 7543df8823..e5a791768e 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0+ # # Copyright (c) 2013 Google, Inc +# (C) Copyright 2022 ARM Limited
obj-$(CONFIG_UT_DM) += test-dm.o
@@ -81,6 +82,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_SANDBOX_FFA) += 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 0000000000..052d5fc3f4 --- /dev/null +++ b/test/dm/ffa.c @@ -0,0 +1,394 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Functional tests for UCLASS_FFA class + * + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <console.h> +#include <dm.h> +#include <dm/test.h> +#include "../../drivers/firmware/arm-ffa/sandbox_arm_ffa_prv.h" +#include <sandbox_arm_ffa.h> +#include <test/test.h> +#include <test/ut.h> + +/* Macros */ + +#define LOG_MSG_SZ (100) +#define LOG_CMD_SZ (LOG_MSG_SZ * 2) + +/* Functional tests for the UCLASS_FFA */ + +static int dm_test_ffa_log(struct unit_test_state *uts, char *msg) +{ + char cmd[LOG_CMD_SZ] = {0}; + + console_record_reset(); + + snprintf(cmd, LOG_CMD_SZ, "echo "%s"", msg); + run_command(cmd, 0); + + ut_assert_console_end(); + + return CMD_RET_SUCCESS; +} + +static int check_fwk_version(struct ffa_prvdata *prvdata, struct sandbox_ffa_prvdata *sdx_prvdata, + struct unit_test_state *uts) +{ + if (prvdata->fwk_version != sdx_prvdata->fwk_version) { + char msg[LOG_MSG_SZ] = {0}; + + snprintf(msg, LOG_MSG_SZ, + "[%s]: Error: framework version: core = 0x%x , sandbox = 0x%x", __func__, + prvdata->fwk_version, + sdx_prvdata->fwk_version); + + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + return CMD_RET_SUCCESS; +} + +static int check_endpoint_id(struct ffa_prvdata *prvdata, struct unit_test_state *uts) +{ + if (prvdata->id) { + char msg[LOG_MSG_SZ] = {0}; + + snprintf(msg, LOG_MSG_SZ, + "[%s]: Error: endpoint id: core = 0x%x", __func__, prvdata->id); + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + return CMD_RET_SUCCESS; +} + +static int check_core_dev(struct ffa_prvdata *prvdata, struct unit_test_state *uts) +{ + if (!prvdata->dev) { + char msg[LOG_MSG_SZ] = {0}; + + snprintf(msg, LOG_MSG_SZ, "[%s]: Error: core device NULL", __func__); + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + return CMD_RET_SUCCESS; +} + +static int check_sandbox_dev(struct sandbox_ffa_prvdata *sdx_prvdata, struct unit_test_state *uts) +{ + if (!sdx_prvdata->dev) { + char msg[LOG_MSG_SZ] = {0}; + + snprintf(msg, LOG_MSG_SZ, "[%s]: Error: sandbox device NULL", __func__); + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + return CMD_RET_SUCCESS; +} + +static int check_rxtxbuf(struct ffa_prvdata *prvdata, struct unit_test_state *uts) +{ + if (!prvdata->pair.rxbuf && prvdata->pair.txbuf) { + char msg[LOG_MSG_SZ] = {0}; + + snprintf(msg, LOG_MSG_SZ, "[%s]: Error: rxbuf = 0x%llx txbuf = 0x%llx", __func__, + prvdata->pair.rxbuf, + prvdata->pair.txbuf); + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + return CMD_RET_SUCCESS; +} + +static int check_features(struct ffa_prvdata *prvdata, struct unit_test_state *uts) +{ + char msg[LOG_MSG_SZ] = {0}; + + if (prvdata->pair.rxtx_min_pages != RXTX_4K && + prvdata->pair.rxtx_min_pages != RXTX_16K && + prvdata->pair.rxtx_min_pages != RXTX_64K) { + snprintf(msg, + LOG_MSG_SZ, + "[%s]: Error: FFA_RXTX_MAP features = 0x%lx", + __func__, + prvdata->pair.rxtx_min_pages); + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + + return CMD_RET_SUCCESS; +} + +static int check_rxbuf_mapped_flag(u32 queried_func_id, + u8 rxbuf_mapped, + struct unit_test_state *uts) +{ + char msg[LOG_MSG_SZ] = {0}; + + switch (queried_func_id) { + case FFA_RXTX_MAP: + { + if (rxbuf_mapped) + return CMD_RET_SUCCESS; + break; + } + case FFA_RXTX_UNMAP: + { + if (!rxbuf_mapped) + return CMD_RET_SUCCESS; + break; + } + default: + return CMD_RET_FAILURE; + } + + snprintf(msg, LOG_MSG_SZ, "[%s]: Error: %s mapping issue", __func__, + (queried_func_id == FFA_RXTX_MAP ? "FFA_RXTX_MAP" : "FFA_RXTX_UNMAP")); + dm_test_ffa_log(uts, msg); + + return CMD_RET_FAILURE; +} + +static int check_rxbuf_release_flag(u8 rxbuf_owned, struct unit_test_state *uts) +{ + if (rxbuf_owned) { + char msg[LOG_MSG_SZ] = {0}; + + snprintf(msg, LOG_MSG_SZ, "[%s]: Error: RX buffer not released", __func__); + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + return CMD_RET_SUCCESS; +} + +static int test_ffa_msg_send_direct_req(u16 part_id, struct unit_test_state *uts) +{ + struct ffa_send_direct_data msg = {0}; + u8 cnt; + + ut_assertok(ffa_bus_ops_get()->sync_send_receive(part_id, &msg)); + + for (cnt = 0; cnt < sizeof(struct ffa_send_direct_data) / sizeof(u64); cnt++) + ut_assertok(((u64 *)&msg)[cnt] != 0xffffffffffffffff); + + return CMD_RET_SUCCESS; +} + +static int test_partitions_and_comms(const char *service_uuid, + struct sandbox_ffa_prvdata *sdx_prvdata, + struct unit_test_state *uts) +{ + u32 count = 0, size = 0; + struct ffa_partition_info *parts_info; + u32 info_idx, exp_info_idx; + int ret; + + /* + * get from the driver the count of the SPs matching the UUID + */ + ret = ffa_bus_ops_get()->partition_info_get(service_uuid, &count, NULL); + /* make sure partitions are detected */ + ut_assertok(ret != 0); + ut_assertok(count != SANDBOX_SP_COUNT_PER_VALID_SERVICE); + + /* + * pre-allocate a buffer to be filled by the driver + * with ffa_partition_info structs + */ + + parts_info = calloc(count, sizeof(struct ffa_partition_info)); + ut_assertok(!parts_info); + + size = count * sizeof(struct ffa_partition_info); + + /* + * ask the driver to fill the buffer with the SPs info + */ + ret = ffa_bus_ops_get()->partition_info_get(service_uuid, &size, parts_info); + if (ret != 0) { + free(parts_info); + ut_assertok(ret != 0); + } + + /* + * SPs found , verify the partitions information + */ + + ret = CMD_RET_FAILURE; + + for (info_idx = 0; info_idx < count ; info_idx++) { + for (exp_info_idx = 0; + exp_info_idx < sdx_prvdata->partitions.count; + exp_info_idx++) { + if (parts_info[info_idx].id == + sdx_prvdata->partitions.descs[exp_info_idx].info.id) { + ret = memcmp(&parts_info[info_idx], + &sdx_prvdata->partitions.descs[exp_info_idx] + .info, + sizeof(struct ffa_partition_info)); + if (ret) + free(parts_info); + ut_assertok(ret != 0); + /* send and receive data from the current partition */ + test_ffa_msg_send_direct_req(parts_info[info_idx].id, uts); + } + ret = CMD_RET_SUCCESS; + } + } + + free(parts_info); + + /* Verify expected partitions found in the emulated secure world*/ + ut_assertok(ret != CMD_RET_SUCCESS); + + return CMD_RET_SUCCESS; +} + +static int dm_test_ffa_ack(struct unit_test_state *uts) +{ + struct ffa_prvdata *prvdata = NULL; + struct sandbox_ffa_prvdata *sdx_prvdata = NULL; + struct ffa_sandbox_data func_data = {0}; + u8 rxbuf_flag = 0; + const char *svc1_uuid = SANDBOX_SERVICE1_UUID; + const char *svc2_uuid = SANDBOX_SERVICE2_UUID; + int ret; + + /* test probing FF-A devices */ + ut_assertok(ffa_bus_discover()); + + /* get a pointer to the FF-A core and sandbox drivers private data */ + func_data.data0 = &prvdata; + func_data.data0_size = sizeof(prvdata); + func_data.data1 = &sdx_prvdata; + func_data.data1_size = sizeof(sdx_prvdata); + + ut_assertok(sandbox_ffa_query_core_state(FFA_VERSION, &func_data)); + + /* make sure private data pointers are retrieved */ + ut_assertok(prvdata == 0); + ut_assertok(sdx_prvdata == 0); + + /* make sure dev devices created */ + ut_assertok(check_core_dev(prvdata, uts)); + ut_assertok(check_sandbox_dev(sdx_prvdata, uts)); + + /* test FFA_VERSION */ + ut_assertok(check_fwk_version(prvdata, sdx_prvdata, uts)); + + /* test FFA_ID_GET */ + ut_assertok(check_endpoint_id(prvdata, uts)); + + /* test FFA_FEATURES */ + ut_assertok(check_features(prvdata, uts)); + + /* test core RX/TX buffers */ + ut_assertok(check_rxtxbuf(prvdata, uts)); + + /* test FFA_RXTX_MAP */ + func_data.data0 = &rxbuf_flag; + func_data.data0_size = sizeof(rxbuf_flag); + + rxbuf_flag = 0; + ut_assertok(sandbox_ffa_query_core_state(FFA_RXTX_MAP, &func_data)); + ut_assertok(check_rxbuf_mapped_flag(FFA_RXTX_MAP, rxbuf_flag, uts)); + + /* FFA_PARTITION_INFO_GET / FFA_MSG_SEND_DIRECT_REQ */ + ret = test_partitions_and_comms(svc1_uuid, sdx_prvdata, uts); + ut_assertok(ret != CMD_RET_SUCCESS); + + /* test FFA_RX_RELEASE */ + rxbuf_flag = 1; + ut_assertok(sandbox_ffa_query_core_state(FFA_RX_RELEASE, &func_data)); + ut_assertok(check_rxbuf_release_flag(rxbuf_flag, uts)); + + /* FFA_PARTITION_INFO_GET / FFA_MSG_SEND_DIRECT_REQ */ + ret = test_partitions_and_comms(svc2_uuid, sdx_prvdata, uts); + ut_assertok(ret != CMD_RET_SUCCESS); + + /* test FFA_RX_RELEASE */ + rxbuf_flag = 1; + ut_assertok(sandbox_ffa_query_core_state(FFA_RX_RELEASE, &func_data)); + ut_assertok(check_rxbuf_release_flag(rxbuf_flag, uts)); + + /* test FFA_RXTX_UNMAP */ + ut_assertok(ffa_bus_ops_get()->rxtx_unmap()); + + rxbuf_flag = 1; + ut_assertok(sandbox_ffa_query_core_state(FFA_RXTX_UNMAP, &func_data)); + ut_assertok(check_rxbuf_mapped_flag(FFA_RXTX_UNMAP, rxbuf_flag, uts)); + + return CMD_RET_SUCCESS; +} + +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_prvdata *prvdata = NULL; + struct sandbox_ffa_prvdata *sdx_prvdata = NULL; + struct ffa_sandbox_data func_data = {0}; + 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 = {0}; + int ret; + u32 count = 0; + u16 part_id = 0; + + /* test probing FF-A devices */ + ut_assertok(ffa_bus_discover()); + + /* get a pointer to the FF-A core and sandbox drivers private data */ + func_data.data0 = &prvdata; + func_data.data0_size = sizeof(prvdata); + func_data.data1 = &sdx_prvdata; + func_data.data1_size = sizeof(sdx_prvdata); + + ut_assertok(sandbox_ffa_query_core_state(FFA_VERSION, &func_data)); + + /* make sure private data pointers are retrieved */ + ut_assertok(prvdata == 0); + ut_assertok(sdx_prvdata == 0); + + /* make sure dev devices created */ + ut_assertok(check_core_dev(prvdata, uts)); + ut_assertok(check_sandbox_dev(sdx_prvdata, uts)); + + /* query partitions count using invalid arguments */ + ret = ffa_bus_ops_get()->partition_info_get(unvalid_svc_uuid, NULL, NULL); + ut_assertok(ret != -EINVAL); + + /* query partitions count using an invalid UUID string */ + ret = ffa_bus_ops_get()->partition_info_get(unvalid_svc_uuid_str, &count, NULL); + ut_assertok(ret != -EINVAL); + + /* query partitions count using an invalid UUID (no matching SP) */ + count = 0; + ret = ffa_bus_ops_get()->partition_info_get(unvalid_svc_uuid, &count, NULL); + ut_assertok(count != 0); + + /* query partitions count using a valid UUID */ + count = 0; + ret = ffa_bus_ops_get()->partition_info_get(valid_svc_uuid, &count, NULL); + /* make sure partitions are detected */ + ut_assertok(ret != 0); + ut_assertok(count != SANDBOX_SP_COUNT_PER_VALID_SERVICE); + + /* send data to an invalid partition */ + ret = ffa_bus_ops_get()->sync_send_receive(part_id, &msg); + ut_assertok(ret != -EINVAL); + + /* send data to a valid partition */ + part_id = prvdata->partitions.descs[0].info.id; + ret = ffa_bus_ops_get()->sync_send_receive(part_id, &msg); + ut_assertok(ret != 0); + + return CMD_RET_SUCCESS; +} + +DM_TEST(dm_test_ffa_nack, UT_TESTF_SCAN_FDT | UT_TESTF_CONSOLE_REC);

Add Sandbox test for the armffa command
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog: ===============
v4: drop use of helper APIs
v1: introduce armffa command sandbox test --- MAINTAINERS | 1 + test/cmd/Makefile | 2 ++ test/cmd/armffa.c | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+) create mode 100644 test/cmd/armffa.c
diff --git a/MAINTAINERS b/MAINTAINERS index 8c0dfff5f8..8ed94da4c8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -256,6 +256,7 @@ F: doc/README.ffa.drv F: drivers/firmware/arm-ffa/ F: include/arm_ffa.h F: include/sandbox_arm_ffa.h +F: test/cmd/armffa.c F: test/dm/ffa.c
ARM FREESCALE IMX diff --git a/test/cmd/Makefile b/test/cmd/Makefile index c331757425..19e9d0a995 100644 --- a/test/cmd/Makefile +++ b/test/cmd/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0+ # # Copyright (c) 2013 Google, Inc +# (C) Copyright 2022 ARM Limited
ifdef CONFIG_HUSH_PARSER obj-$(CONFIG_CONSOLE_RECORD) += test_echo.o @@ -13,3 +14,4 @@ obj-$(CONFIG_CMD_MEM_SEARCH) += mem_search.o obj-$(CONFIG_CMD_PINMUX) += pinmux.o obj-$(CONFIG_CMD_PWM) += pwm.o obj-$(CONFIG_CMD_SETEXPR) += setexpr.o +obj-$(CONFIG_SANDBOX_FFA) += armffa.o diff --git a/test/cmd/armffa.c b/test/cmd/armffa.c new file mode 100644 index 0000000000..531f82066e --- /dev/null +++ b/test/cmd/armffa.c @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test for armffa command + * + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <dm.h> +#include <dm/test.h> +#include <sandbox_arm_ffa.h> +#include <string.h> +#include <test/test.h> +#include <test/ut.h> + +#define PING_CMD_SIZE 19 + +/* Basic test of 'armffa' command */ +static int dm_test_armffa_cmd(struct unit_test_state *uts) +{ + char ping_cmd[PING_CMD_SIZE] = {0}; + + ut_assertok(ffa_bus_discover()); + + /* armffa getpart <UUID> */ + ut_assertok(run_command("armffa getpart " SANDBOX_SERVICE1_UUID, 0)); + + snprintf(ping_cmd, PING_CMD_SIZE, "armffa ping 0x%x", SANDBOX_SP1_ID); + + /* armffa ping <ID> */ + ut_assertok(run_command(ping_cmd, 0)); + + /* armffa devlist */ + ut_assertok(run_command("armffa devlist", 0)); + + return CMD_RET_SUCCESS; +} + +DM_TEST(dm_test_armffa_cmd, UT_TESTF_SCAN_FDT | UT_TESTF_CONSOLE_REC);

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.
FF-A driver private data is copied to EFI runtime section at ExitBootServices(). This garantees secure world partitions data are available at EFI runtime level.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Signed-off-by: Gowtham Suresh Kumar gowtham.sureshkumar@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog: ===============
v4:
* use the new FF-A driver interfaces * discover MM partitions at runtime * copy FF-A driver private data to EFI runtime section at ExitBootServices() * drop use of FFA_ERR_STAT_SUCCESS error code * replace EFI_BUFFER_TOO_SMALL with EFI_OUT_OF_RESOURCES in ffa_mm_communicate(). No need for efi_memcpy_runtime() anymore * revert the error log in mm_communicate() in case of failure * remove packed attribute from efi_mm_communicate_header and smm_variable_communicate_header
v2:
* set default values to 0 for FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_ADDR and MM_SP_UUID_DATA and add warnings
v1:
* introduce FF-A MM communication --- arch/arm/cpu/armv8/cache.S | 19 +++ arch/arm/cpu/armv8/cache_v8.c | 6 +- include/mm_communication.h | 9 +- lib/efi_loader/Kconfig | 14 +- lib/efi_loader/efi_boottime.c | 7 + lib/efi_loader/efi_variable_tee.c | 263 +++++++++++++++++++++++++++++- 6 files changed, 309 insertions(+), 9 deletions(-)
diff --git a/arch/arm/cpu/armv8/cache.S b/arch/arm/cpu/armv8/cache.S index d1cee23437..91b7c73c17 100644 --- a/arch/arm/cpu/armv8/cache.S +++ b/arch/arm/cpu/armv8/cache.S @@ -3,6 +3,9 @@ * (C) Copyright 2013 * David Feng fenghua@phytium.com.cn * + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + * * This file is based on sample code from ARMv8 ARM. */
@@ -21,7 +24,11 @@ * x1: 0 clean & invalidate, 1 invalidate only * x2~x9: clobbered */ +#ifdef CONFIG_EFI_LOADER +.pushsection .text.efi_runtime, "ax" +#else .pushsection .text.__asm_dcache_level, "ax" +#endif ENTRY(__asm_dcache_level) lsl x12, x0, #1 msr csselr_el1, x12 /* select cache level */ @@ -65,7 +72,11 @@ ENDPROC(__asm_dcache_level) * * flush or invalidate all data cache by SET/WAY. */ +#ifdef CONFIG_EFI_LOADER +.pushsection .text.efi_runtime, "ax" +#else .pushsection .text.__asm_dcache_all, "ax" +#endif ENTRY(__asm_dcache_all) mov x1, x0 dsb sy @@ -109,7 +120,11 @@ ENTRY(__asm_flush_dcache_all) ENDPROC(__asm_flush_dcache_all) .popsection
+#ifdef CONFIG_EFI_LOADER +.pushsection .text.efi_runtime, "ax" +#else .pushsection .text.__asm_invalidate_dcache_all, "ax" +#endif ENTRY(__asm_invalidate_dcache_all) mov x0, #0x1 b __asm_dcache_all @@ -182,7 +197,11 @@ ENTRY(__asm_invalidate_icache_all) ENDPROC(__asm_invalidate_icache_all) .popsection
+#ifdef CONFIG_EFI_LOADER +.pushsection .text.efi_runtime, "ax" +#else .pushsection .text.__asm_invalidate_l3_dcache, "ax" +#endif WEAK(__asm_invalidate_l3_dcache) mov x0, #0 /* return status as success */ ret diff --git a/arch/arm/cpu/armv8/cache_v8.c b/arch/arm/cpu/armv8/cache_v8.c index e4736e5643..45f57372c2 100644 --- a/arch/arm/cpu/armv8/cache_v8.c +++ b/arch/arm/cpu/armv8/cache_v8.c @@ -5,10 +5,14 @@ * * (C) Copyright 2016 * Alexander Graf agraf@suse.de + * + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> #include <cpu_func.h> +#include <efi_loader.h> #include <hang.h> #include <log.h> #include <asm/cache.h> @@ -445,7 +449,7 @@ __weak void mmu_setup(void) /* * Performs a invalidation of the entire data cache at all levels */ -void invalidate_dcache_all(void) +void __efi_runtime invalidate_dcache_all(void) { __asm_invalidate_dcache_all(); __asm_invalidate_l3_dcache(); diff --git a/include/mm_communication.h b/include/mm_communication.h index e65fbde60d..fe9104c56d 100644 --- a/include/mm_communication.h +++ b/include/mm_communication.h @@ -6,6 +6,8 @@ * Copyright (c) 2017, Intel Corporation. All rights reserved. * Copyright (C) 2020 Linaro Ltd. sughosh.ganu@linaro.org * Copyright (C) 2020 Linaro Ltd. ilias.apalodimas@linaro.org + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#ifndef _MM_COMMUNICATION_H_ @@ -13,6 +15,9 @@
#include <part_efi.h>
+/* MM service UUID string (big-endian format). This UUID is common across all MM SPs */ +#define MM_SP_UUID "33d532ed-e699-0942-c09c-a798d9cd722d" + /* * Interface to the pseudo Trusted Application (TA), which provides a * communication channel with the Standalone MM (Management Mode) @@ -43,7 +48,7 @@ * To avoid confusion in interpreting frames, the communication buffer should * always begin with efi_mm_communicate_header. */ -struct __packed efi_mm_communicate_header { +struct efi_mm_communicate_header { efi_guid_t header_guid; size_t message_len; u8 data[]; @@ -145,7 +150,7 @@ struct smm_variable_communicate_header { * Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE. * */ -struct smm_variable_access { +struct __packed smm_variable_access { efi_guid_t guid; efi_uintn_t data_size; efi_uintn_t name_size; diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index b8fb2701a7..d292f57244 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -61,13 +61,23 @@ 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" - depends on OPTEE + bool "UEFI variables storage service via the trusted world" + depends on OPTEE || ARM_FFA_TRANSPORT 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 EFI_VARIABLE_NO_STORE bool "Don't persist non-volatile UEFI variables" help diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index d404343a7d..8a397ea21b 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -2193,6 +2193,13 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, debug("[efi_boottime][INFO]: FF-A RX/TX buffers unmapped\n"); #endif
+#if CONFIG_IS_ENABLED(ARM_FFA_EFI_RUNTIME_MODE) && !CONFIG_IS_ENABLED(SANDBOX_FFA) + if (ffa_copy_runtime_data()) + printf("ERROR: EFI: FFA: copying runtime data\n"); + else + printf("INFO: EFI: FFA: runtime data copied\n"); +#endif + /* Patch out unsupported runtime function */ efi_runtime_detach();
diff --git a/lib/efi_loader/efi_variable_tee.c b/lib/efi_loader/efi_variable_tee.c index dfef18435d..d6f24f85bd 100644 --- a/lib/efi_loader/efi_variable_tee.c +++ b/lib/efi_loader/efi_variable_tee.c @@ -4,6 +4,8 @@ * * Copyright (C) 2019 Linaro Ltd. sughosh.ganu@linaro.org * Copyright (C) 2019 Linaro Ltd. ilias.apalodimas@linaro.org + * Copyright (C) 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> @@ -15,6 +17,36 @@ #include <malloc.h> #include <mm_communication.h>
+#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) + +#include <arm_ffa.h> +#include <cpu_func.h> +#include <mapmem.h> + +#ifndef FFA_SHARED_MM_BUFFER_SIZE +#warning "FFA_SHARED_MM_BUFFER_SIZE must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_SIZE 0 +#endif + +#ifndef FFA_SHARED_MM_BUFFER_OFFSET +#warning "FFA_SHARED_MM_BUFFER_OFFSET must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_OFFSET 0 +#endif + +#ifndef FFA_SHARED_MM_BUFFER_ADDR +#warning "FFA_SHARED_MM_BUFFER_ADDR must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_ADDR 0 +#endif + +/* MM return codes */ +#define MM_SUCCESS (0) + +const char *mm_sp_svc_uuid = MM_SP_UUID; + +static __efi_runtime_data 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 */ static efi_uintn_t max_payload_size; /* func + data */ @@ -24,6 +56,7 @@ struct mm_connection { u32 session; };
+#if (IS_ENABLED(CONFIG_OPTEE)) /** * get_connection() - Retrieve OP-TEE session for a specific UUID. * @@ -143,16 +176,227 @@ static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize)
return ret; } +#endif + +#if (IS_ENABLED(CONFIG_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 + * + * Notifies 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 __efi_runtime ffa_notify_mm_sp(void) +{ + struct ffa_send_direct_data msg = {0}; + int ret; + int sp_event_ret = -1; + + if (!ffa_bus_ops_get()) + return -EINVAL; + + msg.data0 = FFA_SHARED_MM_BUFFER_OFFSET; /* x3 */ + + ret = ffa_bus_ops_get()->sync_send_receive(mm_sp_id, &msg); + if (ret != 0) + return ret; + + sp_event_ret = msg.data0; /* x3 */ + + if (sp_event_ret == MM_SUCCESS) + return 0; + + /* + * Failure to notify the MM SP + */ + + return -EACCES; +} + +/** + * 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, size = 0; + int ret; + struct ffa_partition_info *parts_info; + + if (!ffa_bus_ops_get()) + return -EINVAL; + + /* + * get from the driver the count of the SPs matching the UUID + */ + ret = ffa_bus_ops_get()->partition_info_get(mm_sp_svc_uuid, &count, NULL); + if (ret != 0) { + log_err("EFI: Failure in querying partitions count (error code: %d)\n", ret); + return ret; + } + + if (!count) { + log_info("EFI: No MM partition found\n"); + return ret; + } + + /* + * pre-allocate a buffer to be filled by the driver + * with ffa_partition_info structs + */ + + log_info("EFI: Pre-allocating %d partition(s) info structures\n", count); + + parts_info = calloc(count, sizeof(struct ffa_partition_info)); + if (!parts_info) + return -EINVAL; + + size = count * sizeof(struct ffa_partition_info); + + /* + * ask the driver to fill the + * buffer with the SPs info + */ + ret = ffa_bus_ops_get()->partition_info_get(mm_sp_svc_uuid, &size, parts_info); + if (ret != 0) { + log_err("EFI: Failure in querying partition(s) info (error code: %d)\n", ret); + free(parts_info); + return ret; + } + + /* + * MM SPs found , use the first one + */ + + mm_sp_id = parts_info[0].id; + + log_info("EFI: MM partition ID 0x%x\n", mm_sp_id); + + free(parts_info); + + 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 + * + * Issues 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 __efi_runtime ffa_mm_communicate(void *comm_buf, ulong comm_buf_size) +{ + ulong tx_data_size; + int ffa_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() != 0) { + log_err("EFI: Failure to discover MM partition ID at boot time\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 > FFA_SHARED_MM_BUFFER_SIZE) + return EFI_INVALID_PARAMETER; + + /* Copy the data to the shared buffer */ + + virt_shared_buf = (void *)map_sysmem((phys_addr_t)FFA_SHARED_MM_BUFFER_ADDR, 0); + efi_memcpy_runtime(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(); + if (ffa_ret) + unmap_sysmem(virt_shared_buf); + + 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) { + unmap_sysmem(virt_shared_buf); + return EFI_OUT_OF_RESOURCES; + } + + efi_memcpy_runtime(comm_buf, virt_shared_buf, rx_data_size); + unmap_sysmem(virt_shared_buf); + + return EFI_SUCCESS; + } + case -EINVAL: + return EFI_DEVICE_ERROR; + case -EPERM: + return EFI_INVALID_PARAMETER; + case -EACCES: + return EFI_ACCESS_DENIED; + case -EBUSY: + return EFI_OUT_OF_RESOURCES; + default: + return EFI_ACCESS_DENIED; + } +} +#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 MM SP (also called partition) can be 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, StandAlonneMM and smm-gateway are supported. + * * Return: status code */ -static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) +static efi_status_t __efi_runtime mm_communicate(u8 *comm_buf, efi_uintn_t dsize) { efi_status_t ret; struct efi_mm_communicate_header *mm_hdr; @@ -162,7 +406,11 @@ static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) mm_hdr = (struct efi_mm_communicate_header *)comm_buf; var_hdr = (struct smm_variable_communicate_header *)mm_hdr->data;
+ #if (IS_ENABLED(CONFIG_OPTEE)) ret = optee_mm_communicate(comm_buf, dsize); + #elif (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) + ret = ffa_mm_communicate(comm_buf, dsize); + #endif if (ret != EFI_SUCCESS) { log_err("%s failed!\n", __func__); return ret; @@ -258,6 +506,13 @@ efi_status_t EFIAPI get_max_payload(efi_uintn_t *size) goto out; } *size = var_payload->size; + + #if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) + if (*size > FFA_SHARED_MM_BUFFER_SIZE) + *size = FFA_SHARED_MM_BUFFER_SIZE - MM_COMMUNICATE_HEADER_SIZE - + MM_VARIABLE_COMMUNICATE_SIZE; + #endif + /* * There seems to be a bug in EDK2 miscalculating the boundaries and * size checks, so deduct 2 more bytes to fulfill this requirement. Fix @@ -697,7 +952,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);
/*

turn on EFI MM communication
On corstone1000 platform MM communication between u-boot and the secure world (Optee) is done using the FF-A bus.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org --- configs/corstone1000_defconfig | 4 ++++ include/configs/corstone1000.h | 9 +++++++++ 2 files changed, 13 insertions(+)
diff --git a/configs/corstone1000_defconfig b/configs/corstone1000_defconfig index ed2e0fe70a..5028c746c5 100644 --- a/configs/corstone1000_defconfig +++ b/configs/corstone1000_defconfig @@ -52,3 +52,7 @@ CONFIG_DM_SERIAL=y CONFIG_USB=y CONFIG_USB_ISP1760=y CONFIG_ERRNO_STR=y +CONFIG_EFI_MM_COMM_TEE=y +# CONFIG_OPTEE is not set +CONFIG_ARM_FFA_TRANSPORT=y +CONFIG_ARM_FFA_EFI_RUNTIME_MODE=y diff --git a/include/configs/corstone1000.h b/include/configs/corstone1000.h index 8e0230c135..997d0bebaf 100644 --- a/include/configs/corstone1000.h +++ b/include/configs/corstone1000.h @@ -14,6 +14,15 @@
#include <linux/sizes.h>
+#define FFA_SHARED_MM_BUFFER_SIZE SZ_4K /* 4 KB */ + +/* + * shared buffer physical address used for communication between + * u-boot and the MM SP + */ +#define FFA_SHARED_MM_BUFFER_ADDR (0x023F8000) +#define FFA_SHARED_MM_BUFFER_OFFSET (0) + #define V2M_BASE 0x80000000
#define CONFIG_PL011_CLOCK 50000000

Moving the changelogs in each commit to the changes section.
No code change in v5.
Changelog of the major changes: ===========================
v5:
* move changelogs in each commit to the changes section
v4: [4]
* add FF-A support README (doc/README.ffa.drv) * improving error handling by mapping the FF-A errors to standard errors and logs * replacing panics with an error log * align sandbox driver and tests with the new FF-A driver interfaces and new way of error handling * use the new FF-A driver interfaces for MM communication * discover MM partitions at runtime * copy FF-A driver private data to EFI runtime section at ExitBootServices() * moving the FF-A driver work to drivers/firmware/arm-ffa * improving features discovery in FFA_FEATURES * add remove/bind functions to the FF-A core device * improve how the driver behaves when bus discovery is done more than once * move clearing x0-x17 registers code into a new macro like done in the linux kernel * enable EFI MM communication for the Corstone1000 platform
v3: [3]
* port x0-x17 registers support from linux kernel as defined by SMCCCv1.2 * align the interfaces of the u-boot FF-A driver with those in the linux FF-A driver * remove the FF-A helper layer * make the u-boot FF-A driver independent from EFI * provide an optional config that enables copying the driver data to EFI runtime section at ExitBootServices service * use 64-bit version of FFA_RXTX_MAP, FFA_MSG_SEND_DIRECT_{REQ, RESP} * update armffa command with the new driver interfaces
v2 [2]:
* make FF-A bus discoverable using device_{bind, probe} APIs * remove device tree support
v1 [1]:
* introduce FF-A bus driver with device tree support * introduce armffa command * introduce FF-A Sandbox driver * add FF-A Sandbox test cases * introduce FF-A MM communication
Cheers, Abdellatif
[1]: https://lore.kernel.org/all/20220329151659.16894-1-abdellatif.elkhlifi@arm.c... [2]: https://lore.kernel.org/all/20220415122803.16666-1-abdellatif.elkhlifi@arm.c... [3]: https://lore.kernel.org/all/20220801172053.20163-1-abdellatif.elkhlifi@arm.c... [4]: https://lore.kernel.org/all/20220926101723.9965-1-abdellatif.elkhlifi@arm.co...
Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Achin Gupta achin.gupta@arm.com Cc: Vishnu Banavath vishnu.banavath@arm.com Cc: Xueliang Zhong Xueliang.Zhong@arm.com
Abdellatif El Khlifi (10): arm64: smccc: add support for SMCCCv1.2 x0-x17 registers arm64: smccc: clear the Xn registers after SMC calls lib: uuid: introduce be_uuid_str_to_le_bin function arm_ffa: introduce Arm FF-A low-level driver arm_ffa: introduce armffa command arm_ffa: introduce the FF-A Sandbox driver arm_ffa: introduce Sandbox test cases for UCLASS_FFA arm_ffa: introduce armffa command Sandbox test arm_ffa: introduce FF-A MM communication arm_ffa: corstone1000: enable EFI MM communication
MAINTAINERS | 11 + arch/arm/cpu/armv8/cache.S | 19 + arch/arm/cpu/armv8/cache_v8.c | 6 +- arch/arm/cpu/armv8/smccc-call.S | 62 + arch/arm/lib/asm-offsets.c | 14 + cmd/Kconfig | 10 + cmd/Makefile | 2 + cmd/armffa.c | 242 +++ common/board_r.c | 9 + configs/corstone1000_defconfig | 4 + configs/sandbox64_defconfig | 2 + configs/sandbox_defconfig | 2 + doc/README.ffa.drv | 160 ++ doc/arch/sandbox.rst | 1 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/firmware/arm-ffa/Kconfig | 46 + drivers/firmware/arm-ffa/Makefile | 8 + drivers/firmware/arm-ffa/arm-ffa-uclass.c | 16 + drivers/firmware/arm-ffa/arm_ffa_prv.h | 201 +++ drivers/firmware/arm-ffa/core.c | 1358 +++++++++++++++++ .../arm-ffa/efi_ffa_runtime_data_mgr.c | 94 ++ drivers/firmware/arm-ffa/sandbox.c | 659 ++++++++ .../firmware/arm-ffa/sandbox_arm_ffa_prv.h | 144 ++ include/arm_ffa.h | 127 ++ include/configs/corstone1000.h | 9 + include/dm/uclass-id.h | 4 + include/linux/arm-smccc.h | 43 + include/mm_communication.h | 9 +- include/sandbox_arm_ffa.h | 91 ++ include/uuid.h | 8 + lib/efi_loader/Kconfig | 14 +- lib/efi_loader/efi_boottime.c | 22 + lib/efi_loader/efi_variable_tee.c | 263 +++- lib/uuid.c | 64 + test/cmd/Makefile | 2 + test/cmd/armffa.c | 40 + test/dm/Makefile | 2 + test/dm/ffa.c | 394 +++++ 39 files changed, 4156 insertions(+), 9 deletions(-) create mode 100644 cmd/armffa.c create mode 100644 doc/README.ffa.drv 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_prv.h create mode 100644 drivers/firmware/arm-ffa/core.c create mode 100644 drivers/firmware/arm-ffa/efi_ffa_runtime_data_mgr.c create mode 100644 drivers/firmware/arm-ffa/sandbox.c create mode 100644 drivers/firmware/arm-ffa/sandbox_arm_ffa_prv.h create mode 100644 include/arm_ffa.h create mode 100644 include/sandbox_arm_ffa.h create mode 100644 test/cmd/armffa.c create mode 100644 test/dm/ffa.c

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 abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org ---
Changelog: ===============
v4:
* rename the commit title and improve description new commit title: the current
v3:
* port x0-x17 registers support from linux kernel as defined by SMCCCv1.2 commit title: arm64: smccc: add Xn registers support used by SMC calls
arch/arm/cpu/armv8/smccc-call.S | 53 +++++++++++++++++++++++++++++++++ arch/arm/lib/asm-offsets.c | 14 +++++++++ include/linux/arm-smccc.h | 43 ++++++++++++++++++++++++++ 3 files changed, 110 insertions(+)
diff --git a/arch/arm/cpu/armv8/smccc-call.S b/arch/arm/cpu/armv8/smccc-call.S index dc92b28777..ec6f299bc9 100644 --- a/arch/arm/cpu/armv8/smccc-call.S +++ b/arch/arm/cpu/armv8/smccc-call.S @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2015, Linaro Limited + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */ #include <linux/linkage.h> #include <linux/arm-smccc.h> @@ -45,3 +47,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 22fd541f9a..1bc2d90faa 100644 --- a/arch/arm/lib/asm-offsets.c +++ b/arch/arm/lib/asm-offsets.c @@ -9,6 +9,9 @@ * generate asm statements containing #defines, * compile this file to assembler, and then extract the * #defines from the assembly-language output. + * + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> @@ -117,6 +120,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 e1d09884a1..9105031d55 100644 --- a/include/linux/arm-smccc.h +++ b/include/linux/arm-smccc.h @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2015, Linaro Limited + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */ #ifndef __LINUX_ARM_SMCCC_H #define __LINUX_ARM_SMCCC_H @@ -70,6 +72,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

set to zero the x0-x17 registers
As per the SMCCC v1.2 spec, unused result and scratch registers can leak information after an SMC call. We can mitigate against this risk by returning zero in each register.
The leakage we are referring to is data leakage across exception levels. The intent is to prevent lower exception levels (EL1/EL0) from reading the SMC data exchanged at EL2.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org ---
Changelog: ===============
v4:
* move the clearing code into a new macro: clear_gp_regs
v3:
* clear the Xn registers after SMC calls
arch/arm/cpu/armv8/smccc-call.S | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/arch/arm/cpu/armv8/smccc-call.S b/arch/arm/cpu/armv8/smccc-call.S index ec6f299bc9..32f3eb8eeb 100644 --- a/arch/arm/cpu/armv8/smccc-call.S +++ b/arch/arm/cpu/armv8/smccc-call.S @@ -50,6 +50,12 @@ ENDPROC(__arm_smccc_hvc)
#ifdef CONFIG_ARM64
+ .macro clear_gp_regs + .irp n,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17 + mov x\n, xzr + .endr + .endm + .macro SMCCC_1_2 instr /* Save `res` and free a GPR that won't be clobbered */ stp x1, x19, [sp, #-16]! @@ -84,6 +90,9 @@ ENDPROC(__arm_smccc_hvc) stp x14, x15, [x19, #ARM_SMCCC_1_2_REGS_X14_OFFS] stp x16, x17, [x19, #ARM_SMCCC_1_2_REGS_X16_OFFS]
+ /* x0-x17 registers can leak information after an SMC or HVC call. Let's clear them */ + clear_gp_regs + /* Restore original x19 */ ldp xzr, x19, [sp], #16 ret

On Mon, Sep 26, 2022 at 03:08:19PM +0100, Abdellatif El Khlifi wrote:
set to zero the x0-x17 registers
As per the SMCCC v1.2 spec, unused result and scratch registers can leak information after an SMC call. We can mitigate against this risk by returning zero in each register.
The leakage we are referring to is data leakage across exception levels. The intent is to prevent lower exception levels (EL1/EL0) from reading the SMC data exchanged at EL2.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v4:
- move the clearing code into a new macro: clear_gp_regs
v3:
- clear the Xn registers after SMC calls
arch/arm/cpu/armv8/smccc-call.S | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/arch/arm/cpu/armv8/smccc-call.S b/arch/arm/cpu/armv8/smccc-call.S index ec6f299bc9..32f3eb8eeb 100644 --- a/arch/arm/cpu/armv8/smccc-call.S +++ b/arch/arm/cpu/armv8/smccc-call.S @@ -50,6 +50,12 @@ ENDPROC(__arm_smccc_hvc)
#ifdef CONFIG_ARM64
- .macro clear_gp_regs
- .irp n,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17
- mov x\n, xzr
- .endr
- .endm
- .macro SMCCC_1_2 instr /* Save `res` and free a GPR that won't be clobbered */ stp x1, x19, [sp, #-16]!
@@ -84,6 +90,9 @@ ENDPROC(__arm_smccc_hvc) stp x14, x15, [x19, #ARM_SMCCC_1_2_REGS_X14_OFFS] stp x16, x17, [x19, #ARM_SMCCC_1_2_REGS_X16_OFFS]
- /* x0-x17 registers can leak information after an SMC or HVC call. Let's clear them */
- clear_gp_regs
This should in my opinion not be needed. The higher exception level should only return what it indends to return and certainly not rely on lower exception levels to try to hide eventual unintentionally revealed secrets.
In an earlier conversation you said:
The leakage we are referring to is data leakage across exception levels. The intent is to prevent lower exception levels (EL1/EL0) to read the data exchanged at EL2.
The linux kernel clears the general purpose registers before switching to EL0. As far as I know u-boot doesn't.
So, the code above makes sure the registers are cleared.
U-Boot is as far as I know not changing to EL0. Do you have a real example where this cleaning actually would be needed? If it's needed I'd expect the cleaning to be done just before changing exception level.
Cheers, Jens
/* Restore original x19 */ ldp xzr, x19, [sp], #16 ret -- 2.17.1

On Fri, Sep 30, 2022 at 11:48:04AM +0200, Jens Wiklander wrote:
On Mon, Sep 26, 2022 at 03:08:19PM +0100, Abdellatif El Khlifi wrote:
set to zero the x0-x17 registers
As per the SMCCC v1.2 spec, unused result and scratch registers can leak information after an SMC call. We can mitigate against this risk by returning zero in each register.
The leakage we are referring to is data leakage across exception levels. The intent is to prevent lower exception levels (EL1/EL0) from reading the SMC data exchanged at EL2.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v4:
- move the clearing code into a new macro: clear_gp_regs
v3:
- clear the Xn registers after SMC calls
arch/arm/cpu/armv8/smccc-call.S | 9 +++++++++ 1 file changed, 9 insertions(+)
diff --git a/arch/arm/cpu/armv8/smccc-call.S b/arch/arm/cpu/armv8/smccc-call.S index ec6f299bc9..32f3eb8eeb 100644 --- a/arch/arm/cpu/armv8/smccc-call.S +++ b/arch/arm/cpu/armv8/smccc-call.S @@ -50,6 +50,12 @@ ENDPROC(__arm_smccc_hvc)
#ifdef CONFIG_ARM64
- .macro clear_gp_regs
- .irp n,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17
- mov x\n, xzr
- .endr
- .endm
- .macro SMCCC_1_2 instr /* Save `res` and free a GPR that won't be clobbered */ stp x1, x19, [sp, #-16]!
@@ -84,6 +90,9 @@ ENDPROC(__arm_smccc_hvc) stp x14, x15, [x19, #ARM_SMCCC_1_2_REGS_X14_OFFS] stp x16, x17, [x19, #ARM_SMCCC_1_2_REGS_X16_OFFS]
- /* x0-x17 registers can leak information after an SMC or HVC call. Let's clear them */
- clear_gp_regs
This should in my opinion not be needed. The higher exception level should only return what it indends to return and certainly not rely on lower exception levels to try to hide eventual unintentionally revealed secrets.
In an earlier conversation you said:
The leakage we are referring to is data leakage across exception levels. The intent is to prevent lower exception levels (EL1/EL0) to read the data exchanged at EL2.
The linux kernel clears the general purpose registers before switching to EL0. As far as I know u-boot doesn't.
So, the code above makes sure the registers are cleared.
U-Boot is as far as I know not changing to EL0. Do you have a real example where this cleaning actually would be needed? If it's needed I'd expect the cleaning to be done just before changing exception level.
No real example for that. But I tried to follow the spec as much as possible. Since, no real need for this I'm happy to remove the commit and this has been done in patchset v6. Thanks.
Cheers, Jens
/* Restore original x19 */ ldp xzr, x19, [sp], #16 ret -- 2.17.1

convert big endian UUID string to little endian buffer
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org ---
Changelog: ===============
v4:
* rename ffa_uuid_str_to_bin to be_uuid_str_to_le_bin and put in a standalone commit (the current)
v3:
* introduce ffa_uuid_str_to_bin (provided by arm_ffa: introduce Arm FF-A low-level driver)
include/uuid.h | 8 +++++++ lib/uuid.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+)
diff --git a/include/uuid.h b/include/uuid.h index 4a4883d3b5..ad3af350f9 100644 --- a/include/uuid.h +++ b/include/uuid.h @@ -2,6 +2,8 @@ /* * Copyright (C) 2014 Samsung Electronics * Przemyslaw Marczak p.marczak@samsung.com + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */ #ifndef __UUID_H__ #define __UUID_H__ @@ -44,4 +46,10 @@ 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); + +/** + * be_uuid_str_to_le_bin - Converts a big endian UUID string to a little endian buffer + */ +int be_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 465e1ac38f..15a9ab49d5 100644 --- a/lib/uuid.c +++ b/lib/uuid.c @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-2.0+ /* * Copyright 2011 Calxeda, Inc. + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> @@ -346,6 +348,68 @@ int uuid_str_to_bin(const char *uuid_str, unsigned char *uuid_bin, return 0; }
+/** + * be_uuid_str_to_le_bin - Converts a big endian UUID string to a little endian buffer + * @uuid_str: UUID string in big endian format (36 bytes wide + '/0') + * @uuid_bin: preallocated 16 bytes UUID buffer in little endian format + * + * UUID string is 36 characters (36 bytes): + * + * xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + * be be be be be + * + * where x is a hexadecimal character. Fields are separated by '-'s. + * When converting to a binary UUID, these endianness rules apply: + * be: means the field in the string is considered a big endian hex number + * and should be converted to little endian binary format + * + * Return: + * + * uuid_bin filled with little endian UUID data + * On success 0 is returned. Otherwise, failure code. + */ +int be_uuid_str_to_le_bin(const char *uuid_str, unsigned char *uuid_bin) +{ + u16 tmp16 = 0; + u32 tmp32 = 0; + u64 tmp64 = 0; + + if (!uuid_str_valid(uuid_str) || !uuid_bin) + return -EINVAL; + + /* + * reverse bytes from big to little endian + */ + tmp32 = simple_strtoul(uuid_str, NULL, 16); + memcpy(uuid_bin, &tmp32, 4); + + /* + * reverse bytes from big to little endian + */ + tmp16 = simple_strtoul(uuid_str + 9, NULL, 16); + memcpy(uuid_bin + 4, &tmp16, 2); + + /* + * reverse bytes from big to little endian + */ + tmp16 = simple_strtoul(uuid_str + 14, NULL, 16); + memcpy(uuid_bin + 6, &tmp16, 2); + + /* + * reverse bytes from big to little endian + */ + tmp16 = simple_strtoul(uuid_str + 19, NULL, 16); + memcpy(uuid_bin + 8, &tmp16, 2); + + /* + * reverse bytes from big to little endian + */ + tmp64 = simple_strtoull(uuid_str + 24, NULL, 16); + memcpy(uuid_bin + 10, (char *)&tmp64, 6); + + return 0; +} + /* * uuid_bin_to_str() - convert big endian binary data to string UUID or GUID. *

Add the driver 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.
32-bit version of the ABIs is supported and 64-bit version of FFA_RXTX_MAP and FFA_MSG_SEND_DIRECT_{REQ, RESP}.
In u-boot FF-A design, FF-A is considered as a discoverable bus. 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:
- partition_info_get - sync_send_receive - rxtx_unmap
This implementation provides an optional feature to copy the driver data to EFI runtime area.
[1]: https://developer.arm.com/documentation/den0077/latest/
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org ---
Changelog: ===============
v4:
* add doc/README.ffa.drv * moving the FF-A driver work to drivers/firmware/arm-ffa * use less #ifdefs in lib/efi_loader/efi_boottime.c and replace #if defined by #if CONFIG_IS_ENABLED * improving error handling by mapping the FF-A errors to standard errors and logs * replacing panics with an error log and returning an error code * improving features discovery in FFA_FEATURES by introducing rxtx_min_pages private data field * add ffa_remove and ffa_bind functions * improve how the driver behaves when bus discovery is done more than once
v3:
* align the interfaces of the u-boot FF-A driver with those in the linux FF-A driver * remove the FF-A helper layer * make the u-boot FF-A driver independent from EFI * provide an optional config that enables copying the driver data to EFI runtime section at ExitBootServices service * use 64-bit version of FFA_RXTX_MAP, FFA_MSG_SEND_DIRECT_{REQ, RESP}
v2:
* make FF-A bus discoverable using device_{bind, probe} APIs * remove device tree support
v1:
* introduce FF-A bus driver with device tree support
MAINTAINERS | 7 + common/board_r.c | 9 + doc/README.ffa.drv | 160 ++ drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/firmware/arm-ffa/Kconfig | 39 + drivers/firmware/arm-ffa/Makefile | 7 + drivers/firmware/arm-ffa/arm-ffa-uclass.c | 16 + drivers/firmware/arm-ffa/arm_ffa_prv.h | 196 +++ drivers/firmware/arm-ffa/core.c | 1344 +++++++++++++++++ .../arm-ffa/efi_ffa_runtime_data_mgr.c | 94 ++ include/arm_ffa.h | 127 ++ include/dm/uclass-id.h | 4 + lib/efi_loader/efi_boottime.c | 15 + 14 files changed, 2021 insertions(+) create mode 100644 doc/README.ffa.drv 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_prv.h create mode 100644 drivers/firmware/arm-ffa/core.c create mode 100644 drivers/firmware/arm-ffa/efi_ffa_runtime_data_mgr.c create mode 100644 include/arm_ffa.h
diff --git a/MAINTAINERS b/MAINTAINERS index 83346183ee..02b84d5074 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -248,6 +248,13 @@ 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 abdellatif.elkhlifi@arm.com +S: Maintained +F: doc/README.ffa.drv +F: drivers/firmware/arm-ffa/ +F: include/arm_ffa.h + ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de M: Fabio Estevam festevam@gmail.com diff --git a/common/board_r.c b/common/board_r.c index 56eb60fa27..8c99faddfd 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -7,6 +7,8 @@ * (C) Copyright 2002 * Sysgo Real-Time Solutions, GmbH <www.elinos.com> * Marius Groeger mgroeger@sysgo.de + * + * (C) Copyright 2022 ARM Limited */
#include <common.h> @@ -66,6 +68,10 @@ #include <efi_loader.h> #include <relocate.h>
+#ifdef CONFIG_ARM_FFA_TRANSPORT +#include <arm_ffa.h> +#endif + DECLARE_GLOBAL_DATA_PTR;
ulong monitor_flash_len; @@ -770,6 +776,9 @@ static init_fnc_t init_sequence_r[] = { INIT_FUNC_WATCHDOG_RESET initr_net, #endif +#ifdef CONFIG_ARM_FFA_TRANSPORT + ffa_bus_discover, +#endif #ifdef CONFIG_POST initr_post, #endif diff --git a/doc/README.ffa.drv b/doc/README.ffa.drv new file mode 100644 index 0000000000..1c0a33deb8 --- /dev/null +++ b/doc/README.ffa.drv @@ -0,0 +1,160 @@ +Arm FF-A Driver +==================== + +Introduction +-------------------- + +FF-A stands for Firmware Framework for Arm A-profile processors. + +FF-A specifies interfaces that enable a pair of software sandboxes to communicate with each other. A sandbox aka partition could +be a VM in the Normal or Secure world, an application in S-EL0, or a Trusted OS in S-EL1. + +This FF-A driver implements the interfaces to communicate with partitions in the Secure world aka Secure partitions (SPs). + +The driver 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. + +FF-A driver 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. + +FF-A and SMC specifications +------------------------------------------- + +The current implementation of the driver relies on FF-A specification v1.0 and uses SMC32 calling convention. + +The driver has been tested with Optee OS which supports SMC32 for most of the SMC ABIs. + +For more details please refer to: https://developer.arm.com/documentation/den0077/a/?lang=en + +The FF-A driver uses 64-bit registers as per SMCCCv1.2 specification. + +For more details please refer to: https://documentation-service.arm.com/static/5f8edaeff86e16515cdbe4c6?token= + +Supported hardware +-------------------------------- + +Aarch64 plaforms + +Configuration +---------------------- + +CONFIG_ARM_FFA_TRANSPORT + Enables the FF-A bus driver. Turn this on if you want to use FF-A communication. + +CONFIG_ARM_FFA_EFI_RUNTIME_MODE + Optional config that enables EFI runtime support for FF-A data and code. + ffa_copy_runtime_data allows to copy the FF-A driver data structures to EFI runtime data section. + Turning the config on makes ffa_copy_runtime_data available for use and the driver code placed at EFI runtime code section. + Call ffa_copy_runtime_data at the event on which you want the FF-A data to be copied (example: at ExitBootServices). + +CONFIG_SANDBOX_FFA + Enables FF-A Sandbox driver. This emulates the FF-A ABIs handling under Sandbox and provides + functional tests for FF-A. + +FF-A ABIs under the hood +--------------------------------------- + +Invoking an FF-A ABI involves providing to the secure world/hypervisor the expected arguments from the ABI. + +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 giving back to the u-boot FF-A driver (non secure world). + +The driver reads the response and processes it accordingly. + +This methodology applies to all the FF-A ABIs in the driver. + +FF-A bus discovery in u-boot +------------------------------------------- + +When CONFIG_ARM_FFA_TRANSPORT is enabled, the FF-A bus is automatically discovered at initcall level (after u-boot relocation). + +The function that triggers the discovery process is ffa_bus_discover. + +ffa_bus_discover creates, binds and probes the arm_ffa device using device_{bind, probe} APIs. + +When the device is probed, ffa_probe is called which tries to communicate with the secure world or hypervisor. + +The FF-A bus is usable when these checks succeed: + +- querying the FF-A framework version +- querying from secure world the u-boot endpoint ID +- querying from secure world the supported features of the specified FF-A calls +- mapping the RX/TX buffers +- querying from secure world all the partitions information + +Probing fails when any of these operations fail. The FF-A bus discovery succeeds when probing is successful. + +When discovery fails the arm_ffa device is destroyed. + +The bus driver layer +------------------------------ + +The driver comes on top of the SMCCC layer and is implemented in drivers/firmware/arm-ffa/core.c + +The driver provides the following features: + +- 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 + +- 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, +the driver takes care of how to transport that to the secure world/hypervisor using FF-A. + +- The driver provides callbacks to be used by clients to access the FF-A bus: + +partition_info_get +sync_send_receive +rxtx_unmap + +- FF-A bus discovery at initcalls level (after u-boot relocation). The bus is up and running if the FF-A framework is responsive and compatible with the driver. + +- When EFI is enabled, unmap the RX/TX buffers at ExitBootServices() level. + +- When CONFIG_ARM_FFA_EFI_RUNTIME_MODE enabled, ffa_copy_runtime_data function is available for use. + +Using armffa command +----------------------------------- + +armffa is a command showcasing how to use the FF-A driver and how to invoke its operations. + +This provides a guidance to the client developers on how to call the FF-A bus interfaces. + +Usage: + +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 the arm_ffa device info diff --git a/drivers/Kconfig b/drivers/Kconfig index 8b6fead351..b06b1ae481 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -6,6 +6,8 @@ source "drivers/core/Kconfig"
source "drivers/adc/Kconfig"
+source "drivers/firmware/arm-ffa/Kconfig" + source "drivers/ata/Kconfig"
source "drivers/axi/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index eba9940231..c3bfad94ac 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -110,6 +110,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/arm-ffa/Kconfig b/drivers/firmware/arm-ffa/Kconfig new file mode 100644 index 0000000000..aceb61cf49 --- /dev/null +++ b/drivers/firmware/arm-ffa/Kconfig @@ -0,0 +1,39 @@ +# 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 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. + + This driver 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. + 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. + + For more details about the FF-A driver, please refer to doc/README.ffa.drv + +config ARM_FFA_EFI_RUNTIME_MODE + bool "Enable EFI runtime support for FF-A data and code" + depends on ARM_FFA_TRANSPORT && EFI_LOADER + help + Allows FF-A driver data structures and code to be accessible at EFI runtime. + FF-A data is copied by ffa_copy_runtime_data function. + The driver Code needed at runtime is placed at EFI runtime code section. + Turning this on makes ffa_copy_runtime_data available for use and the driver + code placed at EFI runtime code section. diff --git a/drivers/firmware/arm-ffa/Makefile b/drivers/firmware/arm-ffa/Makefile new file mode 100644 index 0000000000..0b9b0a61b4 --- /dev/null +++ b/drivers/firmware/arm-ffa/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2022 Abdellatif El Khlifi abdellatif.elkhlifi@arm.com +# + +obj-y += arm-ffa-uclass.o core.o +obj-$(CONFIG_ARM_FFA_EFI_RUNTIME_MODE) += efi_ffa_runtime_data_mgr.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 0000000000..7d9695d289 --- /dev/null +++ b/drivers/firmware/arm-ffa/arm-ffa-uclass.c @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <dm.h> +#include <asm/global_data.h> + +DECLARE_GLOBAL_DATA_PTR; + +UCLASS_DRIVER(ffa) = { + .name = "ffa", + .id = UCLASS_FFA, +}; diff --git a/drivers/firmware/arm-ffa/arm_ffa_prv.h b/drivers/firmware/arm-ffa/arm_ffa_prv.h new file mode 100644 index 0000000000..7bc90f7f66 --- /dev/null +++ b/drivers/firmware/arm-ffa/arm_ffa_prv.h @@ -0,0 +1,196 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __ARM_FFA_PRV_H +#define __ARM_FFA_PRV_H + +#include <arm_ffa.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/arm-smccc.h> + +/* + * This header is private. It is exclusively used by the FF-A driver + */ + +/* FF-A core driver name */ +#define FFA_DRV_NAME "arm_ffa" + +/* 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*/ +}; + +/* number of the errors supported by the FF-A specification */ +#define MAX_NUMBER_FFA_ERR 9 + +/* 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) + +/* The FF-A SMC function definitions */ + +typedef struct arm_smccc_1_2_regs ffa_value_t; +typedef void (*invoke_ffa_fn_t)(ffa_value_t args, ffa_value_t *res); + +/* + * 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 */ +}; + +/** + * 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 - structure hosting 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 + * + * Data structure hosting 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 { + u64 rxbuf; /* virtual address */ + u64 txbuf; /* virtual address */ + size_t rxtx_min_pages; /* minimum number of pages in each of the RX/TX buffers */ +}; + +/** + * 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_partitions - descriptors for all secure partitions + * @count: The number of partitions descriptors + * @descs The partitions descriptors table + * + * This data structure contains the partitions descriptors table + */ +struct ffa_partitions { + u32 count; + struct ffa_partition_desc *descs; /* virtual address */ +}; + +/** + * struct ffa_prvdata - the driver private data structure + * + * @dev: The arm_ffa device under u-boot driver model + * @ffa_ops: The driver operations structure + * @fwk_version: FF-A framework version + * @id: u-boot endpoint ID + * @partitions: The partitions descriptors structure + * @pair: The RX/TX buffers pair + * @invoke_ffa_fn: The function executing the FF-A function + * + * The driver data structure hosting all resident data. + */ +struct ffa_prvdata { + struct udevice *dev; + struct ffa_bus_ops ffa_ops; + u32 fwk_version; + u16 id; + struct ffa_partitions partitions; + struct ffa_rxtxpair pair; + invoke_ffa_fn_t invoke_ffa_fn; +}; + +/** + * ffa_device_get - create, bind and probe the arm_ffa device + */ +int ffa_device_get(void); + +/** + * ffa_bus_prvdata_get - bus driver private data getter + */ +struct ffa_prvdata **ffa_bus_prvdata_get(void); + +#endif diff --git a/drivers/firmware/arm-ffa/core.c b/drivers/firmware/arm-ffa/core.c new file mode 100644 index 0000000000..41c7b96e68 --- /dev/null +++ b/drivers/firmware/arm-ffa/core.c @@ -0,0 +1,1344 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include "arm_ffa_prv.h" +#include <asm/global_data.h> +#include <common.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <dm/devres.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> +#include <log.h> +#include <malloc.h> +#include <string.h> +#include <uuid.h> + +DECLARE_GLOBAL_DATA_PTR; + +/** + * The device private data structure containing all the resident + * data read from secure world + */ +__ffa_runtime_data struct ffa_prvdata *ffa_priv_data; + +/* Error mapping declarations */ + +__ffa_runtime_data int ffa_to_std_errmap[MAX_NUMBER_FFA_ERR] = { + 0, + -EOPNOTSUPP, /* NOT_SUPPORTED */ + -EINVAL, /* INVALID_PARAMETERS */ + -ENOMEM, /* NO_MEMORY */ + -EBUSY, /* BUSY */ + -EINTR, /* INTERRUPTED */ + -EACCES, /* DENIED */ + -EAGAIN, /* RETRY */ + -ECANCELED, /* ABORTED */ +}; + +struct ffa_abi_errmap err_msg_map[FFA_ERRMAP_COUNT] = { + [FFA_ID_TO_ERRMAP_ID(FFA_VERSION)] = { + { + "", + "NOT_SUPPORTED: A Firmware Framework implementation does not exist", + "", /* INVALID_PARAMETERS */ + "", /* NO_MEMORY */ + "", /* BUSY */ + "", /* INTERRUPTED */ + "", /* DENIED */ + "", /* RETRY */ + "", /* ABORTED */ + }, + }, + [FFA_ID_TO_ERRMAP_ID(FFA_ID_GET)] = { + { + "", + "NOT_SUPPORTED: This function is not implemented at this FF-A instance", + "", /* INVALID_PARAMETERS */ + "", /* NO_MEMORY */ + "", /* BUSY */ + "", /* INTERRUPTED */ + "", /* DENIED */ + "", /* RETRY */ + "", /* ABORTED */ + }, + }, + [FFA_ID_TO_ERRMAP_ID(FFA_FEATURES)] = { + { + "", + "NOT_SUPPORTED: FFA_RXTX_MAP is not implemented at this FF-A instance", + "", /* INVALID_PARAMETERS */ + "", /* NO_MEMORY */ + "", /* BUSY */ + "", /* INTERRUPTED */ + "", /* DENIED */ + "", /* RETRY */ + "", /* ABORTED */ + }, + }, + [FFA_ID_TO_ERRMAP_ID(FFA_PARTITION_INFO_GET)] = { + { + "", + "NOT_SUPPORTED: This function is not implemented at this FF-A instance", + "INVALID_PARAMETERS: Unrecognized UUID", + "NO_MEMORY: Results cannot fit in RX buffer of the caller", + "BUSY: RX buffer of the caller is not free", + "", /* INTERRUPTED */ + "DENIED: Callee is not in a state to handle this request", + "", /* RETRY */ + "", /* ABORTED */ + }, + }, + [FFA_ID_TO_ERRMAP_ID(FFA_RXTX_UNMAP)] = { + { + "", + "NOT_SUPPORTED: FFA_RXTX_UNMAP is not implemented at this FF-A instance", + "INVALID_PARAMETERS: No buffer pair registered on behalf of the caller", + "", /* NO_MEMORY */ + "", /* BUSY */ + "", /* INTERRUPTED */ + "", /* DENIED */ + "", /* RETRY */ + "", /* ABORTED */ + }, + }, + [FFA_ID_TO_ERRMAP_ID(FFA_RX_RELEASE)] = { + { + "", + "NOT_SUPPORTED: FFA_RX_RELEASE is not implemented at this FF-A instance", + "", /* INVALID_PARAMETERS */ + "", /* NO_MEMORY */ + "", /* BUSY */ + "", /* INTERRUPTED */ + "DENIED: Caller did not have ownership of the RX buffer", + "", /* RETRY */ + "", /* ABORTED */ + }, + }, + [FFA_ID_TO_ERRMAP_ID(FFA_RXTX_MAP)] = { + { + "", + "NOT_SUPPORTED: This function is not implemented at this FF-A instance", + "INVALID_PARAMETERS: Field(s) in input parameters incorrectly encoded", + "NO_MEMORY: Not enough memory", + "", /* BUSY */ + "", /* INTERRUPTED */ + "DENIED: Buffer pair already registered", + "", /* RETRY */ + "", /* ABORTED */ + }, + }, +}; + +/** + * ffa_to_std_errno - convert FF-A error code to standard error code + * @ffa_errno: Error code returned by the FF-A ABI + * + * This runtime function maps 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 + */ +__ffa_runtime 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 + * + * This boot time function maps 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 + */ +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_msg_map[abi_idx].err_str[err_idx]) + return -EINVAL; + + ffa_err("%s", err_msg_map[abi_idx].err_str[err_idx]); + + return 0; +} + +/* + * Driver core functions + */ + +/** + * ffa_remove_device - removes the arm_ffa device + * @dev: the device to be removed + * + * This boot time function makes sure the arm_ffa device is removed + * No need to free the kmalloced data when the device is destroyed. + * It's automatically done by devm management by + * device_remove() -> device_free() -> devres_release_probe(). + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int ffa_remove_device(struct udevice *dev) +{ + int ret; + + if (!dev) { + ffa_err("no udevice found"); + return -ENODEV; + } + + ret = device_remove(dev, DM_REMOVE_NORMAL); + if (ret) { + ffa_err("unable to remove. err:%d\n", ret); + return ret; + } + + ffa_info("device removed and freed"); + + ret = device_unbind(dev); + if (ret) { + ffa_err("unable to unbind. err:%d\n", ret); + return ret; + } + + ffa_info("device unbound"); + + return 0; +} + +/** + * ffa_device_get - create, bind and probe the arm_ffa device + * + * This boot time function makes sure the arm_ffa device is + * created, bound to this driver, probed and ready to use. + * Arm FF-A transport is implemented through a single u-boot + * device managing the FF-A bus (arm_ffa). + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int ffa_device_get(void) +{ + int ret; + struct udevice *dev = NULL; + + ret = device_bind(dm_root(), + DM_DRIVER_GET(arm_ffa), + FFA_DRV_NAME, + NULL, + ofnode_null(), + &dev); + if (ret) + return ret; + + /* The FF-A bus discovery succeeds when probing is successful */ + ret = device_probe(dev); + if (ret) { + ffa_err("arm_ffa device probing failed"); + ffa_remove_device(dev); + return ret; + } + + return 0; +} + +/** + * ffa_get_version - FFA_VERSION handler function + * + * This is the boot time function that implements FFA_VERSION FF-A function + * to get from the secure world the FF-A framework version + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_get_version(void) +{ + u16 major, minor; + ffa_value_t res = {0}; + int ffa_errno; + + ffa_priv_data->invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_32(FFA_VERSION), + .a1 = FFA_VERSION_1_0, .a2 = 0, .a3 = 0, .a4 = 0, .a5 = 0, .a6 = 0, .a7 = 0, + }, &res); + + ffa_errno = (int)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((u32)res.a0); + minor = GET_FFA_MINOR_VERSION((u32)res.a0); + + ffa_info("FF-A driver %d.%d\nFF-A framework %d.%d", + FFA_MAJOR_VERSION, FFA_MINOR_VERSION, major, minor); + + if ((major == FFA_MAJOR_VERSION && minor >= FFA_MINOR_VERSION)) { + ffa_info("Versions are compatible "); + + ffa_priv_data->fwk_version = (u32)res.a0; + + return 0; + } + + ffa_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 + * + * This is the boot time function that implements 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(void) +{ + ffa_value_t res = {0}; + int ffa_errno; + + ffa_priv_data->invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_32(FFA_ID_GET), + .a1 = 0, .a2 = 0, .a3 = 0, .a4 = 0, .a5 = 0, .a6 = 0, .a7 = 0, + }, &res); + + if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) { + ffa_priv_data->id = GET_SELF_ENDPOINT_ID((u32)res.a2); + ffa_info("endpoint ID is %u", ffa_priv_data->id); + + return 0; + } + + ffa_errno = (int)res.a2; + + ffa_print_error_log(FFA_ID_GET, ffa_errno); + + return ffa_to_std_errno(ffa_errno); +} + +/** + * ffa_set_rxtx_buffers_pages_cnt - sets the minimum number of pages in each of the RX/TX buffers + * @prop_field: properties field obtained from FFA_FEATURES ABI + * + * This boot time function sets the minimum number of pages + * in each of the RX/TX buffers in the private data structure + * + * Return: + * + * buf_4k_pages points to the returned number of pages + * 0 on success. Otherwise, failure + */ +static int ffa_set_rxtx_buffers_pages_cnt(u32 prop_field) +{ + if (!ffa_priv_data) + return -EINVAL; + + switch (prop_field) { + case RXTX_4K: + ffa_priv_data->pair.rxtx_min_pages = 1; + break; + case RXTX_16K: + ffa_priv_data->pair.rxtx_min_pages = 4; + break; + case RXTX_64K: + ffa_priv_data->pair.rxtx_min_pages = 16; + break; + default: + ffa_err("RX/TX buffer size not supported"); + return -EINVAL; + } + + return 0; +} + +/** + * ffa_get_rxtx_map_features - FFA_FEATURES handler function with FFA_RXTX_MAP argument + * + * This is the boot time function that implements 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(void) +{ + ffa_value_t res = {0}; + int ffa_errno; + + ffa_priv_data->invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_32(FFA_FEATURES), + .a1 = FFA_SMC_64(FFA_RXTX_MAP), + .a2 = 0, .a3 = 0, .a4 = 0, .a5 = 0, .a6 = 0, .a7 = 0, + }, &res); + + if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) + return ffa_set_rxtx_buffers_pages_cnt((u32)res.a2); + + ffa_errno = (int)res.a2; + ffa_print_error_log(FFA_FEATURES, ffa_errno); + + return ffa_to_std_errno(ffa_errno); +} + +/** + * ffa_free_rxtx_buffers - frees the RX/TX buffers + * + * This is the boot time function used to free the RX/TX buffers + * + */ +static void ffa_free_rxtx_buffers(void) +{ + ffa_info("Freeing RX/TX buffers"); + + if (ffa_priv_data->pair.rxbuf) { + free((void *)ffa_priv_data->pair.rxbuf); + ffa_priv_data->pair.rxbuf = 0; + } + + if (ffa_priv_data->pair.txbuf) { + free((void *)ffa_priv_data->pair.txbuf); + ffa_priv_data->pair.txbuf = 0; + } +} + +/** + * ffa_alloc_rxtx_buffers - allocates the RX/TX buffers + * + * This is the boot time function 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. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_alloc_rxtx_buffers(void) +{ + u64 bytes; + + ffa_info("Using %lu 4KB page(s) for RX/TX buffers size", + ffa_priv_data->pair.rxtx_min_pages); + + bytes = ffa_priv_data->pair.rxtx_min_pages * SZ_4K; + + /* RX/TX buffers addresses should be PAGE_SIZE aligned */ + + ffa_priv_data->pair.rxbuf = (u64)memalign(PAGE_SIZE, bytes); + if (!ffa_priv_data->pair.rxbuf) { + ffa_err("failure to allocate RX buffer"); + return -ENOBUFS; + } + + ffa_info("RX buffer at virtual address 0x%llx", ffa_priv_data->pair.rxbuf); + + ffa_priv_data->pair.txbuf = (u64)memalign(PAGE_SIZE, bytes); + if (!ffa_priv_data->pair.txbuf) { + free((void *)ffa_priv_data->pair.rxbuf); + ffa_priv_data->pair.rxbuf = 0; + ffa_err("failure to allocate the TX buffer"); + return -ENOBUFS; + } + + ffa_info("TX buffer at virtual address 0x%llx", ffa_priv_data->pair.txbuf); + + /* + * make sure the buffers are cleared before use + */ + memset((void *)ffa_priv_data->pair.rxbuf, 0, bytes); + memset((void *)ffa_priv_data->pair.txbuf, 0, bytes); + + return 0; +} + +/** + * ffa_map_rxtx_buffers - FFA_RXTX_MAP handler function + * + * This is the boot time function that implements FFA_RXTX_MAP FF-A function + * to map the RX/TX buffers + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_map_rxtx_buffers(void) +{ + int ret; + ffa_value_t res = {0}; + int ffa_errno; + + ret = ffa_alloc_rxtx_buffers(); + 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 + */ + + ffa_priv_data->invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_64(FFA_RXTX_MAP), + .a1 = ffa_priv_data->pair.txbuf, + .a2 = ffa_priv_data->pair.rxbuf, + .a3 = ffa_priv_data->pair.rxtx_min_pages, + .a4 = 0, .a5 = 0, .a6 = 0, .a7 = 0, + }, &res); + + if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) { + ffa_info("RX/TX buffers mapped"); + return 0; + } + + ffa_errno = (int)res.a2; + ffa_print_error_log(FFA_RXTX_MAP, ffa_errno); + + ffa_free_rxtx_buffers(); + + return ffa_to_std_errno(ffa_errno); +} + +/** + * ffa_unmap_rxtx_buffers - FFA_RXTX_UNMAP handler function + * + * This is the boot time function that implements FFA_RXTX_UNMAP FF-A function + * to unmap the RX/TX buffers + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_unmap_rxtx_buffers(void) +{ + ffa_value_t res = {0}; + int ffa_errno; + + ffa_priv_data->invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_32(FFA_RXTX_UNMAP), + .a1 = PREP_SELF_ENDPOINT_ID(ffa_priv_data->id), + .a2 = 0, .a3 = 0, .a4 = 0, .a5 = 0, .a6 = 0, .a7 = 0, + }, &res); + + if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) { + ffa_free_rxtx_buffers(); + return 0; + } + + ffa_errno = (int)res.a2; + ffa_print_error_log(FFA_RXTX_UNMAP, ffa_errno); + + return ffa_to_std_errno(ffa_errno); +} + +/** + * ffa_release_rx_buffer - FFA_RX_RELEASE handler function + * + * This is the boot time function that invokes 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(void) +{ + ffa_value_t res = {0}; + int ffa_errno; + + ffa_priv_data->invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_32(FFA_RX_RELEASE), + .a1 = 0, .a2 = 0, .a3 = 0, .a4 = 0, .a5 = 0, .a6 = 0, .a7 = 0, + }, &res); + + if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) + return 0; + + ffa_errno = (int)res.a2; + ffa_print_error_log(FFA_RX_RELEASE, ffa_errno); + + return ffa_to_std_errno(ffa_errno); +} + +/** + * ffa_uuid_are_identical - checks whether two given UUIDs are identical + * @uuid1: first UUID + * @uuid2: second UUID + * + * This is a boot time function used by ffa_read_partitions_info to search + * for a UUID in the partitions descriptors table + * + * Return: + * + * 1 when UUIDs match. Otherwise, 0 + */ +int 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 - reads the data queried by FFA_PARTITION_INFO_GET + * and saves it in the private structure + * @count: The number of partitions queried + * @part_uuid: Pointer to the partition(s) UUID + * + * This is the boot time function that reads the partitions information + * returned by the FFA_PARTITION_INFO_GET and saves it in the private + * data structure. + * + * Return: + * + * The private data structure is updated with the partition(s) information + * 0 is returned on success. Otherwise, failure + */ +static int ffa_read_partitions_info(u32 count, struct ffa_partition_uuid *part_uuid) +{ + if (!count) { + ffa_err("no partition detected"); + return -ENODATA; + } + + ffa_info("Reading partitions data from the RX buffer"); + + 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 = ffa_priv_data->pair.rxtx_min_pages * SZ_4K; + + if (data_bytes > buf_bytes) { + ffa_err("partitions data size exceeds the RX buffer size:"); + ffa_err(" sizes in bytes: data %llu , RX buffer %llu ", + data_bytes, + buf_bytes); + + return -ENOMEM; + } + + ffa_priv_data->partitions.descs = (struct ffa_partition_desc *) + devm_kmalloc(ffa_priv_data->dev, data_bytes, __GFP_ZERO); + if (!ffa_priv_data->partitions.descs) { + ffa_err("cannot allocate partitions data buffer"); + return -ENOMEM; + } + + parts_info = (struct ffa_partition_info *)ffa_priv_data->pair.rxbuf; + + for (desc_idx = 0 ; desc_idx < count ; desc_idx++) { + ffa_priv_data->partitions.descs[desc_idx].info = + parts_info[desc_idx]; + + ffa_info("Partition ID %x : info cached", + ffa_priv_data->partitions.descs[desc_idx].info.id); + } + + ffa_priv_data->partitions.count = count; + + ffa_info("%d partition(s) found and cached", count); + + } else { + u32 rx_desc_idx, cached_desc_idx; + struct ffa_partition_info *parts_info; + u8 desc_found; + + parts_info = (struct ffa_partition_info *)ffa_priv_data->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 < ffa_priv_data->partitions.count; + cached_desc_idx++) { + /* + * save the UUID + */ + if (ffa_priv_data->partitions.descs[cached_desc_idx].info.id == + parts_info[rx_desc_idx].id) { + ffa_priv_data->partitions.descs[cached_desc_idx].sp_uuid = + *part_uuid; + + desc_found = 1; + break; + } + } + + if (!desc_found) + return -ENODATA; + } + } + + return 0; +} + +/** + * ffa_query_partitions_info - invokes FFA_PARTITION_INFO_GET and saves partitions data + * + * @part_uuid: Pointer to the partition(s) UUID + * @pcount: Pointer to the number of partitions variable filled when querying + * + * This is the boot time function that executes the FFA_PARTITION_INFO_GET + * to query the partitions data. Then, it calls ffa_read_partitions_info + * to save the data in the private data structure. + * + * 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 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; + } + + ffa_priv_data->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, + .a5 = 0, + .a6 = 0, + .a7 = 0, + }, &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((u32)res.a2, part_uuid); + if (ret) { + ffa_err("failed to read partition(s) data , error (%d)", ret); + ffa_release_rx_buffer(); + 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(); + + return ret; + } + + ffa_errno = (int)res.a2; + ffa_print_error_log(FFA_PARTITION_INFO_GET, ffa_errno); + + return ffa_to_std_errno(ffa_errno); +} + +/** + * ffa_get_partitions_info - FFA_PARTITION_INFO_GET handler function + * + * The passed arguments: + * Mode 1: When getting from the driver the number of + * secure partitions: + * @uuid_str: pointer to the UUID string + * @parts_size: pointer to the variable that contains the number of partitions + * The variable will be set by the driver + * @buffer: NULL + * + * Mode 2: When requesting the driver to return the + * partitions information: + * @uuid_str: pointer to the UUID string + * @parts_size: pointer to the size of the SPs information buffer in bytes + * @buffer: pointer to SPs information buffer + * (allocated by the client). + * The buffer will be filled by the driver + * + * This is the boot time function that queries the secure partition data from + * the private data structure. If not found, it invokes 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 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(). + * + * When invoked through a client request, ffa_get_partitions_info should be + * called twice. First call is to get from the driver the number of secure + * partitions (SPs) associated to a particular UUID. + * Then, the caller (client) allocates the buffer to host the SPs data and + * issues a 2nd call. Then, the driver fills the SPs data in the pre-allocated + * buffer. + * + * To achieve the mechanism described above, ffa_get_partitions_info uses the + * following functions: + * ffa_read_partitions_info + * ffa_query_partitions_info + * + * Return: + * + * @parts_size: When pointing to the number of partitions variable, the number is + * set by the driver. + * When pointing to the partitions information buffer size, the buffer will be + * filled by the driver. + * + * On success 0 is returned. Otherwise, failure + */ +static int ffa_get_partitions_info(const char *uuid_str, + u32 *parts_size, struct ffa_partition_info *buffer) +{ + /* + * fill_data: + * 0: return the SP count + * 1: fill SP data and return it to the caller + * -1: undefined mode + */ + int fill_data = -1; + u32 desc_idx, client_desc_idx; + struct ffa_partition_uuid part_uuid = {0}; + u32 client_desc_max_cnt; + u32 parts_found = 0; + + if (!ffa_priv_data->partitions.count || !ffa_priv_data->partitions.descs) { + ffa_err("no partition installed"); + return -EINVAL; + } + + if (!uuid_str) { + ffa_err("no UUID provided"); + return -EINVAL; + } + + if (!parts_size) { + ffa_err("no size/count provided"); + return -EINVAL; + } + + if (be_uuid_str_to_le_bin(uuid_str, (unsigned char *)&part_uuid)) { + ffa_err("invalid UUID"); + return -EINVAL; + } + + if (!buffer) { + /* Mode 1: getting the number of secure partitions */ + + fill_data = 0; + + ffa_info("Preparing for checking partitions count"); + + } else if ((*parts_size >= sizeof(struct ffa_partition_info)) && + !(*parts_size % sizeof(struct ffa_partition_info))) { + /* Mode 2: retrieving the partitions information */ + + fill_data = 1; + + client_desc_idx = 0; + + /* + * number of empty descriptors preallocated by the caller + */ + client_desc_max_cnt = *parts_size / sizeof(struct ffa_partition_info); + + ffa_info("Preparing for filling partitions info"); + + } else { + ffa_err("invalid function arguments provided"); + return -EINVAL; + } + + ffa_info("Searching partitions using the provided UUID"); + + /* + * search in the cached partitions + */ + for (desc_idx = 0; + desc_idx < ffa_priv_data->partitions.count; + desc_idx++) { + if (ffa_uuid_are_identical(&ffa_priv_data->partitions.descs[desc_idx].sp_uuid, + &part_uuid)) { + ffa_info("Partition ID %x matches the provided UUID", + ffa_priv_data->partitions.descs[desc_idx].info.id); + + parts_found++; + + if (fill_data) { + /* + * trying to fill the partition info in the input buffer + */ + + if (client_desc_idx < client_desc_max_cnt) { + buffer[client_desc_idx++] = + ffa_priv_data->partitions.descs[desc_idx].info; + continue; + } + + ffa_err("failed to fill the current descriptor client buffer full"); + return -ENOBUFS; + } + } + } + + if (!parts_found) { + int ret; + + ffa_info("No partition found. Querying framework ..."); + + ret = ffa_query_partitions_info(&part_uuid, &parts_found); + + if (ret == 0) { + if (!fill_data) { + *parts_size = parts_found; + + ffa_info("Number of partition(s) found matching the UUID: %d", + parts_found); + } else { + /* + * If SPs data detected, they are already in the private data + * structure, retry searching SP data again to return them + * to the caller + */ + if (parts_found) + ret = ffa_get_partitions_info(uuid_str, parts_size, buffer); + else + ret = -ENODATA; + } + } + + return ret; + } + + /* partition(s) found */ + if (!fill_data) + *parts_size = parts_found; + + return 0; +} + +/** + * ffa_cache_partitions_info - Queries and saves all secure partitions data + * + * This is a boot time function that invokes 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 the + * resident private data structure and we keep the UUID field empty + * (in FF-A 1.0 UUID is not provided by the partition descriptor) + * + * This function is 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(void) +{ + return ffa_query_partitions_info(NULL, NULL); +} + +/** + * ffa_msg_send_direct_req - FFA_MSG_SEND_DIRECT_{REQ,RESP} handler function + * @dst_part_id: destination partition ID + * @msg: pointer to the message data preallocated by the client (in/out) + * + * This is the runtime function that 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 + */ +static int __ffa_runtime ffa_msg_send_direct_req(u16 dst_part_id, struct ffa_send_direct_data *msg) +{ + ffa_value_t res = {0}; + int ffa_errno; + + if (!ffa_priv_data || !ffa_priv_data->invoke_ffa_fn) + return -EINVAL; + + /* No partition installed */ + if (!ffa_priv_data->partitions.count || !ffa_priv_data->partitions.descs) + return -ENODEV; + + ffa_priv_data->invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_64(FFA_MSG_SEND_DIRECT_REQ), + .a1 = PREP_SELF_ENDPOINT_ID(ffa_priv_data->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)) + ffa_priv_data->invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_32(FFA_RUN), + .a1 = res.a1, .a2 = 0, .a3 = 0, .a4 = 0, .a5 = 0, .a6 = 0, .a7 = 0, + }, &res); + + if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) { + /* Message sent with no response */ + return 0; + } + + if (res.a0 == FFA_SMC_64(FFA_MSG_SEND_DIRECT_RESP)) { + /* + * 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 = (int)res.a2; + return ffa_to_std_errno(ffa_errno); +} + +/** + * __arm_ffa_fn_smc - 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 + * + * Return: void + */ +void __ffa_runtime __arm_ffa_fn_smc(ffa_value_t args, ffa_value_t *res) +{ + arm_smccc_1_2_smc(&args, res); +} + +/** + * ffa_set_smc_conduit - Set the SMC conduit + * + * This boot time function selects the SMC conduit by setting the driver invoke function + * to SMC assembly function + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_set_smc_conduit(void) +{ + ffa_priv_data->invoke_ffa_fn = __arm_ffa_fn_smc; + + if (!ffa_priv_data->invoke_ffa_fn) { + ffa_err("failure to set the invoke function"); + return -EINVAL; + } + + ffa_info("Conduit is SMC"); + + return 0; +} + +/** + * ffa_set_bus_ops - Set the bus driver operations + * + * Setting the driver callbacks. + * + */ +static void ffa_set_bus_ops(void) +{ + ffa_priv_data->ffa_ops.partition_info_get = ffa_get_partitions_info; + ffa_priv_data->ffa_ops.sync_send_receive = ffa_msg_send_direct_req; + ffa_priv_data->ffa_ops.rxtx_unmap = ffa_unmap_rxtx_buffers; +} + +/** + * ffa_alloc_prvdata - allocate the driver main data structure and sets the device + * @dev: the arm_ffa device + * + * This boot time function creates the main data structure embedding all the driver data. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_alloc_prvdata(struct udevice *dev) +{ + if (!dev) { + ffa_err("no udevice found"); + return -ENODEV; + } + + /* The device is registered with the DM. Let's create the driver main data structure*/ + + ffa_priv_data = devm_kmalloc(dev, sizeof(struct ffa_prvdata), __GFP_ZERO); + if (!ffa_priv_data) { + ffa_err("can not allocate the driver main data structure"); + return -ENOMEM; + } + + ffa_priv_data->dev = dev; + + return 0; +} + +/** + * ffa_probe - The driver probe function + * @dev: the arm_ffa device + * + * Probing is done at boot time and triggered by the uclass device discovery. + * At probe level the following actions are done: + * - setting the conduit + * - querying the FF-A framework version + * - 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 the resident private data structure. + * + * The probe will fail if either FF-A framework is not detected or the + * FF-A requests are not behaving correctly. This ensures that the + * driver is not installed and its operations are not exported to the clients. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_probe(struct udevice *dev) +{ + int ret; + + ret = ffa_alloc_prvdata(dev); + if (ret != 0) + return ret; + + ffa_set_bus_ops(); + + ret = ffa_set_smc_conduit(); + if (ret != 0) + return ret; + + ret = ffa_get_version(); + if (ret != 0) + return ret; + + ret = ffa_get_endpoint_id(); + if (ret != 0) + return ret; + + ret = ffa_get_rxtx_map_features(); + if (ret != 0) + return ret; + + ret = ffa_map_rxtx_buffers(); + if (ret != 0) + return ret; + + ret = ffa_cache_partitions_info(); + if (ret != 0) { + ffa_free_rxtx_buffers(); + return ret; + } + + return 0; +} + +/** + * ffa_remove - The driver remove function + * @dev: the arm_ffa device + * When the device is about to be removed , unmap the RX/TX buffers and free the memory + * Return: + * + * 0 on success. + */ +static int ffa_remove(struct udevice *dev) +{ + ffa_info("removing the device"); + + ffa_unmap_rxtx_buffers(); + + if (ffa_priv_data->pair.rxbuf || ffa_priv_data->pair.txbuf) + ffa_free_rxtx_buffers(); + + return 0; +} + +/** + * ffa_unbind - The driver unbind function + * @dev: the arm_ffa device + * After the device is removed and memory freed the device is unbound + * Return: + * + * 0 on success. + */ +static int ffa_unbind(struct udevice *dev) +{ + ffa_info("unbinding the device , private data already released"); + + ffa_priv_data = NULL; + + return 0; +} + +/** + * ffa_bus_ops_get - bus driver operations getter + * + * Return: + * This runtime function returns a pointer to the driver operations structure + */ +const struct ffa_bus_ops * __ffa_runtime ffa_bus_ops_get(void) +{ + return &ffa_priv_data->ffa_ops; +} + +/** + * ffa_bus_prvdata_get - bus driver private data getter + * + * Return: + * This boot time function returns a pointer to the main private data structure + */ +struct ffa_prvdata **ffa_bus_prvdata_get(void) +{ + return &ffa_priv_data; +} + +/** + * ffa_bus_discover - discover FF-A bus and probe the arm_ffa device + * + * This boot time function makes sure the FF-A bus is discoverable. + * Then, the arm_ffa device is probed and ready to use. + * This function is called automatically at initcalls + * level (after u-boot relocation). + * + * When the bus was already discovered successfully the discovery will not run again. + * + * Arm FF-A transport is implemented through arm_ffa u-boot device managing the FF-A + * communication. + * All FF-A clients should use the arm_ffa device to use the FF-A transport. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int ffa_bus_discover(void) +{ + int ret = 0; + + if (!ffa_priv_data) + ret = ffa_device_get(); + + return ret; +} + +/** + * Declaring the arm_ffa driver under UCLASS_FFA + */ + +U_BOOT_DRIVER(arm_ffa) = { + .name = FFA_DRV_NAME, + .id = UCLASS_FFA, + .probe = ffa_probe, + .remove = ffa_remove, + .unbind = ffa_unbind, +}; diff --git a/drivers/firmware/arm-ffa/efi_ffa_runtime_data_mgr.c b/drivers/firmware/arm-ffa/efi_ffa_runtime_data_mgr.c new file mode 100644 index 0000000000..c76cf2147b --- /dev/null +++ b/drivers/firmware/arm-ffa/efi_ffa_runtime_data_mgr.c @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include "arm_ffa_prv.h" + +/** + * ffa_copy_runtime_data - copy the private data structure to the runtime area + * + * This boot time function copies the arm_ffa driver data structures including + * partitions data to the EFI runtime data section. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +efi_status_t ffa_copy_runtime_data(void) +{ + efi_status_t efi_ret; + efi_uintn_t prvdata_pages; + efi_uintn_t descs_pages; + struct ffa_prvdata **prvdata = NULL; /* Pointer to the current structure */ + struct ffa_prvdata *runtime_prvdata = NULL; /* Pointer to the structure runtime copy */ + u64 runtime_descs = 0; + + prvdata = ffa_bus_prvdata_get(); + + printf("INFO: EFI: FFA: prv data area at 0x%llx\n", (u64)prvdata); + + /* allocate private data runtime area */ + + prvdata_pages = efi_size_in_pages(sizeof(struct ffa_prvdata)); + efi_ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES, + EFI_RUNTIME_SERVICES_DATA, + prvdata_pages, + (u64 *)&runtime_prvdata); + + if (efi_ret != EFI_SUCCESS) { + printf("ERROR: EFI: FFA: allocating runtime data (err: 0x%lx, addr 0x%llx)\n", + efi_ret, (u64)runtime_prvdata); + + return efi_ret; + } + + printf("INFO: EFI: FFA: runtime data area at 0x%llx\n", (u64)runtime_prvdata); + + if (!runtime_prvdata) + return EFI_INVALID_PARAMETER; + + /* allocate the partition data runtime area */ + + descs_pages = efi_size_in_pages((*prvdata)->partitions.count * + sizeof(struct ffa_partition_desc)); + efi_ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES, + EFI_RUNTIME_SERVICES_DATA, + descs_pages, + &runtime_descs); + + if (efi_ret != EFI_SUCCESS) { + printf("ERROR: EFI: FFA: allocating runtime SPs data (err: 0x%lx, addr 0x%llx)\n", + efi_ret, runtime_descs); + + efi_free_pages((u64)runtime_prvdata, prvdata_pages); + + return efi_ret; + } + + printf("INFO: EFI: FFA: SPs runtime area at 0x%llx\n", (u64)runtime_descs); + + if (!runtime_descs) + return EFI_INVALID_PARAMETER; + + *runtime_prvdata = **prvdata; + + runtime_prvdata->dev = NULL; + runtime_prvdata->ffa_ops.partition_info_get = NULL; + runtime_prvdata->ffa_ops.rxtx_unmap = NULL; + runtime_prvdata->partitions.descs = (struct ffa_partition_desc *)runtime_descs; + runtime_prvdata->pair.rxbuf = 0; + runtime_prvdata->pair.txbuf = 0; + + /* + * Update the private data structure pointer in the driver + * no need to free the old structure. devm takes care of that + */ + *prvdata = runtime_prvdata; + + printf("INFO: EFI: FFA: runtime prv data now at 0x%llx , SPs count %d\n", + (u64)*prvdata, (*prvdata)->partitions.count); + + return 0; +} diff --git a/include/arm_ffa.h b/include/arm_ffa.h new file mode 100644 index 0000000000..f17b100497 --- /dev/null +++ b/include/arm_ffa.h @@ -0,0 +1,127 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __ARM_FFA_H +#define __ARM_FFA_H + +#include <linux/printk.h> + +/* + * This header is public. It can be used by clients to access + * data structures and definitions they need + */ + +/* + * Macros for displaying logs + */ + +#define ffa_info(fmt, ...) pr_info("[FFA] " fmt "\n", ##__VA_ARGS__) +#define ffa_err(fmt, ...) pr_err("[FFA] " fmt "\n", ##__VA_ARGS__) + +/* + * 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 __packed 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_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 __packed ffa_send_direct_data { + unsigned long data0; /* w3/x3 */ + unsigned long data1; /* w4/x4 */ + unsigned long data2; /* w5/x5 */ + unsigned long data3; /* w6/x6 */ + unsigned long data4; /* w7/x7 */ +}; + +#if CONFIG_IS_ENABLED(ARM_FFA_EFI_RUNTIME_MODE) + +#include <efi_loader.h> + +/* + * __ffa_runtime - controls whether functions are + * available after calling the EFI ExitBootServices service. + * Functions tagged with these keywords are resident (available at boot time and + * at runtime) + */ + +#define __ffa_runtime_data __efi_runtime_data +#define __ffa_runtime __efi_runtime + +#else + +/* + * The FF-A driver is independent from EFI + */ + +#define __ffa_runtime_data +#define __ffa_runtime + +#endif + +/** + * struct ffa_bus_ops - The driver operations structure + * @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)(const char *uuid_str, + u32 *parts_size, struct ffa_partition_info *buffer); + int (*sync_send_receive)(u16 dst_part_id, struct ffa_send_direct_data *msg); + int (*rxtx_unmap)(void); +}; + +/** + * The device driver and the Uclass driver public functions + */ + +/** + * ffa_bus_ops_get - driver operations getter + */ +const struct ffa_bus_ops * __ffa_runtime ffa_bus_ops_get(void); + +/** + * ffa_bus_discover - discover FF-A bus and probes the arm_ffa device + */ +int ffa_bus_discover(void); + +#if CONFIG_IS_ENABLED(ARM_FFA_EFI_RUNTIME_MODE) + +/** + * ffa_copy_runtime_data - copy the private data structure and the SPs data to the runtime area + */ +efi_status_t ffa_copy_runtime_data(void); + +#endif + +#endif diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index a432e43871..5dd698b7a9 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -4,6 +4,9 @@ * * (C) Copyright 2012 * Pavel Herrmann morpheus.ibis@gmail.com + * + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#ifndef _DM_UCLASS_ID_H @@ -55,6 +58,7 @@ enum uclass_id { UCLASS_EFI_MEDIA, /* Devices provided by UEFI firmware */ UCLASS_ETH, /* Ethernet device */ UCLASS_ETH_PHY, /* Ethernet PHY device */ + UCLASS_FFA, /* Arm Firmware Framework for Armv8-A */ UCLASS_FIRMWARE, /* Firmware */ UCLASS_FUZZING_ENGINE, /* Fuzzing engine */ UCLASS_FS_FIRMWARE_LOADER, /* Generic loader */ diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 6f7333638a..af0b0f3db1 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -3,6 +3,9 @@ * EFI application boot time services * * Copyright (c) 2016 Alexander Graf + * + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> @@ -23,6 +26,10 @@ #include <asm/setjmp.h> #include <linux/libfdt_env.h>
+#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT) +#include <arm_ffa.h> +#endif + DECLARE_GLOBAL_DATA_PTR;
/* Task priority level */ @@ -2178,6 +2185,14 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL); }
+#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT) + /* unmap FF-A RX/TX buffers */ + if (ffa_bus_ops_get()->rxtx_unmap()) + debug("[efi_boottime][ERROR]: can not unmap FF-A RX/TX buffers\n"); + else + debug("[efi_boottime][INFO]: FF-A RX/TX buffers unmapped\n"); +#endif + /* Patch out unsupported runtime function */ efi_runtime_detach();

On Mon, Sep 26, 2022 at 03:08:21PM +0100, Abdellatif El Khlifi wrote:
Add the driver 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.
32-bit version of the ABIs is supported and 64-bit version of FFA_RXTX_MAP and FFA_MSG_SEND_DIRECT_{REQ, RESP}.
In u-boot FF-A design, FF-A is considered as a discoverable bus. 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:
- partition_info_get
- sync_send_receive
- rxtx_unmap
This implementation provides an optional feature to copy the driver data to EFI runtime area.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v4:
- add doc/README.ffa.drv
- moving the FF-A driver work to drivers/firmware/arm-ffa
- use less #ifdefs in lib/efi_loader/efi_boottime.c and replace #if defined by #if CONFIG_IS_ENABLED
- improving error handling by mapping the FF-A errors to standard errors and logs
- replacing panics with an error log and returning an error code
- improving features discovery in FFA_FEATURES by introducing rxtx_min_pages private data field
- add ffa_remove and ffa_bind functions
- improve how the driver behaves when bus discovery is done more than once
v3:
- align the interfaces of the u-boot FF-A driver with those in the linux FF-A driver
- remove the FF-A helper layer
- make the u-boot FF-A driver independent from EFI
- provide an optional config that enables copying the driver data to EFI runtime section at ExitBootServices service
- use 64-bit version of FFA_RXTX_MAP, FFA_MSG_SEND_DIRECT_{REQ, RESP}
v2:
- make FF-A bus discoverable using device_{bind, probe} APIs
- remove device tree support
v1:
- introduce FF-A bus driver with device tree support
MAINTAINERS | 7 + common/board_r.c | 9 + doc/README.ffa.drv | 160 ++ drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/firmware/arm-ffa/Kconfig | 39 + drivers/firmware/arm-ffa/Makefile | 7 + drivers/firmware/arm-ffa/arm-ffa-uclass.c | 16 + drivers/firmware/arm-ffa/arm_ffa_prv.h | 196 +++ drivers/firmware/arm-ffa/core.c | 1344 +++++++++++++++++ .../arm-ffa/efi_ffa_runtime_data_mgr.c | 94 ++ include/arm_ffa.h | 127 ++ include/dm/uclass-id.h | 4 + lib/efi_loader/efi_boottime.c | 15 + 14 files changed, 2021 insertions(+) create mode 100644 doc/README.ffa.drv 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_prv.h create mode 100644 drivers/firmware/arm-ffa/core.c create mode 100644 drivers/firmware/arm-ffa/efi_ffa_runtime_data_mgr.c create mode 100644 include/arm_ffa.h
diff --git a/MAINTAINERS b/MAINTAINERS index 83346183ee..02b84d5074 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -248,6 +248,13 @@ 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 abdellatif.elkhlifi@arm.com +S: Maintained +F: doc/README.ffa.drv +F: drivers/firmware/arm-ffa/ +F: include/arm_ffa.h
ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de M: Fabio Estevam festevam@gmail.com diff --git a/common/board_r.c b/common/board_r.c index 56eb60fa27..8c99faddfd 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -7,6 +7,8 @@
- (C) Copyright 2002
- Sysgo Real-Time Solutions, GmbH <www.elinos.com>
- Marius Groeger mgroeger@sysgo.de
*/
- (C) Copyright 2022 ARM Limited
#include <common.h> @@ -66,6 +68,10 @@ #include <efi_loader.h> #include <relocate.h>
+#ifdef CONFIG_ARM_FFA_TRANSPORT +#include <arm_ffa.h> +#endif
DECLARE_GLOBAL_DATA_PTR;
ulong monitor_flash_len; @@ -770,6 +776,9 @@ static init_fnc_t init_sequence_r[] = { INIT_FUNC_WATCHDOG_RESET initr_net, #endif +#ifdef CONFIG_ARM_FFA_TRANSPORT
- ffa_bus_discover,
+#endif #ifdef CONFIG_POST initr_post, #endif diff --git a/doc/README.ffa.drv b/doc/README.ffa.drv new file mode 100644 index 0000000000..1c0a33deb8 --- /dev/null +++ b/doc/README.ffa.drv @@ -0,0 +1,160 @@ +Arm FF-A Driver +====================
+Introduction +--------------------
+FF-A stands for Firmware Framework for Arm A-profile processors.
+FF-A specifies interfaces that enable a pair of software sandboxes to communicate with each other. A sandbox aka partition could +be a VM in the Normal or Secure world, an application in S-EL0, or a Trusted OS in S-EL1.
These lines are a bit long, from the coding guide lines: The preferred limit on the length of a single line is 80 columns.
+This FF-A driver implements the interfaces to communicate with partitions in the Secure world aka Secure partitions (SPs).
+The driver 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.
+FF-A driver 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.
+FF-A and SMC specifications +-------------------------------------------
+The current implementation of the driver relies on FF-A specification v1.0 and uses SMC32 calling convention.
+The driver has been tested with Optee OS which supports SMC32 for most of the SMC ABIs.
+For more details please refer to: https://developer.arm.com/documentation/den0077/a/?lang=en
+The FF-A driver uses 64-bit registers as per SMCCCv1.2 specification.
+For more details please refer to: https://documentation-service.arm.com/static/5f8edaeff86e16515cdbe4c6?token=
+Supported hardware +--------------------------------
+Aarch64 plaforms
+Configuration +----------------------
+CONFIG_ARM_FFA_TRANSPORT
- Enables the FF-A bus driver. Turn this on if you want to use FF-A communication.
+CONFIG_ARM_FFA_EFI_RUNTIME_MODE
- Optional config that enables EFI runtime support for FF-A data and code.
- ffa_copy_runtime_data allows to copy the FF-A driver data structures to EFI runtime data section.
- Turning the config on makes ffa_copy_runtime_data available for use and the driver code placed at EFI runtime code section.
- Call ffa_copy_runtime_data at the event on which you want the FF-A data to be copied (example: at ExitBootServices).
+CONFIG_SANDBOX_FFA
- Enables FF-A Sandbox driver. This emulates the FF-A ABIs handling under Sandbox and provides
- functional tests for FF-A.
+FF-A ABIs under the hood +---------------------------------------
+Invoking an FF-A ABI involves providing to the secure world/hypervisor the expected arguments from the ABI.
+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 giving back to the u-boot FF-A driver (non secure world).
U-Boot, non-secure
+The driver reads the response and processes it accordingly.
+This methodology applies to all the FF-A ABIs in the driver.
+FF-A bus discovery in u-boot
U-Boot, you might as well search and replace.
+-------------------------------------------
+When CONFIG_ARM_FFA_TRANSPORT is enabled, the FF-A bus is automatically discovered at initcall level (after u-boot relocation).
+The function that triggers the discovery process is ffa_bus_discover.
+ffa_bus_discover creates, binds and probes the arm_ffa device using device_{bind, probe} APIs.
+When the device is probed, ffa_probe is called which tries to communicate with the secure world or hypervisor.
+The FF-A bus is usable when these checks succeed:
+- querying the FF-A framework version +- querying from secure world the u-boot endpoint ID +- querying from secure world the supported features of the specified FF-A calls +- mapping the RX/TX buffers +- querying from secure world all the partitions information
+Probing fails when any of these operations fail. The FF-A bus discovery succeeds when probing is successful.
+When discovery fails the arm_ffa device is destroyed.
+The bus driver layer +------------------------------
+The driver comes on top of the SMCCC layer and is implemented in drivers/firmware/arm-ffa/core.c
+The driver provides the following features:
+- 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
+- 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, +the driver takes care of how to transport that to the secure world/hypervisor using FF-A.
+- The driver provides callbacks to be used by clients to access the FF-A bus:
+partition_info_get +sync_send_receive +rxtx_unmap
+- FF-A bus discovery at initcalls level (after u-boot relocation). The bus is up and running if the FF-A framework is responsive and compatible with the driver.
+- When EFI is enabled, unmap the RX/TX buffers at ExitBootServices() level.
+- When CONFIG_ARM_FFA_EFI_RUNTIME_MODE enabled, ffa_copy_runtime_data function is available for use.
+Using armffa command +-----------------------------------
+armffa is a command showcasing how to use the FF-A driver and how to invoke its operations.
+This provides a guidance to the client developers on how to call the FF-A bus interfaces.
+Usage:
+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 the arm_ffa device info
diff --git a/drivers/Kconfig b/drivers/Kconfig index 8b6fead351..b06b1ae481 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -6,6 +6,8 @@ source "drivers/core/Kconfig"
source "drivers/adc/Kconfig"
+source "drivers/firmware/arm-ffa/Kconfig"
source "drivers/ata/Kconfig"
source "drivers/axi/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index eba9940231..c3bfad94ac 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -110,6 +110,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/arm-ffa/Kconfig b/drivers/firmware/arm-ffa/Kconfig new file mode 100644 index 0000000000..aceb61cf49 --- /dev/null +++ b/drivers/firmware/arm-ffa/Kconfig @@ -0,0 +1,39 @@ +# 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 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.
This driver 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.
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.
For more details about the FF-A driver, please refer to doc/README.ffa.drv
+config ARM_FFA_EFI_RUNTIME_MODE
- bool "Enable EFI runtime support for FF-A data and code"
- depends on ARM_FFA_TRANSPORT && EFI_LOADER
- help
Allows FF-A driver data structures and code to be accessible at EFI runtime.
FF-A data is copied by ffa_copy_runtime_data function.
The driver Code needed at runtime is placed at EFI runtime code section.
Turning this on makes ffa_copy_runtime_data available for use and the driver
code placed at EFI runtime code section.
diff --git a/drivers/firmware/arm-ffa/Makefile b/drivers/firmware/arm-ffa/Makefile new file mode 100644 index 0000000000..0b9b0a61b4 --- /dev/null +++ b/drivers/firmware/arm-ffa/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2022 Abdellatif El Khlifi abdellatif.elkhlifi@arm.com +#
+obj-y += arm-ffa-uclass.o core.o +obj-$(CONFIG_ARM_FFA_EFI_RUNTIME_MODE) += efi_ffa_runtime_data_mgr.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 0000000000..7d9695d289 --- /dev/null +++ b/drivers/firmware/arm-ffa/arm-ffa-uclass.c @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include <common.h> +#include <dm.h> +#include <asm/global_data.h>
+DECLARE_GLOBAL_DATA_PTR;
+UCLASS_DRIVER(ffa) = {
- .name = "ffa",
- .id = UCLASS_FFA,
+}; diff --git a/drivers/firmware/arm-ffa/arm_ffa_prv.h b/drivers/firmware/arm-ffa/arm_ffa_prv.h new file mode 100644 index 0000000000..7bc90f7f66 --- /dev/null +++ b/drivers/firmware/arm-ffa/arm_ffa_prv.h @@ -0,0 +1,196 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#ifndef __ARM_FFA_PRV_H +#define __ARM_FFA_PRV_H
+#include <arm_ffa.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/arm-smccc.h>
+/*
- This header is private. It is exclusively used by the FF-A driver
- */
+/* FF-A core driver name */ +#define FFA_DRV_NAME "arm_ffa"
+/* 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*/
+};
+/* number of the errors supported by the FF-A specification */ +#define MAX_NUMBER_FFA_ERR 9
+/* 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)
+/* The FF-A SMC function definitions */
+typedef struct arm_smccc_1_2_regs ffa_value_t; +typedef void (*invoke_ffa_fn_t)(ffa_value_t args, ffa_value_t *res);
+/*
- 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 */
+};
+/**
- 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 - structure hosting 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
- Data structure hosting 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 {
- u64 rxbuf; /* virtual address */
- u64 txbuf; /* virtual address */
- size_t rxtx_min_pages; /* minimum number of pages in each of the RX/TX buffers */
+};
+/**
- 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_partitions - descriptors for all secure partitions
- @count: The number of partitions descriptors
- @descs The partitions descriptors table
- This data structure contains the partitions descriptors table
- */
+struct ffa_partitions {
- u32 count;
- struct ffa_partition_desc *descs; /* virtual address */
+};
+/**
- struct ffa_prvdata - the driver private data structure
- @dev: The arm_ffa device under u-boot driver model
- @ffa_ops: The driver operations structure
- @fwk_version: FF-A framework version
- @id: u-boot endpoint ID
- @partitions: The partitions descriptors structure
- @pair: The RX/TX buffers pair
- @invoke_ffa_fn: The function executing the FF-A function
- The driver data structure hosting all resident data.
- */
+struct ffa_prvdata {
- struct udevice *dev;
- struct ffa_bus_ops ffa_ops;
- u32 fwk_version;
- u16 id;
- struct ffa_partitions partitions;
- struct ffa_rxtxpair pair;
- invoke_ffa_fn_t invoke_ffa_fn;
+};
+/**
- ffa_device_get - create, bind and probe the arm_ffa device
- */
+int ffa_device_get(void);
+/**
- ffa_bus_prvdata_get - bus driver private data getter
- */
+struct ffa_prvdata **ffa_bus_prvdata_get(void);
+#endif diff --git a/drivers/firmware/arm-ffa/core.c b/drivers/firmware/arm-ffa/core.c new file mode 100644 index 0000000000..41c7b96e68 --- /dev/null +++ b/drivers/firmware/arm-ffa/core.c @@ -0,0 +1,1344 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include "arm_ffa_prv.h" +#include <asm/global_data.h> +#include <common.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <dm/devres.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> +#include <log.h> +#include <malloc.h> +#include <string.h> +#include <uuid.h>
+DECLARE_GLOBAL_DATA_PTR;
+/**
- The device private data structure containing all the resident
- data read from secure world
- */
+__ffa_runtime_data struct ffa_prvdata *ffa_priv_data;
+/* Error mapping declarations */
+__ffa_runtime_data int ffa_to_std_errmap[MAX_NUMBER_FFA_ERR] = {
- 0,
- -EOPNOTSUPP, /* NOT_SUPPORTED */
- -EINVAL, /* INVALID_PARAMETERS */
- -ENOMEM, /* NO_MEMORY */
- -EBUSY, /* BUSY */
- -EINTR, /* INTERRUPTED */
- -EACCES, /* DENIED */
- -EAGAIN, /* RETRY */
- -ECANCELED, /* ABORTED */
+};
+struct ffa_abi_errmap err_msg_map[FFA_ERRMAP_COUNT] = {
- [FFA_ID_TO_ERRMAP_ID(FFA_VERSION)] = {
{
"",
"NOT_SUPPORTED: A Firmware Framework implementation does not exist",
"", /* INVALID_PARAMETERS */
"", /* NO_MEMORY */
"", /* BUSY */
"", /* INTERRUPTED */
"", /* DENIED */
"", /* RETRY */
"", /* ABORTED */
},
- },
- [FFA_ID_TO_ERRMAP_ID(FFA_ID_GET)] = {
{
"",
"NOT_SUPPORTED: This function is not implemented at this FF-A instance",
"", /* INVALID_PARAMETERS */
"", /* NO_MEMORY */
"", /* BUSY */
"", /* INTERRUPTED */
"", /* DENIED */
"", /* RETRY */
"", /* ABORTED */
},
- },
- [FFA_ID_TO_ERRMAP_ID(FFA_FEATURES)] = {
{
"",
"NOT_SUPPORTED: FFA_RXTX_MAP is not implemented at this FF-A instance",
"", /* INVALID_PARAMETERS */
"", /* NO_MEMORY */
"", /* BUSY */
"", /* INTERRUPTED */
"", /* DENIED */
"", /* RETRY */
"", /* ABORTED */
},
- },
- [FFA_ID_TO_ERRMAP_ID(FFA_PARTITION_INFO_GET)] = {
{
"",
"NOT_SUPPORTED: This function is not implemented at this FF-A instance",
"INVALID_PARAMETERS: Unrecognized UUID",
"NO_MEMORY: Results cannot fit in RX buffer of the caller",
"BUSY: RX buffer of the caller is not free",
"", /* INTERRUPTED */
"DENIED: Callee is not in a state to handle this request",
"", /* RETRY */
"", /* ABORTED */
},
- },
- [FFA_ID_TO_ERRMAP_ID(FFA_RXTX_UNMAP)] = {
{
"",
"NOT_SUPPORTED: FFA_RXTX_UNMAP is not implemented at this FF-A instance",
"INVALID_PARAMETERS: No buffer pair registered on behalf of the caller",
"", /* NO_MEMORY */
"", /* BUSY */
"", /* INTERRUPTED */
"", /* DENIED */
"", /* RETRY */
"", /* ABORTED */
},
- },
- [FFA_ID_TO_ERRMAP_ID(FFA_RX_RELEASE)] = {
{
"",
"NOT_SUPPORTED: FFA_RX_RELEASE is not implemented at this FF-A instance",
"", /* INVALID_PARAMETERS */
"", /* NO_MEMORY */
"", /* BUSY */
"", /* INTERRUPTED */
"DENIED: Caller did not have ownership of the RX buffer",
"", /* RETRY */
"", /* ABORTED */
},
- },
- [FFA_ID_TO_ERRMAP_ID(FFA_RXTX_MAP)] = {
{
"",
"NOT_SUPPORTED: This function is not implemented at this FF-A instance",
"INVALID_PARAMETERS: Field(s) in input parameters incorrectly encoded",
"NO_MEMORY: Not enough memory",
"", /* BUSY */
"", /* INTERRUPTED */
"DENIED: Buffer pair already registered",
"", /* RETRY */
"", /* ABORTED */
},
- },
+};
+/**
- ffa_to_std_errno - convert FF-A error code to standard error code
- @ffa_errno: Error code returned by the FF-A ABI
- This runtime function maps 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
- */
+__ffa_runtime 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
- This boot time function maps 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
- */
+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_msg_map[abi_idx].err_str[err_idx])
return -EINVAL;
- ffa_err("%s", err_msg_map[abi_idx].err_str[err_idx]);
- return 0;
+}
+/*
- Driver core functions
- */
+/**
- ffa_remove_device - removes the arm_ffa device
- @dev: the device to be removed
- This boot time function makes sure the arm_ffa device is removed
- No need to free the kmalloced data when the device is destroyed.
- It's automatically done by devm management by
- device_remove() -> device_free() -> devres_release_probe().
- Return:
- 0 on success. Otherwise, failure
- */
+int ffa_remove_device(struct udevice *dev) +{
- int ret;
- if (!dev) {
ffa_err("no udevice found");
return -ENODEV;
- }
- ret = device_remove(dev, DM_REMOVE_NORMAL);
- if (ret) {
ffa_err("unable to remove. err:%d\n", ret);
return ret;
- }
- ffa_info("device removed and freed");
- ret = device_unbind(dev);
- if (ret) {
ffa_err("unable to unbind. err:%d\n", ret);
return ret;
- }
- ffa_info("device unbound");
- return 0;
+}
+/**
- ffa_device_get - create, bind and probe the arm_ffa device
- This boot time function makes sure the arm_ffa device is
- created, bound to this driver, probed and ready to use.
- Arm FF-A transport is implemented through a single u-boot
- device managing the FF-A bus (arm_ffa).
- Return:
- 0 on success. Otherwise, failure
- */
+int ffa_device_get(void) +{
- int ret;
- struct udevice *dev = NULL;
- ret = device_bind(dm_root(),
DM_DRIVER_GET(arm_ffa),
FFA_DRV_NAME,
NULL,
ofnode_null(),
&dev);
- if (ret)
return ret;
- /* The FF-A bus discovery succeeds when probing is successful */
- ret = device_probe(dev);
- if (ret) {
ffa_err("arm_ffa device probing failed");
ffa_remove_device(dev);
return ret;
- }
- return 0;
+}
+/**
- ffa_get_version - FFA_VERSION handler function
- This is the boot time function that implements FFA_VERSION FF-A function
- to get from the secure world the FF-A framework version
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_get_version(void) +{
- u16 major, minor;
- ffa_value_t res = {0};
- int ffa_errno;
- ffa_priv_data->invoke_ffa_fn((ffa_value_t){
.a0 = FFA_SMC_32(FFA_VERSION),
.a1 = FFA_VERSION_1_0, .a2 = 0, .a3 = 0, .a4 = 0, .a5 = 0, .a6 = 0, .a7 = 0,
Why not support the latest version, 1.1?
The zero initializations are done implicitly so you could just as well remove them.
}, &res);
- ffa_errno = (int)res.a0;
Is this cast needed, or any of the other casts below in this function?
- if (ffa_errno < 0) {
ffa_print_error_log(FFA_VERSION, ffa_errno);
return ffa_to_std_errno(ffa_errno);
- }
- major = GET_FFA_MAJOR_VERSION((u32)res.a0);
- minor = GET_FFA_MINOR_VERSION((u32)res.a0);
- ffa_info("FF-A driver %d.%d\nFF-A framework %d.%d",
FFA_MAJOR_VERSION, FFA_MINOR_VERSION, major, minor);
- if ((major == FFA_MAJOR_VERSION && minor >= FFA_MINOR_VERSION)) {
ffa_info("Versions are compatible ");
ffa_priv_data->fwk_version = (u32)res.a0;
return 0;
- }
- ffa_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
- This is the boot time function that implements 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(void) +{
- ffa_value_t res = {0};
- int ffa_errno;
- ffa_priv_data->invoke_ffa_fn((ffa_value_t){
.a0 = FFA_SMC_32(FFA_ID_GET),
.a1 = 0, .a2 = 0, .a3 = 0, .a4 = 0, .a5 = 0, .a6 = 0, .a7 = 0,
}, &res);
- if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) {
ffa_priv_data->id = GET_SELF_ENDPOINT_ID((u32)res.a2);
ffa_info("endpoint ID is %u", ffa_priv_data->id);
return 0;
- }
- ffa_errno = (int)res.a2;
- ffa_print_error_log(FFA_ID_GET, ffa_errno);
- return ffa_to_std_errno(ffa_errno);
+}
+/**
- ffa_set_rxtx_buffers_pages_cnt - sets the minimum number of pages in each of the RX/TX buffers
- @prop_field: properties field obtained from FFA_FEATURES ABI
- This boot time function sets the minimum number of pages
- in each of the RX/TX buffers in the private data structure
- Return:
- buf_4k_pages points to the returned number of pages
- 0 on success. Otherwise, failure
- */
+static int ffa_set_rxtx_buffers_pages_cnt(u32 prop_field) +{
- if (!ffa_priv_data)
return -EINVAL;
- switch (prop_field) {
- case RXTX_4K:
ffa_priv_data->pair.rxtx_min_pages = 1;
break;
- case RXTX_16K:
ffa_priv_data->pair.rxtx_min_pages = 4;
break;
- case RXTX_64K:
ffa_priv_data->pair.rxtx_min_pages = 16;
break;
- default:
ffa_err("RX/TX buffer size not supported");
return -EINVAL;
- }
- return 0;
+}
+/**
- ffa_get_rxtx_map_features - FFA_FEATURES handler function with FFA_RXTX_MAP argument
- This is the boot time function that implements 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(void) +{
- ffa_value_t res = {0};
- int ffa_errno;
- ffa_priv_data->invoke_ffa_fn((ffa_value_t){
.a0 = FFA_SMC_32(FFA_FEATURES),
.a1 = FFA_SMC_64(FFA_RXTX_MAP),
.a2 = 0, .a3 = 0, .a4 = 0, .a5 = 0, .a6 = 0, .a7 = 0,
}, &res);
- if (res.a0 == FFA_SMC_32(FFA_SUCCESS))
return ffa_set_rxtx_buffers_pages_cnt((u32)res.a2);
- ffa_errno = (int)res.a2;
- ffa_print_error_log(FFA_FEATURES, ffa_errno);
- return ffa_to_std_errno(ffa_errno);
+}
+/**
- ffa_free_rxtx_buffers - frees the RX/TX buffers
- This is the boot time function used to free the RX/TX buffers
- */
+static void ffa_free_rxtx_buffers(void) +{
- ffa_info("Freeing RX/TX buffers");
- if (ffa_priv_data->pair.rxbuf) {
free((void *)ffa_priv_data->pair.rxbuf);
ffa_priv_data->pair.rxbuf = 0;
- }
- if (ffa_priv_data->pair.txbuf) {
free((void *)ffa_priv_data->pair.txbuf);
ffa_priv_data->pair.txbuf = 0;
- }
+}
+/**
- ffa_alloc_rxtx_buffers - allocates the RX/TX buffers
- This is the boot time function 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.
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_alloc_rxtx_buffers(void) +{
- u64 bytes;
- ffa_info("Using %lu 4KB page(s) for RX/TX buffers size",
ffa_priv_data->pair.rxtx_min_pages);
- bytes = ffa_priv_data->pair.rxtx_min_pages * SZ_4K;
- /* RX/TX buffers addresses should be PAGE_SIZE aligned */
- ffa_priv_data->pair.rxbuf = (u64)memalign(PAGE_SIZE, bytes);
Shouldn't this be "bytes" aligned too? Same below with the TX buffer.
- if (!ffa_priv_data->pair.rxbuf) {
ffa_err("failure to allocate RX buffer");
return -ENOBUFS;
- }
- ffa_info("RX buffer at virtual address 0x%llx", ffa_priv_data->pair.rxbuf);
- ffa_priv_data->pair.txbuf = (u64)memalign(PAGE_SIZE, bytes);
- if (!ffa_priv_data->pair.txbuf) {
free((void *)ffa_priv_data->pair.rxbuf);
ffa_priv_data->pair.rxbuf = 0;
ffa_err("failure to allocate the TX buffer");
return -ENOBUFS;
- }
- ffa_info("TX buffer at virtual address 0x%llx", ffa_priv_data->pair.txbuf);
- /*
* make sure the buffers are cleared before use
*/
- memset((void *)ffa_priv_data->pair.rxbuf, 0, bytes);
- memset((void *)ffa_priv_data->pair.txbuf, 0, bytes);
- return 0;
+}
+/**
- ffa_map_rxtx_buffers - FFA_RXTX_MAP handler function
- This is the boot time function that implements FFA_RXTX_MAP FF-A function
- to map the RX/TX buffers
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_map_rxtx_buffers(void) +{
- int ret;
- ffa_value_t res = {0};
- int ffa_errno;
- ret = ffa_alloc_rxtx_buffers();
- 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
*/
- ffa_priv_data->invoke_ffa_fn((ffa_value_t){
.a0 = FFA_SMC_64(FFA_RXTX_MAP),
.a1 = ffa_priv_data->pair.txbuf,
.a2 = ffa_priv_data->pair.rxbuf,
.a3 = ffa_priv_data->pair.rxtx_min_pages,
.a4 = 0, .a5 = 0, .a6 = 0, .a7 = 0,
}, &res);
- if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) {
ffa_info("RX/TX buffers mapped");
return 0;
- }
- ffa_errno = (int)res.a2;
- ffa_print_error_log(FFA_RXTX_MAP, ffa_errno);
- ffa_free_rxtx_buffers();
- return ffa_to_std_errno(ffa_errno);
+}
+/**
- ffa_unmap_rxtx_buffers - FFA_RXTX_UNMAP handler function
- This is the boot time function that implements FFA_RXTX_UNMAP FF-A function
- to unmap the RX/TX buffers
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_unmap_rxtx_buffers(void) +{
- ffa_value_t res = {0};
- int ffa_errno;
- ffa_priv_data->invoke_ffa_fn((ffa_value_t){
.a0 = FFA_SMC_32(FFA_RXTX_UNMAP),
.a1 = PREP_SELF_ENDPOINT_ID(ffa_priv_data->id),
.a2 = 0, .a3 = 0, .a4 = 0, .a5 = 0, .a6 = 0, .a7 = 0,
}, &res);
- if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) {
ffa_free_rxtx_buffers();
return 0;
- }
- ffa_errno = (int)res.a2;
- ffa_print_error_log(FFA_RXTX_UNMAP, ffa_errno);
- return ffa_to_std_errno(ffa_errno);
+}
+/**
- ffa_release_rx_buffer - FFA_RX_RELEASE handler function
- This is the boot time function that invokes 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(void) +{
- ffa_value_t res = {0};
- int ffa_errno;
- ffa_priv_data->invoke_ffa_fn((ffa_value_t){
.a0 = FFA_SMC_32(FFA_RX_RELEASE),
.a1 = 0, .a2 = 0, .a3 = 0, .a4 = 0, .a5 = 0, .a6 = 0, .a7 = 0,
}, &res);
- if (res.a0 == FFA_SMC_32(FFA_SUCCESS))
return 0;
- ffa_errno = (int)res.a2;
- ffa_print_error_log(FFA_RX_RELEASE, ffa_errno);
- return ffa_to_std_errno(ffa_errno);
+}
+/**
- ffa_uuid_are_identical - checks whether two given UUIDs are identical
- @uuid1: first UUID
- @uuid2: second UUID
- This is a boot time function used by ffa_read_partitions_info to search
- for a UUID in the partitions descriptors table
- Return:
- 1 when UUIDs match. Otherwise, 0
- */
+int 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)));
Please drop the outer ().
+}
+/**
- ffa_read_partitions_info - reads the data queried by FFA_PARTITION_INFO_GET
and saves it in the private structure
- @count: The number of partitions queried
- @part_uuid: Pointer to the partition(s) UUID
- This is the boot time function that reads the partitions information
- returned by the FFA_PARTITION_INFO_GET and saves it in the private
- data structure.
- Return:
- The private data structure is updated with the partition(s) information
- 0 is returned on success. Otherwise, failure
- */
+static int ffa_read_partitions_info(u32 count, struct ffa_partition_uuid *part_uuid) +{
- if (!count) {
ffa_err("no partition detected");
return -ENODATA;
- }
- ffa_info("Reading partitions data from the RX buffer");
- 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 = ffa_priv_data->pair.rxtx_min_pages * SZ_4K;
if (data_bytes > buf_bytes) {
ffa_err("partitions data size exceeds the RX buffer size:");
ffa_err(" sizes in bytes: data %llu , RX buffer %llu ",
data_bytes,
buf_bytes);
return -ENOMEM;
}
ffa_priv_data->partitions.descs = (struct ffa_partition_desc *)
devm_kmalloc(ffa_priv_data->dev, data_bytes, __GFP_ZERO);
There's no need to cast the void pointer.
if (!ffa_priv_data->partitions.descs) {
ffa_err("cannot allocate partitions data buffer");
return -ENOMEM;
}
parts_info = (struct ffa_partition_info *)ffa_priv_data->pair.rxbuf;
for (desc_idx = 0 ; desc_idx < count ; desc_idx++) {
ffa_priv_data->partitions.descs[desc_idx].info =
parts_info[desc_idx];
ffa_info("Partition ID %x : info cached",
ffa_priv_data->partitions.descs[desc_idx].info.id);
}
ffa_priv_data->partitions.count = count;
ffa_info("%d partition(s) found and cached", count);
- } else {
u32 rx_desc_idx, cached_desc_idx;
struct ffa_partition_info *parts_info;
u8 desc_found;
parts_info = (struct ffa_partition_info *)ffa_priv_data->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 < ffa_priv_data->partitions.count;
cached_desc_idx++) {
/*
* save the UUID
*/
if (ffa_priv_data->partitions.descs[cached_desc_idx].info.id ==
parts_info[rx_desc_idx].id) {
ffa_priv_data->partitions.descs[cached_desc_idx].sp_uuid =
*part_uuid;
desc_found = 1;
break;
}
}
if (!desc_found)
return -ENODATA;
}
- }
- return 0;
+}
+/**
- ffa_query_partitions_info - invokes FFA_PARTITION_INFO_GET and saves partitions data
- @part_uuid: Pointer to the partition(s) UUID
- @pcount: Pointer to the number of partitions variable filled when querying
- This is the boot time function that executes the FFA_PARTITION_INFO_GET
- to query the partitions data. Then, it calls ffa_read_partitions_info
- to save the data in the private data structure.
- 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 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;
- }
- ffa_priv_data->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,
.a5 = 0,
.a6 = 0,
.a7 = 0,
}, &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((u32)res.a2, part_uuid);
if (ret) {
ffa_err("failed to read partition(s) data , error (%d)", ret);
ffa_release_rx_buffer();
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();
return ret;
- }
- ffa_errno = (int)res.a2;
- ffa_print_error_log(FFA_PARTITION_INFO_GET, ffa_errno);
- return ffa_to_std_errno(ffa_errno);
+}
+/**
- ffa_get_partitions_info - FFA_PARTITION_INFO_GET handler function
- The passed arguments:
- Mode 1: When getting from the driver the number of
- secure partitions:
- @uuid_str: pointer to the UUID string
- @parts_size: pointer to the variable that contains the number of partitions
The variable will be set by the driver
- @buffer: NULL
- Mode 2: When requesting the driver to return the
- partitions information:
- @uuid_str: pointer to the UUID string
- @parts_size: pointer to the size of the SPs information buffer in bytes
- @buffer: pointer to SPs information buffer
(allocated by the client).
The buffer will be filled by the driver
- This is the boot time function that queries the secure partition data from
- the private data structure. If not found, it invokes 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 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().
- When invoked through a client request, ffa_get_partitions_info should be
- called twice. First call is to get from the driver the number of secure
- partitions (SPs) associated to a particular UUID.
- Then, the caller (client) allocates the buffer to host the SPs data and
- issues a 2nd call. Then, the driver fills the SPs data in the pre-allocated
- buffer.
- To achieve the mechanism described above, ffa_get_partitions_info uses the
- following functions:
ffa_read_partitions_info
ffa_query_partitions_info
- Return:
- @parts_size: When pointing to the number of partitions variable, the number is
- set by the driver.
- When pointing to the partitions information buffer size, the buffer will be
- filled by the driver.
- On success 0 is returned. Otherwise, failure
- */
+static int ffa_get_partitions_info(const char *uuid_str,
u32 *parts_size, struct ffa_partition_info *buffer)
+{
- /*
* fill_data:
* 0: return the SP count
* 1: fill SP data and return it to the caller
* -1: undefined mode
*/
- int fill_data = -1;
- u32 desc_idx, client_desc_idx;
- struct ffa_partition_uuid part_uuid = {0};
- u32 client_desc_max_cnt;
- u32 parts_found = 0;
- if (!ffa_priv_data->partitions.count || !ffa_priv_data->partitions.descs) {
ffa_err("no partition installed");
return -EINVAL;
- }
- if (!uuid_str) {
ffa_err("no UUID provided");
return -EINVAL;
- }
- if (!parts_size) {
ffa_err("no size/count provided");
return -EINVAL;
- }
- if (be_uuid_str_to_le_bin(uuid_str, (unsigned char *)&part_uuid)) {
ffa_err("invalid UUID");
return -EINVAL;
- }
- if (!buffer) {
/* Mode 1: getting the number of secure partitions */
fill_data = 0;
ffa_info("Preparing for checking partitions count");
- } else if ((*parts_size >= sizeof(struct ffa_partition_info)) &&
!(*parts_size % sizeof(struct ffa_partition_info))) {
/* Mode 2: retrieving the partitions information */
fill_data = 1;
client_desc_idx = 0;
/*
* number of empty descriptors preallocated by the caller
*/
client_desc_max_cnt = *parts_size / sizeof(struct ffa_partition_info);
ffa_info("Preparing for filling partitions info");
- } else {
ffa_err("invalid function arguments provided");
return -EINVAL;
- }
- ffa_info("Searching partitions using the provided UUID");
- /*
* search in the cached partitions
*/
- for (desc_idx = 0;
desc_idx < ffa_priv_data->partitions.count;
desc_idx++) {
if (ffa_uuid_are_identical(&ffa_priv_data->partitions.descs[desc_idx].sp_uuid,
&part_uuid)) {
ffa_info("Partition ID %x matches the provided UUID",
ffa_priv_data->partitions.descs[desc_idx].info.id);
parts_found++;
if (fill_data) {
/*
* trying to fill the partition info in the input buffer
*/
if (client_desc_idx < client_desc_max_cnt) {
buffer[client_desc_idx++] =
ffa_priv_data->partitions.descs[desc_idx].info;
continue;
}
ffa_err("failed to fill the current descriptor client buffer full");
return -ENOBUFS;
}
}
- }
- if (!parts_found) {
int ret;
ffa_info("No partition found. Querying framework ...");
ret = ffa_query_partitions_info(&part_uuid, &parts_found);
if (ret == 0) {
if (!fill_data) {
*parts_size = parts_found;
ffa_info("Number of partition(s) found matching the UUID: %d",
parts_found);
} else {
/*
* If SPs data detected, they are already in the private data
* structure, retry searching SP data again to return them
* to the caller
*/
if (parts_found)
ret = ffa_get_partitions_info(uuid_str, parts_size, buffer);
else
ret = -ENODATA;
}
}
return ret;
- }
- /* partition(s) found */
- if (!fill_data)
*parts_size = parts_found;
- return 0;
+}
+/**
- ffa_cache_partitions_info - Queries and saves all secure partitions data
- This is a boot time function that invokes 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 the
- resident private data structure and we keep the UUID field empty
- (in FF-A 1.0 UUID is not provided by the partition descriptor)
- This function is 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(void) +{
- return ffa_query_partitions_info(NULL, NULL);
+}
+/**
- ffa_msg_send_direct_req - FFA_MSG_SEND_DIRECT_{REQ,RESP} handler function
- @dst_part_id: destination partition ID
- @msg: pointer to the message data preallocated by the client (in/out)
- This is the runtime function that 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
- */
+static int __ffa_runtime ffa_msg_send_direct_req(u16 dst_part_id, struct ffa_send_direct_data *msg) +{
- ffa_value_t res = {0};
- int ffa_errno;
- if (!ffa_priv_data || !ffa_priv_data->invoke_ffa_fn)
return -EINVAL;
- /* No partition installed */
- if (!ffa_priv_data->partitions.count || !ffa_priv_data->partitions.descs)
return -ENODEV;
- ffa_priv_data->invoke_ffa_fn((ffa_value_t){
.a0 = FFA_SMC_64(FFA_MSG_SEND_DIRECT_REQ),
.a1 = PREP_SELF_ENDPOINT_ID(ffa_priv_data->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))
ffa_priv_data->invoke_ffa_fn((ffa_value_t){
.a0 = FFA_SMC_32(FFA_RUN),
.a1 = res.a1, .a2 = 0, .a3 = 0, .a4 = 0, .a5 = 0, .a6 = 0, .a7 = 0,
}, &res);
- if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) {
How can the caller tell this response apart from a FFA_MSG_SEND_DIRECT_RESP?
/* Message sent with no response */
return 0;
- }
- if (res.a0 == FFA_SMC_64(FFA_MSG_SEND_DIRECT_RESP)) {
/*
* 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 = (int)res.a2;
- return ffa_to_std_errno(ffa_errno);
+}
+/**
- __arm_ffa_fn_smc - 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
- Return: void
- */
+void __ffa_runtime __arm_ffa_fn_smc(ffa_value_t args, ffa_value_t *res) +{
- arm_smccc_1_2_smc(&args, res);
+}
+/**
- ffa_set_smc_conduit - Set the SMC conduit
- This boot time function selects the SMC conduit by setting the driver invoke function
- to SMC assembly function
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_set_smc_conduit(void) +{
- ffa_priv_data->invoke_ffa_fn = __arm_ffa_fn_smc;
- if (!ffa_priv_data->invoke_ffa_fn) {
ffa_err("failure to set the invoke function");
return -EINVAL;
- }
- ffa_info("Conduit is SMC");
- return 0;
+}
+/**
- ffa_set_bus_ops - Set the bus driver operations
- Setting the driver callbacks.
- */
+static void ffa_set_bus_ops(void) +{
- ffa_priv_data->ffa_ops.partition_info_get = ffa_get_partitions_info;
- ffa_priv_data->ffa_ops.sync_send_receive = ffa_msg_send_direct_req;
- ffa_priv_data->ffa_ops.rxtx_unmap = ffa_unmap_rxtx_buffers;
+}
+/**
- ffa_alloc_prvdata - allocate the driver main data structure and sets the device
- @dev: the arm_ffa device
- This boot time function creates the main data structure embedding all the driver data.
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_alloc_prvdata(struct udevice *dev) +{
- if (!dev) {
ffa_err("no udevice found");
return -ENODEV;
- }
- /* The device is registered with the DM. Let's create the driver main data structure*/
- ffa_priv_data = devm_kmalloc(dev, sizeof(struct ffa_prvdata), __GFP_ZERO);
- if (!ffa_priv_data) {
ffa_err("can not allocate the driver main data structure");
return -ENOMEM;
- }
- ffa_priv_data->dev = dev;
- return 0;
+}
+/**
- ffa_probe - The driver probe function
- @dev: the arm_ffa device
- Probing is done at boot time and triggered by the uclass device discovery.
- At probe level the following actions are done:
- setting the conduit
- querying the FF-A framework version
- 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 the resident private data structure.
- The probe will fail if either FF-A framework is not detected or the
- FF-A requests are not behaving correctly. This ensures that the
- driver is not installed and its operations are not exported to the clients.
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_probe(struct udevice *dev) +{
- int ret;
- ret = ffa_alloc_prvdata(dev);
- if (ret != 0)
return ret;
- ffa_set_bus_ops();
- ret = ffa_set_smc_conduit();
What if there's a hypervisor involved and HVC would be expected intead?
- if (ret != 0)
return ret;
- ret = ffa_get_version();
- if (ret != 0)
return ret;
- ret = ffa_get_endpoint_id();
- if (ret != 0)
return ret;
- ret = ffa_get_rxtx_map_features();
- if (ret != 0)
return ret;
- ret = ffa_map_rxtx_buffers();
- if (ret != 0)
return ret;
- ret = ffa_cache_partitions_info();
Why are we saving all the found partitions in a cache? It seems that FFA_PARTITION_INFO_GET could be called each time when needed instead without any noticeable overhead. Or is the result cached for some other reason?
- if (ret != 0) {
ffa_free_rxtx_buffers();
return ret;
- }
- return 0;
+}
+/**
- ffa_remove - The driver remove function
- @dev: the arm_ffa device
- When the device is about to be removed , unmap the RX/TX buffers and free the memory
- Return:
- 0 on success.
- */
+static int ffa_remove(struct udevice *dev) +{
- ffa_info("removing the device");
- ffa_unmap_rxtx_buffers();
- if (ffa_priv_data->pair.rxbuf || ffa_priv_data->pair.txbuf)
ffa_free_rxtx_buffers();
- return 0;
+}
+/**
- ffa_unbind - The driver unbind function
- @dev: the arm_ffa device
- After the device is removed and memory freed the device is unbound
- Return:
- 0 on success.
- */
+static int ffa_unbind(struct udevice *dev) +{
- ffa_info("unbinding the device , private data already released");
- ffa_priv_data = NULL;
- return 0;
+}
+/**
- ffa_bus_ops_get - bus driver operations getter
- Return:
- This runtime function returns a pointer to the driver operations structure
- */
+const struct ffa_bus_ops * __ffa_runtime ffa_bus_ops_get(void) +{
- return &ffa_priv_data->ffa_ops;
+}
+/**
- ffa_bus_prvdata_get - bus driver private data getter
- Return:
- This boot time function returns a pointer to the main private data structure
- */
+struct ffa_prvdata **ffa_bus_prvdata_get(void) +{
- return &ffa_priv_data;
+}
+/**
- ffa_bus_discover - discover FF-A bus and probe the arm_ffa device
- This boot time function makes sure the FF-A bus is discoverable.
- Then, the arm_ffa device is probed and ready to use.
- This function is called automatically at initcalls
- level (after u-boot relocation).
- When the bus was already discovered successfully the discovery will not run again.
- Arm FF-A transport is implemented through arm_ffa u-boot device managing the FF-A
- communication.
- All FF-A clients should use the arm_ffa device to use the FF-A transport.
- Return:
- 0 on success. Otherwise, failure
- */
+int ffa_bus_discover(void) +{
- int ret = 0;
- if (!ffa_priv_data)
ret = ffa_device_get();
- return ret;
+}
+/**
- Declaring the arm_ffa driver under UCLASS_FFA
- */
+U_BOOT_DRIVER(arm_ffa) = {
- .name = FFA_DRV_NAME,
- .id = UCLASS_FFA,
- .probe = ffa_probe,
- .remove = ffa_remove,
- .unbind = ffa_unbind,
+}; diff --git a/drivers/firmware/arm-ffa/efi_ffa_runtime_data_mgr.c b/drivers/firmware/arm-ffa/efi_ffa_runtime_data_mgr.c new file mode 100644 index 0000000000..c76cf2147b --- /dev/null +++ b/drivers/firmware/arm-ffa/efi_ffa_runtime_data_mgr.c @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include "arm_ffa_prv.h"
+/**
- ffa_copy_runtime_data - copy the private data structure to the runtime area
- This boot time function copies the arm_ffa driver data structures including
- partitions data to the EFI runtime data section.
- Return:
- 0 on success. Otherwise, failure
- */
+efi_status_t ffa_copy_runtime_data(void) +{
- efi_status_t efi_ret;
- efi_uintn_t prvdata_pages;
- efi_uintn_t descs_pages;
- struct ffa_prvdata **prvdata = NULL; /* Pointer to the current structure */
- struct ffa_prvdata *runtime_prvdata = NULL; /* Pointer to the structure runtime copy */
- u64 runtime_descs = 0;
- prvdata = ffa_bus_prvdata_get();
- printf("INFO: EFI: FFA: prv data area at 0x%llx\n", (u64)prvdata);
- /* allocate private data runtime area */
- prvdata_pages = efi_size_in_pages(sizeof(struct ffa_prvdata));
- efi_ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
EFI_RUNTIME_SERVICES_DATA,
prvdata_pages,
(u64 *)&runtime_prvdata);
Better to use a temporary u64 instead of casting the pointer.
- if (efi_ret != EFI_SUCCESS) {
printf("ERROR: EFI: FFA: allocating runtime data (err: 0x%lx, addr 0x%llx)\n",
efi_ret, (u64)runtime_prvdata);
return efi_ret;
- }
- printf("INFO: EFI: FFA: runtime data area at 0x%llx\n", (u64)runtime_prvdata);
- if (!runtime_prvdata)
return EFI_INVALID_PARAMETER;
- /* allocate the partition data runtime area */
- descs_pages = efi_size_in_pages((*prvdata)->partitions.count *
sizeof(struct ffa_partition_desc));
- efi_ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
EFI_RUNTIME_SERVICES_DATA,
descs_pages,
&runtime_descs);
- if (efi_ret != EFI_SUCCESS) {
printf("ERROR: EFI: FFA: allocating runtime SPs data (err: 0x%lx, addr 0x%llx)\n",
efi_ret, runtime_descs);
efi_free_pages((u64)runtime_prvdata, prvdata_pages);
return efi_ret;
- }
- printf("INFO: EFI: FFA: SPs runtime area at 0x%llx\n", (u64)runtime_descs);
- if (!runtime_descs)
return EFI_INVALID_PARAMETER;
- *runtime_prvdata = **prvdata;
With this you copy fwk_versien. This field will become out of data if the kernel driver negotiates a different framework version.
- runtime_prvdata->dev = NULL;
- runtime_prvdata->ffa_ops.partition_info_get = NULL;
- runtime_prvdata->ffa_ops.rxtx_unmap = NULL;
- runtime_prvdata->partitions.descs = (struct ffa_partition_desc *)runtime_descs;
- runtime_prvdata->pair.rxbuf = 0;
- runtime_prvdata->pair.txbuf = 0;
- /*
* Update the private data structure pointer in the driver
* no need to free the old structure. devm takes care of that
*/
- *prvdata = runtime_prvdata;
- printf("INFO: EFI: FFA: runtime prv data now at 0x%llx , SPs count %d\n",
(u64)*prvdata, (*prvdata)->partitions.count);
- return 0;
+} diff --git a/include/arm_ffa.h b/include/arm_ffa.h new file mode 100644 index 0000000000..f17b100497 --- /dev/null +++ b/include/arm_ffa.h @@ -0,0 +1,127 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#ifndef __ARM_FFA_H +#define __ARM_FFA_H
+#include <linux/printk.h>
+/*
- This header is public. It can be used by clients to access
- data structures and definitions they need
- */
+/*
- Macros for displaying logs
- */
+#define ffa_info(fmt, ...) pr_info("[FFA] " fmt "\n", ##__VA_ARGS__) +#define ffa_err(fmt, ...) pr_err("[FFA] " fmt "\n", ##__VA_ARGS__)
+/*
- 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 __packed 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_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 __packed ffa_send_direct_data {
- unsigned long data0; /* w3/x3 */
- unsigned long data1; /* w4/x4 */
- unsigned long data2; /* w5/x5 */
- unsigned long data3; /* w6/x6 */
- unsigned long data4; /* w7/x7 */
+};
+#if CONFIG_IS_ENABLED(ARM_FFA_EFI_RUNTIME_MODE)
+#include <efi_loader.h>
+/*
- __ffa_runtime - controls whether functions are
- available after calling the EFI ExitBootServices service.
- Functions tagged with these keywords are resident (available at boot time and
- at runtime)
- */
+#define __ffa_runtime_data __efi_runtime_data +#define __ffa_runtime __efi_runtime
+#else
+/*
- The FF-A driver is independent from EFI
- */
+#define __ffa_runtime_data +#define __ffa_runtime
+#endif
+/**
- struct ffa_bus_ops - The driver operations structure
- @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)(const char *uuid_str,
u32 *parts_size, struct ffa_partition_info *buffer);
- int (*sync_send_receive)(u16 dst_part_id, struct ffa_send_direct_data *msg);
- int (*rxtx_unmap)(void);
+};
+/**
- The device driver and the Uclass driver public functions
- */
+/**
- ffa_bus_ops_get - driver operations getter
- */
+const struct ffa_bus_ops * __ffa_runtime ffa_bus_ops_get(void);
+/**
- ffa_bus_discover - discover FF-A bus and probes the arm_ffa device
- */
+int ffa_bus_discover(void);
+#if CONFIG_IS_ENABLED(ARM_FFA_EFI_RUNTIME_MODE)
+/**
- ffa_copy_runtime_data - copy the private data structure and the SPs data to the runtime area
- */
+efi_status_t ffa_copy_runtime_data(void);
+#endif
+#endif diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index a432e43871..5dd698b7a9 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -4,6 +4,9 @@
- (C) Copyright 2012
- Pavel Herrmann morpheus.ibis@gmail.com
- (C) Copyright 2022 ARM Limited
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#ifndef _DM_UCLASS_ID_H @@ -55,6 +58,7 @@ enum uclass_id { UCLASS_EFI_MEDIA, /* Devices provided by UEFI firmware */ UCLASS_ETH, /* Ethernet device */ UCLASS_ETH_PHY, /* Ethernet PHY device */
- UCLASS_FFA, /* Arm Firmware Framework for Armv8-A */ UCLASS_FIRMWARE, /* Firmware */ UCLASS_FUZZING_ENGINE, /* Fuzzing engine */ UCLASS_FS_FIRMWARE_LOADER, /* Generic loader */
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 6f7333638a..af0b0f3db1 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -3,6 +3,9 @@
- EFI application boot time services
- Copyright (c) 2016 Alexander Graf
- (C) Copyright 2022 ARM Limited
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#include <common.h> @@ -23,6 +26,10 @@ #include <asm/setjmp.h> #include <linux/libfdt_env.h>
+#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT) +#include <arm_ffa.h> +#endif
DECLARE_GLOBAL_DATA_PTR;
/* Task priority level */ @@ -2178,6 +2185,14 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL); }
+#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT)
/* unmap FF-A RX/TX buffers */
if (ffa_bus_ops_get()->rxtx_unmap())
debug("[efi_boottime][ERROR]: can not unmap FF-A RX/TX buffers\n");
else
debug("[efi_boottime][INFO]: FF-A RX/TX buffers unmapped\n");
+#endif
- /* Patch out unsupported runtime function */ efi_runtime_detach();
-- 2.17.1

On Mon, Oct 03, 2022 at 10:49:23AM +0200, Jens Wiklander wrote:
On Mon, Sep 26, 2022 at 03:08:21PM +0100, Abdellatif El Khlifi wrote:
[snip]
diff --git a/doc/README.ffa.drv b/doc/README.ffa.drv new file mode 100644 index 0000000000..1c0a33deb8 --- /dev/null +++ b/doc/README.ffa.drv @@ -0,0 +1,160 @@ +Arm FF-A Driver +====================
+Introduction +--------------------
+FF-A stands for Firmware Framework for Arm A-profile processors.
+FF-A specifies interfaces that enable a pair of software sandboxes to communicate with each other. A sandbox aka partition could +be a VM in the Normal or Secure world, an application in S-EL0, or a Trusted OS in S-EL1.
These lines are a bit long, from the coding guide lines: The preferred limit on the length of a single line is 80 columns.
Line length isn't a huge problem, we follow what the linux kernel allows these days. But, this needs to be in proper sphinx format, under doc/arch perhaps (unless Heinrich has another idea) and 'make htmldocs' must pass (I see a lot of formatting errors that will get tripped up on and fail).

On Mon, Oct 03, 2022 at 11:22:33AM -0400, Tom Rini wrote:
On Mon, Oct 03, 2022 at 10:49:23AM +0200, Jens Wiklander wrote:
On Mon, Sep 26, 2022 at 03:08:21PM +0100, Abdellatif El Khlifi wrote:
[snip]
diff --git a/doc/README.ffa.drv b/doc/README.ffa.drv new file mode 100644 index 0000000000..1c0a33deb8 --- /dev/null +++ b/doc/README.ffa.drv @@ -0,0 +1,160 @@ +Arm FF-A Driver +====================
+Introduction +--------------------
+FF-A stands for Firmware Framework for Arm A-profile processors.
+FF-A specifies interfaces that enable a pair of software sandboxes to communicate with each other. A sandbox aka partition could +be a VM in the Normal or Secure world, an application in S-EL0, or a Trusted OS in S-EL1.
These lines are a bit long, from the coding guide lines: The preferred limit on the length of a single line is 80 columns.
Line length isn't a huge problem, we follow what the linux kernel allows these days. But, this needs to be in proper sphinx format, under doc/arch perhaps (unless Heinrich has another idea) and 'make htmldocs' must pass (I see a lot of formatting errors that will get tripped up on and fail).
In patchset v6, FF-A documentation is at doc/arch/arm64.ffa.rst and comes with Sphinx format. 'make htmldocs' passes.
-- Tom

On Mon, Oct 03, 2022 at 10:49:23AM +0200, Jens Wiklander wrote:
On Mon, Sep 26, 2022 at 03:08:21PM +0100, Abdellatif El Khlifi wrote:
Add the driver 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.
32-bit version of the ABIs is supported and 64-bit version of FFA_RXTX_MAP and FFA_MSG_SEND_DIRECT_{REQ, RESP}.
In u-boot FF-A design, FF-A is considered as a discoverable bus. 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:
- partition_info_get
- sync_send_receive
- rxtx_unmap
This implementation provides an optional feature to copy the driver data to EFI runtime area.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v4:
- add doc/README.ffa.drv
- moving the FF-A driver work to drivers/firmware/arm-ffa
- use less #ifdefs in lib/efi_loader/efi_boottime.c and replace #if defined by #if CONFIG_IS_ENABLED
- improving error handling by mapping the FF-A errors to standard errors and logs
- replacing panics with an error log and returning an error code
- improving features discovery in FFA_FEATURES by introducing rxtx_min_pages private data field
- add ffa_remove and ffa_bind functions
- improve how the driver behaves when bus discovery is done more than once
v3:
- align the interfaces of the u-boot FF-A driver with those in the linux FF-A driver
- remove the FF-A helper layer
- make the u-boot FF-A driver independent from EFI
- provide an optional config that enables copying the driver data to EFI runtime section at ExitBootServices service
- use 64-bit version of FFA_RXTX_MAP, FFA_MSG_SEND_DIRECT_{REQ, RESP}
v2:
- make FF-A bus discoverable using device_{bind, probe} APIs
- remove device tree support
v1:
- introduce FF-A bus driver with device tree support
MAINTAINERS | 7 + common/board_r.c | 9 + doc/README.ffa.drv | 160 ++ drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/firmware/arm-ffa/Kconfig | 39 + drivers/firmware/arm-ffa/Makefile | 7 + drivers/firmware/arm-ffa/arm-ffa-uclass.c | 16 + drivers/firmware/arm-ffa/arm_ffa_prv.h | 196 +++ drivers/firmware/arm-ffa/core.c | 1344 +++++++++++++++++ .../arm-ffa/efi_ffa_runtime_data_mgr.c | 94 ++ include/arm_ffa.h | 127 ++ include/dm/uclass-id.h | 4 + lib/efi_loader/efi_boottime.c | 15 + 14 files changed, 2021 insertions(+) create mode 100644 doc/README.ffa.drv 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_prv.h create mode 100644 drivers/firmware/arm-ffa/core.c create mode 100644 drivers/firmware/arm-ffa/efi_ffa_runtime_data_mgr.c create mode 100644 include/arm_ffa.h
diff --git a/MAINTAINERS b/MAINTAINERS index 83346183ee..02b84d5074 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -248,6 +248,13 @@ 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 abdellatif.elkhlifi@arm.com +S: Maintained +F: doc/README.ffa.drv +F: drivers/firmware/arm-ffa/ +F: include/arm_ffa.h
ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de M: Fabio Estevam festevam@gmail.com diff --git a/common/board_r.c b/common/board_r.c index 56eb60fa27..8c99faddfd 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -7,6 +7,8 @@
- (C) Copyright 2002
- Sysgo Real-Time Solutions, GmbH <www.elinos.com>
- Marius Groeger mgroeger@sysgo.de
*/
- (C) Copyright 2022 ARM Limited
#include <common.h> @@ -66,6 +68,10 @@ #include <efi_loader.h> #include <relocate.h>
+#ifdef CONFIG_ARM_FFA_TRANSPORT +#include <arm_ffa.h> +#endif
DECLARE_GLOBAL_DATA_PTR;
ulong monitor_flash_len; @@ -770,6 +776,9 @@ static init_fnc_t init_sequence_r[] = { INIT_FUNC_WATCHDOG_RESET initr_net, #endif +#ifdef CONFIG_ARM_FFA_TRANSPORT
- ffa_bus_discover,
+#endif #ifdef CONFIG_POST initr_post, #endif diff --git a/doc/README.ffa.drv b/doc/README.ffa.drv new file mode 100644 index 0000000000..1c0a33deb8 --- /dev/null +++ b/doc/README.ffa.drv @@ -0,0 +1,160 @@ +Arm FF-A Driver +====================
+Introduction +--------------------
+FF-A stands for Firmware Framework for Arm A-profile processors.
+FF-A specifies interfaces that enable a pair of software sandboxes to communicate with each other. A sandbox aka partition could +be a VM in the Normal or Secure world, an application in S-EL0, or a Trusted OS in S-EL1.
These lines are a bit long, from the coding guide lines: The preferred limit on the length of a single line is 80 columns.
+This FF-A driver implements the interfaces to communicate with partitions in the Secure world aka Secure partitions (SPs).
+The driver 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.
+FF-A driver 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.
+FF-A and SMC specifications +-------------------------------------------
+The current implementation of the driver relies on FF-A specification v1.0 and uses SMC32 calling convention.
+The driver has been tested with Optee OS which supports SMC32 for most of the SMC ABIs.
+For more details please refer to: https://developer.arm.com/documentation/den0077/a/?lang=en
+The FF-A driver uses 64-bit registers as per SMCCCv1.2 specification.
+For more details please refer to: https://documentation-service.arm.com/static/5f8edaeff86e16515cdbe4c6?token=
+Supported hardware +--------------------------------
+Aarch64 plaforms
+Configuration +----------------------
+CONFIG_ARM_FFA_TRANSPORT
- Enables the FF-A bus driver. Turn this on if you want to use FF-A communication.
+CONFIG_ARM_FFA_EFI_RUNTIME_MODE
- Optional config that enables EFI runtime support for FF-A data and code.
- ffa_copy_runtime_data allows to copy the FF-A driver data structures to EFI runtime data section.
- Turning the config on makes ffa_copy_runtime_data available for use and the driver code placed at EFI runtime code section.
- Call ffa_copy_runtime_data at the event on which you want the FF-A data to be copied (example: at ExitBootServices).
+CONFIG_SANDBOX_FFA
- Enables FF-A Sandbox driver. This emulates the FF-A ABIs handling under Sandbox and provides
- functional tests for FF-A.
+FF-A ABIs under the hood +---------------------------------------
+Invoking an FF-A ABI involves providing to the secure world/hypervisor the expected arguments from the ABI.
+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 giving back to the u-boot FF-A driver (non secure world).
U-Boot, non-secure
+The driver reads the response and processes it accordingly.
+This methodology applies to all the FF-A ABIs in the driver.
+FF-A bus discovery in u-boot
U-Boot, you might as well search and replace.
+-------------------------------------------
+When CONFIG_ARM_FFA_TRANSPORT is enabled, the FF-A bus is automatically discovered at initcall level (after u-boot relocation).
+The function that triggers the discovery process is ffa_bus_discover.
+ffa_bus_discover creates, binds and probes the arm_ffa device using device_{bind, probe} APIs.
+When the device is probed, ffa_probe is called which tries to communicate with the secure world or hypervisor.
+The FF-A bus is usable when these checks succeed:
+- querying the FF-A framework version +- querying from secure world the u-boot endpoint ID +- querying from secure world the supported features of the specified FF-A calls +- mapping the RX/TX buffers +- querying from secure world all the partitions information
+Probing fails when any of these operations fail. The FF-A bus discovery succeeds when probing is successful.
+When discovery fails the arm_ffa device is destroyed.
+The bus driver layer +------------------------------
+The driver comes on top of the SMCCC layer and is implemented in drivers/firmware/arm-ffa/core.c
+The driver provides the following features:
+- 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
+- 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, +the driver takes care of how to transport that to the secure world/hypervisor using FF-A.
+- The driver provides callbacks to be used by clients to access the FF-A bus:
+partition_info_get +sync_send_receive +rxtx_unmap
+- FF-A bus discovery at initcalls level (after u-boot relocation). The bus is up and running if the FF-A framework is responsive and compatible with the driver.
+- When EFI is enabled, unmap the RX/TX buffers at ExitBootServices() level.
+- When CONFIG_ARM_FFA_EFI_RUNTIME_MODE enabled, ffa_copy_runtime_data function is available for use.
+Using armffa command +-----------------------------------
+armffa is a command showcasing how to use the FF-A driver and how to invoke its operations.
+This provides a guidance to the client developers on how to call the FF-A bus interfaces.
+Usage:
+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 the arm_ffa device info
diff --git a/drivers/Kconfig b/drivers/Kconfig index 8b6fead351..b06b1ae481 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -6,6 +6,8 @@ source "drivers/core/Kconfig"
source "drivers/adc/Kconfig"
+source "drivers/firmware/arm-ffa/Kconfig"
source "drivers/ata/Kconfig"
source "drivers/axi/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index eba9940231..c3bfad94ac 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -110,6 +110,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/arm-ffa/Kconfig b/drivers/firmware/arm-ffa/Kconfig new file mode 100644 index 0000000000..aceb61cf49 --- /dev/null +++ b/drivers/firmware/arm-ffa/Kconfig @@ -0,0 +1,39 @@ +# 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 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.
This driver 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.
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.
For more details about the FF-A driver, please refer to doc/README.ffa.drv
+config ARM_FFA_EFI_RUNTIME_MODE
- bool "Enable EFI runtime support for FF-A data and code"
- depends on ARM_FFA_TRANSPORT && EFI_LOADER
- help
Allows FF-A driver data structures and code to be accessible at EFI runtime.
FF-A data is copied by ffa_copy_runtime_data function.
The driver Code needed at runtime is placed at EFI runtime code section.
Turning this on makes ffa_copy_runtime_data available for use and the driver
code placed at EFI runtime code section.
diff --git a/drivers/firmware/arm-ffa/Makefile b/drivers/firmware/arm-ffa/Makefile new file mode 100644 index 0000000000..0b9b0a61b4 --- /dev/null +++ b/drivers/firmware/arm-ffa/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2022 Abdellatif El Khlifi abdellatif.elkhlifi@arm.com +#
+obj-y += arm-ffa-uclass.o core.o +obj-$(CONFIG_ARM_FFA_EFI_RUNTIME_MODE) += efi_ffa_runtime_data_mgr.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 0000000000..7d9695d289 --- /dev/null +++ b/drivers/firmware/arm-ffa/arm-ffa-uclass.c @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include <common.h> +#include <dm.h> +#include <asm/global_data.h>
+DECLARE_GLOBAL_DATA_PTR;
+UCLASS_DRIVER(ffa) = {
- .name = "ffa",
- .id = UCLASS_FFA,
+}; diff --git a/drivers/firmware/arm-ffa/arm_ffa_prv.h b/drivers/firmware/arm-ffa/arm_ffa_prv.h new file mode 100644 index 0000000000..7bc90f7f66 --- /dev/null +++ b/drivers/firmware/arm-ffa/arm_ffa_prv.h @@ -0,0 +1,196 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#ifndef __ARM_FFA_PRV_H +#define __ARM_FFA_PRV_H
+#include <arm_ffa.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/arm-smccc.h>
+/*
- This header is private. It is exclusively used by the FF-A driver
- */
+/* FF-A core driver name */ +#define FFA_DRV_NAME "arm_ffa"
+/* 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*/
+};
+/* number of the errors supported by the FF-A specification */ +#define MAX_NUMBER_FFA_ERR 9
+/* 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)
+/* The FF-A SMC function definitions */
+typedef struct arm_smccc_1_2_regs ffa_value_t; +typedef void (*invoke_ffa_fn_t)(ffa_value_t args, ffa_value_t *res);
+/*
- 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 */
+};
+/**
- 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 - structure hosting 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
- Data structure hosting 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 {
- u64 rxbuf; /* virtual address */
- u64 txbuf; /* virtual address */
- size_t rxtx_min_pages; /* minimum number of pages in each of the RX/TX buffers */
+};
+/**
- 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_partitions - descriptors for all secure partitions
- @count: The number of partitions descriptors
- @descs The partitions descriptors table
- This data structure contains the partitions descriptors table
- */
+struct ffa_partitions {
- u32 count;
- struct ffa_partition_desc *descs; /* virtual address */
+};
+/**
- struct ffa_prvdata - the driver private data structure
- @dev: The arm_ffa device under u-boot driver model
- @ffa_ops: The driver operations structure
- @fwk_version: FF-A framework version
- @id: u-boot endpoint ID
- @partitions: The partitions descriptors structure
- @pair: The RX/TX buffers pair
- @invoke_ffa_fn: The function executing the FF-A function
- The driver data structure hosting all resident data.
- */
+struct ffa_prvdata {
- struct udevice *dev;
- struct ffa_bus_ops ffa_ops;
- u32 fwk_version;
- u16 id;
- struct ffa_partitions partitions;
- struct ffa_rxtxpair pair;
- invoke_ffa_fn_t invoke_ffa_fn;
+};
+/**
- ffa_device_get - create, bind and probe the arm_ffa device
- */
+int ffa_device_get(void);
+/**
- ffa_bus_prvdata_get - bus driver private data getter
- */
+struct ffa_prvdata **ffa_bus_prvdata_get(void);
+#endif diff --git a/drivers/firmware/arm-ffa/core.c b/drivers/firmware/arm-ffa/core.c new file mode 100644 index 0000000000..41c7b96e68 --- /dev/null +++ b/drivers/firmware/arm-ffa/core.c @@ -0,0 +1,1344 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include "arm_ffa_prv.h" +#include <asm/global_data.h> +#include <common.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <dm/devres.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> +#include <log.h> +#include <malloc.h> +#include <string.h> +#include <uuid.h>
+DECLARE_GLOBAL_DATA_PTR;
+/**
- The device private data structure containing all the resident
- data read from secure world
- */
+__ffa_runtime_data struct ffa_prvdata *ffa_priv_data;
+/* Error mapping declarations */
+__ffa_runtime_data int ffa_to_std_errmap[MAX_NUMBER_FFA_ERR] = {
- 0,
- -EOPNOTSUPP, /* NOT_SUPPORTED */
- -EINVAL, /* INVALID_PARAMETERS */
- -ENOMEM, /* NO_MEMORY */
- -EBUSY, /* BUSY */
- -EINTR, /* INTERRUPTED */
- -EACCES, /* DENIED */
- -EAGAIN, /* RETRY */
- -ECANCELED, /* ABORTED */
+};
+struct ffa_abi_errmap err_msg_map[FFA_ERRMAP_COUNT] = {
- [FFA_ID_TO_ERRMAP_ID(FFA_VERSION)] = {
{
"",
"NOT_SUPPORTED: A Firmware Framework implementation does not exist",
"", /* INVALID_PARAMETERS */
"", /* NO_MEMORY */
"", /* BUSY */
"", /* INTERRUPTED */
"", /* DENIED */
"", /* RETRY */
"", /* ABORTED */
},
- },
- [FFA_ID_TO_ERRMAP_ID(FFA_ID_GET)] = {
{
"",
"NOT_SUPPORTED: This function is not implemented at this FF-A instance",
"", /* INVALID_PARAMETERS */
"", /* NO_MEMORY */
"", /* BUSY */
"", /* INTERRUPTED */
"", /* DENIED */
"", /* RETRY */
"", /* ABORTED */
},
- },
- [FFA_ID_TO_ERRMAP_ID(FFA_FEATURES)] = {
{
"",
"NOT_SUPPORTED: FFA_RXTX_MAP is not implemented at this FF-A instance",
"", /* INVALID_PARAMETERS */
"", /* NO_MEMORY */
"", /* BUSY */
"", /* INTERRUPTED */
"", /* DENIED */
"", /* RETRY */
"", /* ABORTED */
},
- },
- [FFA_ID_TO_ERRMAP_ID(FFA_PARTITION_INFO_GET)] = {
{
"",
"NOT_SUPPORTED: This function is not implemented at this FF-A instance",
"INVALID_PARAMETERS: Unrecognized UUID",
"NO_MEMORY: Results cannot fit in RX buffer of the caller",
"BUSY: RX buffer of the caller is not free",
"", /* INTERRUPTED */
"DENIED: Callee is not in a state to handle this request",
"", /* RETRY */
"", /* ABORTED */
},
- },
- [FFA_ID_TO_ERRMAP_ID(FFA_RXTX_UNMAP)] = {
{
"",
"NOT_SUPPORTED: FFA_RXTX_UNMAP is not implemented at this FF-A instance",
"INVALID_PARAMETERS: No buffer pair registered on behalf of the caller",
"", /* NO_MEMORY */
"", /* BUSY */
"", /* INTERRUPTED */
"", /* DENIED */
"", /* RETRY */
"", /* ABORTED */
},
- },
- [FFA_ID_TO_ERRMAP_ID(FFA_RX_RELEASE)] = {
{
"",
"NOT_SUPPORTED: FFA_RX_RELEASE is not implemented at this FF-A instance",
"", /* INVALID_PARAMETERS */
"", /* NO_MEMORY */
"", /* BUSY */
"", /* INTERRUPTED */
"DENIED: Caller did not have ownership of the RX buffer",
"", /* RETRY */
"", /* ABORTED */
},
- },
- [FFA_ID_TO_ERRMAP_ID(FFA_RXTX_MAP)] = {
{
"",
"NOT_SUPPORTED: This function is not implemented at this FF-A instance",
"INVALID_PARAMETERS: Field(s) in input parameters incorrectly encoded",
"NO_MEMORY: Not enough memory",
"", /* BUSY */
"", /* INTERRUPTED */
"DENIED: Buffer pair already registered",
"", /* RETRY */
"", /* ABORTED */
},
- },
+};
+/**
- ffa_to_std_errno - convert FF-A error code to standard error code
- @ffa_errno: Error code returned by the FF-A ABI
- This runtime function maps 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
- */
+__ffa_runtime 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
- This boot time function maps 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
- */
+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_msg_map[abi_idx].err_str[err_idx])
return -EINVAL;
- ffa_err("%s", err_msg_map[abi_idx].err_str[err_idx]);
- return 0;
+}
+/*
- Driver core functions
- */
+/**
- ffa_remove_device - removes the arm_ffa device
- @dev: the device to be removed
- This boot time function makes sure the arm_ffa device is removed
- No need to free the kmalloced data when the device is destroyed.
- It's automatically done by devm management by
- device_remove() -> device_free() -> devres_release_probe().
- Return:
- 0 on success. Otherwise, failure
- */
+int ffa_remove_device(struct udevice *dev) +{
- int ret;
- if (!dev) {
ffa_err("no udevice found");
return -ENODEV;
- }
- ret = device_remove(dev, DM_REMOVE_NORMAL);
- if (ret) {
ffa_err("unable to remove. err:%d\n", ret);
return ret;
- }
- ffa_info("device removed and freed");
- ret = device_unbind(dev);
- if (ret) {
ffa_err("unable to unbind. err:%d\n", ret);
return ret;
- }
- ffa_info("device unbound");
- return 0;
+}
+/**
- ffa_device_get - create, bind and probe the arm_ffa device
- This boot time function makes sure the arm_ffa device is
- created, bound to this driver, probed and ready to use.
- Arm FF-A transport is implemented through a single u-boot
- device managing the FF-A bus (arm_ffa).
- Return:
- 0 on success. Otherwise, failure
- */
+int ffa_device_get(void) +{
- int ret;
- struct udevice *dev = NULL;
- ret = device_bind(dm_root(),
DM_DRIVER_GET(arm_ffa),
FFA_DRV_NAME,
NULL,
ofnode_null(),
&dev);
- if (ret)
return ret;
- /* The FF-A bus discovery succeeds when probing is successful */
- ret = device_probe(dev);
- if (ret) {
ffa_err("arm_ffa device probing failed");
ffa_remove_device(dev);
return ret;
- }
- return 0;
+}
+/**
- ffa_get_version - FFA_VERSION handler function
- This is the boot time function that implements FFA_VERSION FF-A function
- to get from the secure world the FF-A framework version
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_get_version(void) +{
- u16 major, minor;
- ffa_value_t res = {0};
- int ffa_errno;
- ffa_priv_data->invoke_ffa_fn((ffa_value_t){
.a0 = FFA_SMC_32(FFA_VERSION),
.a1 = FFA_VERSION_1_0, .a2 = 0, .a3 = 0, .a4 = 0, .a5 = 0, .a6 = 0, .a7 = 0,
Why not support the latest version, 1.1?
We only need the v1.0 features.
The zero initializations are done implicitly so you could just as well remove them.
Regarding all the code comments made in this patch, they have been addressed in patchset v6. Thanks.
}, &res);
- ffa_errno = (int)res.a0;
Is this cast needed, or any of the other casts below in this function?
- if (ffa_errno < 0) {
ffa_print_error_log(FFA_VERSION, ffa_errno);
return ffa_to_std_errno(ffa_errno);
- }
- major = GET_FFA_MAJOR_VERSION((u32)res.a0);
- minor = GET_FFA_MINOR_VERSION((u32)res.a0);
- ffa_info("FF-A driver %d.%d\nFF-A framework %d.%d",
FFA_MAJOR_VERSION, FFA_MINOR_VERSION, major, minor);
- if ((major == FFA_MAJOR_VERSION && minor >= FFA_MINOR_VERSION)) {
ffa_info("Versions are compatible ");
ffa_priv_data->fwk_version = (u32)res.a0;
return 0;
- }
- ffa_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
- This is the boot time function that implements 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(void) +{
- ffa_value_t res = {0};
- int ffa_errno;
- ffa_priv_data->invoke_ffa_fn((ffa_value_t){
.a0 = FFA_SMC_32(FFA_ID_GET),
.a1 = 0, .a2 = 0, .a3 = 0, .a4 = 0, .a5 = 0, .a6 = 0, .a7 = 0,
}, &res);
- if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) {
ffa_priv_data->id = GET_SELF_ENDPOINT_ID((u32)res.a2);
ffa_info("endpoint ID is %u", ffa_priv_data->id);
return 0;
- }
- ffa_errno = (int)res.a2;
- ffa_print_error_log(FFA_ID_GET, ffa_errno);
- return ffa_to_std_errno(ffa_errno);
+}
+/**
- ffa_set_rxtx_buffers_pages_cnt - sets the minimum number of pages in each of the RX/TX buffers
- @prop_field: properties field obtained from FFA_FEATURES ABI
- This boot time function sets the minimum number of pages
- in each of the RX/TX buffers in the private data structure
- Return:
- buf_4k_pages points to the returned number of pages
- 0 on success. Otherwise, failure
- */
+static int ffa_set_rxtx_buffers_pages_cnt(u32 prop_field) +{
- if (!ffa_priv_data)
return -EINVAL;
- switch (prop_field) {
- case RXTX_4K:
ffa_priv_data->pair.rxtx_min_pages = 1;
break;
- case RXTX_16K:
ffa_priv_data->pair.rxtx_min_pages = 4;
break;
- case RXTX_64K:
ffa_priv_data->pair.rxtx_min_pages = 16;
break;
- default:
ffa_err("RX/TX buffer size not supported");
return -EINVAL;
- }
- return 0;
+}
+/**
- ffa_get_rxtx_map_features - FFA_FEATURES handler function with FFA_RXTX_MAP argument
- This is the boot time function that implements 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(void) +{
- ffa_value_t res = {0};
- int ffa_errno;
- ffa_priv_data->invoke_ffa_fn((ffa_value_t){
.a0 = FFA_SMC_32(FFA_FEATURES),
.a1 = FFA_SMC_64(FFA_RXTX_MAP),
.a2 = 0, .a3 = 0, .a4 = 0, .a5 = 0, .a6 = 0, .a7 = 0,
}, &res);
- if (res.a0 == FFA_SMC_32(FFA_SUCCESS))
return ffa_set_rxtx_buffers_pages_cnt((u32)res.a2);
- ffa_errno = (int)res.a2;
- ffa_print_error_log(FFA_FEATURES, ffa_errno);
- return ffa_to_std_errno(ffa_errno);
+}
+/**
- ffa_free_rxtx_buffers - frees the RX/TX buffers
- This is the boot time function used to free the RX/TX buffers
- */
+static void ffa_free_rxtx_buffers(void) +{
- ffa_info("Freeing RX/TX buffers");
- if (ffa_priv_data->pair.rxbuf) {
free((void *)ffa_priv_data->pair.rxbuf);
ffa_priv_data->pair.rxbuf = 0;
- }
- if (ffa_priv_data->pair.txbuf) {
free((void *)ffa_priv_data->pair.txbuf);
ffa_priv_data->pair.txbuf = 0;
- }
+}
+/**
- ffa_alloc_rxtx_buffers - allocates the RX/TX buffers
- This is the boot time function 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.
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_alloc_rxtx_buffers(void) +{
- u64 bytes;
- ffa_info("Using %lu 4KB page(s) for RX/TX buffers size",
ffa_priv_data->pair.rxtx_min_pages);
- bytes = ffa_priv_data->pair.rxtx_min_pages * SZ_4K;
- /* RX/TX buffers addresses should be PAGE_SIZE aligned */
- ffa_priv_data->pair.rxbuf = (u64)memalign(PAGE_SIZE, bytes);
Shouldn't this be "bytes" aligned too? Same below with the TX buffer.
- if (!ffa_priv_data->pair.rxbuf) {
ffa_err("failure to allocate RX buffer");
return -ENOBUFS;
- }
- ffa_info("RX buffer at virtual address 0x%llx", ffa_priv_data->pair.rxbuf);
- ffa_priv_data->pair.txbuf = (u64)memalign(PAGE_SIZE, bytes);
- if (!ffa_priv_data->pair.txbuf) {
free((void *)ffa_priv_data->pair.rxbuf);
ffa_priv_data->pair.rxbuf = 0;
ffa_err("failure to allocate the TX buffer");
return -ENOBUFS;
- }
- ffa_info("TX buffer at virtual address 0x%llx", ffa_priv_data->pair.txbuf);
- /*
* make sure the buffers are cleared before use
*/
- memset((void *)ffa_priv_data->pair.rxbuf, 0, bytes);
- memset((void *)ffa_priv_data->pair.txbuf, 0, bytes);
- return 0;
+}
+/**
- ffa_map_rxtx_buffers - FFA_RXTX_MAP handler function
- This is the boot time function that implements FFA_RXTX_MAP FF-A function
- to map the RX/TX buffers
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_map_rxtx_buffers(void) +{
- int ret;
- ffa_value_t res = {0};
- int ffa_errno;
- ret = ffa_alloc_rxtx_buffers();
- 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
*/
- ffa_priv_data->invoke_ffa_fn((ffa_value_t){
.a0 = FFA_SMC_64(FFA_RXTX_MAP),
.a1 = ffa_priv_data->pair.txbuf,
.a2 = ffa_priv_data->pair.rxbuf,
.a3 = ffa_priv_data->pair.rxtx_min_pages,
.a4 = 0, .a5 = 0, .a6 = 0, .a7 = 0,
}, &res);
- if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) {
ffa_info("RX/TX buffers mapped");
return 0;
- }
- ffa_errno = (int)res.a2;
- ffa_print_error_log(FFA_RXTX_MAP, ffa_errno);
- ffa_free_rxtx_buffers();
- return ffa_to_std_errno(ffa_errno);
+}
+/**
- ffa_unmap_rxtx_buffers - FFA_RXTX_UNMAP handler function
- This is the boot time function that implements FFA_RXTX_UNMAP FF-A function
- to unmap the RX/TX buffers
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_unmap_rxtx_buffers(void) +{
- ffa_value_t res = {0};
- int ffa_errno;
- ffa_priv_data->invoke_ffa_fn((ffa_value_t){
.a0 = FFA_SMC_32(FFA_RXTX_UNMAP),
.a1 = PREP_SELF_ENDPOINT_ID(ffa_priv_data->id),
.a2 = 0, .a3 = 0, .a4 = 0, .a5 = 0, .a6 = 0, .a7 = 0,
}, &res);
- if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) {
ffa_free_rxtx_buffers();
return 0;
- }
- ffa_errno = (int)res.a2;
- ffa_print_error_log(FFA_RXTX_UNMAP, ffa_errno);
- return ffa_to_std_errno(ffa_errno);
+}
+/**
- ffa_release_rx_buffer - FFA_RX_RELEASE handler function
- This is the boot time function that invokes 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(void) +{
- ffa_value_t res = {0};
- int ffa_errno;
- ffa_priv_data->invoke_ffa_fn((ffa_value_t){
.a0 = FFA_SMC_32(FFA_RX_RELEASE),
.a1 = 0, .a2 = 0, .a3 = 0, .a4 = 0, .a5 = 0, .a6 = 0, .a7 = 0,
}, &res);
- if (res.a0 == FFA_SMC_32(FFA_SUCCESS))
return 0;
- ffa_errno = (int)res.a2;
- ffa_print_error_log(FFA_RX_RELEASE, ffa_errno);
- return ffa_to_std_errno(ffa_errno);
+}
+/**
- ffa_uuid_are_identical - checks whether two given UUIDs are identical
- @uuid1: first UUID
- @uuid2: second UUID
- This is a boot time function used by ffa_read_partitions_info to search
- for a UUID in the partitions descriptors table
- Return:
- 1 when UUIDs match. Otherwise, 0
- */
+int 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)));
Please drop the outer ().
+}
+/**
- ffa_read_partitions_info - reads the data queried by FFA_PARTITION_INFO_GET
and saves it in the private structure
- @count: The number of partitions queried
- @part_uuid: Pointer to the partition(s) UUID
- This is the boot time function that reads the partitions information
- returned by the FFA_PARTITION_INFO_GET and saves it in the private
- data structure.
- Return:
- The private data structure is updated with the partition(s) information
- 0 is returned on success. Otherwise, failure
- */
+static int ffa_read_partitions_info(u32 count, struct ffa_partition_uuid *part_uuid) +{
- if (!count) {
ffa_err("no partition detected");
return -ENODATA;
- }
- ffa_info("Reading partitions data from the RX buffer");
- 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 = ffa_priv_data->pair.rxtx_min_pages * SZ_4K;
if (data_bytes > buf_bytes) {
ffa_err("partitions data size exceeds the RX buffer size:");
ffa_err(" sizes in bytes: data %llu , RX buffer %llu ",
data_bytes,
buf_bytes);
return -ENOMEM;
}
ffa_priv_data->partitions.descs = (struct ffa_partition_desc *)
devm_kmalloc(ffa_priv_data->dev, data_bytes, __GFP_ZERO);
There's no need to cast the void pointer.
if (!ffa_priv_data->partitions.descs) {
ffa_err("cannot allocate partitions data buffer");
return -ENOMEM;
}
parts_info = (struct ffa_partition_info *)ffa_priv_data->pair.rxbuf;
for (desc_idx = 0 ; desc_idx < count ; desc_idx++) {
ffa_priv_data->partitions.descs[desc_idx].info =
parts_info[desc_idx];
ffa_info("Partition ID %x : info cached",
ffa_priv_data->partitions.descs[desc_idx].info.id);
}
ffa_priv_data->partitions.count = count;
ffa_info("%d partition(s) found and cached", count);
- } else {
u32 rx_desc_idx, cached_desc_idx;
struct ffa_partition_info *parts_info;
u8 desc_found;
parts_info = (struct ffa_partition_info *)ffa_priv_data->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 < ffa_priv_data->partitions.count;
cached_desc_idx++) {
/*
* save the UUID
*/
if (ffa_priv_data->partitions.descs[cached_desc_idx].info.id ==
parts_info[rx_desc_idx].id) {
ffa_priv_data->partitions.descs[cached_desc_idx].sp_uuid =
*part_uuid;
desc_found = 1;
break;
}
}
if (!desc_found)
return -ENODATA;
}
- }
- return 0;
+}
+/**
- ffa_query_partitions_info - invokes FFA_PARTITION_INFO_GET and saves partitions data
- @part_uuid: Pointer to the partition(s) UUID
- @pcount: Pointer to the number of partitions variable filled when querying
- This is the boot time function that executes the FFA_PARTITION_INFO_GET
- to query the partitions data. Then, it calls ffa_read_partitions_info
- to save the data in the private data structure.
- 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 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;
- }
- ffa_priv_data->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,
.a5 = 0,
.a6 = 0,
.a7 = 0,
}, &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((u32)res.a2, part_uuid);
if (ret) {
ffa_err("failed to read partition(s) data , error (%d)", ret);
ffa_release_rx_buffer();
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();
return ret;
- }
- ffa_errno = (int)res.a2;
- ffa_print_error_log(FFA_PARTITION_INFO_GET, ffa_errno);
- return ffa_to_std_errno(ffa_errno);
+}
+/**
- ffa_get_partitions_info - FFA_PARTITION_INFO_GET handler function
- The passed arguments:
- Mode 1: When getting from the driver the number of
- secure partitions:
- @uuid_str: pointer to the UUID string
- @parts_size: pointer to the variable that contains the number of partitions
The variable will be set by the driver
- @buffer: NULL
- Mode 2: When requesting the driver to return the
- partitions information:
- @uuid_str: pointer to the UUID string
- @parts_size: pointer to the size of the SPs information buffer in bytes
- @buffer: pointer to SPs information buffer
(allocated by the client).
The buffer will be filled by the driver
- This is the boot time function that queries the secure partition data from
- the private data structure. If not found, it invokes 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 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().
- When invoked through a client request, ffa_get_partitions_info should be
- called twice. First call is to get from the driver the number of secure
- partitions (SPs) associated to a particular UUID.
- Then, the caller (client) allocates the buffer to host the SPs data and
- issues a 2nd call. Then, the driver fills the SPs data in the pre-allocated
- buffer.
- To achieve the mechanism described above, ffa_get_partitions_info uses the
- following functions:
ffa_read_partitions_info
ffa_query_partitions_info
- Return:
- @parts_size: When pointing to the number of partitions variable, the number is
- set by the driver.
- When pointing to the partitions information buffer size, the buffer will be
- filled by the driver.
- On success 0 is returned. Otherwise, failure
- */
+static int ffa_get_partitions_info(const char *uuid_str,
u32 *parts_size, struct ffa_partition_info *buffer)
+{
- /*
* fill_data:
* 0: return the SP count
* 1: fill SP data and return it to the caller
* -1: undefined mode
*/
- int fill_data = -1;
- u32 desc_idx, client_desc_idx;
- struct ffa_partition_uuid part_uuid = {0};
- u32 client_desc_max_cnt;
- u32 parts_found = 0;
- if (!ffa_priv_data->partitions.count || !ffa_priv_data->partitions.descs) {
ffa_err("no partition installed");
return -EINVAL;
- }
- if (!uuid_str) {
ffa_err("no UUID provided");
return -EINVAL;
- }
- if (!parts_size) {
ffa_err("no size/count provided");
return -EINVAL;
- }
- if (be_uuid_str_to_le_bin(uuid_str, (unsigned char *)&part_uuid)) {
ffa_err("invalid UUID");
return -EINVAL;
- }
- if (!buffer) {
/* Mode 1: getting the number of secure partitions */
fill_data = 0;
ffa_info("Preparing for checking partitions count");
- } else if ((*parts_size >= sizeof(struct ffa_partition_info)) &&
!(*parts_size % sizeof(struct ffa_partition_info))) {
/* Mode 2: retrieving the partitions information */
fill_data = 1;
client_desc_idx = 0;
/*
* number of empty descriptors preallocated by the caller
*/
client_desc_max_cnt = *parts_size / sizeof(struct ffa_partition_info);
ffa_info("Preparing for filling partitions info");
- } else {
ffa_err("invalid function arguments provided");
return -EINVAL;
- }
- ffa_info("Searching partitions using the provided UUID");
- /*
* search in the cached partitions
*/
- for (desc_idx = 0;
desc_idx < ffa_priv_data->partitions.count;
desc_idx++) {
if (ffa_uuid_are_identical(&ffa_priv_data->partitions.descs[desc_idx].sp_uuid,
&part_uuid)) {
ffa_info("Partition ID %x matches the provided UUID",
ffa_priv_data->partitions.descs[desc_idx].info.id);
parts_found++;
if (fill_data) {
/*
* trying to fill the partition info in the input buffer
*/
if (client_desc_idx < client_desc_max_cnt) {
buffer[client_desc_idx++] =
ffa_priv_data->partitions.descs[desc_idx].info;
continue;
}
ffa_err("failed to fill the current descriptor client buffer full");
return -ENOBUFS;
}
}
- }
- if (!parts_found) {
int ret;
ffa_info("No partition found. Querying framework ...");
ret = ffa_query_partitions_info(&part_uuid, &parts_found);
if (ret == 0) {
if (!fill_data) {
*parts_size = parts_found;
ffa_info("Number of partition(s) found matching the UUID: %d",
parts_found);
} else {
/*
* If SPs data detected, they are already in the private data
* structure, retry searching SP data again to return them
* to the caller
*/
if (parts_found)
ret = ffa_get_partitions_info(uuid_str, parts_size, buffer);
else
ret = -ENODATA;
}
}
return ret;
- }
- /* partition(s) found */
- if (!fill_data)
*parts_size = parts_found;
- return 0;
+}
+/**
- ffa_cache_partitions_info - Queries and saves all secure partitions data
- This is a boot time function that invokes 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 the
- resident private data structure and we keep the UUID field empty
- (in FF-A 1.0 UUID is not provided by the partition descriptor)
- This function is 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(void) +{
- return ffa_query_partitions_info(NULL, NULL);
+}
+/**
- ffa_msg_send_direct_req - FFA_MSG_SEND_DIRECT_{REQ,RESP} handler function
- @dst_part_id: destination partition ID
- @msg: pointer to the message data preallocated by the client (in/out)
- This is the runtime function that 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
- */
+static int __ffa_runtime ffa_msg_send_direct_req(u16 dst_part_id, struct ffa_send_direct_data *msg) +{
- ffa_value_t res = {0};
- int ffa_errno;
- if (!ffa_priv_data || !ffa_priv_data->invoke_ffa_fn)
return -EINVAL;
- /* No partition installed */
- if (!ffa_priv_data->partitions.count || !ffa_priv_data->partitions.descs)
return -ENODEV;
- ffa_priv_data->invoke_ffa_fn((ffa_value_t){
.a0 = FFA_SMC_64(FFA_MSG_SEND_DIRECT_REQ),
.a1 = PREP_SELF_ENDPOINT_ID(ffa_priv_data->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))
ffa_priv_data->invoke_ffa_fn((ffa_value_t){
.a0 = FFA_SMC_32(FFA_RUN),
.a1 = res.a1, .a2 = 0, .a3 = 0, .a4 = 0, .a5 = 0, .a6 = 0, .a7 = 0,
}, &res);
- if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) {
How can the caller tell this response apart from a FFA_MSG_SEND_DIRECT_RESP?
According to the FF-A 1.0 spec, paragraph 10.2:
Successful completion of FFA_MSG_SEND_DIRECT_REQ is indicated through an invocation of the following interfaces by the callee: FFA_MSG_SEND_DIRECT_RESP, FFA_INTERRUPT, FFA_SUCCESS, FFA_ERROR.
All these are supported by the u-boot FF-A driver.
/* Message sent with no response */
return 0;
- }
- if (res.a0 == FFA_SMC_64(FFA_MSG_SEND_DIRECT_RESP)) {
/*
* 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 = (int)res.a2;
- return ffa_to_std_errno(ffa_errno);
+}
+/**
- __arm_ffa_fn_smc - 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
- Return: void
- */
+void __ffa_runtime __arm_ffa_fn_smc(ffa_value_t args, ffa_value_t *res) +{
- arm_smccc_1_2_smc(&args, res);
+}
+/**
- ffa_set_smc_conduit - Set the SMC conduit
- This boot time function selects the SMC conduit by setting the driver invoke function
- to SMC assembly function
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_set_smc_conduit(void) +{
- ffa_priv_data->invoke_ffa_fn = __arm_ffa_fn_smc;
- if (!ffa_priv_data->invoke_ffa_fn) {
ffa_err("failure to set the invoke function");
return -EINVAL;
- }
- ffa_info("Conduit is SMC");
- return 0;
+}
+/**
- ffa_set_bus_ops - Set the bus driver operations
- Setting the driver callbacks.
- */
+static void ffa_set_bus_ops(void) +{
- ffa_priv_data->ffa_ops.partition_info_get = ffa_get_partitions_info;
- ffa_priv_data->ffa_ops.sync_send_receive = ffa_msg_send_direct_req;
- ffa_priv_data->ffa_ops.rxtx_unmap = ffa_unmap_rxtx_buffers;
+}
+/**
- ffa_alloc_prvdata - allocate the driver main data structure and sets the device
- @dev: the arm_ffa device
- This boot time function creates the main data structure embedding all the driver data.
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_alloc_prvdata(struct udevice *dev) +{
- if (!dev) {
ffa_err("no udevice found");
return -ENODEV;
- }
- /* The device is registered with the DM. Let's create the driver main data structure*/
- ffa_priv_data = devm_kmalloc(dev, sizeof(struct ffa_prvdata), __GFP_ZERO);
- if (!ffa_priv_data) {
ffa_err("can not allocate the driver main data structure");
return -ENOMEM;
- }
- ffa_priv_data->dev = dev;
- return 0;
+}
+/**
- ffa_probe - The driver probe function
- @dev: the arm_ffa device
- Probing is done at boot time and triggered by the uclass device discovery.
- At probe level the following actions are done:
- setting the conduit
- querying the FF-A framework version
- 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 the resident private data structure.
- The probe will fail if either FF-A framework is not detected or the
- FF-A requests are not behaving correctly. This ensures that the
- driver is not installed and its operations are not exported to the clients.
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_probe(struct udevice *dev) +{
- int ret;
- ret = ffa_alloc_prvdata(dev);
- if (ret != 0)
return ret;
- ffa_set_bus_ops();
- ret = ffa_set_smc_conduit();
What if there's a hypervisor involved and HVC would be expected intead?
At this stage of development, we only support a Hypervisor that can trap an SMC if the need arises.
- if (ret != 0)
return ret;
- ret = ffa_get_version();
- if (ret != 0)
return ret;
- ret = ffa_get_endpoint_id();
- if (ret != 0)
return ret;
- ret = ffa_get_rxtx_map_features();
- if (ret != 0)
return ret;
- ret = ffa_map_rxtx_buffers();
- if (ret != 0)
return ret;
- ret = ffa_cache_partitions_info();
Why are we saving all the found partitions in a cache? It seems that FFA_PARTITION_INFO_GET could be called each time when needed instead without any noticeable overhead. Or is the result cached for some other reason?
The RX/TX buffers are only available at EFI boot time. Querying partitions is done at EFI boot time and data is cached for future use including EFI runtime use. RX/TX buffers are unmapped before EFI runtime mode starts. If we would allow RX/TX buffers created by u-boot to be available at EFI runtime, secure world will get confused about RX/TX buffers ownership (owned by the kernel ? By u-boot ? ).
- if (ret != 0) {
ffa_free_rxtx_buffers();
return ret;
- }
- return 0;
+}
+/**
- ffa_remove - The driver remove function
- @dev: the arm_ffa device
- When the device is about to be removed , unmap the RX/TX buffers and free the memory
- Return:
- 0 on success.
- */
+static int ffa_remove(struct udevice *dev) +{
- ffa_info("removing the device");
- ffa_unmap_rxtx_buffers();
- if (ffa_priv_data->pair.rxbuf || ffa_priv_data->pair.txbuf)
ffa_free_rxtx_buffers();
- return 0;
+}
+/**
- ffa_unbind - The driver unbind function
- @dev: the arm_ffa device
- After the device is removed and memory freed the device is unbound
- Return:
- 0 on success.
- */
+static int ffa_unbind(struct udevice *dev) +{
- ffa_info("unbinding the device , private data already released");
- ffa_priv_data = NULL;
- return 0;
+}
+/**
- ffa_bus_ops_get - bus driver operations getter
- Return:
- This runtime function returns a pointer to the driver operations structure
- */
+const struct ffa_bus_ops * __ffa_runtime ffa_bus_ops_get(void) +{
- return &ffa_priv_data->ffa_ops;
+}
+/**
- ffa_bus_prvdata_get - bus driver private data getter
- Return:
- This boot time function returns a pointer to the main private data structure
- */
+struct ffa_prvdata **ffa_bus_prvdata_get(void) +{
- return &ffa_priv_data;
+}
+/**
- ffa_bus_discover - discover FF-A bus and probe the arm_ffa device
- This boot time function makes sure the FF-A bus is discoverable.
- Then, the arm_ffa device is probed and ready to use.
- This function is called automatically at initcalls
- level (after u-boot relocation).
- When the bus was already discovered successfully the discovery will not run again.
- Arm FF-A transport is implemented through arm_ffa u-boot device managing the FF-A
- communication.
- All FF-A clients should use the arm_ffa device to use the FF-A transport.
- Return:
- 0 on success. Otherwise, failure
- */
+int ffa_bus_discover(void) +{
- int ret = 0;
- if (!ffa_priv_data)
ret = ffa_device_get();
- return ret;
+}
+/**
- Declaring the arm_ffa driver under UCLASS_FFA
- */
+U_BOOT_DRIVER(arm_ffa) = {
- .name = FFA_DRV_NAME,
- .id = UCLASS_FFA,
- .probe = ffa_probe,
- .remove = ffa_remove,
- .unbind = ffa_unbind,
+}; diff --git a/drivers/firmware/arm-ffa/efi_ffa_runtime_data_mgr.c b/drivers/firmware/arm-ffa/efi_ffa_runtime_data_mgr.c new file mode 100644 index 0000000000..c76cf2147b --- /dev/null +++ b/drivers/firmware/arm-ffa/efi_ffa_runtime_data_mgr.c @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include "arm_ffa_prv.h"
+/**
- ffa_copy_runtime_data - copy the private data structure to the runtime area
- This boot time function copies the arm_ffa driver data structures including
- partitions data to the EFI runtime data section.
- Return:
- 0 on success. Otherwise, failure
- */
+efi_status_t ffa_copy_runtime_data(void) +{
- efi_status_t efi_ret;
- efi_uintn_t prvdata_pages;
- efi_uintn_t descs_pages;
- struct ffa_prvdata **prvdata = NULL; /* Pointer to the current structure */
- struct ffa_prvdata *runtime_prvdata = NULL; /* Pointer to the structure runtime copy */
- u64 runtime_descs = 0;
- prvdata = ffa_bus_prvdata_get();
- printf("INFO: EFI: FFA: prv data area at 0x%llx\n", (u64)prvdata);
- /* allocate private data runtime area */
- prvdata_pages = efi_size_in_pages(sizeof(struct ffa_prvdata));
- efi_ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
EFI_RUNTIME_SERVICES_DATA,
prvdata_pages,
(u64 *)&runtime_prvdata);
Better to use a temporary u64 instead of casting the pointer.
- if (efi_ret != EFI_SUCCESS) {
printf("ERROR: EFI: FFA: allocating runtime data (err: 0x%lx, addr 0x%llx)\n",
efi_ret, (u64)runtime_prvdata);
return efi_ret;
- }
- printf("INFO: EFI: FFA: runtime data area at 0x%llx\n", (u64)runtime_prvdata);
- if (!runtime_prvdata)
return EFI_INVALID_PARAMETER;
- /* allocate the partition data runtime area */
- descs_pages = efi_size_in_pages((*prvdata)->partitions.count *
sizeof(struct ffa_partition_desc));
- efi_ret = efi_allocate_pages(EFI_ALLOCATE_ANY_PAGES,
EFI_RUNTIME_SERVICES_DATA,
descs_pages,
&runtime_descs);
- if (efi_ret != EFI_SUCCESS) {
printf("ERROR: EFI: FFA: allocating runtime SPs data (err: 0x%lx, addr 0x%llx)\n",
efi_ret, runtime_descs);
efi_free_pages((u64)runtime_prvdata, prvdata_pages);
return efi_ret;
- }
- printf("INFO: EFI: FFA: SPs runtime area at 0x%llx\n", (u64)runtime_descs);
- if (!runtime_descs)
return EFI_INVALID_PARAMETER;
- *runtime_prvdata = **prvdata;
With this you copy fwk_versien. This field will become out of data if the kernel driver negotiates a different framework version.
Very valid point thanks. The version can be negotiated as per the spec. However, v1.1 is backwards compatible with v1.0. So even if the kernel negotiates, EFI RT FFA_MSG_SEND_DIRECT_REQ calls should work. The problematic ABIs would be the memory management ABIs and FFA_PARTITION_INFO_GET (See v1.1 spec paragraph 18.6). But we do not use any of these ABIs in EFI RT.
So, no need to copy that framework version to the RT structure.
Ilias advised to remove EFI RT support from the patchset and adding it later when needed. When it's decided to add RT support back again, we will not copy the framework version to the RT structure.
This has been addressed in patchset v6.
- runtime_prvdata->dev = NULL;
- runtime_prvdata->ffa_ops.partition_info_get = NULL;
- runtime_prvdata->ffa_ops.rxtx_unmap = NULL;
- runtime_prvdata->partitions.descs = (struct ffa_partition_desc *)runtime_descs;
- runtime_prvdata->pair.rxbuf = 0;
- runtime_prvdata->pair.txbuf = 0;
- /*
* Update the private data structure pointer in the driver
* no need to free the old structure. devm takes care of that
*/
- *prvdata = runtime_prvdata;
- printf("INFO: EFI: FFA: runtime prv data now at 0x%llx , SPs count %d\n",
(u64)*prvdata, (*prvdata)->partitions.count);
- return 0;
+} diff --git a/include/arm_ffa.h b/include/arm_ffa.h new file mode 100644 index 0000000000..f17b100497 --- /dev/null +++ b/include/arm_ffa.h @@ -0,0 +1,127 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#ifndef __ARM_FFA_H +#define __ARM_FFA_H
+#include <linux/printk.h>
+/*
- This header is public. It can be used by clients to access
- data structures and definitions they need
- */
+/*
- Macros for displaying logs
- */
+#define ffa_info(fmt, ...) pr_info("[FFA] " fmt "\n", ##__VA_ARGS__) +#define ffa_err(fmt, ...) pr_err("[FFA] " fmt "\n", ##__VA_ARGS__)
+/*
- 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 __packed 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_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 __packed ffa_send_direct_data {
- unsigned long data0; /* w3/x3 */
- unsigned long data1; /* w4/x4 */
- unsigned long data2; /* w5/x5 */
- unsigned long data3; /* w6/x6 */
- unsigned long data4; /* w7/x7 */
+};
+#if CONFIG_IS_ENABLED(ARM_FFA_EFI_RUNTIME_MODE)
+#include <efi_loader.h>
+/*
- __ffa_runtime - controls whether functions are
- available after calling the EFI ExitBootServices service.
- Functions tagged with these keywords are resident (available at boot time and
- at runtime)
- */
+#define __ffa_runtime_data __efi_runtime_data +#define __ffa_runtime __efi_runtime
+#else
+/*
- The FF-A driver is independent from EFI
- */
+#define __ffa_runtime_data +#define __ffa_runtime
+#endif
+/**
- struct ffa_bus_ops - The driver operations structure
- @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)(const char *uuid_str,
u32 *parts_size, struct ffa_partition_info *buffer);
- int (*sync_send_receive)(u16 dst_part_id, struct ffa_send_direct_data *msg);
- int (*rxtx_unmap)(void);
+};
+/**
- The device driver and the Uclass driver public functions
- */
+/**
- ffa_bus_ops_get - driver operations getter
- */
+const struct ffa_bus_ops * __ffa_runtime ffa_bus_ops_get(void);
+/**
- ffa_bus_discover - discover FF-A bus and probes the arm_ffa device
- */
+int ffa_bus_discover(void);
+#if CONFIG_IS_ENABLED(ARM_FFA_EFI_RUNTIME_MODE)
+/**
- ffa_copy_runtime_data - copy the private data structure and the SPs data to the runtime area
- */
+efi_status_t ffa_copy_runtime_data(void);
+#endif
+#endif diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index a432e43871..5dd698b7a9 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -4,6 +4,9 @@
- (C) Copyright 2012
- Pavel Herrmann morpheus.ibis@gmail.com
- (C) Copyright 2022 ARM Limited
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#ifndef _DM_UCLASS_ID_H @@ -55,6 +58,7 @@ enum uclass_id { UCLASS_EFI_MEDIA, /* Devices provided by UEFI firmware */ UCLASS_ETH, /* Ethernet device */ UCLASS_ETH_PHY, /* Ethernet PHY device */
- UCLASS_FFA, /* Arm Firmware Framework for Armv8-A */ UCLASS_FIRMWARE, /* Firmware */ UCLASS_FUZZING_ENGINE, /* Fuzzing engine */ UCLASS_FS_FIRMWARE_LOADER, /* Generic loader */
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 6f7333638a..af0b0f3db1 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -3,6 +3,9 @@
- EFI application boot time services
- Copyright (c) 2016 Alexander Graf
- (C) Copyright 2022 ARM Limited
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#include <common.h> @@ -23,6 +26,10 @@ #include <asm/setjmp.h> #include <linux/libfdt_env.h>
+#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT) +#include <arm_ffa.h> +#endif
DECLARE_GLOBAL_DATA_PTR;
/* Task priority level */ @@ -2178,6 +2185,14 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL); }
+#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT)
/* unmap FF-A RX/TX buffers */
if (ffa_bus_ops_get()->rxtx_unmap())
debug("[efi_boottime][ERROR]: can not unmap FF-A RX/TX buffers\n");
else
debug("[efi_boottime][INFO]: FF-A RX/TX buffers unmapped\n");
+#endif
- /* Patch out unsupported runtime function */ efi_runtime_detach();
-- 2.17.1

Provide armffa command showcasing the use of the FF-A driver
The armffa command allows to query secure partitions data from the secure world and exchanging messages with the partitions.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org ---
Changelog: ===============
v4:
* remove pattern data in do_ffa_msg_send_direct_req
v3:
* use the new driver interfaces (partition_info_get, sync_send_receive) in armffa command
v2:
* replace use of ffa_helper_init_device function by ffa_helper_bus_discover
v1:
* introduce armffa command
MAINTAINERS | 1 + cmd/Kconfig | 10 ++ cmd/Makefile | 2 + cmd/armffa.c | 242 +++++++++++++++++++++++++++++++ drivers/firmware/arm-ffa/Kconfig | 1 + 5 files changed, 256 insertions(+) create mode 100644 cmd/armffa.c
diff --git a/MAINTAINERS b/MAINTAINERS index 02b84d5074..fd3b2c4263 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -251,6 +251,7 @@ F: configs/cortina_presidio-asic-pnand_defconfig ARM FF-A M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com S: Maintained +F: cmd/armffa.c F: doc/README.ffa.drv F: drivers/firmware/arm-ffa/ F: include/arm_ffa.h diff --git a/cmd/Kconfig b/cmd/Kconfig index 0e0be94f41..013d4c3da7 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -912,6 +912,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 Arm FF-A driver + 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 6e87522b62..979c6d59df 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -12,6 +12,8 @@ obj-y += panic.o obj-y += version.o
# command + +obj-$(CONFIG_CMD_ARMFFA) += armffa.o obj-$(CONFIG_CMD_ACPI) += acpi.o obj-$(CONFIG_CMD_ADDRMAP) += addrmap.o obj-$(CONFIG_CMD_AES) += aes.o diff --git a/cmd/armffa.c b/cmd/armffa.c new file mode 100644 index 0000000000..9b56e8a830 --- /dev/null +++ b/cmd/armffa.c @@ -0,0 +1,242 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <arm_ffa.h> +#include <asm/io.h> +#include <common.h> +#include <command.h> +#include <dm.h> +#include <mapmem.h> +#include <stdlib.h> + +/** + * do_ffa_get_singular_partition_info - implementation of the getpart subcommand + * @cmdtp: Command Table + * @flag: flags + * @argc: number of arguments + * @argv: arguments + * + * This function queries the secure partition information which the UUID is provided + * as an argument. The function uses the arm_ffa driver partition_info_get operation + * 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_get_singular_partition_info(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + u32 count = 0, size = 0; + int ret; + struct ffa_partition_info *parts_info; + u32 info_idx; + + if (argc != 1) + return -EINVAL; + + /* Mode 1: getting the number of secure partitions */ + ret = ffa_bus_ops_get()->partition_info_get(argv[0], &count, NULL); + if (ret != 0) { + ffa_err("Failure in querying partitions count (error code: %d)", ret); + return ret; + } + + if (!count) { + ffa_info("No secure partition found"); + return ret; + } + + /* + * pre-allocate a buffer to be filled by the driver + * with ffa_partition_info structs + */ + + ffa_info("Pre-allocating %d partition(s) info structures", count); + + parts_info = calloc(count, sizeof(struct ffa_partition_info)); + if (!parts_info) + return -EINVAL; + + size = count * sizeof(struct ffa_partition_info); + + /* + * ask the driver to fill the buffer with the SPs info + */ + + ret = ffa_bus_ops_get()->partition_info_get(argv[0], &size, parts_info); + if (ret != 0) { + ffa_err("Failure in querying partition(s) info (error code: %d)", ret); + free(parts_info); + return ret; + } + + /* + * SPs found , show the partition information + */ + for (info_idx = 0; info_idx < count ; info_idx++) { + ffa_info("Partition: id = 0x%x , exec_ctxt 0x%x , properties 0x%x", + parts_info[info_idx].id, + parts_info[info_idx].exec_ctxt, + parts_info[info_idx].properties); + } + + free(parts_info); + + return 0; +} + +/** + * do_ffa_msg_send_direct_req - implementation of the ping subcommand + * @cmdtp: Command Table + * @flag: flags + * @argc: number of arguments + * @argv: arguments + * + * This function sends data to the secure partition which the ID is provided + * as an argument. The function uses the arm_ffa driver sync_send_receive operation + * to send data. + * + * Return: + * + * CMD_RET_SUCCESS: on success, otherwise failure + */ +int do_ffa_msg_send_direct_req(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; + + if (argc != 1) + return -EINVAL; + + errno = 0; + part_id = strtoul(argv[0], NULL, 16); + + if (errno) { + ffa_err("Invalid partition ID"); + return -EINVAL; + } + + ret = ffa_bus_ops_get()->sync_send_receive(part_id, &msg); + if (ret == 0) { + u8 cnt; + + ffa_info("SP response:\n[LSB]"); + for (cnt = 0; + cnt < sizeof(struct ffa_send_direct_data) / sizeof(u64); + cnt++) + ffa_info("0x%llx", ((u64 *)&msg)[cnt]); + } else { + ffa_err("Sending direct request error (%d)", ret); + } + + return ret; +} + +/** + *do_ffa_dev_list - implementation of the devlist subcommand + * @cmdtp: [in] Command Table + * @flag: flags + * @argc: number of arguments + * @argv: arguments + * + * This function queries the devices belonging to the UCLASS_FFA + * class. Currently, one device is expected to show up: the arm_ffa device + * + * Return: + * + * CMD_RET_SUCCESS: on success, otherwise failure + */ +int do_ffa_dev_list(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct udevice *dev = NULL; + int i, ret; + + ffa_info("arm_ffa uclass entries:"); + + for (i = 0, ret = uclass_first_device(UCLASS_FFA, &dev); + dev; + ret = uclass_next_device(&dev), i++) { + if (ret) + break; + + ffa_info("entry %d - instance %08x, ops %08x, plat %08x", + i, + (u32)map_to_sysmem(dev), + (u32)map_to_sysmem(dev->driver->ops), + (u32)map_to_sysmem(dev_get_plat(dev))); + } + + return cmd_process_error(cmdtp, ret); +} + +static struct cmd_tbl armffa_commands[] = { + U_BOOT_CMD_MKENT(getpart, 1, 1, do_ffa_get_singular_partition_info, "", ""), + U_BOOT_CMD_MKENT(ping, 1, 1, do_ffa_msg_send_direct_req, "", ""), + U_BOOT_CMD_MKENT(devlist, 0, 1, do_ffa_dev_list, "", ""), +}; + +/** + * do_armffa - the armffa command main function + * @cmdtp: Command Table + * @flag: flags + * @argc: number of arguments + * @argv: arguments + * + * This function identifies which armffa subcommand to run. + * Then, it makes sure the arm_ffa device is probed and + * ready for use. + * Then, it runs the subcommand. + * + * Return: + * + * CMD_RET_SUCCESS: on success, otherwise failure + */ +static int do_armffa(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct cmd_tbl *armffa_cmd; + int ret; + + if (argc < 2) + return CMD_RET_USAGE; + + armffa_cmd = find_cmd_tbl(argv[1], armffa_commands, ARRAY_SIZE(armffa_commands)); + + argc -= 2; + argv += 2; + + if (!armffa_cmd || argc > armffa_cmd->maxargs) + return CMD_RET_USAGE; + + ret = ffa_bus_discover(); + if (ret != 0) + return cmd_process_error(cmdtp, ret); + + if (!ffa_bus_ops_get()) + return -EINVAL; + + ret = armffa_cmd->cmd(armffa_cmd, flag, argc, argv); + + return cmd_process_error(armffa_cmd, ret); +} + +U_BOOT_CMD(armffa, 4, 1, do_armffa, + "Arm FF-A operations test command", + "getpart <partition UUID>\n" + " - lists the partition(s) info\n" + "ping <partition ID>\n" + " - sends a data pattern to the specified partition\n" + "devlist\n" + " - displays the arm_ffa device info\n"); diff --git a/drivers/firmware/arm-ffa/Kconfig b/drivers/firmware/arm-ffa/Kconfig index aceb61cf49..40b467b0a5 100644 --- a/drivers/firmware/arm-ffa/Kconfig +++ b/drivers/firmware/arm-ffa/Kconfig @@ -4,6 +4,7 @@ config ARM_FFA_TRANSPORT bool "Enable Arm Firmware Framework for Armv8-A driver" depends on DM && ARM64 select ARM_SMCCC + select CMD_ARMFFA select LIB_UUID select DEVRES help

Provide a Sandbox driver to emulate the FF-A ABIs
The emulated ABIs are those supported by the FF-A core driver and according to FF-A specification v1.0.
The Sandbox driver provides operations allowing the test application to read the status of all the inspected ABIs and perform functional tests based on that.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v4: align sandbox driver with the new FF-A driver interfaces and new way of error handling
v1: introduce the sandbox driver
MAINTAINERS | 1 + common/board_r.c | 2 +- configs/sandbox64_defconfig | 2 + configs/sandbox_defconfig | 2 + doc/arch/sandbox.rst | 1 + drivers/firmware/arm-ffa/Kconfig | 10 +- drivers/firmware/arm-ffa/Makefile | 1 + drivers/firmware/arm-ffa/arm_ffa_prv.h | 15 +- drivers/firmware/arm-ffa/core.c | 24 +- drivers/firmware/arm-ffa/sandbox.c | 659 ++++++++++++++++++ .../firmware/arm-ffa/sandbox_arm_ffa_prv.h | 144 ++++ include/arm_ffa.h | 2 +- include/sandbox_arm_ffa.h | 91 +++ lib/efi_loader/efi_boottime.c | 2 +- 14 files changed, 941 insertions(+), 15 deletions(-) create mode 100644 drivers/firmware/arm-ffa/sandbox.c create mode 100644 drivers/firmware/arm-ffa/sandbox_arm_ffa_prv.h create mode 100644 include/sandbox_arm_ffa.h
diff --git a/MAINTAINERS b/MAINTAINERS index fd3b2c4263..6f01002e34 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -255,6 +255,7 @@ F: cmd/armffa.c F: doc/README.ffa.drv F: drivers/firmware/arm-ffa/ F: include/arm_ffa.h +F: include/sandbox_arm_ffa.h
ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de diff --git a/common/board_r.c b/common/board_r.c index 8c99faddfd..7f1eae65df 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -776,7 +776,7 @@ static init_fnc_t init_sequence_r[] = { INIT_FUNC_WATCHDOG_RESET initr_net, #endif -#ifdef CONFIG_ARM_FFA_TRANSPORT +#if defined(CONFIG_ARM_FFA_TRANSPORT) && !defined(CONFIG_SANDBOX_FFA) ffa_bus_discover, #endif #ifdef CONFIG_POST diff --git a/configs/sandbox64_defconfig b/configs/sandbox64_defconfig index 290d1506c2..36e6448968 100644 --- a/configs/sandbox64_defconfig +++ b/configs/sandbox64_defconfig @@ -248,3 +248,5 @@ CONFIG_TEST_FDTDEC=y CONFIG_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y +CONFIG_ARM_FFA_TRANSPORT=y +CONFIG_SANDBOX_FFA=y \ No newline at end of file diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index ab5d3f19bf..8bf3848788 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -328,3 +328,5 @@ CONFIG_TEST_FDTDEC=y CONFIG_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y +CONFIG_ARM_FFA_TRANSPORT=y +CONFIG_SANDBOX_FFA=y \ No newline at end of file diff --git a/doc/arch/sandbox.rst b/doc/arch/sandbox.rst index 068d4a3be4..5d7e1b2c48 100644 --- a/doc/arch/sandbox.rst +++ b/doc/arch/sandbox.rst @@ -203,6 +203,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 40b467b0a5..263481de96 100644 --- a/drivers/firmware/arm-ffa/Kconfig +++ b/drivers/firmware/arm-ffa/Kconfig @@ -2,8 +2,8 @@
config ARM_FFA_TRANSPORT bool "Enable Arm Firmware Framework for Armv8-A driver" - depends on DM && ARM64 - select ARM_SMCCC + depends on DM && (ARM64 || SANDBOX) + select ARM_SMCCC if !SANDBOX select CMD_ARMFFA select LIB_UUID select DEVRES @@ -38,3 +38,9 @@ config ARM_FFA_EFI_RUNTIME_MODE The driver Code needed at runtime is placed at EFI runtime code section. Turning this on makes ffa_copy_runtime_data available for use and the driver code placed at EFI runtime code section. + +config SANDBOX_FFA + bool "FF-A Sandbox driver" + depends on ARM_FFA_TRANSPORT && SANDBOX + help + This emulates the FF-A handling under Sandbox and allows to test the FF-A driver diff --git a/drivers/firmware/arm-ffa/Makefile b/drivers/firmware/arm-ffa/Makefile index 0b9b0a61b4..d50060b836 100644 --- a/drivers/firmware/arm-ffa/Makefile +++ b/drivers/firmware/arm-ffa/Makefile @@ -5,3 +5,4 @@
obj-y += arm-ffa-uclass.o core.o obj-$(CONFIG_ARM_FFA_EFI_RUNTIME_MODE) += efi_ffa_runtime_data_mgr.o +obj-$(CONFIG_SANDBOX_FFA) += sandbox.o diff --git a/drivers/firmware/arm-ffa/arm_ffa_prv.h b/drivers/firmware/arm-ffa/arm_ffa_prv.h index 7bc90f7f66..3e0d4c112c 100644 --- a/drivers/firmware/arm-ffa/arm_ffa_prv.h +++ b/drivers/firmware/arm-ffa/arm_ffa_prv.h @@ -19,6 +19,16 @@ /* FF-A core driver name */ #define FFA_DRV_NAME "arm_ffa"
+/* The FF-A SMC function definitions */ + +#if CONFIG_IS_ENABLED(SANDBOX_FFA) +#include "sandbox_arm_ffa.h" +#else +typedef struct arm_smccc_1_2_regs ffa_value_t; +#endif + +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) @@ -94,11 +104,6 @@ struct ffa_abi_errmap { #define FFA_ERRMAP_COUNT (FFA_LAST_ID - FFA_FIRST_ID + 1) #define FFA_ID_TO_ERRMAP_ID(ffa_id) ((ffa_id) - FFA_FIRST_ID)
-/* The FF-A SMC function definitions */ - -typedef struct arm_smccc_1_2_regs ffa_value_t; -typedef void (*invoke_ffa_fn_t)(ffa_value_t args, ffa_value_t *res); - /* * struct ffa_partition_uuid - 16 bytes UUID transmitted by FFA_PARTITION_INFO_GET * @a1-4: 32-bit words access to the UUID data diff --git a/drivers/firmware/arm-ffa/core.c b/drivers/firmware/arm-ffa/core.c index 41c7b96e68..caba10caae 100644 --- a/drivers/firmware/arm-ffa/core.c +++ b/drivers/firmware/arm-ffa/core.c @@ -1101,6 +1101,7 @@ static int __ffa_runtime ffa_msg_send_direct_req(u16 dst_part_id, struct ffa_sen return ffa_to_std_errno(ffa_errno); }
+#if !CONFIG_IS_ENABLED(SANDBOX_FFA) /** * __arm_ffa_fn_smc - SMC wrapper * @args: FF-A ABI arguments to be copied to Xn registers @@ -1114,6 +1115,7 @@ void __ffa_runtime __arm_ffa_fn_smc(ffa_value_t args, ffa_value_t *res) { arm_smccc_1_2_smc(&args, res); } +#endif
/** * ffa_set_smc_conduit - Set the SMC conduit @@ -1127,7 +1129,12 @@ void __ffa_runtime __arm_ffa_fn_smc(ffa_value_t args, ffa_value_t *res) */ static int ffa_set_smc_conduit(void) { - ffa_priv_data->invoke_ffa_fn = __arm_ffa_fn_smc; +#if CONFIG_IS_ENABLED(SANDBOX_FFA) + ffa_priv_data->invoke_ffa_fn = sandbox_arm_ffa_smccc_smc; + ffa_info("Using SMC emulation"); +#else + ffa_priv_data->invoke_ffa_fn = __arm_ffa_fn_smc; +#endif
if (!ffa_priv_data->invoke_ffa_fn) { ffa_err("failure to set the invoke function"); @@ -1304,17 +1311,18 @@ struct ffa_prvdata **ffa_bus_prvdata_get(void) }
/** - * ffa_bus_discover - discover FF-A bus and probe the arm_ffa device + * ffa_bus_discover - discover FF-A bus and probe arm_ffa and sandbox_arm_ffa devices * * This boot time function makes sure the FF-A bus is discoverable. - * Then, the arm_ffa device is probed and ready to use. + * Then, the arm_ffa and sandbox_arm_ffa devices are ready to use. + * * This function is called automatically at initcalls * level (after u-boot relocation). * * When the bus was already discovered successfully the discovery will not run again. * * Arm FF-A transport is implemented through arm_ffa u-boot device managing the FF-A - * communication. + * communication. In Sandbox mode sandbox_arm_ffa is used to test arm_ffa driver. * All FF-A clients should use the arm_ffa device to use the FF-A transport. * * Return: @@ -1325,9 +1333,15 @@ int ffa_bus_discover(void) { int ret = 0;
- if (!ffa_priv_data) + if (!ffa_priv_data) { ret = ffa_device_get();
+#if CONFIG_IS_ENABLED(SANDBOX_FFA) + if (ret == 0) + ret = sandbox_ffa_device_get(); +#endif + } + return ret; }
diff --git a/drivers/firmware/arm-ffa/sandbox.c b/drivers/firmware/arm-ffa/sandbox.c new file mode 100644 index 0000000000..16e1fdc809 --- /dev/null +++ b/drivers/firmware/arm-ffa/sandbox.c @@ -0,0 +1,659 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include "sandbox_arm_ffa_prv.h" +#include <asm/global_data.h> +#include <common.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> +#include <mapmem.h> +#include <string.h> + +DECLARE_GLOBAL_DATA_PTR; + +/** + * The device private data structure containing all the emulated secure world data + */ +static struct sandbox_ffa_prvdata sandbox_ffa_priv_data = {0}; + +/* 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_SP2_ID, .exec_ctxt = 0x9587, .properties = 0x45325621 }, + .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_SP3_ID, .exec_ctxt = 0x7687, .properties = 0x23325621 }, + .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, + } + } + +}; + +/* + * Driver functions + */ + +/** + * sandbox_ffa_get_device - probes the sandbox_arm_ffa device + * + * This function makes sure the sandbox_arm_ffa device is probed + * This function makes sure the sandbox_arm_ffa device is + * created, bound to this driver, probed and ready to use. + * + * sandbox_arm_ffa depends on arm_ffa device. This dependency is + * handled by ffa_bus_discover function. arm_ffa is probed first then + * sandbox_arm_ffa. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int sandbox_ffa_device_get(void) +{ + int ret; + + if (sandbox_ffa_priv_data.dev) + return 0; + + ret = device_bind(dm_root(), + DM_DRIVER_GET(sandbox_arm_ffa), + FFA_SANDBOX_DRV_NAME, + NULL, + ofnode_null(), + &sandbox_ffa_priv_data.dev); + if (ret) { + sandbox_ffa_priv_data.dev = NULL; + return ret; + } + + ret = device_probe(sandbox_ffa_priv_data.dev); + if (ret) { + ffa_err("[Sandbox] can not probe the device"); + device_unbind(sandbox_ffa_priv_data.dev); + sandbox_ffa_priv_data.dev = NULL; + return ret; + } + + return 0; +} + +/** + * sandbox_ffa_version - Emulated FFA_VERSION handler function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_VERSION FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_version) +{ + sandbox_ffa_priv_data.fwk_version = FFA_VERSION_1_0; + res->a0 = sandbox_ffa_priv_data.fwk_version; + + /* x1-x7 MBZ */ + memset(FFA_X1X7_MBZ_REG_START, 0, FFA_X1X7_MBZ_CNT * sizeof(unsigned long)); + + return 0; +} + +/** + * sandbox_ffa_id_get - Emulated FFA_ID_GET handler function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_ID_GET FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_id_get) +{ + res->a0 = FFA_SMC_32(FFA_SUCCESS); + res->a1 = 0; + + sandbox_ffa_priv_data.id = NS_PHYS_ENDPOINT_ID; + res->a2 = sandbox_ffa_priv_data.id; + + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, 0, FFA_X3X7_MBZ_CNT * sizeof(unsigned long)); + + return 0; +} + +/** + * sandbox_ffa_features - Emulated FFA_FEATURES handler function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_FEATURES FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_features) +{ + 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(unsigned long)); + } else { + res->a0 = FFA_SMC_32(FFA_ERROR); + res->a2 = FFA_ERR_STAT_NOT_SUPPORTED; + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, + 0, FFA_X3X7_MBZ_CNT * sizeof(unsigned long)); + ffa_err("[Sandbox] FF-A interface 0x%lx not implemented", pargs->a1); + } + + res->a1 = 0; + + return 0; +} + +/** + * sandbox_ffa_partition_info_get - Emulated FFA_PARTITION_INFO_GET handler function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_PARTITION_INFO_GET FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_partition_info_get) +{ + struct ffa_partition_info *rxbuf_desc_info = NULL; + u32 descs_cnt; + u32 descs_size_bytes; + + res->a0 = FFA_SMC_32(FFA_ERROR); + + if (!sandbox_ffa_priv_data.pair.rxbuf) { + res->a2 = FFA_ERR_STAT_DENIED; + goto cleanup; + } + + if (sandbox_ffa_priv_data.pair_info.rxbuf_owned) { + res->a2 = FFA_ERR_STAT_BUSY; + goto cleanup; + } + + if (!sandbox_ffa_priv_data.partitions.descs) { + sandbox_ffa_priv_data.partitions.descs = sandbox_partitions; + sandbox_ffa_priv_data.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 descriptors buffer size */ + if ((sandbox_ffa_priv_data.pair_info.rxtx_buf_size * SZ_4K) < descs_size_bytes) { + res->a2 = FFA_ERR_STAT_NO_MEMORY; + goto cleanup; + } + + rxbuf_desc_info = (struct ffa_partition_info *)sandbox_ffa_priv_data.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++) = + sandbox_ffa_priv_data.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 */ + sandbox_ffa_priv_data.pair_info.rxbuf_owned = 1; + + goto cleanup; + } + + /* + * A UUID is specified. Return the information of all partitions matching + * the UUID + */ + + for (descs_cnt = 0 ; descs_cnt < SANDBOX_PARTITIONS_CNT ; descs_cnt++) + if (pargs->a1 == sandbox_ffa_priv_data.partitions.descs[descs_cnt].sp_uuid.a1 && + pargs->a2 == sandbox_ffa_priv_data.partitions.descs[descs_cnt].sp_uuid.a2 && + pargs->a3 == sandbox_ffa_priv_data.partitions.descs[descs_cnt].sp_uuid.a3 && + pargs->a4 == sandbox_ffa_priv_data.partitions.descs[descs_cnt].sp_uuid.a4) { + *(rxbuf_desc_info++) = + sandbox_ffa_priv_data.partitions.descs[descs_cnt].info; + } + + if (rxbuf_desc_info != ((struct ffa_partition_info *)sandbox_ffa_priv_data.pair.rxbuf)) { + res->a0 = FFA_SMC_32(FFA_SUCCESS); + /* store the partitions count */ + res->a2 = (unsigned long) + (rxbuf_desc_info - (struct ffa_partition_info *) + sandbox_ffa_priv_data.pair.rxbuf); + + /* transfer ownership to the consumer: the non secure world */ + sandbox_ffa_priv_data.pair_info.rxbuf_owned = 1; + } else { + /* Unrecognized UUID */ + res->a2 = FFA_ERR_STAT_INVALID_PARAMETERS; + } + +cleanup: + + ffa_err("[Sandbox] FFA_PARTITION_INFO_GET (%ld)", res->a2); + + res->a1 = 0; + + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, 0, FFA_X3X7_MBZ_CNT * sizeof(unsigned long)); + + return 0; +} + +/** + * sandbox_ffa_rxtx_map - Emulated FFA_RXTX_MAP handler function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_RXTX_MAP FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_rxtx_map) +{ + res->a0 = FFA_SMC_32(FFA_ERROR); + + if (sandbox_ffa_priv_data.pair.txbuf && sandbox_ffa_priv_data.pair.rxbuf) { + res->a2 = FFA_ERR_STAT_DENIED; + goto feedback; + } + + if (pargs->a3 >= RXTX_BUFFERS_MIN_PAGES && pargs->a1 && pargs->a2) { + sandbox_ffa_priv_data.pair.txbuf = pargs->a1; + sandbox_ffa_priv_data.pair.rxbuf = pargs->a2; + sandbox_ffa_priv_data.pair_info.rxtx_buf_size = pargs->a3; + sandbox_ffa_priv_data.pair_info.rxbuf_mapped = 1; + res->a0 = FFA_SMC_32(FFA_SUCCESS); + res->a2 = 0; + goto feedback; + } + + if (!pargs->a1 || !pargs->a2) + res->a2 = FFA_ERR_STAT_INVALID_PARAMETERS; + else + res->a2 = FFA_ERR_STAT_NO_MEMORY; + + ffa_err("[Sandbox] error in FFA_RXTX_MAP arguments (%d)", (int)res->a2); + +feedback: + + res->a1 = 0; + + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, + 0, FFA_X3X7_MBZ_CNT * sizeof(unsigned long)); + + return 0; +} + +/** + * sandbox_ffa_rxtx_unmap - Emulated FFA_RXTX_UNMAP handler function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_RXTX_UNMAP FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_rxtx_unmap) +{ + res->a0 = FFA_SMC_32(FFA_ERROR); + res->a2 = FFA_ERR_STAT_INVALID_PARAMETERS; + + if (GET_NS_PHYS_ENDPOINT_ID(pargs->a1) != sandbox_ffa_priv_data.id) + goto feedback; + + if (sandbox_ffa_priv_data.pair.txbuf && sandbox_ffa_priv_data.pair.rxbuf) { + sandbox_ffa_priv_data.pair.txbuf = 0; + sandbox_ffa_priv_data.pair.rxbuf = 0; + sandbox_ffa_priv_data.pair_info.rxtx_buf_size = 0; + sandbox_ffa_priv_data.pair_info.rxbuf_mapped = 0; + res->a0 = FFA_SMC_32(FFA_SUCCESS); + res->a2 = 0; + goto feedback; + } + + ffa_err("[Sandbox] No buffer pair registered on behalf of the caller"); + +feedback: + + res->a1 = 0; + + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, + 0, FFA_X3X7_MBZ_CNT * sizeof(unsigned long)); + + return 0; +} + +/** + * sandbox_ffa_rx_release - Emulated FFA_RX_RELEASE handler function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_RX_RELEASE FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_rx_release) +{ + if (!sandbox_ffa_priv_data.pair_info.rxbuf_owned) { + res->a0 = FFA_SMC_32(FFA_ERROR); + res->a2 = FFA_ERR_STAT_DENIED; + } else { + sandbox_ffa_priv_data.pair_info.rxbuf_owned = 0; + res->a0 = FFA_SMC_32(FFA_SUCCESS); + res->a2 = 0; + } + + res->a1 = 0; + + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, + 0, FFA_X3X7_MBZ_CNT * sizeof(unsigned long)); + + return 0; +} + +/** + * sandbox_ffa_sp_valid - Checks SP validity + * @part_id: partition ID to check + * + * This is the function searches the input ID in the descriptors table. + * + * Return: + * + * 1 on success (Partition found). Otherwise, failure + */ +static int sandbox_ffa_sp_valid(u16 part_id) +{ + u32 descs_cnt; + + for (descs_cnt = 0 ; descs_cnt < SANDBOX_PARTITIONS_CNT ; descs_cnt++) + if (sandbox_ffa_priv_data.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 function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_MSG_SEND_DIRECT_{REQ,RESP} + * FF-A functions. + * + * 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 + */ +SANDBOX_SMC_FFA_ABI(ffa_msg_send_direct_req) +{ + u16 part_id; + + part_id = GET_DST_SP_ID(pargs->a1); + + if ((GET_NS_PHYS_ENDPOINT_ID(pargs->a1) != sandbox_ffa_priv_data.id) || + !sandbox_ffa_sp_valid(part_id) || + pargs->a2) { + res->a0 = FFA_SMC_32(FFA_ERROR); + res->a1 = 0; + res->a2 = FFA_ERR_STAT_INVALID_PARAMETERS; + + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, + 0, FFA_X3X7_MBZ_CNT * sizeof(unsigned long)); + + return 0; + } + + res->a0 = FFA_SMC_64(FFA_MSG_SEND_DIRECT_RESP); + + res->a1 = PREP_SRC_SP_ID(part_id) | + PREP_NS_PHYS_ENDPOINT_ID(sandbox_ffa_priv_data.id); + + res->a2 = 0; + + /* + * return 0xff bytes as a response + */ + res->a3 = 0xffffffffffffffff; + res->a4 = 0xffffffffffffffff; + res->a5 = 0xffffffffffffffff; + res->a6 = 0xffffffffffffffff; + res->a7 = 0xffffffffffffffff; + + return 0; +} + +/** + * sandbox_ffa_get_prv_data - Returns the pointer to FF-A core pivate data + * @func_data: Pointer to the FF-A function arguments container structure + * + * This is the handler that returns the address of the FF-A core pivate data. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int sandbox_ffa_get_prv_data(struct ffa_sandbox_data *func_data) +{ + if (!func_data) + return -EINVAL; + + if (!func_data->data0 || func_data->data0_size != sizeof(struct ffa_prvdata **)) + return -EINVAL; + + if (!func_data->data1 || func_data->data1_size != sizeof(struct sandbox_ffa_prvdata **)) + return -EINVAL; + + *((struct ffa_prvdata **)func_data->data0) = *(ffa_bus_prvdata_get()); + *((struct sandbox_ffa_prvdata **)func_data->data1) = &sandbox_ffa_priv_data; + + return 0; +} + +/** + * sandbox_ffa_get_rxbuf_flags - Reading the mapping/ownership flags + * @queried_func_id: The FF-A function to be queried + * @func_data: Pointer to the FF-A function arguments container structure + * + * This is the handler that queries 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(u32 queried_func_id, struct ffa_sandbox_data *func_data) +{ + 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) = sandbox_ffa_priv_data.pair_info.rxbuf_mapped; + return 0; + case FFA_RX_RELEASE: + *((u8 *)func_data->data0) = sandbox_ffa_priv_data.pair_info.rxbuf_owned; + return 0; + default: + ffa_err("[Sandbox] The querried FF-A interface flag (%d) undefined", + queried_func_id); + return -EINVAL; + } +} + +/** + * sandbox_ffa_query_core_state - The driver dispatcher function + * @queried_func_id: The FF-A function to be queried + * @func_data: Pointer to the FF-A function arguments container structure + * + * Queries the status of FF-A ABI specified in the input argument. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int sandbox_ffa_query_core_state(u32 queried_func_id, struct ffa_sandbox_data *func_data) +{ + switch (queried_func_id) { + case FFA_VERSION: + case FFA_ID_GET: + case FFA_FEATURES: + return sandbox_ffa_get_prv_data(func_data); + case FFA_RXTX_MAP: + case FFA_RXTX_UNMAP: + case FFA_RX_RELEASE: + return sandbox_ffa_get_rxbuf_flags(queried_func_id, func_data); + default: + ffa_err("[Sandbox] The querried FF-A interface (%d) undefined", 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 + * + * Sandbox driver emulates the FF-A ABIs SMC call using this function. + * 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; + + switch (args.a0) { + case FFA_SMC_32(FFA_VERSION): + ret = sandbox_ffa_version(&args, res); + break; + case FFA_SMC_32(FFA_PARTITION_INFO_GET): + ret = sandbox_ffa_partition_info_get(&args, res); + break; + case FFA_SMC_32(FFA_RXTX_UNMAP): + ret = sandbox_ffa_rxtx_unmap(&args, res); + break; + case FFA_SMC_64(FFA_MSG_SEND_DIRECT_REQ): + ret = sandbox_ffa_msg_send_direct_req(&args, res); + break; + case FFA_SMC_32(FFA_ID_GET): + ret = sandbox_ffa_id_get(&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(&args, res); + break; + case FFA_SMC_32(FFA_RX_RELEASE): + ret = sandbox_ffa_rx_release(&args, res); + break; + default: + ffa_err("[Sandbox] Undefined FF-A interface (0x%lx)", args.a0); + } + + if (ret != 0) + ffa_err("[Sandbox] FF-A ABI internal failure (%d)", ret); +} + +/** + * sandbox_ffa_probe - The driver probe function + * @dev: the sandbox_arm_ffa device + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int sandbox_ffa_probe(struct udevice *dev) +{ + return 0; +} + +/** + * sandbox_ffa_remove - The driver remove function + * @dev: the sandbox_arm_ffa device + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int sandbox_ffa_remove(struct udevice *dev) +{ + ffa_info("[Sandbox] removing the device"); + memset(&sandbox_ffa_priv_data, 0, sizeof(sandbox_ffa_priv_data)); + return 0; +} + +/** + * Declaring the sandbox_arm_ffa driver under UCLASS_FFA + */ +U_BOOT_DRIVER(sandbox_arm_ffa) = { + .name = FFA_SANDBOX_DRV_NAME, + .id = UCLASS_FFA, + .probe = sandbox_ffa_probe, + .remove = sandbox_ffa_remove, +}; diff --git a/drivers/firmware/arm-ffa/sandbox_arm_ffa_prv.h b/drivers/firmware/arm-ffa/sandbox_arm_ffa_prv.h new file mode 100644 index 0000000000..4db57f5092 --- /dev/null +++ b/drivers/firmware/arm-ffa/sandbox_arm_ffa_prv.h @@ -0,0 +1,144 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __SANDBOX_ARM_FFA_PRV_H +#define __SANDBOX_ARM_FFA_PRV_H + +#include "arm_ffa_prv.h" +#include <sandbox_arm_ffa.h> + +/* + * This header is private. It is exclusively used by the Sandbox FF-A driver + */ + +/* FF-A core driver name */ +#define FFA_SANDBOX_DRV_NAME "sandbox_arm_ffa" + +/* FF-A ABIs internal error codes (as defined by the spec) */ + +#define FFA_ERR_STAT_NOT_SUPPORTED -1 +#define FFA_ERR_STAT_INVALID_PARAMETERS -2 +#define FFA_ERR_STAT_NO_MEMORY -3 +#define FFA_ERR_STAT_BUSY -4 +#define FFA_ERR_STAT_DENIED -6 + +/* Providing Arm SMCCC declarations to sandbox */ + +#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)) + +/* 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) + +/* secure partitions count */ +#define SANDBOX_PARTITIONS_CNT (4) + +/* 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: the consumer) + * @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 as set by the FF-A core driver + * + * Data structure hosting 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_prvdata - the driver private data structure + * + * @dev: The arm_ffa device under u-boot driver model + * @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 + * @conduit: The selected conduit + * + * The driver data structure hosting all the emulated secure world data. + */ +struct sandbox_ffa_prvdata { + struct udevice *dev; + u32 fwk_version; + u16 id; + struct ffa_partitions partitions; + struct ffa_rxtxpair pair; + struct ffa_rxtxpair_info pair_info; +}; + +#define SANDBOX_SMC_FFA_ABI(ffabi) static int sandbox_##ffabi(ffa_value_t *pargs, ffa_value_t *res) + +#endif diff --git a/include/arm_ffa.h b/include/arm_ffa.h index f17b100497..665413a0c5 100644 --- a/include/arm_ffa.h +++ b/include/arm_ffa.h @@ -111,7 +111,7 @@ struct ffa_bus_ops { const struct ffa_bus_ops * __ffa_runtime ffa_bus_ops_get(void);
/** - * ffa_bus_discover - discover FF-A bus and probes the arm_ffa device + * ffa_bus_discover - discover FF-A bus and probes the arm_ffa and sandbox_arm_ffa devices */ int ffa_bus_discover(void);
diff --git a/include/sandbox_arm_ffa.h b/include/sandbox_arm_ffa.h new file mode 100644 index 0000000000..d5df16f282 --- /dev/null +++ b/include/sandbox_arm_ffa.h @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __SANDBOX_ARM_FFA_H +#define __SANDBOX_ARM_FFA_H + +#include <arm_ffa.h> + +/** + * struct sandbox_smccc_1_2_regs - Arguments for or Results from emulated SMC call + * @a0-a17 argument values from registers 0 to 17 + */ +struct sandbox_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; +}; + +typedef struct sandbox_smccc_1_2_regs ffa_value_t; + +/* UUIDs of services supported by the sandbox driver */ +#define SANDBOX_SERVICE1_UUID "ed32d533-4209-99e6-2d72-cdd998a79cc0" +#define SANDBOX_SERVICE2_UUID "ed32d544-4209-99e6-2d72-cdd998a79cc0" +#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" + +#define SANDBOX_SP_COUNT_PER_VALID_SERVICE 2 + +/** + * struct ffa_sandbox_data - generic data structure used to exchange + * data between test cases and the sandbox driver + * @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 + * + * Using this structure sandbox test cases can pass various types of data with different sizes. + */ +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 driver public functions + */ + +/** + * sandbox_ffa_query_core_state - Queries the status of FF-A ABIs + */ +int sandbox_ffa_query_core_state(u32 queried_func_id, struct ffa_sandbox_data *func_data); + +/** + * sandbox_ffa_get_device - create, bind and probe the sandbox_arm_ffa device + */ +int sandbox_ffa_device_get(void); + +/** + * sandbox_arm_ffa_smccc_smc - FF-A SMC call emulation + */ +void sandbox_arm_ffa_smccc_smc(ffa_value_t args, ffa_value_t *res); + +#endif diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index af0b0f3db1..d404343a7d 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -2185,7 +2185,7 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL); }
-#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT) +#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT) && !CONFIG_IS_ENABLED(SANDBOX_FFA) /* unmap FF-A RX/TX buffers */ if (ffa_bus_ops_get()->rxtx_unmap()) debug("[efi_boottime][ERROR]: can not unmap FF-A RX/TX buffers\n");

Add functional test cases for the FF-A core driver
These tests rely on the FF-A Sandbox driver which helps in inspecting the FF-A core driver.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v4: align sandbox tests with the new FF-A driver interfaces and new way of error handling
v1: introduce sandbox tests
MAINTAINERS | 1 + test/dm/Makefile | 2 + test/dm/ffa.c | 394 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 397 insertions(+) create mode 100644 test/dm/ffa.c
diff --git a/MAINTAINERS b/MAINTAINERS index 6f01002e34..8c0dfff5f8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -256,6 +256,7 @@ F: doc/README.ffa.drv F: drivers/firmware/arm-ffa/ F: include/arm_ffa.h F: include/sandbox_arm_ffa.h +F: test/dm/ffa.c
ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de diff --git a/test/dm/Makefile b/test/dm/Makefile index 7543df8823..e5a791768e 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0+ # # Copyright (c) 2013 Google, Inc +# (C) Copyright 2022 ARM Limited
obj-$(CONFIG_UT_DM) += test-dm.o
@@ -81,6 +82,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_SANDBOX_FFA) += 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 0000000000..052d5fc3f4 --- /dev/null +++ b/test/dm/ffa.c @@ -0,0 +1,394 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Functional tests for UCLASS_FFA class + * + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <console.h> +#include <dm.h> +#include <dm/test.h> +#include "../../drivers/firmware/arm-ffa/sandbox_arm_ffa_prv.h" +#include <sandbox_arm_ffa.h> +#include <test/test.h> +#include <test/ut.h> + +/* Macros */ + +#define LOG_MSG_SZ (100) +#define LOG_CMD_SZ (LOG_MSG_SZ * 2) + +/* Functional tests for the UCLASS_FFA */ + +static int dm_test_ffa_log(struct unit_test_state *uts, char *msg) +{ + char cmd[LOG_CMD_SZ] = {0}; + + console_record_reset(); + + snprintf(cmd, LOG_CMD_SZ, "echo "%s"", msg); + run_command(cmd, 0); + + ut_assert_console_end(); + + return CMD_RET_SUCCESS; +} + +static int check_fwk_version(struct ffa_prvdata *prvdata, struct sandbox_ffa_prvdata *sdx_prvdata, + struct unit_test_state *uts) +{ + if (prvdata->fwk_version != sdx_prvdata->fwk_version) { + char msg[LOG_MSG_SZ] = {0}; + + snprintf(msg, LOG_MSG_SZ, + "[%s]: Error: framework version: core = 0x%x , sandbox = 0x%x", __func__, + prvdata->fwk_version, + sdx_prvdata->fwk_version); + + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + return CMD_RET_SUCCESS; +} + +static int check_endpoint_id(struct ffa_prvdata *prvdata, struct unit_test_state *uts) +{ + if (prvdata->id) { + char msg[LOG_MSG_SZ] = {0}; + + snprintf(msg, LOG_MSG_SZ, + "[%s]: Error: endpoint id: core = 0x%x", __func__, prvdata->id); + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + return CMD_RET_SUCCESS; +} + +static int check_core_dev(struct ffa_prvdata *prvdata, struct unit_test_state *uts) +{ + if (!prvdata->dev) { + char msg[LOG_MSG_SZ] = {0}; + + snprintf(msg, LOG_MSG_SZ, "[%s]: Error: core device NULL", __func__); + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + return CMD_RET_SUCCESS; +} + +static int check_sandbox_dev(struct sandbox_ffa_prvdata *sdx_prvdata, struct unit_test_state *uts) +{ + if (!sdx_prvdata->dev) { + char msg[LOG_MSG_SZ] = {0}; + + snprintf(msg, LOG_MSG_SZ, "[%s]: Error: sandbox device NULL", __func__); + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + return CMD_RET_SUCCESS; +} + +static int check_rxtxbuf(struct ffa_prvdata *prvdata, struct unit_test_state *uts) +{ + if (!prvdata->pair.rxbuf && prvdata->pair.txbuf) { + char msg[LOG_MSG_SZ] = {0}; + + snprintf(msg, LOG_MSG_SZ, "[%s]: Error: rxbuf = 0x%llx txbuf = 0x%llx", __func__, + prvdata->pair.rxbuf, + prvdata->pair.txbuf); + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + return CMD_RET_SUCCESS; +} + +static int check_features(struct ffa_prvdata *prvdata, struct unit_test_state *uts) +{ + char msg[LOG_MSG_SZ] = {0}; + + if (prvdata->pair.rxtx_min_pages != RXTX_4K && + prvdata->pair.rxtx_min_pages != RXTX_16K && + prvdata->pair.rxtx_min_pages != RXTX_64K) { + snprintf(msg, + LOG_MSG_SZ, + "[%s]: Error: FFA_RXTX_MAP features = 0x%lx", + __func__, + prvdata->pair.rxtx_min_pages); + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + + return CMD_RET_SUCCESS; +} + +static int check_rxbuf_mapped_flag(u32 queried_func_id, + u8 rxbuf_mapped, + struct unit_test_state *uts) +{ + char msg[LOG_MSG_SZ] = {0}; + + switch (queried_func_id) { + case FFA_RXTX_MAP: + { + if (rxbuf_mapped) + return CMD_RET_SUCCESS; + break; + } + case FFA_RXTX_UNMAP: + { + if (!rxbuf_mapped) + return CMD_RET_SUCCESS; + break; + } + default: + return CMD_RET_FAILURE; + } + + snprintf(msg, LOG_MSG_SZ, "[%s]: Error: %s mapping issue", __func__, + (queried_func_id == FFA_RXTX_MAP ? "FFA_RXTX_MAP" : "FFA_RXTX_UNMAP")); + dm_test_ffa_log(uts, msg); + + return CMD_RET_FAILURE; +} + +static int check_rxbuf_release_flag(u8 rxbuf_owned, struct unit_test_state *uts) +{ + if (rxbuf_owned) { + char msg[LOG_MSG_SZ] = {0}; + + snprintf(msg, LOG_MSG_SZ, "[%s]: Error: RX buffer not released", __func__); + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + return CMD_RET_SUCCESS; +} + +static int test_ffa_msg_send_direct_req(u16 part_id, struct unit_test_state *uts) +{ + struct ffa_send_direct_data msg = {0}; + u8 cnt; + + ut_assertok(ffa_bus_ops_get()->sync_send_receive(part_id, &msg)); + + for (cnt = 0; cnt < sizeof(struct ffa_send_direct_data) / sizeof(u64); cnt++) + ut_assertok(((u64 *)&msg)[cnt] != 0xffffffffffffffff); + + return CMD_RET_SUCCESS; +} + +static int test_partitions_and_comms(const char *service_uuid, + struct sandbox_ffa_prvdata *sdx_prvdata, + struct unit_test_state *uts) +{ + u32 count = 0, size = 0; + struct ffa_partition_info *parts_info; + u32 info_idx, exp_info_idx; + int ret; + + /* + * get from the driver the count of the SPs matching the UUID + */ + ret = ffa_bus_ops_get()->partition_info_get(service_uuid, &count, NULL); + /* make sure partitions are detected */ + ut_assertok(ret != 0); + ut_assertok(count != SANDBOX_SP_COUNT_PER_VALID_SERVICE); + + /* + * pre-allocate a buffer to be filled by the driver + * with ffa_partition_info structs + */ + + parts_info = calloc(count, sizeof(struct ffa_partition_info)); + ut_assertok(!parts_info); + + size = count * sizeof(struct ffa_partition_info); + + /* + * ask the driver to fill the buffer with the SPs info + */ + ret = ffa_bus_ops_get()->partition_info_get(service_uuid, &size, parts_info); + if (ret != 0) { + free(parts_info); + ut_assertok(ret != 0); + } + + /* + * SPs found , verify the partitions information + */ + + ret = CMD_RET_FAILURE; + + for (info_idx = 0; info_idx < count ; info_idx++) { + for (exp_info_idx = 0; + exp_info_idx < sdx_prvdata->partitions.count; + exp_info_idx++) { + if (parts_info[info_idx].id == + sdx_prvdata->partitions.descs[exp_info_idx].info.id) { + ret = memcmp(&parts_info[info_idx], + &sdx_prvdata->partitions.descs[exp_info_idx] + .info, + sizeof(struct ffa_partition_info)); + if (ret) + free(parts_info); + ut_assertok(ret != 0); + /* send and receive data from the current partition */ + test_ffa_msg_send_direct_req(parts_info[info_idx].id, uts); + } + ret = CMD_RET_SUCCESS; + } + } + + free(parts_info); + + /* Verify expected partitions found in the emulated secure world*/ + ut_assertok(ret != CMD_RET_SUCCESS); + + return CMD_RET_SUCCESS; +} + +static int dm_test_ffa_ack(struct unit_test_state *uts) +{ + struct ffa_prvdata *prvdata = NULL; + struct sandbox_ffa_prvdata *sdx_prvdata = NULL; + struct ffa_sandbox_data func_data = {0}; + u8 rxbuf_flag = 0; + const char *svc1_uuid = SANDBOX_SERVICE1_UUID; + const char *svc2_uuid = SANDBOX_SERVICE2_UUID; + int ret; + + /* test probing FF-A devices */ + ut_assertok(ffa_bus_discover()); + + /* get a pointer to the FF-A core and sandbox drivers private data */ + func_data.data0 = &prvdata; + func_data.data0_size = sizeof(prvdata); + func_data.data1 = &sdx_prvdata; + func_data.data1_size = sizeof(sdx_prvdata); + + ut_assertok(sandbox_ffa_query_core_state(FFA_VERSION, &func_data)); + + /* make sure private data pointers are retrieved */ + ut_assertok(prvdata == 0); + ut_assertok(sdx_prvdata == 0); + + /* make sure dev devices created */ + ut_assertok(check_core_dev(prvdata, uts)); + ut_assertok(check_sandbox_dev(sdx_prvdata, uts)); + + /* test FFA_VERSION */ + ut_assertok(check_fwk_version(prvdata, sdx_prvdata, uts)); + + /* test FFA_ID_GET */ + ut_assertok(check_endpoint_id(prvdata, uts)); + + /* test FFA_FEATURES */ + ut_assertok(check_features(prvdata, uts)); + + /* test core RX/TX buffers */ + ut_assertok(check_rxtxbuf(prvdata, uts)); + + /* test FFA_RXTX_MAP */ + func_data.data0 = &rxbuf_flag; + func_data.data0_size = sizeof(rxbuf_flag); + + rxbuf_flag = 0; + ut_assertok(sandbox_ffa_query_core_state(FFA_RXTX_MAP, &func_data)); + ut_assertok(check_rxbuf_mapped_flag(FFA_RXTX_MAP, rxbuf_flag, uts)); + + /* FFA_PARTITION_INFO_GET / FFA_MSG_SEND_DIRECT_REQ */ + ret = test_partitions_and_comms(svc1_uuid, sdx_prvdata, uts); + ut_assertok(ret != CMD_RET_SUCCESS); + + /* test FFA_RX_RELEASE */ + rxbuf_flag = 1; + ut_assertok(sandbox_ffa_query_core_state(FFA_RX_RELEASE, &func_data)); + ut_assertok(check_rxbuf_release_flag(rxbuf_flag, uts)); + + /* FFA_PARTITION_INFO_GET / FFA_MSG_SEND_DIRECT_REQ */ + ret = test_partitions_and_comms(svc2_uuid, sdx_prvdata, uts); + ut_assertok(ret != CMD_RET_SUCCESS); + + /* test FFA_RX_RELEASE */ + rxbuf_flag = 1; + ut_assertok(sandbox_ffa_query_core_state(FFA_RX_RELEASE, &func_data)); + ut_assertok(check_rxbuf_release_flag(rxbuf_flag, uts)); + + /* test FFA_RXTX_UNMAP */ + ut_assertok(ffa_bus_ops_get()->rxtx_unmap()); + + rxbuf_flag = 1; + ut_assertok(sandbox_ffa_query_core_state(FFA_RXTX_UNMAP, &func_data)); + ut_assertok(check_rxbuf_mapped_flag(FFA_RXTX_UNMAP, rxbuf_flag, uts)); + + return CMD_RET_SUCCESS; +} + +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_prvdata *prvdata = NULL; + struct sandbox_ffa_prvdata *sdx_prvdata = NULL; + struct ffa_sandbox_data func_data = {0}; + 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 = {0}; + int ret; + u32 count = 0; + u16 part_id = 0; + + /* test probing FF-A devices */ + ut_assertok(ffa_bus_discover()); + + /* get a pointer to the FF-A core and sandbox drivers private data */ + func_data.data0 = &prvdata; + func_data.data0_size = sizeof(prvdata); + func_data.data1 = &sdx_prvdata; + func_data.data1_size = sizeof(sdx_prvdata); + + ut_assertok(sandbox_ffa_query_core_state(FFA_VERSION, &func_data)); + + /* make sure private data pointers are retrieved */ + ut_assertok(prvdata == 0); + ut_assertok(sdx_prvdata == 0); + + /* make sure dev devices created */ + ut_assertok(check_core_dev(prvdata, uts)); + ut_assertok(check_sandbox_dev(sdx_prvdata, uts)); + + /* query partitions count using invalid arguments */ + ret = ffa_bus_ops_get()->partition_info_get(unvalid_svc_uuid, NULL, NULL); + ut_assertok(ret != -EINVAL); + + /* query partitions count using an invalid UUID string */ + ret = ffa_bus_ops_get()->partition_info_get(unvalid_svc_uuid_str, &count, NULL); + ut_assertok(ret != -EINVAL); + + /* query partitions count using an invalid UUID (no matching SP) */ + count = 0; + ret = ffa_bus_ops_get()->partition_info_get(unvalid_svc_uuid, &count, NULL); + ut_assertok(count != 0); + + /* query partitions count using a valid UUID */ + count = 0; + ret = ffa_bus_ops_get()->partition_info_get(valid_svc_uuid, &count, NULL); + /* make sure partitions are detected */ + ut_assertok(ret != 0); + ut_assertok(count != SANDBOX_SP_COUNT_PER_VALID_SERVICE); + + /* send data to an invalid partition */ + ret = ffa_bus_ops_get()->sync_send_receive(part_id, &msg); + ut_assertok(ret != -EINVAL); + + /* send data to a valid partition */ + part_id = prvdata->partitions.descs[0].info.id; + ret = ffa_bus_ops_get()->sync_send_receive(part_id, &msg); + ut_assertok(ret != 0); + + return CMD_RET_SUCCESS; +} + +DM_TEST(dm_test_ffa_nack, UT_TESTF_SCAN_FDT | UT_TESTF_CONSOLE_REC);

Add Sandbox test for the armffa command
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v4: drop use of helper APIs
v1: introduce armffa command sandbox test
MAINTAINERS | 1 + test/cmd/Makefile | 2 ++ test/cmd/armffa.c | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+) create mode 100644 test/cmd/armffa.c
diff --git a/MAINTAINERS b/MAINTAINERS index 8c0dfff5f8..8ed94da4c8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -256,6 +256,7 @@ F: doc/README.ffa.drv F: drivers/firmware/arm-ffa/ F: include/arm_ffa.h F: include/sandbox_arm_ffa.h +F: test/cmd/armffa.c F: test/dm/ffa.c
ARM FREESCALE IMX diff --git a/test/cmd/Makefile b/test/cmd/Makefile index c331757425..19e9d0a995 100644 --- a/test/cmd/Makefile +++ b/test/cmd/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0+ # # Copyright (c) 2013 Google, Inc +# (C) Copyright 2022 ARM Limited
ifdef CONFIG_HUSH_PARSER obj-$(CONFIG_CONSOLE_RECORD) += test_echo.o @@ -13,3 +14,4 @@ obj-$(CONFIG_CMD_MEM_SEARCH) += mem_search.o obj-$(CONFIG_CMD_PINMUX) += pinmux.o obj-$(CONFIG_CMD_PWM) += pwm.o obj-$(CONFIG_CMD_SETEXPR) += setexpr.o +obj-$(CONFIG_SANDBOX_FFA) += armffa.o diff --git a/test/cmd/armffa.c b/test/cmd/armffa.c new file mode 100644 index 0000000000..531f82066e --- /dev/null +++ b/test/cmd/armffa.c @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test for armffa command + * + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <dm.h> +#include <dm/test.h> +#include <sandbox_arm_ffa.h> +#include <string.h> +#include <test/test.h> +#include <test/ut.h> + +#define PING_CMD_SIZE 19 + +/* Basic test of 'armffa' command */ +static int dm_test_armffa_cmd(struct unit_test_state *uts) +{ + char ping_cmd[PING_CMD_SIZE] = {0}; + + ut_assertok(ffa_bus_discover()); + + /* armffa getpart <UUID> */ + ut_assertok(run_command("armffa getpart " SANDBOX_SERVICE1_UUID, 0)); + + snprintf(ping_cmd, PING_CMD_SIZE, "armffa ping 0x%x", SANDBOX_SP1_ID); + + /* armffa ping <ID> */ + ut_assertok(run_command(ping_cmd, 0)); + + /* armffa devlist */ + ut_assertok(run_command("armffa devlist", 0)); + + return CMD_RET_SUCCESS; +} + +DM_TEST(dm_test_armffa_cmd, UT_TESTF_SCAN_FDT | UT_TESTF_CONSOLE_REC);

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.
FF-A driver private data is copied to EFI runtime section at ExitBootServices(). This garantees secure world partitions data are available at EFI runtime level.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Signed-off-by: Gowtham Suresh Kumar gowtham.sureshkumar@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v4:
* use the new FF-A driver interfaces * discover MM partitions at runtime * copy FF-A driver private data to EFI runtime section at ExitBootServices() * drop use of FFA_ERR_STAT_SUCCESS error code * replace EFI_BUFFER_TOO_SMALL with EFI_OUT_OF_RESOURCES in ffa_mm_communicate(). No need for efi_memcpy_runtime() anymore * revert the error log in mm_communicate() in case of failure * remove packed attribute from efi_mm_communicate_header and smm_variable_communicate_header
v2:
* set default values to 0 for FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_ADDR and MM_SP_UUID_DATA and add warnings
v1:
* introduce FF-A MM communication
arch/arm/cpu/armv8/cache.S | 19 +++ arch/arm/cpu/armv8/cache_v8.c | 6 +- include/mm_communication.h | 9 +- lib/efi_loader/Kconfig | 14 +- lib/efi_loader/efi_boottime.c | 7 + lib/efi_loader/efi_variable_tee.c | 263 +++++++++++++++++++++++++++++- 6 files changed, 309 insertions(+), 9 deletions(-)
diff --git a/arch/arm/cpu/armv8/cache.S b/arch/arm/cpu/armv8/cache.S index d1cee23437..91b7c73c17 100644 --- a/arch/arm/cpu/armv8/cache.S +++ b/arch/arm/cpu/armv8/cache.S @@ -3,6 +3,9 @@ * (C) Copyright 2013 * David Feng fenghua@phytium.com.cn * + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + * * This file is based on sample code from ARMv8 ARM. */
@@ -21,7 +24,11 @@ * x1: 0 clean & invalidate, 1 invalidate only * x2~x9: clobbered */ +#ifdef CONFIG_EFI_LOADER +.pushsection .text.efi_runtime, "ax" +#else .pushsection .text.__asm_dcache_level, "ax" +#endif ENTRY(__asm_dcache_level) lsl x12, x0, #1 msr csselr_el1, x12 /* select cache level */ @@ -65,7 +72,11 @@ ENDPROC(__asm_dcache_level) * * flush or invalidate all data cache by SET/WAY. */ +#ifdef CONFIG_EFI_LOADER +.pushsection .text.efi_runtime, "ax" +#else .pushsection .text.__asm_dcache_all, "ax" +#endif ENTRY(__asm_dcache_all) mov x1, x0 dsb sy @@ -109,7 +120,11 @@ ENTRY(__asm_flush_dcache_all) ENDPROC(__asm_flush_dcache_all) .popsection
+#ifdef CONFIG_EFI_LOADER +.pushsection .text.efi_runtime, "ax" +#else .pushsection .text.__asm_invalidate_dcache_all, "ax" +#endif ENTRY(__asm_invalidate_dcache_all) mov x0, #0x1 b __asm_dcache_all @@ -182,7 +197,11 @@ ENTRY(__asm_invalidate_icache_all) ENDPROC(__asm_invalidate_icache_all) .popsection
+#ifdef CONFIG_EFI_LOADER +.pushsection .text.efi_runtime, "ax" +#else .pushsection .text.__asm_invalidate_l3_dcache, "ax" +#endif WEAK(__asm_invalidate_l3_dcache) mov x0, #0 /* return status as success */ ret diff --git a/arch/arm/cpu/armv8/cache_v8.c b/arch/arm/cpu/armv8/cache_v8.c index e4736e5643..45f57372c2 100644 --- a/arch/arm/cpu/armv8/cache_v8.c +++ b/arch/arm/cpu/armv8/cache_v8.c @@ -5,10 +5,14 @@ * * (C) Copyright 2016 * Alexander Graf agraf@suse.de + * + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> #include <cpu_func.h> +#include <efi_loader.h> #include <hang.h> #include <log.h> #include <asm/cache.h> @@ -445,7 +449,7 @@ __weak void mmu_setup(void) /* * Performs a invalidation of the entire data cache at all levels */ -void invalidate_dcache_all(void) +void __efi_runtime invalidate_dcache_all(void) { __asm_invalidate_dcache_all(); __asm_invalidate_l3_dcache(); diff --git a/include/mm_communication.h b/include/mm_communication.h index e65fbde60d..fe9104c56d 100644 --- a/include/mm_communication.h +++ b/include/mm_communication.h @@ -6,6 +6,8 @@ * Copyright (c) 2017, Intel Corporation. All rights reserved. * Copyright (C) 2020 Linaro Ltd. sughosh.ganu@linaro.org * Copyright (C) 2020 Linaro Ltd. ilias.apalodimas@linaro.org + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#ifndef _MM_COMMUNICATION_H_ @@ -13,6 +15,9 @@
#include <part_efi.h>
+/* MM service UUID string (big-endian format). This UUID is common across all MM SPs */ +#define MM_SP_UUID "33d532ed-e699-0942-c09c-a798d9cd722d" + /* * Interface to the pseudo Trusted Application (TA), which provides a * communication channel with the Standalone MM (Management Mode) @@ -43,7 +48,7 @@ * To avoid confusion in interpreting frames, the communication buffer should * always begin with efi_mm_communicate_header. */ -struct __packed efi_mm_communicate_header { +struct efi_mm_communicate_header { efi_guid_t header_guid; size_t message_len; u8 data[]; @@ -145,7 +150,7 @@ struct smm_variable_communicate_header { * Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE. * */ -struct smm_variable_access { +struct __packed smm_variable_access { efi_guid_t guid; efi_uintn_t data_size; efi_uintn_t name_size; diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index b8fb2701a7..d292f57244 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -61,13 +61,23 @@ 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" - depends on OPTEE + bool "UEFI variables storage service via the trusted world" + depends on OPTEE || ARM_FFA_TRANSPORT 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 EFI_VARIABLE_NO_STORE bool "Don't persist non-volatile UEFI variables" help diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index d404343a7d..8a397ea21b 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -2193,6 +2193,13 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, debug("[efi_boottime][INFO]: FF-A RX/TX buffers unmapped\n"); #endif
+#if CONFIG_IS_ENABLED(ARM_FFA_EFI_RUNTIME_MODE) && !CONFIG_IS_ENABLED(SANDBOX_FFA) + if (ffa_copy_runtime_data()) + printf("ERROR: EFI: FFA: copying runtime data\n"); + else + printf("INFO: EFI: FFA: runtime data copied\n"); +#endif + /* Patch out unsupported runtime function */ efi_runtime_detach();
diff --git a/lib/efi_loader/efi_variable_tee.c b/lib/efi_loader/efi_variable_tee.c index dfef18435d..d6f24f85bd 100644 --- a/lib/efi_loader/efi_variable_tee.c +++ b/lib/efi_loader/efi_variable_tee.c @@ -4,6 +4,8 @@ * * Copyright (C) 2019 Linaro Ltd. sughosh.ganu@linaro.org * Copyright (C) 2019 Linaro Ltd. ilias.apalodimas@linaro.org + * Copyright (C) 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> @@ -15,6 +17,36 @@ #include <malloc.h> #include <mm_communication.h>
+#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) + +#include <arm_ffa.h> +#include <cpu_func.h> +#include <mapmem.h> + +#ifndef FFA_SHARED_MM_BUFFER_SIZE +#warning "FFA_SHARED_MM_BUFFER_SIZE must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_SIZE 0 +#endif + +#ifndef FFA_SHARED_MM_BUFFER_OFFSET +#warning "FFA_SHARED_MM_BUFFER_OFFSET must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_OFFSET 0 +#endif + +#ifndef FFA_SHARED_MM_BUFFER_ADDR +#warning "FFA_SHARED_MM_BUFFER_ADDR must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_ADDR 0 +#endif + +/* MM return codes */ +#define MM_SUCCESS (0) + +const char *mm_sp_svc_uuid = MM_SP_UUID; + +static __efi_runtime_data 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 */ static efi_uintn_t max_payload_size; /* func + data */ @@ -24,6 +56,7 @@ struct mm_connection { u32 session; };
+#if (IS_ENABLED(CONFIG_OPTEE)) /** * get_connection() - Retrieve OP-TEE session for a specific UUID. * @@ -143,16 +176,227 @@ static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize)
return ret; } +#endif + +#if (IS_ENABLED(CONFIG_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 + * + * Notifies 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 __efi_runtime ffa_notify_mm_sp(void) +{ + struct ffa_send_direct_data msg = {0}; + int ret; + int sp_event_ret = -1; + + if (!ffa_bus_ops_get()) + return -EINVAL; + + msg.data0 = FFA_SHARED_MM_BUFFER_OFFSET; /* x3 */ + + ret = ffa_bus_ops_get()->sync_send_receive(mm_sp_id, &msg); + if (ret != 0) + return ret; + + sp_event_ret = msg.data0; /* x3 */ + + if (sp_event_ret == MM_SUCCESS) + return 0; + + /* + * Failure to notify the MM SP + */ + + return -EACCES; +} + +/** + * 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, size = 0; + int ret; + struct ffa_partition_info *parts_info; + + if (!ffa_bus_ops_get()) + return -EINVAL; + + /* + * get from the driver the count of the SPs matching the UUID + */ + ret = ffa_bus_ops_get()->partition_info_get(mm_sp_svc_uuid, &count, NULL); + if (ret != 0) { + log_err("EFI: Failure in querying partitions count (error code: %d)\n", ret); + return ret; + } + + if (!count) { + log_info("EFI: No MM partition found\n"); + return ret; + } + + /* + * pre-allocate a buffer to be filled by the driver + * with ffa_partition_info structs + */ + + log_info("EFI: Pre-allocating %d partition(s) info structures\n", count); + + parts_info = calloc(count, sizeof(struct ffa_partition_info)); + if (!parts_info) + return -EINVAL; + + size = count * sizeof(struct ffa_partition_info); + + /* + * ask the driver to fill the + * buffer with the SPs info + */ + ret = ffa_bus_ops_get()->partition_info_get(mm_sp_svc_uuid, &size, parts_info); + if (ret != 0) { + log_err("EFI: Failure in querying partition(s) info (error code: %d)\n", ret); + free(parts_info); + return ret; + } + + /* + * MM SPs found , use the first one + */ + + mm_sp_id = parts_info[0].id; + + log_info("EFI: MM partition ID 0x%x\n", mm_sp_id); + + free(parts_info); + + 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 + * + * Issues 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 __efi_runtime ffa_mm_communicate(void *comm_buf, ulong comm_buf_size) +{ + ulong tx_data_size; + int ffa_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() != 0) { + log_err("EFI: Failure to discover MM partition ID at boot time\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 > FFA_SHARED_MM_BUFFER_SIZE) + return EFI_INVALID_PARAMETER; + + /* Copy the data to the shared buffer */ + + virt_shared_buf = (void *)map_sysmem((phys_addr_t)FFA_SHARED_MM_BUFFER_ADDR, 0); + efi_memcpy_runtime(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(); + if (ffa_ret) + unmap_sysmem(virt_shared_buf); + + 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) { + unmap_sysmem(virt_shared_buf); + return EFI_OUT_OF_RESOURCES; + } + + efi_memcpy_runtime(comm_buf, virt_shared_buf, rx_data_size); + unmap_sysmem(virt_shared_buf); + + return EFI_SUCCESS; + } + case -EINVAL: + return EFI_DEVICE_ERROR; + case -EPERM: + return EFI_INVALID_PARAMETER; + case -EACCES: + return EFI_ACCESS_DENIED; + case -EBUSY: + return EFI_OUT_OF_RESOURCES; + default: + return EFI_ACCESS_DENIED; + } +} +#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 MM SP (also called partition) can be 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, StandAlonneMM and smm-gateway are supported. + * * Return: status code */ -static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) +static efi_status_t __efi_runtime mm_communicate(u8 *comm_buf, efi_uintn_t dsize) { efi_status_t ret; struct efi_mm_communicate_header *mm_hdr; @@ -162,7 +406,11 @@ static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) mm_hdr = (struct efi_mm_communicate_header *)comm_buf; var_hdr = (struct smm_variable_communicate_header *)mm_hdr->data;
+ #if (IS_ENABLED(CONFIG_OPTEE)) ret = optee_mm_communicate(comm_buf, dsize); + #elif (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) + ret = ffa_mm_communicate(comm_buf, dsize); + #endif if (ret != EFI_SUCCESS) { log_err("%s failed!\n", __func__); return ret; @@ -258,6 +506,13 @@ efi_status_t EFIAPI get_max_payload(efi_uintn_t *size) goto out; } *size = var_payload->size; + + #if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) + if (*size > FFA_SHARED_MM_BUFFER_SIZE) + *size = FFA_SHARED_MM_BUFFER_SIZE - MM_COMMUNICATE_HEADER_SIZE - + MM_VARIABLE_COMMUNICATE_SIZE; + #endif + /* * There seems to be a bug in EDK2 miscalculating the boundaries and * size checks, so deduct 2 more bytes to fulfill this requirement. Fix @@ -697,7 +952,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);
/*

Hi Abdellatif,
--- a/arch/arm/cpu/armv8/cache.S +++ b/arch/arm/cpu/armv8/cache.S @@ -3,6 +3,9 @@
- (C) Copyright 2013
- David Feng fenghua@phytium.com.cn
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
*/
- This file is based on sample code from ARMv8 ARM.
@@ -21,7 +24,11 @@
- x1: 0 clean & invalidate, 1 invalidate only
- x2~x9: clobbered
*/ +#ifdef CONFIG_EFI_LOADER +.pushsection .text.efi_runtime, "ax"
Maybe we discussed this in the past and I forgot, but why would you need __asm_dcache_level, __asm_dcache_all, __asm_invalidate_dcache_alla etc in the runtime section ?
+#else .pushsection .text.__asm_dcache_level, "ax" +#endif ENTRY(__asm_dcache_level) lsl x12, x0, #1 msr csselr_el1, x12 /* select cache level */ @@ -65,7 +72,11 @@ ENDPROC(__asm_dcache_level)
- flush or invalidate all data cache by SET/WAY.
*/ +#ifdef CONFIG_EFI_LOADER +.pushsection .text.efi_runtime, "ax" +#else .pushsection .text.__asm_dcache_all, "ax" +#endif ENTRY(__asm_dcache_all) mov x1, x0 dsb sy @@ -109,7 +120,11 @@ ENTRY(__asm_flush_dcache_all) ENDPROC(__asm_flush_dcache_all) .popsection
+#ifdef CONFIG_EFI_LOADER +.pushsection .text.efi_runtime, "ax" +#else .pushsection .text.__asm_invalidate_dcache_all, "ax" +#endif ENTRY(__asm_invalidate_dcache_all) mov x0, #0x1 b __asm_dcache_all @@ -182,7 +197,11 @@ ENTRY(__asm_invalidate_icache_all) ENDPROC(__asm_invalidate_icache_all) .popsection
+#ifdef CONFIG_EFI_LOADER +.pushsection .text.efi_runtime, "ax" +#else .pushsection .text.__asm_invalidate_l3_dcache, "ax" +#endif WEAK(__asm_invalidate_l3_dcache) mov x0, #0 /* return status as success */ ret diff --git a/arch/arm/cpu/armv8/cache_v8.c b/arch/arm/cpu/armv8/cache_v8.c index e4736e5643..45f57372c2 100644 --- a/arch/arm/cpu/armv8/cache_v8.c +++ b/arch/arm/cpu/armv8/cache_v8.c @@ -5,10 +5,14 @@
- (C) Copyright 2016
- Alexander Graf agraf@suse.de
- (C) Copyright 2022 ARM Limited
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#include <common.h> #include <cpu_func.h> +#include <efi_loader.h> #include <hang.h> #include <log.h> #include <asm/cache.h> @@ -445,7 +449,7 @@ __weak void mmu_setup(void) /*
- Performs a invalidation of the entire data cache at all levels
*/ -void invalidate_dcache_all(void) +void __efi_runtime invalidate_dcache_all(void) { __asm_invalidate_dcache_all(); __asm_invalidate_l3_dcache(); diff --git a/include/mm_communication.h b/include/mm_communication.h index e65fbde60d..fe9104c56d 100644 --- a/include/mm_communication.h
[...]
- always begin with efi_mm_communicate_header.
*/ -struct __packed efi_mm_communicate_header { +struct efi_mm_communicate_header { efi_guid_t header_guid; size_t message_len; u8 data[]; @@ -145,7 +150,7 @@ struct smm_variable_communicate_header {
- Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE.
*/ -struct smm_variable_access { +struct __packed smm_variable_access {
You are randomly adding and deleting __packed cwin both structs. But you can't do that. Those structs are defined in StandAloneMM. This is the reason each struct description has the corresponding EDK2 definition.
efi_guid_t guid; efi_uintn_t data_size; efi_uintn_t name_size; diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index b8fb2701a7..d292f57244 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -61,13 +61,23 @@ 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"
- depends on OPTEE
- bool "UEFI variables storage service via the trusted world"
- depends on OPTEE || ARM_FFA_TRANSPORT
I think we discussed this in the past. I don't want ifdefs in the code for that. FF-A is a discoverable bus. So I think what we should do here, is fire up the variable access driver -> probe for FF-A -> decide what to use.
So it looks something like ************************** **** Variable Driver ***** ************************** | | | *************************** ****** Discover FF-A ****** *************************** | | | yes | No | |
Use SP Use OP-TEE
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 EFI_VARIABLE_NO_STORE bool "Don't persist non-volatile UEFI variables" help diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index d404343a7d..8a397ea21b 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -2193,6 +2193,13 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, debug("[efi_boottime][INFO]: FF-A RX/TX buffers unmapped\n"); #endif
+#if CONFIG_IS_ENABLED(ARM_FFA_EFI_RUNTIME_MODE) && !CONFIG_IS_ENABLED(SANDBOX_FFA)
if (ffa_copy_runtime_data())
printf("ERROR: EFI: FFA: copying runtime data\n");
else
printf("INFO: EFI: FFA: runtime data copied\n");
+#endif
- /* Patch out unsupported runtime function */ efi_runtime_detach();
diff --git a/lib/efi_loader/efi_variable_tee.c b/lib/efi_loader/efi_variable_tee.c index dfef18435d..d6f24f85bd 100644 --- a/lib/efi_loader/efi_variable_tee.c +++ b/lib/efi_loader/efi_variable_tee.c @@ -4,6 +4,8 @@
- Copyright (C) 2019 Linaro Ltd. sughosh.ganu@linaro.org
- Copyright (C) 2019 Linaro Ltd. ilias.apalodimas@linaro.org
- Copyright (C) 2022 ARM Limited
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#include <common.h> @@ -15,6 +17,36 @@ #include <malloc.h> #include <mm_communication.h>
+#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
+#include <arm_ffa.h> +#include <cpu_func.h> +#include <mapmem.h>
+#ifndef FFA_SHARED_MM_BUFFER_SIZE +#warning "FFA_SHARED_MM_BUFFER_SIZE must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_SIZE 0 +#endif
+#ifndef FFA_SHARED_MM_BUFFER_OFFSET +#warning "FFA_SHARED_MM_BUFFER_OFFSET must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_OFFSET 0 +#endif
+#ifndef FFA_SHARED_MM_BUFFER_ADDR +#warning "FFA_SHARED_MM_BUFFER_ADDR must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_ADDR 0 +#endif
+/* MM return codes */ +#define MM_SUCCESS (0)
+const char *mm_sp_svc_uuid = MM_SP_UUID;
+static __efi_runtime_data 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 */ static efi_uintn_t max_payload_size; /* func + data */ @@ -24,6 +56,7 @@ struct mm_connection { u32 session; };
+#if (IS_ENABLED(CONFIG_OPTEE)) /**
- get_connection() - Retrieve OP-TEE session for a specific UUID.
@@ -143,16 +176,227 @@ static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize)
return ret; } +#endif
+#if (IS_ENABLED(CONFIG_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
- Notifies 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 __efi_runtime ffa_notify_mm_sp(void) +{
- struct ffa_send_direct_data msg = {0};
- int ret;
- int sp_event_ret = -1;
- if (!ffa_bus_ops_get())
return -EINVAL;
- msg.data0 = FFA_SHARED_MM_BUFFER_OFFSET; /* x3 */
- ret = ffa_bus_ops_get()->sync_send_receive(mm_sp_id, &msg);
- if (ret != 0)
return ret;
- sp_event_ret = msg.data0; /* x3 */
- if (sp_event_ret == MM_SUCCESS)
return 0;
- /*
* Failure to notify the MM SP
*/
- return -EACCES;
+}
+/**
- 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, size = 0;
- int ret;
- struct ffa_partition_info *parts_info;
- if (!ffa_bus_ops_get())
return -EINVAL;
- /*
* get from the driver the count of the SPs matching the UUID
*/
- ret = ffa_bus_ops_get()->partition_info_get(mm_sp_svc_uuid, &count, NULL);
- if (ret != 0) {
log_err("EFI: Failure in querying partitions count (error code: %d)\n", ret);
return ret;
- }
- if (!count) {
log_info("EFI: No MM partition found\n");
return ret;
- }
- /*
* pre-allocate a buffer to be filled by the driver
* with ffa_partition_info structs
*/
- log_info("EFI: Pre-allocating %d partition(s) info structures\n", count);
- parts_info = calloc(count, sizeof(struct ffa_partition_info));
- if (!parts_info)
return -EINVAL;
- size = count * sizeof(struct ffa_partition_info);
- /*
* ask the driver to fill the
* buffer with the SPs info
*/
- ret = ffa_bus_ops_get()->partition_info_get(mm_sp_svc_uuid, &size, parts_info);
- if (ret != 0) {
log_err("EFI: Failure in querying partition(s) info (error code: %d)\n", ret);
free(parts_info);
return ret;
- }
- /*
* MM SPs found , use the first one
*/
- mm_sp_id = parts_info[0].id;
- log_info("EFI: MM partition ID 0x%x\n", mm_sp_id);
- free(parts_info);
- 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
- Issues 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 __efi_runtime ffa_mm_communicate(void *comm_buf, ulong comm_buf_size) +{
If you are registering this as a *runtime* service you should also tweak the RT_PROPERTIES TABLE and enable runtime services (e.g SetVariable). Otherwise having as runtime makes little sense. To be honest I'd much prefer this whole patchset without the runtime parts. It's much easier to review and reason about and we can always add runtime later.
- ulong tx_data_size;
- int ffa_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() != 0) {
log_err("EFI: Failure to discover MM partition ID at boot time\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 > FFA_SHARED_MM_BUFFER_SIZE)
return EFI_INVALID_PARAMETER;
- /* Copy the data to the shared buffer */
- virt_shared_buf = (void *)map_sysmem((phys_addr_t)FFA_SHARED_MM_BUFFER_ADDR, 0);
- efi_memcpy_runtime(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();
- if (ffa_ret)
unmap_sysmem(virt_shared_buf);
- 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) {
unmap_sysmem(virt_shared_buf);
return EFI_OUT_OF_RESOURCES;
}
efi_memcpy_runtime(comm_buf, virt_shared_buf, rx_data_size);
unmap_sysmem(virt_shared_buf);
return EFI_SUCCESS;
- }
- case -EINVAL:
return EFI_DEVICE_ERROR;
- case -EPERM:
return EFI_INVALID_PARAMETER;
- case -EACCES:
return EFI_ACCESS_DENIED;
- case -EBUSY:
return EFI_OUT_OF_RESOURCES;
- default:
return EFI_ACCESS_DENIED;
- }
+} +#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 MM SP (also called partition) can be 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, StandAlonneMM and smm-gateway are supported.
*/
- Return: status code
-static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) +static efi_status_t __efi_runtime mm_communicate(u8 *comm_buf, efi_uintn_t dsize)
The optee version of the function can't be on the efi_runtime section. The reason is that almost the entire of the OP-TEE subsystem is not marked as runtime, which will lead to a crash if we ever take that path.
{ efi_status_t ret; struct efi_mm_communicate_header *mm_hdr; @@ -162,7 +406,11 @@ static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) mm_hdr = (struct efi_mm_communicate_header *)comm_buf; var_hdr = (struct smm_variable_communicate_header *)mm_hdr->data;
- #if (IS_ENABLED(CONFIG_OPTEE)) ret = optee_mm_communicate(comm_buf, dsize);
- #elif (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
- ret = ffa_mm_communicate(comm_buf, dsize);
If we enable runtime discovery of optee vs ffa, we can get rid of that and just pass an extra argument to mm_communicate
Thanks /Ilias
- #endif if (ret != EFI_SUCCESS) { log_err("%s failed!\n", __func__); return ret;
@@ -258,6 +506,13 @@ efi_status_t EFIAPI get_max_payload(efi_uintn_t *size) goto out; } *size = var_payload->size;
- #if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
if (*size > FFA_SHARED_MM_BUFFER_SIZE)
*size = FFA_SHARED_MM_BUFFER_SIZE - MM_COMMUNICATE_HEADER_SIZE -
MM_VARIABLE_COMMUNICATE_SIZE;
- #endif
- /*
- There seems to be a bug in EDK2 miscalculating the boundaries and
- size checks, so deduct 2 more bytes to fulfill this requirement. Fix
@@ -697,7 +952,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);
/*
-- 2.17.1

On Thu, Sep 29, 2022 at 12:32:42PM +0300, Ilias Apalodimas wrote:
Hi Abdellatif,
--- a/arch/arm/cpu/armv8/cache.S +++ b/arch/arm/cpu/armv8/cache.S @@ -3,6 +3,9 @@
- (C) Copyright 2013
- David Feng fenghua@phytium.com.cn
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
*/
- This file is based on sample code from ARMv8 ARM.
@@ -21,7 +24,11 @@
- x1: 0 clean & invalidate, 1 invalidate only
- x2~x9: clobbered
*/ +#ifdef CONFIG_EFI_LOADER +.pushsection .text.efi_runtime, "ax"
Maybe we discussed this in the past and I forgot, but why would you need __asm_dcache_level, __asm_dcache_all, __asm_invalidate_dcache_alla etc in the runtime section ?
Because cache invalidation needs to be done at ffa_mm_communicate() level (v4 patchset). That code is runtime compatible so all the called functions must be under .text.efi_runtime
However, since we agreed to drop EFI runtime support from the patchset, v6 patchset takes care of that.
+#else .pushsection .text.__asm_dcache_level, "ax" +#endif ENTRY(__asm_dcache_level) lsl x12, x0, #1 msr csselr_el1, x12 /* select cache level */ @@ -65,7 +72,11 @@ ENDPROC(__asm_dcache_level)
- flush or invalidate all data cache by SET/WAY.
*/ +#ifdef CONFIG_EFI_LOADER +.pushsection .text.efi_runtime, "ax" +#else .pushsection .text.__asm_dcache_all, "ax" +#endif ENTRY(__asm_dcache_all) mov x1, x0 dsb sy @@ -109,7 +120,11 @@ ENTRY(__asm_flush_dcache_all) ENDPROC(__asm_flush_dcache_all) .popsection
+#ifdef CONFIG_EFI_LOADER +.pushsection .text.efi_runtime, "ax" +#else .pushsection .text.__asm_invalidate_dcache_all, "ax" +#endif ENTRY(__asm_invalidate_dcache_all) mov x0, #0x1 b __asm_dcache_all @@ -182,7 +197,11 @@ ENTRY(__asm_invalidate_icache_all) ENDPROC(__asm_invalidate_icache_all) .popsection
+#ifdef CONFIG_EFI_LOADER +.pushsection .text.efi_runtime, "ax" +#else .pushsection .text.__asm_invalidate_l3_dcache, "ax" +#endif WEAK(__asm_invalidate_l3_dcache) mov x0, #0 /* return status as success */ ret diff --git a/arch/arm/cpu/armv8/cache_v8.c b/arch/arm/cpu/armv8/cache_v8.c index e4736e5643..45f57372c2 100644 --- a/arch/arm/cpu/armv8/cache_v8.c +++ b/arch/arm/cpu/armv8/cache_v8.c @@ -5,10 +5,14 @@
- (C) Copyright 2016
- Alexander Graf agraf@suse.de
- (C) Copyright 2022 ARM Limited
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#include <common.h> #include <cpu_func.h> +#include <efi_loader.h> #include <hang.h> #include <log.h> #include <asm/cache.h> @@ -445,7 +449,7 @@ __weak void mmu_setup(void) /*
- Performs a invalidation of the entire data cache at all levels
*/ -void invalidate_dcache_all(void) +void __efi_runtime invalidate_dcache_all(void) { __asm_invalidate_dcache_all(); __asm_invalidate_l3_dcache(); diff --git a/include/mm_communication.h b/include/mm_communication.h index e65fbde60d..fe9104c56d 100644 --- a/include/mm_communication.h
[...]
- always begin with efi_mm_communicate_header.
*/ -struct __packed efi_mm_communicate_header { +struct efi_mm_communicate_header { efi_guid_t header_guid; size_t message_len; u8 data[]; @@ -145,7 +150,7 @@ struct smm_variable_communicate_header {
- Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE.
*/ -struct smm_variable_access { +struct __packed smm_variable_access {
You are randomly adding and deleting __packed cwin both structs. But you can't do that. Those structs are defined in StandAloneMM. This is the reason each struct description has the corresponding EDK2 definition.
Thanks for the comment.
However, we are not setting randomly the __packed keyword. There is a good reason for that. It has been explained before in this reply [1]. Basically it's because of data padding issues breaking the communication between u-boot and secure world (Optee).
When upgrading Optee to v3.18, no issues anymore.
The __packed changes have been dropped in patchset v6.
[1]: https://lore.kernel.org/all/20220926105620.GA22382@e121910.cambridge.arm.com...
efi_guid_t guid; efi_uintn_t data_size; efi_uintn_t name_size; diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index b8fb2701a7..d292f57244 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -61,13 +61,23 @@ 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"
- depends on OPTEE
- bool "UEFI variables storage service via the trusted world"
- depends on OPTEE || ARM_FFA_TRANSPORT
I think we discussed this in the past. I don't want ifdefs in the code for that. FF-A is a discoverable bus. So I think what we should do here, is fire up the variable access driver -> probe for FF-A -> decide what to use.
So it looks something like ************************** **** Variable Driver ***** ************************** | | |
****** Discover FF-A ******
| |
| yes | No | |
Use SP Use OP-TEE
In patchset v6, FF-A discovery is done at runtime and on demand. In our case at the MM Variable Driver as you recommended. Thanks.
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 EFI_VARIABLE_NO_STORE bool "Don't persist non-volatile UEFI variables" help diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index d404343a7d..8a397ea21b 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -2193,6 +2193,13 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, debug("[efi_boottime][INFO]: FF-A RX/TX buffers unmapped\n"); #endif
+#if CONFIG_IS_ENABLED(ARM_FFA_EFI_RUNTIME_MODE) && !CONFIG_IS_ENABLED(SANDBOX_FFA)
if (ffa_copy_runtime_data())
printf("ERROR: EFI: FFA: copying runtime data\n");
else
printf("INFO: EFI: FFA: runtime data copied\n");
+#endif
- /* Patch out unsupported runtime function */ efi_runtime_detach();
diff --git a/lib/efi_loader/efi_variable_tee.c b/lib/efi_loader/efi_variable_tee.c index dfef18435d..d6f24f85bd 100644 --- a/lib/efi_loader/efi_variable_tee.c +++ b/lib/efi_loader/efi_variable_tee.c @@ -4,6 +4,8 @@
- Copyright (C) 2019 Linaro Ltd. sughosh.ganu@linaro.org
- Copyright (C) 2019 Linaro Ltd. ilias.apalodimas@linaro.org
- Copyright (C) 2022 ARM Limited
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#include <common.h> @@ -15,6 +17,36 @@ #include <malloc.h> #include <mm_communication.h>
+#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
+#include <arm_ffa.h> +#include <cpu_func.h> +#include <mapmem.h>
+#ifndef FFA_SHARED_MM_BUFFER_SIZE +#warning "FFA_SHARED_MM_BUFFER_SIZE must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_SIZE 0 +#endif
+#ifndef FFA_SHARED_MM_BUFFER_OFFSET +#warning "FFA_SHARED_MM_BUFFER_OFFSET must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_OFFSET 0 +#endif
+#ifndef FFA_SHARED_MM_BUFFER_ADDR +#warning "FFA_SHARED_MM_BUFFER_ADDR must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_ADDR 0 +#endif
+/* MM return codes */ +#define MM_SUCCESS (0)
+const char *mm_sp_svc_uuid = MM_SP_UUID;
+static __efi_runtime_data 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 */ static efi_uintn_t max_payload_size; /* func + data */ @@ -24,6 +56,7 @@ struct mm_connection { u32 session; };
+#if (IS_ENABLED(CONFIG_OPTEE)) /**
- get_connection() - Retrieve OP-TEE session for a specific UUID.
@@ -143,16 +176,227 @@ static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize)
return ret; } +#endif
+#if (IS_ENABLED(CONFIG_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
- Notifies 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 __efi_runtime ffa_notify_mm_sp(void) +{
- struct ffa_send_direct_data msg = {0};
- int ret;
- int sp_event_ret = -1;
- if (!ffa_bus_ops_get())
return -EINVAL;
- msg.data0 = FFA_SHARED_MM_BUFFER_OFFSET; /* x3 */
- ret = ffa_bus_ops_get()->sync_send_receive(mm_sp_id, &msg);
- if (ret != 0)
return ret;
- sp_event_ret = msg.data0; /* x3 */
- if (sp_event_ret == MM_SUCCESS)
return 0;
- /*
* Failure to notify the MM SP
*/
- return -EACCES;
+}
+/**
- 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, size = 0;
- int ret;
- struct ffa_partition_info *parts_info;
- if (!ffa_bus_ops_get())
return -EINVAL;
- /*
* get from the driver the count of the SPs matching the UUID
*/
- ret = ffa_bus_ops_get()->partition_info_get(mm_sp_svc_uuid, &count, NULL);
- if (ret != 0) {
log_err("EFI: Failure in querying partitions count (error code: %d)\n", ret);
return ret;
- }
- if (!count) {
log_info("EFI: No MM partition found\n");
return ret;
- }
- /*
* pre-allocate a buffer to be filled by the driver
* with ffa_partition_info structs
*/
- log_info("EFI: Pre-allocating %d partition(s) info structures\n", count);
- parts_info = calloc(count, sizeof(struct ffa_partition_info));
- if (!parts_info)
return -EINVAL;
- size = count * sizeof(struct ffa_partition_info);
- /*
* ask the driver to fill the
* buffer with the SPs info
*/
- ret = ffa_bus_ops_get()->partition_info_get(mm_sp_svc_uuid, &size, parts_info);
- if (ret != 0) {
log_err("EFI: Failure in querying partition(s) info (error code: %d)\n", ret);
free(parts_info);
return ret;
- }
- /*
* MM SPs found , use the first one
*/
- mm_sp_id = parts_info[0].id;
- log_info("EFI: MM partition ID 0x%x\n", mm_sp_id);
- free(parts_info);
- 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
- Issues 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 __efi_runtime ffa_mm_communicate(void *comm_buf, ulong comm_buf_size) +{
If you are registering this as a *runtime* service you should also tweak the RT_PROPERTIES TABLE and enable runtime services (e.g SetVariable). Otherwise having as runtime makes little sense. To be honest I'd much prefer this whole patchset without the runtime parts. It's much easier to review and reason about and we can always add runtime later.
EFI runtime support removed from the FF-A work in patchset v6.
- ulong tx_data_size;
- int ffa_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() != 0) {
log_err("EFI: Failure to discover MM partition ID at boot time\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 > FFA_SHARED_MM_BUFFER_SIZE)
return EFI_INVALID_PARAMETER;
- /* Copy the data to the shared buffer */
- virt_shared_buf = (void *)map_sysmem((phys_addr_t)FFA_SHARED_MM_BUFFER_ADDR, 0);
- efi_memcpy_runtime(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();
- if (ffa_ret)
unmap_sysmem(virt_shared_buf);
- 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) {
unmap_sysmem(virt_shared_buf);
return EFI_OUT_OF_RESOURCES;
}
efi_memcpy_runtime(comm_buf, virt_shared_buf, rx_data_size);
unmap_sysmem(virt_shared_buf);
return EFI_SUCCESS;
- }
- case -EINVAL:
return EFI_DEVICE_ERROR;
- case -EPERM:
return EFI_INVALID_PARAMETER;
- case -EACCES:
return EFI_ACCESS_DENIED;
- case -EBUSY:
return EFI_OUT_OF_RESOURCES;
- default:
return EFI_ACCESS_DENIED;
- }
+} +#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 MM SP (also called partition) can be 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, StandAlonneMM and smm-gateway are supported.
*/
- Return: status code
-static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) +static efi_status_t __efi_runtime mm_communicate(u8 *comm_buf, efi_uintn_t dsize)
The optee version of the function can't be on the efi_runtime section. The reason is that almost the entire of the OP-TEE subsystem is not marked as runtime, which will lead to a crash if we ever take that path.
{ efi_status_t ret; struct efi_mm_communicate_header *mm_hdr; @@ -162,7 +406,11 @@ static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) mm_hdr = (struct efi_mm_communicate_header *)comm_buf; var_hdr = (struct smm_variable_communicate_header *)mm_hdr->data;
- #if (IS_ENABLED(CONFIG_OPTEE)) ret = optee_mm_communicate(comm_buf, dsize);
- #elif (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
- ret = ffa_mm_communicate(comm_buf, dsize);
If we enable runtime discovery of optee vs ffa, we can get rid of that and just pass an extra argument to mm_communicate
Thanks /Ilias
- #endif if (ret != EFI_SUCCESS) { log_err("%s failed!\n", __func__); return ret;
@@ -258,6 +506,13 @@ efi_status_t EFIAPI get_max_payload(efi_uintn_t *size) goto out; } *size = var_payload->size;
- #if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
if (*size > FFA_SHARED_MM_BUFFER_SIZE)
*size = FFA_SHARED_MM_BUFFER_SIZE - MM_COMMUNICATE_HEADER_SIZE -
MM_VARIABLE_COMMUNICATE_SIZE;
- #endif
- /*
- There seems to be a bug in EDK2 miscalculating the boundaries and
- size checks, so deduct 2 more bytes to fulfill this requirement. Fix
@@ -697,7 +952,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);
/*
-- 2.17.1

Hi,
On Fri, 14 Oct 2022 at 04:38, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
On Thu, Sep 29, 2022 at 12:32:42PM +0300, Ilias Apalodimas wrote:
Hi Abdellatif,
--- a/arch/arm/cpu/armv8/cache.S +++ b/arch/arm/cpu/armv8/cache.S @@ -3,6 +3,9 @@
- (C) Copyright 2013
- David Feng fenghua@phytium.com.cn
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
*/
- This file is based on sample code from ARMv8 ARM.
@@ -21,7 +24,11 @@
- x1: 0 clean & invalidate, 1 invalidate only
- x2~x9: clobbered
*/ +#ifdef CONFIG_EFI_LOADER +.pushsection .text.efi_runtime, "ax"
Maybe we discussed this in the past and I forgot, but why would you need __asm_dcache_level, __asm_dcache_all, __asm_invalidate_dcache_alla etc in the runtime section ?
Because cache invalidation needs to be done at ffa_mm_communicate() level (v4 patchset). That code is runtime compatible so all the called functions must be under .text.efi_runtime
However, since we agreed to drop EFI runtime support from the patchset, v6 patchset takes care of that.
+#else .pushsection .text.__asm_dcache_level, "ax" +#endif ENTRY(__asm_dcache_level) lsl x12, x0, #1 msr csselr_el1, x12 /* select cache level */ @@ -65,7 +72,11 @@ ENDPROC(__asm_dcache_level)
- flush or invalidate all data cache by SET/WAY.
*/ +#ifdef CONFIG_EFI_LOADER +.pushsection .text.efi_runtime, "ax" +#else .pushsection .text.__asm_dcache_all, "ax" +#endif ENTRY(__asm_dcache_all) mov x1, x0 dsb sy @@ -109,7 +120,11 @@ ENTRY(__asm_flush_dcache_all) ENDPROC(__asm_flush_dcache_all) .popsection
+#ifdef CONFIG_EFI_LOADER +.pushsection .text.efi_runtime, "ax" +#else .pushsection .text.__asm_invalidate_dcache_all, "ax" +#endif ENTRY(__asm_invalidate_dcache_all) mov x0, #0x1 b __asm_dcache_all @@ -182,7 +197,11 @@ ENTRY(__asm_invalidate_icache_all) ENDPROC(__asm_invalidate_icache_all) .popsection
+#ifdef CONFIG_EFI_LOADER +.pushsection .text.efi_runtime, "ax" +#else .pushsection .text.__asm_invalidate_l3_dcache, "ax" +#endif WEAK(__asm_invalidate_l3_dcache) mov x0, #0 /* return status as success */ ret diff --git a/arch/arm/cpu/armv8/cache_v8.c b/arch/arm/cpu/armv8/cache_v8.c index e4736e5643..45f57372c2 100644 --- a/arch/arm/cpu/armv8/cache_v8.c +++ b/arch/arm/cpu/armv8/cache_v8.c @@ -5,10 +5,14 @@
- (C) Copyright 2016
- Alexander Graf agraf@suse.de
- (C) Copyright 2022 ARM Limited
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#include <common.h> #include <cpu_func.h> +#include <efi_loader.h> #include <hang.h> #include <log.h> #include <asm/cache.h> @@ -445,7 +449,7 @@ __weak void mmu_setup(void) /*
- Performs a invalidation of the entire data cache at all levels
*/ -void invalidate_dcache_all(void) +void __efi_runtime invalidate_dcache_all(void) { __asm_invalidate_dcache_all(); __asm_invalidate_l3_dcache(); diff --git a/include/mm_communication.h b/include/mm_communication.h index e65fbde60d..fe9104c56d 100644 --- a/include/mm_communication.h
[...]
- always begin with efi_mm_communicate_header.
*/ -struct __packed efi_mm_communicate_header { +struct efi_mm_communicate_header { efi_guid_t header_guid; size_t message_len; u8 data[]; @@ -145,7 +150,7 @@ struct smm_variable_communicate_header {
- Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE.
*/ -struct smm_variable_access { +struct __packed smm_variable_access {
You are randomly adding and deleting __packed cwin both structs. But you can't do that. Those structs are defined in StandAloneMM. This is the reason each struct description has the corresponding EDK2 definition.
Thanks for the comment.
However, we are not setting randomly the __packed keyword. There is a good reason for that. It has been explained before in this reply [1]. Basically it's because of data padding issues breaking the communication between u-boot and secure world (Optee).
When upgrading Optee to v3.18, no issues anymore.
The __packed changes have been dropped in patchset v6.
That is the Linux mailing list. I cannot see any reason to add __packed to this struct as it is already set up that way.
Also efi_mm_communicate_header is really long. I suggest efi_mm_hdr or efi_mm_comms_hdr
Why are you using SMM on ARM? Isn't that an Intel thing?
[..]
Regards, SImon

Hi Simon,
[...]
- always begin with efi_mm_communicate_header.
*/ -struct __packed efi_mm_communicate_header { +struct efi_mm_communicate_header { efi_guid_t header_guid; size_t message_len; u8 data[]; @@ -145,7 +150,7 @@ struct smm_variable_communicate_header {
- Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE.
*/ -struct smm_variable_access { +struct __packed smm_variable_access {
You are randomly adding and deleting __packed cwin both structs. But you can't do that. Those structs are defined in StandAloneMM. This is the reason each struct description has the corresponding EDK2 definition.
Thanks for the comment.
However, we are not setting randomly the __packed keyword. There is a good reason for that. It has been explained before in this reply [1]. Basically it's because of data padding issues breaking the communication between u-boot and secure world (Optee).
When upgrading Optee to v3.18, no issues anymore.
The __packed changes have been dropped in patchset v6.
That is the Linux mailing list. I cannot see any reason to add __packed to this struct as it is already set up that way.
Also efi_mm_communicate_header is really long. I suggest efi_mm_hdr or efi_mm_comms_hdr
Why are you using SMM on ARM? Isn't that an Intel thing?
This is due to the fact that we run StandAloneMM, which is a specific EDK2 'package' to handle the variables in Arm secure world. There's a really long explanation on how that works here [0] in case you are interested. The naming is a bit unfortunate, but since we have to read it and reason about the EDK2 API in future versions, I suggest keeping it as close to the original as possible. Alternatively we could just add a comment with the EDK2 equivalent, but in any case, the change you are request is out of scope for this patchset.
[0] https://www.linaro.org/blog/protected-uefi-variables-with-u-boot/
Regards /Ilias
[..]
Regards, SImon

On Fri, Oct 14, 2022 at 09:55:59AM -0600, Simon Glass wrote:
Hi,
On Fri, 14 Oct 2022 at 04:38, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
On Thu, Sep 29, 2022 at 12:32:42PM +0300, Ilias Apalodimas wrote:
Hi Abdellatif,
--- a/arch/arm/cpu/armv8/cache.S +++ b/arch/arm/cpu/armv8/cache.S @@ -3,6 +3,9 @@
- (C) Copyright 2013
- David Feng fenghua@phytium.com.cn
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
*/
- This file is based on sample code from ARMv8 ARM.
@@ -21,7 +24,11 @@
- x1: 0 clean & invalidate, 1 invalidate only
- x2~x9: clobbered
*/ +#ifdef CONFIG_EFI_LOADER +.pushsection .text.efi_runtime, "ax"
Maybe we discussed this in the past and I forgot, but why would you need __asm_dcache_level, __asm_dcache_all, __asm_invalidate_dcache_alla etc in the runtime section ?
Because cache invalidation needs to be done at ffa_mm_communicate() level (v4 patchset). That code is runtime compatible so all the called functions must be under .text.efi_runtime
However, since we agreed to drop EFI runtime support from the patchset, v6 patchset takes care of that.
+#else .pushsection .text.__asm_dcache_level, "ax" +#endif ENTRY(__asm_dcache_level) lsl x12, x0, #1 msr csselr_el1, x12 /* select cache level */ @@ -65,7 +72,11 @@ ENDPROC(__asm_dcache_level)
- flush or invalidate all data cache by SET/WAY.
*/ +#ifdef CONFIG_EFI_LOADER +.pushsection .text.efi_runtime, "ax" +#else .pushsection .text.__asm_dcache_all, "ax" +#endif ENTRY(__asm_dcache_all) mov x1, x0 dsb sy @@ -109,7 +120,11 @@ ENTRY(__asm_flush_dcache_all) ENDPROC(__asm_flush_dcache_all) .popsection
+#ifdef CONFIG_EFI_LOADER +.pushsection .text.efi_runtime, "ax" +#else .pushsection .text.__asm_invalidate_dcache_all, "ax" +#endif ENTRY(__asm_invalidate_dcache_all) mov x0, #0x1 b __asm_dcache_all @@ -182,7 +197,11 @@ ENTRY(__asm_invalidate_icache_all) ENDPROC(__asm_invalidate_icache_all) .popsection
+#ifdef CONFIG_EFI_LOADER +.pushsection .text.efi_runtime, "ax" +#else .pushsection .text.__asm_invalidate_l3_dcache, "ax" +#endif WEAK(__asm_invalidate_l3_dcache) mov x0, #0 /* return status as success */ ret diff --git a/arch/arm/cpu/armv8/cache_v8.c b/arch/arm/cpu/armv8/cache_v8.c index e4736e5643..45f57372c2 100644 --- a/arch/arm/cpu/armv8/cache_v8.c +++ b/arch/arm/cpu/armv8/cache_v8.c @@ -5,10 +5,14 @@
- (C) Copyright 2016
- Alexander Graf agraf@suse.de
- (C) Copyright 2022 ARM Limited
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#include <common.h> #include <cpu_func.h> +#include <efi_loader.h> #include <hang.h> #include <log.h> #include <asm/cache.h> @@ -445,7 +449,7 @@ __weak void mmu_setup(void) /*
- Performs a invalidation of the entire data cache at all levels
*/ -void invalidate_dcache_all(void) +void __efi_runtime invalidate_dcache_all(void) { __asm_invalidate_dcache_all(); __asm_invalidate_l3_dcache(); diff --git a/include/mm_communication.h b/include/mm_communication.h index e65fbde60d..fe9104c56d 100644 --- a/include/mm_communication.h
[...]
- always begin with efi_mm_communicate_header.
*/ -struct __packed efi_mm_communicate_header { +struct efi_mm_communicate_header { efi_guid_t header_guid; size_t message_len; u8 data[]; @@ -145,7 +150,7 @@ struct smm_variable_communicate_header {
- Defined in EDK2 as SMM_VARIABLE_COMMUNICATE_ACCESS_VARIABLE.
*/ -struct smm_variable_access { +struct __packed smm_variable_access {
You are randomly adding and deleting __packed cwin both structs. But you can't do that. Those structs are defined in StandAloneMM. This is the reason each struct description has the corresponding EDK2 definition.
Thanks for the comment.
However, we are not setting randomly the __packed keyword. There is a good reason for that. It has been explained before in this reply [1]. Basically it's because of data padding issues breaking the communication between u-boot and secure world (Optee).
When upgrading Optee to v3.18, no issues anymore.
The __packed changes have been dropped in patchset v6.
That is the Linux mailing list. I cannot see any reason to add
Thanks Simon. The link above is not part of the linux mailing list. It points to the mirror of the u-boot mailing list under lore.kernel.org
__packed to this struct as it is already set up that way.
Also efi_mm_communicate_header is really long. I suggest efi_mm_hdr or efi_mm_comms_hdr
Why are you using SMM on ARM? Isn't that an Intel thing?
[..]
Regards, SImon

turn on EFI MM communication
On corstone1000 platform MM communication between u-boot and the secure world (Optee) is done using the FF-A bus.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org --- configs/corstone1000_defconfig | 4 ++++ include/configs/corstone1000.h | 9 +++++++++ 2 files changed, 13 insertions(+)
diff --git a/configs/corstone1000_defconfig b/configs/corstone1000_defconfig index ed2e0fe70a..5028c746c5 100644 --- a/configs/corstone1000_defconfig +++ b/configs/corstone1000_defconfig @@ -52,3 +52,7 @@ CONFIG_DM_SERIAL=y CONFIG_USB=y CONFIG_USB_ISP1760=y CONFIG_ERRNO_STR=y +CONFIG_EFI_MM_COMM_TEE=y +# CONFIG_OPTEE is not set +CONFIG_ARM_FFA_TRANSPORT=y +CONFIG_ARM_FFA_EFI_RUNTIME_MODE=y diff --git a/include/configs/corstone1000.h b/include/configs/corstone1000.h index 8e0230c135..997d0bebaf 100644 --- a/include/configs/corstone1000.h +++ b/include/configs/corstone1000.h @@ -14,6 +14,15 @@
#include <linux/sizes.h>
+#define FFA_SHARED_MM_BUFFER_SIZE SZ_4K /* 4 KB */ + +/* + * shared buffer physical address used for communication between + * u-boot and the MM SP + */ +#define FFA_SHARED_MM_BUFFER_ADDR (0x023F8000) +#define FFA_SHARED_MM_BUFFER_OFFSET (0) + #define V2M_BASE 0x80000000
#define CONFIG_PL011_CLOCK 50000000

Adding support for Arm FF-A v1.0 (Arm Firmware Framework for Armv8-A) [6].
FF-A describes interfaces (ABIs) that standardize communication between the Secure World and Normal World. These interfaces enable a pair of software sandboxes to communicate with each other. A sandbox aka partition could be a VM in the Normal or Secure world, an application in S-EL0, or a Trusted OS in S-EL1.
The FF-A transport is implemented as a data bus and a core driver is provided.
The FF-A driver uses the SMC ABIs defined by the FF-A specification to:
- Discover the presence of secure partitions (SPs) of interest - Access an SP's service through communication protocols e.g. EFI MM communication protocol
The FF-A support provides the following features:
- Being generic by design and can be used by any Arm 64-bit platform - The FF-A core driver - The FF-A bus is discoverable on demand at runtime - The driver provides callbacks to be used by clients to access the FF-A bus - FF-A driver can be compiled and used without EFI - Support for SMCCCv1.2 x0-x17 registers - A new command called armffa is provided as an example of how to access the FF-A bus - An FF-A Sandbox driver is provided with test cases - Support for FF-A MM communication - Enabling FF-A and MM communication in Corstone1000 platform
For more details about the FF-A core driver please refer to [7].
Please find at [8] an example of the expected boot logs when enabling FF-A support for a platform. In this example the platform is Corstone1000. But it can be any Arm 64-bit platform.
Changelog of the major changes: ===========================
v6:
* remove clearing x0-x17 registers after SMC calls * drop use of EFI runtime support for FF-A (We decided with Linaro to add this later) * drop discovery from initcalls (discovery will be on demand by FF-A users) * add FF-A runtime discovery at MM communication level * update the documentation and move it to doc/arch/arm64.ffa.rst
v5: [5]
* move changelogs in each commit to the changes section
v4: [4]
* add FF-A support README (doc/README.ffa.drv) * improving error handling by mapping the FF-A errors to standard errors and logs * replacing panics with an error log * align sandbox driver and tests with the new FF-A driver interfaces and new way of error handling * use the new FF-A driver interfaces for MM communication * discover MM partitions at runtime * copy FF-A driver private data to EFI runtime section at ExitBootServices() * moving the FF-A driver work to drivers/firmware/arm-ffa * improving features discovery in FFA_FEATURES * add remove/unbind functions to the FF-A core device * improve how the driver behaves when bus discovery is done more than once * move clearing x0-x17 registers code into a new macro like done in the linux kernel * enable EFI MM communication for the Corstone1000 platform
v3: [3]
* port x0-x17 registers support from linux kernel as defined by SMCCCv1.2 * align the interfaces of the u-boot FF-A driver with those in the linux FF-A driver * remove the FF-A helper layer * make the u-boot FF-A driver independent from EFI * provide an optional config that enables copying the driver data to EFI runtime section at ExitBootServices service * use 64-bit version of FFA_RXTX_MAP, FFA_MSG_SEND_DIRECT_{REQ, RESP} * update armffa command with the new driver interfaces
v2 [2]:
* make FF-A bus discoverable using device_{bind, probe} APIs * remove device tree support
v1 [1]:
* introduce FF-A bus driver with device tree support * introduce armffa command * introduce FF-A Sandbox driver * add FF-A Sandbox test cases * introduce FF-A MM communication
Cheers, Abdellatif
[1]: https://lore.kernel.org/all/20220329151659.16894-1-abdellatif.elkhlifi@arm.c... [2]: https://lore.kernel.org/all/20220415122803.16666-1-abdellatif.elkhlifi@arm.c... [3]: https://lore.kernel.org/all/20220801172053.20163-1-abdellatif.elkhlifi@arm.c... [4]: https://lore.kernel.org/all/20220926101723.9965-1-abdellatif.elkhlifi@arm.co... [5]: https://lore.kernel.org/all/20220926140827.15125-1-abdellatif.elkhlifi@arm.c... [6]: https://developer.arm.com/documentation/den0077/latest/ [7]: doc/arch/arm64.ffa.rst [8]: example of boot logs when enabling FF-A
``` U-Boot 2022.07 (Jul 11 2022 - 13:42:58 +0000) corstone1000 aarch64 ... Hit any key to stop autoboot: 0 ... [FFA] Conduit is SMC [FFA] FF-A driver 1.0 FF-A framework 1.0 [FFA] Versions are compatible [FFA] endpoint ID is 0 [FFA] Using 1 4KB page(s) for RX/TX buffers size [FFA] RX buffer at virtual address 0xfdf1b000 [FFA] TX buffer at virtual address 0xfdf1d000 [FFA] RX/TX buffers mapped [FFA] Reading partitions data from the RX buffer [FFA] Partition ID 8001 : info cached [FFA] Partition ID 8002 : info cached [FFA] Partition ID 8003 : info cached [FFA] 3 partition(s) found and cached [FFA] Preparing for checking partitions count [FFA] Searching partitions using the provided UUID [FFA] No partition found. Querying framework ... [FFA] Reading partitions data from the RX buffer [FFA] Number of partition(s) found matching the UUID: 1 EFI: Pre-allocating 1 partition(s) info structures [FFA] Preparing for filling partitions info [FFA] Searching partitions using the provided UUID [FFA] Partition ID 8003 matches the provided UUID EFI: MM partition ID 0x8003 ... EFI stub: Booting Linux Kernel... ... EFI stub: Exiting boot services... [FFA] Freeing RX/TX buffers Booting Linux on physical CPU 0x0000000000 [0x411fd040] ```
Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Achin Gupta achin.gupta@arm.com Cc: Drew Reed Drew.Reed@arm.com Cc: Vishnu Banavath vishnu.banavath@arm.com Cc: Xueliang Zhong Xueliang.Zhong@arm.com
Abdellatif El Khlifi (10): arm64: smccc: add support for SMCCCv1.2 x0-x17 registers lib: uuid: introduce be_uuid_str_to_le_bin function arm_ffa: introduce Arm FF-A low-level driver arm_ffa: efi: unmap RX/TX buffers arm_ffa: introduce armffa command arm_ffa: introduce the FF-A Sandbox driver arm_ffa: introduce Sandbox test cases for UCLASS_FFA arm_ffa: introduce armffa command Sandbox test arm_ffa: efi: introduce FF-A MM communication arm_ffa: efi: corstone1000: enable MM communication
MAINTAINERS | 11 + arch/arm/cpu/armv8/smccc-call.S | 53 + arch/arm/lib/asm-offsets.c | 14 + cmd/Kconfig | 10 + cmd/Makefile | 2 + cmd/armffa.c | 242 +++ configs/corstone1000_defconfig | 4 + configs/sandbox64_defconfig | 2 + configs/sandbox_defconfig | 2 + doc/arch/arm64.ffa.rst | 207 +++ doc/arch/index.rst | 1 + doc/arch/sandbox.rst | 1 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/firmware/arm-ffa/Kconfig | 36 + drivers/firmware/arm-ffa/Makefile | 7 + drivers/firmware/arm-ffa/arm-ffa-uclass.c | 16 + drivers/firmware/arm-ffa/arm_ffa_prv.h | 201 +++ drivers/firmware/arm-ffa/core.c | 1351 +++++++++++++++++ drivers/firmware/arm-ffa/sandbox.c | 659 ++++++++ .../firmware/arm-ffa/sandbox_arm_ffa_prv.h | 144 ++ include/arm_ffa.h | 93 ++ include/configs/corstone1000.h | 9 + include/dm/uclass-id.h | 4 + include/linux/arm-smccc.h | 43 + include/mm_communication.h | 5 + include/sandbox_arm_ffa.h | 91 ++ include/uuid.h | 8 + lib/efi_loader/Kconfig | 14 +- lib/efi_loader/efi_boottime.c | 15 + lib/efi_loader/efi_variable_tee.c | 264 +++- lib/uuid.c | 64 + test/cmd/Makefile | 2 + test/cmd/armffa.c | 40 + test/dm/Makefile | 2 + test/dm/ffa.c | 394 +++++ 36 files changed, 4008 insertions(+), 6 deletions(-) create mode 100644 cmd/armffa.c 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_prv.h create mode 100644 drivers/firmware/arm-ffa/core.c create mode 100644 drivers/firmware/arm-ffa/sandbox.c create mode 100644 drivers/firmware/arm-ffa/sandbox_arm_ffa_prv.h create mode 100644 include/arm_ffa.h create mode 100644 include/sandbox_arm_ffa.h create mode 100644 test/cmd/armffa.c create mode 100644 test/dm/ffa.c

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 abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v4:
* rename the commit title and improve description new commit title: the current
v3:
* port x0-x17 registers support from linux kernel as defined by SMCCCv1.2 commit title: arm64: smccc: add Xn registers support used by SMC calls
arch/arm/cpu/armv8/smccc-call.S | 53 +++++++++++++++++++++++++++++++++ arch/arm/lib/asm-offsets.c | 14 +++++++++ include/linux/arm-smccc.h | 43 ++++++++++++++++++++++++++ 3 files changed, 110 insertions(+)
diff --git a/arch/arm/cpu/armv8/smccc-call.S b/arch/arm/cpu/armv8/smccc-call.S index dc92b28777..ec6f299bc9 100644 --- a/arch/arm/cpu/armv8/smccc-call.S +++ b/arch/arm/cpu/armv8/smccc-call.S @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2015, Linaro Limited + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */ #include <linux/linkage.h> #include <linux/arm-smccc.h> @@ -45,3 +47,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 22fd541f9a..1bc2d90faa 100644 --- a/arch/arm/lib/asm-offsets.c +++ b/arch/arm/lib/asm-offsets.c @@ -9,6 +9,9 @@ * generate asm statements containing #defines, * compile this file to assembler, and then extract the * #defines from the assembly-language output. + * + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> @@ -117,6 +120,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 e1d09884a1..9105031d55 100644 --- a/include/linux/arm-smccc.h +++ b/include/linux/arm-smccc.h @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2015, Linaro Limited + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */ #ifndef __LINUX_ARM_SMCCC_H #define __LINUX_ARM_SMCCC_H @@ -70,6 +72,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

On Thu, Oct 13, 2022 at 11:38:48AM +0100, Abdellatif El Khlifi wrote:
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
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v4:
- rename the commit title and improve description new commit title: the current
v3:
- port x0-x17 registers support from linux kernel as defined by SMCCCv1.2 commit title: arm64: smccc: add Xn registers support used by SMC calls
arch/arm/cpu/armv8/smccc-call.S | 53 +++++++++++++++++++++++++++++++++ arch/arm/lib/asm-offsets.c | 14 +++++++++ include/linux/arm-smccc.h | 43 ++++++++++++++++++++++++++ 3 files changed, 110 insertions(+)
diff --git a/arch/arm/cpu/armv8/smccc-call.S b/arch/arm/cpu/armv8/smccc-call.S index dc92b28777..ec6f299bc9 100644 --- a/arch/arm/cpu/armv8/smccc-call.S +++ b/arch/arm/cpu/armv8/smccc-call.S @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0 */ /*
- Copyright (c) 2015, Linaro Limited
- (C) Copyright 2022 ARM Limited
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#include <linux/linkage.h> #include <linux/arm-smccc.h> @@ -45,3 +47,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 22fd541f9a..1bc2d90faa 100644 --- a/arch/arm/lib/asm-offsets.c +++ b/arch/arm/lib/asm-offsets.c @@ -9,6 +9,9 @@
- generate asm statements containing #defines,
- compile this file to assembler, and then extract the
- #defines from the assembly-language output.
- (C) Copyright 2022 ARM Limited
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#include <common.h> @@ -117,6 +120,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
We don't need another level of indentation here, please follow the established style in this file.
With that fixed please add Reviewed-by: Jens Wiklander jens.wiklander@linaro.org
Cheers, Jens
#endif
return 0; diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h index e1d09884a1..9105031d55 100644 --- a/include/linux/arm-smccc.h +++ b/include/linux/arm-smccc.h @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0 */ /*
- Copyright (c) 2015, Linaro Limited
- (C) Copyright 2022 ARM Limited
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#ifndef __LINUX_ARM_SMCCC_H #define __LINUX_ARM_SMCCC_H @@ -70,6 +72,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
-- 2.17.1

On Mon, Oct 24, 2022 at 04:19:18PM +0200, Jens Wiklander wrote:
On Thu, Oct 13, 2022 at 11:38:48AM +0100, Abdellatif El Khlifi wrote:
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
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v4:
- rename the commit title and improve description new commit title: the current
v3:
- port x0-x17 registers support from linux kernel as defined by SMCCCv1.2 commit title: arm64: smccc: add Xn registers support used by SMC calls
arch/arm/cpu/armv8/smccc-call.S | 53 +++++++++++++++++++++++++++++++++ arch/arm/lib/asm-offsets.c | 14 +++++++++ include/linux/arm-smccc.h | 43 ++++++++++++++++++++++++++ 3 files changed, 110 insertions(+)
diff --git a/arch/arm/cpu/armv8/smccc-call.S b/arch/arm/cpu/armv8/smccc-call.S index dc92b28777..ec6f299bc9 100644 --- a/arch/arm/cpu/armv8/smccc-call.S +++ b/arch/arm/cpu/armv8/smccc-call.S @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0 */ /*
- Copyright (c) 2015, Linaro Limited
- (C) Copyright 2022 ARM Limited
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#include <linux/linkage.h> #include <linux/arm-smccc.h> @@ -45,3 +47,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 22fd541f9a..1bc2d90faa 100644 --- a/arch/arm/lib/asm-offsets.c +++ b/arch/arm/lib/asm-offsets.c @@ -9,6 +9,9 @@
- generate asm statements containing #defines,
- compile this file to assembler, and then extract the
- #defines from the assembly-language output.
- (C) Copyright 2022 ARM Limited
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#include <common.h> @@ -117,6 +120,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
We don't need another level of indentation here, please follow the established style in this file.
With that fixed please add Reviewed-by: Jens Wiklander jens.wiklander@linaro.org
Thanks, done in v7.
Cheers, Jens
#endif
return 0; diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h index e1d09884a1..9105031d55 100644 --- a/include/linux/arm-smccc.h +++ b/include/linux/arm-smccc.h @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0 */ /*
- Copyright (c) 2015, Linaro Limited
- (C) Copyright 2022 ARM Limited
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#ifndef __LINUX_ARM_SMCCC_H #define __LINUX_ARM_SMCCC_H @@ -70,6 +72,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
-- 2.17.1

convert big endian UUID string to little endian buffer
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v4:
* rename ffa_uuid_str_to_bin to be_uuid_str_to_le_bin and put in a standalone commit (the current)
v3:
* introduce ffa_uuid_str_to_bin (provided by arm_ffa: introduce Arm FF-A low-level driver)
include/uuid.h | 8 +++++++ lib/uuid.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+)
diff --git a/include/uuid.h b/include/uuid.h index 4a4883d3b5..ad3af350f9 100644 --- a/include/uuid.h +++ b/include/uuid.h @@ -2,6 +2,8 @@ /* * Copyright (C) 2014 Samsung Electronics * Przemyslaw Marczak p.marczak@samsung.com + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */ #ifndef __UUID_H__ #define __UUID_H__ @@ -44,4 +46,10 @@ 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); + +/** + * be_uuid_str_to_le_bin - Converts a big endian UUID string to a little endian buffer + */ +int be_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 465e1ac38f..15a9ab49d5 100644 --- a/lib/uuid.c +++ b/lib/uuid.c @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-2.0+ /* * Copyright 2011 Calxeda, Inc. + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> @@ -346,6 +348,68 @@ int uuid_str_to_bin(const char *uuid_str, unsigned char *uuid_bin, return 0; }
+/** + * be_uuid_str_to_le_bin - Converts a big endian UUID string to a little endian buffer + * @uuid_str: UUID string in big endian format (36 bytes wide + '/0') + * @uuid_bin: preallocated 16 bytes UUID buffer in little endian format + * + * UUID string is 36 characters (36 bytes): + * + * xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + * be be be be be + * + * where x is a hexadecimal character. Fields are separated by '-'s. + * When converting to a binary UUID, these endianness rules apply: + * be: means the field in the string is considered a big endian hex number + * and should be converted to little endian binary format + * + * Return: + * + * uuid_bin filled with little endian UUID data + * On success 0 is returned. Otherwise, failure code. + */ +int be_uuid_str_to_le_bin(const char *uuid_str, unsigned char *uuid_bin) +{ + u16 tmp16 = 0; + u32 tmp32 = 0; + u64 tmp64 = 0; + + if (!uuid_str_valid(uuid_str) || !uuid_bin) + return -EINVAL; + + /* + * reverse bytes from big to little endian + */ + tmp32 = simple_strtoul(uuid_str, NULL, 16); + memcpy(uuid_bin, &tmp32, 4); + + /* + * reverse bytes from big to little endian + */ + tmp16 = simple_strtoul(uuid_str + 9, NULL, 16); + memcpy(uuid_bin + 4, &tmp16, 2); + + /* + * reverse bytes from big to little endian + */ + tmp16 = simple_strtoul(uuid_str + 14, NULL, 16); + memcpy(uuid_bin + 6, &tmp16, 2); + + /* + * reverse bytes from big to little endian + */ + tmp16 = simple_strtoul(uuid_str + 19, NULL, 16); + memcpy(uuid_bin + 8, &tmp16, 2); + + /* + * reverse bytes from big to little endian + */ + tmp64 = simple_strtoull(uuid_str + 24, NULL, 16); + memcpy(uuid_bin + 10, (char *)&tmp64, 6); + + return 0; +} + /* * uuid_bin_to_str() - convert big endian binary data to string UUID or GUID. *

Hi Abdellatif,
On Thu, Oct 13, 2022 at 11:38:49AM +0100, Abdellatif El Khlifi wrote:
convert big endian UUID string to little endian buffer
You don't really need the be_ suffix on the declaration, endianness doesn't apply to strings. Can't we do something similar to uuid_str_to_bin() and just convert to LE?
Thanks /Ilias
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v4:
- rename ffa_uuid_str_to_bin to be_uuid_str_to_le_bin and put in a standalone commit (the current)
v3:
- introduce ffa_uuid_str_to_bin (provided by arm_ffa: introduce Arm FF-A low-level driver)
include/uuid.h | 8 +++++++ lib/uuid.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+)
diff --git a/include/uuid.h b/include/uuid.h index 4a4883d3b5..ad3af350f9 100644 --- a/include/uuid.h +++ b/include/uuid.h @@ -2,6 +2,8 @@ /*
- Copyright (C) 2014 Samsung Electronics
- Przemyslaw Marczak p.marczak@samsung.com
- (C) Copyright 2022 ARM Limited
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#ifndef __UUID_H__ #define __UUID_H__ @@ -44,4 +46,10 @@ 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);
+/**
- be_uuid_str_to_le_bin - Converts a big endian UUID string to a little endian buffer
- */
+int be_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 465e1ac38f..15a9ab49d5 100644 --- a/lib/uuid.c +++ b/lib/uuid.c @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-2.0+ /*
- Copyright 2011 Calxeda, Inc.
- (C) Copyright 2022 ARM Limited
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#include <common.h> @@ -346,6 +348,68 @@ int uuid_str_to_bin(const char *uuid_str, unsigned char *uuid_bin, return 0; }
+/**
- be_uuid_str_to_le_bin - Converts a big endian UUID string to a little endian buffer
- @uuid_str: UUID string in big endian format (36 bytes wide + '/0')
- @uuid_bin: preallocated 16 bytes UUID buffer in little endian format
- UUID string is 36 characters (36 bytes):
- xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
- be be be be be
- where x is a hexadecimal character. Fields are separated by '-'s.
- When converting to a binary UUID, these endianness rules apply:
be: means the field in the string is considered a big endian hex number
and should be converted to little endian binary format
- Return:
- uuid_bin filled with little endian UUID data
- On success 0 is returned. Otherwise, failure code.
- */
+int be_uuid_str_to_le_bin(const char *uuid_str, unsigned char *uuid_bin) +{
- u16 tmp16 = 0;
- u32 tmp32 = 0;
- u64 tmp64 = 0;
- if (!uuid_str_valid(uuid_str) || !uuid_bin)
return -EINVAL;
- /*
* reverse bytes from big to little endian
*/
- tmp32 = simple_strtoul(uuid_str, NULL, 16);
- memcpy(uuid_bin, &tmp32, 4);
- /*
* reverse bytes from big to little endian
*/
- tmp16 = simple_strtoul(uuid_str + 9, NULL, 16);
- memcpy(uuid_bin + 4, &tmp16, 2);
- /*
* reverse bytes from big to little endian
*/
- tmp16 = simple_strtoul(uuid_str + 14, NULL, 16);
- memcpy(uuid_bin + 6, &tmp16, 2);
- /*
* reverse bytes from big to little endian
*/
- tmp16 = simple_strtoul(uuid_str + 19, NULL, 16);
- memcpy(uuid_bin + 8, &tmp16, 2);
- /*
* reverse bytes from big to little endian
*/
- tmp64 = simple_strtoull(uuid_str + 24, NULL, 16);
- memcpy(uuid_bin + 10, (char *)&tmp64, 6);
- return 0;
+}
/*
- uuid_bin_to_str() - convert big endian binary data to string UUID or GUID.
-- 2.17.1

On Mon, Oct 24, 2022 at 03:07:35PM +0300, Ilias Apalodimas wrote:
Hi Abdellatif,
On Thu, Oct 13, 2022 at 11:38:49AM +0100, Abdellatif El Khlifi wrote:
convert big endian UUID string to little endian buffer
You don't really need the be_ suffix on the declaration, endianness doesn't apply to strings. Can't we do something similar to uuid_str_to_bin() and just convert to LE?
Thanks, done in v7 [1]. Please have a look.
[1]: https://lore.kernel.org/all/20221107192055.21669-3-abdellatif.elkhlifi@arm.c...
Thanks /Ilias
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v4:
- rename ffa_uuid_str_to_bin to be_uuid_str_to_le_bin and put in a standalone commit (the current)
v3:
- introduce ffa_uuid_str_to_bin (provided by arm_ffa: introduce Arm FF-A low-level driver)
include/uuid.h | 8 +++++++ lib/uuid.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+)
diff --git a/include/uuid.h b/include/uuid.h index 4a4883d3b5..ad3af350f9 100644 --- a/include/uuid.h +++ b/include/uuid.h @@ -2,6 +2,8 @@ /*
- Copyright (C) 2014 Samsung Electronics
- Przemyslaw Marczak p.marczak@samsung.com
- (C) Copyright 2022 ARM Limited
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#ifndef __UUID_H__ #define __UUID_H__ @@ -44,4 +46,10 @@ 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);
+/**
- be_uuid_str_to_le_bin - Converts a big endian UUID string to a little endian buffer
- */
+int be_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 465e1ac38f..15a9ab49d5 100644 --- a/lib/uuid.c +++ b/lib/uuid.c @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-2.0+ /*
- Copyright 2011 Calxeda, Inc.
- (C) Copyright 2022 ARM Limited
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#include <common.h> @@ -346,6 +348,68 @@ int uuid_str_to_bin(const char *uuid_str, unsigned char *uuid_bin, return 0; }
+/**
- be_uuid_str_to_le_bin - Converts a big endian UUID string to a little endian buffer
- @uuid_str: UUID string in big endian format (36 bytes wide + '/0')
- @uuid_bin: preallocated 16 bytes UUID buffer in little endian format
- UUID string is 36 characters (36 bytes):
- xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
- be be be be be
- where x is a hexadecimal character. Fields are separated by '-'s.
- When converting to a binary UUID, these endianness rules apply:
be: means the field in the string is considered a big endian hex number
and should be converted to little endian binary format
- Return:
- uuid_bin filled with little endian UUID data
- On success 0 is returned. Otherwise, failure code.
- */
+int be_uuid_str_to_le_bin(const char *uuid_str, unsigned char *uuid_bin) +{
- u16 tmp16 = 0;
- u32 tmp32 = 0;
- u64 tmp64 = 0;
- if (!uuid_str_valid(uuid_str) || !uuid_bin)
return -EINVAL;
- /*
* reverse bytes from big to little endian
*/
- tmp32 = simple_strtoul(uuid_str, NULL, 16);
- memcpy(uuid_bin, &tmp32, 4);
- /*
* reverse bytes from big to little endian
*/
- tmp16 = simple_strtoul(uuid_str + 9, NULL, 16);
- memcpy(uuid_bin + 4, &tmp16, 2);
- /*
* reverse bytes from big to little endian
*/
- tmp16 = simple_strtoul(uuid_str + 14, NULL, 16);
- memcpy(uuid_bin + 6, &tmp16, 2);
- /*
* reverse bytes from big to little endian
*/
- tmp16 = simple_strtoul(uuid_str + 19, NULL, 16);
- memcpy(uuid_bin + 8, &tmp16, 2);
- /*
* reverse bytes from big to little endian
*/
- tmp64 = simple_strtoull(uuid_str + 24, NULL, 16);
- memcpy(uuid_bin + 10, (char *)&tmp64, 6);
- return 0;
+}
/*
- uuid_bin_to_str() - convert big endian binary data to string UUID or GUID.
-- 2.17.1

Add the core driver 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.
32-bit version of the ABIs is supported and 64-bit version of FFA_RXTX_MAP and FFA_MSG_SEND_DIRECT_{REQ, RESP}.
In u-boot FF-A design, FF-A is considered as a discoverable bus. 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:
- partition_info_get - sync_send_receive - rxtx_unmap
[1]: https://developer.arm.com/documentation/den0077/latest/
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v6:
* drop use of EFI runtime support (We decided with Linaro to add this later) * drop discovery from initcalls (discovery will be on demand by FF-A users) * set the alignment of the RX/TX buffers to the larger translation granule size * move FF-A RX/TX buffers unmapping at ExitBootServices() to a separate commit * update the documentation and move it to doc/arch/arm64.ffa.rst
v4:
* add doc/README.ffa.drv * moving the FF-A driver work to drivers/firmware/arm-ffa * use less #ifdefs in lib/efi_loader/efi_boottime.c and replace #if defined by #if CONFIG_IS_ENABLED * improving error handling by mapping the FF-A errors to standard errors and logs * replacing panics with an error log and returning an error code * improving features discovery in FFA_FEATURES by introducing rxtx_min_pages private data field * add ffa_remove and ffa_unbind functions * improve how the driver behaves when bus discovery is done more than once
v3:
* align the interfaces of the u-boot FF-A driver with those in the linux FF-A driver * remove the FF-A helper layer * make the u-boot FF-A driver independent from EFI * provide an optional config that enables copying the driver data to EFI runtime section at ExitBootServices service * use 64-bit version of FFA_RXTX_MAP, FFA_MSG_SEND_DIRECT_{REQ, RESP}
v2:
* make FF-A bus discoverable using device_{bind, probe} APIs * remove device tree support
v1:
* introduce FF-A bus driver with device tree support
MAINTAINERS | 7 + doc/arch/arm64.ffa.rst | 207 ++++ doc/arch/index.rst | 1 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/firmware/arm-ffa/Kconfig | 30 + drivers/firmware/arm-ffa/Makefile | 6 + drivers/firmware/arm-ffa/arm-ffa-uclass.c | 16 + drivers/firmware/arm-ffa/arm_ffa_prv.h | 196 +++ drivers/firmware/arm-ffa/core.c | 1337 +++++++++++++++++++++ include/arm_ffa.h | 93 ++ include/dm/uclass-id.h | 4 + 12 files changed, 1900 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_prv.h create mode 100644 drivers/firmware/arm-ffa/core.c create mode 100644 include/arm_ffa.h
diff --git a/MAINTAINERS b/MAINTAINERS index a26b36c7c2..496f47a516 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -248,6 +248,13 @@ 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 abdellatif.elkhlifi@arm.com +S: Maintained +F: doc/arch/arm64.ffa.rst +F: drivers/firmware/arm-ffa/ +F: include/arm_ffa.h + ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de M: Fabio Estevam festevam@gmail.com diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst new file mode 100644 index 0000000000..e98d2cf2b3 --- /dev/null +++ b/doc/arch/arm64.ffa.rst @@ -0,0 +1,207 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +Arm FF-A Driver +=============== + +Summary +------- + +FF-A stands for Firmware Framework for Arm A-profile processors. + +FF-A specifies interfaces that enable a pair of software sandboxes to +communicate with each other. A sandbox aka partition could +be a VM in the Normal or Secure world, an application in S-EL0, or a +Trusted OS in S-EL1. + +This FF-A driver implements the interfaces to communicate with partitions in +the Secure world aka Secure partitions (SPs). + +The driver 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. + +FF-A driver 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 + +FF-A and SMC specifications +------------------------------------------- + +The current implementation of the driver relies on FF-A specification v1.0 +and uses SMC32 calling convention. + +At this stage we only need the FF-A v1.0 features. + +The driver has been tested with Optee OS which supports SMC32 for most of +the SMC ABIs. + +For more details please refer to the FF-A v1.0 spec: +https://documentation-service.arm.com/static/5fb7e8a6ca04df4095c1d65e?token= + +Hypervisors are supported if they are configured to trap SMC calls. + +The FF-A driver uses 64-bit registers as per SMCCCv1.2 specification. + +For more details please refer to the SMC Calling Convention v1.2 spec: +https://documentation-service.arm.com/static/5f8edaeff86e16515cdbe4c6?token= + +Supported hardware +-------------------------------- + +Aarch64 plaforms + +Configuration +---------------------- + +CONFIG_ARM_FFA_TRANSPORT + Enables the FF-A bus driver. Turn this on if you want to use FF-A + communication. + +CONFIG_SANDBOX_FFA + Enables FF-A Sandbox driver. This emulates the FF-A ABIs handling under + Sandbox and provides functional tests for FF-A. + +FF-A ABIs under the hood +--------------------------------------- + +Invoking an FF-A ABI involves providing to the secure world/hypervisor the +expected arguments from the ABI. + +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 FF-A driver (non-secure world). + +The driver reads the response and processes it accordingly. + +This methodology applies to all the FF-A ABIs in the driver. + +FF-A bus discovery in U-boot +------------------------------------------- + +When CONFIG_ARM_FFA_TRANSPORT is enabled, the FF-A bus is discovered on +demand by the clients (users). + +Clients can discover the FF-A bus using ffa_bus_discover() API which triggers the +discovery process. + +ffa_bus_discover() creates, binds and probes the arm_ffa device using +device_{bind, probe} APIs. + +The discovery process consists in communicating with secure world (or hypervisor) +and querying specific data. + +The discovery process takes place during the arm_ffa device probing which is +handled by ffa_probe(). + +The FF-A bus discovery is successful and the bus is ready for use when these +operations succeed: + +- querying the FF-A framework version +- querying from secure world the U-boot endpoint ID +- querying from secure world the RX/TX mapping features +- mapping the RX/TX buffers +- querying from secure world all the partitions information + +Discovery failure results in a probing failure and the arm_ffa device is +destroyed. + +Requirements for clients +------------------------------------- + +When using the FF-A bus with EFI, clients must: + +- Query SPs in EFI boot time mode using the service UUID. +- Unmap RX/TX buffers before EFI runtime mode starts. + +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 by the user before EFI runtime mode +starts. The driver provides a bus operation for that: rxtx_unmap() + +If RX/TX buffers created by U-boot are not unmapped and by +consequence becoming available at EFI runtime, secure world will get confused +about RX/TX buffers ownership (U-boot vs kernel). + +The bus driver layer +------------------------------ + +The driver comes on top of the SMCCC layer and is implemented in +drivers/firmware/arm-ffa/core.c + +The driver provides the following features: + +- 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 + +- 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, the driver takes care of how to transport + that to the secure world/hypervisor using FF-A + +- The driver provides callbacks to be used by clients to access the FF-A bus: + +partition_info_get +sync_send_receive +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 + +Using armffa command +----------------------------------- + +armffa is a command showcasing how to use the FF-A driver and how to invoke +its operations. + +This provides a guidance to the client developers on how to call the FF-A bus +interfaces. + +Usage: + +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 the arm_ffa device info + +Contributors +------------ + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com diff --git a/doc/arch/index.rst b/doc/arch/index.rst index 792d9182c3..8d1ab0ad4e 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/Kconfig b/drivers/Kconfig index 8b6fead351..b06b1ae481 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -6,6 +6,8 @@ source "drivers/core/Kconfig"
source "drivers/adc/Kconfig"
+source "drivers/firmware/arm-ffa/Kconfig" + source "drivers/ata/Kconfig"
source "drivers/axi/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 9d9f69a3c9..bf8d7b8cfc 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -111,6 +111,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/arm-ffa/Kconfig b/drivers/firmware/arm-ffa/Kconfig new file mode 100644 index 0000000000..f1427535f9 --- /dev/null +++ b/drivers/firmware/arm-ffa/Kconfig @@ -0,0 +1,30 @@ +# 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 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. + + This driver 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. + 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. + + For more details about the FF-A driver, please refer to doc/README.ffa.drv + diff --git a/drivers/firmware/arm-ffa/Makefile b/drivers/firmware/arm-ffa/Makefile new file mode 100644 index 0000000000..043a8915be --- /dev/null +++ b/drivers/firmware/arm-ffa/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2022 +# Abdellatif El Khlifi, Arm Limited, abdellatif.elkhlifi@arm.com. + +obj-y += arm-ffa-uclass.o core.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 0000000000..7d9695d289 --- /dev/null +++ b/drivers/firmware/arm-ffa/arm-ffa-uclass.c @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <dm.h> +#include <asm/global_data.h> + +DECLARE_GLOBAL_DATA_PTR; + +UCLASS_DRIVER(ffa) = { + .name = "ffa", + .id = UCLASS_FFA, +}; diff --git a/drivers/firmware/arm-ffa/arm_ffa_prv.h b/drivers/firmware/arm-ffa/arm_ffa_prv.h new file mode 100644 index 0000000000..7bc90f7f66 --- /dev/null +++ b/drivers/firmware/arm-ffa/arm_ffa_prv.h @@ -0,0 +1,196 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __ARM_FFA_PRV_H +#define __ARM_FFA_PRV_H + +#include <arm_ffa.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/arm-smccc.h> + +/* + * This header is private. It is exclusively used by the FF-A driver + */ + +/* FF-A core driver name */ +#define FFA_DRV_NAME "arm_ffa" + +/* 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*/ +}; + +/* number of the errors supported by the FF-A specification */ +#define MAX_NUMBER_FFA_ERR 9 + +/* 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) + +/* The FF-A SMC function definitions */ + +typedef struct arm_smccc_1_2_regs ffa_value_t; +typedef void (*invoke_ffa_fn_t)(ffa_value_t args, ffa_value_t *res); + +/* + * 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 */ +}; + +/** + * 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 - structure hosting 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 + * + * Data structure hosting 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 { + u64 rxbuf; /* virtual address */ + u64 txbuf; /* virtual address */ + size_t rxtx_min_pages; /* minimum number of pages in each of the RX/TX buffers */ +}; + +/** + * 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_partitions - descriptors for all secure partitions + * @count: The number of partitions descriptors + * @descs The partitions descriptors table + * + * This data structure contains the partitions descriptors table + */ +struct ffa_partitions { + u32 count; + struct ffa_partition_desc *descs; /* virtual address */ +}; + +/** + * struct ffa_prvdata - the driver private data structure + * + * @dev: The arm_ffa device under u-boot driver model + * @ffa_ops: The driver operations structure + * @fwk_version: FF-A framework version + * @id: u-boot endpoint ID + * @partitions: The partitions descriptors structure + * @pair: The RX/TX buffers pair + * @invoke_ffa_fn: The function executing the FF-A function + * + * The driver data structure hosting all resident data. + */ +struct ffa_prvdata { + struct udevice *dev; + struct ffa_bus_ops ffa_ops; + u32 fwk_version; + u16 id; + struct ffa_partitions partitions; + struct ffa_rxtxpair pair; + invoke_ffa_fn_t invoke_ffa_fn; +}; + +/** + * ffa_device_get - create, bind and probe the arm_ffa device + */ +int ffa_device_get(void); + +/** + * ffa_bus_prvdata_get - bus driver private data getter + */ +struct ffa_prvdata **ffa_bus_prvdata_get(void); + +#endif diff --git a/drivers/firmware/arm-ffa/core.c b/drivers/firmware/arm-ffa/core.c new file mode 100644 index 0000000000..324367d12b --- /dev/null +++ b/drivers/firmware/arm-ffa/core.c @@ -0,0 +1,1337 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include "arm_ffa_prv.h" +#include <asm/global_data.h> +#include <common.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <dm/devres.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> +#include <log.h> +#include <malloc.h> +#include <string.h> +#include <uuid.h> + +DECLARE_GLOBAL_DATA_PTR; + +/** + * The device private data structure containing all the + * data read from secure world + */ +struct ffa_prvdata *ffa_priv_data; + +/* Error mapping declarations */ + +int ffa_to_std_errmap[MAX_NUMBER_FFA_ERR] = { + 0, + -EOPNOTSUPP, /* NOT_SUPPORTED */ + -EINVAL, /* INVALID_PARAMETERS */ + -ENOMEM, /* NO_MEMORY */ + -EBUSY, /* BUSY */ + -EINTR, /* INTERRUPTED */ + -EACCES, /* DENIED */ + -EAGAIN, /* RETRY */ + -ECANCELED, /* ABORTED */ +}; + +struct ffa_abi_errmap err_msg_map[FFA_ERRMAP_COUNT] = { + [FFA_ID_TO_ERRMAP_ID(FFA_VERSION)] = { + { + "", + "NOT_SUPPORTED: A Firmware Framework implementation does not exist", + "", /* INVALID_PARAMETERS */ + "", /* NO_MEMORY */ + "", /* BUSY */ + "", /* INTERRUPTED */ + "", /* DENIED */ + "", /* RETRY */ + "", /* ABORTED */ + }, + }, + [FFA_ID_TO_ERRMAP_ID(FFA_ID_GET)] = { + { + "", + "NOT_SUPPORTED: This function is not implemented at this FF-A instance", + "", /* INVALID_PARAMETERS */ + "", /* NO_MEMORY */ + "", /* BUSY */ + "", /* INTERRUPTED */ + "", /* DENIED */ + "", /* RETRY */ + "", /* ABORTED */ + }, + }, + [FFA_ID_TO_ERRMAP_ID(FFA_FEATURES)] = { + { + "", + "NOT_SUPPORTED: FFA_RXTX_MAP is not implemented at this FF-A instance", + "", /* INVALID_PARAMETERS */ + "", /* NO_MEMORY */ + "", /* BUSY */ + "", /* INTERRUPTED */ + "", /* DENIED */ + "", /* RETRY */ + "", /* ABORTED */ + }, + }, + [FFA_ID_TO_ERRMAP_ID(FFA_PARTITION_INFO_GET)] = { + { + "", + "NOT_SUPPORTED: This function is not implemented at this FF-A instance", + "INVALID_PARAMETERS: Unrecognized UUID", + "NO_MEMORY: Results cannot fit in RX buffer of the caller", + "BUSY: RX buffer of the caller is not free", + "", /* INTERRUPTED */ + "DENIED: Callee is not in a state to handle this request", + "", /* RETRY */ + "", /* ABORTED */ + }, + }, + [FFA_ID_TO_ERRMAP_ID(FFA_RXTX_UNMAP)] = { + { + "", + "NOT_SUPPORTED: FFA_RXTX_UNMAP is not implemented at this FF-A instance", + "INVALID_PARAMETERS: No buffer pair registered on behalf of the caller", + "", /* NO_MEMORY */ + "", /* BUSY */ + "", /* INTERRUPTED */ + "", /* DENIED */ + "", /* RETRY */ + "", /* ABORTED */ + }, + }, + [FFA_ID_TO_ERRMAP_ID(FFA_RX_RELEASE)] = { + { + "", + "NOT_SUPPORTED: FFA_RX_RELEASE is not implemented at this FF-A instance", + "", /* INVALID_PARAMETERS */ + "", /* NO_MEMORY */ + "", /* BUSY */ + "", /* INTERRUPTED */ + "DENIED: Caller did not have ownership of the RX buffer", + "", /* RETRY */ + "", /* ABORTED */ + }, + }, + [FFA_ID_TO_ERRMAP_ID(FFA_RXTX_MAP)] = { + { + "", + "NOT_SUPPORTED: This function is not implemented at this FF-A instance", + "INVALID_PARAMETERS: Field(s) in input parameters incorrectly encoded", + "NO_MEMORY: Not enough memory", + "", /* BUSY */ + "", /* INTERRUPTED */ + "DENIED: Buffer pair already registered", + "", /* RETRY */ + "", /* ABORTED */ + }, + }, +}; + +/** + * ffa_to_std_errno - convert FF-A error code to standard error code + * @ffa_errno: Error code returned by the FF-A ABI + * + * This function maps 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 + */ +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 + * + * This function maps 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 + */ +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_msg_map[abi_idx].err_str[err_idx]) + return -EINVAL; + + ffa_err("%s", err_msg_map[abi_idx].err_str[err_idx]); + + return 0; +} + +/* + * Driver core functions + */ + +/** + * ffa_remove_device - removes the arm_ffa device + * @dev: the device to be removed + * + * This function makes sure the arm_ffa device is removed + * No need to free the kmalloced data when the device is destroyed. + * It's automatically done by devm management by + * device_remove() -> device_free() -> devres_release_probe(). + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int ffa_remove_device(struct udevice *dev) +{ + int ret; + + if (!dev) { + ffa_err("no udevice found"); + return -ENODEV; + } + + ret = device_remove(dev, DM_REMOVE_NORMAL); + if (ret) { + ffa_err("unable to remove. err:%d\n", ret); + return ret; + } + + ffa_info("device removed and freed"); + + ret = device_unbind(dev); + if (ret) { + ffa_err("unable to unbind. err:%d\n", ret); + return ret; + } + + ffa_info("device unbound"); + + return 0; +} + +/** + * ffa_device_get - create, bind and probe the arm_ffa device + * + * This function makes sure the arm_ffa device is + * created, bound to this driver, probed and ready to use. + * Arm FF-A transport is implemented through a single u-boot + * device managing the FF-A bus (arm_ffa). + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int ffa_device_get(void) +{ + int ret; + struct udevice *dev = NULL; + + ret = device_bind(dm_root(), + DM_DRIVER_GET(arm_ffa), + FFA_DRV_NAME, + NULL, + ofnode_null(), + &dev); + if (ret) + return ret; + + /* The FF-A bus discovery succeeds when probing is successful */ + ret = device_probe(dev); + if (ret) { + ffa_err("arm_ffa device probing failed"); + ffa_remove_device(dev); + return ret; + } + + return 0; +} + +/** + * ffa_get_version - FFA_VERSION handler function + * + * This function implements FFA_VERSION FF-A function + * to get from the secure world the FF-A framework version + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_get_version(void) +{ + u16 major, minor; + ffa_value_t res = {0}; + int ffa_errno; + + ffa_priv_data->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); + + ffa_info("FF-A driver %d.%d\nFF-A framework %d.%d", + FFA_MAJOR_VERSION, FFA_MINOR_VERSION, major, minor); + + if ((major == FFA_MAJOR_VERSION && minor >= FFA_MINOR_VERSION)) { + ffa_info("Versions are compatible "); + + ffa_priv_data->fwk_version = res.a0; + + return 0; + } + + ffa_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 + * + * This function implements 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(void) +{ + ffa_value_t res = {0}; + int ffa_errno; + + ffa_priv_data->invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_32(FFA_ID_GET) + }, &res); + + if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) { + ffa_priv_data->id = GET_SELF_ENDPOINT_ID((u32)res.a2); + ffa_info("endpoint ID is %u", ffa_priv_data->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 - sets the minimum number of pages in each of the RX/TX buffers + * @prop_field: properties field obtained from FFA_FEATURES ABI + * + * This function sets the minimum number of pages + * in each of the RX/TX buffers in the private data structure + * + * Return: + * + * buf_4k_pages points to the returned number of pages + * 0 on success. Otherwise, failure + */ +static int ffa_set_rxtx_buffers_pages_cnt(u32 prop_field) +{ + if (!ffa_priv_data) + return -EINVAL; + + switch (prop_field) { + case RXTX_4K: + ffa_priv_data->pair.rxtx_min_pages = 1; + break; + case RXTX_16K: + ffa_priv_data->pair.rxtx_min_pages = 4; + break; + case RXTX_64K: + ffa_priv_data->pair.rxtx_min_pages = 16; + break; + default: + ffa_err("RX/TX buffer size not supported"); + return -EINVAL; + } + + return 0; +} + +/** + * ffa_get_rxtx_map_features - FFA_FEATURES handler function with FFA_RXTX_MAP argument + * + * This function implements 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(void) +{ + ffa_value_t res = {0}; + int ffa_errno; + + ffa_priv_data->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((u32)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 - frees the RX/TX buffers + * + * This function frees the RX/TX buffers + * + */ +static void ffa_free_rxtx_buffers(void) +{ + ffa_info("Freeing RX/TX buffers"); + + if (ffa_priv_data->pair.rxbuf) { + free((void *)ffa_priv_data->pair.rxbuf); + ffa_priv_data->pair.rxbuf = 0; + } + + if (ffa_priv_data->pair.txbuf) { + free((void *)ffa_priv_data->pair.txbuf); + ffa_priv_data->pair.txbuf = 0; + } +} + +/** + * ffa_alloc_rxtx_buffers - allocates the RX/TX buffers + * + * This function is 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. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_alloc_rxtx_buffers(void) +{ + u64 bytes; + + ffa_info("Using %lu 4KB page(s) for RX/TX buffers size", + ffa_priv_data->pair.rxtx_min_pages); + + bytes = ffa_priv_data->pair.rxtx_min_pages * SZ_4K; + + /* + * The alignment of the RX and TX buffers must be equal + * to the larger translation granule size + */ + + ffa_priv_data->pair.rxbuf = (u64)memalign(bytes, bytes); + if (!ffa_priv_data->pair.rxbuf) { + ffa_err("failure to allocate RX buffer"); + return -ENOBUFS; + } + + ffa_info("RX buffer at virtual address 0x%llx", ffa_priv_data->pair.rxbuf); + + ffa_priv_data->pair.txbuf = (u64)memalign(bytes, bytes); + if (!ffa_priv_data->pair.txbuf) { + free((void *)ffa_priv_data->pair.rxbuf); + ffa_priv_data->pair.rxbuf = 0; + ffa_err("failure to allocate the TX buffer"); + return -ENOBUFS; + } + + ffa_info("TX buffer at virtual address 0x%llx", ffa_priv_data->pair.txbuf); + + /* + * make sure the buffers are cleared before use + */ + memset((void *)ffa_priv_data->pair.rxbuf, 0, bytes); + memset((void *)ffa_priv_data->pair.txbuf, 0, bytes); + + return 0; +} + +/** + * ffa_map_rxtx_buffers - FFA_RXTX_MAP handler function + * + * This function implements FFA_RXTX_MAP FF-A function + * to map the RX/TX buffers + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_map_rxtx_buffers(void) +{ + int ret; + ffa_value_t res = {0}; + int ffa_errno; + + ret = ffa_alloc_rxtx_buffers(); + 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 + */ + + ffa_priv_data->invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_64(FFA_RXTX_MAP), + .a1 = ffa_priv_data->pair.txbuf, + .a2 = ffa_priv_data->pair.rxbuf, + .a3 = ffa_priv_data->pair.rxtx_min_pages, + }, &res); + + if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) { + ffa_info("RX/TX buffers mapped"); + return 0; + } + + ffa_errno = res.a2; + ffa_print_error_log(FFA_RXTX_MAP, ffa_errno); + + ffa_free_rxtx_buffers(); + + return ffa_to_std_errno(ffa_errno); +} + +/** + * ffa_unmap_rxtx_buffers - FFA_RXTX_UNMAP handler function + * + * This function implements FFA_RXTX_UNMAP FF-A function + * to unmap the RX/TX buffers + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_unmap_rxtx_buffers(void) +{ + ffa_value_t res = {0}; + int ffa_errno; + + ffa_priv_data->invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_32(FFA_RXTX_UNMAP), + .a1 = PREP_SELF_ENDPOINT_ID(ffa_priv_data->id) + }, &res); + + if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) { + ffa_free_rxtx_buffers(); + 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 - FFA_RX_RELEASE handler function + * + * This function invokes 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(void) +{ + ffa_value_t res = {0}; + int ffa_errno; + + ffa_priv_data->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 - checks whether two given UUIDs are identical + * @uuid1: first UUID + * @uuid2: second UUID + * + * This function is used by ffa_read_partitions_info to search + * for a UUID in the partitions descriptors table + * + * Return: + * + * 1 when UUIDs match. Otherwise, 0 + */ +int 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 - reads the data queried by FFA_PARTITION_INFO_GET + * and saves it in the private structure + * @count: The number of partitions queried + * @part_uuid: Pointer to the partition(s) UUID + * + * This function reads the partitions information + * returned by the FFA_PARTITION_INFO_GET and saves it in the private + * data structure. + * + * Return: + * + * The private data structure is updated with the partition(s) information + * 0 is returned on success. Otherwise, failure + */ +static int ffa_read_partitions_info(u32 count, struct ffa_partition_uuid *part_uuid) +{ + if (!count) { + ffa_err("no partition detected"); + return -ENODATA; + } + + ffa_info("Reading partitions data from the RX buffer"); + + 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 = ffa_priv_data->pair.rxtx_min_pages * SZ_4K; + + if (data_bytes > buf_bytes) { + ffa_err("partitions data size exceeds the RX buffer size:"); + ffa_err(" sizes in bytes: data %llu , RX buffer %llu ", + data_bytes, + buf_bytes); + + return -ENOMEM; + } + + ffa_priv_data->partitions.descs = devm_kmalloc(ffa_priv_data->dev, data_bytes, + __GFP_ZERO); + if (!ffa_priv_data->partitions.descs) { + ffa_err("cannot allocate partitions data buffer"); + return -ENOMEM; + } + + parts_info = (struct ffa_partition_info *)ffa_priv_data->pair.rxbuf; + + for (desc_idx = 0 ; desc_idx < count ; desc_idx++) { + ffa_priv_data->partitions.descs[desc_idx].info = + parts_info[desc_idx]; + + ffa_info("Partition ID %x : info cached", + ffa_priv_data->partitions.descs[desc_idx].info.id); + } + + ffa_priv_data->partitions.count = count; + + ffa_info("%d partition(s) found and cached", count); + + } else { + u32 rx_desc_idx, cached_desc_idx; + struct ffa_partition_info *parts_info; + u8 desc_found; + + parts_info = (struct ffa_partition_info *)ffa_priv_data->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 < ffa_priv_data->partitions.count; + cached_desc_idx++) { + /* + * save the UUID + */ + if (ffa_priv_data->partitions.descs[cached_desc_idx].info.id == + parts_info[rx_desc_idx].id) { + ffa_priv_data->partitions.descs[cached_desc_idx].sp_uuid = + *part_uuid; + + desc_found = 1; + break; + } + } + + if (!desc_found) + return -ENODATA; + } + } + + return 0; +} + +/** + * ffa_query_partitions_info - invokes FFA_PARTITION_INFO_GET and saves partitions data + * + * @part_uuid: Pointer to the partition(s) UUID + * @pcount: Pointer to the number of partitions variable filled when querying + * + * This function executes the FFA_PARTITION_INFO_GET + * to query the partitions data. Then, it calls ffa_read_partitions_info + * to save the data in the private data structure. + * + * 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 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; + } + + ffa_priv_data->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((u32)res.a2, part_uuid); + if (ret) { + ffa_err("failed to read partition(s) data , error (%d)", ret); + ffa_release_rx_buffer(); + 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(); + + 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 - FFA_PARTITION_INFO_GET handler function + * + * The passed arguments: + * Mode 1: When getting from the driver the number of + * secure partitions: + * @uuid_str: pointer to the UUID string + * @parts_size: pointer to the variable that contains the number of partitions + * The variable will be set by the driver + * @buffer: NULL + * + * Mode 2: When requesting the driver to return the + * partitions information: + * @uuid_str: pointer to the UUID string + * @parts_size: pointer to the size of the SPs information buffer in bytes + * @buffer: pointer to SPs information buffer + * (allocated by the client). + * The buffer will be filled by the driver + * + * This function queries the secure partition data from + * the private data structure. If not found, it invokes 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 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(). + * + * When invoked through a client request, ffa_get_partitions_info should be + * called twice. First call is to get from the driver the number of secure + * partitions (SPs) associated to a particular UUID. + * Then, the caller (client) allocates the buffer to host the SPs data and + * issues a 2nd call. Then, the driver fills the SPs data in the pre-allocated + * buffer. + * + * To achieve the mechanism described above, ffa_get_partitions_info uses the + * following functions: + * ffa_read_partitions_info + * ffa_query_partitions_info + * + * Return: + * + * @parts_size: When pointing to the number of partitions variable, the number is + * set by the driver. + * When pointing to the partitions information buffer size, the buffer will be + * filled by the driver. + * + * On success 0 is returned. Otherwise, failure + */ +static int ffa_get_partitions_info(const char *uuid_str, + u32 *parts_size, struct ffa_partition_info *buffer) +{ + /* + * fill_data: + * 0: return the SP count + * 1: fill SP data and return it to the caller + * -1: undefined mode + */ + int fill_data = -1; + u32 desc_idx, client_desc_idx; + struct ffa_partition_uuid part_uuid = {0}; + u32 client_desc_max_cnt; + u32 parts_found = 0; + + if (!ffa_priv_data->partitions.count || !ffa_priv_data->partitions.descs) { + ffa_err("no partition installed"); + return -EINVAL; + } + + if (!uuid_str) { + ffa_err("no UUID provided"); + return -EINVAL; + } + + if (!parts_size) { + ffa_err("no size/count provided"); + return -EINVAL; + } + + if (be_uuid_str_to_le_bin(uuid_str, (unsigned char *)&part_uuid)) { + ffa_err("invalid UUID"); + return -EINVAL; + } + + if (!buffer) { + /* Mode 1: getting the number of secure partitions */ + + fill_data = 0; + + ffa_info("Preparing for checking partitions count"); + + } else if ((*parts_size >= sizeof(struct ffa_partition_info)) && + !(*parts_size % sizeof(struct ffa_partition_info))) { + /* Mode 2: retrieving the partitions information */ + + fill_data = 1; + + client_desc_idx = 0; + + /* + * number of empty descriptors preallocated by the caller + */ + client_desc_max_cnt = *parts_size / sizeof(struct ffa_partition_info); + + ffa_info("Preparing for filling partitions info"); + + } else { + ffa_err("invalid function arguments provided"); + return -EINVAL; + } + + ffa_info("Searching partitions using the provided UUID"); + + /* + * search in the cached partitions + */ + for (desc_idx = 0; + desc_idx < ffa_priv_data->partitions.count; + desc_idx++) { + if (ffa_uuid_are_identical(&ffa_priv_data->partitions.descs[desc_idx].sp_uuid, + &part_uuid)) { + ffa_info("Partition ID %x matches the provided UUID", + ffa_priv_data->partitions.descs[desc_idx].info.id); + + parts_found++; + + if (fill_data) { + /* + * trying to fill the partition info in the input buffer + */ + + if (client_desc_idx < client_desc_max_cnt) { + buffer[client_desc_idx++] = + ffa_priv_data->partitions.descs[desc_idx].info; + continue; + } + + ffa_err("failed to fill the current descriptor client buffer full"); + return -ENOBUFS; + } + } + } + + if (!parts_found) { + int ret; + + ffa_info("No partition found. Querying framework ..."); + + ret = ffa_query_partitions_info(&part_uuid, &parts_found); + + if (ret == 0) { + if (!fill_data) { + *parts_size = parts_found; + + ffa_info("Number of partition(s) found matching the UUID: %d", + parts_found); + } else { + /* + * If SPs data detected, they are already in the private data + * structure, retry searching SP data again to return them + * to the caller + */ + if (parts_found) + ret = ffa_get_partitions_info(uuid_str, parts_size, buffer); + else + ret = -ENODATA; + } + } + + return ret; + } + + /* partition(s) found */ + if (!fill_data) + *parts_size = parts_found; + + return 0; +} + +/** + * ffa_cache_partitions_info - Queries and saves all secure partitions data + * + * This function invokes 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 the + * resident private data structure and we keep the UUID field empty + * (in FF-A 1.0 UUID is not provided by the partition descriptor) + * + * This function is 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(void) +{ + return ffa_query_partitions_info(NULL, NULL); +} + +/** + * ffa_msg_send_direct_req - FFA_MSG_SEND_DIRECT_{REQ,RESP} handler function + * @dst_part_id: destination partition ID + * @msg: pointer to the message data preallocated by the client (in/out) + * + * 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 + */ +static int ffa_msg_send_direct_req(u16 dst_part_id, struct ffa_send_direct_data *msg) +{ + ffa_value_t res = {0}; + int ffa_errno; + + if (!ffa_priv_data || !ffa_priv_data->invoke_ffa_fn) + return -EINVAL; + + /* No partition installed */ + if (!ffa_priv_data->partitions.count || !ffa_priv_data->partitions.descs) + return -ENODEV; + + ffa_priv_data->invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_64(FFA_MSG_SEND_DIRECT_REQ), + .a1 = PREP_SELF_ENDPOINT_ID(ffa_priv_data->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)) + ffa_priv_data->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 == FFA_SMC_64(FFA_MSG_SEND_DIRECT_RESP)) { + /* + * 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); +} + +/** + * __arm_ffa_fn_smc - 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 + * + * Return: void + */ +void __arm_ffa_fn_smc(ffa_value_t args, ffa_value_t *res) +{ + arm_smccc_1_2_smc(&args, res); +} + +/** + * ffa_set_smc_conduit - Set the SMC conduit + * + * This function selects the SMC conduit by setting the driver invoke function + * to SMC assembly function + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_set_smc_conduit(void) +{ + ffa_priv_data->invoke_ffa_fn = __arm_ffa_fn_smc; + + if (!ffa_priv_data->invoke_ffa_fn) { + ffa_err("failure to set the invoke function"); + return -EINVAL; + } + + ffa_info("Conduit is SMC"); + + return 0; +} + +/** + * ffa_set_bus_ops - Set the bus driver operations + * + * Setting the driver callbacks. + * + */ +static void ffa_set_bus_ops(void) +{ + ffa_priv_data->ffa_ops.partition_info_get = ffa_get_partitions_info; + ffa_priv_data->ffa_ops.sync_send_receive = ffa_msg_send_direct_req; + ffa_priv_data->ffa_ops.rxtx_unmap = ffa_unmap_rxtx_buffers; +} + +/** + * ffa_alloc_prvdata - allocate the driver main data structure and sets the device + * @dev: the arm_ffa device + * + * This function creates the main data structure embedding all the driver data. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_alloc_prvdata(struct udevice *dev) +{ + if (!dev) { + ffa_err("no udevice found"); + return -ENODEV; + } + + /* The device is registered with the DM. Let's create the driver main data structure*/ + + ffa_priv_data = devm_kmalloc(dev, sizeof(struct ffa_prvdata), __GFP_ZERO); + if (!ffa_priv_data) { + ffa_err("can not allocate the driver main data structure"); + return -ENOMEM; + } + + ffa_priv_data->dev = dev; + + return 0; +} + +/** + * ffa_probe - The driver probe function + * @dev: the arm_ffa device + * + * Probing is done at boot time and triggered by the uclass device discovery. + * At probe level the following actions are done: + * - setting the conduit + * - querying the FF-A framework version + * - 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 the resident private data structure. + * + * The probe will fail if either FF-A framework is not detected or the + * FF-A requests are not behaving correctly. This ensures that the + * driver is not installed and its operations are not exported to the clients. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_probe(struct udevice *dev) +{ + int ret; + + ret = ffa_alloc_prvdata(dev); + if (ret != 0) + return ret; + + ffa_set_bus_ops(); + + ret = ffa_set_smc_conduit(); + if (ret != 0) + return ret; + + ret = ffa_get_version(); + if (ret != 0) + return ret; + + ret = ffa_get_endpoint_id(); + if (ret != 0) + return ret; + + ret = ffa_get_rxtx_map_features(); + if (ret != 0) + return ret; + + ret = ffa_map_rxtx_buffers(); + if (ret != 0) + return ret; + + ret = ffa_cache_partitions_info(); + if (ret != 0) { + ffa_free_rxtx_buffers(); + return ret; + } + + return 0; +} + +/** + * ffa_remove - The driver remove function + * @dev: the arm_ffa device + * When the device is about to be removed , unmap the RX/TX buffers and free the memory + * Return: + * + * 0 on success. + */ +static int ffa_remove(struct udevice *dev) +{ + ffa_info("removing the device"); + + ffa_unmap_rxtx_buffers(); + + if (ffa_priv_data->pair.rxbuf || ffa_priv_data->pair.txbuf) + ffa_free_rxtx_buffers(); + + return 0; +} + +/** + * ffa_unbind - The driver unbind function + * @dev: the arm_ffa device + * After the device is removed and memory freed the device is unbound + * Return: + * + * 0 on success. + */ +static int ffa_unbind(struct udevice *dev) +{ + ffa_info("unbinding the device , private data already released"); + + ffa_priv_data = NULL; + + return 0; +} + +/** + * ffa_bus_ops_get - bus driver operations getter + * + * Return: + * This function returns a pointer to the driver operations structure + */ +const struct ffa_bus_ops *ffa_bus_ops_get(void) +{ + return &ffa_priv_data->ffa_ops; +} + +/** + * ffa_bus_prvdata_get - bus driver private data getter + * + * Return: + * This function returns a pointer to the main private data structure + */ +struct ffa_prvdata **ffa_bus_prvdata_get(void) +{ + return &ffa_priv_data; +} + +/** + * ffa_bus_discover - discover FF-A bus and probe the arm_ffa device + * + * This function makes sure the FF-A bus is discoverable. + * When probing succeeds FF-A discovery is done. The arm_ffa device is ready to use. + * + * When the bus was already discovered successfully the discovery will not run again. + * + * Arm FF-A transport is implemented through arm_ffa u-boot device managing the FF-A + * communication. + * All FF-A clients should use the arm_ffa device to use the FF-A transport. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int ffa_bus_discover(void) +{ + int ret = 0; + + if (!ffa_priv_data) + ret = ffa_device_get(); + + return ret; +} + +/** + * Declaring the arm_ffa driver under UCLASS_FFA + */ + +U_BOOT_DRIVER(arm_ffa) = { + .name = FFA_DRV_NAME, + .id = UCLASS_FFA, + .probe = ffa_probe, + .remove = ffa_remove, + .unbind = ffa_unbind, +}; diff --git a/include/arm_ffa.h b/include/arm_ffa.h new file mode 100644 index 0000000000..d146e7b328 --- /dev/null +++ b/include/arm_ffa.h @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __ARM_FFA_H +#define __ARM_FFA_H + +#include <linux/printk.h> + +/* + * This header is public. It can be used by clients to access + * data structures and definitions they need + */ + +/* + * Macros for displaying logs + */ + +#define ffa_info(fmt, ...) pr_info("[FFA] " fmt "\n", ##__VA_ARGS__) +#define ffa_err(fmt, ...) pr_err("[FFA] " fmt "\n", ##__VA_ARGS__) + +/* + * 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 __packed 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_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 __packed ffa_send_direct_data { + unsigned long data0; /* w3/x3 */ + unsigned long data1; /* w4/x4 */ + unsigned long data2; /* w5/x5 */ + unsigned long data3; /* w6/x6 */ + unsigned long data4; /* w7/x7 */ +}; + +/** + * struct ffa_bus_ops - The driver operations structure + * @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)(const char *uuid_str, + u32 *parts_size, struct ffa_partition_info *buffer); + int (*sync_send_receive)(u16 dst_part_id, struct ffa_send_direct_data *msg); + int (*rxtx_unmap)(void); +}; + +/** + * The device driver and the Uclass driver public functions + */ + +/** + * ffa_bus_ops_get - driver operations getter + */ +const struct ffa_bus_ops *ffa_bus_ops_get(void); + +/** + * ffa_bus_discover - discover FF-A bus and probes the arm_ffa device + */ +int ffa_bus_discover(void); + +#endif diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index a432e43871..5dd698b7a9 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -4,6 +4,9 @@ * * (C) Copyright 2012 * Pavel Herrmann morpheus.ibis@gmail.com + * + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#ifndef _DM_UCLASS_ID_H @@ -55,6 +58,7 @@ enum uclass_id { UCLASS_EFI_MEDIA, /* Devices provided by UEFI firmware */ UCLASS_ETH, /* Ethernet device */ UCLASS_ETH_PHY, /* Ethernet PHY device */ + UCLASS_FFA, /* Arm Firmware Framework for Armv8-A */ UCLASS_FIRMWARE, /* Firmware */ UCLASS_FUZZING_ENGINE, /* Fuzzing engine */ UCLASS_FS_FIRMWARE_LOADER, /* Generic loader */

On Thu, Oct 13, 2022 at 11:38:50AM +0100, Abdellatif El Khlifi wrote:
Add the core driver 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.
32-bit version of the ABIs is supported and 64-bit version of FFA_RXTX_MAP and FFA_MSG_SEND_DIRECT_{REQ, RESP}.
In u-boot FF-A design, FF-A is considered as a discoverable bus.
U-Boot
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:
- partition_info_get
- sync_send_receive
- rxtx_unmap
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v6:
- drop use of EFI runtime support (We decided with Linaro to add this later)
- drop discovery from initcalls (discovery will be on demand by FF-A users)
- set the alignment of the RX/TX buffers to the larger translation granule size
- move FF-A RX/TX buffers unmapping at ExitBootServices() to a separate commit
- update the documentation and move it to doc/arch/arm64.ffa.rst
v4:
- add doc/README.ffa.drv
- moving the FF-A driver work to drivers/firmware/arm-ffa
- use less #ifdefs in lib/efi_loader/efi_boottime.c and replace #if defined by #if CONFIG_IS_ENABLED
- improving error handling by mapping the FF-A errors to standard errors and logs
- replacing panics with an error log and returning an error code
- improving features discovery in FFA_FEATURES by introducing rxtx_min_pages private data field
- add ffa_remove and ffa_unbind functions
- improve how the driver behaves when bus discovery is done more than once
v3:
- align the interfaces of the u-boot FF-A driver with those in the linux FF-A driver
- remove the FF-A helper layer
- make the u-boot FF-A driver independent from EFI
- provide an optional config that enables copying the driver data to EFI runtime section at ExitBootServices service
- use 64-bit version of FFA_RXTX_MAP, FFA_MSG_SEND_DIRECT_{REQ, RESP}
v2:
- make FF-A bus discoverable using device_{bind, probe} APIs
- remove device tree support
v1:
- introduce FF-A bus driver with device tree support
MAINTAINERS | 7 + doc/arch/arm64.ffa.rst | 207 ++++ doc/arch/index.rst | 1 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/firmware/arm-ffa/Kconfig | 30 + drivers/firmware/arm-ffa/Makefile | 6 + drivers/firmware/arm-ffa/arm-ffa-uclass.c | 16 + drivers/firmware/arm-ffa/arm_ffa_prv.h | 196 +++ drivers/firmware/arm-ffa/core.c | 1337 +++++++++++++++++++++ include/arm_ffa.h | 93 ++ include/dm/uclass-id.h | 4 + 12 files changed, 1900 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_prv.h create mode 100644 drivers/firmware/arm-ffa/core.c create mode 100644 include/arm_ffa.h
diff --git a/MAINTAINERS b/MAINTAINERS index a26b36c7c2..496f47a516 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -248,6 +248,13 @@ 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 abdellatif.elkhlifi@arm.com +S: Maintained +F: doc/arch/arm64.ffa.rst +F: drivers/firmware/arm-ffa/ +F: include/arm_ffa.h
ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de M: Fabio Estevam festevam@gmail.com diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst new file mode 100644 index 0000000000..e98d2cf2b3 --- /dev/null +++ b/doc/arch/arm64.ffa.rst @@ -0,0 +1,207 @@ +.. SPDX-License-Identifier: GPL-2.0+
+Arm FF-A Driver +===============
+Summary +-------
+FF-A stands for Firmware Framework for Arm A-profile processors.
+FF-A specifies interfaces that enable a pair of software sandboxes to +communicate with each other. A sandbox aka partition could +be a VM in the Normal or Secure world, an application in S-EL0, or a +Trusted OS in S-EL1.
+This FF-A driver implements the interfaces to communicate with partitions in +the Secure world aka Secure partitions (SPs).
+The driver 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.
+FF-A driver 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
+FF-A and SMC specifications +-------------------------------------------
+The current implementation of the driver relies on FF-A specification v1.0 +and uses SMC32 calling convention.
+At this stage we only need the FF-A v1.0 features.
+The driver has been tested with Optee OS which supports SMC32 for most of
s/Optee OS/OP-TEE/
+the SMC ABIs.
+For more details please refer to the FF-A v1.0 spec: +https://documentation-service.arm.com/static/5fb7e8a6ca04df4095c1d65e?token=
+Hypervisors are supported if they are configured to trap SMC calls.
+The FF-A driver uses 64-bit registers as per SMCCCv1.2 specification.
+For more details please refer to the SMC Calling Convention v1.2 spec: +https://documentation-service.arm.com/static/5f8edaeff86e16515cdbe4c6?token=
+Supported hardware +--------------------------------
+Aarch64 plaforms
+Configuration +----------------------
+CONFIG_ARM_FFA_TRANSPORT
- Enables the FF-A bus driver. Turn this on if you want to use FF-A
- communication.
+CONFIG_SANDBOX_FFA
- Enables FF-A Sandbox driver. This emulates the FF-A ABIs handling under
- Sandbox and provides functional tests for FF-A.
+FF-A ABIs under the hood +---------------------------------------
+Invoking an FF-A ABI involves providing to the secure world/hypervisor the +expected arguments from the ABI.
+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 FF-A driver (non-secure world).
+The driver reads the response and processes it accordingly.
+This methodology applies to all the FF-A ABIs in the driver.
+FF-A bus discovery in U-boot
U-Boot, please search and replace.
+-------------------------------------------
+When CONFIG_ARM_FFA_TRANSPORT is enabled, the FF-A bus is discovered on +demand by the clients (users).
+Clients can discover the FF-A bus using ffa_bus_discover() API which triggers the +discovery process.
+ffa_bus_discover() creates, binds and probes the arm_ffa device using +device_{bind, probe} APIs.
+The discovery process consists in communicating with secure world (or hypervisor) +and querying specific data.
+The discovery process takes place during the arm_ffa device probing which is +handled by ffa_probe().
+The FF-A bus discovery is successful and the bus is ready for use when these +operations succeed:
+- querying the FF-A framework version +- querying from secure world the U-boot endpoint ID +- querying from secure world the RX/TX mapping features +- mapping the RX/TX buffers +- querying from secure world all the partitions information
+Discovery failure results in a probing failure and the arm_ffa device is +destroyed.
+Requirements for clients +-------------------------------------
+When using the FF-A bus with EFI, clients must:
+- Query SPs in EFI boot time mode using the service UUID. +- Unmap RX/TX buffers before EFI runtime mode starts.
+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 by the user before EFI runtime mode +starts. The driver provides a bus operation for that: rxtx_unmap()
+If RX/TX buffers created by U-boot are not unmapped and by +consequence becoming available at EFI runtime, secure world will get confused +about RX/TX buffers ownership (U-boot vs kernel).
+The bus driver layer +------------------------------
+The driver comes on top of the SMCCC layer and is implemented in +drivers/firmware/arm-ffa/core.c
+The driver provides the following features:
+- 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
+- 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, the driver takes care of how to transport
- that to the secure world/hypervisor using FF-A
+- The driver provides callbacks to be used by clients to access the FF-A bus:
+partition_info_get +sync_send_receive +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
+Using armffa command +-----------------------------------
+armffa is a command showcasing how to use the FF-A driver and how to invoke +its operations.
+This provides a guidance to the client developers on how to call the FF-A bus +interfaces.
+Usage:
+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
It may be worth mentioning that this is an implementation defined command.
devlist
displays the arm_ffa device info
+Contributors +------------
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
diff --git a/doc/arch/index.rst b/doc/arch/index.rst index 792d9182c3..8d1ab0ad4e 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/Kconfig b/drivers/Kconfig index 8b6fead351..b06b1ae481 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -6,6 +6,8 @@ source "drivers/core/Kconfig"
source "drivers/adc/Kconfig"
+source "drivers/firmware/arm-ffa/Kconfig"
source "drivers/ata/Kconfig"
source "drivers/axi/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 9d9f69a3c9..bf8d7b8cfc 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -111,6 +111,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/arm-ffa/Kconfig b/drivers/firmware/arm-ffa/Kconfig new file mode 100644 index 0000000000..f1427535f9 --- /dev/null +++ b/drivers/firmware/arm-ffa/Kconfig @@ -0,0 +1,30 @@ +# 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 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.
This driver 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.
U-Boot
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.
For more details about the FF-A driver, please refer to doc/README.ffa.drv
Not doc/arch/arm64.ffa.rst?
diff --git a/drivers/firmware/arm-ffa/Makefile b/drivers/firmware/arm-ffa/Makefile new file mode 100644 index 0000000000..043a8915be --- /dev/null +++ b/drivers/firmware/arm-ffa/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2022 +# Abdellatif El Khlifi, Arm Limited, abdellatif.elkhlifi@arm.com.
+obj-y += arm-ffa-uclass.o core.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 0000000000..7d9695d289 --- /dev/null +++ b/drivers/firmware/arm-ffa/arm-ffa-uclass.c @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include <common.h> +#include <dm.h> +#include <asm/global_data.h>
+DECLARE_GLOBAL_DATA_PTR;
+UCLASS_DRIVER(ffa) = {
- .name = "ffa",
- .id = UCLASS_FFA,
+}; diff --git a/drivers/firmware/arm-ffa/arm_ffa_prv.h b/drivers/firmware/arm-ffa/arm_ffa_prv.h new file mode 100644 index 0000000000..7bc90f7f66 --- /dev/null +++ b/drivers/firmware/arm-ffa/arm_ffa_prv.h @@ -0,0 +1,196 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#ifndef __ARM_FFA_PRV_H +#define __ARM_FFA_PRV_H
+#include <arm_ffa.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/arm-smccc.h>
+/*
- This header is private. It is exclusively used by the FF-A driver
- */
+/* FF-A core driver name */ +#define FFA_DRV_NAME "arm_ffa"
+/* 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*/
+};
+/* number of the errors supported by the FF-A specification */ +#define MAX_NUMBER_FFA_ERR 9
+/* 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)
+/* The FF-A SMC function definitions */
+typedef struct arm_smccc_1_2_regs ffa_value_t; +typedef void (*invoke_ffa_fn_t)(ffa_value_t args, ffa_value_t *res);
+/*
- 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 */
+};
+/**
- 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 - structure hosting 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
- Data structure hosting 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 {
- u64 rxbuf; /* virtual address */
- u64 txbuf; /* virtual address */
- size_t rxtx_min_pages; /* minimum number of pages in each of the RX/TX buffers */
+};
+/**
- 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_partitions - descriptors for all secure partitions
- @count: The number of partitions descriptors
- @descs The partitions descriptors table
- This data structure contains the partitions descriptors table
- */
+struct ffa_partitions {
- u32 count;
- struct ffa_partition_desc *descs; /* virtual address */
+};
+/**
- struct ffa_prvdata - the driver private data structure
- @dev: The arm_ffa device under u-boot driver model
- @ffa_ops: The driver operations structure
- @fwk_version: FF-A framework version
- @id: u-boot endpoint ID
- @partitions: The partitions descriptors structure
- @pair: The RX/TX buffers pair
- @invoke_ffa_fn: The function executing the FF-A function
- The driver data structure hosting all resident data.
- */
+struct ffa_prvdata {
- struct udevice *dev;
- struct ffa_bus_ops ffa_ops;
- u32 fwk_version;
- u16 id;
- struct ffa_partitions partitions;
- struct ffa_rxtxpair pair;
- invoke_ffa_fn_t invoke_ffa_fn;
+};
+/**
- ffa_device_get - create, bind and probe the arm_ffa device
- */
+int ffa_device_get(void);
+/**
- ffa_bus_prvdata_get - bus driver private data getter
- */
+struct ffa_prvdata **ffa_bus_prvdata_get(void);
+#endif diff --git a/drivers/firmware/arm-ffa/core.c b/drivers/firmware/arm-ffa/core.c new file mode 100644 index 0000000000..324367d12b --- /dev/null +++ b/drivers/firmware/arm-ffa/core.c @@ -0,0 +1,1337 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include "arm_ffa_prv.h" +#include <asm/global_data.h> +#include <common.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <dm/devres.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> +#include <log.h> +#include <malloc.h> +#include <string.h> +#include <uuid.h>
+DECLARE_GLOBAL_DATA_PTR;
+/**
- The device private data structure containing all the
- data read from secure world
- */
+struct ffa_prvdata *ffa_priv_data;
+/* Error mapping declarations */
+int ffa_to_std_errmap[MAX_NUMBER_FFA_ERR] = {
- 0,
- -EOPNOTSUPP, /* NOT_SUPPORTED */
- -EINVAL, /* INVALID_PARAMETERS */
- -ENOMEM, /* NO_MEMORY */
- -EBUSY, /* BUSY */
- -EINTR, /* INTERRUPTED */
- -EACCES, /* DENIED */
- -EAGAIN, /* RETRY */
- -ECANCELED, /* ABORTED */
+};
+struct ffa_abi_errmap err_msg_map[FFA_ERRMAP_COUNT] = {
- [FFA_ID_TO_ERRMAP_ID(FFA_VERSION)] = {
{
"",
Shouldn't all these empty strings be NULL instead? Given that's that what you check for in ffa_print_error_log() below.
You could save a few lines by using designated initializers for only the used elements in err_str for each FF-A function ID.
"NOT_SUPPORTED: A Firmware Framework implementation does not exist",
"", /* INVALID_PARAMETERS */
"", /* NO_MEMORY */
"", /* BUSY */
"", /* INTERRUPTED */
"", /* DENIED */
"", /* RETRY */
"", /* ABORTED */
},
- },
- [FFA_ID_TO_ERRMAP_ID(FFA_ID_GET)] = {
{
"",
"NOT_SUPPORTED: This function is not implemented at this FF-A instance",
"", /* INVALID_PARAMETERS */
"", /* NO_MEMORY */
"", /* BUSY */
"", /* INTERRUPTED */
"", /* DENIED */
"", /* RETRY */
"", /* ABORTED */
},
- },
- [FFA_ID_TO_ERRMAP_ID(FFA_FEATURES)] = {
{
"",
"NOT_SUPPORTED: FFA_RXTX_MAP is not implemented at this FF-A instance",
"", /* INVALID_PARAMETERS */
"", /* NO_MEMORY */
"", /* BUSY */
"", /* INTERRUPTED */
"", /* DENIED */
"", /* RETRY */
"", /* ABORTED */
},
- },
- [FFA_ID_TO_ERRMAP_ID(FFA_PARTITION_INFO_GET)] = {
{
"",
"NOT_SUPPORTED: This function is not implemented at this FF-A instance",
"INVALID_PARAMETERS: Unrecognized UUID",
"NO_MEMORY: Results cannot fit in RX buffer of the caller",
"BUSY: RX buffer of the caller is not free",
"", /* INTERRUPTED */
"DENIED: Callee is not in a state to handle this request",
"", /* RETRY */
"", /* ABORTED */
},
- },
- [FFA_ID_TO_ERRMAP_ID(FFA_RXTX_UNMAP)] = {
{
"",
"NOT_SUPPORTED: FFA_RXTX_UNMAP is not implemented at this FF-A instance",
"INVALID_PARAMETERS: No buffer pair registered on behalf of the caller",
"", /* NO_MEMORY */
"", /* BUSY */
"", /* INTERRUPTED */
"", /* DENIED */
"", /* RETRY */
"", /* ABORTED */
},
- },
- [FFA_ID_TO_ERRMAP_ID(FFA_RX_RELEASE)] = {
{
"",
"NOT_SUPPORTED: FFA_RX_RELEASE is not implemented at this FF-A instance",
"", /* INVALID_PARAMETERS */
"", /* NO_MEMORY */
"", /* BUSY */
"", /* INTERRUPTED */
"DENIED: Caller did not have ownership of the RX buffer",
"", /* RETRY */
"", /* ABORTED */
},
- },
- [FFA_ID_TO_ERRMAP_ID(FFA_RXTX_MAP)] = {
{
"",
"NOT_SUPPORTED: This function is not implemented at this FF-A instance",
"INVALID_PARAMETERS: Field(s) in input parameters incorrectly encoded",
"NO_MEMORY: Not enough memory",
"", /* BUSY */
"", /* INTERRUPTED */
"DENIED: Buffer pair already registered",
"", /* RETRY */
"", /* ABORTED */
},
- },
+};
+/**
- ffa_to_std_errno - convert FF-A error code to standard error code
- @ffa_errno: Error code returned by the FF-A ABI
- This function maps 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
- */
+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
- This function maps 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
- */
+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_msg_map[abi_idx].err_str[err_idx])
err_str can't be NULL since it's an array in the struct ffa_abi_errmap.
return -EINVAL;
- ffa_err("%s", err_msg_map[abi_idx].err_str[err_idx]);
- return 0;
+}
+/*
- Driver core functions
- */
+/**
- ffa_remove_device - removes the arm_ffa device
- @dev: the device to be removed
- This function makes sure the arm_ffa device is removed
- No need to free the kmalloced data when the device is destroyed.
- It's automatically done by devm management by
- device_remove() -> device_free() -> devres_release_probe().
- Return:
- 0 on success. Otherwise, failure
- */
+int ffa_remove_device(struct udevice *dev) +{
- int ret;
- if (!dev) {
ffa_err("no udevice found");
return -ENODEV;
- }
- ret = device_remove(dev, DM_REMOVE_NORMAL);
- if (ret) {
ffa_err("unable to remove. err:%d\n", ret);
return ret;
- }
- ffa_info("device removed and freed");
- ret = device_unbind(dev);
- if (ret) {
ffa_err("unable to unbind. err:%d\n", ret);
return ret;
- }
- ffa_info("device unbound");
- return 0;
+}
+/**
- ffa_device_get - create, bind and probe the arm_ffa device
- This function makes sure the arm_ffa device is
- created, bound to this driver, probed and ready to use.
- Arm FF-A transport is implemented through a single u-boot
- device managing the FF-A bus (arm_ffa).
- Return:
- 0 on success. Otherwise, failure
- */
+int ffa_device_get(void) +{
- int ret;
- struct udevice *dev = NULL;
- ret = device_bind(dm_root(),
DM_DRIVER_GET(arm_ffa),
FFA_DRV_NAME,
NULL,
ofnode_null(),
&dev);
There's no need for separate lines for each argument, please fold up a bit.
- if (ret)
return ret;
- /* The FF-A bus discovery succeeds when probing is successful */
- ret = device_probe(dev);
- if (ret) {
ffa_err("arm_ffa device probing failed");
ffa_remove_device(dev);
return ret;
- }
- return 0;
+}
+/**
- ffa_get_version - FFA_VERSION handler function
- This function implements FFA_VERSION FF-A function
- to get from the secure world the FF-A framework version
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_get_version(void) +{
- u16 major, minor;
- ffa_value_t res = {0};
- int ffa_errno;
- ffa_priv_data->invoke_ffa_fn((ffa_value_t){
.a0 = FFA_SMC_32(FFA_VERSION),
.a1 = FFA_VERSION_1_0
It's generally preferred to have comma after the last initializer too.
It could also be worth comparing with the style (alignment and folding up where possible) of the corresponding driver in the Linux kernel which I find a bit easier to read. However, checkpatch seems happy enough so I guess it's not a big deal.
I asked earlier why you're not supporting the latest version, 1.1, but you never answered that.
}, &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);
- ffa_info("FF-A driver %d.%d\nFF-A framework %d.%d",
FFA_MAJOR_VERSION, FFA_MINOR_VERSION, major, minor);
- if ((major == FFA_MAJOR_VERSION && minor >= FFA_MINOR_VERSION)) {
ffa_info("Versions are compatible ");
ffa_priv_data->fwk_version = res.a0;
return 0;
- }
- ffa_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
- This function implements 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(void) +{
- ffa_value_t res = {0};
- int ffa_errno;
- ffa_priv_data->invoke_ffa_fn((ffa_value_t){
.a0 = FFA_SMC_32(FFA_ID_GET)
}, &res);
- if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) {
ffa_priv_data->id = GET_SELF_ENDPOINT_ID((u32)res.a2);
ffa_info("endpoint ID is %u", ffa_priv_data->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 - sets the minimum number of pages in each of the RX/TX buffers
- @prop_field: properties field obtained from FFA_FEATURES ABI
- This function sets the minimum number of pages
- in each of the RX/TX buffers in the private data structure
- Return:
- buf_4k_pages points to the returned number of pages
- 0 on success. Otherwise, failure
- */
+static int ffa_set_rxtx_buffers_pages_cnt(u32 prop_field) +{
- if (!ffa_priv_data)
return -EINVAL;
- switch (prop_field) {
- case RXTX_4K:
ffa_priv_data->pair.rxtx_min_pages = 1;
break;
- case RXTX_16K:
ffa_priv_data->pair.rxtx_min_pages = 4;
break;
- case RXTX_64K:
ffa_priv_data->pair.rxtx_min_pages = 16;
break;
- default:
ffa_err("RX/TX buffer size not supported");
return -EINVAL;
- }
- return 0;
+}
+/**
- ffa_get_rxtx_map_features - FFA_FEATURES handler function with FFA_RXTX_MAP argument
- This function implements 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(void) +{
- ffa_value_t res = {0};
- int ffa_errno;
- ffa_priv_data->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((u32)res.a2);
The cast isn't needed.
- ffa_errno = res.a2;
- ffa_print_error_log(FFA_FEATURES, ffa_errno);
- return ffa_to_std_errno(ffa_errno);
+}
+/**
- ffa_free_rxtx_buffers - frees the RX/TX buffers
- This function frees the RX/TX buffers
- */
+static void ffa_free_rxtx_buffers(void) +{
- ffa_info("Freeing RX/TX buffers");
- if (ffa_priv_data->pair.rxbuf) {
free((void *)ffa_priv_data->pair.rxbuf);
ffa_priv_data->pair.rxbuf = 0;
- }
- if (ffa_priv_data->pair.txbuf) {
free((void *)ffa_priv_data->pair.txbuf);
ffa_priv_data->pair.txbuf = 0;
- }
+}
+/**
- ffa_alloc_rxtx_buffers - allocates the RX/TX buffers
- This function is 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.
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_alloc_rxtx_buffers(void) +{
- u64 bytes;
- ffa_info("Using %lu 4KB page(s) for RX/TX buffers size",
ffa_priv_data->pair.rxtx_min_pages);
- bytes = ffa_priv_data->pair.rxtx_min_pages * SZ_4K;
- /*
* The alignment of the RX and TX buffers must be equal
* to the larger translation granule size
*/
- ffa_priv_data->pair.rxbuf = (u64)memalign(bytes, bytes);
- if (!ffa_priv_data->pair.rxbuf) {
ffa_err("failure to allocate RX buffer");
return -ENOBUFS;
- }
- ffa_info("RX buffer at virtual address 0x%llx", ffa_priv_data->pair.rxbuf);
- ffa_priv_data->pair.txbuf = (u64)memalign(bytes, bytes);
- if (!ffa_priv_data->pair.txbuf) {
free((void *)ffa_priv_data->pair.rxbuf);
ffa_priv_data->pair.rxbuf = 0;
ffa_err("failure to allocate the TX buffer");
return -ENOBUFS;
- }
- ffa_info("TX buffer at virtual address 0x%llx", ffa_priv_data->pair.txbuf);
- /*
* make sure the buffers are cleared before use
*/
- memset((void *)ffa_priv_data->pair.rxbuf, 0, bytes);
- memset((void *)ffa_priv_data->pair.txbuf, 0, bytes);
- return 0;
+}
+/**
- ffa_map_rxtx_buffers - FFA_RXTX_MAP handler function
- This function implements FFA_RXTX_MAP FF-A function
- to map the RX/TX buffers
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_map_rxtx_buffers(void) +{
- int ret;
- ffa_value_t res = {0};
- int ffa_errno;
- ret = ffa_alloc_rxtx_buffers();
- 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
A space is missing after the '*'.
*/
- ffa_priv_data->invoke_ffa_fn((ffa_value_t){
.a0 = FFA_SMC_64(FFA_RXTX_MAP),
.a1 = ffa_priv_data->pair.txbuf,
.a2 = ffa_priv_data->pair.rxbuf,
.a3 = ffa_priv_data->pair.rxtx_min_pages,
}, &res);
- if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) {
ffa_info("RX/TX buffers mapped");
return 0;
- }
- ffa_errno = res.a2;
- ffa_print_error_log(FFA_RXTX_MAP, ffa_errno);
- ffa_free_rxtx_buffers();
- return ffa_to_std_errno(ffa_errno);
+}
+/**
- ffa_unmap_rxtx_buffers - FFA_RXTX_UNMAP handler function
- This function implements FFA_RXTX_UNMAP FF-A function
- to unmap the RX/TX buffers
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_unmap_rxtx_buffers(void) +{
- ffa_value_t res = {0};
- int ffa_errno;
- ffa_priv_data->invoke_ffa_fn((ffa_value_t){
.a0 = FFA_SMC_32(FFA_RXTX_UNMAP),
.a1 = PREP_SELF_ENDPOINT_ID(ffa_priv_data->id)
}, &res);
- if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) {
ffa_free_rxtx_buffers();
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 - FFA_RX_RELEASE handler function
- This function invokes 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(void) +{
- ffa_value_t res = {0};
- int ffa_errno;
- ffa_priv_data->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 - checks whether two given UUIDs are identical
- @uuid1: first UUID
- @uuid2: second UUID
- This function is used by ffa_read_partitions_info to search
- for a UUID in the partitions descriptors table
- Return:
- 1 when UUIDs match. Otherwise, 0
- */
+int ffa_uuid_are_identical(const struct ffa_partition_uuid *uuid1,
const struct ffa_partition_uuid *uuid2)
Would it make sense to use the bool type instead?
+{
- if (!uuid1 || !uuid2)
return 0;
- return !memcmp(uuid1, uuid2, sizeof(struct ffa_partition_uuid));
+}
+/**
- ffa_read_partitions_info - reads the data queried by FFA_PARTITION_INFO_GET
and saves it in the private structure
- @count: The number of partitions queried
- @part_uuid: Pointer to the partition(s) UUID
- This function reads the partitions information
- returned by the FFA_PARTITION_INFO_GET and saves it in the private
- data structure.
- Return:
- The private data structure is updated with the partition(s) information
- 0 is returned on success. Otherwise, failure
- */
+static int ffa_read_partitions_info(u32 count, struct ffa_partition_uuid *part_uuid) +{
- if (!count) {
ffa_err("no partition detected");
return -ENODATA;
- }
- ffa_info("Reading partitions data from the RX buffer");
- 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 = ffa_priv_data->pair.rxtx_min_pages * SZ_4K;
if (data_bytes > buf_bytes) {
ffa_err("partitions data size exceeds the RX buffer size:");
ffa_err(" sizes in bytes: data %llu , RX buffer %llu ",
data_bytes,
buf_bytes);
return -ENOMEM;
}
ffa_priv_data->partitions.descs = devm_kmalloc(ffa_priv_data->dev, data_bytes,
__GFP_ZERO);
if (!ffa_priv_data->partitions.descs) {
ffa_err("cannot allocate partitions data buffer");
return -ENOMEM;
}
parts_info = (struct ffa_partition_info *)ffa_priv_data->pair.rxbuf;
for (desc_idx = 0 ; desc_idx < count ; desc_idx++) {
ffa_priv_data->partitions.descs[desc_idx].info =
parts_info[desc_idx];
ffa_info("Partition ID %x : info cached",
ffa_priv_data->partitions.descs[desc_idx].info.id);
}
ffa_priv_data->partitions.count = count;
ffa_info("%d partition(s) found and cached", count);
- } else {
u32 rx_desc_idx, cached_desc_idx;
struct ffa_partition_info *parts_info;
u8 desc_found;
parts_info = (struct ffa_partition_info *)ffa_priv_data->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 < ffa_priv_data->partitions.count;
cached_desc_idx++) {
/*
* save the UUID
*/
if (ffa_priv_data->partitions.descs[cached_desc_idx].info.id ==
parts_info[rx_desc_idx].id) {
ffa_priv_data->partitions.descs[cached_desc_idx].sp_uuid =
*part_uuid;
desc_found = 1;
break;
}
}
if (!desc_found)
return -ENODATA;
}
- }
- return 0;
+}
+/**
- ffa_query_partitions_info - invokes FFA_PARTITION_INFO_GET and saves partitions data
- @part_uuid: Pointer to the partition(s) UUID
- @pcount: Pointer to the number of partitions variable filled when querying
- This function executes the FFA_PARTITION_INFO_GET
- to query the partitions data. Then, it calls ffa_read_partitions_info
- to save the data in the private data structure.
- 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 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;
- }
- ffa_priv_data->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((u32)res.a2, part_uuid);
if (ret) {
ffa_err("failed to read partition(s) data , error (%d)", ret);
ffa_release_rx_buffer();
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();
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 - FFA_PARTITION_INFO_GET handler function
- The passed arguments:
- Mode 1: When getting from the driver the number of
- secure partitions:
- @uuid_str: pointer to the UUID string
- @parts_size: pointer to the variable that contains the number of partitions
The variable will be set by the driver
- @buffer: NULL
- Mode 2: When requesting the driver to return the
- partitions information:
- @uuid_str: pointer to the UUID string
- @parts_size: pointer to the size of the SPs information buffer in bytes
- @buffer: pointer to SPs information buffer
(allocated by the client).
The buffer will be filled by the driver
- This function queries the secure partition data from
- the private data structure. If not found, it invokes 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 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().
- When invoked through a client request, ffa_get_partitions_info should be
- called twice. First call is to get from the driver the number of secure
- partitions (SPs) associated to a particular UUID.
- Then, the caller (client) allocates the buffer to host the SPs data and
- issues a 2nd call. Then, the driver fills the SPs data in the pre-allocated
- buffer.
- To achieve the mechanism described above, ffa_get_partitions_info uses the
- following functions:
ffa_read_partitions_info
ffa_query_partitions_info
- Return:
- @parts_size: When pointing to the number of partitions variable, the number is
- set by the driver.
- When pointing to the partitions information buffer size, the buffer will be
- filled by the driver.
- On success 0 is returned. Otherwise, failure
- */
+static int ffa_get_partitions_info(const char *uuid_str,
u32 *parts_size, struct ffa_partition_info *buffer)
+{
- /*
* fill_data:
* 0: return the SP count
* 1: fill SP data and return it to the caller
* -1: undefined mode
*/
- int fill_data = -1;
- u32 desc_idx, client_desc_idx;
- struct ffa_partition_uuid part_uuid = {0};
- u32 client_desc_max_cnt;
- u32 parts_found = 0;
- if (!ffa_priv_data->partitions.count || !ffa_priv_data->partitions.descs) {
ffa_err("no partition installed");
return -EINVAL;
- }
- if (!uuid_str) {
ffa_err("no UUID provided");
return -EINVAL;
- }
- if (!parts_size) {
ffa_err("no size/count provided");
return -EINVAL;
- }
- if (be_uuid_str_to_le_bin(uuid_str, (unsigned char *)&part_uuid)) {
ffa_err("invalid UUID");
return -EINVAL;
- }
- if (!buffer) {
/* Mode 1: getting the number of secure partitions */
fill_data = 0;
ffa_info("Preparing for checking partitions count");
- } else if ((*parts_size >= sizeof(struct ffa_partition_info)) &&
!(*parts_size % sizeof(struct ffa_partition_info))) {
/* Mode 2: retrieving the partitions information */
fill_data = 1;
client_desc_idx = 0;
/*
* number of empty descriptors preallocated by the caller
*/
client_desc_max_cnt = *parts_size / sizeof(struct ffa_partition_info);
ffa_info("Preparing for filling partitions info");
- } else {
ffa_err("invalid function arguments provided");
return -EINVAL;
- }
- ffa_info("Searching partitions using the provided UUID");
- /*
* search in the cached partitions
*/
- for (desc_idx = 0;
desc_idx < ffa_priv_data->partitions.count;
desc_idx++) {
if (ffa_uuid_are_identical(&ffa_priv_data->partitions.descs[desc_idx].sp_uuid,
&part_uuid)) {
ffa_info("Partition ID %x matches the provided UUID",
ffa_priv_data->partitions.descs[desc_idx].info.id);
parts_found++;
if (fill_data) {
/*
* trying to fill the partition info in the input buffer
*/
if (client_desc_idx < client_desc_max_cnt) {
buffer[client_desc_idx++] =
ffa_priv_data->partitions.descs[desc_idx].info;
continue;
}
ffa_err("failed to fill the current descriptor client buffer full");
return -ENOBUFS;
}
}
- }
- if (!parts_found) {
int ret;
ffa_info("No partition found. Querying framework ...");
ret = ffa_query_partitions_info(&part_uuid, &parts_found);
if (ret == 0) {
if (!fill_data) {
*parts_size = parts_found;
ffa_info("Number of partition(s) found matching the UUID: %d",
parts_found);
} else {
/*
* If SPs data detected, they are already in the private data
* structure, retry searching SP data again to return them
* to the caller
*/
if (parts_found)
ret = ffa_get_partitions_info(uuid_str, parts_size, buffer);
else
ret = -ENODATA;
}
}
return ret;
- }
- /* partition(s) found */
- if (!fill_data)
*parts_size = parts_found;
- return 0;
+}
+/**
- ffa_cache_partitions_info - Queries and saves all secure partitions data
- This function invokes 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 the
- resident private data structure and we keep the UUID field empty
- (in FF-A 1.0 UUID is not provided by the partition descriptor)
- This function is 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(void) +{
- return ffa_query_partitions_info(NULL, NULL);
+}
+/**
- ffa_msg_send_direct_req - FFA_MSG_SEND_DIRECT_{REQ,RESP} handler function
- @dst_part_id: destination partition ID
- @msg: pointer to the message data preallocated by the client (in/out)
- 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
- */
+static int ffa_msg_send_direct_req(u16 dst_part_id, struct ffa_send_direct_data *msg) +{
- ffa_value_t res = {0};
- int ffa_errno;
- if (!ffa_priv_data || !ffa_priv_data->invoke_ffa_fn)
return -EINVAL;
- /* No partition installed */
- if (!ffa_priv_data->partitions.count || !ffa_priv_data->partitions.descs)
return -ENODEV;
- ffa_priv_data->invoke_ffa_fn((ffa_value_t){
.a0 = FFA_SMC_64(FFA_MSG_SEND_DIRECT_REQ),
.a1 = PREP_SELF_ENDPOINT_ID(ffa_priv_data->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))
ffa_priv_data->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 == FFA_SMC_64(FFA_MSG_SEND_DIRECT_RESP)) {
/*
* 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);
+}
+/**
- __arm_ffa_fn_smc - 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
- Return: void
- */
+void __arm_ffa_fn_smc(ffa_value_t args, ffa_value_t *res) +{
- arm_smccc_1_2_smc(&args, res);
+}
+/**
- ffa_set_smc_conduit - Set the SMC conduit
- This function selects the SMC conduit by setting the driver invoke function
- to SMC assembly function
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_set_smc_conduit(void) +{
- ffa_priv_data->invoke_ffa_fn = __arm_ffa_fn_smc;
- if (!ffa_priv_data->invoke_ffa_fn) {
ffa_err("failure to set the invoke function");
return -EINVAL;
- }
- ffa_info("Conduit is SMC");
- return 0;
+}
+/**
- ffa_set_bus_ops - Set the bus driver operations
- Setting the driver callbacks.
- */
+static void ffa_set_bus_ops(void) +{
- ffa_priv_data->ffa_ops.partition_info_get = ffa_get_partitions_info;
- ffa_priv_data->ffa_ops.sync_send_receive = ffa_msg_send_direct_req;
- ffa_priv_data->ffa_ops.rxtx_unmap = ffa_unmap_rxtx_buffers;
+}
+/**
- ffa_alloc_prvdata - allocate the driver main data structure and sets the device
- @dev: the arm_ffa device
- This function creates the main data structure embedding all the driver data.
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_alloc_prvdata(struct udevice *dev) +{
- if (!dev) {
ffa_err("no udevice found");
return -ENODEV;
- }
- /* The device is registered with the DM. Let's create the driver main data structure*/
- ffa_priv_data = devm_kmalloc(dev, sizeof(struct ffa_prvdata), __GFP_ZERO);
- if (!ffa_priv_data) {
ffa_err("can not allocate the driver main data structure");
return -ENOMEM;
- }
- ffa_priv_data->dev = dev;
- return 0;
+}
+/**
- ffa_probe - The driver probe function
- @dev: the arm_ffa device
- Probing is done at boot time and triggered by the uclass device discovery.
- At probe level the following actions are done:
- setting the conduit
- querying the FF-A framework version
- 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 the resident private data structure.
- The probe will fail if either FF-A framework is not detected or the
- FF-A requests are not behaving correctly. This ensures that the
- driver is not installed and its operations are not exported to the clients.
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_probe(struct udevice *dev) +{
- int ret;
- ret = ffa_alloc_prvdata(dev);
- if (ret != 0)
return ret;
- ffa_set_bus_ops();
- ret = ffa_set_smc_conduit();
- if (ret != 0)
return ret;
- ret = ffa_get_version();
- if (ret != 0)
return ret;
- ret = ffa_get_endpoint_id();
- if (ret != 0)
return ret;
- ret = ffa_get_rxtx_map_features();
- if (ret != 0)
return ret;
- ret = ffa_map_rxtx_buffers();
- if (ret != 0)
return ret;
- ret = ffa_cache_partitions_info();
I've asked this before, but never got an answer: Why are we saving all the found partitions in a cache? It seems that FFA_PARTITION_INFO_GET could be called each time when needed instead without any noticeable overhead. Or is the result cached for some other reason?
Cheers, Jens
- if (ret != 0) {
ffa_free_rxtx_buffers();
return ret;
- }
- return 0;
+}
+/**
- ffa_remove - The driver remove function
- @dev: the arm_ffa device
- When the device is about to be removed , unmap the RX/TX buffers and free the memory
- Return:
- 0 on success.
- */
+static int ffa_remove(struct udevice *dev) +{
- ffa_info("removing the device");
- ffa_unmap_rxtx_buffers();
- if (ffa_priv_data->pair.rxbuf || ffa_priv_data->pair.txbuf)
ffa_free_rxtx_buffers();
- return 0;
+}
+/**
- ffa_unbind - The driver unbind function
- @dev: the arm_ffa device
- After the device is removed and memory freed the device is unbound
- Return:
- 0 on success.
- */
+static int ffa_unbind(struct udevice *dev) +{
- ffa_info("unbinding the device , private data already released");
- ffa_priv_data = NULL;
- return 0;
+}
+/**
- ffa_bus_ops_get - bus driver operations getter
- Return:
- This function returns a pointer to the driver operations structure
- */
+const struct ffa_bus_ops *ffa_bus_ops_get(void) +{
- return &ffa_priv_data->ffa_ops;
+}
+/**
- ffa_bus_prvdata_get - bus driver private data getter
- Return:
- This function returns a pointer to the main private data structure
- */
+struct ffa_prvdata **ffa_bus_prvdata_get(void) +{
- return &ffa_priv_data;
+}
+/**
- ffa_bus_discover - discover FF-A bus and probe the arm_ffa device
- This function makes sure the FF-A bus is discoverable.
- When probing succeeds FF-A discovery is done. The arm_ffa device is ready to use.
- When the bus was already discovered successfully the discovery will not run again.
- Arm FF-A transport is implemented through arm_ffa u-boot device managing the FF-A
- communication.
- All FF-A clients should use the arm_ffa device to use the FF-A transport.
- Return:
- 0 on success. Otherwise, failure
- */
+int ffa_bus_discover(void) +{
- int ret = 0;
- if (!ffa_priv_data)
ret = ffa_device_get();
- return ret;
+}
+/**
- Declaring the arm_ffa driver under UCLASS_FFA
- */
+U_BOOT_DRIVER(arm_ffa) = {
- .name = FFA_DRV_NAME,
- .id = UCLASS_FFA,
- .probe = ffa_probe,
- .remove = ffa_remove,
- .unbind = ffa_unbind,
+}; diff --git a/include/arm_ffa.h b/include/arm_ffa.h new file mode 100644 index 0000000000..d146e7b328 --- /dev/null +++ b/include/arm_ffa.h @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#ifndef __ARM_FFA_H +#define __ARM_FFA_H
+#include <linux/printk.h>
+/*
- This header is public. It can be used by clients to access
- data structures and definitions they need
- */
+/*
- Macros for displaying logs
- */
+#define ffa_info(fmt, ...) pr_info("[FFA] " fmt "\n", ##__VA_ARGS__) +#define ffa_err(fmt, ...) pr_err("[FFA] " fmt "\n", ##__VA_ARGS__)
+/*
- 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 __packed 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_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 __packed ffa_send_direct_data {
- unsigned long data0; /* w3/x3 */
- unsigned long data1; /* w4/x4 */
- unsigned long data2; /* w5/x5 */
- unsigned long data3; /* w6/x6 */
- unsigned long data4; /* w7/x7 */
+};
+/**
- struct ffa_bus_ops - The driver operations structure
- @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)(const char *uuid_str,
u32 *parts_size, struct ffa_partition_info *buffer);
- int (*sync_send_receive)(u16 dst_part_id, struct ffa_send_direct_data *msg);
- int (*rxtx_unmap)(void);
+};
+/**
- The device driver and the Uclass driver public functions
- */
+/**
- ffa_bus_ops_get - driver operations getter
- */
+const struct ffa_bus_ops *ffa_bus_ops_get(void);
+/**
- ffa_bus_discover - discover FF-A bus and probes the arm_ffa device
- */
+int ffa_bus_discover(void);
+#endif diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index a432e43871..5dd698b7a9 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -4,6 +4,9 @@
- (C) Copyright 2012
- Pavel Herrmann morpheus.ibis@gmail.com
- (C) Copyright 2022 ARM Limited
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#ifndef _DM_UCLASS_ID_H @@ -55,6 +58,7 @@ enum uclass_id { UCLASS_EFI_MEDIA, /* Devices provided by UEFI firmware */ UCLASS_ETH, /* Ethernet device */ UCLASS_ETH_PHY, /* Ethernet PHY device */
- UCLASS_FFA, /* Arm Firmware Framework for Armv8-A */ UCLASS_FIRMWARE, /* Firmware */ UCLASS_FUZZING_ENGINE, /* Fuzzing engine */ UCLASS_FS_FIRMWARE_LOADER, /* Generic loader */
-- 2.17.1

On Tue, Oct 25, 2022 at 11:31:11AM +0200, Jens Wiklander wrote:
On Thu, Oct 13, 2022 at 11:38:50AM +0100, Abdellatif El Khlifi wrote:
Add the core driver 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.
32-bit version of the ABIs is supported and 64-bit version of FFA_RXTX_MAP and FFA_MSG_SEND_DIRECT_{REQ, RESP}.
In u-boot FF-A design, FF-A is considered as a discoverable bus.
U-Boot
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:
- partition_info_get
- sync_send_receive
- rxtx_unmap
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v6:
- drop use of EFI runtime support (We decided with Linaro to add this later)
- drop discovery from initcalls (discovery will be on demand by FF-A users)
- set the alignment of the RX/TX buffers to the larger translation granule size
- move FF-A RX/TX buffers unmapping at ExitBootServices() to a separate commit
- update the documentation and move it to doc/arch/arm64.ffa.rst
v4:
- add doc/README.ffa.drv
- moving the FF-A driver work to drivers/firmware/arm-ffa
- use less #ifdefs in lib/efi_loader/efi_boottime.c and replace #if defined by #if CONFIG_IS_ENABLED
- improving error handling by mapping the FF-A errors to standard errors and logs
- replacing panics with an error log and returning an error code
- improving features discovery in FFA_FEATURES by introducing rxtx_min_pages private data field
- add ffa_remove and ffa_unbind functions
- improve how the driver behaves when bus discovery is done more than once
v3:
- align the interfaces of the u-boot FF-A driver with those in the linux FF-A driver
- remove the FF-A helper layer
- make the u-boot FF-A driver independent from EFI
- provide an optional config that enables copying the driver data to EFI runtime section at ExitBootServices service
- use 64-bit version of FFA_RXTX_MAP, FFA_MSG_SEND_DIRECT_{REQ, RESP}
v2:
- make FF-A bus discoverable using device_{bind, probe} APIs
- remove device tree support
v1:
- introduce FF-A bus driver with device tree support
MAINTAINERS | 7 + doc/arch/arm64.ffa.rst | 207 ++++ doc/arch/index.rst | 1 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/firmware/arm-ffa/Kconfig | 30 + drivers/firmware/arm-ffa/Makefile | 6 + drivers/firmware/arm-ffa/arm-ffa-uclass.c | 16 + drivers/firmware/arm-ffa/arm_ffa_prv.h | 196 +++ drivers/firmware/arm-ffa/core.c | 1337 +++++++++++++++++++++ include/arm_ffa.h | 93 ++ include/dm/uclass-id.h | 4 + 12 files changed, 1900 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_prv.h create mode 100644 drivers/firmware/arm-ffa/core.c create mode 100644 include/arm_ffa.h
diff --git a/MAINTAINERS b/MAINTAINERS index a26b36c7c2..496f47a516 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -248,6 +248,13 @@ 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 abdellatif.elkhlifi@arm.com +S: Maintained +F: doc/arch/arm64.ffa.rst +F: drivers/firmware/arm-ffa/ +F: include/arm_ffa.h
ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de M: Fabio Estevam festevam@gmail.com diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst new file mode 100644 index 0000000000..e98d2cf2b3 --- /dev/null +++ b/doc/arch/arm64.ffa.rst @@ -0,0 +1,207 @@ +.. SPDX-License-Identifier: GPL-2.0+
+Arm FF-A Driver +===============
+Summary +-------
+FF-A stands for Firmware Framework for Arm A-profile processors.
+FF-A specifies interfaces that enable a pair of software sandboxes to +communicate with each other. A sandbox aka partition could +be a VM in the Normal or Secure world, an application in S-EL0, or a +Trusted OS in S-EL1.
+This FF-A driver implements the interfaces to communicate with partitions in +the Secure world aka Secure partitions (SPs).
+The driver 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.
+FF-A driver 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
+FF-A and SMC specifications +-------------------------------------------
+The current implementation of the driver relies on FF-A specification v1.0 +and uses SMC32 calling convention.
+At this stage we only need the FF-A v1.0 features.
+The driver has been tested with Optee OS which supports SMC32 for most of
s/Optee OS/OP-TEE/
+the SMC ABIs.
+For more details please refer to the FF-A v1.0 spec: +https://documentation-service.arm.com/static/5fb7e8a6ca04df4095c1d65e?token=
+Hypervisors are supported if they are configured to trap SMC calls.
+The FF-A driver uses 64-bit registers as per SMCCCv1.2 specification.
+For more details please refer to the SMC Calling Convention v1.2 spec: +https://documentation-service.arm.com/static/5f8edaeff86e16515cdbe4c6?token=
+Supported hardware +--------------------------------
+Aarch64 plaforms
+Configuration +----------------------
+CONFIG_ARM_FFA_TRANSPORT
- Enables the FF-A bus driver. Turn this on if you want to use FF-A
- communication.
+CONFIG_SANDBOX_FFA
- Enables FF-A Sandbox driver. This emulates the FF-A ABIs handling under
- Sandbox and provides functional tests for FF-A.
+FF-A ABIs under the hood +---------------------------------------
+Invoking an FF-A ABI involves providing to the secure world/hypervisor the +expected arguments from the ABI.
+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 FF-A driver (non-secure world).
+The driver reads the response and processes it accordingly.
+This methodology applies to all the FF-A ABIs in the driver.
+FF-A bus discovery in U-boot
U-Boot, please search and replace.
+-------------------------------------------
+When CONFIG_ARM_FFA_TRANSPORT is enabled, the FF-A bus is discovered on +demand by the clients (users).
+Clients can discover the FF-A bus using ffa_bus_discover() API which triggers the +discovery process.
+ffa_bus_discover() creates, binds and probes the arm_ffa device using +device_{bind, probe} APIs.
+The discovery process consists in communicating with secure world (or hypervisor) +and querying specific data.
+The discovery process takes place during the arm_ffa device probing which is +handled by ffa_probe().
+The FF-A bus discovery is successful and the bus is ready for use when these +operations succeed:
+- querying the FF-A framework version +- querying from secure world the U-boot endpoint ID +- querying from secure world the RX/TX mapping features +- mapping the RX/TX buffers +- querying from secure world all the partitions information
+Discovery failure results in a probing failure and the arm_ffa device is +destroyed.
+Requirements for clients +-------------------------------------
+When using the FF-A bus with EFI, clients must:
+- Query SPs in EFI boot time mode using the service UUID. +- Unmap RX/TX buffers before EFI runtime mode starts.
+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 by the user before EFI runtime mode +starts. The driver provides a bus operation for that: rxtx_unmap()
+If RX/TX buffers created by U-boot are not unmapped and by +consequence becoming available at EFI runtime, secure world will get confused +about RX/TX buffers ownership (U-boot vs kernel).
+The bus driver layer +------------------------------
+The driver comes on top of the SMCCC layer and is implemented in +drivers/firmware/arm-ffa/core.c
+The driver provides the following features:
+- 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
+- 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, the driver takes care of how to transport
- that to the secure world/hypervisor using FF-A
+- The driver provides callbacks to be used by clients to access the FF-A bus:
+partition_info_get +sync_send_receive +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
+Using armffa command +-----------------------------------
+armffa is a command showcasing how to use the FF-A driver and how to invoke +its operations.
+This provides a guidance to the client developers on how to call the FF-A bus +interfaces.
+Usage:
+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
It may be worth mentioning that this is an implementation defined command.
devlist
displays the arm_ffa device info
+Contributors +------------
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
diff --git a/doc/arch/index.rst b/doc/arch/index.rst index 792d9182c3..8d1ab0ad4e 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/Kconfig b/drivers/Kconfig index 8b6fead351..b06b1ae481 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -6,6 +6,8 @@ source "drivers/core/Kconfig"
source "drivers/adc/Kconfig"
+source "drivers/firmware/arm-ffa/Kconfig"
source "drivers/ata/Kconfig"
source "drivers/axi/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 9d9f69a3c9..bf8d7b8cfc 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -111,6 +111,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/arm-ffa/Kconfig b/drivers/firmware/arm-ffa/Kconfig new file mode 100644 index 0000000000..f1427535f9 --- /dev/null +++ b/drivers/firmware/arm-ffa/Kconfig @@ -0,0 +1,30 @@ +# 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 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.
This driver 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.
U-Boot
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.
For more details about the FF-A driver, please refer to doc/README.ffa.drv
Not doc/arch/arm64.ffa.rst?
diff --git a/drivers/firmware/arm-ffa/Makefile b/drivers/firmware/arm-ffa/Makefile new file mode 100644 index 0000000000..043a8915be --- /dev/null +++ b/drivers/firmware/arm-ffa/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2022 +# Abdellatif El Khlifi, Arm Limited, abdellatif.elkhlifi@arm.com.
+obj-y += arm-ffa-uclass.o core.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 0000000000..7d9695d289 --- /dev/null +++ b/drivers/firmware/arm-ffa/arm-ffa-uclass.c @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include <common.h> +#include <dm.h> +#include <asm/global_data.h>
+DECLARE_GLOBAL_DATA_PTR;
+UCLASS_DRIVER(ffa) = {
- .name = "ffa",
- .id = UCLASS_FFA,
+}; diff --git a/drivers/firmware/arm-ffa/arm_ffa_prv.h b/drivers/firmware/arm-ffa/arm_ffa_prv.h new file mode 100644 index 0000000000..7bc90f7f66 --- /dev/null +++ b/drivers/firmware/arm-ffa/arm_ffa_prv.h @@ -0,0 +1,196 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#ifndef __ARM_FFA_PRV_H +#define __ARM_FFA_PRV_H
+#include <arm_ffa.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/arm-smccc.h>
+/*
- This header is private. It is exclusively used by the FF-A driver
- */
+/* FF-A core driver name */ +#define FFA_DRV_NAME "arm_ffa"
+/* 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*/
+};
+/* number of the errors supported by the FF-A specification */ +#define MAX_NUMBER_FFA_ERR 9
+/* 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)
+/* The FF-A SMC function definitions */
+typedef struct arm_smccc_1_2_regs ffa_value_t; +typedef void (*invoke_ffa_fn_t)(ffa_value_t args, ffa_value_t *res);
+/*
- 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 */
+};
+/**
- 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 - structure hosting 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
- Data structure hosting 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 {
- u64 rxbuf; /* virtual address */
- u64 txbuf; /* virtual address */
- size_t rxtx_min_pages; /* minimum number of pages in each of the RX/TX buffers */
+};
+/**
- 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_partitions - descriptors for all secure partitions
- @count: The number of partitions descriptors
- @descs The partitions descriptors table
- This data structure contains the partitions descriptors table
- */
+struct ffa_partitions {
- u32 count;
- struct ffa_partition_desc *descs; /* virtual address */
+};
+/**
- struct ffa_prvdata - the driver private data structure
- @dev: The arm_ffa device under u-boot driver model
- @ffa_ops: The driver operations structure
- @fwk_version: FF-A framework version
- @id: u-boot endpoint ID
- @partitions: The partitions descriptors structure
- @pair: The RX/TX buffers pair
- @invoke_ffa_fn: The function executing the FF-A function
- The driver data structure hosting all resident data.
- */
+struct ffa_prvdata {
- struct udevice *dev;
- struct ffa_bus_ops ffa_ops;
- u32 fwk_version;
- u16 id;
- struct ffa_partitions partitions;
- struct ffa_rxtxpair pair;
- invoke_ffa_fn_t invoke_ffa_fn;
+};
+/**
- ffa_device_get - create, bind and probe the arm_ffa device
- */
+int ffa_device_get(void);
+/**
- ffa_bus_prvdata_get - bus driver private data getter
- */
+struct ffa_prvdata **ffa_bus_prvdata_get(void);
+#endif diff --git a/drivers/firmware/arm-ffa/core.c b/drivers/firmware/arm-ffa/core.c new file mode 100644 index 0000000000..324367d12b --- /dev/null +++ b/drivers/firmware/arm-ffa/core.c @@ -0,0 +1,1337 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include "arm_ffa_prv.h" +#include <asm/global_data.h> +#include <common.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <dm/devres.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> +#include <log.h> +#include <malloc.h> +#include <string.h> +#include <uuid.h>
+DECLARE_GLOBAL_DATA_PTR;
+/**
- The device private data structure containing all the
- data read from secure world
- */
+struct ffa_prvdata *ffa_priv_data;
+/* Error mapping declarations */
+int ffa_to_std_errmap[MAX_NUMBER_FFA_ERR] = {
- 0,
- -EOPNOTSUPP, /* NOT_SUPPORTED */
- -EINVAL, /* INVALID_PARAMETERS */
- -ENOMEM, /* NO_MEMORY */
- -EBUSY, /* BUSY */
- -EINTR, /* INTERRUPTED */
- -EACCES, /* DENIED */
- -EAGAIN, /* RETRY */
- -ECANCELED, /* ABORTED */
+};
+struct ffa_abi_errmap err_msg_map[FFA_ERRMAP_COUNT] = {
- [FFA_ID_TO_ERRMAP_ID(FFA_VERSION)] = {
{
"",
Shouldn't all these empty strings be NULL instead? Given that's that what you check for in ffa_print_error_log() below.
You could save a few lines by using designated initializers for only the used elements in err_str for each FF-A function ID.
"NOT_SUPPORTED: A Firmware Framework implementation does not exist",
"", /* INVALID_PARAMETERS */
"", /* NO_MEMORY */
"", /* BUSY */
"", /* INTERRUPTED */
"", /* DENIED */
"", /* RETRY */
"", /* ABORTED */
},
- },
- [FFA_ID_TO_ERRMAP_ID(FFA_ID_GET)] = {
{
"",
"NOT_SUPPORTED: This function is not implemented at this FF-A instance",
"", /* INVALID_PARAMETERS */
"", /* NO_MEMORY */
"", /* BUSY */
"", /* INTERRUPTED */
"", /* DENIED */
"", /* RETRY */
"", /* ABORTED */
},
- },
- [FFA_ID_TO_ERRMAP_ID(FFA_FEATURES)] = {
{
"",
"NOT_SUPPORTED: FFA_RXTX_MAP is not implemented at this FF-A instance",
"", /* INVALID_PARAMETERS */
"", /* NO_MEMORY */
"", /* BUSY */
"", /* INTERRUPTED */
"", /* DENIED */
"", /* RETRY */
"", /* ABORTED */
},
- },
- [FFA_ID_TO_ERRMAP_ID(FFA_PARTITION_INFO_GET)] = {
{
"",
"NOT_SUPPORTED: This function is not implemented at this FF-A instance",
"INVALID_PARAMETERS: Unrecognized UUID",
"NO_MEMORY: Results cannot fit in RX buffer of the caller",
"BUSY: RX buffer of the caller is not free",
"", /* INTERRUPTED */
"DENIED: Callee is not in a state to handle this request",
"", /* RETRY */
"", /* ABORTED */
},
- },
- [FFA_ID_TO_ERRMAP_ID(FFA_RXTX_UNMAP)] = {
{
"",
"NOT_SUPPORTED: FFA_RXTX_UNMAP is not implemented at this FF-A instance",
"INVALID_PARAMETERS: No buffer pair registered on behalf of the caller",
"", /* NO_MEMORY */
"", /* BUSY */
"", /* INTERRUPTED */
"", /* DENIED */
"", /* RETRY */
"", /* ABORTED */
},
- },
- [FFA_ID_TO_ERRMAP_ID(FFA_RX_RELEASE)] = {
{
"",
"NOT_SUPPORTED: FFA_RX_RELEASE is not implemented at this FF-A instance",
"", /* INVALID_PARAMETERS */
"", /* NO_MEMORY */
"", /* BUSY */
"", /* INTERRUPTED */
"DENIED: Caller did not have ownership of the RX buffer",
"", /* RETRY */
"", /* ABORTED */
},
- },
- [FFA_ID_TO_ERRMAP_ID(FFA_RXTX_MAP)] = {
{
"",
"NOT_SUPPORTED: This function is not implemented at this FF-A instance",
"INVALID_PARAMETERS: Field(s) in input parameters incorrectly encoded",
"NO_MEMORY: Not enough memory",
"", /* BUSY */
"", /* INTERRUPTED */
"DENIED: Buffer pair already registered",
"", /* RETRY */
"", /* ABORTED */
},
- },
+};
+/**
- ffa_to_std_errno - convert FF-A error code to standard error code
- @ffa_errno: Error code returned by the FF-A ABI
- This function maps 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
- */
+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
- This function maps 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
- */
+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_msg_map[abi_idx].err_str[err_idx])
err_str can't be NULL since it's an array in the struct ffa_abi_errmap.
return -EINVAL;
- ffa_err("%s", err_msg_map[abi_idx].err_str[err_idx]);
- return 0;
+}
+/*
- Driver core functions
- */
+/**
- ffa_remove_device - removes the arm_ffa device
- @dev: the device to be removed
- This function makes sure the arm_ffa device is removed
- No need to free the kmalloced data when the device is destroyed.
- It's automatically done by devm management by
- device_remove() -> device_free() -> devres_release_probe().
- Return:
- 0 on success. Otherwise, failure
- */
+int ffa_remove_device(struct udevice *dev) +{
- int ret;
- if (!dev) {
ffa_err("no udevice found");
return -ENODEV;
- }
- ret = device_remove(dev, DM_REMOVE_NORMAL);
- if (ret) {
ffa_err("unable to remove. err:%d\n", ret);
return ret;
- }
- ffa_info("device removed and freed");
- ret = device_unbind(dev);
- if (ret) {
ffa_err("unable to unbind. err:%d\n", ret);
return ret;
- }
- ffa_info("device unbound");
- return 0;
+}
+/**
- ffa_device_get - create, bind and probe the arm_ffa device
- This function makes sure the arm_ffa device is
- created, bound to this driver, probed and ready to use.
- Arm FF-A transport is implemented through a single u-boot
- device managing the FF-A bus (arm_ffa).
- Return:
- 0 on success. Otherwise, failure
- */
+int ffa_device_get(void) +{
- int ret;
- struct udevice *dev = NULL;
- ret = device_bind(dm_root(),
DM_DRIVER_GET(arm_ffa),
FFA_DRV_NAME,
NULL,
ofnode_null(),
&dev);
There's no need for separate lines for each argument, please fold up a bit.
- if (ret)
return ret;
- /* The FF-A bus discovery succeeds when probing is successful */
- ret = device_probe(dev);
- if (ret) {
ffa_err("arm_ffa device probing failed");
ffa_remove_device(dev);
return ret;
- }
- return 0;
+}
+/**
- ffa_get_version - FFA_VERSION handler function
- This function implements FFA_VERSION FF-A function
- to get from the secure world the FF-A framework version
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_get_version(void) +{
- u16 major, minor;
- ffa_value_t res = {0};
- int ffa_errno;
- ffa_priv_data->invoke_ffa_fn((ffa_value_t){
.a0 = FFA_SMC_32(FFA_VERSION),
.a1 = FFA_VERSION_1_0
It's generally preferred to have comma after the last initializer too.
It could also be worth comparing with the style (alignment and folding up where possible) of the corresponding driver in the Linux kernel which I find a bit easier to read. However, checkpatch seems happy enough so I guess it's not a big deal.
I asked earlier why you're not supporting the latest version, 1.1, but you never answered that.
Thanks for all the new comments. I'll address these in v7.
Regarding you older comments, I already replied here [1]. Please have a look.
[1]: https://lore.kernel.org/all/20221014102849.GA14576@e121910.cambridge.arm.com...
}, &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);
- ffa_info("FF-A driver %d.%d\nFF-A framework %d.%d",
FFA_MAJOR_VERSION, FFA_MINOR_VERSION, major, minor);
- if ((major == FFA_MAJOR_VERSION && minor >= FFA_MINOR_VERSION)) {
ffa_info("Versions are compatible ");
ffa_priv_data->fwk_version = res.a0;
return 0;
- }
- ffa_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
- This function implements 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(void) +{
- ffa_value_t res = {0};
- int ffa_errno;
- ffa_priv_data->invoke_ffa_fn((ffa_value_t){
.a0 = FFA_SMC_32(FFA_ID_GET)
}, &res);
- if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) {
ffa_priv_data->id = GET_SELF_ENDPOINT_ID((u32)res.a2);
ffa_info("endpoint ID is %u", ffa_priv_data->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 - sets the minimum number of pages in each of the RX/TX buffers
- @prop_field: properties field obtained from FFA_FEATURES ABI
- This function sets the minimum number of pages
- in each of the RX/TX buffers in the private data structure
- Return:
- buf_4k_pages points to the returned number of pages
- 0 on success. Otherwise, failure
- */
+static int ffa_set_rxtx_buffers_pages_cnt(u32 prop_field) +{
- if (!ffa_priv_data)
return -EINVAL;
- switch (prop_field) {
- case RXTX_4K:
ffa_priv_data->pair.rxtx_min_pages = 1;
break;
- case RXTX_16K:
ffa_priv_data->pair.rxtx_min_pages = 4;
break;
- case RXTX_64K:
ffa_priv_data->pair.rxtx_min_pages = 16;
break;
- default:
ffa_err("RX/TX buffer size not supported");
return -EINVAL;
- }
- return 0;
+}
+/**
- ffa_get_rxtx_map_features - FFA_FEATURES handler function with FFA_RXTX_MAP argument
- This function implements 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(void) +{
- ffa_value_t res = {0};
- int ffa_errno;
- ffa_priv_data->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((u32)res.a2);
The cast isn't needed.
- ffa_errno = res.a2;
- ffa_print_error_log(FFA_FEATURES, ffa_errno);
- return ffa_to_std_errno(ffa_errno);
+}
+/**
- ffa_free_rxtx_buffers - frees the RX/TX buffers
- This function frees the RX/TX buffers
- */
+static void ffa_free_rxtx_buffers(void) +{
- ffa_info("Freeing RX/TX buffers");
- if (ffa_priv_data->pair.rxbuf) {
free((void *)ffa_priv_data->pair.rxbuf);
ffa_priv_data->pair.rxbuf = 0;
- }
- if (ffa_priv_data->pair.txbuf) {
free((void *)ffa_priv_data->pair.txbuf);
ffa_priv_data->pair.txbuf = 0;
- }
+}
+/**
- ffa_alloc_rxtx_buffers - allocates the RX/TX buffers
- This function is 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.
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_alloc_rxtx_buffers(void) +{
- u64 bytes;
- ffa_info("Using %lu 4KB page(s) for RX/TX buffers size",
ffa_priv_data->pair.rxtx_min_pages);
- bytes = ffa_priv_data->pair.rxtx_min_pages * SZ_4K;
- /*
* The alignment of the RX and TX buffers must be equal
* to the larger translation granule size
*/
- ffa_priv_data->pair.rxbuf = (u64)memalign(bytes, bytes);
- if (!ffa_priv_data->pair.rxbuf) {
ffa_err("failure to allocate RX buffer");
return -ENOBUFS;
- }
- ffa_info("RX buffer at virtual address 0x%llx", ffa_priv_data->pair.rxbuf);
- ffa_priv_data->pair.txbuf = (u64)memalign(bytes, bytes);
- if (!ffa_priv_data->pair.txbuf) {
free((void *)ffa_priv_data->pair.rxbuf);
ffa_priv_data->pair.rxbuf = 0;
ffa_err("failure to allocate the TX buffer");
return -ENOBUFS;
- }
- ffa_info("TX buffer at virtual address 0x%llx", ffa_priv_data->pair.txbuf);
- /*
* make sure the buffers are cleared before use
*/
- memset((void *)ffa_priv_data->pair.rxbuf, 0, bytes);
- memset((void *)ffa_priv_data->pair.txbuf, 0, bytes);
- return 0;
+}
+/**
- ffa_map_rxtx_buffers - FFA_RXTX_MAP handler function
- This function implements FFA_RXTX_MAP FF-A function
- to map the RX/TX buffers
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_map_rxtx_buffers(void) +{
- int ret;
- ffa_value_t res = {0};
- int ffa_errno;
- ret = ffa_alloc_rxtx_buffers();
- 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
A space is missing after the '*'.
*/
- ffa_priv_data->invoke_ffa_fn((ffa_value_t){
.a0 = FFA_SMC_64(FFA_RXTX_MAP),
.a1 = ffa_priv_data->pair.txbuf,
.a2 = ffa_priv_data->pair.rxbuf,
.a3 = ffa_priv_data->pair.rxtx_min_pages,
}, &res);
- if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) {
ffa_info("RX/TX buffers mapped");
return 0;
- }
- ffa_errno = res.a2;
- ffa_print_error_log(FFA_RXTX_MAP, ffa_errno);
- ffa_free_rxtx_buffers();
- return ffa_to_std_errno(ffa_errno);
+}
+/**
- ffa_unmap_rxtx_buffers - FFA_RXTX_UNMAP handler function
- This function implements FFA_RXTX_UNMAP FF-A function
- to unmap the RX/TX buffers
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_unmap_rxtx_buffers(void) +{
- ffa_value_t res = {0};
- int ffa_errno;
- ffa_priv_data->invoke_ffa_fn((ffa_value_t){
.a0 = FFA_SMC_32(FFA_RXTX_UNMAP),
.a1 = PREP_SELF_ENDPOINT_ID(ffa_priv_data->id)
}, &res);
- if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) {
ffa_free_rxtx_buffers();
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 - FFA_RX_RELEASE handler function
- This function invokes 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(void) +{
- ffa_value_t res = {0};
- int ffa_errno;
- ffa_priv_data->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 - checks whether two given UUIDs are identical
- @uuid1: first UUID
- @uuid2: second UUID
- This function is used by ffa_read_partitions_info to search
- for a UUID in the partitions descriptors table
- Return:
- 1 when UUIDs match. Otherwise, 0
- */
+int ffa_uuid_are_identical(const struct ffa_partition_uuid *uuid1,
const struct ffa_partition_uuid *uuid2)
Would it make sense to use the bool type instead?
+{
- if (!uuid1 || !uuid2)
return 0;
- return !memcmp(uuid1, uuid2, sizeof(struct ffa_partition_uuid));
+}
+/**
- ffa_read_partitions_info - reads the data queried by FFA_PARTITION_INFO_GET
and saves it in the private structure
- @count: The number of partitions queried
- @part_uuid: Pointer to the partition(s) UUID
- This function reads the partitions information
- returned by the FFA_PARTITION_INFO_GET and saves it in the private
- data structure.
- Return:
- The private data structure is updated with the partition(s) information
- 0 is returned on success. Otherwise, failure
- */
+static int ffa_read_partitions_info(u32 count, struct ffa_partition_uuid *part_uuid) +{
- if (!count) {
ffa_err("no partition detected");
return -ENODATA;
- }
- ffa_info("Reading partitions data from the RX buffer");
- 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 = ffa_priv_data->pair.rxtx_min_pages * SZ_4K;
if (data_bytes > buf_bytes) {
ffa_err("partitions data size exceeds the RX buffer size:");
ffa_err(" sizes in bytes: data %llu , RX buffer %llu ",
data_bytes,
buf_bytes);
return -ENOMEM;
}
ffa_priv_data->partitions.descs = devm_kmalloc(ffa_priv_data->dev, data_bytes,
__GFP_ZERO);
if (!ffa_priv_data->partitions.descs) {
ffa_err("cannot allocate partitions data buffer");
return -ENOMEM;
}
parts_info = (struct ffa_partition_info *)ffa_priv_data->pair.rxbuf;
for (desc_idx = 0 ; desc_idx < count ; desc_idx++) {
ffa_priv_data->partitions.descs[desc_idx].info =
parts_info[desc_idx];
ffa_info("Partition ID %x : info cached",
ffa_priv_data->partitions.descs[desc_idx].info.id);
}
ffa_priv_data->partitions.count = count;
ffa_info("%d partition(s) found and cached", count);
- } else {
u32 rx_desc_idx, cached_desc_idx;
struct ffa_partition_info *parts_info;
u8 desc_found;
parts_info = (struct ffa_partition_info *)ffa_priv_data->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 < ffa_priv_data->partitions.count;
cached_desc_idx++) {
/*
* save the UUID
*/
if (ffa_priv_data->partitions.descs[cached_desc_idx].info.id ==
parts_info[rx_desc_idx].id) {
ffa_priv_data->partitions.descs[cached_desc_idx].sp_uuid =
*part_uuid;
desc_found = 1;
break;
}
}
if (!desc_found)
return -ENODATA;
}
- }
- return 0;
+}
+/**
- ffa_query_partitions_info - invokes FFA_PARTITION_INFO_GET and saves partitions data
- @part_uuid: Pointer to the partition(s) UUID
- @pcount: Pointer to the number of partitions variable filled when querying
- This function executes the FFA_PARTITION_INFO_GET
- to query the partitions data. Then, it calls ffa_read_partitions_info
- to save the data in the private data structure.
- 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 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;
- }
- ffa_priv_data->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((u32)res.a2, part_uuid);
if (ret) {
ffa_err("failed to read partition(s) data , error (%d)", ret);
ffa_release_rx_buffer();
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();
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 - FFA_PARTITION_INFO_GET handler function
- The passed arguments:
- Mode 1: When getting from the driver the number of
- secure partitions:
- @uuid_str: pointer to the UUID string
- @parts_size: pointer to the variable that contains the number of partitions
The variable will be set by the driver
- @buffer: NULL
- Mode 2: When requesting the driver to return the
- partitions information:
- @uuid_str: pointer to the UUID string
- @parts_size: pointer to the size of the SPs information buffer in bytes
- @buffer: pointer to SPs information buffer
(allocated by the client).
The buffer will be filled by the driver
- This function queries the secure partition data from
- the private data structure. If not found, it invokes 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 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().
- When invoked through a client request, ffa_get_partitions_info should be
- called twice. First call is to get from the driver the number of secure
- partitions (SPs) associated to a particular UUID.
- Then, the caller (client) allocates the buffer to host the SPs data and
- issues a 2nd call. Then, the driver fills the SPs data in the pre-allocated
- buffer.
- To achieve the mechanism described above, ffa_get_partitions_info uses the
- following functions:
ffa_read_partitions_info
ffa_query_partitions_info
- Return:
- @parts_size: When pointing to the number of partitions variable, the number is
- set by the driver.
- When pointing to the partitions information buffer size, the buffer will be
- filled by the driver.
- On success 0 is returned. Otherwise, failure
- */
+static int ffa_get_partitions_info(const char *uuid_str,
u32 *parts_size, struct ffa_partition_info *buffer)
+{
- /*
* fill_data:
* 0: return the SP count
* 1: fill SP data and return it to the caller
* -1: undefined mode
*/
- int fill_data = -1;
- u32 desc_idx, client_desc_idx;
- struct ffa_partition_uuid part_uuid = {0};
- u32 client_desc_max_cnt;
- u32 parts_found = 0;
- if (!ffa_priv_data->partitions.count || !ffa_priv_data->partitions.descs) {
ffa_err("no partition installed");
return -EINVAL;
- }
- if (!uuid_str) {
ffa_err("no UUID provided");
return -EINVAL;
- }
- if (!parts_size) {
ffa_err("no size/count provided");
return -EINVAL;
- }
- if (be_uuid_str_to_le_bin(uuid_str, (unsigned char *)&part_uuid)) {
ffa_err("invalid UUID");
return -EINVAL;
- }
- if (!buffer) {
/* Mode 1: getting the number of secure partitions */
fill_data = 0;
ffa_info("Preparing for checking partitions count");
- } else if ((*parts_size >= sizeof(struct ffa_partition_info)) &&
!(*parts_size % sizeof(struct ffa_partition_info))) {
/* Mode 2: retrieving the partitions information */
fill_data = 1;
client_desc_idx = 0;
/*
* number of empty descriptors preallocated by the caller
*/
client_desc_max_cnt = *parts_size / sizeof(struct ffa_partition_info);
ffa_info("Preparing for filling partitions info");
- } else {
ffa_err("invalid function arguments provided");
return -EINVAL;
- }
- ffa_info("Searching partitions using the provided UUID");
- /*
* search in the cached partitions
*/
- for (desc_idx = 0;
desc_idx < ffa_priv_data->partitions.count;
desc_idx++) {
if (ffa_uuid_are_identical(&ffa_priv_data->partitions.descs[desc_idx].sp_uuid,
&part_uuid)) {
ffa_info("Partition ID %x matches the provided UUID",
ffa_priv_data->partitions.descs[desc_idx].info.id);
parts_found++;
if (fill_data) {
/*
* trying to fill the partition info in the input buffer
*/
if (client_desc_idx < client_desc_max_cnt) {
buffer[client_desc_idx++] =
ffa_priv_data->partitions.descs[desc_idx].info;
continue;
}
ffa_err("failed to fill the current descriptor client buffer full");
return -ENOBUFS;
}
}
- }
- if (!parts_found) {
int ret;
ffa_info("No partition found. Querying framework ...");
ret = ffa_query_partitions_info(&part_uuid, &parts_found);
if (ret == 0) {
if (!fill_data) {
*parts_size = parts_found;
ffa_info("Number of partition(s) found matching the UUID: %d",
parts_found);
} else {
/*
* If SPs data detected, they are already in the private data
* structure, retry searching SP data again to return them
* to the caller
*/
if (parts_found)
ret = ffa_get_partitions_info(uuid_str, parts_size, buffer);
else
ret = -ENODATA;
}
}
return ret;
- }
- /* partition(s) found */
- if (!fill_data)
*parts_size = parts_found;
- return 0;
+}
+/**
- ffa_cache_partitions_info - Queries and saves all secure partitions data
- This function invokes 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 the
- resident private data structure and we keep the UUID field empty
- (in FF-A 1.0 UUID is not provided by the partition descriptor)
- This function is 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(void) +{
- return ffa_query_partitions_info(NULL, NULL);
+}
+/**
- ffa_msg_send_direct_req - FFA_MSG_SEND_DIRECT_{REQ,RESP} handler function
- @dst_part_id: destination partition ID
- @msg: pointer to the message data preallocated by the client (in/out)
- 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
- */
+static int ffa_msg_send_direct_req(u16 dst_part_id, struct ffa_send_direct_data *msg) +{
- ffa_value_t res = {0};
- int ffa_errno;
- if (!ffa_priv_data || !ffa_priv_data->invoke_ffa_fn)
return -EINVAL;
- /* No partition installed */
- if (!ffa_priv_data->partitions.count || !ffa_priv_data->partitions.descs)
return -ENODEV;
- ffa_priv_data->invoke_ffa_fn((ffa_value_t){
.a0 = FFA_SMC_64(FFA_MSG_SEND_DIRECT_REQ),
.a1 = PREP_SELF_ENDPOINT_ID(ffa_priv_data->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))
ffa_priv_data->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 == FFA_SMC_64(FFA_MSG_SEND_DIRECT_RESP)) {
/*
* 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);
+}
+/**
- __arm_ffa_fn_smc - 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
- Return: void
- */
+void __arm_ffa_fn_smc(ffa_value_t args, ffa_value_t *res) +{
- arm_smccc_1_2_smc(&args, res);
+}
+/**
- ffa_set_smc_conduit - Set the SMC conduit
- This function selects the SMC conduit by setting the driver invoke function
- to SMC assembly function
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_set_smc_conduit(void) +{
- ffa_priv_data->invoke_ffa_fn = __arm_ffa_fn_smc;
- if (!ffa_priv_data->invoke_ffa_fn) {
ffa_err("failure to set the invoke function");
return -EINVAL;
- }
- ffa_info("Conduit is SMC");
- return 0;
+}
+/**
- ffa_set_bus_ops - Set the bus driver operations
- Setting the driver callbacks.
- */
+static void ffa_set_bus_ops(void) +{
- ffa_priv_data->ffa_ops.partition_info_get = ffa_get_partitions_info;
- ffa_priv_data->ffa_ops.sync_send_receive = ffa_msg_send_direct_req;
- ffa_priv_data->ffa_ops.rxtx_unmap = ffa_unmap_rxtx_buffers;
+}
+/**
- ffa_alloc_prvdata - allocate the driver main data structure and sets the device
- @dev: the arm_ffa device
- This function creates the main data structure embedding all the driver data.
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_alloc_prvdata(struct udevice *dev) +{
- if (!dev) {
ffa_err("no udevice found");
return -ENODEV;
- }
- /* The device is registered with the DM. Let's create the driver main data structure*/
- ffa_priv_data = devm_kmalloc(dev, sizeof(struct ffa_prvdata), __GFP_ZERO);
- if (!ffa_priv_data) {
ffa_err("can not allocate the driver main data structure");
return -ENOMEM;
- }
- ffa_priv_data->dev = dev;
- return 0;
+}
+/**
- ffa_probe - The driver probe function
- @dev: the arm_ffa device
- Probing is done at boot time and triggered by the uclass device discovery.
- At probe level the following actions are done:
- setting the conduit
- querying the FF-A framework version
- 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 the resident private data structure.
- The probe will fail if either FF-A framework is not detected or the
- FF-A requests are not behaving correctly. This ensures that the
- driver is not installed and its operations are not exported to the clients.
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_probe(struct udevice *dev) +{
- int ret;
- ret = ffa_alloc_prvdata(dev);
- if (ret != 0)
return ret;
- ffa_set_bus_ops();
- ret = ffa_set_smc_conduit();
- if (ret != 0)
return ret;
- ret = ffa_get_version();
- if (ret != 0)
return ret;
- ret = ffa_get_endpoint_id();
- if (ret != 0)
return ret;
- ret = ffa_get_rxtx_map_features();
- if (ret != 0)
return ret;
- ret = ffa_map_rxtx_buffers();
- if (ret != 0)
return ret;
- ret = ffa_cache_partitions_info();
I've asked this before, but never got an answer: Why are we saving all the found partitions in a cache? It seems that FFA_PARTITION_INFO_GET could be called each time when needed instead without any noticeable overhead. Or is the result cached for some other reason?
I already replied here [1]. Please have a look.
[1]: https://lore.kernel.org/all/20221014102849.GA14576@e121910.cambridge.arm.com...
Cheers, Jens
- if (ret != 0) {
ffa_free_rxtx_buffers();
return ret;
- }
- return 0;
+}
+/**
- ffa_remove - The driver remove function
- @dev: the arm_ffa device
- When the device is about to be removed , unmap the RX/TX buffers and free the memory
- Return:
- 0 on success.
- */
+static int ffa_remove(struct udevice *dev) +{
- ffa_info("removing the device");
- ffa_unmap_rxtx_buffers();
- if (ffa_priv_data->pair.rxbuf || ffa_priv_data->pair.txbuf)
ffa_free_rxtx_buffers();
- return 0;
+}
+/**
- ffa_unbind - The driver unbind function
- @dev: the arm_ffa device
- After the device is removed and memory freed the device is unbound
- Return:
- 0 on success.
- */
+static int ffa_unbind(struct udevice *dev) +{
- ffa_info("unbinding the device , private data already released");
- ffa_priv_data = NULL;
- return 0;
+}
+/**
- ffa_bus_ops_get - bus driver operations getter
- Return:
- This function returns a pointer to the driver operations structure
- */
+const struct ffa_bus_ops *ffa_bus_ops_get(void) +{
- return &ffa_priv_data->ffa_ops;
+}
+/**
- ffa_bus_prvdata_get - bus driver private data getter
- Return:
- This function returns a pointer to the main private data structure
- */
+struct ffa_prvdata **ffa_bus_prvdata_get(void) +{
- return &ffa_priv_data;
+}
+/**
- ffa_bus_discover - discover FF-A bus and probe the arm_ffa device
- This function makes sure the FF-A bus is discoverable.
- When probing succeeds FF-A discovery is done. The arm_ffa device is ready to use.
- When the bus was already discovered successfully the discovery will not run again.
- Arm FF-A transport is implemented through arm_ffa u-boot device managing the FF-A
- communication.
- All FF-A clients should use the arm_ffa device to use the FF-A transport.
- Return:
- 0 on success. Otherwise, failure
- */
+int ffa_bus_discover(void) +{
- int ret = 0;
- if (!ffa_priv_data)
ret = ffa_device_get();
- return ret;
+}
+/**
- Declaring the arm_ffa driver under UCLASS_FFA
- */
+U_BOOT_DRIVER(arm_ffa) = {
- .name = FFA_DRV_NAME,
- .id = UCLASS_FFA,
- .probe = ffa_probe,
- .remove = ffa_remove,
- .unbind = ffa_unbind,
+}; diff --git a/include/arm_ffa.h b/include/arm_ffa.h new file mode 100644 index 0000000000..d146e7b328 --- /dev/null +++ b/include/arm_ffa.h @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#ifndef __ARM_FFA_H +#define __ARM_FFA_H
+#include <linux/printk.h>
+/*
- This header is public. It can be used by clients to access
- data structures and definitions they need
- */
+/*
- Macros for displaying logs
- */
+#define ffa_info(fmt, ...) pr_info("[FFA] " fmt "\n", ##__VA_ARGS__) +#define ffa_err(fmt, ...) pr_err("[FFA] " fmt "\n", ##__VA_ARGS__)
+/*
- 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 __packed 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_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 __packed ffa_send_direct_data {
- unsigned long data0; /* w3/x3 */
- unsigned long data1; /* w4/x4 */
- unsigned long data2; /* w5/x5 */
- unsigned long data3; /* w6/x6 */
- unsigned long data4; /* w7/x7 */
+};
+/**
- struct ffa_bus_ops - The driver operations structure
- @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)(const char *uuid_str,
u32 *parts_size, struct ffa_partition_info *buffer);
- int (*sync_send_receive)(u16 dst_part_id, struct ffa_send_direct_data *msg);
- int (*rxtx_unmap)(void);
+};
+/**
- The device driver and the Uclass driver public functions
- */
+/**
- ffa_bus_ops_get - driver operations getter
- */
+const struct ffa_bus_ops *ffa_bus_ops_get(void);
+/**
- ffa_bus_discover - discover FF-A bus and probes the arm_ffa device
- */
+int ffa_bus_discover(void);
+#endif diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index a432e43871..5dd698b7a9 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -4,6 +4,9 @@
- (C) Copyright 2012
- Pavel Herrmann morpheus.ibis@gmail.com
- (C) Copyright 2022 ARM Limited
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#ifndef _DM_UCLASS_ID_H @@ -55,6 +58,7 @@ enum uclass_id { UCLASS_EFI_MEDIA, /* Devices provided by UEFI firmware */ UCLASS_ETH, /* Ethernet device */ UCLASS_ETH_PHY, /* Ethernet PHY device */
- UCLASS_FFA, /* Arm Firmware Framework for Armv8-A */ UCLASS_FIRMWARE, /* Firmware */ UCLASS_FUZZING_ENGINE, /* Fuzzing engine */ UCLASS_FS_FIRMWARE_LOADER, /* Generic loader */
-- 2.17.1

On Tue, Oct 25, 2022 at 11:31:11AM +0200, Jens Wiklander wrote:
On Thu, Oct 13, 2022 at 11:38:50AM +0100, Abdellatif El Khlifi wrote:
Add the core driver 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.
32-bit version of the ABIs is supported and 64-bit version of FFA_RXTX_MAP and FFA_MSG_SEND_DIRECT_{REQ, RESP}.
In u-boot FF-A design, FF-A is considered as a discoverable bus.
U-Boot
All comments in this patch are addressed in v7, thanks.
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:
- partition_info_get
- sync_send_receive
- rxtx_unmap
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v6:
- drop use of EFI runtime support (We decided with Linaro to add this later)
- drop discovery from initcalls (discovery will be on demand by FF-A users)
- set the alignment of the RX/TX buffers to the larger translation granule size
- move FF-A RX/TX buffers unmapping at ExitBootServices() to a separate commit
- update the documentation and move it to doc/arch/arm64.ffa.rst
v4:
- add doc/README.ffa.drv
- moving the FF-A driver work to drivers/firmware/arm-ffa
- use less #ifdefs in lib/efi_loader/efi_boottime.c and replace #if defined by #if CONFIG_IS_ENABLED
- improving error handling by mapping the FF-A errors to standard errors and logs
- replacing panics with an error log and returning an error code
- improving features discovery in FFA_FEATURES by introducing rxtx_min_pages private data field
- add ffa_remove and ffa_unbind functions
- improve how the driver behaves when bus discovery is done more than once
v3:
- align the interfaces of the u-boot FF-A driver with those in the linux FF-A driver
- remove the FF-A helper layer
- make the u-boot FF-A driver independent from EFI
- provide an optional config that enables copying the driver data to EFI runtime section at ExitBootServices service
- use 64-bit version of FFA_RXTX_MAP, FFA_MSG_SEND_DIRECT_{REQ, RESP}
v2:
- make FF-A bus discoverable using device_{bind, probe} APIs
- remove device tree support
v1:
- introduce FF-A bus driver with device tree support
MAINTAINERS | 7 + doc/arch/arm64.ffa.rst | 207 ++++ doc/arch/index.rst | 1 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/firmware/arm-ffa/Kconfig | 30 + drivers/firmware/arm-ffa/Makefile | 6 + drivers/firmware/arm-ffa/arm-ffa-uclass.c | 16 + drivers/firmware/arm-ffa/arm_ffa_prv.h | 196 +++ drivers/firmware/arm-ffa/core.c | 1337 +++++++++++++++++++++ include/arm_ffa.h | 93 ++ include/dm/uclass-id.h | 4 + 12 files changed, 1900 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_prv.h create mode 100644 drivers/firmware/arm-ffa/core.c create mode 100644 include/arm_ffa.h
diff --git a/MAINTAINERS b/MAINTAINERS index a26b36c7c2..496f47a516 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -248,6 +248,13 @@ 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 abdellatif.elkhlifi@arm.com +S: Maintained +F: doc/arch/arm64.ffa.rst +F: drivers/firmware/arm-ffa/ +F: include/arm_ffa.h
ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de M: Fabio Estevam festevam@gmail.com diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst new file mode 100644 index 0000000000..e98d2cf2b3 --- /dev/null +++ b/doc/arch/arm64.ffa.rst @@ -0,0 +1,207 @@ +.. SPDX-License-Identifier: GPL-2.0+
+Arm FF-A Driver +===============
+Summary +-------
+FF-A stands for Firmware Framework for Arm A-profile processors.
+FF-A specifies interfaces that enable a pair of software sandboxes to +communicate with each other. A sandbox aka partition could +be a VM in the Normal or Secure world, an application in S-EL0, or a +Trusted OS in S-EL1.
+This FF-A driver implements the interfaces to communicate with partitions in +the Secure world aka Secure partitions (SPs).
+The driver 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.
+FF-A driver 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
+FF-A and SMC specifications +-------------------------------------------
+The current implementation of the driver relies on FF-A specification v1.0 +and uses SMC32 calling convention.
+At this stage we only need the FF-A v1.0 features.
+The driver has been tested with Optee OS which supports SMC32 for most of
s/Optee OS/OP-TEE/
+the SMC ABIs.
+For more details please refer to the FF-A v1.0 spec: +https://documentation-service.arm.com/static/5fb7e8a6ca04df4095c1d65e?token=
+Hypervisors are supported if they are configured to trap SMC calls.
+The FF-A driver uses 64-bit registers as per SMCCCv1.2 specification.
+For more details please refer to the SMC Calling Convention v1.2 spec: +https://documentation-service.arm.com/static/5f8edaeff86e16515cdbe4c6?token=
+Supported hardware +--------------------------------
+Aarch64 plaforms
+Configuration +----------------------
+CONFIG_ARM_FFA_TRANSPORT
- Enables the FF-A bus driver. Turn this on if you want to use FF-A
- communication.
+CONFIG_SANDBOX_FFA
- Enables FF-A Sandbox driver. This emulates the FF-A ABIs handling under
- Sandbox and provides functional tests for FF-A.
+FF-A ABIs under the hood +---------------------------------------
+Invoking an FF-A ABI involves providing to the secure world/hypervisor the +expected arguments from the ABI.
+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 FF-A driver (non-secure world).
+The driver reads the response and processes it accordingly.
+This methodology applies to all the FF-A ABIs in the driver.
+FF-A bus discovery in U-boot
U-Boot, please search and replace.
+-------------------------------------------
+When CONFIG_ARM_FFA_TRANSPORT is enabled, the FF-A bus is discovered on +demand by the clients (users).
+Clients can discover the FF-A bus using ffa_bus_discover() API which triggers the +discovery process.
+ffa_bus_discover() creates, binds and probes the arm_ffa device using +device_{bind, probe} APIs.
+The discovery process consists in communicating with secure world (or hypervisor) +and querying specific data.
+The discovery process takes place during the arm_ffa device probing which is +handled by ffa_probe().
+The FF-A bus discovery is successful and the bus is ready for use when these +operations succeed:
+- querying the FF-A framework version +- querying from secure world the U-boot endpoint ID +- querying from secure world the RX/TX mapping features +- mapping the RX/TX buffers +- querying from secure world all the partitions information
+Discovery failure results in a probing failure and the arm_ffa device is +destroyed.
+Requirements for clients +-------------------------------------
+When using the FF-A bus with EFI, clients must:
+- Query SPs in EFI boot time mode using the service UUID. +- Unmap RX/TX buffers before EFI runtime mode starts.
+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 by the user before EFI runtime mode +starts. The driver provides a bus operation for that: rxtx_unmap()
+If RX/TX buffers created by U-boot are not unmapped and by +consequence becoming available at EFI runtime, secure world will get confused +about RX/TX buffers ownership (U-boot vs kernel).
+The bus driver layer +------------------------------
+The driver comes on top of the SMCCC layer and is implemented in +drivers/firmware/arm-ffa/core.c
+The driver provides the following features:
+- 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
+- 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, the driver takes care of how to transport
- that to the secure world/hypervisor using FF-A
+- The driver provides callbacks to be used by clients to access the FF-A bus:
+partition_info_get +sync_send_receive +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
+Using armffa command +-----------------------------------
+armffa is a command showcasing how to use the FF-A driver and how to invoke +its operations.
+This provides a guidance to the client developers on how to call the FF-A bus +interfaces.
+Usage:
+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
It may be worth mentioning that this is an implementation defined command.
devlist
displays the arm_ffa device info
+Contributors +------------
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
diff --git a/doc/arch/index.rst b/doc/arch/index.rst index 792d9182c3..8d1ab0ad4e 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/Kconfig b/drivers/Kconfig index 8b6fead351..b06b1ae481 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -6,6 +6,8 @@ source "drivers/core/Kconfig"
source "drivers/adc/Kconfig"
+source "drivers/firmware/arm-ffa/Kconfig"
source "drivers/ata/Kconfig"
source "drivers/axi/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 9d9f69a3c9..bf8d7b8cfc 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -111,6 +111,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/arm-ffa/Kconfig b/drivers/firmware/arm-ffa/Kconfig new file mode 100644 index 0000000000..f1427535f9 --- /dev/null +++ b/drivers/firmware/arm-ffa/Kconfig @@ -0,0 +1,30 @@ +# 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 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.
This driver 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.
U-Boot
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.
For more details about the FF-A driver, please refer to doc/README.ffa.drv
Not doc/arch/arm64.ffa.rst?
diff --git a/drivers/firmware/arm-ffa/Makefile b/drivers/firmware/arm-ffa/Makefile new file mode 100644 index 0000000000..043a8915be --- /dev/null +++ b/drivers/firmware/arm-ffa/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2022 +# Abdellatif El Khlifi, Arm Limited, abdellatif.elkhlifi@arm.com.
+obj-y += arm-ffa-uclass.o core.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 0000000000..7d9695d289 --- /dev/null +++ b/drivers/firmware/arm-ffa/arm-ffa-uclass.c @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include <common.h> +#include <dm.h> +#include <asm/global_data.h>
+DECLARE_GLOBAL_DATA_PTR;
+UCLASS_DRIVER(ffa) = {
- .name = "ffa",
- .id = UCLASS_FFA,
+}; diff --git a/drivers/firmware/arm-ffa/arm_ffa_prv.h b/drivers/firmware/arm-ffa/arm_ffa_prv.h new file mode 100644 index 0000000000..7bc90f7f66 --- /dev/null +++ b/drivers/firmware/arm-ffa/arm_ffa_prv.h @@ -0,0 +1,196 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#ifndef __ARM_FFA_PRV_H +#define __ARM_FFA_PRV_H
+#include <arm_ffa.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/arm-smccc.h>
+/*
- This header is private. It is exclusively used by the FF-A driver
- */
+/* FF-A core driver name */ +#define FFA_DRV_NAME "arm_ffa"
+/* 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*/
+};
+/* number of the errors supported by the FF-A specification */ +#define MAX_NUMBER_FFA_ERR 9
+/* 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)
+/* The FF-A SMC function definitions */
+typedef struct arm_smccc_1_2_regs ffa_value_t; +typedef void (*invoke_ffa_fn_t)(ffa_value_t args, ffa_value_t *res);
+/*
- 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 */
+};
+/**
- 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 - structure hosting 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
- Data structure hosting 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 {
- u64 rxbuf; /* virtual address */
- u64 txbuf; /* virtual address */
- size_t rxtx_min_pages; /* minimum number of pages in each of the RX/TX buffers */
+};
+/**
- 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_partitions - descriptors for all secure partitions
- @count: The number of partitions descriptors
- @descs The partitions descriptors table
- This data structure contains the partitions descriptors table
- */
+struct ffa_partitions {
- u32 count;
- struct ffa_partition_desc *descs; /* virtual address */
+};
+/**
- struct ffa_prvdata - the driver private data structure
- @dev: The arm_ffa device under u-boot driver model
- @ffa_ops: The driver operations structure
- @fwk_version: FF-A framework version
- @id: u-boot endpoint ID
- @partitions: The partitions descriptors structure
- @pair: The RX/TX buffers pair
- @invoke_ffa_fn: The function executing the FF-A function
- The driver data structure hosting all resident data.
- */
+struct ffa_prvdata {
- struct udevice *dev;
- struct ffa_bus_ops ffa_ops;
- u32 fwk_version;
- u16 id;
- struct ffa_partitions partitions;
- struct ffa_rxtxpair pair;
- invoke_ffa_fn_t invoke_ffa_fn;
+};
+/**
- ffa_device_get - create, bind and probe the arm_ffa device
- */
+int ffa_device_get(void);
+/**
- ffa_bus_prvdata_get - bus driver private data getter
- */
+struct ffa_prvdata **ffa_bus_prvdata_get(void);
+#endif diff --git a/drivers/firmware/arm-ffa/core.c b/drivers/firmware/arm-ffa/core.c new file mode 100644 index 0000000000..324367d12b --- /dev/null +++ b/drivers/firmware/arm-ffa/core.c @@ -0,0 +1,1337 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include "arm_ffa_prv.h" +#include <asm/global_data.h> +#include <common.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <dm/devres.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> +#include <log.h> +#include <malloc.h> +#include <string.h> +#include <uuid.h>
+DECLARE_GLOBAL_DATA_PTR;
+/**
- The device private data structure containing all the
- data read from secure world
- */
+struct ffa_prvdata *ffa_priv_data;
+/* Error mapping declarations */
+int ffa_to_std_errmap[MAX_NUMBER_FFA_ERR] = {
- 0,
- -EOPNOTSUPP, /* NOT_SUPPORTED */
- -EINVAL, /* INVALID_PARAMETERS */
- -ENOMEM, /* NO_MEMORY */
- -EBUSY, /* BUSY */
- -EINTR, /* INTERRUPTED */
- -EACCES, /* DENIED */
- -EAGAIN, /* RETRY */
- -ECANCELED, /* ABORTED */
+};
+struct ffa_abi_errmap err_msg_map[FFA_ERRMAP_COUNT] = {
- [FFA_ID_TO_ERRMAP_ID(FFA_VERSION)] = {
{
"",
Shouldn't all these empty strings be NULL instead? Given that's that what you check for in ffa_print_error_log() below.
You could save a few lines by using designated initializers for only the used elements in err_str for each FF-A function ID.
"NOT_SUPPORTED: A Firmware Framework implementation does not exist",
"", /* INVALID_PARAMETERS */
"", /* NO_MEMORY */
"", /* BUSY */
"", /* INTERRUPTED */
"", /* DENIED */
"", /* RETRY */
"", /* ABORTED */
},
- },
- [FFA_ID_TO_ERRMAP_ID(FFA_ID_GET)] = {
{
"",
"NOT_SUPPORTED: This function is not implemented at this FF-A instance",
"", /* INVALID_PARAMETERS */
"", /* NO_MEMORY */
"", /* BUSY */
"", /* INTERRUPTED */
"", /* DENIED */
"", /* RETRY */
"", /* ABORTED */
},
- },
- [FFA_ID_TO_ERRMAP_ID(FFA_FEATURES)] = {
{
"",
"NOT_SUPPORTED: FFA_RXTX_MAP is not implemented at this FF-A instance",
"", /* INVALID_PARAMETERS */
"", /* NO_MEMORY */
"", /* BUSY */
"", /* INTERRUPTED */
"", /* DENIED */
"", /* RETRY */
"", /* ABORTED */
},
- },
- [FFA_ID_TO_ERRMAP_ID(FFA_PARTITION_INFO_GET)] = {
{
"",
"NOT_SUPPORTED: This function is not implemented at this FF-A instance",
"INVALID_PARAMETERS: Unrecognized UUID",
"NO_MEMORY: Results cannot fit in RX buffer of the caller",
"BUSY: RX buffer of the caller is not free",
"", /* INTERRUPTED */
"DENIED: Callee is not in a state to handle this request",
"", /* RETRY */
"", /* ABORTED */
},
- },
- [FFA_ID_TO_ERRMAP_ID(FFA_RXTX_UNMAP)] = {
{
"",
"NOT_SUPPORTED: FFA_RXTX_UNMAP is not implemented at this FF-A instance",
"INVALID_PARAMETERS: No buffer pair registered on behalf of the caller",
"", /* NO_MEMORY */
"", /* BUSY */
"", /* INTERRUPTED */
"", /* DENIED */
"", /* RETRY */
"", /* ABORTED */
},
- },
- [FFA_ID_TO_ERRMAP_ID(FFA_RX_RELEASE)] = {
{
"",
"NOT_SUPPORTED: FFA_RX_RELEASE is not implemented at this FF-A instance",
"", /* INVALID_PARAMETERS */
"", /* NO_MEMORY */
"", /* BUSY */
"", /* INTERRUPTED */
"DENIED: Caller did not have ownership of the RX buffer",
"", /* RETRY */
"", /* ABORTED */
},
- },
- [FFA_ID_TO_ERRMAP_ID(FFA_RXTX_MAP)] = {
{
"",
"NOT_SUPPORTED: This function is not implemented at this FF-A instance",
"INVALID_PARAMETERS: Field(s) in input parameters incorrectly encoded",
"NO_MEMORY: Not enough memory",
"", /* BUSY */
"", /* INTERRUPTED */
"DENIED: Buffer pair already registered",
"", /* RETRY */
"", /* ABORTED */
},
- },
+};
+/**
- ffa_to_std_errno - convert FF-A error code to standard error code
- @ffa_errno: Error code returned by the FF-A ABI
- This function maps 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
- */
+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
- This function maps 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
- */
+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_msg_map[abi_idx].err_str[err_idx])
err_str can't be NULL since it's an array in the struct ffa_abi_errmap.
return -EINVAL;
- ffa_err("%s", err_msg_map[abi_idx].err_str[err_idx]);
- return 0;
+}
+/*
- Driver core functions
- */
+/**
- ffa_remove_device - removes the arm_ffa device
- @dev: the device to be removed
- This function makes sure the arm_ffa device is removed
- No need to free the kmalloced data when the device is destroyed.
- It's automatically done by devm management by
- device_remove() -> device_free() -> devres_release_probe().
- Return:
- 0 on success. Otherwise, failure
- */
+int ffa_remove_device(struct udevice *dev) +{
- int ret;
- if (!dev) {
ffa_err("no udevice found");
return -ENODEV;
- }
- ret = device_remove(dev, DM_REMOVE_NORMAL);
- if (ret) {
ffa_err("unable to remove. err:%d\n", ret);
return ret;
- }
- ffa_info("device removed and freed");
- ret = device_unbind(dev);
- if (ret) {
ffa_err("unable to unbind. err:%d\n", ret);
return ret;
- }
- ffa_info("device unbound");
- return 0;
+}
+/**
- ffa_device_get - create, bind and probe the arm_ffa device
- This function makes sure the arm_ffa device is
- created, bound to this driver, probed and ready to use.
- Arm FF-A transport is implemented through a single u-boot
- device managing the FF-A bus (arm_ffa).
- Return:
- 0 on success. Otherwise, failure
- */
+int ffa_device_get(void) +{
- int ret;
- struct udevice *dev = NULL;
- ret = device_bind(dm_root(),
DM_DRIVER_GET(arm_ffa),
FFA_DRV_NAME,
NULL,
ofnode_null(),
&dev);
There's no need for separate lines for each argument, please fold up a bit.
- if (ret)
return ret;
- /* The FF-A bus discovery succeeds when probing is successful */
- ret = device_probe(dev);
- if (ret) {
ffa_err("arm_ffa device probing failed");
ffa_remove_device(dev);
return ret;
- }
- return 0;
+}
+/**
- ffa_get_version - FFA_VERSION handler function
- This function implements FFA_VERSION FF-A function
- to get from the secure world the FF-A framework version
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_get_version(void) +{
- u16 major, minor;
- ffa_value_t res = {0};
- int ffa_errno;
- ffa_priv_data->invoke_ffa_fn((ffa_value_t){
.a0 = FFA_SMC_32(FFA_VERSION),
.a1 = FFA_VERSION_1_0
It's generally preferred to have comma after the last initializer too.
It could also be worth comparing with the style (alignment and folding up where possible) of the corresponding driver in the Linux kernel which I find a bit easier to read. However, checkpatch seems happy enough so I guess it's not a big deal.
I asked earlier why you're not supporting the latest version, 1.1, but you never answered that.
}, &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);
- ffa_info("FF-A driver %d.%d\nFF-A framework %d.%d",
FFA_MAJOR_VERSION, FFA_MINOR_VERSION, major, minor);
- if ((major == FFA_MAJOR_VERSION && minor >= FFA_MINOR_VERSION)) {
ffa_info("Versions are compatible ");
ffa_priv_data->fwk_version = res.a0;
return 0;
- }
- ffa_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
- This function implements 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(void) +{
- ffa_value_t res = {0};
- int ffa_errno;
- ffa_priv_data->invoke_ffa_fn((ffa_value_t){
.a0 = FFA_SMC_32(FFA_ID_GET)
}, &res);
- if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) {
ffa_priv_data->id = GET_SELF_ENDPOINT_ID((u32)res.a2);
ffa_info("endpoint ID is %u", ffa_priv_data->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 - sets the minimum number of pages in each of the RX/TX buffers
- @prop_field: properties field obtained from FFA_FEATURES ABI
- This function sets the minimum number of pages
- in each of the RX/TX buffers in the private data structure
- Return:
- buf_4k_pages points to the returned number of pages
- 0 on success. Otherwise, failure
- */
+static int ffa_set_rxtx_buffers_pages_cnt(u32 prop_field) +{
- if (!ffa_priv_data)
return -EINVAL;
- switch (prop_field) {
- case RXTX_4K:
ffa_priv_data->pair.rxtx_min_pages = 1;
break;
- case RXTX_16K:
ffa_priv_data->pair.rxtx_min_pages = 4;
break;
- case RXTX_64K:
ffa_priv_data->pair.rxtx_min_pages = 16;
break;
- default:
ffa_err("RX/TX buffer size not supported");
return -EINVAL;
- }
- return 0;
+}
+/**
- ffa_get_rxtx_map_features - FFA_FEATURES handler function with FFA_RXTX_MAP argument
- This function implements 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(void) +{
- ffa_value_t res = {0};
- int ffa_errno;
- ffa_priv_data->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((u32)res.a2);
The cast isn't needed.
- ffa_errno = res.a2;
- ffa_print_error_log(FFA_FEATURES, ffa_errno);
- return ffa_to_std_errno(ffa_errno);
+}
+/**
- ffa_free_rxtx_buffers - frees the RX/TX buffers
- This function frees the RX/TX buffers
- */
+static void ffa_free_rxtx_buffers(void) +{
- ffa_info("Freeing RX/TX buffers");
- if (ffa_priv_data->pair.rxbuf) {
free((void *)ffa_priv_data->pair.rxbuf);
ffa_priv_data->pair.rxbuf = 0;
- }
- if (ffa_priv_data->pair.txbuf) {
free((void *)ffa_priv_data->pair.txbuf);
ffa_priv_data->pair.txbuf = 0;
- }
+}
+/**
- ffa_alloc_rxtx_buffers - allocates the RX/TX buffers
- This function is 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.
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_alloc_rxtx_buffers(void) +{
- u64 bytes;
- ffa_info("Using %lu 4KB page(s) for RX/TX buffers size",
ffa_priv_data->pair.rxtx_min_pages);
- bytes = ffa_priv_data->pair.rxtx_min_pages * SZ_4K;
- /*
* The alignment of the RX and TX buffers must be equal
* to the larger translation granule size
*/
- ffa_priv_data->pair.rxbuf = (u64)memalign(bytes, bytes);
- if (!ffa_priv_data->pair.rxbuf) {
ffa_err("failure to allocate RX buffer");
return -ENOBUFS;
- }
- ffa_info("RX buffer at virtual address 0x%llx", ffa_priv_data->pair.rxbuf);
- ffa_priv_data->pair.txbuf = (u64)memalign(bytes, bytes);
- if (!ffa_priv_data->pair.txbuf) {
free((void *)ffa_priv_data->pair.rxbuf);
ffa_priv_data->pair.rxbuf = 0;
ffa_err("failure to allocate the TX buffer");
return -ENOBUFS;
- }
- ffa_info("TX buffer at virtual address 0x%llx", ffa_priv_data->pair.txbuf);
- /*
* make sure the buffers are cleared before use
*/
- memset((void *)ffa_priv_data->pair.rxbuf, 0, bytes);
- memset((void *)ffa_priv_data->pair.txbuf, 0, bytes);
- return 0;
+}
+/**
- ffa_map_rxtx_buffers - FFA_RXTX_MAP handler function
- This function implements FFA_RXTX_MAP FF-A function
- to map the RX/TX buffers
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_map_rxtx_buffers(void) +{
- int ret;
- ffa_value_t res = {0};
- int ffa_errno;
- ret = ffa_alloc_rxtx_buffers();
- 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
A space is missing after the '*'.
*/
- ffa_priv_data->invoke_ffa_fn((ffa_value_t){
.a0 = FFA_SMC_64(FFA_RXTX_MAP),
.a1 = ffa_priv_data->pair.txbuf,
.a2 = ffa_priv_data->pair.rxbuf,
.a3 = ffa_priv_data->pair.rxtx_min_pages,
}, &res);
- if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) {
ffa_info("RX/TX buffers mapped");
return 0;
- }
- ffa_errno = res.a2;
- ffa_print_error_log(FFA_RXTX_MAP, ffa_errno);
- ffa_free_rxtx_buffers();
- return ffa_to_std_errno(ffa_errno);
+}
+/**
- ffa_unmap_rxtx_buffers - FFA_RXTX_UNMAP handler function
- This function implements FFA_RXTX_UNMAP FF-A function
- to unmap the RX/TX buffers
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_unmap_rxtx_buffers(void) +{
- ffa_value_t res = {0};
- int ffa_errno;
- ffa_priv_data->invoke_ffa_fn((ffa_value_t){
.a0 = FFA_SMC_32(FFA_RXTX_UNMAP),
.a1 = PREP_SELF_ENDPOINT_ID(ffa_priv_data->id)
}, &res);
- if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) {
ffa_free_rxtx_buffers();
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 - FFA_RX_RELEASE handler function
- This function invokes 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(void) +{
- ffa_value_t res = {0};
- int ffa_errno;
- ffa_priv_data->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 - checks whether two given UUIDs are identical
- @uuid1: first UUID
- @uuid2: second UUID
- This function is used by ffa_read_partitions_info to search
- for a UUID in the partitions descriptors table
- Return:
- 1 when UUIDs match. Otherwise, 0
- */
+int ffa_uuid_are_identical(const struct ffa_partition_uuid *uuid1,
const struct ffa_partition_uuid *uuid2)
Would it make sense to use the bool type instead?
+{
- if (!uuid1 || !uuid2)
return 0;
- return !memcmp(uuid1, uuid2, sizeof(struct ffa_partition_uuid));
+}
+/**
- ffa_read_partitions_info - reads the data queried by FFA_PARTITION_INFO_GET
and saves it in the private structure
- @count: The number of partitions queried
- @part_uuid: Pointer to the partition(s) UUID
- This function reads the partitions information
- returned by the FFA_PARTITION_INFO_GET and saves it in the private
- data structure.
- Return:
- The private data structure is updated with the partition(s) information
- 0 is returned on success. Otherwise, failure
- */
+static int ffa_read_partitions_info(u32 count, struct ffa_partition_uuid *part_uuid) +{
- if (!count) {
ffa_err("no partition detected");
return -ENODATA;
- }
- ffa_info("Reading partitions data from the RX buffer");
- 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 = ffa_priv_data->pair.rxtx_min_pages * SZ_4K;
if (data_bytes > buf_bytes) {
ffa_err("partitions data size exceeds the RX buffer size:");
ffa_err(" sizes in bytes: data %llu , RX buffer %llu ",
data_bytes,
buf_bytes);
return -ENOMEM;
}
ffa_priv_data->partitions.descs = devm_kmalloc(ffa_priv_data->dev, data_bytes,
__GFP_ZERO);
if (!ffa_priv_data->partitions.descs) {
ffa_err("cannot allocate partitions data buffer");
return -ENOMEM;
}
parts_info = (struct ffa_partition_info *)ffa_priv_data->pair.rxbuf;
for (desc_idx = 0 ; desc_idx < count ; desc_idx++) {
ffa_priv_data->partitions.descs[desc_idx].info =
parts_info[desc_idx];
ffa_info("Partition ID %x : info cached",
ffa_priv_data->partitions.descs[desc_idx].info.id);
}
ffa_priv_data->partitions.count = count;
ffa_info("%d partition(s) found and cached", count);
- } else {
u32 rx_desc_idx, cached_desc_idx;
struct ffa_partition_info *parts_info;
u8 desc_found;
parts_info = (struct ffa_partition_info *)ffa_priv_data->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 < ffa_priv_data->partitions.count;
cached_desc_idx++) {
/*
* save the UUID
*/
if (ffa_priv_data->partitions.descs[cached_desc_idx].info.id ==
parts_info[rx_desc_idx].id) {
ffa_priv_data->partitions.descs[cached_desc_idx].sp_uuid =
*part_uuid;
desc_found = 1;
break;
}
}
if (!desc_found)
return -ENODATA;
}
- }
- return 0;
+}
+/**
- ffa_query_partitions_info - invokes FFA_PARTITION_INFO_GET and saves partitions data
- @part_uuid: Pointer to the partition(s) UUID
- @pcount: Pointer to the number of partitions variable filled when querying
- This function executes the FFA_PARTITION_INFO_GET
- to query the partitions data. Then, it calls ffa_read_partitions_info
- to save the data in the private data structure.
- 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 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;
- }
- ffa_priv_data->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((u32)res.a2, part_uuid);
if (ret) {
ffa_err("failed to read partition(s) data , error (%d)", ret);
ffa_release_rx_buffer();
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();
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 - FFA_PARTITION_INFO_GET handler function
- The passed arguments:
- Mode 1: When getting from the driver the number of
- secure partitions:
- @uuid_str: pointer to the UUID string
- @parts_size: pointer to the variable that contains the number of partitions
The variable will be set by the driver
- @buffer: NULL
- Mode 2: When requesting the driver to return the
- partitions information:
- @uuid_str: pointer to the UUID string
- @parts_size: pointer to the size of the SPs information buffer in bytes
- @buffer: pointer to SPs information buffer
(allocated by the client).
The buffer will be filled by the driver
- This function queries the secure partition data from
- the private data structure. If not found, it invokes 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 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().
- When invoked through a client request, ffa_get_partitions_info should be
- called twice. First call is to get from the driver the number of secure
- partitions (SPs) associated to a particular UUID.
- Then, the caller (client) allocates the buffer to host the SPs data and
- issues a 2nd call. Then, the driver fills the SPs data in the pre-allocated
- buffer.
- To achieve the mechanism described above, ffa_get_partitions_info uses the
- following functions:
ffa_read_partitions_info
ffa_query_partitions_info
- Return:
- @parts_size: When pointing to the number of partitions variable, the number is
- set by the driver.
- When pointing to the partitions information buffer size, the buffer will be
- filled by the driver.
- On success 0 is returned. Otherwise, failure
- */
+static int ffa_get_partitions_info(const char *uuid_str,
u32 *parts_size, struct ffa_partition_info *buffer)
+{
- /*
* fill_data:
* 0: return the SP count
* 1: fill SP data and return it to the caller
* -1: undefined mode
*/
- int fill_data = -1;
- u32 desc_idx, client_desc_idx;
- struct ffa_partition_uuid part_uuid = {0};
- u32 client_desc_max_cnt;
- u32 parts_found = 0;
- if (!ffa_priv_data->partitions.count || !ffa_priv_data->partitions.descs) {
ffa_err("no partition installed");
return -EINVAL;
- }
- if (!uuid_str) {
ffa_err("no UUID provided");
return -EINVAL;
- }
- if (!parts_size) {
ffa_err("no size/count provided");
return -EINVAL;
- }
- if (be_uuid_str_to_le_bin(uuid_str, (unsigned char *)&part_uuid)) {
ffa_err("invalid UUID");
return -EINVAL;
- }
- if (!buffer) {
/* Mode 1: getting the number of secure partitions */
fill_data = 0;
ffa_info("Preparing for checking partitions count");
- } else if ((*parts_size >= sizeof(struct ffa_partition_info)) &&
!(*parts_size % sizeof(struct ffa_partition_info))) {
/* Mode 2: retrieving the partitions information */
fill_data = 1;
client_desc_idx = 0;
/*
* number of empty descriptors preallocated by the caller
*/
client_desc_max_cnt = *parts_size / sizeof(struct ffa_partition_info);
ffa_info("Preparing for filling partitions info");
- } else {
ffa_err("invalid function arguments provided");
return -EINVAL;
- }
- ffa_info("Searching partitions using the provided UUID");
- /*
* search in the cached partitions
*/
- for (desc_idx = 0;
desc_idx < ffa_priv_data->partitions.count;
desc_idx++) {
if (ffa_uuid_are_identical(&ffa_priv_data->partitions.descs[desc_idx].sp_uuid,
&part_uuid)) {
ffa_info("Partition ID %x matches the provided UUID",
ffa_priv_data->partitions.descs[desc_idx].info.id);
parts_found++;
if (fill_data) {
/*
* trying to fill the partition info in the input buffer
*/
if (client_desc_idx < client_desc_max_cnt) {
buffer[client_desc_idx++] =
ffa_priv_data->partitions.descs[desc_idx].info;
continue;
}
ffa_err("failed to fill the current descriptor client buffer full");
return -ENOBUFS;
}
}
- }
- if (!parts_found) {
int ret;
ffa_info("No partition found. Querying framework ...");
ret = ffa_query_partitions_info(&part_uuid, &parts_found);
if (ret == 0) {
if (!fill_data) {
*parts_size = parts_found;
ffa_info("Number of partition(s) found matching the UUID: %d",
parts_found);
} else {
/*
* If SPs data detected, they are already in the private data
* structure, retry searching SP data again to return them
* to the caller
*/
if (parts_found)
ret = ffa_get_partitions_info(uuid_str, parts_size, buffer);
else
ret = -ENODATA;
}
}
return ret;
- }
- /* partition(s) found */
- if (!fill_data)
*parts_size = parts_found;
- return 0;
+}
+/**
- ffa_cache_partitions_info - Queries and saves all secure partitions data
- This function invokes 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 the
- resident private data structure and we keep the UUID field empty
- (in FF-A 1.0 UUID is not provided by the partition descriptor)
- This function is 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(void) +{
- return ffa_query_partitions_info(NULL, NULL);
+}
+/**
- ffa_msg_send_direct_req - FFA_MSG_SEND_DIRECT_{REQ,RESP} handler function
- @dst_part_id: destination partition ID
- @msg: pointer to the message data preallocated by the client (in/out)
- 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
- */
+static int ffa_msg_send_direct_req(u16 dst_part_id, struct ffa_send_direct_data *msg) +{
- ffa_value_t res = {0};
- int ffa_errno;
- if (!ffa_priv_data || !ffa_priv_data->invoke_ffa_fn)
return -EINVAL;
- /* No partition installed */
- if (!ffa_priv_data->partitions.count || !ffa_priv_data->partitions.descs)
return -ENODEV;
- ffa_priv_data->invoke_ffa_fn((ffa_value_t){
.a0 = FFA_SMC_64(FFA_MSG_SEND_DIRECT_REQ),
.a1 = PREP_SELF_ENDPOINT_ID(ffa_priv_data->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))
ffa_priv_data->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 == FFA_SMC_64(FFA_MSG_SEND_DIRECT_RESP)) {
/*
* 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);
+}
+/**
- __arm_ffa_fn_smc - 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
- Return: void
- */
+void __arm_ffa_fn_smc(ffa_value_t args, ffa_value_t *res) +{
- arm_smccc_1_2_smc(&args, res);
+}
+/**
- ffa_set_smc_conduit - Set the SMC conduit
- This function selects the SMC conduit by setting the driver invoke function
- to SMC assembly function
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_set_smc_conduit(void) +{
- ffa_priv_data->invoke_ffa_fn = __arm_ffa_fn_smc;
- if (!ffa_priv_data->invoke_ffa_fn) {
ffa_err("failure to set the invoke function");
return -EINVAL;
- }
- ffa_info("Conduit is SMC");
- return 0;
+}
+/**
- ffa_set_bus_ops - Set the bus driver operations
- Setting the driver callbacks.
- */
+static void ffa_set_bus_ops(void) +{
- ffa_priv_data->ffa_ops.partition_info_get = ffa_get_partitions_info;
- ffa_priv_data->ffa_ops.sync_send_receive = ffa_msg_send_direct_req;
- ffa_priv_data->ffa_ops.rxtx_unmap = ffa_unmap_rxtx_buffers;
+}
+/**
- ffa_alloc_prvdata - allocate the driver main data structure and sets the device
- @dev: the arm_ffa device
- This function creates the main data structure embedding all the driver data.
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_alloc_prvdata(struct udevice *dev) +{
- if (!dev) {
ffa_err("no udevice found");
return -ENODEV;
- }
- /* The device is registered with the DM. Let's create the driver main data structure*/
- ffa_priv_data = devm_kmalloc(dev, sizeof(struct ffa_prvdata), __GFP_ZERO);
- if (!ffa_priv_data) {
ffa_err("can not allocate the driver main data structure");
return -ENOMEM;
- }
- ffa_priv_data->dev = dev;
- return 0;
+}
+/**
- ffa_probe - The driver probe function
- @dev: the arm_ffa device
- Probing is done at boot time and triggered by the uclass device discovery.
- At probe level the following actions are done:
- setting the conduit
- querying the FF-A framework version
- 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 the resident private data structure.
- The probe will fail if either FF-A framework is not detected or the
- FF-A requests are not behaving correctly. This ensures that the
- driver is not installed and its operations are not exported to the clients.
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_probe(struct udevice *dev) +{
- int ret;
- ret = ffa_alloc_prvdata(dev);
- if (ret != 0)
return ret;
- ffa_set_bus_ops();
- ret = ffa_set_smc_conduit();
- if (ret != 0)
return ret;
- ret = ffa_get_version();
- if (ret != 0)
return ret;
- ret = ffa_get_endpoint_id();
- if (ret != 0)
return ret;
- ret = ffa_get_rxtx_map_features();
- if (ret != 0)
return ret;
- ret = ffa_map_rxtx_buffers();
- if (ret != 0)
return ret;
- ret = ffa_cache_partitions_info();
I've asked this before, but never got an answer: Why are we saving all the found partitions in a cache? It seems that FFA_PARTITION_INFO_GET could be called each time when needed instead without any noticeable overhead. Or is the result cached for some other reason?
Cheers, Jens
- if (ret != 0) {
ffa_free_rxtx_buffers();
return ret;
- }
- return 0;
+}
+/**
- ffa_remove - The driver remove function
- @dev: the arm_ffa device
- When the device is about to be removed , unmap the RX/TX buffers and free the memory
- Return:
- 0 on success.
- */
+static int ffa_remove(struct udevice *dev) +{
- ffa_info("removing the device");
- ffa_unmap_rxtx_buffers();
- if (ffa_priv_data->pair.rxbuf || ffa_priv_data->pair.txbuf)
ffa_free_rxtx_buffers();
- return 0;
+}
+/**
- ffa_unbind - The driver unbind function
- @dev: the arm_ffa device
- After the device is removed and memory freed the device is unbound
- Return:
- 0 on success.
- */
+static int ffa_unbind(struct udevice *dev) +{
- ffa_info("unbinding the device , private data already released");
- ffa_priv_data = NULL;
- return 0;
+}
+/**
- ffa_bus_ops_get - bus driver operations getter
- Return:
- This function returns a pointer to the driver operations structure
- */
+const struct ffa_bus_ops *ffa_bus_ops_get(void) +{
- return &ffa_priv_data->ffa_ops;
+}
+/**
- ffa_bus_prvdata_get - bus driver private data getter
- Return:
- This function returns a pointer to the main private data structure
- */
+struct ffa_prvdata **ffa_bus_prvdata_get(void) +{
- return &ffa_priv_data;
+}
+/**
- ffa_bus_discover - discover FF-A bus and probe the arm_ffa device
- This function makes sure the FF-A bus is discoverable.
- When probing succeeds FF-A discovery is done. The arm_ffa device is ready to use.
- When the bus was already discovered successfully the discovery will not run again.
- Arm FF-A transport is implemented through arm_ffa u-boot device managing the FF-A
- communication.
- All FF-A clients should use the arm_ffa device to use the FF-A transport.
- Return:
- 0 on success. Otherwise, failure
- */
+int ffa_bus_discover(void) +{
- int ret = 0;
- if (!ffa_priv_data)
ret = ffa_device_get();
- return ret;
+}
+/**
- Declaring the arm_ffa driver under UCLASS_FFA
- */
+U_BOOT_DRIVER(arm_ffa) = {
- .name = FFA_DRV_NAME,
- .id = UCLASS_FFA,
- .probe = ffa_probe,
- .remove = ffa_remove,
- .unbind = ffa_unbind,
+}; diff --git a/include/arm_ffa.h b/include/arm_ffa.h new file mode 100644 index 0000000000..d146e7b328 --- /dev/null +++ b/include/arm_ffa.h @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#ifndef __ARM_FFA_H +#define __ARM_FFA_H
+#include <linux/printk.h>
+/*
- This header is public. It can be used by clients to access
- data structures and definitions they need
- */
+/*
- Macros for displaying logs
- */
+#define ffa_info(fmt, ...) pr_info("[FFA] " fmt "\n", ##__VA_ARGS__) +#define ffa_err(fmt, ...) pr_err("[FFA] " fmt "\n", ##__VA_ARGS__)
+/*
- 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 __packed 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_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 __packed ffa_send_direct_data {
- unsigned long data0; /* w3/x3 */
- unsigned long data1; /* w4/x4 */
- unsigned long data2; /* w5/x5 */
- unsigned long data3; /* w6/x6 */
- unsigned long data4; /* w7/x7 */
+};
+/**
- struct ffa_bus_ops - The driver operations structure
- @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)(const char *uuid_str,
u32 *parts_size, struct ffa_partition_info *buffer);
- int (*sync_send_receive)(u16 dst_part_id, struct ffa_send_direct_data *msg);
- int (*rxtx_unmap)(void);
+};
+/**
- The device driver and the Uclass driver public functions
- */
+/**
- ffa_bus_ops_get - driver operations getter
- */
+const struct ffa_bus_ops *ffa_bus_ops_get(void);
+/**
- ffa_bus_discover - discover FF-A bus and probes the arm_ffa device
- */
+int ffa_bus_discover(void);
+#endif diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index a432e43871..5dd698b7a9 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -4,6 +4,9 @@
- (C) Copyright 2012
- Pavel Herrmann morpheus.ibis@gmail.com
- (C) Copyright 2022 ARM Limited
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#ifndef _DM_UCLASS_ID_H @@ -55,6 +58,7 @@ enum uclass_id { UCLASS_EFI_MEDIA, /* Devices provided by UEFI firmware */ UCLASS_ETH, /* Ethernet device */ UCLASS_ETH_PHY, /* Ethernet PHY device */
- UCLASS_FFA, /* Arm Firmware Framework for Armv8-A */ UCLASS_FIRMWARE, /* Firmware */ UCLASS_FUZZING_ENGINE, /* Fuzzing engine */ UCLASS_FS_FIRMWARE_LOADER, /* Generic loader */
-- 2.17.1

unmap RX/TX buffers at ExitBootServices()
Unmapping the RX/TX buffers created by u-boot is needed before EFI runtime.
At EFI runtime the linux kernel takes care of allocating its own RX/TX buffers and registering them with the secure world.
Secure world should be using the RX/TX buffers created by the kernel. So, RX/TX buffers created by u-boot must be unmapped.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org --- lib/efi_loader/efi_boottime.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index a56021559b..2054b33568 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -3,6 +3,9 @@ * EFI application boot time services * * Copyright (c) 2016 Alexander Graf + * + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> @@ -23,6 +26,10 @@ #include <asm/setjmp.h> #include <linux/libfdt_env.h>
+#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT) +#include <arm_ffa.h> +#endif + DECLARE_GLOBAL_DATA_PTR;
/* Task priority level */ @@ -2178,6 +2185,14 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL); }
+#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT) + /* unmap FF-A RX/TX buffers */ + if (ffa_bus_ops_get()->rxtx_unmap()) + debug("[efi_boottime][ERROR]: can not unmap FF-A RX/TX buffers\n"); + else + debug("[efi_boottime][INFO]: FF-A RX/TX buffers unmapped\n"); +#endif + /* Patch out unsupported runtime function */ efi_runtime_detach();

On Thu, Oct 13, 2022 at 11:38:51AM +0100, Abdellatif El Khlifi wrote:
unmap RX/TX buffers at ExitBootServices()
Unmapping the RX/TX buffers created by u-boot is needed before EFI runtime.
At EFI runtime the linux kernel takes care of allocating its own RX/TX buffers and registering them with the secure world.
Secure world should be using the RX/TX buffers created by the kernel. So, RX/TX buffers created by u-boot must be unmapped.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
lib/efi_loader/efi_boottime.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index a56021559b..2054b33568 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -3,6 +3,9 @@
- EFI application boot time services
- Copyright (c) 2016 Alexander Graf
- (C) Copyright 2022 ARM Limited
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#include <common.h> @@ -23,6 +26,10 @@ #include <asm/setjmp.h> #include <linux/libfdt_env.h>
+#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT) +#include <arm_ffa.h> +#endif
DECLARE_GLOBAL_DATA_PTR;
/* Task priority level */ @@ -2178,6 +2185,14 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL); }
+#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT)
/* unmap FF-A RX/TX buffers */
if (ffa_bus_ops_get()->rxtx_unmap())
debug("[efi_boottime][ERROR]: can not unmap FF-A RX/TX buffers\n");
Just do log_err() here and get rid of the else
else
debug("[efi_boottime][INFO]: FF-A RX/TX buffers unmapped\n");
+#endif
- /* Patch out unsupported runtime function */ efi_runtime_detach();
-- 2.17.1
Thanks /Ilias

On Mon, Oct 24, 2022 at 03:08:53PM +0300, Ilias Apalodimas wrote:
On Thu, Oct 13, 2022 at 11:38:51AM +0100, Abdellatif El Khlifi wrote:
unmap RX/TX buffers at ExitBootServices()
Unmapping the RX/TX buffers created by u-boot is needed before EFI runtime.
At EFI runtime the linux kernel takes care of allocating its own RX/TX buffers and registering them with the secure world.
Secure world should be using the RX/TX buffers created by the kernel. So, RX/TX buffers created by u-boot must be unmapped.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
lib/efi_loader/efi_boottime.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index a56021559b..2054b33568 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -3,6 +3,9 @@
- EFI application boot time services
- Copyright (c) 2016 Alexander Graf
- (C) Copyright 2022 ARM Limited
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#include <common.h> @@ -23,6 +26,10 @@ #include <asm/setjmp.h> #include <linux/libfdt_env.h>
+#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT) +#include <arm_ffa.h> +#endif
DECLARE_GLOBAL_DATA_PTR;
/* Task priority level */ @@ -2178,6 +2185,14 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL); }
+#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT)
/* unmap FF-A RX/TX buffers */
if (ffa_bus_ops_get()->rxtx_unmap())
debug("[efi_boottime][ERROR]: can not unmap FF-A RX/TX buffers\n");
Just do log_err() here and get rid of the else
Thanks. Done in v7.
else
debug("[efi_boottime][INFO]: FF-A RX/TX buffers unmapped\n");
+#endif
- /* Patch out unsupported runtime function */ efi_runtime_detach();
-- 2.17.1
Thanks /Ilias

Provide armffa command showcasing the use of the FF-A driver
The armffa command allows to query secure partitions data from the secure world and exchanging messages with the partitions.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v4:
* remove pattern data in do_ffa_msg_send_direct_req
v3:
* use the new driver interfaces (partition_info_get, sync_send_receive) in armffa command
v2:
* replace use of ffa_helper_init_device function by ffa_helper_bus_discover
v1:
* introduce armffa command
MAINTAINERS | 1 + cmd/Kconfig | 10 ++ cmd/Makefile | 2 + cmd/armffa.c | 242 +++++++++++++++++++++++++++++++ drivers/firmware/arm-ffa/Kconfig | 1 + 5 files changed, 256 insertions(+) create mode 100644 cmd/armffa.c
diff --git a/MAINTAINERS b/MAINTAINERS index 496f47a516..663e2abccd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -251,6 +251,7 @@ F: configs/cortina_presidio-asic-pnand_defconfig ARM FF-A M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com S: Maintained +F: cmd/armffa.c F: doc/arch/arm64.ffa.rst F: drivers/firmware/arm-ffa/ F: include/arm_ffa.h diff --git a/cmd/Kconfig b/cmd/Kconfig index 6f00bd9307..6b1ffee3cf 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -912,6 +912,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 Arm FF-A driver + 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 cf6ce1bd6f..7d806817b1 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -12,6 +12,8 @@ obj-y += panic.o obj-y += version.o
# command + +obj-$(CONFIG_CMD_ARMFFA) += armffa.o obj-$(CONFIG_CMD_ACPI) += acpi.o obj-$(CONFIG_CMD_ADDRMAP) += addrmap.o obj-$(CONFIG_CMD_AES) += aes.o diff --git a/cmd/armffa.c b/cmd/armffa.c new file mode 100644 index 0000000000..9b56e8a830 --- /dev/null +++ b/cmd/armffa.c @@ -0,0 +1,242 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <arm_ffa.h> +#include <asm/io.h> +#include <common.h> +#include <command.h> +#include <dm.h> +#include <mapmem.h> +#include <stdlib.h> + +/** + * do_ffa_get_singular_partition_info - implementation of the getpart subcommand + * @cmdtp: Command Table + * @flag: flags + * @argc: number of arguments + * @argv: arguments + * + * This function queries the secure partition information which the UUID is provided + * as an argument. The function uses the arm_ffa driver partition_info_get operation + * 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_get_singular_partition_info(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + u32 count = 0, size = 0; + int ret; + struct ffa_partition_info *parts_info; + u32 info_idx; + + if (argc != 1) + return -EINVAL; + + /* Mode 1: getting the number of secure partitions */ + ret = ffa_bus_ops_get()->partition_info_get(argv[0], &count, NULL); + if (ret != 0) { + ffa_err("Failure in querying partitions count (error code: %d)", ret); + return ret; + } + + if (!count) { + ffa_info("No secure partition found"); + return ret; + } + + /* + * pre-allocate a buffer to be filled by the driver + * with ffa_partition_info structs + */ + + ffa_info("Pre-allocating %d partition(s) info structures", count); + + parts_info = calloc(count, sizeof(struct ffa_partition_info)); + if (!parts_info) + return -EINVAL; + + size = count * sizeof(struct ffa_partition_info); + + /* + * ask the driver to fill the buffer with the SPs info + */ + + ret = ffa_bus_ops_get()->partition_info_get(argv[0], &size, parts_info); + if (ret != 0) { + ffa_err("Failure in querying partition(s) info (error code: %d)", ret); + free(parts_info); + return ret; + } + + /* + * SPs found , show the partition information + */ + for (info_idx = 0; info_idx < count ; info_idx++) { + ffa_info("Partition: id = 0x%x , exec_ctxt 0x%x , properties 0x%x", + parts_info[info_idx].id, + parts_info[info_idx].exec_ctxt, + parts_info[info_idx].properties); + } + + free(parts_info); + + return 0; +} + +/** + * do_ffa_msg_send_direct_req - implementation of the ping subcommand + * @cmdtp: Command Table + * @flag: flags + * @argc: number of arguments + * @argv: arguments + * + * This function sends data to the secure partition which the ID is provided + * as an argument. The function uses the arm_ffa driver sync_send_receive operation + * to send data. + * + * Return: + * + * CMD_RET_SUCCESS: on success, otherwise failure + */ +int do_ffa_msg_send_direct_req(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; + + if (argc != 1) + return -EINVAL; + + errno = 0; + part_id = strtoul(argv[0], NULL, 16); + + if (errno) { + ffa_err("Invalid partition ID"); + return -EINVAL; + } + + ret = ffa_bus_ops_get()->sync_send_receive(part_id, &msg); + if (ret == 0) { + u8 cnt; + + ffa_info("SP response:\n[LSB]"); + for (cnt = 0; + cnt < sizeof(struct ffa_send_direct_data) / sizeof(u64); + cnt++) + ffa_info("0x%llx", ((u64 *)&msg)[cnt]); + } else { + ffa_err("Sending direct request error (%d)", ret); + } + + return ret; +} + +/** + *do_ffa_dev_list - implementation of the devlist subcommand + * @cmdtp: [in] Command Table + * @flag: flags + * @argc: number of arguments + * @argv: arguments + * + * This function queries the devices belonging to the UCLASS_FFA + * class. Currently, one device is expected to show up: the arm_ffa device + * + * Return: + * + * CMD_RET_SUCCESS: on success, otherwise failure + */ +int do_ffa_dev_list(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct udevice *dev = NULL; + int i, ret; + + ffa_info("arm_ffa uclass entries:"); + + for (i = 0, ret = uclass_first_device(UCLASS_FFA, &dev); + dev; + ret = uclass_next_device(&dev), i++) { + if (ret) + break; + + ffa_info("entry %d - instance %08x, ops %08x, plat %08x", + i, + (u32)map_to_sysmem(dev), + (u32)map_to_sysmem(dev->driver->ops), + (u32)map_to_sysmem(dev_get_plat(dev))); + } + + return cmd_process_error(cmdtp, ret); +} + +static struct cmd_tbl armffa_commands[] = { + U_BOOT_CMD_MKENT(getpart, 1, 1, do_ffa_get_singular_partition_info, "", ""), + U_BOOT_CMD_MKENT(ping, 1, 1, do_ffa_msg_send_direct_req, "", ""), + U_BOOT_CMD_MKENT(devlist, 0, 1, do_ffa_dev_list, "", ""), +}; + +/** + * do_armffa - the armffa command main function + * @cmdtp: Command Table + * @flag: flags + * @argc: number of arguments + * @argv: arguments + * + * This function identifies which armffa subcommand to run. + * Then, it makes sure the arm_ffa device is probed and + * ready for use. + * Then, it runs the subcommand. + * + * Return: + * + * CMD_RET_SUCCESS: on success, otherwise failure + */ +static int do_armffa(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct cmd_tbl *armffa_cmd; + int ret; + + if (argc < 2) + return CMD_RET_USAGE; + + armffa_cmd = find_cmd_tbl(argv[1], armffa_commands, ARRAY_SIZE(armffa_commands)); + + argc -= 2; + argv += 2; + + if (!armffa_cmd || argc > armffa_cmd->maxargs) + return CMD_RET_USAGE; + + ret = ffa_bus_discover(); + if (ret != 0) + return cmd_process_error(cmdtp, ret); + + if (!ffa_bus_ops_get()) + return -EINVAL; + + ret = armffa_cmd->cmd(armffa_cmd, flag, argc, argv); + + return cmd_process_error(armffa_cmd, ret); +} + +U_BOOT_CMD(armffa, 4, 1, do_armffa, + "Arm FF-A operations test command", + "getpart <partition UUID>\n" + " - lists the partition(s) info\n" + "ping <partition ID>\n" + " - sends a data pattern to the specified partition\n" + "devlist\n" + " - displays the arm_ffa device info\n"); diff --git a/drivers/firmware/arm-ffa/Kconfig b/drivers/firmware/arm-ffa/Kconfig index f1427535f9..624e834b1f 100644 --- a/drivers/firmware/arm-ffa/Kconfig +++ b/drivers/firmware/arm-ffa/Kconfig @@ -4,6 +4,7 @@ config ARM_FFA_TRANSPORT bool "Enable Arm Firmware Framework for Armv8-A driver" depends on DM && ARM64 select ARM_SMCCC + select CMD_ARMFFA select LIB_UUID select DEVRES help

Provide a Sandbox driver to emulate the FF-A ABIs
The emulated ABIs are those supported by the FF-A core driver and according to FF-A specification v1.0.
The Sandbox driver provides operations allowing the test application to read the status of all the inspected ABIs and perform functional tests based on that.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v4: align sandbox driver with the new FF-A driver interfaces and new way of error handling
v1: introduce the sandbox driver
MAINTAINERS | 1 + configs/sandbox64_defconfig | 2 + configs/sandbox_defconfig | 2 + doc/arch/sandbox.rst | 1 + drivers/firmware/arm-ffa/Kconfig | 9 +- drivers/firmware/arm-ffa/Makefile | 1 + drivers/firmware/arm-ffa/arm_ffa_prv.h | 15 +- drivers/firmware/arm-ffa/core.c | 24 +- drivers/firmware/arm-ffa/sandbox.c | 659 ++++++++++++++++++ .../firmware/arm-ffa/sandbox_arm_ffa_prv.h | 144 ++++ include/arm_ffa.h | 2 +- include/sandbox_arm_ffa.h | 91 +++ lib/efi_loader/efi_boottime.c | 2 +- 13 files changed, 939 insertions(+), 14 deletions(-) create mode 100644 drivers/firmware/arm-ffa/sandbox.c create mode 100644 drivers/firmware/arm-ffa/sandbox_arm_ffa_prv.h create mode 100644 include/sandbox_arm_ffa.h
diff --git a/MAINTAINERS b/MAINTAINERS index 663e2abccd..598ae76e16 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -255,6 +255,7 @@ F: cmd/armffa.c 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 sbabic@denx.de diff --git a/configs/sandbox64_defconfig b/configs/sandbox64_defconfig index b20b181ab1..cd5575ce61 100644 --- a/configs/sandbox64_defconfig +++ b/configs/sandbox64_defconfig @@ -250,3 +250,5 @@ CONFIG_TEST_FDTDEC=y CONFIG_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y +CONFIG_ARM_FFA_TRANSPORT=y +CONFIG_SANDBOX_FFA=y \ No newline at end of file diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index df6a28ef24..27eb2f7ab3 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -327,3 +327,5 @@ CONFIG_TEST_FDTDEC=y CONFIG_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y +CONFIG_ARM_FFA_TRANSPORT=y +CONFIG_SANDBOX_FFA=y \ No newline at end of file diff --git a/doc/arch/sandbox.rst b/doc/arch/sandbox.rst index 068d4a3be4..5d7e1b2c48 100644 --- a/doc/arch/sandbox.rst +++ b/doc/arch/sandbox.rst @@ -203,6 +203,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 624e834b1f..343f6499d0 100644 --- a/drivers/firmware/arm-ffa/Kconfig +++ b/drivers/firmware/arm-ffa/Kconfig @@ -2,8 +2,8 @@
config ARM_FFA_TRANSPORT bool "Enable Arm Firmware Framework for Armv8-A driver" - depends on DM && ARM64 - select ARM_SMCCC + depends on DM && (ARM64 || SANDBOX) + select ARM_SMCCC if !SANDBOX select CMD_ARMFFA select LIB_UUID select DEVRES @@ -29,3 +29,8 @@ config ARM_FFA_TRANSPORT
For more details about the FF-A driver, please refer to doc/README.ffa.drv
+config SANDBOX_FFA + bool "FF-A Sandbox driver" + depends on ARM_FFA_TRANSPORT && SANDBOX + help + This emulates the FF-A handling under Sandbox and allows to test the FF-A driver diff --git a/drivers/firmware/arm-ffa/Makefile b/drivers/firmware/arm-ffa/Makefile index 043a8915be..0d21d6b47a 100644 --- a/drivers/firmware/arm-ffa/Makefile +++ b/drivers/firmware/arm-ffa/Makefile @@ -4,3 +4,4 @@ # Abdellatif El Khlifi, Arm Limited, abdellatif.elkhlifi@arm.com.
obj-y += arm-ffa-uclass.o core.o +obj-$(CONFIG_SANDBOX_FFA) += sandbox.o diff --git a/drivers/firmware/arm-ffa/arm_ffa_prv.h b/drivers/firmware/arm-ffa/arm_ffa_prv.h index 7bc90f7f66..3e0d4c112c 100644 --- a/drivers/firmware/arm-ffa/arm_ffa_prv.h +++ b/drivers/firmware/arm-ffa/arm_ffa_prv.h @@ -19,6 +19,16 @@ /* FF-A core driver name */ #define FFA_DRV_NAME "arm_ffa"
+/* The FF-A SMC function definitions */ + +#if CONFIG_IS_ENABLED(SANDBOX_FFA) +#include "sandbox_arm_ffa.h" +#else +typedef struct arm_smccc_1_2_regs ffa_value_t; +#endif + +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) @@ -94,11 +104,6 @@ struct ffa_abi_errmap { #define FFA_ERRMAP_COUNT (FFA_LAST_ID - FFA_FIRST_ID + 1) #define FFA_ID_TO_ERRMAP_ID(ffa_id) ((ffa_id) - FFA_FIRST_ID)
-/* The FF-A SMC function definitions */ - -typedef struct arm_smccc_1_2_regs ffa_value_t; -typedef void (*invoke_ffa_fn_t)(ffa_value_t args, ffa_value_t *res); - /* * struct ffa_partition_uuid - 16 bytes UUID transmitted by FFA_PARTITION_INFO_GET * @a1-4: 32-bit words access to the UUID data diff --git a/drivers/firmware/arm-ffa/core.c b/drivers/firmware/arm-ffa/core.c index 324367d12b..2810e4a636 100644 --- a/drivers/firmware/arm-ffa/core.c +++ b/drivers/firmware/arm-ffa/core.c @@ -1096,6 +1096,7 @@ static int ffa_msg_send_direct_req(u16 dst_part_id, struct ffa_send_direct_data return ffa_to_std_errno(ffa_errno); }
+#if !CONFIG_IS_ENABLED(SANDBOX_FFA) /** * __arm_ffa_fn_smc - SMC wrapper * @args: FF-A ABI arguments to be copied to Xn registers @@ -1109,6 +1110,7 @@ void __arm_ffa_fn_smc(ffa_value_t args, ffa_value_t *res) { arm_smccc_1_2_smc(&args, res); } +#endif
/** * ffa_set_smc_conduit - Set the SMC conduit @@ -1122,7 +1124,12 @@ void __arm_ffa_fn_smc(ffa_value_t args, ffa_value_t *res) */ static int ffa_set_smc_conduit(void) { - ffa_priv_data->invoke_ffa_fn = __arm_ffa_fn_smc; +#if CONFIG_IS_ENABLED(SANDBOX_FFA) + ffa_priv_data->invoke_ffa_fn = sandbox_arm_ffa_smccc_smc; + ffa_info("Using SMC emulation"); +#else + ffa_priv_data->invoke_ffa_fn = __arm_ffa_fn_smc; +#endif
if (!ffa_priv_data->invoke_ffa_fn) { ffa_err("failure to set the invoke function"); @@ -1299,15 +1306,16 @@ struct ffa_prvdata **ffa_bus_prvdata_get(void) }
/** - * ffa_bus_discover - discover FF-A bus and probe the arm_ffa device + * ffa_bus_discover - discover FF-A bus and probe arm_ffa and sandbox_arm_ffa devices * * This function makes sure the FF-A bus is discoverable. - * When probing succeeds FF-A discovery is done. The arm_ffa device is ready to use. + * When probing succeeds FF-A discovery is done. The arm_ffa and sandbox_arm_ffa devices + * are ready to use. * * When the bus was already discovered successfully the discovery will not run again. * * Arm FF-A transport is implemented through arm_ffa u-boot device managing the FF-A - * communication. + * communication. In Sandbox mode sandbox_arm_ffa is used to test arm_ffa driver. * All FF-A clients should use the arm_ffa device to use the FF-A transport. * * Return: @@ -1318,9 +1326,15 @@ int ffa_bus_discover(void) { int ret = 0;
- if (!ffa_priv_data) + if (!ffa_priv_data) { ret = ffa_device_get();
+#if CONFIG_IS_ENABLED(SANDBOX_FFA) + if (ret == 0) + ret = sandbox_ffa_device_get(); +#endif + } + return ret; }
diff --git a/drivers/firmware/arm-ffa/sandbox.c b/drivers/firmware/arm-ffa/sandbox.c new file mode 100644 index 0000000000..16e1fdc809 --- /dev/null +++ b/drivers/firmware/arm-ffa/sandbox.c @@ -0,0 +1,659 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include "sandbox_arm_ffa_prv.h" +#include <asm/global_data.h> +#include <common.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> +#include <mapmem.h> +#include <string.h> + +DECLARE_GLOBAL_DATA_PTR; + +/** + * The device private data structure containing all the emulated secure world data + */ +static struct sandbox_ffa_prvdata sandbox_ffa_priv_data = {0}; + +/* 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_SP2_ID, .exec_ctxt = 0x9587, .properties = 0x45325621 }, + .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_SP3_ID, .exec_ctxt = 0x7687, .properties = 0x23325621 }, + .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, + } + } + +}; + +/* + * Driver functions + */ + +/** + * sandbox_ffa_get_device - probes the sandbox_arm_ffa device + * + * This function makes sure the sandbox_arm_ffa device is probed + * This function makes sure the sandbox_arm_ffa device is + * created, bound to this driver, probed and ready to use. + * + * sandbox_arm_ffa depends on arm_ffa device. This dependency is + * handled by ffa_bus_discover function. arm_ffa is probed first then + * sandbox_arm_ffa. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int sandbox_ffa_device_get(void) +{ + int ret; + + if (sandbox_ffa_priv_data.dev) + return 0; + + ret = device_bind(dm_root(), + DM_DRIVER_GET(sandbox_arm_ffa), + FFA_SANDBOX_DRV_NAME, + NULL, + ofnode_null(), + &sandbox_ffa_priv_data.dev); + if (ret) { + sandbox_ffa_priv_data.dev = NULL; + return ret; + } + + ret = device_probe(sandbox_ffa_priv_data.dev); + if (ret) { + ffa_err("[Sandbox] can not probe the device"); + device_unbind(sandbox_ffa_priv_data.dev); + sandbox_ffa_priv_data.dev = NULL; + return ret; + } + + return 0; +} + +/** + * sandbox_ffa_version - Emulated FFA_VERSION handler function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_VERSION FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_version) +{ + sandbox_ffa_priv_data.fwk_version = FFA_VERSION_1_0; + res->a0 = sandbox_ffa_priv_data.fwk_version; + + /* x1-x7 MBZ */ + memset(FFA_X1X7_MBZ_REG_START, 0, FFA_X1X7_MBZ_CNT * sizeof(unsigned long)); + + return 0; +} + +/** + * sandbox_ffa_id_get - Emulated FFA_ID_GET handler function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_ID_GET FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_id_get) +{ + res->a0 = FFA_SMC_32(FFA_SUCCESS); + res->a1 = 0; + + sandbox_ffa_priv_data.id = NS_PHYS_ENDPOINT_ID; + res->a2 = sandbox_ffa_priv_data.id; + + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, 0, FFA_X3X7_MBZ_CNT * sizeof(unsigned long)); + + return 0; +} + +/** + * sandbox_ffa_features - Emulated FFA_FEATURES handler function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_FEATURES FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_features) +{ + 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(unsigned long)); + } else { + res->a0 = FFA_SMC_32(FFA_ERROR); + res->a2 = FFA_ERR_STAT_NOT_SUPPORTED; + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, + 0, FFA_X3X7_MBZ_CNT * sizeof(unsigned long)); + ffa_err("[Sandbox] FF-A interface 0x%lx not implemented", pargs->a1); + } + + res->a1 = 0; + + return 0; +} + +/** + * sandbox_ffa_partition_info_get - Emulated FFA_PARTITION_INFO_GET handler function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_PARTITION_INFO_GET FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_partition_info_get) +{ + struct ffa_partition_info *rxbuf_desc_info = NULL; + u32 descs_cnt; + u32 descs_size_bytes; + + res->a0 = FFA_SMC_32(FFA_ERROR); + + if (!sandbox_ffa_priv_data.pair.rxbuf) { + res->a2 = FFA_ERR_STAT_DENIED; + goto cleanup; + } + + if (sandbox_ffa_priv_data.pair_info.rxbuf_owned) { + res->a2 = FFA_ERR_STAT_BUSY; + goto cleanup; + } + + if (!sandbox_ffa_priv_data.partitions.descs) { + sandbox_ffa_priv_data.partitions.descs = sandbox_partitions; + sandbox_ffa_priv_data.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 descriptors buffer size */ + if ((sandbox_ffa_priv_data.pair_info.rxtx_buf_size * SZ_4K) < descs_size_bytes) { + res->a2 = FFA_ERR_STAT_NO_MEMORY; + goto cleanup; + } + + rxbuf_desc_info = (struct ffa_partition_info *)sandbox_ffa_priv_data.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++) = + sandbox_ffa_priv_data.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 */ + sandbox_ffa_priv_data.pair_info.rxbuf_owned = 1; + + goto cleanup; + } + + /* + * A UUID is specified. Return the information of all partitions matching + * the UUID + */ + + for (descs_cnt = 0 ; descs_cnt < SANDBOX_PARTITIONS_CNT ; descs_cnt++) + if (pargs->a1 == sandbox_ffa_priv_data.partitions.descs[descs_cnt].sp_uuid.a1 && + pargs->a2 == sandbox_ffa_priv_data.partitions.descs[descs_cnt].sp_uuid.a2 && + pargs->a3 == sandbox_ffa_priv_data.partitions.descs[descs_cnt].sp_uuid.a3 && + pargs->a4 == sandbox_ffa_priv_data.partitions.descs[descs_cnt].sp_uuid.a4) { + *(rxbuf_desc_info++) = + sandbox_ffa_priv_data.partitions.descs[descs_cnt].info; + } + + if (rxbuf_desc_info != ((struct ffa_partition_info *)sandbox_ffa_priv_data.pair.rxbuf)) { + res->a0 = FFA_SMC_32(FFA_SUCCESS); + /* store the partitions count */ + res->a2 = (unsigned long) + (rxbuf_desc_info - (struct ffa_partition_info *) + sandbox_ffa_priv_data.pair.rxbuf); + + /* transfer ownership to the consumer: the non secure world */ + sandbox_ffa_priv_data.pair_info.rxbuf_owned = 1; + } else { + /* Unrecognized UUID */ + res->a2 = FFA_ERR_STAT_INVALID_PARAMETERS; + } + +cleanup: + + ffa_err("[Sandbox] FFA_PARTITION_INFO_GET (%ld)", res->a2); + + res->a1 = 0; + + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, 0, FFA_X3X7_MBZ_CNT * sizeof(unsigned long)); + + return 0; +} + +/** + * sandbox_ffa_rxtx_map - Emulated FFA_RXTX_MAP handler function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_RXTX_MAP FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_rxtx_map) +{ + res->a0 = FFA_SMC_32(FFA_ERROR); + + if (sandbox_ffa_priv_data.pair.txbuf && sandbox_ffa_priv_data.pair.rxbuf) { + res->a2 = FFA_ERR_STAT_DENIED; + goto feedback; + } + + if (pargs->a3 >= RXTX_BUFFERS_MIN_PAGES && pargs->a1 && pargs->a2) { + sandbox_ffa_priv_data.pair.txbuf = pargs->a1; + sandbox_ffa_priv_data.pair.rxbuf = pargs->a2; + sandbox_ffa_priv_data.pair_info.rxtx_buf_size = pargs->a3; + sandbox_ffa_priv_data.pair_info.rxbuf_mapped = 1; + res->a0 = FFA_SMC_32(FFA_SUCCESS); + res->a2 = 0; + goto feedback; + } + + if (!pargs->a1 || !pargs->a2) + res->a2 = FFA_ERR_STAT_INVALID_PARAMETERS; + else + res->a2 = FFA_ERR_STAT_NO_MEMORY; + + ffa_err("[Sandbox] error in FFA_RXTX_MAP arguments (%d)", (int)res->a2); + +feedback: + + res->a1 = 0; + + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, + 0, FFA_X3X7_MBZ_CNT * sizeof(unsigned long)); + + return 0; +} + +/** + * sandbox_ffa_rxtx_unmap - Emulated FFA_RXTX_UNMAP handler function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_RXTX_UNMAP FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_rxtx_unmap) +{ + res->a0 = FFA_SMC_32(FFA_ERROR); + res->a2 = FFA_ERR_STAT_INVALID_PARAMETERS; + + if (GET_NS_PHYS_ENDPOINT_ID(pargs->a1) != sandbox_ffa_priv_data.id) + goto feedback; + + if (sandbox_ffa_priv_data.pair.txbuf && sandbox_ffa_priv_data.pair.rxbuf) { + sandbox_ffa_priv_data.pair.txbuf = 0; + sandbox_ffa_priv_data.pair.rxbuf = 0; + sandbox_ffa_priv_data.pair_info.rxtx_buf_size = 0; + sandbox_ffa_priv_data.pair_info.rxbuf_mapped = 0; + res->a0 = FFA_SMC_32(FFA_SUCCESS); + res->a2 = 0; + goto feedback; + } + + ffa_err("[Sandbox] No buffer pair registered on behalf of the caller"); + +feedback: + + res->a1 = 0; + + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, + 0, FFA_X3X7_MBZ_CNT * sizeof(unsigned long)); + + return 0; +} + +/** + * sandbox_ffa_rx_release - Emulated FFA_RX_RELEASE handler function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_RX_RELEASE FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_rx_release) +{ + if (!sandbox_ffa_priv_data.pair_info.rxbuf_owned) { + res->a0 = FFA_SMC_32(FFA_ERROR); + res->a2 = FFA_ERR_STAT_DENIED; + } else { + sandbox_ffa_priv_data.pair_info.rxbuf_owned = 0; + res->a0 = FFA_SMC_32(FFA_SUCCESS); + res->a2 = 0; + } + + res->a1 = 0; + + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, + 0, FFA_X3X7_MBZ_CNT * sizeof(unsigned long)); + + return 0; +} + +/** + * sandbox_ffa_sp_valid - Checks SP validity + * @part_id: partition ID to check + * + * This is the function searches the input ID in the descriptors table. + * + * Return: + * + * 1 on success (Partition found). Otherwise, failure + */ +static int sandbox_ffa_sp_valid(u16 part_id) +{ + u32 descs_cnt; + + for (descs_cnt = 0 ; descs_cnt < SANDBOX_PARTITIONS_CNT ; descs_cnt++) + if (sandbox_ffa_priv_data.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 function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_MSG_SEND_DIRECT_{REQ,RESP} + * FF-A functions. + * + * 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 + */ +SANDBOX_SMC_FFA_ABI(ffa_msg_send_direct_req) +{ + u16 part_id; + + part_id = GET_DST_SP_ID(pargs->a1); + + if ((GET_NS_PHYS_ENDPOINT_ID(pargs->a1) != sandbox_ffa_priv_data.id) || + !sandbox_ffa_sp_valid(part_id) || + pargs->a2) { + res->a0 = FFA_SMC_32(FFA_ERROR); + res->a1 = 0; + res->a2 = FFA_ERR_STAT_INVALID_PARAMETERS; + + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, + 0, FFA_X3X7_MBZ_CNT * sizeof(unsigned long)); + + return 0; + } + + res->a0 = FFA_SMC_64(FFA_MSG_SEND_DIRECT_RESP); + + res->a1 = PREP_SRC_SP_ID(part_id) | + PREP_NS_PHYS_ENDPOINT_ID(sandbox_ffa_priv_data.id); + + res->a2 = 0; + + /* + * return 0xff bytes as a response + */ + res->a3 = 0xffffffffffffffff; + res->a4 = 0xffffffffffffffff; + res->a5 = 0xffffffffffffffff; + res->a6 = 0xffffffffffffffff; + res->a7 = 0xffffffffffffffff; + + return 0; +} + +/** + * sandbox_ffa_get_prv_data - Returns the pointer to FF-A core pivate data + * @func_data: Pointer to the FF-A function arguments container structure + * + * This is the handler that returns the address of the FF-A core pivate data. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int sandbox_ffa_get_prv_data(struct ffa_sandbox_data *func_data) +{ + if (!func_data) + return -EINVAL; + + if (!func_data->data0 || func_data->data0_size != sizeof(struct ffa_prvdata **)) + return -EINVAL; + + if (!func_data->data1 || func_data->data1_size != sizeof(struct sandbox_ffa_prvdata **)) + return -EINVAL; + + *((struct ffa_prvdata **)func_data->data0) = *(ffa_bus_prvdata_get()); + *((struct sandbox_ffa_prvdata **)func_data->data1) = &sandbox_ffa_priv_data; + + return 0; +} + +/** + * sandbox_ffa_get_rxbuf_flags - Reading the mapping/ownership flags + * @queried_func_id: The FF-A function to be queried + * @func_data: Pointer to the FF-A function arguments container structure + * + * This is the handler that queries 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(u32 queried_func_id, struct ffa_sandbox_data *func_data) +{ + 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) = sandbox_ffa_priv_data.pair_info.rxbuf_mapped; + return 0; + case FFA_RX_RELEASE: + *((u8 *)func_data->data0) = sandbox_ffa_priv_data.pair_info.rxbuf_owned; + return 0; + default: + ffa_err("[Sandbox] The querried FF-A interface flag (%d) undefined", + queried_func_id); + return -EINVAL; + } +} + +/** + * sandbox_ffa_query_core_state - The driver dispatcher function + * @queried_func_id: The FF-A function to be queried + * @func_data: Pointer to the FF-A function arguments container structure + * + * Queries the status of FF-A ABI specified in the input argument. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int sandbox_ffa_query_core_state(u32 queried_func_id, struct ffa_sandbox_data *func_data) +{ + switch (queried_func_id) { + case FFA_VERSION: + case FFA_ID_GET: + case FFA_FEATURES: + return sandbox_ffa_get_prv_data(func_data); + case FFA_RXTX_MAP: + case FFA_RXTX_UNMAP: + case FFA_RX_RELEASE: + return sandbox_ffa_get_rxbuf_flags(queried_func_id, func_data); + default: + ffa_err("[Sandbox] The querried FF-A interface (%d) undefined", 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 + * + * Sandbox driver emulates the FF-A ABIs SMC call using this function. + * 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; + + switch (args.a0) { + case FFA_SMC_32(FFA_VERSION): + ret = sandbox_ffa_version(&args, res); + break; + case FFA_SMC_32(FFA_PARTITION_INFO_GET): + ret = sandbox_ffa_partition_info_get(&args, res); + break; + case FFA_SMC_32(FFA_RXTX_UNMAP): + ret = sandbox_ffa_rxtx_unmap(&args, res); + break; + case FFA_SMC_64(FFA_MSG_SEND_DIRECT_REQ): + ret = sandbox_ffa_msg_send_direct_req(&args, res); + break; + case FFA_SMC_32(FFA_ID_GET): + ret = sandbox_ffa_id_get(&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(&args, res); + break; + case FFA_SMC_32(FFA_RX_RELEASE): + ret = sandbox_ffa_rx_release(&args, res); + break; + default: + ffa_err("[Sandbox] Undefined FF-A interface (0x%lx)", args.a0); + } + + if (ret != 0) + ffa_err("[Sandbox] FF-A ABI internal failure (%d)", ret); +} + +/** + * sandbox_ffa_probe - The driver probe function + * @dev: the sandbox_arm_ffa device + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int sandbox_ffa_probe(struct udevice *dev) +{ + return 0; +} + +/** + * sandbox_ffa_remove - The driver remove function + * @dev: the sandbox_arm_ffa device + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int sandbox_ffa_remove(struct udevice *dev) +{ + ffa_info("[Sandbox] removing the device"); + memset(&sandbox_ffa_priv_data, 0, sizeof(sandbox_ffa_priv_data)); + return 0; +} + +/** + * Declaring the sandbox_arm_ffa driver under UCLASS_FFA + */ +U_BOOT_DRIVER(sandbox_arm_ffa) = { + .name = FFA_SANDBOX_DRV_NAME, + .id = UCLASS_FFA, + .probe = sandbox_ffa_probe, + .remove = sandbox_ffa_remove, +}; diff --git a/drivers/firmware/arm-ffa/sandbox_arm_ffa_prv.h b/drivers/firmware/arm-ffa/sandbox_arm_ffa_prv.h new file mode 100644 index 0000000000..4db57f5092 --- /dev/null +++ b/drivers/firmware/arm-ffa/sandbox_arm_ffa_prv.h @@ -0,0 +1,144 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __SANDBOX_ARM_FFA_PRV_H +#define __SANDBOX_ARM_FFA_PRV_H + +#include "arm_ffa_prv.h" +#include <sandbox_arm_ffa.h> + +/* + * This header is private. It is exclusively used by the Sandbox FF-A driver + */ + +/* FF-A core driver name */ +#define FFA_SANDBOX_DRV_NAME "sandbox_arm_ffa" + +/* FF-A ABIs internal error codes (as defined by the spec) */ + +#define FFA_ERR_STAT_NOT_SUPPORTED -1 +#define FFA_ERR_STAT_INVALID_PARAMETERS -2 +#define FFA_ERR_STAT_NO_MEMORY -3 +#define FFA_ERR_STAT_BUSY -4 +#define FFA_ERR_STAT_DENIED -6 + +/* Providing Arm SMCCC declarations to sandbox */ + +#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)) + +/* 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) + +/* secure partitions count */ +#define SANDBOX_PARTITIONS_CNT (4) + +/* 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: the consumer) + * @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 as set by the FF-A core driver + * + * Data structure hosting 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_prvdata - the driver private data structure + * + * @dev: The arm_ffa device under u-boot driver model + * @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 + * @conduit: The selected conduit + * + * The driver data structure hosting all the emulated secure world data. + */ +struct sandbox_ffa_prvdata { + struct udevice *dev; + u32 fwk_version; + u16 id; + struct ffa_partitions partitions; + struct ffa_rxtxpair pair; + struct ffa_rxtxpair_info pair_info; +}; + +#define SANDBOX_SMC_FFA_ABI(ffabi) static int sandbox_##ffabi(ffa_value_t *pargs, ffa_value_t *res) + +#endif diff --git a/include/arm_ffa.h b/include/arm_ffa.h index d146e7b328..6fa097d4ed 100644 --- a/include/arm_ffa.h +++ b/include/arm_ffa.h @@ -86,7 +86,7 @@ struct ffa_bus_ops { const struct ffa_bus_ops *ffa_bus_ops_get(void);
/** - * ffa_bus_discover - discover FF-A bus and probes the arm_ffa device + * ffa_bus_discover - discover FF-A bus and probes the arm_ffa and sandbox_arm_ffa devices */ int ffa_bus_discover(void);
diff --git a/include/sandbox_arm_ffa.h b/include/sandbox_arm_ffa.h new file mode 100644 index 0000000000..d5df16f282 --- /dev/null +++ b/include/sandbox_arm_ffa.h @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __SANDBOX_ARM_FFA_H +#define __SANDBOX_ARM_FFA_H + +#include <arm_ffa.h> + +/** + * struct sandbox_smccc_1_2_regs - Arguments for or Results from emulated SMC call + * @a0-a17 argument values from registers 0 to 17 + */ +struct sandbox_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; +}; + +typedef struct sandbox_smccc_1_2_regs ffa_value_t; + +/* UUIDs of services supported by the sandbox driver */ +#define SANDBOX_SERVICE1_UUID "ed32d533-4209-99e6-2d72-cdd998a79cc0" +#define SANDBOX_SERVICE2_UUID "ed32d544-4209-99e6-2d72-cdd998a79cc0" +#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" + +#define SANDBOX_SP_COUNT_PER_VALID_SERVICE 2 + +/** + * struct ffa_sandbox_data - generic data structure used to exchange + * data between test cases and the sandbox driver + * @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 + * + * Using this structure sandbox test cases can pass various types of data with different sizes. + */ +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 driver public functions + */ + +/** + * sandbox_ffa_query_core_state - Queries the status of FF-A ABIs + */ +int sandbox_ffa_query_core_state(u32 queried_func_id, struct ffa_sandbox_data *func_data); + +/** + * sandbox_ffa_get_device - create, bind and probe the sandbox_arm_ffa device + */ +int sandbox_ffa_device_get(void); + +/** + * sandbox_arm_ffa_smccc_smc - FF-A SMC call emulation + */ +void sandbox_arm_ffa_smccc_smc(ffa_value_t args, ffa_value_t *res); + +#endif diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 2054b33568..bea7ad7608 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -2185,7 +2185,7 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL); }
-#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT) +#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT) && !CONFIG_IS_ENABLED(SANDBOX_FFA) /* unmap FF-A RX/TX buffers */ if (ffa_bus_ops_get()->rxtx_unmap()) debug("[efi_boottime][ERROR]: can not unmap FF-A RX/TX buffers\n");

Add functional test cases for the FF-A core driver
These tests rely on the FF-A Sandbox driver which helps in inspecting the FF-A core driver.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v4: align sandbox tests with the new FF-A driver interfaces and new way of error handling
v1: introduce sandbox tests
MAINTAINERS | 1 + test/dm/Makefile | 2 + test/dm/ffa.c | 394 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 397 insertions(+) create mode 100644 test/dm/ffa.c
diff --git a/MAINTAINERS b/MAINTAINERS index 598ae76e16..bf198f4ce1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -256,6 +256,7 @@ F: doc/arch/arm64.ffa.rst F: drivers/firmware/arm-ffa/ F: include/arm_ffa.h F: include/sandbox_arm_ffa.h +F: test/dm/ffa.c
ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de diff --git a/test/dm/Makefile b/test/dm/Makefile index 5178daa7cf..e5bc4b4bd6 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0+ # # Copyright (c) 2013 Google, Inc +# (C) Copyright 2022 ARM Limited
obj-$(CONFIG_UT_DM) += test-dm.o
@@ -81,6 +82,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_SANDBOX_FFA) += 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 0000000000..052d5fc3f4 --- /dev/null +++ b/test/dm/ffa.c @@ -0,0 +1,394 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Functional tests for UCLASS_FFA class + * + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <console.h> +#include <dm.h> +#include <dm/test.h> +#include "../../drivers/firmware/arm-ffa/sandbox_arm_ffa_prv.h" +#include <sandbox_arm_ffa.h> +#include <test/test.h> +#include <test/ut.h> + +/* Macros */ + +#define LOG_MSG_SZ (100) +#define LOG_CMD_SZ (LOG_MSG_SZ * 2) + +/* Functional tests for the UCLASS_FFA */ + +static int dm_test_ffa_log(struct unit_test_state *uts, char *msg) +{ + char cmd[LOG_CMD_SZ] = {0}; + + console_record_reset(); + + snprintf(cmd, LOG_CMD_SZ, "echo "%s"", msg); + run_command(cmd, 0); + + ut_assert_console_end(); + + return CMD_RET_SUCCESS; +} + +static int check_fwk_version(struct ffa_prvdata *prvdata, struct sandbox_ffa_prvdata *sdx_prvdata, + struct unit_test_state *uts) +{ + if (prvdata->fwk_version != sdx_prvdata->fwk_version) { + char msg[LOG_MSG_SZ] = {0}; + + snprintf(msg, LOG_MSG_SZ, + "[%s]: Error: framework version: core = 0x%x , sandbox = 0x%x", __func__, + prvdata->fwk_version, + sdx_prvdata->fwk_version); + + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + return CMD_RET_SUCCESS; +} + +static int check_endpoint_id(struct ffa_prvdata *prvdata, struct unit_test_state *uts) +{ + if (prvdata->id) { + char msg[LOG_MSG_SZ] = {0}; + + snprintf(msg, LOG_MSG_SZ, + "[%s]: Error: endpoint id: core = 0x%x", __func__, prvdata->id); + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + return CMD_RET_SUCCESS; +} + +static int check_core_dev(struct ffa_prvdata *prvdata, struct unit_test_state *uts) +{ + if (!prvdata->dev) { + char msg[LOG_MSG_SZ] = {0}; + + snprintf(msg, LOG_MSG_SZ, "[%s]: Error: core device NULL", __func__); + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + return CMD_RET_SUCCESS; +} + +static int check_sandbox_dev(struct sandbox_ffa_prvdata *sdx_prvdata, struct unit_test_state *uts) +{ + if (!sdx_prvdata->dev) { + char msg[LOG_MSG_SZ] = {0}; + + snprintf(msg, LOG_MSG_SZ, "[%s]: Error: sandbox device NULL", __func__); + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + return CMD_RET_SUCCESS; +} + +static int check_rxtxbuf(struct ffa_prvdata *prvdata, struct unit_test_state *uts) +{ + if (!prvdata->pair.rxbuf && prvdata->pair.txbuf) { + char msg[LOG_MSG_SZ] = {0}; + + snprintf(msg, LOG_MSG_SZ, "[%s]: Error: rxbuf = 0x%llx txbuf = 0x%llx", __func__, + prvdata->pair.rxbuf, + prvdata->pair.txbuf); + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + return CMD_RET_SUCCESS; +} + +static int check_features(struct ffa_prvdata *prvdata, struct unit_test_state *uts) +{ + char msg[LOG_MSG_SZ] = {0}; + + if (prvdata->pair.rxtx_min_pages != RXTX_4K && + prvdata->pair.rxtx_min_pages != RXTX_16K && + prvdata->pair.rxtx_min_pages != RXTX_64K) { + snprintf(msg, + LOG_MSG_SZ, + "[%s]: Error: FFA_RXTX_MAP features = 0x%lx", + __func__, + prvdata->pair.rxtx_min_pages); + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + + return CMD_RET_SUCCESS; +} + +static int check_rxbuf_mapped_flag(u32 queried_func_id, + u8 rxbuf_mapped, + struct unit_test_state *uts) +{ + char msg[LOG_MSG_SZ] = {0}; + + switch (queried_func_id) { + case FFA_RXTX_MAP: + { + if (rxbuf_mapped) + return CMD_RET_SUCCESS; + break; + } + case FFA_RXTX_UNMAP: + { + if (!rxbuf_mapped) + return CMD_RET_SUCCESS; + break; + } + default: + return CMD_RET_FAILURE; + } + + snprintf(msg, LOG_MSG_SZ, "[%s]: Error: %s mapping issue", __func__, + (queried_func_id == FFA_RXTX_MAP ? "FFA_RXTX_MAP" : "FFA_RXTX_UNMAP")); + dm_test_ffa_log(uts, msg); + + return CMD_RET_FAILURE; +} + +static int check_rxbuf_release_flag(u8 rxbuf_owned, struct unit_test_state *uts) +{ + if (rxbuf_owned) { + char msg[LOG_MSG_SZ] = {0}; + + snprintf(msg, LOG_MSG_SZ, "[%s]: Error: RX buffer not released", __func__); + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + return CMD_RET_SUCCESS; +} + +static int test_ffa_msg_send_direct_req(u16 part_id, struct unit_test_state *uts) +{ + struct ffa_send_direct_data msg = {0}; + u8 cnt; + + ut_assertok(ffa_bus_ops_get()->sync_send_receive(part_id, &msg)); + + for (cnt = 0; cnt < sizeof(struct ffa_send_direct_data) / sizeof(u64); cnt++) + ut_assertok(((u64 *)&msg)[cnt] != 0xffffffffffffffff); + + return CMD_RET_SUCCESS; +} + +static int test_partitions_and_comms(const char *service_uuid, + struct sandbox_ffa_prvdata *sdx_prvdata, + struct unit_test_state *uts) +{ + u32 count = 0, size = 0; + struct ffa_partition_info *parts_info; + u32 info_idx, exp_info_idx; + int ret; + + /* + * get from the driver the count of the SPs matching the UUID + */ + ret = ffa_bus_ops_get()->partition_info_get(service_uuid, &count, NULL); + /* make sure partitions are detected */ + ut_assertok(ret != 0); + ut_assertok(count != SANDBOX_SP_COUNT_PER_VALID_SERVICE); + + /* + * pre-allocate a buffer to be filled by the driver + * with ffa_partition_info structs + */ + + parts_info = calloc(count, sizeof(struct ffa_partition_info)); + ut_assertok(!parts_info); + + size = count * sizeof(struct ffa_partition_info); + + /* + * ask the driver to fill the buffer with the SPs info + */ + ret = ffa_bus_ops_get()->partition_info_get(service_uuid, &size, parts_info); + if (ret != 0) { + free(parts_info); + ut_assertok(ret != 0); + } + + /* + * SPs found , verify the partitions information + */ + + ret = CMD_RET_FAILURE; + + for (info_idx = 0; info_idx < count ; info_idx++) { + for (exp_info_idx = 0; + exp_info_idx < sdx_prvdata->partitions.count; + exp_info_idx++) { + if (parts_info[info_idx].id == + sdx_prvdata->partitions.descs[exp_info_idx].info.id) { + ret = memcmp(&parts_info[info_idx], + &sdx_prvdata->partitions.descs[exp_info_idx] + .info, + sizeof(struct ffa_partition_info)); + if (ret) + free(parts_info); + ut_assertok(ret != 0); + /* send and receive data from the current partition */ + test_ffa_msg_send_direct_req(parts_info[info_idx].id, uts); + } + ret = CMD_RET_SUCCESS; + } + } + + free(parts_info); + + /* Verify expected partitions found in the emulated secure world*/ + ut_assertok(ret != CMD_RET_SUCCESS); + + return CMD_RET_SUCCESS; +} + +static int dm_test_ffa_ack(struct unit_test_state *uts) +{ + struct ffa_prvdata *prvdata = NULL; + struct sandbox_ffa_prvdata *sdx_prvdata = NULL; + struct ffa_sandbox_data func_data = {0}; + u8 rxbuf_flag = 0; + const char *svc1_uuid = SANDBOX_SERVICE1_UUID; + const char *svc2_uuid = SANDBOX_SERVICE2_UUID; + int ret; + + /* test probing FF-A devices */ + ut_assertok(ffa_bus_discover()); + + /* get a pointer to the FF-A core and sandbox drivers private data */ + func_data.data0 = &prvdata; + func_data.data0_size = sizeof(prvdata); + func_data.data1 = &sdx_prvdata; + func_data.data1_size = sizeof(sdx_prvdata); + + ut_assertok(sandbox_ffa_query_core_state(FFA_VERSION, &func_data)); + + /* make sure private data pointers are retrieved */ + ut_assertok(prvdata == 0); + ut_assertok(sdx_prvdata == 0); + + /* make sure dev devices created */ + ut_assertok(check_core_dev(prvdata, uts)); + ut_assertok(check_sandbox_dev(sdx_prvdata, uts)); + + /* test FFA_VERSION */ + ut_assertok(check_fwk_version(prvdata, sdx_prvdata, uts)); + + /* test FFA_ID_GET */ + ut_assertok(check_endpoint_id(prvdata, uts)); + + /* test FFA_FEATURES */ + ut_assertok(check_features(prvdata, uts)); + + /* test core RX/TX buffers */ + ut_assertok(check_rxtxbuf(prvdata, uts)); + + /* test FFA_RXTX_MAP */ + func_data.data0 = &rxbuf_flag; + func_data.data0_size = sizeof(rxbuf_flag); + + rxbuf_flag = 0; + ut_assertok(sandbox_ffa_query_core_state(FFA_RXTX_MAP, &func_data)); + ut_assertok(check_rxbuf_mapped_flag(FFA_RXTX_MAP, rxbuf_flag, uts)); + + /* FFA_PARTITION_INFO_GET / FFA_MSG_SEND_DIRECT_REQ */ + ret = test_partitions_and_comms(svc1_uuid, sdx_prvdata, uts); + ut_assertok(ret != CMD_RET_SUCCESS); + + /* test FFA_RX_RELEASE */ + rxbuf_flag = 1; + ut_assertok(sandbox_ffa_query_core_state(FFA_RX_RELEASE, &func_data)); + ut_assertok(check_rxbuf_release_flag(rxbuf_flag, uts)); + + /* FFA_PARTITION_INFO_GET / FFA_MSG_SEND_DIRECT_REQ */ + ret = test_partitions_and_comms(svc2_uuid, sdx_prvdata, uts); + ut_assertok(ret != CMD_RET_SUCCESS); + + /* test FFA_RX_RELEASE */ + rxbuf_flag = 1; + ut_assertok(sandbox_ffa_query_core_state(FFA_RX_RELEASE, &func_data)); + ut_assertok(check_rxbuf_release_flag(rxbuf_flag, uts)); + + /* test FFA_RXTX_UNMAP */ + ut_assertok(ffa_bus_ops_get()->rxtx_unmap()); + + rxbuf_flag = 1; + ut_assertok(sandbox_ffa_query_core_state(FFA_RXTX_UNMAP, &func_data)); + ut_assertok(check_rxbuf_mapped_flag(FFA_RXTX_UNMAP, rxbuf_flag, uts)); + + return CMD_RET_SUCCESS; +} + +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_prvdata *prvdata = NULL; + struct sandbox_ffa_prvdata *sdx_prvdata = NULL; + struct ffa_sandbox_data func_data = {0}; + 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 = {0}; + int ret; + u32 count = 0; + u16 part_id = 0; + + /* test probing FF-A devices */ + ut_assertok(ffa_bus_discover()); + + /* get a pointer to the FF-A core and sandbox drivers private data */ + func_data.data0 = &prvdata; + func_data.data0_size = sizeof(prvdata); + func_data.data1 = &sdx_prvdata; + func_data.data1_size = sizeof(sdx_prvdata); + + ut_assertok(sandbox_ffa_query_core_state(FFA_VERSION, &func_data)); + + /* make sure private data pointers are retrieved */ + ut_assertok(prvdata == 0); + ut_assertok(sdx_prvdata == 0); + + /* make sure dev devices created */ + ut_assertok(check_core_dev(prvdata, uts)); + ut_assertok(check_sandbox_dev(sdx_prvdata, uts)); + + /* query partitions count using invalid arguments */ + ret = ffa_bus_ops_get()->partition_info_get(unvalid_svc_uuid, NULL, NULL); + ut_assertok(ret != -EINVAL); + + /* query partitions count using an invalid UUID string */ + ret = ffa_bus_ops_get()->partition_info_get(unvalid_svc_uuid_str, &count, NULL); + ut_assertok(ret != -EINVAL); + + /* query partitions count using an invalid UUID (no matching SP) */ + count = 0; + ret = ffa_bus_ops_get()->partition_info_get(unvalid_svc_uuid, &count, NULL); + ut_assertok(count != 0); + + /* query partitions count using a valid UUID */ + count = 0; + ret = ffa_bus_ops_get()->partition_info_get(valid_svc_uuid, &count, NULL); + /* make sure partitions are detected */ + ut_assertok(ret != 0); + ut_assertok(count != SANDBOX_SP_COUNT_PER_VALID_SERVICE); + + /* send data to an invalid partition */ + ret = ffa_bus_ops_get()->sync_send_receive(part_id, &msg); + ut_assertok(ret != -EINVAL); + + /* send data to a valid partition */ + part_id = prvdata->partitions.descs[0].info.id; + ret = ffa_bus_ops_get()->sync_send_receive(part_id, &msg); + ut_assertok(ret != 0); + + return CMD_RET_SUCCESS; +} + +DM_TEST(dm_test_ffa_nack, UT_TESTF_SCAN_FDT | UT_TESTF_CONSOLE_REC);

Hi Abdellatif On Thu, Oct 13, 2022 at 11:38:54AM +0100, Abdellatif El Khlifi wrote:
Add functional test cases for the FF-A core driver
These tests rely on the FF-A Sandbox driver which helps in inspecting the FF-A core driver.
This looks ok from me, but I'd prefer Simon having a quick look, since I am not an expert when it comes to sandbox and testing
Thanks /Ilias
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v4: align sandbox tests with the new FF-A driver interfaces and new way of error handling
v1: introduce sandbox tests
MAINTAINERS | 1 + test/dm/Makefile | 2 + test/dm/ffa.c | 394 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 397 insertions(+) create mode 100644 test/dm/ffa.c
diff --git a/MAINTAINERS b/MAINTAINERS index 598ae76e16..bf198f4ce1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -256,6 +256,7 @@ F: doc/arch/arm64.ffa.rst F: drivers/firmware/arm-ffa/ F: include/arm_ffa.h F: include/sandbox_arm_ffa.h +F: test/dm/ffa.c
ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de diff --git a/test/dm/Makefile b/test/dm/Makefile index 5178daa7cf..e5bc4b4bd6 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0+ # # Copyright (c) 2013 Google, Inc +# (C) Copyright 2022 ARM Limited
obj-$(CONFIG_UT_DM) += test-dm.o
@@ -81,6 +82,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_SANDBOX_FFA) += 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 0000000000..052d5fc3f4 --- /dev/null +++ b/test/dm/ffa.c @@ -0,0 +1,394 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Functional tests for UCLASS_FFA class
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include <common.h> +#include <console.h> +#include <dm.h> +#include <dm/test.h> +#include "../../drivers/firmware/arm-ffa/sandbox_arm_ffa_prv.h" +#include <sandbox_arm_ffa.h> +#include <test/test.h> +#include <test/ut.h>
+/* Macros */
+#define LOG_MSG_SZ (100) +#define LOG_CMD_SZ (LOG_MSG_SZ * 2)
+/* Functional tests for the UCLASS_FFA */
+static int dm_test_ffa_log(struct unit_test_state *uts, char *msg) +{
- char cmd[LOG_CMD_SZ] = {0};
- console_record_reset();
- snprintf(cmd, LOG_CMD_SZ, "echo "%s"", msg);
- run_command(cmd, 0);
- ut_assert_console_end();
- return CMD_RET_SUCCESS;
+}
+static int check_fwk_version(struct ffa_prvdata *prvdata, struct sandbox_ffa_prvdata *sdx_prvdata,
struct unit_test_state *uts)
+{
- if (prvdata->fwk_version != sdx_prvdata->fwk_version) {
char msg[LOG_MSG_SZ] = {0};
snprintf(msg, LOG_MSG_SZ,
"[%s]: Error: framework version: core = 0x%x , sandbox = 0x%x", __func__,
prvdata->fwk_version,
sdx_prvdata->fwk_version);
dm_test_ffa_log(uts, msg);
return CMD_RET_FAILURE;
- }
- return CMD_RET_SUCCESS;
+}
+static int check_endpoint_id(struct ffa_prvdata *prvdata, struct unit_test_state *uts) +{
- if (prvdata->id) {
char msg[LOG_MSG_SZ] = {0};
snprintf(msg, LOG_MSG_SZ,
"[%s]: Error: endpoint id: core = 0x%x", __func__, prvdata->id);
dm_test_ffa_log(uts, msg);
return CMD_RET_FAILURE;
- }
- return CMD_RET_SUCCESS;
+}
+static int check_core_dev(struct ffa_prvdata *prvdata, struct unit_test_state *uts) +{
- if (!prvdata->dev) {
char msg[LOG_MSG_SZ] = {0};
snprintf(msg, LOG_MSG_SZ, "[%s]: Error: core device NULL", __func__);
dm_test_ffa_log(uts, msg);
return CMD_RET_FAILURE;
- }
- return CMD_RET_SUCCESS;
+}
+static int check_sandbox_dev(struct sandbox_ffa_prvdata *sdx_prvdata, struct unit_test_state *uts) +{
- if (!sdx_prvdata->dev) {
char msg[LOG_MSG_SZ] = {0};
snprintf(msg, LOG_MSG_SZ, "[%s]: Error: sandbox device NULL", __func__);
dm_test_ffa_log(uts, msg);
return CMD_RET_FAILURE;
- }
- return CMD_RET_SUCCESS;
+}
+static int check_rxtxbuf(struct ffa_prvdata *prvdata, struct unit_test_state *uts) +{
- if (!prvdata->pair.rxbuf && prvdata->pair.txbuf) {
char msg[LOG_MSG_SZ] = {0};
snprintf(msg, LOG_MSG_SZ, "[%s]: Error: rxbuf = 0x%llx txbuf = 0x%llx", __func__,
prvdata->pair.rxbuf,
prvdata->pair.txbuf);
dm_test_ffa_log(uts, msg);
return CMD_RET_FAILURE;
- }
- return CMD_RET_SUCCESS;
+}
+static int check_features(struct ffa_prvdata *prvdata, struct unit_test_state *uts) +{
- char msg[LOG_MSG_SZ] = {0};
- if (prvdata->pair.rxtx_min_pages != RXTX_4K &&
prvdata->pair.rxtx_min_pages != RXTX_16K &&
prvdata->pair.rxtx_min_pages != RXTX_64K) {
snprintf(msg,
LOG_MSG_SZ,
"[%s]: Error: FFA_RXTX_MAP features = 0x%lx",
__func__,
prvdata->pair.rxtx_min_pages);
dm_test_ffa_log(uts, msg);
return CMD_RET_FAILURE;
- }
- return CMD_RET_SUCCESS;
+}
+static int check_rxbuf_mapped_flag(u32 queried_func_id,
u8 rxbuf_mapped,
struct unit_test_state *uts)
+{
- char msg[LOG_MSG_SZ] = {0};
- switch (queried_func_id) {
- case FFA_RXTX_MAP:
- {
if (rxbuf_mapped)
return CMD_RET_SUCCESS;
break;
- }
- case FFA_RXTX_UNMAP:
- {
if (!rxbuf_mapped)
return CMD_RET_SUCCESS;
break;
- }
- default:
return CMD_RET_FAILURE;
- }
- snprintf(msg, LOG_MSG_SZ, "[%s]: Error: %s mapping issue", __func__,
(queried_func_id == FFA_RXTX_MAP ? "FFA_RXTX_MAP" : "FFA_RXTX_UNMAP"));
- dm_test_ffa_log(uts, msg);
- return CMD_RET_FAILURE;
+}
+static int check_rxbuf_release_flag(u8 rxbuf_owned, struct unit_test_state *uts) +{
- if (rxbuf_owned) {
char msg[LOG_MSG_SZ] = {0};
snprintf(msg, LOG_MSG_SZ, "[%s]: Error: RX buffer not released", __func__);
dm_test_ffa_log(uts, msg);
return CMD_RET_FAILURE;
- }
- return CMD_RET_SUCCESS;
+}
+static int test_ffa_msg_send_direct_req(u16 part_id, struct unit_test_state *uts) +{
- struct ffa_send_direct_data msg = {0};
- u8 cnt;
- ut_assertok(ffa_bus_ops_get()->sync_send_receive(part_id, &msg));
- for (cnt = 0; cnt < sizeof(struct ffa_send_direct_data) / sizeof(u64); cnt++)
ut_assertok(((u64 *)&msg)[cnt] != 0xffffffffffffffff);
- return CMD_RET_SUCCESS;
+}
+static int test_partitions_and_comms(const char *service_uuid,
struct sandbox_ffa_prvdata *sdx_prvdata,
struct unit_test_state *uts)
+{
- u32 count = 0, size = 0;
- struct ffa_partition_info *parts_info;
- u32 info_idx, exp_info_idx;
- int ret;
- /*
* get from the driver the count of the SPs matching the UUID
*/
- ret = ffa_bus_ops_get()->partition_info_get(service_uuid, &count, NULL);
- /* make sure partitions are detected */
- ut_assertok(ret != 0);
- ut_assertok(count != SANDBOX_SP_COUNT_PER_VALID_SERVICE);
- /*
* pre-allocate a buffer to be filled by the driver
* with ffa_partition_info structs
*/
- parts_info = calloc(count, sizeof(struct ffa_partition_info));
- ut_assertok(!parts_info);
- size = count * sizeof(struct ffa_partition_info);
- /*
* ask the driver to fill the buffer with the SPs info
*/
- ret = ffa_bus_ops_get()->partition_info_get(service_uuid, &size, parts_info);
- if (ret != 0) {
free(parts_info);
ut_assertok(ret != 0);
- }
- /*
* SPs found , verify the partitions information
*/
- ret = CMD_RET_FAILURE;
- for (info_idx = 0; info_idx < count ; info_idx++) {
for (exp_info_idx = 0;
exp_info_idx < sdx_prvdata->partitions.count;
exp_info_idx++) {
if (parts_info[info_idx].id ==
sdx_prvdata->partitions.descs[exp_info_idx].info.id) {
ret = memcmp(&parts_info[info_idx],
&sdx_prvdata->partitions.descs[exp_info_idx]
.info,
sizeof(struct ffa_partition_info));
if (ret)
free(parts_info);
ut_assertok(ret != 0);
/* send and receive data from the current partition */
test_ffa_msg_send_direct_req(parts_info[info_idx].id, uts);
}
ret = CMD_RET_SUCCESS;
}
- }
- free(parts_info);
- /* Verify expected partitions found in the emulated secure world*/
- ut_assertok(ret != CMD_RET_SUCCESS);
- return CMD_RET_SUCCESS;
+}
+static int dm_test_ffa_ack(struct unit_test_state *uts) +{
- struct ffa_prvdata *prvdata = NULL;
- struct sandbox_ffa_prvdata *sdx_prvdata = NULL;
- struct ffa_sandbox_data func_data = {0};
- u8 rxbuf_flag = 0;
- const char *svc1_uuid = SANDBOX_SERVICE1_UUID;
- const char *svc2_uuid = SANDBOX_SERVICE2_UUID;
- int ret;
- /* test probing FF-A devices */
- ut_assertok(ffa_bus_discover());
- /* get a pointer to the FF-A core and sandbox drivers private data */
- func_data.data0 = &prvdata;
- func_data.data0_size = sizeof(prvdata);
- func_data.data1 = &sdx_prvdata;
- func_data.data1_size = sizeof(sdx_prvdata);
- ut_assertok(sandbox_ffa_query_core_state(FFA_VERSION, &func_data));
- /* make sure private data pointers are retrieved */
- ut_assertok(prvdata == 0);
- ut_assertok(sdx_prvdata == 0);
- /* make sure dev devices created */
- ut_assertok(check_core_dev(prvdata, uts));
- ut_assertok(check_sandbox_dev(sdx_prvdata, uts));
- /* test FFA_VERSION */
- ut_assertok(check_fwk_version(prvdata, sdx_prvdata, uts));
- /* test FFA_ID_GET */
- ut_assertok(check_endpoint_id(prvdata, uts));
- /* test FFA_FEATURES */
- ut_assertok(check_features(prvdata, uts));
- /* test core RX/TX buffers */
- ut_assertok(check_rxtxbuf(prvdata, uts));
- /* test FFA_RXTX_MAP */
- func_data.data0 = &rxbuf_flag;
- func_data.data0_size = sizeof(rxbuf_flag);
- rxbuf_flag = 0;
- ut_assertok(sandbox_ffa_query_core_state(FFA_RXTX_MAP, &func_data));
- ut_assertok(check_rxbuf_mapped_flag(FFA_RXTX_MAP, rxbuf_flag, uts));
- /* FFA_PARTITION_INFO_GET / FFA_MSG_SEND_DIRECT_REQ */
- ret = test_partitions_and_comms(svc1_uuid, sdx_prvdata, uts);
- ut_assertok(ret != CMD_RET_SUCCESS);
- /* test FFA_RX_RELEASE */
- rxbuf_flag = 1;
- ut_assertok(sandbox_ffa_query_core_state(FFA_RX_RELEASE, &func_data));
- ut_assertok(check_rxbuf_release_flag(rxbuf_flag, uts));
- /* FFA_PARTITION_INFO_GET / FFA_MSG_SEND_DIRECT_REQ */
- ret = test_partitions_and_comms(svc2_uuid, sdx_prvdata, uts);
- ut_assertok(ret != CMD_RET_SUCCESS);
- /* test FFA_RX_RELEASE */
- rxbuf_flag = 1;
- ut_assertok(sandbox_ffa_query_core_state(FFA_RX_RELEASE, &func_data));
- ut_assertok(check_rxbuf_release_flag(rxbuf_flag, uts));
- /* test FFA_RXTX_UNMAP */
- ut_assertok(ffa_bus_ops_get()->rxtx_unmap());
- rxbuf_flag = 1;
- ut_assertok(sandbox_ffa_query_core_state(FFA_RXTX_UNMAP, &func_data));
- ut_assertok(check_rxbuf_mapped_flag(FFA_RXTX_UNMAP, rxbuf_flag, uts));
- return CMD_RET_SUCCESS;
+}
+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_prvdata *prvdata = NULL;
- struct sandbox_ffa_prvdata *sdx_prvdata = NULL;
- struct ffa_sandbox_data func_data = {0};
- 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 = {0};
- int ret;
- u32 count = 0;
- u16 part_id = 0;
- /* test probing FF-A devices */
- ut_assertok(ffa_bus_discover());
- /* get a pointer to the FF-A core and sandbox drivers private data */
- func_data.data0 = &prvdata;
- func_data.data0_size = sizeof(prvdata);
- func_data.data1 = &sdx_prvdata;
- func_data.data1_size = sizeof(sdx_prvdata);
- ut_assertok(sandbox_ffa_query_core_state(FFA_VERSION, &func_data));
- /* make sure private data pointers are retrieved */
- ut_assertok(prvdata == 0);
- ut_assertok(sdx_prvdata == 0);
- /* make sure dev devices created */
- ut_assertok(check_core_dev(prvdata, uts));
- ut_assertok(check_sandbox_dev(sdx_prvdata, uts));
- /* query partitions count using invalid arguments */
- ret = ffa_bus_ops_get()->partition_info_get(unvalid_svc_uuid, NULL, NULL);
- ut_assertok(ret != -EINVAL);
- /* query partitions count using an invalid UUID string */
- ret = ffa_bus_ops_get()->partition_info_get(unvalid_svc_uuid_str, &count, NULL);
- ut_assertok(ret != -EINVAL);
- /* query partitions count using an invalid UUID (no matching SP) */
- count = 0;
- ret = ffa_bus_ops_get()->partition_info_get(unvalid_svc_uuid, &count, NULL);
- ut_assertok(count != 0);
- /* query partitions count using a valid UUID */
- count = 0;
- ret = ffa_bus_ops_get()->partition_info_get(valid_svc_uuid, &count, NULL);
- /* make sure partitions are detected */
- ut_assertok(ret != 0);
- ut_assertok(count != SANDBOX_SP_COUNT_PER_VALID_SERVICE);
- /* send data to an invalid partition */
- ret = ffa_bus_ops_get()->sync_send_receive(part_id, &msg);
- ut_assertok(ret != -EINVAL);
- /* send data to a valid partition */
- part_id = prvdata->partitions.descs[0].info.id;
- ret = ffa_bus_ops_get()->sync_send_receive(part_id, &msg);
- ut_assertok(ret != 0);
- return CMD_RET_SUCCESS;
+}
+DM_TEST(dm_test_ffa_nack, UT_TESTF_SCAN_FDT | UT_TESTF_CONSOLE_REC);
2.17.1

On Mon, Oct 24, 2022 at 03:10:09PM +0300, Ilias Apalodimas wrote:
Hi Abdellatif On Thu, Oct 13, 2022 at 11:38:54AM +0100, Abdellatif El Khlifi wrote:
Add functional test cases for the FF-A core driver
These tests rely on the FF-A Sandbox driver which helps in inspecting the FF-A core driver.
This looks ok from me, but I'd prefer Simon having a quick look, since I am not an expert when it comes to sandbox and testing
Thanks for the feedback Ilias.
Simon, could you please provide a feedback about the Sandbox part ?
Thanks /Ilias
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v4: align sandbox tests with the new FF-A driver interfaces and new way of error handling
v1: introduce sandbox tests
MAINTAINERS | 1 + test/dm/Makefile | 2 + test/dm/ffa.c | 394 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 397 insertions(+) create mode 100644 test/dm/ffa.c
diff --git a/MAINTAINERS b/MAINTAINERS index 598ae76e16..bf198f4ce1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -256,6 +256,7 @@ F: doc/arch/arm64.ffa.rst F: drivers/firmware/arm-ffa/ F: include/arm_ffa.h F: include/sandbox_arm_ffa.h +F: test/dm/ffa.c
ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de diff --git a/test/dm/Makefile b/test/dm/Makefile index 5178daa7cf..e5bc4b4bd6 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0+ # # Copyright (c) 2013 Google, Inc +# (C) Copyright 2022 ARM Limited
obj-$(CONFIG_UT_DM) += test-dm.o
@@ -81,6 +82,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_SANDBOX_FFA) += 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 0000000000..052d5fc3f4 --- /dev/null +++ b/test/dm/ffa.c @@ -0,0 +1,394 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Functional tests for UCLASS_FFA class
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include <common.h> +#include <console.h> +#include <dm.h> +#include <dm/test.h> +#include "../../drivers/firmware/arm-ffa/sandbox_arm_ffa_prv.h" +#include <sandbox_arm_ffa.h> +#include <test/test.h> +#include <test/ut.h>
+/* Macros */
+#define LOG_MSG_SZ (100) +#define LOG_CMD_SZ (LOG_MSG_SZ * 2)
+/* Functional tests for the UCLASS_FFA */
+static int dm_test_ffa_log(struct unit_test_state *uts, char *msg) +{
- char cmd[LOG_CMD_SZ] = {0};
- console_record_reset();
- snprintf(cmd, LOG_CMD_SZ, "echo "%s"", msg);
- run_command(cmd, 0);
- ut_assert_console_end();
- return CMD_RET_SUCCESS;
+}
+static int check_fwk_version(struct ffa_prvdata *prvdata, struct sandbox_ffa_prvdata *sdx_prvdata,
struct unit_test_state *uts)
+{
- if (prvdata->fwk_version != sdx_prvdata->fwk_version) {
char msg[LOG_MSG_SZ] = {0};
snprintf(msg, LOG_MSG_SZ,
"[%s]: Error: framework version: core = 0x%x , sandbox = 0x%x", __func__,
prvdata->fwk_version,
sdx_prvdata->fwk_version);
dm_test_ffa_log(uts, msg);
return CMD_RET_FAILURE;
- }
- return CMD_RET_SUCCESS;
+}
+static int check_endpoint_id(struct ffa_prvdata *prvdata, struct unit_test_state *uts) +{
- if (prvdata->id) {
char msg[LOG_MSG_SZ] = {0};
snprintf(msg, LOG_MSG_SZ,
"[%s]: Error: endpoint id: core = 0x%x", __func__, prvdata->id);
dm_test_ffa_log(uts, msg);
return CMD_RET_FAILURE;
- }
- return CMD_RET_SUCCESS;
+}
+static int check_core_dev(struct ffa_prvdata *prvdata, struct unit_test_state *uts) +{
- if (!prvdata->dev) {
char msg[LOG_MSG_SZ] = {0};
snprintf(msg, LOG_MSG_SZ, "[%s]: Error: core device NULL", __func__);
dm_test_ffa_log(uts, msg);
return CMD_RET_FAILURE;
- }
- return CMD_RET_SUCCESS;
+}
+static int check_sandbox_dev(struct sandbox_ffa_prvdata *sdx_prvdata, struct unit_test_state *uts) +{
- if (!sdx_prvdata->dev) {
char msg[LOG_MSG_SZ] = {0};
snprintf(msg, LOG_MSG_SZ, "[%s]: Error: sandbox device NULL", __func__);
dm_test_ffa_log(uts, msg);
return CMD_RET_FAILURE;
- }
- return CMD_RET_SUCCESS;
+}
+static int check_rxtxbuf(struct ffa_prvdata *prvdata, struct unit_test_state *uts) +{
- if (!prvdata->pair.rxbuf && prvdata->pair.txbuf) {
char msg[LOG_MSG_SZ] = {0};
snprintf(msg, LOG_MSG_SZ, "[%s]: Error: rxbuf = 0x%llx txbuf = 0x%llx", __func__,
prvdata->pair.rxbuf,
prvdata->pair.txbuf);
dm_test_ffa_log(uts, msg);
return CMD_RET_FAILURE;
- }
- return CMD_RET_SUCCESS;
+}
+static int check_features(struct ffa_prvdata *prvdata, struct unit_test_state *uts) +{
- char msg[LOG_MSG_SZ] = {0};
- if (prvdata->pair.rxtx_min_pages != RXTX_4K &&
prvdata->pair.rxtx_min_pages != RXTX_16K &&
prvdata->pair.rxtx_min_pages != RXTX_64K) {
snprintf(msg,
LOG_MSG_SZ,
"[%s]: Error: FFA_RXTX_MAP features = 0x%lx",
__func__,
prvdata->pair.rxtx_min_pages);
dm_test_ffa_log(uts, msg);
return CMD_RET_FAILURE;
- }
- return CMD_RET_SUCCESS;
+}
+static int check_rxbuf_mapped_flag(u32 queried_func_id,
u8 rxbuf_mapped,
struct unit_test_state *uts)
+{
- char msg[LOG_MSG_SZ] = {0};
- switch (queried_func_id) {
- case FFA_RXTX_MAP:
- {
if (rxbuf_mapped)
return CMD_RET_SUCCESS;
break;
- }
- case FFA_RXTX_UNMAP:
- {
if (!rxbuf_mapped)
return CMD_RET_SUCCESS;
break;
- }
- default:
return CMD_RET_FAILURE;
- }
- snprintf(msg, LOG_MSG_SZ, "[%s]: Error: %s mapping issue", __func__,
(queried_func_id == FFA_RXTX_MAP ? "FFA_RXTX_MAP" : "FFA_RXTX_UNMAP"));
- dm_test_ffa_log(uts, msg);
- return CMD_RET_FAILURE;
+}
+static int check_rxbuf_release_flag(u8 rxbuf_owned, struct unit_test_state *uts) +{
- if (rxbuf_owned) {
char msg[LOG_MSG_SZ] = {0};
snprintf(msg, LOG_MSG_SZ, "[%s]: Error: RX buffer not released", __func__);
dm_test_ffa_log(uts, msg);
return CMD_RET_FAILURE;
- }
- return CMD_RET_SUCCESS;
+}
+static int test_ffa_msg_send_direct_req(u16 part_id, struct unit_test_state *uts) +{
- struct ffa_send_direct_data msg = {0};
- u8 cnt;
- ut_assertok(ffa_bus_ops_get()->sync_send_receive(part_id, &msg));
- for (cnt = 0; cnt < sizeof(struct ffa_send_direct_data) / sizeof(u64); cnt++)
ut_assertok(((u64 *)&msg)[cnt] != 0xffffffffffffffff);
- return CMD_RET_SUCCESS;
+}
+static int test_partitions_and_comms(const char *service_uuid,
struct sandbox_ffa_prvdata *sdx_prvdata,
struct unit_test_state *uts)
+{
- u32 count = 0, size = 0;
- struct ffa_partition_info *parts_info;
- u32 info_idx, exp_info_idx;
- int ret;
- /*
* get from the driver the count of the SPs matching the UUID
*/
- ret = ffa_bus_ops_get()->partition_info_get(service_uuid, &count, NULL);
- /* make sure partitions are detected */
- ut_assertok(ret != 0);
- ut_assertok(count != SANDBOX_SP_COUNT_PER_VALID_SERVICE);
- /*
* pre-allocate a buffer to be filled by the driver
* with ffa_partition_info structs
*/
- parts_info = calloc(count, sizeof(struct ffa_partition_info));
- ut_assertok(!parts_info);
- size = count * sizeof(struct ffa_partition_info);
- /*
* ask the driver to fill the buffer with the SPs info
*/
- ret = ffa_bus_ops_get()->partition_info_get(service_uuid, &size, parts_info);
- if (ret != 0) {
free(parts_info);
ut_assertok(ret != 0);
- }
- /*
* SPs found , verify the partitions information
*/
- ret = CMD_RET_FAILURE;
- for (info_idx = 0; info_idx < count ; info_idx++) {
for (exp_info_idx = 0;
exp_info_idx < sdx_prvdata->partitions.count;
exp_info_idx++) {
if (parts_info[info_idx].id ==
sdx_prvdata->partitions.descs[exp_info_idx].info.id) {
ret = memcmp(&parts_info[info_idx],
&sdx_prvdata->partitions.descs[exp_info_idx]
.info,
sizeof(struct ffa_partition_info));
if (ret)
free(parts_info);
ut_assertok(ret != 0);
/* send and receive data from the current partition */
test_ffa_msg_send_direct_req(parts_info[info_idx].id, uts);
}
ret = CMD_RET_SUCCESS;
}
- }
- free(parts_info);
- /* Verify expected partitions found in the emulated secure world*/
- ut_assertok(ret != CMD_RET_SUCCESS);
- return CMD_RET_SUCCESS;
+}
+static int dm_test_ffa_ack(struct unit_test_state *uts) +{
- struct ffa_prvdata *prvdata = NULL;
- struct sandbox_ffa_prvdata *sdx_prvdata = NULL;
- struct ffa_sandbox_data func_data = {0};
- u8 rxbuf_flag = 0;
- const char *svc1_uuid = SANDBOX_SERVICE1_UUID;
- const char *svc2_uuid = SANDBOX_SERVICE2_UUID;
- int ret;
- /* test probing FF-A devices */
- ut_assertok(ffa_bus_discover());
- /* get a pointer to the FF-A core and sandbox drivers private data */
- func_data.data0 = &prvdata;
- func_data.data0_size = sizeof(prvdata);
- func_data.data1 = &sdx_prvdata;
- func_data.data1_size = sizeof(sdx_prvdata);
- ut_assertok(sandbox_ffa_query_core_state(FFA_VERSION, &func_data));
- /* make sure private data pointers are retrieved */
- ut_assertok(prvdata == 0);
- ut_assertok(sdx_prvdata == 0);
- /* make sure dev devices created */
- ut_assertok(check_core_dev(prvdata, uts));
- ut_assertok(check_sandbox_dev(sdx_prvdata, uts));
- /* test FFA_VERSION */
- ut_assertok(check_fwk_version(prvdata, sdx_prvdata, uts));
- /* test FFA_ID_GET */
- ut_assertok(check_endpoint_id(prvdata, uts));
- /* test FFA_FEATURES */
- ut_assertok(check_features(prvdata, uts));
- /* test core RX/TX buffers */
- ut_assertok(check_rxtxbuf(prvdata, uts));
- /* test FFA_RXTX_MAP */
- func_data.data0 = &rxbuf_flag;
- func_data.data0_size = sizeof(rxbuf_flag);
- rxbuf_flag = 0;
- ut_assertok(sandbox_ffa_query_core_state(FFA_RXTX_MAP, &func_data));
- ut_assertok(check_rxbuf_mapped_flag(FFA_RXTX_MAP, rxbuf_flag, uts));
- /* FFA_PARTITION_INFO_GET / FFA_MSG_SEND_DIRECT_REQ */
- ret = test_partitions_and_comms(svc1_uuid, sdx_prvdata, uts);
- ut_assertok(ret != CMD_RET_SUCCESS);
- /* test FFA_RX_RELEASE */
- rxbuf_flag = 1;
- ut_assertok(sandbox_ffa_query_core_state(FFA_RX_RELEASE, &func_data));
- ut_assertok(check_rxbuf_release_flag(rxbuf_flag, uts));
- /* FFA_PARTITION_INFO_GET / FFA_MSG_SEND_DIRECT_REQ */
- ret = test_partitions_and_comms(svc2_uuid, sdx_prvdata, uts);
- ut_assertok(ret != CMD_RET_SUCCESS);
- /* test FFA_RX_RELEASE */
- rxbuf_flag = 1;
- ut_assertok(sandbox_ffa_query_core_state(FFA_RX_RELEASE, &func_data));
- ut_assertok(check_rxbuf_release_flag(rxbuf_flag, uts));
- /* test FFA_RXTX_UNMAP */
- ut_assertok(ffa_bus_ops_get()->rxtx_unmap());
- rxbuf_flag = 1;
- ut_assertok(sandbox_ffa_query_core_state(FFA_RXTX_UNMAP, &func_data));
- ut_assertok(check_rxbuf_mapped_flag(FFA_RXTX_UNMAP, rxbuf_flag, uts));
- return CMD_RET_SUCCESS;
+}
+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_prvdata *prvdata = NULL;
- struct sandbox_ffa_prvdata *sdx_prvdata = NULL;
- struct ffa_sandbox_data func_data = {0};
- 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 = {0};
- int ret;
- u32 count = 0;
- u16 part_id = 0;
- /* test probing FF-A devices */
- ut_assertok(ffa_bus_discover());
- /* get a pointer to the FF-A core and sandbox drivers private data */
- func_data.data0 = &prvdata;
- func_data.data0_size = sizeof(prvdata);
- func_data.data1 = &sdx_prvdata;
- func_data.data1_size = sizeof(sdx_prvdata);
- ut_assertok(sandbox_ffa_query_core_state(FFA_VERSION, &func_data));
- /* make sure private data pointers are retrieved */
- ut_assertok(prvdata == 0);
- ut_assertok(sdx_prvdata == 0);
- /* make sure dev devices created */
- ut_assertok(check_core_dev(prvdata, uts));
- ut_assertok(check_sandbox_dev(sdx_prvdata, uts));
- /* query partitions count using invalid arguments */
- ret = ffa_bus_ops_get()->partition_info_get(unvalid_svc_uuid, NULL, NULL);
- ut_assertok(ret != -EINVAL);
- /* query partitions count using an invalid UUID string */
- ret = ffa_bus_ops_get()->partition_info_get(unvalid_svc_uuid_str, &count, NULL);
- ut_assertok(ret != -EINVAL);
- /* query partitions count using an invalid UUID (no matching SP) */
- count = 0;
- ret = ffa_bus_ops_get()->partition_info_get(unvalid_svc_uuid, &count, NULL);
- ut_assertok(count != 0);
- /* query partitions count using a valid UUID */
- count = 0;
- ret = ffa_bus_ops_get()->partition_info_get(valid_svc_uuid, &count, NULL);
- /* make sure partitions are detected */
- ut_assertok(ret != 0);
- ut_assertok(count != SANDBOX_SP_COUNT_PER_VALID_SERVICE);
- /* send data to an invalid partition */
- ret = ffa_bus_ops_get()->sync_send_receive(part_id, &msg);
- ut_assertok(ret != -EINVAL);
- /* send data to a valid partition */
- part_id = prvdata->partitions.descs[0].info.id;
- ret = ffa_bus_ops_get()->sync_send_receive(part_id, &msg);
- ut_assertok(ret != 0);
- return CMD_RET_SUCCESS;
+}
+DM_TEST(dm_test_ffa_nack, UT_TESTF_SCAN_FDT | UT_TESTF_CONSOLE_REC);
2.17.1

Add Sandbox test for the armffa command
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v4: drop use of helper APIs
v1: introduce armffa command sandbox test
MAINTAINERS | 1 + test/cmd/Makefile | 2 ++ test/cmd/armffa.c | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+) create mode 100644 test/cmd/armffa.c
diff --git a/MAINTAINERS b/MAINTAINERS index bf198f4ce1..9a90826954 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -256,6 +256,7 @@ F: doc/arch/arm64.ffa.rst F: drivers/firmware/arm-ffa/ F: include/arm_ffa.h F: include/sandbox_arm_ffa.h +F: test/cmd/armffa.c F: test/dm/ffa.c
ARM FREESCALE IMX diff --git a/test/cmd/Makefile b/test/cmd/Makefile index 1bb02d93a2..9a04ea7b9b 100644 --- a/test/cmd/Makefile +++ b/test/cmd/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0+ # # Copyright (c) 2013 Google, Inc +# (C) Copyright 2022 ARM Limited
ifdef CONFIG_HUSH_PARSER obj-$(CONFIG_CONSOLE_RECORD) += test_echo.o @@ -16,3 +17,4 @@ obj-$(CONFIG_CMD_MEM_SEARCH) += mem_search.o obj-$(CONFIG_CMD_PINMUX) += pinmux.o obj-$(CONFIG_CMD_PWM) += pwm.o obj-$(CONFIG_CMD_SETEXPR) += setexpr.o +obj-$(CONFIG_SANDBOX_FFA) += armffa.o diff --git a/test/cmd/armffa.c b/test/cmd/armffa.c new file mode 100644 index 0000000000..531f82066e --- /dev/null +++ b/test/cmd/armffa.c @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test for armffa command + * + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <dm.h> +#include <dm/test.h> +#include <sandbox_arm_ffa.h> +#include <string.h> +#include <test/test.h> +#include <test/ut.h> + +#define PING_CMD_SIZE 19 + +/* Basic test of 'armffa' command */ +static int dm_test_armffa_cmd(struct unit_test_state *uts) +{ + char ping_cmd[PING_CMD_SIZE] = {0}; + + ut_assertok(ffa_bus_discover()); + + /* armffa getpart <UUID> */ + ut_assertok(run_command("armffa getpart " SANDBOX_SERVICE1_UUID, 0)); + + snprintf(ping_cmd, PING_CMD_SIZE, "armffa ping 0x%x", SANDBOX_SP1_ID); + + /* armffa ping <ID> */ + ut_assertok(run_command(ping_cmd, 0)); + + /* armffa devlist */ + ut_assertok(run_command("armffa devlist", 0)); + + return CMD_RET_SUCCESS; +} + +DM_TEST(dm_test_armffa_cmd, UT_TESTF_SCAN_FDT | UT_TESTF_CONSOLE_REC);

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.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Signed-off-by: Gowtham Suresh Kumar gowtham.sureshkumar@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v6:
* add FF-A runtime discovery at MM communication level * drop EFI runtime support for FF-A MM communication * revert the changes in include/mm_communication.h for efi_mm_communicate_header and smm_variable_access structures
v4:
* use the new FF-A driver interfaces * discover MM partitions at runtime * copy FF-A driver private data to EFI runtime section at ExitBootServices() * drop use of FFA_ERR_STAT_SUCCESS error code * replace EFI_BUFFER_TOO_SMALL with EFI_OUT_OF_RESOURCES in ffa_mm_communicate(). No need for efi_memcpy_runtime() anymore * revert the error log in mm_communicate() in case of failure * remove packed attribute from efi_mm_communicate_header and smm_variable_communicate_header
v2:
* set default values to 0 for FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_ADDR and MM_SP_UUID_DATA and add warnings
v1:
* introduce FF-A MM communication
include/mm_communication.h | 5 + lib/efi_loader/Kconfig | 14 +- lib/efi_loader/efi_variable_tee.c | 264 +++++++++++++++++++++++++++++- 3 files changed, 277 insertions(+), 6 deletions(-)
diff --git a/include/mm_communication.h b/include/mm_communication.h index e65fbde60d..d409bed777 100644 --- a/include/mm_communication.h +++ b/include/mm_communication.h @@ -6,6 +6,8 @@ * Copyright (c) 2017, Intel Corporation. All rights reserved. * Copyright (C) 2020 Linaro Ltd. sughosh.ganu@linaro.org * Copyright (C) 2020 Linaro Ltd. ilias.apalodimas@linaro.org + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#ifndef _MM_COMMUNICATION_H_ @@ -13,6 +15,9 @@
#include <part_efi.h>
+/* MM service UUID string (big-endian format). This UUID is common across all MM SPs */ +#define MM_SP_UUID "33d532ed-e699-0942-c09c-a798d9cd722d" + /* * Interface to the pseudo Trusted Application (TA), which provides a * communication channel with the Standalone MM (Management Mode) diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index 41756ea539..f0d843d5ca 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -60,13 +60,23 @@ 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" - depends on OPTEE + bool "UEFI variables storage service via the trusted world" + depends on OPTEE && ARM_FFA_TRANSPORT 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 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 dfef18435d..04e990880d 100644 --- a/lib/efi_loader/efi_variable_tee.c +++ b/lib/efi_loader/efi_variable_tee.c @@ -4,6 +4,8 @@ * * Copyright (C) 2019 Linaro Ltd. sughosh.ganu@linaro.org * Copyright (C) 2019 Linaro Ltd. ilias.apalodimas@linaro.org + * Copyright (C) 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> @@ -15,6 +17,36 @@ #include <malloc.h> #include <mm_communication.h>
+#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) + +#include <arm_ffa.h> +#include <cpu_func.h> +#include <mapmem.h> + +#ifndef FFA_SHARED_MM_BUFFER_SIZE +#warning "FFA_SHARED_MM_BUFFER_SIZE must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_SIZE 0 +#endif + +#ifndef FFA_SHARED_MM_BUFFER_OFFSET +#warning "FFA_SHARED_MM_BUFFER_OFFSET must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_OFFSET 0 +#endif + +#ifndef FFA_SHARED_MM_BUFFER_ADDR +#warning "FFA_SHARED_MM_BUFFER_ADDR must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_ADDR 0 +#endif + +/* MM return codes */ +#define MM_SUCCESS (0) + +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 */ static efi_uintn_t max_payload_size; /* func + data */ @@ -24,6 +56,7 @@ struct mm_connection { u32 session; };
+#if (IS_ENABLED(CONFIG_OPTEE)) /** * get_connection() - Retrieve OP-TEE session for a specific UUID. * @@ -143,13 +176,224 @@ static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize)
return ret; } +#endif + +#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) + +/** + * ffa_notify_mm_sp() - Announce there is data in the shared buffer + * + * Notifies 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 = -1; + + if (!ffa_bus_ops_get()) + return -EINVAL; + + msg.data0 = FFA_SHARED_MM_BUFFER_OFFSET; /* x3 */ + + ret = ffa_bus_ops_get()->sync_send_receive(mm_sp_id, &msg); + if (ret != 0) + return ret; + + sp_event_ret = msg.data0; /* x3 */ + + if (sp_event_ret == MM_SUCCESS) + return 0; + + /* + * Failure to notify the MM SP + */ + + return -EACCES; +}
/** - * mm_communicate() - Adjust the cmonnucation buffer to StandAlonneMM and send + * 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, size = 0; + int ret; + struct ffa_partition_info *parts_info; + + if (!ffa_bus_ops_get()) + return -EINVAL; + + /* + * get from the driver the count of the SPs matching the UUID + */ + ret = ffa_bus_ops_get()->partition_info_get(mm_sp_svc_uuid, &count, NULL); + if (ret != 0) { + log_err("EFI: Failure in querying partitions count (error code: %d)\n", ret); + return ret; + } + + if (!count) { + log_info("EFI: No MM partition found\n"); + return ret; + } + + /* + * pre-allocate a buffer to be filled by the driver + * with ffa_partition_info structs + */ + + log_info("EFI: Pre-allocating %d partition(s) info structures\n", count); + + parts_info = calloc(count, sizeof(struct ffa_partition_info)); + if (!parts_info) + return -EINVAL; + + size = count * sizeof(struct ffa_partition_info); + + /* + * ask the driver to fill the + * buffer with the SPs info + */ + ret = ffa_bus_ops_get()->partition_info_get(mm_sp_svc_uuid, &size, parts_info); + if (ret != 0) { + log_err("EFI: Failure in querying partition(s) info (error code: %d)\n", ret); + free(parts_info); + return ret; + } + + /* + * MM SPs found , use the first one + */ + + mm_sp_id = parts_info[0].id; + + log_info("EFI: MM partition ID 0x%x\n", mm_sp_id); + + free(parts_info); + + 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 + * + * Issues 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; + 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() != 0) { + log_err("EFI: Failure to discover MM partition ID at boot time\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 > FFA_SHARED_MM_BUFFER_SIZE) + return EFI_INVALID_PARAMETER; + + /* Copy the data to the shared buffer */ + + virt_shared_buf = (void *)map_sysmem((phys_addr_t)FFA_SHARED_MM_BUFFER_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(); + if (ffa_ret) + unmap_sysmem(virt_shared_buf); + + 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) { + unmap_sysmem(virt_shared_buf); + return EFI_OUT_OF_RESOURCES; + } + + memcpy(comm_buf, virt_shared_buf, rx_data_size); + unmap_sysmem(virt_shared_buf); + + return EFI_SUCCESS; + } + case -EINVAL: + return EFI_DEVICE_ERROR; + case -EPERM: + return EFI_INVALID_PARAMETER; + case -EACCES: + return EFI_ACCESS_DENIED; + case -EBUSY: + return EFI_OUT_OF_RESOURCES; + default: + return EFI_ACCESS_DENIED; + } +} +#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) @@ -162,7 +406,12 @@ static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) 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); + ret = ffa_bus_discover(); + if (ret != 0) + ret = optee_mm_communicate(comm_buf, dsize); + else + ret = ffa_mm_communicate(comm_buf, dsize); + if (ret != EFI_SUCCESS) { log_err("%s failed!\n", __func__); return ret; @@ -258,6 +507,13 @@ efi_status_t EFIAPI get_max_payload(efi_uintn_t *size) goto out; } *size = var_payload->size; + + #if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) + if (*size > FFA_SHARED_MM_BUFFER_SIZE) + *size = FFA_SHARED_MM_BUFFER_SIZE - MM_COMMUNICATE_HEADER_SIZE - + MM_VARIABLE_COMMUNICATE_SIZE; + #endif + /* * There seems to be a bug in EDK2 miscalculating the boundaries and * size checks, so deduct 2 more bytes to fulfill this requirement. Fix @@ -697,7 +953,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);
/*

Hi Abdellatif,
[...]
#include <common.h> @@ -15,6 +17,36 @@ #include <malloc.h> #include <mm_communication.h>
+#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
+#include <arm_ffa.h> +#include <cpu_func.h> +#include <mapmem.h>
+#ifndef FFA_SHARED_MM_BUFFER_SIZE +#warning "FFA_SHARED_MM_BUFFER_SIZE must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_SIZE 0 +#endif
+#ifndef FFA_SHARED_MM_BUFFER_OFFSET +#warning "FFA_SHARED_MM_BUFFER_OFFSET must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_OFFSET 0 +#endif
+#ifndef FFA_SHARED_MM_BUFFER_ADDR +#warning "FFA_SHARED_MM_BUFFER_ADDR must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_ADDR 0 +#endif
Is the device going to work with these defaults? I am assuming not, so isn't better to treat this as compile time errors?
+/* MM return codes */ +#define MM_SUCCESS (0)
+const char *mm_sp_svc_uuid = MM_SP_UUID;
static ?
+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 */ static efi_uintn_t max_payload_size; /* func + data */ @@ -24,6 +56,7 @@ struct mm_connection { u32 session; };
+#if (IS_ENABLED(CONFIG_OPTEE)) /**
- get_connection() - Retrieve OP-TEE session for a specific UUID.
@@ -143,13 +176,224 @@ static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize)
return ret; } +#endif
+#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
+/**
- ffa_notify_mm_sp() - Announce there is data in the shared buffer
- Notifies 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 = -1;
- if (!ffa_bus_ops_get())
return -EINVAL;
- msg.data0 = FFA_SHARED_MM_BUFFER_OFFSET; /* x3 */
- ret = ffa_bus_ops_get()->sync_send_receive(mm_sp_id, &msg);
- if (ret != 0)
return ret;
- sp_event_ret = msg.data0; /* x3 */
- if (sp_event_ret == MM_SUCCESS)
return 0;
- /*
* Failure to notify the MM SP
*/
- return -EACCES;
+}
/**
- mm_communicate() - Adjust the cmonnucation buffer to StandAlonneMM and send
- 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, size = 0;
- int ret;
- struct ffa_partition_info *parts_info;
- if (!ffa_bus_ops_get())
return -EINVAL;
- /*
* get from the driver the count of the SPs matching the UUID
*/
- ret = ffa_bus_ops_get()->partition_info_get(mm_sp_svc_uuid, &count, NULL);
- if (ret != 0) {
log_err("EFI: Failure in querying partitions count (error code: %d)\n", ret);
return ret;
- }
- if (!count) {
log_info("EFI: No MM partition found\n");
return ret;
- }
- /*
* pre-allocate a buffer to be filled by the driver
* with ffa_partition_info structs
*/
- log_info("EFI: Pre-allocating %d partition(s) info structures\n", count);
- parts_info = calloc(count, sizeof(struct ffa_partition_info));
- if (!parts_info)
return -EINVAL;
-ENOMEM here is more accurate
- size = count * sizeof(struct ffa_partition_info);
- /*
* ask the driver to fill the
* buffer with the SPs info
*/
- ret = ffa_bus_ops_get()->partition_info_get(mm_sp_svc_uuid, &size, parts_info);
- if (ret != 0) {
log_err("EFI: Failure in querying partition(s) info (error code: %d)\n", ret);
free(parts_info);
return ret;
- }
- /*
* MM SPs found , use the first one
*/
- mm_sp_id = parts_info[0].id;
- log_info("EFI: MM partition ID 0x%x\n", mm_sp_id);
- free(parts_info);
- 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
- Issues 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;
- 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() != 0) {
log_err("EFI: Failure to discover MM partition ID at boot time\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 > FFA_SHARED_MM_BUFFER_SIZE)
return EFI_INVALID_PARAMETER;
- /* Copy the data to the shared buffer */
- virt_shared_buf = (void *)map_sysmem((phys_addr_t)FFA_SHARED_MM_BUFFER_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();
Is this only for arm64?
- #endif
- /* Announce there is data in the shared buffer */
- ffa_ret = ffa_notify_mm_sp();
- if (ffa_ret)
unmap_sysmem(virt_shared_buf);
This is a bit confusing handled in different places, can't we just move the check at the end of the switch cases and remote unmap_sysmem() from case 0?
- 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) {
unmap_sysmem(virt_shared_buf);
return EFI_OUT_OF_RESOURCES;
}
memcpy(comm_buf, virt_shared_buf, rx_data_size);
unmap_sysmem(virt_shared_buf);
return EFI_SUCCESS;
- }
- case -EINVAL:
return EFI_DEVICE_ERROR;
- case -EPERM:
return EFI_INVALID_PARAMETER;
- case -EACCES:
return EFI_ACCESS_DENIED;
- case -EBUSY:
return EFI_OUT_OF_RESOURCES;
- default:
return EFI_ACCESS_DENIED;
- }
+} +#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) @@ -162,7 +406,12 @@ static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) 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);
- ret = ffa_bus_discover();
- if (ret != 0)
ret = optee_mm_communicate(comm_buf, dsize);
- else
ret = ffa_mm_communicate(comm_buf, dsize);
- if (ret != EFI_SUCCESS) { log_err("%s failed!\n", __func__); return ret;
@@ -258,6 +507,13 @@ efi_status_t EFIAPI get_max_payload(efi_uintn_t *size) goto out; } *size = var_payload->size;
- #if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
if (*size > FFA_SHARED_MM_BUFFER_SIZE)
*size = FFA_SHARED_MM_BUFFER_SIZE - MM_COMMUNICATE_HEADER_SIZE -
MM_VARIABLE_COMMUNICATE_SIZE;
- #endif
- /*
- There seems to be a bug in EDK2 miscalculating the boundaries and
- size checks, so deduct 2 more bytes to fulfill this requirement. Fix
@@ -697,7 +953,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);
/*
-- 2.17.1
Thanks /Ilias

On Mon, Oct 24, 2022 at 03:30:11PM +0300, Ilias Apalodimas wrote:
Hi Abdellatif,
[...]
#include <common.h> @@ -15,6 +17,36 @@ #include <malloc.h> #include <mm_communication.h>
+#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
+#include <arm_ffa.h> +#include <cpu_func.h> +#include <mapmem.h>
+#ifndef FFA_SHARED_MM_BUFFER_SIZE +#warning "FFA_SHARED_MM_BUFFER_SIZE must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_SIZE 0 +#endif
+#ifndef FFA_SHARED_MM_BUFFER_OFFSET +#warning "FFA_SHARED_MM_BUFFER_OFFSET must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_OFFSET 0 +#endif
+#ifndef FFA_SHARED_MM_BUFFER_ADDR +#warning "FFA_SHARED_MM_BUFFER_ADDR must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_ADDR 0 +#endif
Is the device going to work with these defaults? I am assuming not, so isn't better to treat this as compile time errors?
Thanks. Done in v7.
+/* MM return codes */ +#define MM_SUCCESS (0)
+const char *mm_sp_svc_uuid = MM_SP_UUID;
static ?
+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 */ static efi_uintn_t max_payload_size; /* func + data */ @@ -24,6 +56,7 @@ struct mm_connection { u32 session; };
+#if (IS_ENABLED(CONFIG_OPTEE)) /**
- get_connection() - Retrieve OP-TEE session for a specific UUID.
@@ -143,13 +176,224 @@ static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize)
return ret; } +#endif
+#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
+/**
- ffa_notify_mm_sp() - Announce there is data in the shared buffer
- Notifies 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 = -1;
- if (!ffa_bus_ops_get())
return -EINVAL;
- msg.data0 = FFA_SHARED_MM_BUFFER_OFFSET; /* x3 */
- ret = ffa_bus_ops_get()->sync_send_receive(mm_sp_id, &msg);
- if (ret != 0)
return ret;
- sp_event_ret = msg.data0; /* x3 */
- if (sp_event_ret == MM_SUCCESS)
return 0;
- /*
* Failure to notify the MM SP
*/
- return -EACCES;
+}
/**
- mm_communicate() - Adjust the cmonnucation buffer to StandAlonneMM and send
- 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, size = 0;
- int ret;
- struct ffa_partition_info *parts_info;
- if (!ffa_bus_ops_get())
return -EINVAL;
- /*
* get from the driver the count of the SPs matching the UUID
*/
- ret = ffa_bus_ops_get()->partition_info_get(mm_sp_svc_uuid, &count, NULL);
- if (ret != 0) {
log_err("EFI: Failure in querying partitions count (error code: %d)\n", ret);
return ret;
- }
- if (!count) {
log_info("EFI: No MM partition found\n");
return ret;
- }
- /*
* pre-allocate a buffer to be filled by the driver
* with ffa_partition_info structs
*/
- log_info("EFI: Pre-allocating %d partition(s) info structures\n", count);
- parts_info = calloc(count, sizeof(struct ffa_partition_info));
- if (!parts_info)
return -EINVAL;
-ENOMEM here is more accurate
- size = count * sizeof(struct ffa_partition_info);
- /*
* ask the driver to fill the
* buffer with the SPs info
*/
- ret = ffa_bus_ops_get()->partition_info_get(mm_sp_svc_uuid, &size, parts_info);
- if (ret != 0) {
log_err("EFI: Failure in querying partition(s) info (error code: %d)\n", ret);
free(parts_info);
return ret;
- }
- /*
* MM SPs found , use the first one
*/
- mm_sp_id = parts_info[0].id;
- log_info("EFI: MM partition ID 0x%x\n", mm_sp_id);
- free(parts_info);
- 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
- Issues 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;
- 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() != 0) {
log_err("EFI: Failure to discover MM partition ID at boot time\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 > FFA_SHARED_MM_BUFFER_SIZE)
return EFI_INVALID_PARAMETER;
- /* Copy the data to the shared buffer */
- virt_shared_buf = (void *)map_sysmem((phys_addr_t)FFA_SHARED_MM_BUFFER_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();
Is this only for arm64?
Yes, currently we only support Aarch64 which we currently need. Same done for the FF-A Linux kernel driver [1].
[1]: https://elixir.bootlin.com/linux/v6.1-rc3/source/drivers/firmware/arm_ffa/Kc...
- #endif
- /* Announce there is data in the shared buffer */
- ffa_ret = ffa_notify_mm_sp();
- if (ffa_ret)
unmap_sysmem(virt_shared_buf);
This is a bit confusing handled in different places, can't we just move the check at the end of the switch cases and remote unmap_sysmem() from case 0?
Done in v7.
- 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) {
unmap_sysmem(virt_shared_buf);
return EFI_OUT_OF_RESOURCES;
}
memcpy(comm_buf, virt_shared_buf, rx_data_size);
unmap_sysmem(virt_shared_buf);
return EFI_SUCCESS;
- }
- case -EINVAL:
return EFI_DEVICE_ERROR;
- case -EPERM:
return EFI_INVALID_PARAMETER;
- case -EACCES:
return EFI_ACCESS_DENIED;
- case -EBUSY:
return EFI_OUT_OF_RESOURCES;
- default:
return EFI_ACCESS_DENIED;
- }
+} +#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) @@ -162,7 +406,12 @@ static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) 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);
- ret = ffa_bus_discover();
- if (ret != 0)
ret = optee_mm_communicate(comm_buf, dsize);
- else
ret = ffa_mm_communicate(comm_buf, dsize);
- if (ret != EFI_SUCCESS) { log_err("%s failed!\n", __func__); return ret;
@@ -258,6 +507,13 @@ efi_status_t EFIAPI get_max_payload(efi_uintn_t *size) goto out; } *size = var_payload->size;
- #if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
if (*size > FFA_SHARED_MM_BUFFER_SIZE)
*size = FFA_SHARED_MM_BUFFER_SIZE - MM_COMMUNICATE_HEADER_SIZE -
MM_VARIABLE_COMMUNICATE_SIZE;
- #endif
- /*
- There seems to be a bug in EDK2 miscalculating the boundaries and
- size checks, so deduct 2 more bytes to fulfill this requirement. Fix
@@ -697,7 +953,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);
/*
-- 2.17.1
Thanks /Ilias

turn on EFI MM communication
On corstone1000 platform MM communication between u-boot and the secure world (Optee) is done using the FF-A bus.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v6:
* corstone-1000: enable optee driver * corstone-1000: remove CONFIG_ARM_FFA_EFI_RUNTIME_MODE from the defconfig
v4:
* corstone-1000: turn on EFI MM communication
configs/corstone1000_defconfig | 4 ++++ include/configs/corstone1000.h | 9 +++++++++ 2 files changed, 13 insertions(+)
diff --git a/configs/corstone1000_defconfig b/configs/corstone1000_defconfig index ed2e0fe70a..4c9ed9fb71 100644 --- a/configs/corstone1000_defconfig +++ b/configs/corstone1000_defconfig @@ -52,3 +52,7 @@ CONFIG_DM_SERIAL=y CONFIG_USB=y CONFIG_USB_ISP1760=y CONFIG_ERRNO_STR=y +CONFIG_EFI_MM_COMM_TEE=y +CONFIG_TEE=y +CONFIG_OPTEE=y +CONFIG_ARM_FFA_TRANSPORT=y diff --git a/include/configs/corstone1000.h b/include/configs/corstone1000.h index 8e0230c135..997d0bebaf 100644 --- a/include/configs/corstone1000.h +++ b/include/configs/corstone1000.h @@ -14,6 +14,15 @@
#include <linux/sizes.h>
+#define FFA_SHARED_MM_BUFFER_SIZE SZ_4K /* 4 KB */ + +/* + * shared buffer physical address used for communication between + * u-boot and the MM SP + */ +#define FFA_SHARED_MM_BUFFER_ADDR (0x023F8000) +#define FFA_SHARED_MM_BUFFER_OFFSET (0) + #define V2M_BASE 0x80000000
#define CONFIG_PL011_CLOCK 50000000

On Thu, Oct 13, 2022 at 11:38:57AM +0100, Abdellatif El Khlifi wrote:
turn on EFI MM communication
On corstone1000 platform MM communication between u-boot and the secure world (Optee) is done using the FF-A bus.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v6:
- corstone-1000: enable optee driver
- corstone-1000: remove CONFIG_ARM_FFA_EFI_RUNTIME_MODE from the defconfig
v4:
- corstone-1000: turn on EFI MM communication
configs/corstone1000_defconfig | 4 ++++ include/configs/corstone1000.h | 9 +++++++++ 2 files changed, 13 insertions(+)
diff --git a/configs/corstone1000_defconfig b/configs/corstone1000_defconfig index ed2e0fe70a..4c9ed9fb71 100644 --- a/configs/corstone1000_defconfig +++ b/configs/corstone1000_defconfig @@ -52,3 +52,7 @@ CONFIG_DM_SERIAL=y CONFIG_USB=y CONFIG_USB_ISP1760=y CONFIG_ERRNO_STR=y +CONFIG_EFI_MM_COMM_TEE=y +CONFIG_TEE=y +CONFIG_OPTEE=y +CONFIG_ARM_FFA_TRANSPORT=y diff --git a/include/configs/corstone1000.h b/include/configs/corstone1000.h index 8e0230c135..997d0bebaf 100644 --- a/include/configs/corstone1000.h +++ b/include/configs/corstone1000.h @@ -14,6 +14,15 @@
#include <linux/sizes.h>
+#define FFA_SHARED_MM_BUFFER_SIZE SZ_4K /* 4 KB */
+/*
- shared buffer physical address used for communication between
- u-boot and the MM SP
- */
+#define FFA_SHARED_MM_BUFFER_ADDR (0x023F8000) +#define FFA_SHARED_MM_BUFFER_OFFSET (0)
The rest of the declarations on this file don't have () so please remove them. Also is FFA_SHARED_MM_BUFFER_ADDR used anywhere that would justify UL in the suffix?
Thanks /Ilias
#define V2M_BASE 0x80000000
#define CONFIG_PL011_CLOCK 50000000
2.17.1

On Mon, Oct 24, 2022 at 03:13:08PM +0300, Ilias Apalodimas wrote:
On Thu, Oct 13, 2022 at 11:38:57AM +0100, Abdellatif El Khlifi wrote:
turn on EFI MM communication
On corstone1000 platform MM communication between u-boot and the secure world (Optee) is done using the FF-A bus.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v6:
- corstone-1000: enable optee driver
- corstone-1000: remove CONFIG_ARM_FFA_EFI_RUNTIME_MODE from the defconfig
v4:
- corstone-1000: turn on EFI MM communication
configs/corstone1000_defconfig | 4 ++++ include/configs/corstone1000.h | 9 +++++++++ 2 files changed, 13 insertions(+)
diff --git a/configs/corstone1000_defconfig b/configs/corstone1000_defconfig index ed2e0fe70a..4c9ed9fb71 100644 --- a/configs/corstone1000_defconfig +++ b/configs/corstone1000_defconfig @@ -52,3 +52,7 @@ CONFIG_DM_SERIAL=y CONFIG_USB=y CONFIG_USB_ISP1760=y CONFIG_ERRNO_STR=y +CONFIG_EFI_MM_COMM_TEE=y +CONFIG_TEE=y +CONFIG_OPTEE=y +CONFIG_ARM_FFA_TRANSPORT=y diff --git a/include/configs/corstone1000.h b/include/configs/corstone1000.h index 8e0230c135..997d0bebaf 100644 --- a/include/configs/corstone1000.h +++ b/include/configs/corstone1000.h @@ -14,6 +14,15 @@
#include <linux/sizes.h>
+#define FFA_SHARED_MM_BUFFER_SIZE SZ_4K /* 4 KB */
+/*
- shared buffer physical address used for communication between
- u-boot and the MM SP
- */
+#define FFA_SHARED_MM_BUFFER_ADDR (0x023F8000) +#define FFA_SHARED_MM_BUFFER_OFFSET (0)
The rest of the declarations on this file don't have () so please remove them. Also is FFA_SHARED_MM_BUFFER_ADDR used anywhere that would justify UL in the suffix?
Done in v7.
Thanks /Ilias
#define V2M_BASE 0x80000000
#define CONFIG_PL011_CLOCK 50000000
2.17.1

Hi Abdellatif,
On Thu, Oct 13, 2022 at 11:38:47AM +0100, Abdellatif El Khlifi wrote:
Adding support for Arm FF-A v1.0 (Arm Firmware Framework for Armv8-A) [6].
FF-A describes interfaces (ABIs) that standardize communication between the Secure World and Normal World. These interfaces enable a pair of software sandboxes to communicate with each other. A sandbox aka partition could be a VM in the Normal or Secure world, an application in S-EL0, or a Trusted OS in S-EL1.
The FF-A transport is implemented as a data bus and a core driver is provided.
The FF-A driver uses the SMC ABIs defined by the FF-A specification to:
- Discover the presence of secure partitions (SPs) of interest
- Access an SP's service through communication protocols e.g. EFI MM communication protocol
The FF-A support provides the following features:
- Being generic by design and can be used by any Arm 64-bit platform
- The FF-A core driver
- The FF-A bus is discoverable on demand at runtime
- The driver provides callbacks to be used by clients to access the FF-A bus
- FF-A driver can be compiled and used without EFI
- Support for SMCCCv1.2 x0-x17 registers
- A new command called armffa is provided as an example of how to access the FF-A bus
- An FF-A Sandbox driver is provided with test cases
- Support for FF-A MM communication
- Enabling FF-A and MM communication in Corstone1000 platform
For more details about the FF-A core driver please refer to [7].
Please find at [8] an example of the expected boot logs when enabling FF-A support for a platform. In this example the platform is Corstone1000. But it can be any Arm 64-bit platform.
Changelog of the major changes:
v6:
- remove clearing x0-x17 registers after SMC calls
- drop use of EFI runtime support for FF-A (We decided with Linaro to add this later)
Thanks! This makes the whole process way way easier. I am bit overloaded atm but I'll try squeezing in reviewing these patches
- drop discovery from initcalls (discovery will be on demand by FF-A users)
- add FF-A runtime discovery at MM communication level
- update the documentation and move it to doc/arch/arm64.ffa.rst
v5: [5]
- move changelogs in each commit to the changes section
v4: [4]
- add FF-A support README (doc/README.ffa.drv)
- improving error handling by mapping the FF-A errors to standard errors and logs
- replacing panics with an error log
- align sandbox driver and tests with the new FF-A driver interfaces
and new way of error handling
[...]
Thanks /Ilias

On Thu, Oct 13, 2022 at 03:07:39PM +0300, Ilias Apalodimas wrote:
Hi Abdellatif,
On Thu, Oct 13, 2022 at 11:38:47AM +0100, Abdellatif El Khlifi wrote:
Adding support for Arm FF-A v1.0 (Arm Firmware Framework for Armv8-A) [6].
FF-A describes interfaces (ABIs) that standardize communication between the Secure World and Normal World. These interfaces enable a pair of software sandboxes to communicate with each other. A sandbox aka partition could be a VM in the Normal or Secure world, an application in S-EL0, or a Trusted OS in S-EL1.
The FF-A transport is implemented as a data bus and a core driver is provided.
The FF-A driver uses the SMC ABIs defined by the FF-A specification to:
- Discover the presence of secure partitions (SPs) of interest
- Access an SP's service through communication protocols e.g. EFI MM communication protocol
The FF-A support provides the following features:
- Being generic by design and can be used by any Arm 64-bit platform
- The FF-A core driver
- The FF-A bus is discoverable on demand at runtime
- The driver provides callbacks to be used by clients to access the FF-A bus
- FF-A driver can be compiled and used without EFI
- Support for SMCCCv1.2 x0-x17 registers
- A new command called armffa is provided as an example of how to access the FF-A bus
- An FF-A Sandbox driver is provided with test cases
- Support for FF-A MM communication
- Enabling FF-A and MM communication in Corstone1000 platform
For more details about the FF-A core driver please refer to [7].
Please find at [8] an example of the expected boot logs when enabling FF-A support for a platform. In this example the platform is Corstone1000. But it can be any Arm 64-bit platform.
Changelog of the major changes:
v6:
- remove clearing x0-x17 registers after SMC calls
- drop use of EFI runtime support for FF-A (We decided with Linaro to add this later)
Thanks! This makes the whole process way way easier. I am bit overloaded atm but I'll try squeezing in reviewing these patches
You're welcome! Happy to improve the patchset :)
- drop discovery from initcalls (discovery will be on demand by FF-A users)
- add FF-A runtime discovery at MM communication level
- update the documentation and move it to doc/arch/arm64.ffa.rst
v5: [5]
- move changelogs in each commit to the changes section
v4: [4]
- add FF-A support README (doc/README.ffa.drv)
- improving error handling by mapping the FF-A errors to standard errors and logs
- replacing panics with an error log
- align sandbox driver and tests with the new FF-A driver interfaces
and new way of error handling
[...]
Thanks /Ilias

Adding support for Arm FF-A v1.0 (Arm Firmware Framework for Armv8-A) [7].
FF-A describes interfaces (ABIs) that standardize communication between the Secure World and Normal World. These interfaces enable a pair of software sandboxes to communicate with each other. A sandbox aka partition could be a VM in the Normal or Secure world, an application in S-EL0, or a Trusted OS in S-EL1.
The FF-A transport is implemented as a data bus and a core driver is provided.
This implementation of the specification provides support for Aarch64.
The FF-A driver uses the SMC ABIs defined by the FF-A specification to:
- Discover the presence of secure partitions (SPs) of interest - Access an SP's service through communication protocols e.g. EFI MM communication protocol
The FF-A support provides the following features:
- Being generic by design and can be used by any Arm 64-bit platform - The FF-A core driver - The FF-A bus is discoverable on demand at runtime - The driver provides driver operations to be used by clients to access the FF-A bus - FF-A driver can be compiled and used without EFI - Support for SMCCCv1.2 x0-x17 registers - Support for SMC32 calling convention - Support for 32-bit and 64-bit FF-A direct messaging - A new command called armffa is provided as an example of how to access the FF-A bus - An FF-A Sandbox driver is provided with test cases - Support for FF-A MM communication (compatible with EFI boot time) - Enabling FF-A and MM communication in Corstone1000 platform
For more details about the FF-A core driver please refer to [8].
Please find at [9] an example of the expected boot logs when enabling FF-A support for a platform. In this example the platform is Corstone1000. But it can be any Arm 64-bit platform.
Changelog of the major changes: ===========================
v7:
* add support for 32-bit direct messaging (now we have 32-bit and 64-bit support) * set the MM door bell event to use 64-bit direct messaging * issue a compile time error when one of these macros are not found : FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_OFFSET, FFA_SHARED_MM_BUFFER_ADDR * make uuid_str_to_le_bin() implementation similar to uuid_str_to_bin() by using same APIs
v6: [6]
* remove clearing x0-x17 registers after SMC calls * drop use of EFI runtime support for FF-A (We decided with Linaro to add this later) * drop discovery from initcalls (discovery will be on demand by FF-A users) * add FF-A runtime discovery at MM communication level * update the documentation and move it to doc/arch/arm64.ffa.rst
v5: [5]
* move changelogs in each commit to the changes section
v4: [4]
* add FF-A support README (doc/README.ffa.drv) * improving error handling by mapping the FF-A errors to standard errors and logs * replacing panics with an error log * align sandbox driver and tests with the new FF-A driver interfaces and new way of error handling * use the new FF-A driver interfaces for MM communication * discover MM partitions at runtime * copy FF-A driver private data to EFI runtime section at ExitBootServices() * moving the FF-A driver work to drivers/firmware/arm-ffa * improving features discovery in FFA_FEATURES * add remove/unbind functions to the FF-A core device * improve how the driver behaves when bus discovery is done more than once * move clearing x0-x17 registers code into a new macro like done in the linux kernel * enable EFI MM communication for the Corstone1000 platform
v3: [3]
* port x0-x17 registers support from linux kernel as defined by SMCCCv1.2 * align the interfaces of the u-boot FF-A driver with those in the linux FF-A driver * remove the FF-A helper layer * make the u-boot FF-A driver independent from EFI * provide an optional config that enables copying the driver data to EFI runtime section at ExitBootServices service * use 64-bit version of FFA_RXTX_MAP, FFA_MSG_SEND_DIRECT_{REQ, RESP} * update armffa command with the new driver interfaces
v2 [2]:
* make FF-A bus discoverable using device_{bind, probe} APIs * remove device tree support
v1 [1]:
* introduce FF-A bus driver with device tree support * introduce armffa command * introduce FF-A Sandbox driver * add FF-A Sandbox test cases * introduce FF-A MM communication
Cheers, Abdellatif
[1]: https://lore.kernel.org/all/20220329151659.16894-1-abdellatif.elkhlifi@arm.c... [2]: https://lore.kernel.org/all/20220415122803.16666-1-abdellatif.elkhlifi@arm.c... [3]: https://lore.kernel.org/all/20220801172053.20163-1-abdellatif.elkhlifi@arm.c... [4]: https://lore.kernel.org/all/20220926101723.9965-1-abdellatif.elkhlifi@arm.co... [5]: https://lore.kernel.org/all/20220926140827.15125-1-abdellatif.elkhlifi@arm.c... [6]: https://lore.kernel.org/all/20221013103857.614-1-abdellatif.elkhlifi@arm.com... [7]: https://developer.arm.com/documentation/den0077/latest/ [8]: doc/arch/arm64.ffa.rst [9]: example of boot logs when enabling FF-A
``` U-Boot 2022.07 (Jul 11 2022 - 13:42:58 +0000) corstone1000 aarch64 ... [FFA] Conduit is SMC [FFA] FF-A driver 1.0 FF-A framework 1.0 [FFA] Versions are compatible [FFA] endpoint ID is 0 [FFA] Using 1 4KB page(s) for RX/TX buffers size [FFA] RX buffer at virtual address 0xfdf18000 [FFA] TX buffer at virtual address 0xfdf1a000 [FFA] RX/TX buffers mapped [FFA] Reading partitions data from the RX buffer [FFA] Partition ID 8001 : info cached [FFA] Partition ID 8002 : info cached [FFA] Partition ID 8003 : info cached [FFA] 3 partition(s) found and cached [FFA] Preparing for checking partitions count [FFA] Searching partitions using the provided UUID [FFA] No partition found. Querying framework ... [FFA] Reading partitions data from the RX buffer [FFA] Number of partition(s) found matching the UUID: 1 EFI: Pre-allocating 1 partition(s) info structures [FFA] Preparing for filling partitions info [FFA] Searching partitions using the provided UUID [FFA] Partition ID 8003 matches the provided UUID EFI: MM partition ID 0x8003 ... Booting /MemoryMapped(0x0,0x88200000,0xf00000) EFI stub: Booting Linux Kernel... EFI stub: Using DTB from configuration table EFI stub: Exiting boot services... [FFA] Freeing RX/TX buffers Booting Linux on physical CPU 0x0000000000 [0x410fd040] Linux version 5.19.9-yocto-standard (oe-user@oe-host) (aarch64-poky-linux-musl-gcc (GCC) 12.2.0, GNU ld (GNU Binutils) 2.39.0.20220819) #1 SMP PREEMPT Wed Sep 21 20:11:18 UTC 2022 ```
Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Achin Gupta achin.gupta@arm.com Cc: Drew Reed Drew.Reed@arm.com Cc: Vishnu Banavath vishnu.banavath@arm.com Cc: Xueliang Zhong Xueliang.Zhong@arm.com
Abdellatif El Khlifi (10): arm64: smccc: add support for SMCCCv1.2 x0-x17 registers lib: uuid: introduce uuid_str_to_le_bin function arm_ffa: introduce Arm FF-A low-level driver arm_ffa: efi: unmap RX/TX buffers arm_ffa: introduce armffa command arm_ffa: introduce the FF-A Sandbox driver arm_ffa: introduce Sandbox test cases for UCLASS_FFA arm_ffa: introduce armffa command Sandbox test arm_ffa: efi: introduce FF-A MM communication arm_ffa: efi: corstone1000: enable MM communication
MAINTAINERS | 11 + arch/arm/cpu/armv8/smccc-call.S | 53 + arch/arm/lib/asm-offsets.c | 14 + cmd/Kconfig | 10 + cmd/Makefile | 2 + cmd/armffa.c | 239 +++ configs/corstone1000_defconfig | 4 + configs/sandbox64_defconfig | 2 + configs/sandbox_defconfig | 2 + doc/arch/arm64.ffa.rst | 218 +++ doc/arch/index.rst | 1 + doc/arch/sandbox.rst | 1 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/firmware/arm-ffa/Kconfig | 36 + drivers/firmware/arm-ffa/Makefile | 7 + drivers/firmware/arm-ffa/arm-ffa-uclass.c | 16 + drivers/firmware/arm-ffa/arm_ffa_prv.h | 210 +++ drivers/firmware/arm-ffa/core.c | 1324 +++++++++++++++++ drivers/firmware/arm-ffa/sandbox.c | 659 ++++++++ .../firmware/arm-ffa/sandbox_arm_ffa_prv.h | 144 ++ include/arm_ffa.h | 93 ++ include/configs/corstone1000.h | 9 + include/dm/uclass-id.h | 4 + include/linux/arm-smccc.h | 43 + include/mm_communication.h | 5 + include/sandbox_arm_ffa.h | 91 ++ include/uuid.h | 8 + lib/efi_loader/Kconfig | 14 +- lib/efi_loader/efi_boottime.c | 13 + lib/efi_loader/efi_variable_tee.c | 269 +++- lib/uuid.c | 46 + test/cmd/Makefile | 2 + test/cmd/armffa.c | 40 + test/dm/Makefile | 2 + test/dm/ffa.c | 394 +++++ 36 files changed, 3983 insertions(+), 6 deletions(-) create mode 100644 cmd/armffa.c 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_prv.h create mode 100644 drivers/firmware/arm-ffa/core.c create mode 100644 drivers/firmware/arm-ffa/sandbox.c create mode 100644 drivers/firmware/arm-ffa/sandbox_arm_ffa_prv.h create mode 100644 include/arm_ffa.h create mode 100644 include/sandbox_arm_ffa.h create mode 100644 test/cmd/armffa.c create mode 100644 test/dm/ffa.c

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 abdellatif.elkhlifi@arm.com Reviewed-by: Jens Wiklander jens.wiklander@linaro.org Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org
---
Changelog: ===============
v7:
* improve indentation of ARM_SMCCC_1_2_REGS_Xn_OFFS
v4:
* rename the commit title and improve description new commit title: the current
v3:
* port x0-x17 registers support from linux kernel as defined by SMCCCv1.2 commit title: arm64: smccc: add Xn registers support used by SMC calls
arch/arm/cpu/armv8/smccc-call.S | 53 +++++++++++++++++++++++++++++++++ arch/arm/lib/asm-offsets.c | 14 +++++++++ include/linux/arm-smccc.h | 43 ++++++++++++++++++++++++++ 3 files changed, 110 insertions(+)
diff --git a/arch/arm/cpu/armv8/smccc-call.S b/arch/arm/cpu/armv8/smccc-call.S index dc92b28777..ec6f299bc9 100644 --- a/arch/arm/cpu/armv8/smccc-call.S +++ b/arch/arm/cpu/armv8/smccc-call.S @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2015, Linaro Limited + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */ #include <linux/linkage.h> #include <linux/arm-smccc.h> @@ -45,3 +47,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 22fd541f9a..db6d7ed234 100644 --- a/arch/arm/lib/asm-offsets.c +++ b/arch/arm/lib/asm-offsets.c @@ -9,6 +9,9 @@ * generate asm statements containing #defines, * compile this file to assembler, and then extract the * #defines from the assembly-language output. + * + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> @@ -117,6 +120,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 e1d09884a1..9105031d55 100644 --- a/include/linux/arm-smccc.h +++ b/include/linux/arm-smccc.h @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2015, Linaro Limited + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */ #ifndef __LINUX_ARM_SMCCC_H #define __LINUX_ARM_SMCCC_H @@ -70,6 +72,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

convert UUID string to little endian binary data
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v7:
* rename be_uuid_str_to_le_bin() to uuid_str_to_le_bin() * make uuid_str_to_le_bin() implementation similar to uuid_str_to_bin() by using same APIs
v4:
* rename ffa_uuid_str_to_bin to be_uuid_str_to_le_bin and put in a standalone commit (the current)
v3:
* introduce ffa_uuid_str_to_bin (provided by arm_ffa: introduce Arm FF-A low-level driver)
include/uuid.h | 8 ++++++++ lib/uuid.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+)
diff --git a/include/uuid.h b/include/uuid.h index 4a4883d3b5..293a8eb0a5 100644 --- a/include/uuid.h +++ b/include/uuid.h @@ -2,6 +2,8 @@ /* * Copyright (C) 2014 Samsung Electronics * Przemyslaw Marczak p.marczak@samsung.com + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */ #ifndef __UUID_H__ #define __UUID_H__ @@ -44,4 +46,10 @@ 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 - Converts a UUID string to little endian binary data + */ +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 465e1ac38f..cde5ae2bb7 100644 --- a/lib/uuid.c +++ b/lib/uuid.c @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-2.0+ /* * Copyright 2011 Calxeda, Inc. + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> @@ -346,6 +348,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(hextoul(uuid_str + 24, NULL)); + memcpy(uuid_bin + 10, (char *)&tmp64, 6); + + return 0; +} + /* * uuid_bin_to_str() - convert big endian binary data to string UUID or GUID. *

Hi Abdellatif,
On Mon, Nov 07, 2022 at 07:20:47PM +0000, Abdellatif El Khlifi wrote:
convert UUID string to little endian binary data
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v7:
- rename be_uuid_str_to_le_bin() to uuid_str_to_le_bin()
- make uuid_str_to_le_bin() implementation similar to uuid_str_to_bin() by using same APIs
v4:
- rename ffa_uuid_str_to_bin to be_uuid_str_to_le_bin and put in a standalone commit (the current)
v3:
- introduce ffa_uuid_str_to_bin (provided by arm_ffa: introduce Arm FF-A low-level driver)
include/uuid.h | 8 ++++++++ lib/uuid.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+)
diff --git a/include/uuid.h b/include/uuid.h index 4a4883d3b5..293a8eb0a5 100644 --- a/include/uuid.h +++ b/include/uuid.h @@ -2,6 +2,8 @@ /*
- Copyright (C) 2014 Samsung Electronics
- Przemyslaw Marczak p.marczak@samsung.com
- (C) Copyright 2022 ARM Limited
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#ifndef __UUID_H__ #define __UUID_H__ @@ -44,4 +46,10 @@ 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 - Converts a UUID string to little endian binary data
- */
+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 465e1ac38f..cde5ae2bb7 100644 --- a/lib/uuid.c +++ b/lib/uuid.c @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-2.0+ /*
- Copyright 2011 Calxeda, Inc.
- (C) Copyright 2022 ARM Limited
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#include <common.h> @@ -346,6 +348,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(hextoul(uuid_str + 24, NULL));
- memcpy(uuid_bin + 10, (char *)&tmp64, 6);
The cast isn't needed here
- return 0;
+}
/*
- uuid_bin_to_str() - convert big endian binary data to string UUID or GUID.
-- 2.17.1

On Tue, Nov 08, 2022 at 03:47:46PM +0200, Ilias Apalodimas wrote:
Hi Abdellatif,
On Mon, Nov 07, 2022 at 07:20:47PM +0000, Abdellatif El Khlifi wrote:
convert UUID string to little endian binary data
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v7:
- rename be_uuid_str_to_le_bin() to uuid_str_to_le_bin()
- make uuid_str_to_le_bin() implementation similar to uuid_str_to_bin() by using same APIs
v4:
- rename ffa_uuid_str_to_bin to be_uuid_str_to_le_bin and put in a standalone commit (the current)
v3:
- introduce ffa_uuid_str_to_bin (provided by arm_ffa: introduce Arm FF-A low-level driver)
include/uuid.h | 8 ++++++++ lib/uuid.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+)
diff --git a/include/uuid.h b/include/uuid.h index 4a4883d3b5..293a8eb0a5 100644 --- a/include/uuid.h +++ b/include/uuid.h @@ -2,6 +2,8 @@ /*
- Copyright (C) 2014 Samsung Electronics
- Przemyslaw Marczak p.marczak@samsung.com
- (C) Copyright 2022 ARM Limited
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#ifndef __UUID_H__ #define __UUID_H__ @@ -44,4 +46,10 @@ 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 - Converts a UUID string to little endian binary data
- */
+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 465e1ac38f..cde5ae2bb7 100644 --- a/lib/uuid.c +++ b/lib/uuid.c @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-2.0+ /*
- Copyright 2011 Calxeda, Inc.
- (C) Copyright 2022 ARM Limited
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#include <common.h> @@ -346,6 +348,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(hextoul(uuid_str + 24, NULL));
- memcpy(uuid_bin + 10, (char *)&tmp64, 6);
The cast isn't needed here
A good catch , thanks for pointing out :) I'll fix that in v8.
- return 0;
+}
/*
- uuid_bin_to_str() - convert big endian binary data to string UUID or GUID.
-- 2.17.1

On Fri, 11 Nov 2022 at 14:32, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
On Tue, Nov 08, 2022 at 03:47:46PM +0200, Ilias Apalodimas wrote:
Hi Abdellatif,
On Mon, Nov 07, 2022 at 07:20:47PM +0000, Abdellatif El Khlifi wrote:
convert UUID string to little endian binary data
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v7:
- rename be_uuid_str_to_le_bin() to uuid_str_to_le_bin()
- make uuid_str_to_le_bin() implementation similar to uuid_str_to_bin() by using same APIs
v4:
- rename ffa_uuid_str_to_bin to be_uuid_str_to_le_bin and put in a standalone commit (the current)
v3:
- introduce ffa_uuid_str_to_bin (provided by arm_ffa: introduce Arm FF-A low-level driver)
include/uuid.h | 8 ++++++++ lib/uuid.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+)
diff --git a/include/uuid.h b/include/uuid.h index 4a4883d3b5..293a8eb0a5 100644 --- a/include/uuid.h +++ b/include/uuid.h @@ -2,6 +2,8 @@ /*
- Copyright (C) 2014 Samsung Electronics
- Przemyslaw Marczak p.marczak@samsung.com
- (C) Copyright 2022 ARM Limited
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#ifndef __UUID_H__ #define __UUID_H__ @@ -44,4 +46,10 @@ 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 - Converts a UUID string to little endian binary data
- */
+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 465e1ac38f..cde5ae2bb7 100644 --- a/lib/uuid.c +++ b/lib/uuid.c @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-2.0+ /*
- Copyright 2011 Calxeda, Inc.
- (C) Copyright 2022 ARM Limited
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#include <common.h> @@ -346,6 +348,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(hextoul(uuid_str + 24, NULL));
- memcpy(uuid_bin + 10, (char *)&tmp64, 6);
The cast isn't needed here
A good catch , thanks for pointing out :) I'll fix that in v8.
Sure, don't rush v8. I'll go through the remaining either today or Monday
Thanks /Ilias
- return 0;
+}
/*
- uuid_bin_to_str() - convert big endian binary data to string UUID or GUID.
-- 2.17.1

Hi Abdel,
From: U-Boot u-boot-bounces@lists.denx.de on behalf of Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Date: Monday, 7 November 2022 at 19:22 To: Abdellatif El Khlifi Abdellatif.ElKhlifi@arm.com Cc: Drew Reed Drew.Reed@arm.com, Achin Gupta Achin.Gupta@arm.com, ilias.apalodimas@linaro.org ilias.apalodimas@linaro.org, jens.wiklander@linaro.org jens.wiklander@linaro.org, nd nd@arm.com, sjg@chromium.org sjg@chromium.org, trini@konsulko.com trini@konsulko.com, u-boot@lists.denx.de u-boot@lists.denx.de, Vishnu Banavath Vishnu.Banavath@arm.com, Xueliang Zhong Xueliang.Zhong@arm.com Subject: [PATCH v7 02/10] lib: uuid: introduce uuid_str_to_le_bin function convert UUID string to little endian binary data
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v7:
* rename be_uuid_str_to_le_bin() to uuid_str_to_le_bin() * make uuid_str_to_le_bin() implementation similar to uuid_str_to_bin() by using same APIs
v4:
* rename ffa_uuid_str_to_bin to be_uuid_str_to_le_bin and put in a standalone commit (the current)
v3:
* introduce ffa_uuid_str_to_bin (provided by arm_ffa: introduce Arm FF-A low-level driver)
include/uuid.h | 8 ++++++++ lib/uuid.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+)
diff --git a/include/uuid.h b/include/uuid.h index 4a4883d3b5..293a8eb0a5 100644 --- a/include/uuid.h +++ b/include/uuid.h @@ -2,6 +2,8 @@ /* * Copyright (C) 2014 Samsung Electronics * Przemyslaw Marczak p.marczak@samsung.com + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */ #ifndef __UUID_H__ #define __UUID_H__ @@ -44,4 +46,10 @@ 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 - Converts a UUID string to little endian binary data + */ +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 465e1ac38f..cde5ae2bb7 100644 --- a/lib/uuid.c +++ b/lib/uuid.c @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-2.0+ /* * Copyright 2011 Calxeda, Inc. + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> @@ -346,6 +348,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(hextoul(uuid_str + 24, NULL)); + memcpy(uuid_bin + 10, (char *)&tmp64, 6);
I think we might risk overflow here on platforms where “ulong” happens to be 32 bits.
+ + return 0; +} + /* * uuid_bin_to_str() - convert big endian binary data to string UUID or GUID. * -- 2.17.1

On Fri, Nov 11, 2022 at 05:02:26PM +0000, Anders Dellien wrote:
Hi Abdel,
From: U-Boot u-boot-bounces@lists.denx.de on behalf of Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Date: Monday, 7 November 2022 at 19:22 To: Abdellatif El Khlifi Abdellatif.ElKhlifi@arm.com Cc: Drew Reed Drew.Reed@arm.com, Achin Gupta Achin.Gupta@arm.com, ilias.apalodimas@linaro.org ilias.apalodimas@linaro.org, jens.wiklander@linaro.org jens.wiklander@linaro.org, nd nd@arm.com, sjg@chromium.org sjg@chromium.org, trini@konsulko.com trini@konsulko.com, u-boot@lists.denx.de u-boot@lists.denx.de, Vishnu Banavath Vishnu.Banavath@arm.com, Xueliang Zhong Xueliang.Zhong@arm.com Subject: [PATCH v7 02/10] lib: uuid: introduce uuid_str_to_le_bin function convert UUID string to little endian binary data
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v7:
- rename be_uuid_str_to_le_bin() to uuid_str_to_le_bin()
- make uuid_str_to_le_bin() implementation similar to uuid_str_to_bin() by using same APIs
v4:
- rename ffa_uuid_str_to_bin to be_uuid_str_to_le_bin and put in a standalone commit (the current)
v3:
- introduce ffa_uuid_str_to_bin (provided by arm_ffa: introduce Arm FF-A low-level driver)
include/uuid.h | 8 ++++++++ lib/uuid.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+)
diff --git a/include/uuid.h b/include/uuid.h index 4a4883d3b5..293a8eb0a5 100644 --- a/include/uuid.h +++ b/include/uuid.h @@ -2,6 +2,8 @@ /*
- Copyright (C) 2014 Samsung Electronics
- Przemyslaw Marczak p.marczak@samsung.com
- (C) Copyright 2022 ARM Limited
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#ifndef __UUID_H__ #define __UUID_H__ @@ -44,4 +46,10 @@ 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 - Converts a UUID string to little endian binary data
- */
+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 465e1ac38f..cde5ae2bb7 100644 --- a/lib/uuid.c +++ b/lib/uuid.c @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-2.0+ /*
- Copyright 2011 Calxeda, Inc.
- (C) Copyright 2022 ARM Limited
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#include <common.h> @@ -346,6 +348,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(hextoul(uuid_str + 24, NULL));
memcpy(uuid_bin + 10, (char *)&tmp64, 6);
I think we might risk overflow here on platforms where “ulong” happens to be 32 bits.
Thanks Anders, a very valid point.
I will use simple_strtoull() in place of hextoul()
I'll make the change in v8.
return 0;
+}
/*
- uuid_bin_to_str() - convert big endian binary data to string UUID or GUID.
-- 2.17.1

Add the core driver 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.
In U-Boot FF-A design, FF-A is considered as a discoverable bus. 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:
- partition_info_get - sync_send_receive - rxtx_unmap
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 abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v7:
* add support for 32-bit direct messaging * rename be_uuid_str_to_le_bin() to uuid_str_to_le_bin() * improve the declaration of error handling mapping * stating in doc/arch/arm64.ffa.rst that EFI runtime is not supported
v6:
* drop use of EFI runtime support (We decided with Linaro to add this later) * drop discovery from initcalls (discovery will be on demand by FF-A users) * set the alignment of the RX/TX buffers to the larger translation granule size * move FF-A RX/TX buffers unmapping at ExitBootServices() to a separate commit * update the documentation and move it to doc/arch/arm64.ffa.rst
v4:
* add doc/README.ffa.drv * moving the FF-A driver work to drivers/firmware/arm-ffa * use less #ifdefs in lib/efi_loader/efi_boottime.c and replace #if defined by #if CONFIG_IS_ENABLED * improving error handling by mapping the FF-A errors to standard errors and logs * replacing panics with an error log and returning an error code * improving features discovery in FFA_FEATURES by introducing rxtx_min_pages private data field * add ffa_remove and ffa_unbind functions * improve how the driver behaves when bus discovery is done more than once
v3:
* align the interfaces of the U-Boot FF-A driver with those in the linux FF-A driver * remove the FF-A helper layer * make the U-Boot FF-A driver independent from EFI * provide an optional config that enables copying the driver data to EFI runtime section at ExitBootServices service * use 64-bit version of FFA_RXTX_MAP, FFA_MSG_SEND_DIRECT_{REQ, RESP}
v2:
* make FF-A bus discoverable using device_{bind, probe} APIs * remove device tree support
v1:
* introduce FF-A bus driver with device tree support
MAINTAINERS | 7 + doc/arch/arm64.ffa.rst | 218 ++++ doc/arch/index.rst | 1 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/firmware/arm-ffa/Kconfig | 30 + drivers/firmware/arm-ffa/Makefile | 6 + drivers/firmware/arm-ffa/arm-ffa-uclass.c | 16 + drivers/firmware/arm-ffa/arm_ffa_prv.h | 205 ++++ drivers/firmware/arm-ffa/core.c | 1310 +++++++++++++++++++++ include/arm_ffa.h | 93 ++ include/dm/uclass-id.h | 4 + 12 files changed, 1893 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_prv.h create mode 100644 drivers/firmware/arm-ffa/core.c create mode 100644 include/arm_ffa.h
diff --git a/MAINTAINERS b/MAINTAINERS index 1cf99c1393..450b5725ce 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -263,6 +263,13 @@ 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 abdellatif.elkhlifi@arm.com +S: Maintained +F: doc/arch/arm64.ffa.rst +F: drivers/firmware/arm-ffa/ +F: include/arm_ffa.h + ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de M: Fabio Estevam festevam@gmail.com diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst new file mode 100644 index 0000000000..dfcec82e45 --- /dev/null +++ b/doc/arch/arm64.ffa.rst @@ -0,0 +1,218 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +Arm FF-A Driver +=============== + +Summary +------- + +FF-A stands for Firmware Framework for Arm A-profile processors. + +FF-A specifies interfaces that enable a pair of software sandboxes to +communicate with each other. A sandbox aka partition could +be a VM in the Normal or Secure world, an application in S-EL0, or a +Trusted OS in S-EL1. + +This FF-A driver implements the interfaces to communicate with partitions in +the Secure world aka Secure partitions (SPs). + +The driver 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. + +FF-A driver 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 the FF-A driver supports EFI boot time only. + +Runtime support will be added in future developments. + +FF-A and SMC specifications +------------------------------------------- + +The current implementation of the driver relies on FF-A specification v1.0 +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 driver has been tested with OP-TEE which supports SMC32 calling convention. + +For more details please refer to the FF-A v1.0 spec: +https://documentation-service.arm.com/static/5fb7e8a6ca04df4095c1d65e?token= + +Hypervisors are supported if they are configured to trap SMC calls. + +The FF-A driver uses 64-bit registers as per SMCCCv1.2 specification. + +For more details please refer to the SMC Calling Convention v1.2 spec: +https://documentation-service.arm.com/static/5f8edaeff86e16515cdbe4c6?token= + +Supported hardware +-------------------------------- + +Aarch64 plaforms + +Configuration +---------------------- + +CONFIG_ARM_FFA_TRANSPORT + Enables the FF-A bus driver. Turn this on if you want to use FF-A + communication. + +CONFIG_SANDBOX_FFA + Enables FF-A Sandbox driver. This emulates the FF-A ABIs handling under + Sandbox and provides functional tests for FF-A. + +FF-A ABIs under the hood +--------------------------------------- + +Invoking an FF-A ABI involves providing to the secure world/hypervisor the +expected arguments from the ABI. + +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 FF-A driver (non-secure world). + +The driver reads the response and processes it accordingly. + +This methodology applies to all the FF-A ABIs in the driver. + +FF-A bus discovery in U-Boot +------------------------------------------- + +When CONFIG_ARM_FFA_TRANSPORT is enabled, the FF-A bus is discovered on +demand by the clients (users). + +Clients can discover the FF-A bus using ffa_bus_discover() API which triggers the +discovery process. + +ffa_bus_discover() creates, binds and probes the arm_ffa device using +device_{bind, probe} APIs. + +The discovery process consists in communicating with secure world (or hypervisor) +and querying specific data. + +The discovery process takes place during the arm_ffa device probing which is +handled by ffa_probe(). + +The FF-A bus discovery is successful and the bus is ready for use when these +operations succeed: + +- querying the FF-A framework version +- querying from secure world the U-Boot endpoint ID +- querying from secure world the RX/TX mapping features +- mapping the RX/TX buffers +- querying from secure world all the partitions information + +Discovery failure results in a probing failure and the arm_ffa device is +destroyed. + +Requirements for clients +------------------------------------- + +When using the FF-A bus with EFI, clients must: + +- Query SPs in EFI boot time mode using the service UUID. +- Unmap RX/TX buffers before EFI runtime mode starts. + +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 by the user before EFI runtime mode +starts. The driver provides a bus operation for that: rxtx_unmap() + +If RX/TX buffers created by U-Boot are not unmapped and by +consequence becoming available at EFI runtime, secure world will get confused +about RX/TX buffers ownership (U-Boot vs kernel). + +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 stays the same: SMC32. + +The bus driver layer +------------------------------ + +The driver comes on top of the SMCCC layer and is implemented in +drivers/firmware/arm-ffa/core.c + +The driver provides the following features: + +- 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, the driver takes care of how to transport + that to the secure world/hypervisor using FF-A + +- The driver provides callbacks to be used by clients to access the FF-A bus: + +partition_info_get +sync_send_receive +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 + +Using armffa command +----------------------------------- + +armffa is an implementation defined command showcasing how to use the FF-A driver and how to invoke +its operations. + +This provides a guidance to the client developers on how to call the FF-A bus +interfaces. + +Usage: + +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 the arm_ffa device info + +Contributors +------------ + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com diff --git a/doc/arch/index.rst b/doc/arch/index.rst index 792d9182c3..8d1ab0ad4e 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/Kconfig b/drivers/Kconfig index 8b6fead351..b06b1ae481 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -6,6 +6,8 @@ source "drivers/core/Kconfig"
source "drivers/adc/Kconfig"
+source "drivers/firmware/arm-ffa/Kconfig" + source "drivers/ata/Kconfig"
source "drivers/axi/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index a1700c819d..b86c1d2a42 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -111,6 +111,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/arm-ffa/Kconfig b/drivers/firmware/arm-ffa/Kconfig new file mode 100644 index 0000000000..e4914b9bc7 --- /dev/null +++ b/drivers/firmware/arm-ffa/Kconfig @@ -0,0 +1,30 @@ +# 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 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. + + This driver 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. + 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. + + For more details about the FF-A driver, 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 0000000000..043a8915be --- /dev/null +++ b/drivers/firmware/arm-ffa/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2022 +# Abdellatif El Khlifi, Arm Limited, abdellatif.elkhlifi@arm.com. + +obj-y += arm-ffa-uclass.o core.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 0000000000..7d9695d289 --- /dev/null +++ b/drivers/firmware/arm-ffa/arm-ffa-uclass.c @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <dm.h> +#include <asm/global_data.h> + +DECLARE_GLOBAL_DATA_PTR; + +UCLASS_DRIVER(ffa) = { + .name = "ffa", + .id = UCLASS_FFA, +}; diff --git a/drivers/firmware/arm-ffa/arm_ffa_prv.h b/drivers/firmware/arm-ffa/arm_ffa_prv.h new file mode 100644 index 0000000000..ad7430ada9 --- /dev/null +++ b/drivers/firmware/arm-ffa/arm_ffa_prv.h @@ -0,0 +1,205 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __ARM_FFA_PRV_H +#define __ARM_FFA_PRV_H + +#include <arm_ffa.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/arm-smccc.h> + +/* + * This header is private. It is exclusively used by the FF-A driver + */ + +/* FF-A core driver name */ +#define FFA_DRV_NAME "arm_ffa" + +/* 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 +}; + +/* 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) + +/* The FF-A SMC function definitions */ + +typedef struct arm_smccc_1_2_regs ffa_value_t; +typedef void (*invoke_ffa_fn_t)(ffa_value_t args, ffa_value_t *res); + +/* + * 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 */ +}; + +/** + * 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 - structure hosting 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 + * + * Data structure hosting 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 { + u64 rxbuf; /* virtual address */ + u64 txbuf; /* virtual address */ + size_t rxtx_min_pages; /* minimum number of pages in each of the RX/TX buffers */ +}; + +/** + * 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_partitions - descriptors for all secure partitions + * @count: The number of partitions descriptors + * @descs The partitions descriptors table + * + * This data structure contains the partitions descriptors table + */ +struct ffa_partitions { + u32 count; + struct ffa_partition_desc *descs; /* virtual address */ +}; + +/** + * struct ffa_prvdata - the driver private data structure + * + * @dev: The arm_ffa device under u-boot driver model + * @ffa_ops: The driver operations structure + * @fwk_version: FF-A framework version + * @id: u-boot endpoint ID + * @partitions: The partitions descriptors structure + * @pair: The RX/TX buffers pair + * @invoke_ffa_fn: The function executing the FF-A function + * + * The driver data structure hosting all resident data. + */ +struct ffa_prvdata { + struct udevice *dev; + struct ffa_bus_ops ffa_ops; + u32 fwk_version; + u16 id; + struct ffa_partitions partitions; + struct ffa_rxtxpair pair; + invoke_ffa_fn_t invoke_ffa_fn; +}; + +/** + * ffa_device_get - create, bind and probe the arm_ffa device + */ +int ffa_device_get(void); + +/** + * ffa_bus_prvdata_get - bus driver private data getter + */ +struct ffa_prvdata **ffa_bus_prvdata_get(void); + +#endif diff --git a/drivers/firmware/arm-ffa/core.c b/drivers/firmware/arm-ffa/core.c new file mode 100644 index 0000000000..b955e5187b --- /dev/null +++ b/drivers/firmware/arm-ffa/core.c @@ -0,0 +1,1310 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include "arm_ffa_prv.h" +#include <asm/global_data.h> +#include <common.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <dm/devres.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> +#include <log.h> +#include <malloc.h> +#include <string.h> +#include <uuid.h> + +DECLARE_GLOBAL_DATA_PTR; + +/** + * The device private data structure containing all the + * data read from secure world + */ +struct ffa_prvdata *ffa_priv_data; + +/* 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, +}; + +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 + * + * This function maps 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 + */ +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 + * + * This function maps 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 + */ +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; + + ffa_err("%s", err_msg_map[abi_idx].err_str[err_idx]); + + return 0; +} + +/* + * Driver core functions + */ + +/** + * ffa_remove_device - removes the arm_ffa device + * @dev: the device to be removed + * + * This function makes sure the arm_ffa device is removed + * No need to free the kmalloced data when the device is destroyed. + * It's automatically done by devm management by + * device_remove() -> device_free() -> devres_release_probe(). + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int ffa_remove_device(struct udevice *dev) +{ + int ret; + + if (!dev) { + ffa_err("no udevice found"); + return -ENODEV; + } + + ret = device_remove(dev, DM_REMOVE_NORMAL); + if (ret) { + ffa_err("unable to remove. err:%d\n", ret); + return ret; + } + + ffa_info("device removed and freed"); + + ret = device_unbind(dev); + if (ret) { + ffa_err("unable to unbind. err:%d\n", ret); + return ret; + } + + ffa_info("device unbound"); + + return 0; +} + +/** + * ffa_device_get - create, bind and probe the arm_ffa device + * + * This function makes sure the arm_ffa device is + * created, bound to this driver, probed and ready to use. + * Arm FF-A transport is implemented through a single u-boot + * device managing the FF-A bus (arm_ffa). + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int ffa_device_get(void) +{ + int ret; + struct udevice *dev = NULL; + + ret = device_bind(dm_root(), DM_DRIVER_GET(arm_ffa), FFA_DRV_NAME, NULL, ofnode_null(), + &dev); + if (ret) + return ret; + + /* The FF-A bus discovery succeeds when probing is successful */ + ret = device_probe(dev); + if (ret) { + ffa_err("arm_ffa device probing failed"); + ffa_remove_device(dev); + return ret; + } + + return 0; +} + +/** + * ffa_get_version - FFA_VERSION handler function + * + * This function implements FFA_VERSION FF-A function + * to get from the secure world the FF-A framework version + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_get_version(void) +{ + u16 major, minor; + ffa_value_t res = {0}; + int ffa_errno; + + ffa_priv_data->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); + + ffa_info("FF-A driver %d.%d\nFF-A framework %d.%d", + FFA_MAJOR_VERSION, FFA_MINOR_VERSION, major, minor); + + if ((major == FFA_MAJOR_VERSION && minor >= FFA_MINOR_VERSION)) { + ffa_info("Versions are compatible "); + + ffa_priv_data->fwk_version = res.a0; + + return 0; + } + + ffa_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 + * + * This function implements 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(void) +{ + ffa_value_t res = {0}; + int ffa_errno; + + ffa_priv_data->invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_32(FFA_ID_GET), + }, &res); + + if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) { + ffa_priv_data->id = GET_SELF_ENDPOINT_ID((u32)res.a2); + ffa_info("endpoint ID is %u", ffa_priv_data->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 - sets the minimum number of pages in each of the RX/TX buffers + * @prop_field: properties field obtained from FFA_FEATURES ABI + * + * This function sets the minimum number of pages + * in each of the RX/TX buffers in the private data structure + * + * Return: + * + * buf_4k_pages points to the returned number of pages + * 0 on success. Otherwise, failure + */ +static int ffa_set_rxtx_buffers_pages_cnt(u32 prop_field) +{ + if (!ffa_priv_data) + return -EINVAL; + + switch (prop_field) { + case RXTX_4K: + ffa_priv_data->pair.rxtx_min_pages = 1; + break; + case RXTX_16K: + ffa_priv_data->pair.rxtx_min_pages = 4; + break; + case RXTX_64K: + ffa_priv_data->pair.rxtx_min_pages = 16; + break; + default: + ffa_err("RX/TX buffer size not supported"); + return -EINVAL; + } + + return 0; +} + +/** + * ffa_get_rxtx_map_features - FFA_FEATURES handler function with FFA_RXTX_MAP argument + * + * This function implements 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(void) +{ + ffa_value_t res = {0}; + int ffa_errno; + + ffa_priv_data->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(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 - frees the RX/TX buffers + * + * This function frees the RX/TX buffers + * + */ +static void ffa_free_rxtx_buffers(void) +{ + ffa_info("Freeing RX/TX buffers"); + + if (ffa_priv_data->pair.rxbuf) { + free((void *)ffa_priv_data->pair.rxbuf); + ffa_priv_data->pair.rxbuf = 0; + } + + if (ffa_priv_data->pair.txbuf) { + free((void *)ffa_priv_data->pair.txbuf); + ffa_priv_data->pair.txbuf = 0; + } +} + +/** + * ffa_alloc_rxtx_buffers - allocates the RX/TX buffers + * + * This function is 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. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_alloc_rxtx_buffers(void) +{ + u64 bytes; + + ffa_info("Using %lu 4KB page(s) for RX/TX buffers size", + ffa_priv_data->pair.rxtx_min_pages); + + bytes = ffa_priv_data->pair.rxtx_min_pages * SZ_4K; + + /* + * The alignment of the RX and TX buffers must be equal + * to the larger translation granule size + */ + + ffa_priv_data->pair.rxbuf = (u64)memalign(bytes, bytes); + if (!ffa_priv_data->pair.rxbuf) { + ffa_err("failure to allocate RX buffer"); + return -ENOBUFS; + } + + ffa_info("RX buffer at virtual address 0x%llx", ffa_priv_data->pair.rxbuf); + + ffa_priv_data->pair.txbuf = (u64)memalign(bytes, bytes); + if (!ffa_priv_data->pair.txbuf) { + free((void *)ffa_priv_data->pair.rxbuf); + ffa_priv_data->pair.rxbuf = 0; + ffa_err("failure to allocate the TX buffer"); + return -ENOBUFS; + } + + ffa_info("TX buffer at virtual address 0x%llx", ffa_priv_data->pair.txbuf); + + /* + * make sure the buffers are cleared before use + */ + memset((void *)ffa_priv_data->pair.rxbuf, 0, bytes); + memset((void *)ffa_priv_data->pair.txbuf, 0, bytes); + + return 0; +} + +/** + * ffa_map_rxtx_buffers - FFA_RXTX_MAP handler function + * + * This function implements FFA_RXTX_MAP FF-A function + * to map the RX/TX buffers + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_map_rxtx_buffers(void) +{ + int ret; + ffa_value_t res = {0}; + int ffa_errno; + + ret = ffa_alloc_rxtx_buffers(); + 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 + */ + + ffa_priv_data->invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_64(FFA_RXTX_MAP), + .a1 = ffa_priv_data->pair.txbuf, + .a2 = ffa_priv_data->pair.rxbuf, + .a3 = ffa_priv_data->pair.rxtx_min_pages, + }, &res); + + if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) { + ffa_info("RX/TX buffers mapped"); + return 0; + } + + ffa_errno = res.a2; + ffa_print_error_log(FFA_RXTX_MAP, ffa_errno); + + ffa_free_rxtx_buffers(); + + return ffa_to_std_errno(ffa_errno); +} + +/** + * ffa_unmap_rxtx_buffers - FFA_RXTX_UNMAP handler function + * + * This function implements FFA_RXTX_UNMAP FF-A function + * to unmap the RX/TX buffers + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_unmap_rxtx_buffers(void) +{ + ffa_value_t res = {0}; + int ffa_errno; + + ffa_priv_data->invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_32(FFA_RXTX_UNMAP), + .a1 = PREP_SELF_ENDPOINT_ID(ffa_priv_data->id), + }, &res); + + if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) { + ffa_free_rxtx_buffers(); + 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 - FFA_RX_RELEASE handler function + * + * This function invokes 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(void) +{ + ffa_value_t res = {0}; + int ffa_errno; + + ffa_priv_data->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 - checks whether two given UUIDs are identical + * @uuid1: first UUID + * @uuid2: second UUID + * + * This function is used by ffa_read_partitions_info to search + * for a UUID in the partitions descriptors table + * + * Return: + * + * 1 when UUIDs match. Otherwise, 0 + */ +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 - reads the data queried by FFA_PARTITION_INFO_GET + * and saves it in the private structure + * @count: The number of partitions queried + * @part_uuid: Pointer to the partition(s) UUID + * + * This function reads the partitions information + * returned by the FFA_PARTITION_INFO_GET and saves it in the private + * data structure. + * + * Return: + * + * The private data structure is updated with the partition(s) information + * 0 is returned on success. Otherwise, failure + */ +static int ffa_read_partitions_info(u32 count, struct ffa_partition_uuid *part_uuid) +{ + if (!count) { + ffa_err("no partition detected"); + return -ENODATA; + } + + ffa_info("Reading partitions data from the RX buffer"); + + 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 = ffa_priv_data->pair.rxtx_min_pages * SZ_4K; + + if (data_bytes > buf_bytes) { + ffa_err("partitions data size exceeds the RX buffer size:"); + ffa_err(" sizes in bytes: data %llu , RX buffer %llu ", + data_bytes, + buf_bytes); + + return -ENOMEM; + } + + ffa_priv_data->partitions.descs = devm_kmalloc(ffa_priv_data->dev, data_bytes, + __GFP_ZERO); + if (!ffa_priv_data->partitions.descs) { + ffa_err("cannot allocate partitions data buffer"); + return -ENOMEM; + } + + parts_info = (struct ffa_partition_info *)ffa_priv_data->pair.rxbuf; + + for (desc_idx = 0 ; desc_idx < count ; desc_idx++) { + ffa_priv_data->partitions.descs[desc_idx].info = + parts_info[desc_idx]; + + ffa_info("Partition ID %x : info cached", + ffa_priv_data->partitions.descs[desc_idx].info.id); + } + + ffa_priv_data->partitions.count = count; + + ffa_info("%d partition(s) found and cached", count); + + } else { + u32 rx_desc_idx, cached_desc_idx; + struct ffa_partition_info *parts_info; + u8 desc_found; + + parts_info = (struct ffa_partition_info *)ffa_priv_data->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 < ffa_priv_data->partitions.count; + cached_desc_idx++) { + /* + * save the UUID + */ + if (ffa_priv_data->partitions.descs[cached_desc_idx].info.id == + parts_info[rx_desc_idx].id) { + ffa_priv_data->partitions.descs[cached_desc_idx].sp_uuid = + *part_uuid; + + desc_found = 1; + break; + } + } + + if (!desc_found) + return -ENODATA; + } + } + + return 0; +} + +/** + * ffa_query_partitions_info - invokes FFA_PARTITION_INFO_GET and saves partitions data + * + * @part_uuid: Pointer to the partition(s) UUID + * @pcount: Pointer to the number of partitions variable filled when querying + * + * This function executes the FFA_PARTITION_INFO_GET + * to query the partitions data. Then, it calls ffa_read_partitions_info + * to save the data in the private data structure. + * + * 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 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; + } + + ffa_priv_data->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((u32)res.a2, part_uuid); + if (ret) { + ffa_err("failed to read partition(s) data , error (%d)", ret); + ffa_release_rx_buffer(); + 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(); + + 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 - FFA_PARTITION_INFO_GET handler function + * + * The passed arguments: + * Mode 1: When getting from the driver the number of + * secure partitions: + * @uuid_str: pointer to the UUID string + * @parts_size: pointer to the variable that contains the number of partitions + * The variable will be set by the driver + * @buffer: NULL + * + * Mode 2: When requesting the driver to return the + * partitions information: + * @uuid_str: pointer to the UUID string + * @parts_size: pointer to the size of the SPs information buffer in bytes + * @buffer: pointer to SPs information buffer + * (allocated by the client). + * The buffer will be filled by the driver + * + * This function queries the secure partition data from + * the private data structure. If not found, it invokes 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 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(). + * + * When invoked through a client request, ffa_get_partitions_info should be + * called twice. First call is to get from the driver the number of secure + * partitions (SPs) associated to a particular UUID. + * Then, the caller (client) allocates the buffer to host the SPs data and + * issues a 2nd call. Then, the driver fills the SPs data in the pre-allocated + * buffer. + * + * To achieve the mechanism described above, ffa_get_partitions_info uses the + * following functions: + * ffa_read_partitions_info + * ffa_query_partitions_info + * + * Return: + * + * @parts_size: When pointing to the number of partitions variable, the number is + * set by the driver. + * When pointing to the partitions information buffer size, the buffer will be + * filled by the driver. + * + * On success 0 is returned. Otherwise, failure + */ +static int ffa_get_partitions_info(const char *uuid_str, + u32 *parts_size, struct ffa_partition_info *buffer) +{ + /* + * fill_data: + * 0: return the SP count + * 1: fill SP data and return it to the caller + * -1: undefined mode + */ + int fill_data = -1; + u32 desc_idx, client_desc_idx; + struct ffa_partition_uuid part_uuid = {0}; + u32 client_desc_max_cnt; + u32 parts_found = 0; + + if (!ffa_priv_data->partitions.count || !ffa_priv_data->partitions.descs) { + ffa_err("no partition installed"); + return -EINVAL; + } + + if (!uuid_str) { + ffa_err("no UUID provided"); + return -EINVAL; + } + + if (!parts_size) { + ffa_err("no size/count provided"); + return -EINVAL; + } + + if (uuid_str_to_le_bin(uuid_str, (unsigned char *)&part_uuid)) { + ffa_err("invalid UUID"); + return -EINVAL; + } + + if (!buffer) { + /* Mode 1: getting the number of secure partitions */ + + fill_data = 0; + + ffa_info("Preparing for checking partitions count"); + + } else if ((*parts_size >= sizeof(struct ffa_partition_info)) && + !(*parts_size % sizeof(struct ffa_partition_info))) { + /* Mode 2: retrieving the partitions information */ + + fill_data = 1; + + client_desc_idx = 0; + + /* + * number of empty descriptors preallocated by the caller + */ + client_desc_max_cnt = *parts_size / sizeof(struct ffa_partition_info); + + ffa_info("Preparing for filling partitions info"); + + } else { + ffa_err("invalid function arguments provided"); + return -EINVAL; + } + + ffa_info("Searching partitions using the provided UUID"); + + /* + * search in the cached partitions + */ + for (desc_idx = 0; + desc_idx < ffa_priv_data->partitions.count; + desc_idx++) { + if (ffa_uuid_are_identical(&ffa_priv_data->partitions.descs[desc_idx].sp_uuid, + &part_uuid)) { + ffa_info("Partition ID %x matches the provided UUID", + ffa_priv_data->partitions.descs[desc_idx].info.id); + + parts_found++; + + if (fill_data) { + /* + * trying to fill the partition info in the input buffer + */ + + if (client_desc_idx < client_desc_max_cnt) { + buffer[client_desc_idx++] = + ffa_priv_data->partitions.descs[desc_idx].info; + continue; + } + + ffa_err("failed to fill the current descriptor client buffer full"); + return -ENOBUFS; + } + } + } + + if (!parts_found) { + int ret; + + ffa_info("No partition found. Querying framework ..."); + + ret = ffa_query_partitions_info(&part_uuid, &parts_found); + + if (ret == 0) { + if (!fill_data) { + *parts_size = parts_found; + + ffa_info("Number of partition(s) found matching the UUID: %d", + parts_found); + } else { + /* + * If SPs data detected, they are already in the private data + * structure, retry searching SP data again to return them + * to the caller + */ + if (parts_found) + ret = ffa_get_partitions_info(uuid_str, parts_size, buffer); + else + ret = -ENODATA; + } + } + + return ret; + } + + /* partition(s) found */ + if (!fill_data) + *parts_size = parts_found; + + return 0; +} + +/** + * ffa_cache_partitions_info - Queries and saves all secure partitions data + * + * This function invokes 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 the + * resident private data structure and we keep the UUID field empty + * (in FF-A 1.0 UUID is not provided by the partition descriptor) + * + * This function is 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(void) +{ + return ffa_query_partitions_info(NULL, NULL); +} + +/** + * ffa_msg_send_direct_req - FFA_MSG_SEND_DIRECT_{REQ,RESP} handler function + * @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 + */ +static int ffa_msg_send_direct_req(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; + + if (!ffa_priv_data || !ffa_priv_data->invoke_ffa_fn) + return -EINVAL; + + /* No partition installed */ + if (!ffa_priv_data->partitions.count || !ffa_priv_data->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); + } + + ffa_priv_data->invoke_ffa_fn((ffa_value_t){ + .a0 = req_mode, + .a1 = PREP_SELF_ENDPOINT_ID(ffa_priv_data->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)) + ffa_priv_data->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); +} + +/** + * __arm_ffa_fn_smc - 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 + * + * Return: void + */ +void __arm_ffa_fn_smc(ffa_value_t args, ffa_value_t *res) +{ + arm_smccc_1_2_smc(&args, res); +} + +/** + * ffa_set_smc_conduit - Set the SMC conduit + * + * This function selects the SMC conduit by setting the driver invoke function + * to SMC assembly function + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_set_smc_conduit(void) +{ + ffa_priv_data->invoke_ffa_fn = __arm_ffa_fn_smc; + + if (!ffa_priv_data->invoke_ffa_fn) { + ffa_err("failure to set the invoke function"); + return -EINVAL; + } + + ffa_info("Conduit is SMC"); + + return 0; +} + +/** + * ffa_set_bus_ops - Set the bus driver operations + * + * Setting the driver callbacks. + * + */ +static void ffa_set_bus_ops(void) +{ + ffa_priv_data->ffa_ops.partition_info_get = ffa_get_partitions_info; + ffa_priv_data->ffa_ops.sync_send_receive = ffa_msg_send_direct_req; + ffa_priv_data->ffa_ops.rxtx_unmap = ffa_unmap_rxtx_buffers; +} + +/** + * ffa_alloc_prvdata - allocate the driver main data structure and sets the device + * @dev: the arm_ffa device + * + * This function creates the main data structure embedding all the driver data. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_alloc_prvdata(struct udevice *dev) +{ + if (!dev) { + ffa_err("no udevice found"); + return -ENODEV; + } + + /* The device is registered with the DM. Let's create the driver main data structure*/ + + ffa_priv_data = devm_kmalloc(dev, sizeof(struct ffa_prvdata), __GFP_ZERO); + if (!ffa_priv_data) { + ffa_err("can not allocate the driver main data structure"); + return -ENOMEM; + } + + ffa_priv_data->dev = dev; + + return 0; +} + +/** + * ffa_probe - The driver probe function + * @dev: the arm_ffa device + * + * Probing is done at boot time and triggered by the uclass device discovery. + * At probe level the following actions are done: + * - setting the conduit + * - querying the FF-A framework version + * - 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 the resident private data structure. + * + * The probe will fail if either FF-A framework is not detected or the + * FF-A requests are not behaving correctly. This ensures that the + * driver is not installed and its operations are not exported to the clients. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_probe(struct udevice *dev) +{ + int ret; + + ret = ffa_alloc_prvdata(dev); + if (ret != 0) + return ret; + + ffa_set_bus_ops(); + + ret = ffa_set_smc_conduit(); + if (ret != 0) + return ret; + + ret = ffa_get_version(); + if (ret != 0) + return ret; + + ret = ffa_get_endpoint_id(); + if (ret != 0) + return ret; + + ret = ffa_get_rxtx_map_features(); + if (ret != 0) + return ret; + + ret = ffa_map_rxtx_buffers(); + if (ret != 0) + return ret; + + ret = ffa_cache_partitions_info(); + if (ret != 0) { + ffa_free_rxtx_buffers(); + return ret; + } + + return 0; +} + +/** + * ffa_remove - The driver remove function + * @dev: the arm_ffa device + * When the device is about to be removed , unmap the RX/TX buffers and free the memory + * Return: + * + * 0 on success. + */ +static int ffa_remove(struct udevice *dev) +{ + ffa_info("removing the device"); + + ffa_unmap_rxtx_buffers(); + + if (ffa_priv_data->pair.rxbuf || ffa_priv_data->pair.txbuf) + ffa_free_rxtx_buffers(); + + return 0; +} + +/** + * ffa_unbind - The driver unbind function + * @dev: the arm_ffa device + * After the device is removed and memory freed the device is unbound + * Return: + * + * 0 on success. + */ +static int ffa_unbind(struct udevice *dev) +{ + ffa_info("unbinding the device , private data already released"); + + ffa_priv_data = NULL; + + return 0; +} + +/** + * ffa_bus_ops_get - bus driver operations getter + * + * Return: + * This function returns a pointer to the driver operations structure + */ +const struct ffa_bus_ops *ffa_bus_ops_get(void) +{ + return &ffa_priv_data->ffa_ops; +} + +/** + * ffa_bus_prvdata_get - bus driver private data getter + * + * Return: + * This function returns a pointer to the main private data structure + */ +struct ffa_prvdata **ffa_bus_prvdata_get(void) +{ + return &ffa_priv_data; +} + +/** + * ffa_bus_discover - discover FF-A bus and probe the arm_ffa device + * + * This function makes sure the FF-A bus is discoverable. + * When probing succeeds FF-A discovery is done. The arm_ffa device is ready to use. + * + * When the bus was already discovered successfully the discovery will not run again. + * + * Arm FF-A transport is implemented through arm_ffa u-boot device managing the FF-A + * communication. + * All FF-A clients should use the arm_ffa device to use the FF-A transport. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int ffa_bus_discover(void) +{ + int ret = 0; + + if (!ffa_priv_data) + ret = ffa_device_get(); + + return ret; +} + +/** + * Declaring the arm_ffa driver under UCLASS_FFA + */ + +U_BOOT_DRIVER(arm_ffa) = { + .name = FFA_DRV_NAME, + .id = UCLASS_FFA, + .probe = ffa_probe, + .remove = ffa_remove, + .unbind = ffa_unbind, +}; diff --git a/include/arm_ffa.h b/include/arm_ffa.h new file mode 100644 index 0000000000..b0c8a18926 --- /dev/null +++ b/include/arm_ffa.h @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __ARM_FFA_H +#define __ARM_FFA_H + +#include <linux/printk.h> + +/* + * This header is public. It can be used by clients to access + * data structures and definitions they need + */ + +/* + * Macros for displaying logs + */ + +#define ffa_info(fmt, ...) pr_info("[FFA] " fmt "\n", ##__VA_ARGS__) +#define ffa_err(fmt, ...) pr_err("[FFA] " fmt "\n", ##__VA_ARGS__) + +/* + * 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 __packed 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_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 __packed ffa_send_direct_data { + unsigned long data0; /* w3/x3 */ + unsigned long data1; /* w4/x4 */ + unsigned long data2; /* w5/x5 */ + unsigned long data3; /* w6/x6 */ + unsigned long data4; /* w7/x7 */ +}; + +/** + * struct ffa_bus_ops - The driver operations structure + * @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)(const char *uuid_str, + u32 *parts_size, struct ffa_partition_info *buffer); + int (*sync_send_receive)(u16 dst_part_id, struct ffa_send_direct_data *msg, bool is_smc64); + int (*rxtx_unmap)(void); +}; + +/** + * The device driver and the Uclass driver public functions + */ + +/** + * ffa_bus_ops_get - driver operations getter + */ +const struct ffa_bus_ops *ffa_bus_ops_get(void); + +/** + * ffa_bus_discover - discover FF-A bus and probes the arm_ffa device + */ +int ffa_bus_discover(void); + +#endif diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 4b2c323452..fb59d4f356 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -4,6 +4,9 @@ * * (C) Copyright 2012 * Pavel Herrmann morpheus.ibis@gmail.com + * + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#ifndef _DM_UCLASS_ID_H @@ -55,6 +58,7 @@ enum uclass_id { UCLASS_EFI_MEDIA, /* Devices provided by UEFI firmware */ UCLASS_ETH, /* Ethernet device */ UCLASS_ETH_PHY, /* Ethernet PHY device */ + UCLASS_FFA, /* Arm Firmware Framework for Armv8-A */ UCLASS_FIRMWARE, /* Firmware */ UCLASS_FPGA, /* FPGA device */ UCLASS_FUZZING_ENGINE, /* Fuzzing engine */

On Mon, Nov 07, 2022 at 07:20:48PM +0000, Abdellatif El Khlifi wrote:
Add the core driver 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.
In U-Boot FF-A design, FF-A is considered as a discoverable bus. 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:
- partition_info_get
- sync_send_receive
- rxtx_unmap
For more details please refer to the driver documentation [2].
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v7:
- add support for 32-bit direct messaging
- rename be_uuid_str_to_le_bin() to uuid_str_to_le_bin()
- improve the declaration of error handling mapping
- stating in doc/arch/arm64.ffa.rst that EFI runtime is not supported
v6:
- drop use of EFI runtime support (We decided with Linaro to add this later)
- drop discovery from initcalls (discovery will be on demand by FF-A users)
- set the alignment of the RX/TX buffers to the larger translation granule size
- move FF-A RX/TX buffers unmapping at ExitBootServices() to a separate commit
- update the documentation and move it to doc/arch/arm64.ffa.rst
v4:
- add doc/README.ffa.drv
- moving the FF-A driver work to drivers/firmware/arm-ffa
- use less #ifdefs in lib/efi_loader/efi_boottime.c and replace #if defined by #if CONFIG_IS_ENABLED
- improving error handling by mapping the FF-A errors to standard errors and logs
- replacing panics with an error log and returning an error code
- improving features discovery in FFA_FEATURES by introducing rxtx_min_pages private data field
- add ffa_remove and ffa_unbind functions
- improve how the driver behaves when bus discovery is done more than once
v3:
- align the interfaces of the U-Boot FF-A driver with those in the linux FF-A driver
- remove the FF-A helper layer
- make the U-Boot FF-A driver independent from EFI
- provide an optional config that enables copying the driver data to EFI runtime section at ExitBootServices service
- use 64-bit version of FFA_RXTX_MAP, FFA_MSG_SEND_DIRECT_{REQ, RESP}
v2:
- make FF-A bus discoverable using device_{bind, probe} APIs
- remove device tree support
v1:
- introduce FF-A bus driver with device tree support
MAINTAINERS | 7 + doc/arch/arm64.ffa.rst | 218 ++++ doc/arch/index.rst | 1 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/firmware/arm-ffa/Kconfig | 30 + drivers/firmware/arm-ffa/Makefile | 6 + drivers/firmware/arm-ffa/arm-ffa-uclass.c | 16 + drivers/firmware/arm-ffa/arm_ffa_prv.h | 205 ++++ drivers/firmware/arm-ffa/core.c | 1310 +++++++++++++++++++++ include/arm_ffa.h | 93 ++ include/dm/uclass-id.h | 4 + 12 files changed, 1893 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_prv.h create mode 100644 drivers/firmware/arm-ffa/core.c create mode 100644 include/arm_ffa.h
diff --git a/MAINTAINERS b/MAINTAINERS index 1cf99c1393..450b5725ce 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -263,6 +263,13 @@ 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 abdellatif.elkhlifi@arm.com +S: Maintained +F: doc/arch/arm64.ffa.rst +F: drivers/firmware/arm-ffa/ +F: include/arm_ffa.h
ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de M: Fabio Estevam festevam@gmail.com diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst new file mode 100644 index 0000000000..dfcec82e45 --- /dev/null +++ b/doc/arch/arm64.ffa.rst @@ -0,0 +1,218 @@ +.. SPDX-License-Identifier: GPL-2.0+
+Arm FF-A Driver +===============
+Summary +-------
+FF-A stands for Firmware Framework for Arm A-profile processors.
+FF-A specifies interfaces that enable a pair of software sandboxes to +communicate with each other. A sandbox aka partition could +be a VM in the Normal or Secure world, an application in S-EL0, or a +Trusted OS in S-EL1.
+This FF-A driver implements the interfaces to communicate with partitions in +the Secure world aka Secure partitions (SPs).
+The driver 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.
+FF-A driver 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 the FF-A driver supports EFI boot time only.
+Runtime support will be added in future developments.
+FF-A and SMC specifications +-------------------------------------------
+The current implementation of the driver relies on FF-A specification v1.0 +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 driver has been tested with OP-TEE which supports SMC32 calling convention.
+For more details please refer to the FF-A v1.0 spec: +https://documentation-service.arm.com/static/5fb7e8a6ca04df4095c1d65e?token=
+Hypervisors are supported if they are configured to trap SMC calls.
+The FF-A driver uses 64-bit registers as per SMCCCv1.2 specification.
+For more details please refer to the SMC Calling Convention v1.2 spec: +https://documentation-service.arm.com/static/5f8edaeff86e16515cdbe4c6?token=
+Supported hardware +--------------------------------
+Aarch64 plaforms
+Configuration +----------------------
+CONFIG_ARM_FFA_TRANSPORT
- Enables the FF-A bus driver. Turn this on if you want to use FF-A
- communication.
+CONFIG_SANDBOX_FFA
- Enables FF-A Sandbox driver. This emulates the FF-A ABIs handling under
- Sandbox and provides functional tests for FF-A.
+FF-A ABIs under the hood +---------------------------------------
+Invoking an FF-A ABI involves providing to the secure world/hypervisor the +expected arguments from the ABI.
+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 FF-A driver (non-secure world).
+The driver reads the response and processes it accordingly.
+This methodology applies to all the FF-A ABIs in the driver.
+FF-A bus discovery in U-Boot +-------------------------------------------
+When CONFIG_ARM_FFA_TRANSPORT is enabled, the FF-A bus is discovered on +demand by the clients (users).
+Clients can discover the FF-A bus using ffa_bus_discover() API which triggers the +discovery process.
+ffa_bus_discover() creates, binds and probes the arm_ffa device using +device_{bind, probe} APIs.
+The discovery process consists in communicating with secure world (or hypervisor) +and querying specific data.
+The discovery process takes place during the arm_ffa device probing which is +handled by ffa_probe().
+The FF-A bus discovery is successful and the bus is ready for use when these +operations succeed:
+- querying the FF-A framework version +- querying from secure world the U-Boot endpoint ID +- querying from secure world the RX/TX mapping features +- mapping the RX/TX buffers +- querying from secure world all the partitions information
+Discovery failure results in a probing failure and the arm_ffa device is +destroyed.
+Requirements for clients +-------------------------------------
+When using the FF-A bus with EFI, clients must:
+- Query SPs in EFI boot time mode using the service UUID. +- Unmap RX/TX buffers before EFI runtime mode starts.
+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 by the user before EFI runtime mode +starts. The driver provides a bus operation for that: rxtx_unmap()
+If RX/TX buffers created by U-Boot are not unmapped and by +consequence becoming available at EFI runtime, secure world will get confused +about RX/TX buffers ownership (U-Boot vs kernel).
+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 stays the same: SMC32.
+The bus driver layer +------------------------------
+The driver comes on top of the SMCCC layer and is implemented in +drivers/firmware/arm-ffa/core.c
+The driver provides the following features:
+- 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, the driver takes care of how to transport
- that to the secure world/hypervisor using FF-A
+- The driver provides callbacks to be used by clients to access the FF-A bus:
+partition_info_get +sync_send_receive +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
+Using armffa command +-----------------------------------
+armffa is an implementation defined command showcasing how to use the FF-A driver and how to invoke +its operations.
+This provides a guidance to the client developers on how to call the FF-A bus +interfaces.
+Usage:
+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 the arm_ffa device info
+Contributors +------------
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
diff --git a/doc/arch/index.rst b/doc/arch/index.rst index 792d9182c3..8d1ab0ad4e 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/Kconfig b/drivers/Kconfig index 8b6fead351..b06b1ae481 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -6,6 +6,8 @@ source "drivers/core/Kconfig"
source "drivers/adc/Kconfig"
+source "drivers/firmware/arm-ffa/Kconfig"
source "drivers/ata/Kconfig"
source "drivers/axi/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index a1700c819d..b86c1d2a42 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -111,6 +111,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/arm-ffa/Kconfig b/drivers/firmware/arm-ffa/Kconfig new file mode 100644 index 0000000000..e4914b9bc7 --- /dev/null +++ b/drivers/firmware/arm-ffa/Kconfig @@ -0,0 +1,30 @@ +# 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 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.
This driver 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.
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.
For more details about the FF-A driver, 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 0000000000..043a8915be --- /dev/null +++ b/drivers/firmware/arm-ffa/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2022 +# Abdellatif El Khlifi, Arm Limited, abdellatif.elkhlifi@arm.com.
+obj-y += arm-ffa-uclass.o core.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 0000000000..7d9695d289 --- /dev/null +++ b/drivers/firmware/arm-ffa/arm-ffa-uclass.c @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include <common.h> +#include <dm.h> +#include <asm/global_data.h>
+DECLARE_GLOBAL_DATA_PTR;
+UCLASS_DRIVER(ffa) = {
- .name = "ffa",
- .id = UCLASS_FFA,
+}; diff --git a/drivers/firmware/arm-ffa/arm_ffa_prv.h b/drivers/firmware/arm-ffa/arm_ffa_prv.h new file mode 100644 index 0000000000..ad7430ada9 --- /dev/null +++ b/drivers/firmware/arm-ffa/arm_ffa_prv.h @@ -0,0 +1,205 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#ifndef __ARM_FFA_PRV_H +#define __ARM_FFA_PRV_H
+#include <arm_ffa.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/arm-smccc.h>
+/*
- This header is private. It is exclusively used by the FF-A driver
- */
+/* FF-A core driver name */ +#define FFA_DRV_NAME "arm_ffa"
+/* 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,
Strange indentation above.
- /* 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
+};
+/* 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)
+/* The FF-A SMC function definitions */
+typedef struct arm_smccc_1_2_regs ffa_value_t; +typedef void (*invoke_ffa_fn_t)(ffa_value_t args, ffa_value_t *res);
+/*
- 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 */
+};
+/**
- 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 - structure hosting 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
- Data structure hosting 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 {
- u64 rxbuf; /* virtual address */
- u64 txbuf; /* virtual address */
- size_t rxtx_min_pages; /* minimum number of pages in each of the RX/TX buffers */
+};
+/**
- 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_partitions - descriptors for all secure partitions
- @count: The number of partitions descriptors
- @descs The partitions descriptors table
- This data structure contains the partitions descriptors table
- */
+struct ffa_partitions {
- u32 count;
- struct ffa_partition_desc *descs; /* virtual address */
+};
+/**
- struct ffa_prvdata - the driver private data structure
- @dev: The arm_ffa device under u-boot driver model
- @ffa_ops: The driver operations structure
- @fwk_version: FF-A framework version
- @id: u-boot endpoint ID
- @partitions: The partitions descriptors structure
- @pair: The RX/TX buffers pair
- @invoke_ffa_fn: The function executing the FF-A function
- The driver data structure hosting all resident data.
- */
+struct ffa_prvdata {
- struct udevice *dev;
- struct ffa_bus_ops ffa_ops;
- u32 fwk_version;
- u16 id;
- struct ffa_partitions partitions;
- struct ffa_rxtxpair pair;
- invoke_ffa_fn_t invoke_ffa_fn;
+};
+/**
- ffa_device_get - create, bind and probe the arm_ffa device
- */
+int ffa_device_get(void);
+/**
- ffa_bus_prvdata_get - bus driver private data getter
- */
+struct ffa_prvdata **ffa_bus_prvdata_get(void);
+#endif diff --git a/drivers/firmware/arm-ffa/core.c b/drivers/firmware/arm-ffa/core.c new file mode 100644 index 0000000000..b955e5187b --- /dev/null +++ b/drivers/firmware/arm-ffa/core.c @@ -0,0 +1,1310 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include "arm_ffa_prv.h" +#include <asm/global_data.h> +#include <common.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <dm/devres.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> +#include <log.h> +#include <malloc.h> +#include <string.h> +#include <uuid.h>
+DECLARE_GLOBAL_DATA_PTR;
+/**
- The device private data structure containing all the
- data read from secure world
- */
+struct ffa_prvdata *ffa_priv_data;
+/* 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,
+};
+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",
},
- },
+};
Indentation above looks strange and inconsistent in some places. This entire approach is in my opinion a bit complicated, no big deal, if no one else complains I suppose it's OK.
+/**
- ffa_to_std_errno - convert FF-A error code to standard error code
- @ffa_errno: Error code returned by the FF-A ABI
- This function maps 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
- */
+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
- This function maps 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
- */
+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;
- ffa_err("%s", err_msg_map[abi_idx].err_str[err_idx]);
- return 0;
+}
+/*
- Driver core functions
- */
+/**
- ffa_remove_device - removes the arm_ffa device
- @dev: the device to be removed
- This function makes sure the arm_ffa device is removed
- No need to free the kmalloced data when the device is destroyed.
- It's automatically done by devm management by
- device_remove() -> device_free() -> devres_release_probe().
- Return:
- 0 on success. Otherwise, failure
- */
+int ffa_remove_device(struct udevice *dev) +{
- int ret;
- if (!dev) {
ffa_err("no udevice found");
return -ENODEV;
- }
- ret = device_remove(dev, DM_REMOVE_NORMAL);
- if (ret) {
ffa_err("unable to remove. err:%d\n", ret);
return ret;
- }
- ffa_info("device removed and freed");
- ret = device_unbind(dev);
- if (ret) {
ffa_err("unable to unbind. err:%d\n", ret);
return ret;
- }
- ffa_info("device unbound");
- return 0;
+}
+/**
- ffa_device_get - create, bind and probe the arm_ffa device
- This function makes sure the arm_ffa device is
- created, bound to this driver, probed and ready to use.
- Arm FF-A transport is implemented through a single u-boot
- device managing the FF-A bus (arm_ffa).
- Return:
- 0 on success. Otherwise, failure
- */
+int ffa_device_get(void) +{
- int ret;
- struct udevice *dev = NULL;
- ret = device_bind(dm_root(), DM_DRIVER_GET(arm_ffa), FFA_DRV_NAME, NULL, ofnode_null(),
&dev);
- if (ret)
return ret;
- /* The FF-A bus discovery succeeds when probing is successful */
- ret = device_probe(dev);
- if (ret) {
ffa_err("arm_ffa device probing failed");
ffa_remove_device(dev);
return ret;
- }
- return 0;
+}
+/**
- ffa_get_version - FFA_VERSION handler function
- This function implements FFA_VERSION FF-A function
- to get from the secure world the FF-A framework version
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_get_version(void) +{
- u16 major, minor;
- ffa_value_t res = {0};
- int ffa_errno;
- ffa_priv_data->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);
- ffa_info("FF-A driver %d.%d\nFF-A framework %d.%d",
FFA_MAJOR_VERSION, FFA_MINOR_VERSION, major, minor);
- if ((major == FFA_MAJOR_VERSION && minor >= FFA_MINOR_VERSION)) {
ffa_info("Versions are compatible ");
ffa_priv_data->fwk_version = res.a0;
return 0;
- }
- ffa_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
- This function implements 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(void) +{
- ffa_value_t res = {0};
- int ffa_errno;
- ffa_priv_data->invoke_ffa_fn((ffa_value_t){
.a0 = FFA_SMC_32(FFA_ID_GET),
}, &res);
- if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) {
ffa_priv_data->id = GET_SELF_ENDPOINT_ID((u32)res.a2);
ffa_info("endpoint ID is %u", ffa_priv_data->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 - sets the minimum number of pages in each of the RX/TX buffers
- @prop_field: properties field obtained from FFA_FEATURES ABI
- This function sets the minimum number of pages
- in each of the RX/TX buffers in the private data structure
- Return:
- buf_4k_pages points to the returned number of pages
- 0 on success. Otherwise, failure
- */
+static int ffa_set_rxtx_buffers_pages_cnt(u32 prop_field) +{
- if (!ffa_priv_data)
return -EINVAL;
- switch (prop_field) {
- case RXTX_4K:
ffa_priv_data->pair.rxtx_min_pages = 1;
break;
- case RXTX_16K:
ffa_priv_data->pair.rxtx_min_pages = 4;
break;
- case RXTX_64K:
ffa_priv_data->pair.rxtx_min_pages = 16;
break;
- default:
ffa_err("RX/TX buffer size not supported");
return -EINVAL;
- }
- return 0;
+}
+/**
- ffa_get_rxtx_map_features - FFA_FEATURES handler function with FFA_RXTX_MAP argument
- This function implements 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(void) +{
- ffa_value_t res = {0};
- int ffa_errno;
- ffa_priv_data->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(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 - frees the RX/TX buffers
- This function frees the RX/TX buffers
- */
+static void ffa_free_rxtx_buffers(void) +{
- ffa_info("Freeing RX/TX buffers");
- if (ffa_priv_data->pair.rxbuf) {
free((void *)ffa_priv_data->pair.rxbuf);
ffa_priv_data->pair.rxbuf = 0;
- }
- if (ffa_priv_data->pair.txbuf) {
free((void *)ffa_priv_data->pair.txbuf);
ffa_priv_data->pair.txbuf = 0;
- }
+}
+/**
- ffa_alloc_rxtx_buffers - allocates the RX/TX buffers
- This function is 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.
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_alloc_rxtx_buffers(void) +{
- u64 bytes;
- ffa_info("Using %lu 4KB page(s) for RX/TX buffers size",
ffa_priv_data->pair.rxtx_min_pages);
- bytes = ffa_priv_data->pair.rxtx_min_pages * SZ_4K;
- /*
* The alignment of the RX and TX buffers must be equal
* to the larger translation granule size
*/
- ffa_priv_data->pair.rxbuf = (u64)memalign(bytes, bytes);
- if (!ffa_priv_data->pair.rxbuf) {
ffa_err("failure to allocate RX buffer");
return -ENOBUFS;
- }
- ffa_info("RX buffer at virtual address 0x%llx", ffa_priv_data->pair.rxbuf);
- ffa_priv_data->pair.txbuf = (u64)memalign(bytes, bytes);
- if (!ffa_priv_data->pair.txbuf) {
free((void *)ffa_priv_data->pair.rxbuf);
ffa_priv_data->pair.rxbuf = 0;
ffa_err("failure to allocate the TX buffer");
return -ENOBUFS;
- }
- ffa_info("TX buffer at virtual address 0x%llx", ffa_priv_data->pair.txbuf);
- /*
* make sure the buffers are cleared before use
*/
- memset((void *)ffa_priv_data->pair.rxbuf, 0, bytes);
- memset((void *)ffa_priv_data->pair.txbuf, 0, bytes);
- return 0;
+}
+/**
- ffa_map_rxtx_buffers - FFA_RXTX_MAP handler function
- This function implements FFA_RXTX_MAP FF-A function
- to map the RX/TX buffers
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_map_rxtx_buffers(void) +{
- int ret;
- ffa_value_t res = {0};
- int ffa_errno;
- ret = ffa_alloc_rxtx_buffers();
- 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
*/
- ffa_priv_data->invoke_ffa_fn((ffa_value_t){
.a0 = FFA_SMC_64(FFA_RXTX_MAP),
.a1 = ffa_priv_data->pair.txbuf,
.a2 = ffa_priv_data->pair.rxbuf,
.a3 = ffa_priv_data->pair.rxtx_min_pages,
}, &res);
- if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) {
ffa_info("RX/TX buffers mapped");
return 0;
- }
- ffa_errno = res.a2;
- ffa_print_error_log(FFA_RXTX_MAP, ffa_errno);
- ffa_free_rxtx_buffers();
- return ffa_to_std_errno(ffa_errno);
+}
+/**
- ffa_unmap_rxtx_buffers - FFA_RXTX_UNMAP handler function
- This function implements FFA_RXTX_UNMAP FF-A function
- to unmap the RX/TX buffers
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_unmap_rxtx_buffers(void) +{
- ffa_value_t res = {0};
- int ffa_errno;
- ffa_priv_data->invoke_ffa_fn((ffa_value_t){
.a0 = FFA_SMC_32(FFA_RXTX_UNMAP),
.a1 = PREP_SELF_ENDPOINT_ID(ffa_priv_data->id),
}, &res);
- if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) {
ffa_free_rxtx_buffers();
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 - FFA_RX_RELEASE handler function
- This function invokes 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(void) +{
- ffa_value_t res = {0};
- int ffa_errno;
- ffa_priv_data->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 - checks whether two given UUIDs are identical
- @uuid1: first UUID
- @uuid2: second UUID
- This function is used by ffa_read_partitions_info to search
- for a UUID in the partitions descriptors table
- Return:
- 1 when UUIDs match. Otherwise, 0
- */
+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 - reads the data queried by FFA_PARTITION_INFO_GET
and saves it in the private structure
- @count: The number of partitions queried
- @part_uuid: Pointer to the partition(s) UUID
- This function reads the partitions information
- returned by the FFA_PARTITION_INFO_GET and saves it in the private
- data structure.
- Return:
- The private data structure is updated with the partition(s) information
- 0 is returned on success. Otherwise, failure
- */
+static int ffa_read_partitions_info(u32 count, struct ffa_partition_uuid *part_uuid) +{
- if (!count) {
ffa_err("no partition detected");
return -ENODATA;
- }
- ffa_info("Reading partitions data from the RX buffer");
- 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 = ffa_priv_data->pair.rxtx_min_pages * SZ_4K;
if (data_bytes > buf_bytes) {
ffa_err("partitions data size exceeds the RX buffer size:");
ffa_err(" sizes in bytes: data %llu , RX buffer %llu ",
data_bytes,
buf_bytes);
return -ENOMEM;
}
ffa_priv_data->partitions.descs = devm_kmalloc(ffa_priv_data->dev, data_bytes,
__GFP_ZERO);
if (!ffa_priv_data->partitions.descs) {
ffa_err("cannot allocate partitions data buffer");
return -ENOMEM;
}
parts_info = (struct ffa_partition_info *)ffa_priv_data->pair.rxbuf;
for (desc_idx = 0 ; desc_idx < count ; desc_idx++) {
ffa_priv_data->partitions.descs[desc_idx].info =
parts_info[desc_idx];
ffa_info("Partition ID %x : info cached",
ffa_priv_data->partitions.descs[desc_idx].info.id);
}
ffa_priv_data->partitions.count = count;
ffa_info("%d partition(s) found and cached", count);
- } else {
u32 rx_desc_idx, cached_desc_idx;
struct ffa_partition_info *parts_info;
u8 desc_found;
parts_info = (struct ffa_partition_info *)ffa_priv_data->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 < ffa_priv_data->partitions.count;
cached_desc_idx++) {
/*
* save the UUID
*/
if (ffa_priv_data->partitions.descs[cached_desc_idx].info.id ==
parts_info[rx_desc_idx].id) {
ffa_priv_data->partitions.descs[cached_desc_idx].sp_uuid =
*part_uuid;
desc_found = 1;
break;
}
}
if (!desc_found)
return -ENODATA;
}
- }
- return 0;
+}
+/**
- ffa_query_partitions_info - invokes FFA_PARTITION_INFO_GET and saves partitions data
- @part_uuid: Pointer to the partition(s) UUID
- @pcount: Pointer to the number of partitions variable filled when querying
- This function executes the FFA_PARTITION_INFO_GET
- to query the partitions data. Then, it calls ffa_read_partitions_info
- to save the data in the private data structure.
- 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 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;
- }
- ffa_priv_data->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((u32)res.a2, part_uuid);
if (ret) {
ffa_err("failed to read partition(s) data , error (%d)", ret);
ffa_release_rx_buffer();
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();
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 - FFA_PARTITION_INFO_GET handler function
- The passed arguments:
- Mode 1: When getting from the driver the number of
- secure partitions:
- @uuid_str: pointer to the UUID string
- @parts_size: pointer to the variable that contains the number of partitions
The variable will be set by the driver
- @buffer: NULL
- Mode 2: When requesting the driver to return the
- partitions information:
- @uuid_str: pointer to the UUID string
- @parts_size: pointer to the size of the SPs information buffer in bytes
- @buffer: pointer to SPs information buffer
(allocated by the client).
The buffer will be filled by the driver
- This function queries the secure partition data from
- the private data structure. If not found, it invokes 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 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().
- When invoked through a client request, ffa_get_partitions_info should be
- called twice. First call is to get from the driver the number of secure
- partitions (SPs) associated to a particular UUID.
- Then, the caller (client) allocates the buffer to host the SPs data and
- issues a 2nd call. Then, the driver fills the SPs data in the pre-allocated
- buffer.
- To achieve the mechanism described above, ffa_get_partitions_info uses the
- following functions:
ffa_read_partitions_info
ffa_query_partitions_info
- Return:
- @parts_size: When pointing to the number of partitions variable, the number is
- set by the driver.
- When pointing to the partitions information buffer size, the buffer will be
- filled by the driver.
This is confusing, it's better if you use the same unit all the time. Since you below require the size to be an even multiple of sizeof(struct ffa_partition_info) you should perhaps use that unit.
- On success 0 is returned. Otherwise, failure
- */
+static int ffa_get_partitions_info(const char *uuid_str,
u32 *parts_size, struct ffa_partition_info *buffer)
+{
- /*
* fill_data:
* 0: return the SP count
* 1: fill SP data and return it to the caller
* -1: undefined mode
*/
- int fill_data = -1;
In the code below this is treated as a bool, it's never tested for -1, so you could as well make this a bool.
- u32 desc_idx, client_desc_idx;
- struct ffa_partition_uuid part_uuid = {0};
- u32 client_desc_max_cnt;
- u32 parts_found = 0;
- if (!ffa_priv_data->partitions.count || !ffa_priv_data->partitions.descs) {
ffa_err("no partition installed");
return -EINVAL;
- }
- if (!uuid_str) {
ffa_err("no UUID provided");
return -EINVAL;
- }
- if (!parts_size) {
ffa_err("no size/count provided");
return -EINVAL;
- }
- if (uuid_str_to_le_bin(uuid_str, (unsigned char *)&part_uuid)) {
ffa_err("invalid UUID");
return -EINVAL;
- }
- if (!buffer) {
/* Mode 1: getting the number of secure partitions */
fill_data = 0;
ffa_info("Preparing for checking partitions count");
- } else if ((*parts_size >= sizeof(struct ffa_partition_info)) &&
!(*parts_size % sizeof(struct ffa_partition_info))) {
/* Mode 2: retrieving the partitions information */
fill_data = 1;
client_desc_idx = 0;
/*
* number of empty descriptors preallocated by the caller
*/
client_desc_max_cnt = *parts_size / sizeof(struct ffa_partition_info);
ffa_info("Preparing for filling partitions info");
- } else {
ffa_err("invalid function arguments provided");
return -EINVAL;
- }
- ffa_info("Searching partitions using the provided UUID");
- /*
* search in the cached partitions
*/
- for (desc_idx = 0;
desc_idx < ffa_priv_data->partitions.count;
desc_idx++) {
if (ffa_uuid_are_identical(&ffa_priv_data->partitions.descs[desc_idx].sp_uuid,
&part_uuid)) {
ffa_info("Partition ID %x matches the provided UUID",
ffa_priv_data->partitions.descs[desc_idx].info.id);
parts_found++;
if (fill_data) {
/*
* trying to fill the partition info in the input buffer
*/
if (client_desc_idx < client_desc_max_cnt) {
buffer[client_desc_idx++] =
ffa_priv_data->partitions.descs[desc_idx].info;
continue;
}
ffa_err("failed to fill the current descriptor client buffer full");
return -ENOBUFS;
}
}
- }
- if (!parts_found) {
By asking for a UUID never found ffa_query_partitions_info() will be called again, even if the rx/tx buffers needed are not available any longer. Since you populate the cache in ffa_probe() below, perhaps it would be better to only rely on the cache after that point.
int ret;
ffa_info("No partition found. Querying framework ...");
ret = ffa_query_partitions_info(&part_uuid, &parts_found);
if (ret == 0) {
if (!fill_data) {
*parts_size = parts_found;
ffa_info("Number of partition(s) found matching the UUID: %d",
parts_found);
} else {
/*
* If SPs data detected, they are already in the private data
* structure, retry searching SP data again to return them
* to the caller
*/
if (parts_found)
ret = ffa_get_partitions_info(uuid_str, parts_size, buffer);
else
ret = -ENODATA;
}
}
return ret;
- }
- /* partition(s) found */
- if (!fill_data)
*parts_size = parts_found;
- return 0;
+}
+/**
- ffa_cache_partitions_info - Queries and saves all secure partitions data
- This function invokes 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 the
- resident private data structure and we keep the UUID field empty
- (in FF-A 1.0 UUID is not provided by the partition descriptor)
- This function is 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(void) +{
- return ffa_query_partitions_info(NULL, NULL);
+}
+/**
- ffa_msg_send_direct_req - FFA_MSG_SEND_DIRECT_{REQ,RESP} handler function
- @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
- */
+static int ffa_msg_send_direct_req(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;
- if (!ffa_priv_data || !ffa_priv_data->invoke_ffa_fn)
return -EINVAL;
- /* No partition installed */
- if (!ffa_priv_data->partitions.count || !ffa_priv_data->partitions.descs)
return -ENODEV;
This check isn't needed. What if the partition ID is known by other means?
- 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);
- }
- ffa_priv_data->invoke_ffa_fn((ffa_value_t){
.a0 = req_mode,
.a1 = PREP_SELF_ENDPOINT_ID(ffa_priv_data->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))
ffa_priv_data->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);
+}
+/**
- __arm_ffa_fn_smc - 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
- Return: void
- */
+void __arm_ffa_fn_smc(ffa_value_t args, ffa_value_t *res) +{
- arm_smccc_1_2_smc(&args, res);
+}
+/**
- ffa_set_smc_conduit - Set the SMC conduit
- This function selects the SMC conduit by setting the driver invoke function
- to SMC assembly function
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_set_smc_conduit(void) +{
- ffa_priv_data->invoke_ffa_fn = __arm_ffa_fn_smc;
- if (!ffa_priv_data->invoke_ffa_fn) {
ffa_err("failure to set the invoke function");
return -EINVAL;
- }
- ffa_info("Conduit is SMC");
- return 0;
+}
+/**
- ffa_set_bus_ops - Set the bus driver operations
- Setting the driver callbacks.
- */
+static void ffa_set_bus_ops(void) +{
- ffa_priv_data->ffa_ops.partition_info_get = ffa_get_partitions_info;
- ffa_priv_data->ffa_ops.sync_send_receive = ffa_msg_send_direct_req;
- ffa_priv_data->ffa_ops.rxtx_unmap = ffa_unmap_rxtx_buffers;
+}
+/**
- ffa_alloc_prvdata - allocate the driver main data structure and sets the device
- @dev: the arm_ffa device
- This function creates the main data structure embedding all the driver data.
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_alloc_prvdata(struct udevice *dev) +{
- if (!dev) {
ffa_err("no udevice found");
return -ENODEV;
- }
- /* The device is registered with the DM. Let's create the driver main data structure*/
- ffa_priv_data = devm_kmalloc(dev, sizeof(struct ffa_prvdata), __GFP_ZERO);
- if (!ffa_priv_data) {
ffa_err("can not allocate the driver main data structure");
return -ENOMEM;
- }
- ffa_priv_data->dev = dev;
- return 0;
+}
+/**
- ffa_probe - The driver probe function
- @dev: the arm_ffa device
- Probing is done at boot time and triggered by the uclass device discovery.
- At probe level the following actions are done:
- setting the conduit
- querying the FF-A framework version
- 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 the resident private data structure.
- The probe will fail if either FF-A framework is not detected or the
- FF-A requests are not behaving correctly. This ensures that the
- driver is not installed and its operations are not exported to the clients.
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_probe(struct udevice *dev) +{
- int ret;
- ret = ffa_alloc_prvdata(dev);
- if (ret != 0)
return ret;
- ffa_set_bus_ops();
- ret = ffa_set_smc_conduit();
- if (ret != 0)
return ret;
- ret = ffa_get_version();
- if (ret != 0)
return ret;
- ret = ffa_get_endpoint_id();
- if (ret != 0)
return ret;
- ret = ffa_get_rxtx_map_features();
- if (ret != 0)
return ret;
- ret = ffa_map_rxtx_buffers();
- if (ret != 0)
return ret;
- ret = ffa_cache_partitions_info();
- if (ret != 0) {
ffa_free_rxtx_buffers();
return ret;
- }
- return 0;
+}
+/**
- ffa_remove - The driver remove function
- @dev: the arm_ffa device
- When the device is about to be removed , unmap the RX/TX buffers and free the memory
- Return:
- 0 on success.
- */
+static int ffa_remove(struct udevice *dev) +{
- ffa_info("removing the device");
- ffa_unmap_rxtx_buffers();
- if (ffa_priv_data->pair.rxbuf || ffa_priv_data->pair.txbuf)
ffa_free_rxtx_buffers();
- return 0;
+}
+/**
- ffa_unbind - The driver unbind function
- @dev: the arm_ffa device
- After the device is removed and memory freed the device is unbound
- Return:
- 0 on success.
- */
+static int ffa_unbind(struct udevice *dev) +{
- ffa_info("unbinding the device , private data already released");
- ffa_priv_data = NULL;
- return 0;
+}
+/**
- ffa_bus_ops_get - bus driver operations getter
- Return:
- This function returns a pointer to the driver operations structure
- */
+const struct ffa_bus_ops *ffa_bus_ops_get(void) +{
- return &ffa_priv_data->ffa_ops;
+}
+/**
- ffa_bus_prvdata_get - bus driver private data getter
- Return:
- This function returns a pointer to the main private data structure
- */
+struct ffa_prvdata **ffa_bus_prvdata_get(void)
Why a pointer to a pointer, isn't "struct ffa_prvdata *" enough?
+{
- return &ffa_priv_data;
+}
+/**
- ffa_bus_discover - discover FF-A bus and probe the arm_ffa device
- This function makes sure the FF-A bus is discoverable.
- When probing succeeds FF-A discovery is done. The arm_ffa device is ready to use.
- When the bus was already discovered successfully the discovery will not run again.
- Arm FF-A transport is implemented through arm_ffa u-boot device managing the FF-A
- communication.
- All FF-A clients should use the arm_ffa device to use the FF-A transport.
- Return:
- 0 on success. Otherwise, failure
- */
+int ffa_bus_discover(void) +{
- int ret = 0;
- if (!ffa_priv_data)
ret = ffa_device_get();
- return ret;
+}
+/**
- Declaring the arm_ffa driver under UCLASS_FFA
- */
+U_BOOT_DRIVER(arm_ffa) = {
- .name = FFA_DRV_NAME,
- .id = UCLASS_FFA,
- .probe = ffa_probe,
- .remove = ffa_remove,
- .unbind = ffa_unbind,
+}; diff --git a/include/arm_ffa.h b/include/arm_ffa.h new file mode 100644 index 0000000000..b0c8a18926 --- /dev/null +++ b/include/arm_ffa.h @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#ifndef __ARM_FFA_H +#define __ARM_FFA_H
+#include <linux/printk.h>
+/*
- This header is public. It can be used by clients to access
- data structures and definitions they need
- */
+/*
- Macros for displaying logs
- */
+#define ffa_info(fmt, ...) pr_info("[FFA] " fmt "\n", ##__VA_ARGS__) +#define ffa_err(fmt, ...) pr_err("[FFA] " fmt "\n", ##__VA_ARGS__)
+/*
- 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 __packed 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;
+};
Perhaps this has been discussed before. Why is this packed? Is it to allow unaligned access or to be sure that there is not implicitly added padding? The Linux kernel does seem to need it.
+/*
- 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 __packed ffa_send_direct_data {
- unsigned long data0; /* w3/x3 */
- unsigned long data1; /* w4/x4 */
- unsigned long data2; /* w5/x5 */
- unsigned long data3; /* w6/x6 */
- unsigned long data4; /* w7/x7 */
+};
Why is this __packed? It seems unnecessary.
Cheers, Jens
+/**
- struct ffa_bus_ops - The driver operations structure
- @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)(const char *uuid_str,
u32 *parts_size, struct ffa_partition_info *buffer);
- int (*sync_send_receive)(u16 dst_part_id, struct ffa_send_direct_data *msg, bool is_smc64);
- int (*rxtx_unmap)(void);
+};
+/**
- The device driver and the Uclass driver public functions
- */
+/**
- ffa_bus_ops_get - driver operations getter
- */
+const struct ffa_bus_ops *ffa_bus_ops_get(void);
+/**
- ffa_bus_discover - discover FF-A bus and probes the arm_ffa device
- */
+int ffa_bus_discover(void);
+#endif diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 4b2c323452..fb59d4f356 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -4,6 +4,9 @@
- (C) Copyright 2012
- Pavel Herrmann morpheus.ibis@gmail.com
- (C) Copyright 2022 ARM Limited
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#ifndef _DM_UCLASS_ID_H @@ -55,6 +58,7 @@ enum uclass_id { UCLASS_EFI_MEDIA, /* Devices provided by UEFI firmware */ UCLASS_ETH, /* Ethernet device */ UCLASS_ETH_PHY, /* Ethernet PHY device */
- UCLASS_FFA, /* Arm Firmware Framework for Armv8-A */ UCLASS_FIRMWARE, /* Firmware */ UCLASS_FPGA, /* FPGA device */ UCLASS_FUZZING_ENGINE, /* Fuzzing engine */
-- 2.17.1

On Wed, Nov 09, 2022 at 12:51:26PM +0100, Jens Wiklander wrote:
On Mon, Nov 07, 2022 at 07:20:48PM +0000, Abdellatif El Khlifi wrote:
Add the core driver 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.
In U-Boot FF-A design, FF-A is considered as a discoverable bus. 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:
- partition_info_get
- sync_send_receive
- rxtx_unmap
For more details please refer to the driver documentation [2].
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v7:
- add support for 32-bit direct messaging
- rename be_uuid_str_to_le_bin() to uuid_str_to_le_bin()
- improve the declaration of error handling mapping
- stating in doc/arch/arm64.ffa.rst that EFI runtime is not supported
v6:
- drop use of EFI runtime support (We decided with Linaro to add this later)
- drop discovery from initcalls (discovery will be on demand by FF-A users)
- set the alignment of the RX/TX buffers to the larger translation granule size
- move FF-A RX/TX buffers unmapping at ExitBootServices() to a separate commit
- update the documentation and move it to doc/arch/arm64.ffa.rst
v4:
- add doc/README.ffa.drv
- moving the FF-A driver work to drivers/firmware/arm-ffa
- use less #ifdefs in lib/efi_loader/efi_boottime.c and replace #if defined by #if CONFIG_IS_ENABLED
- improving error handling by mapping the FF-A errors to standard errors and logs
- replacing panics with an error log and returning an error code
- improving features discovery in FFA_FEATURES by introducing rxtx_min_pages private data field
- add ffa_remove and ffa_unbind functions
- improve how the driver behaves when bus discovery is done more than once
v3:
- align the interfaces of the U-Boot FF-A driver with those in the linux FF-A driver
- remove the FF-A helper layer
- make the U-Boot FF-A driver independent from EFI
- provide an optional config that enables copying the driver data to EFI runtime section at ExitBootServices service
- use 64-bit version of FFA_RXTX_MAP, FFA_MSG_SEND_DIRECT_{REQ, RESP}
v2:
- make FF-A bus discoverable using device_{bind, probe} APIs
- remove device tree support
v1:
- introduce FF-A bus driver with device tree support
MAINTAINERS | 7 + doc/arch/arm64.ffa.rst | 218 ++++ doc/arch/index.rst | 1 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/firmware/arm-ffa/Kconfig | 30 + drivers/firmware/arm-ffa/Makefile | 6 + drivers/firmware/arm-ffa/arm-ffa-uclass.c | 16 + drivers/firmware/arm-ffa/arm_ffa_prv.h | 205 ++++ drivers/firmware/arm-ffa/core.c | 1310 +++++++++++++++++++++ include/arm_ffa.h | 93 ++ include/dm/uclass-id.h | 4 + 12 files changed, 1893 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_prv.h create mode 100644 drivers/firmware/arm-ffa/core.c create mode 100644 include/arm_ffa.h
diff --git a/MAINTAINERS b/MAINTAINERS index 1cf99c1393..450b5725ce 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -263,6 +263,13 @@ 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 abdellatif.elkhlifi@arm.com +S: Maintained +F: doc/arch/arm64.ffa.rst +F: drivers/firmware/arm-ffa/ +F: include/arm_ffa.h
ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de M: Fabio Estevam festevam@gmail.com diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst new file mode 100644 index 0000000000..dfcec82e45 --- /dev/null +++ b/doc/arch/arm64.ffa.rst @@ -0,0 +1,218 @@ +.. SPDX-License-Identifier: GPL-2.0+
+Arm FF-A Driver +===============
+Summary +-------
+FF-A stands for Firmware Framework for Arm A-profile processors.
+FF-A specifies interfaces that enable a pair of software sandboxes to +communicate with each other. A sandbox aka partition could +be a VM in the Normal or Secure world, an application in S-EL0, or a +Trusted OS in S-EL1.
+This FF-A driver implements the interfaces to communicate with partitions in +the Secure world aka Secure partitions (SPs).
+The driver 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.
+FF-A driver 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 the FF-A driver supports EFI boot time only.
+Runtime support will be added in future developments.
+FF-A and SMC specifications +-------------------------------------------
+The current implementation of the driver relies on FF-A specification v1.0 +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 driver has been tested with OP-TEE which supports SMC32 calling convention.
+For more details please refer to the FF-A v1.0 spec: +https://documentation-service.arm.com/static/5fb7e8a6ca04df4095c1d65e?token=
+Hypervisors are supported if they are configured to trap SMC calls.
+The FF-A driver uses 64-bit registers as per SMCCCv1.2 specification.
+For more details please refer to the SMC Calling Convention v1.2 spec: +https://documentation-service.arm.com/static/5f8edaeff86e16515cdbe4c6?token=
+Supported hardware +--------------------------------
+Aarch64 plaforms
+Configuration +----------------------
+CONFIG_ARM_FFA_TRANSPORT
- Enables the FF-A bus driver. Turn this on if you want to use FF-A
- communication.
+CONFIG_SANDBOX_FFA
- Enables FF-A Sandbox driver. This emulates the FF-A ABIs handling under
- Sandbox and provides functional tests for FF-A.
+FF-A ABIs under the hood +---------------------------------------
+Invoking an FF-A ABI involves providing to the secure world/hypervisor the +expected arguments from the ABI.
+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 FF-A driver (non-secure world).
+The driver reads the response and processes it accordingly.
+This methodology applies to all the FF-A ABIs in the driver.
+FF-A bus discovery in U-Boot +-------------------------------------------
+When CONFIG_ARM_FFA_TRANSPORT is enabled, the FF-A bus is discovered on +demand by the clients (users).
+Clients can discover the FF-A bus using ffa_bus_discover() API which triggers the +discovery process.
+ffa_bus_discover() creates, binds and probes the arm_ffa device using +device_{bind, probe} APIs.
+The discovery process consists in communicating with secure world (or hypervisor) +and querying specific data.
+The discovery process takes place during the arm_ffa device probing which is +handled by ffa_probe().
+The FF-A bus discovery is successful and the bus is ready for use when these +operations succeed:
+- querying the FF-A framework version +- querying from secure world the U-Boot endpoint ID +- querying from secure world the RX/TX mapping features +- mapping the RX/TX buffers +- querying from secure world all the partitions information
+Discovery failure results in a probing failure and the arm_ffa device is +destroyed.
+Requirements for clients +-------------------------------------
+When using the FF-A bus with EFI, clients must:
+- Query SPs in EFI boot time mode using the service UUID. +- Unmap RX/TX buffers before EFI runtime mode starts.
+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 by the user before EFI runtime mode +starts. The driver provides a bus operation for that: rxtx_unmap()
+If RX/TX buffers created by U-Boot are not unmapped and by +consequence becoming available at EFI runtime, secure world will get confused +about RX/TX buffers ownership (U-Boot vs kernel).
+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 stays the same: SMC32.
+The bus driver layer +------------------------------
+The driver comes on top of the SMCCC layer and is implemented in +drivers/firmware/arm-ffa/core.c
+The driver provides the following features:
+- 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, the driver takes care of how to transport
- that to the secure world/hypervisor using FF-A
+- The driver provides callbacks to be used by clients to access the FF-A bus:
+partition_info_get +sync_send_receive +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
+Using armffa command +-----------------------------------
+armffa is an implementation defined command showcasing how to use the FF-A driver and how to invoke +its operations.
+This provides a guidance to the client developers on how to call the FF-A bus +interfaces.
+Usage:
+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 the arm_ffa device info
+Contributors +------------
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
diff --git a/doc/arch/index.rst b/doc/arch/index.rst index 792d9182c3..8d1ab0ad4e 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/Kconfig b/drivers/Kconfig index 8b6fead351..b06b1ae481 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -6,6 +6,8 @@ source "drivers/core/Kconfig"
source "drivers/adc/Kconfig"
+source "drivers/firmware/arm-ffa/Kconfig"
source "drivers/ata/Kconfig"
source "drivers/axi/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index a1700c819d..b86c1d2a42 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -111,6 +111,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/arm-ffa/Kconfig b/drivers/firmware/arm-ffa/Kconfig new file mode 100644 index 0000000000..e4914b9bc7 --- /dev/null +++ b/drivers/firmware/arm-ffa/Kconfig @@ -0,0 +1,30 @@ +# 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 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.
This driver 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.
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.
For more details about the FF-A driver, 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 0000000000..043a8915be --- /dev/null +++ b/drivers/firmware/arm-ffa/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2022 +# Abdellatif El Khlifi, Arm Limited, abdellatif.elkhlifi@arm.com.
+obj-y += arm-ffa-uclass.o core.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 0000000000..7d9695d289 --- /dev/null +++ b/drivers/firmware/arm-ffa/arm-ffa-uclass.c @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include <common.h> +#include <dm.h> +#include <asm/global_data.h>
+DECLARE_GLOBAL_DATA_PTR;
+UCLASS_DRIVER(ffa) = {
- .name = "ffa",
- .id = UCLASS_FFA,
+}; diff --git a/drivers/firmware/arm-ffa/arm_ffa_prv.h b/drivers/firmware/arm-ffa/arm_ffa_prv.h new file mode 100644 index 0000000000..ad7430ada9 --- /dev/null +++ b/drivers/firmware/arm-ffa/arm_ffa_prv.h @@ -0,0 +1,205 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#ifndef __ARM_FFA_PRV_H +#define __ARM_FFA_PRV_H
+#include <arm_ffa.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/arm-smccc.h>
+/*
- This header is private. It is exclusively used by the FF-A driver
- */
+/* FF-A core driver name */ +#define FFA_DRV_NAME "arm_ffa"
+/* 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,
Strange indentation above.
- /* 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
+};
+/* 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)
+/* The FF-A SMC function definitions */
+typedef struct arm_smccc_1_2_regs ffa_value_t; +typedef void (*invoke_ffa_fn_t)(ffa_value_t args, ffa_value_t *res);
+/*
- 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 */
+};
+/**
- 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 - structure hosting 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
- Data structure hosting 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 {
- u64 rxbuf; /* virtual address */
- u64 txbuf; /* virtual address */
- size_t rxtx_min_pages; /* minimum number of pages in each of the RX/TX buffers */
+};
+/**
- 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_partitions - descriptors for all secure partitions
- @count: The number of partitions descriptors
- @descs The partitions descriptors table
- This data structure contains the partitions descriptors table
- */
+struct ffa_partitions {
- u32 count;
- struct ffa_partition_desc *descs; /* virtual address */
+};
+/**
- struct ffa_prvdata - the driver private data structure
- @dev: The arm_ffa device under u-boot driver model
- @ffa_ops: The driver operations structure
- @fwk_version: FF-A framework version
- @id: u-boot endpoint ID
- @partitions: The partitions descriptors structure
- @pair: The RX/TX buffers pair
- @invoke_ffa_fn: The function executing the FF-A function
- The driver data structure hosting all resident data.
- */
+struct ffa_prvdata {
- struct udevice *dev;
- struct ffa_bus_ops ffa_ops;
- u32 fwk_version;
- u16 id;
- struct ffa_partitions partitions;
- struct ffa_rxtxpair pair;
- invoke_ffa_fn_t invoke_ffa_fn;
+};
+/**
- ffa_device_get - create, bind and probe the arm_ffa device
- */
+int ffa_device_get(void);
+/**
- ffa_bus_prvdata_get - bus driver private data getter
- */
+struct ffa_prvdata **ffa_bus_prvdata_get(void);
+#endif diff --git a/drivers/firmware/arm-ffa/core.c b/drivers/firmware/arm-ffa/core.c new file mode 100644 index 0000000000..b955e5187b --- /dev/null +++ b/drivers/firmware/arm-ffa/core.c @@ -0,0 +1,1310 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include "arm_ffa_prv.h" +#include <asm/global_data.h> +#include <common.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <dm/devres.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> +#include <log.h> +#include <malloc.h> +#include <string.h> +#include <uuid.h>
+DECLARE_GLOBAL_DATA_PTR;
+/**
- The device private data structure containing all the
- data read from secure world
- */
+struct ffa_prvdata *ffa_priv_data;
+/* 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,
+};
+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",
},
- },
+};
Indentation above looks strange and inconsistent in some places. This entire approach is in my opinion a bit complicated, no big deal, if no one else complains I suppose it's OK.
+/**
- ffa_to_std_errno - convert FF-A error code to standard error code
- @ffa_errno: Error code returned by the FF-A ABI
- This function maps 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
- */
+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
- This function maps 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
- */
+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;
- ffa_err("%s", err_msg_map[abi_idx].err_str[err_idx]);
- return 0;
+}
+/*
- Driver core functions
- */
+/**
- ffa_remove_device - removes the arm_ffa device
- @dev: the device to be removed
- This function makes sure the arm_ffa device is removed
- No need to free the kmalloced data when the device is destroyed.
- It's automatically done by devm management by
- device_remove() -> device_free() -> devres_release_probe().
- Return:
- 0 on success. Otherwise, failure
- */
+int ffa_remove_device(struct udevice *dev) +{
- int ret;
- if (!dev) {
ffa_err("no udevice found");
return -ENODEV;
- }
- ret = device_remove(dev, DM_REMOVE_NORMAL);
- if (ret) {
ffa_err("unable to remove. err:%d\n", ret);
return ret;
- }
- ffa_info("device removed and freed");
- ret = device_unbind(dev);
- if (ret) {
ffa_err("unable to unbind. err:%d\n", ret);
return ret;
- }
- ffa_info("device unbound");
- return 0;
+}
+/**
- ffa_device_get - create, bind and probe the arm_ffa device
- This function makes sure the arm_ffa device is
- created, bound to this driver, probed and ready to use.
- Arm FF-A transport is implemented through a single u-boot
- device managing the FF-A bus (arm_ffa).
- Return:
- 0 on success. Otherwise, failure
- */
+int ffa_device_get(void) +{
- int ret;
- struct udevice *dev = NULL;
- ret = device_bind(dm_root(), DM_DRIVER_GET(arm_ffa), FFA_DRV_NAME, NULL, ofnode_null(),
&dev);
- if (ret)
return ret;
- /* The FF-A bus discovery succeeds when probing is successful */
- ret = device_probe(dev);
- if (ret) {
ffa_err("arm_ffa device probing failed");
ffa_remove_device(dev);
return ret;
- }
- return 0;
+}
+/**
- ffa_get_version - FFA_VERSION handler function
- This function implements FFA_VERSION FF-A function
- to get from the secure world the FF-A framework version
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_get_version(void) +{
- u16 major, minor;
- ffa_value_t res = {0};
- int ffa_errno;
- ffa_priv_data->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);
- ffa_info("FF-A driver %d.%d\nFF-A framework %d.%d",
FFA_MAJOR_VERSION, FFA_MINOR_VERSION, major, minor);
- if ((major == FFA_MAJOR_VERSION && minor >= FFA_MINOR_VERSION)) {
ffa_info("Versions are compatible ");
ffa_priv_data->fwk_version = res.a0;
return 0;
- }
- ffa_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
- This function implements 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(void) +{
- ffa_value_t res = {0};
- int ffa_errno;
- ffa_priv_data->invoke_ffa_fn((ffa_value_t){
.a0 = FFA_SMC_32(FFA_ID_GET),
}, &res);
- if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) {
ffa_priv_data->id = GET_SELF_ENDPOINT_ID((u32)res.a2);
ffa_info("endpoint ID is %u", ffa_priv_data->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 - sets the minimum number of pages in each of the RX/TX buffers
- @prop_field: properties field obtained from FFA_FEATURES ABI
- This function sets the minimum number of pages
- in each of the RX/TX buffers in the private data structure
- Return:
- buf_4k_pages points to the returned number of pages
- 0 on success. Otherwise, failure
- */
+static int ffa_set_rxtx_buffers_pages_cnt(u32 prop_field) +{
- if (!ffa_priv_data)
return -EINVAL;
- switch (prop_field) {
- case RXTX_4K:
ffa_priv_data->pair.rxtx_min_pages = 1;
break;
- case RXTX_16K:
ffa_priv_data->pair.rxtx_min_pages = 4;
break;
- case RXTX_64K:
ffa_priv_data->pair.rxtx_min_pages = 16;
break;
- default:
ffa_err("RX/TX buffer size not supported");
return -EINVAL;
- }
- return 0;
+}
+/**
- ffa_get_rxtx_map_features - FFA_FEATURES handler function with FFA_RXTX_MAP argument
- This function implements 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(void) +{
- ffa_value_t res = {0};
- int ffa_errno;
- ffa_priv_data->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(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 - frees the RX/TX buffers
- This function frees the RX/TX buffers
- */
+static void ffa_free_rxtx_buffers(void) +{
- ffa_info("Freeing RX/TX buffers");
- if (ffa_priv_data->pair.rxbuf) {
free((void *)ffa_priv_data->pair.rxbuf);
ffa_priv_data->pair.rxbuf = 0;
- }
- if (ffa_priv_data->pair.txbuf) {
free((void *)ffa_priv_data->pair.txbuf);
ffa_priv_data->pair.txbuf = 0;
- }
+}
+/**
- ffa_alloc_rxtx_buffers - allocates the RX/TX buffers
- This function is 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.
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_alloc_rxtx_buffers(void) +{
- u64 bytes;
- ffa_info("Using %lu 4KB page(s) for RX/TX buffers size",
ffa_priv_data->pair.rxtx_min_pages);
- bytes = ffa_priv_data->pair.rxtx_min_pages * SZ_4K;
- /*
* The alignment of the RX and TX buffers must be equal
* to the larger translation granule size
*/
- ffa_priv_data->pair.rxbuf = (u64)memalign(bytes, bytes);
- if (!ffa_priv_data->pair.rxbuf) {
ffa_err("failure to allocate RX buffer");
return -ENOBUFS;
- }
- ffa_info("RX buffer at virtual address 0x%llx", ffa_priv_data->pair.rxbuf);
- ffa_priv_data->pair.txbuf = (u64)memalign(bytes, bytes);
- if (!ffa_priv_data->pair.txbuf) {
free((void *)ffa_priv_data->pair.rxbuf);
ffa_priv_data->pair.rxbuf = 0;
ffa_err("failure to allocate the TX buffer");
return -ENOBUFS;
- }
- ffa_info("TX buffer at virtual address 0x%llx", ffa_priv_data->pair.txbuf);
- /*
* make sure the buffers are cleared before use
*/
- memset((void *)ffa_priv_data->pair.rxbuf, 0, bytes);
- memset((void *)ffa_priv_data->pair.txbuf, 0, bytes);
- return 0;
+}
+/**
- ffa_map_rxtx_buffers - FFA_RXTX_MAP handler function
- This function implements FFA_RXTX_MAP FF-A function
- to map the RX/TX buffers
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_map_rxtx_buffers(void) +{
- int ret;
- ffa_value_t res = {0};
- int ffa_errno;
- ret = ffa_alloc_rxtx_buffers();
- 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
*/
- ffa_priv_data->invoke_ffa_fn((ffa_value_t){
.a0 = FFA_SMC_64(FFA_RXTX_MAP),
.a1 = ffa_priv_data->pair.txbuf,
.a2 = ffa_priv_data->pair.rxbuf,
.a3 = ffa_priv_data->pair.rxtx_min_pages,
}, &res);
- if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) {
ffa_info("RX/TX buffers mapped");
return 0;
- }
- ffa_errno = res.a2;
- ffa_print_error_log(FFA_RXTX_MAP, ffa_errno);
- ffa_free_rxtx_buffers();
- return ffa_to_std_errno(ffa_errno);
+}
+/**
- ffa_unmap_rxtx_buffers - FFA_RXTX_UNMAP handler function
- This function implements FFA_RXTX_UNMAP FF-A function
- to unmap the RX/TX buffers
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_unmap_rxtx_buffers(void) +{
- ffa_value_t res = {0};
- int ffa_errno;
- ffa_priv_data->invoke_ffa_fn((ffa_value_t){
.a0 = FFA_SMC_32(FFA_RXTX_UNMAP),
.a1 = PREP_SELF_ENDPOINT_ID(ffa_priv_data->id),
}, &res);
- if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) {
ffa_free_rxtx_buffers();
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 - FFA_RX_RELEASE handler function
- This function invokes 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(void) +{
- ffa_value_t res = {0};
- int ffa_errno;
- ffa_priv_data->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 - checks whether two given UUIDs are identical
- @uuid1: first UUID
- @uuid2: second UUID
- This function is used by ffa_read_partitions_info to search
- for a UUID in the partitions descriptors table
- Return:
- 1 when UUIDs match. Otherwise, 0
- */
+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 - reads the data queried by FFA_PARTITION_INFO_GET
and saves it in the private structure
- @count: The number of partitions queried
- @part_uuid: Pointer to the partition(s) UUID
- This function reads the partitions information
- returned by the FFA_PARTITION_INFO_GET and saves it in the private
- data structure.
- Return:
- The private data structure is updated with the partition(s) information
- 0 is returned on success. Otherwise, failure
- */
+static int ffa_read_partitions_info(u32 count, struct ffa_partition_uuid *part_uuid) +{
- if (!count) {
ffa_err("no partition detected");
return -ENODATA;
- }
- ffa_info("Reading partitions data from the RX buffer");
- 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 = ffa_priv_data->pair.rxtx_min_pages * SZ_4K;
if (data_bytes > buf_bytes) {
ffa_err("partitions data size exceeds the RX buffer size:");
ffa_err(" sizes in bytes: data %llu , RX buffer %llu ",
data_bytes,
buf_bytes);
return -ENOMEM;
}
ffa_priv_data->partitions.descs = devm_kmalloc(ffa_priv_data->dev, data_bytes,
__GFP_ZERO);
if (!ffa_priv_data->partitions.descs) {
ffa_err("cannot allocate partitions data buffer");
return -ENOMEM;
}
parts_info = (struct ffa_partition_info *)ffa_priv_data->pair.rxbuf;
for (desc_idx = 0 ; desc_idx < count ; desc_idx++) {
ffa_priv_data->partitions.descs[desc_idx].info =
parts_info[desc_idx];
ffa_info("Partition ID %x : info cached",
ffa_priv_data->partitions.descs[desc_idx].info.id);
}
ffa_priv_data->partitions.count = count;
ffa_info("%d partition(s) found and cached", count);
- } else {
u32 rx_desc_idx, cached_desc_idx;
struct ffa_partition_info *parts_info;
u8 desc_found;
parts_info = (struct ffa_partition_info *)ffa_priv_data->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 < ffa_priv_data->partitions.count;
cached_desc_idx++) {
/*
* save the UUID
*/
if (ffa_priv_data->partitions.descs[cached_desc_idx].info.id ==
parts_info[rx_desc_idx].id) {
ffa_priv_data->partitions.descs[cached_desc_idx].sp_uuid =
*part_uuid;
desc_found = 1;
break;
}
}
if (!desc_found)
return -ENODATA;
}
- }
- return 0;
+}
+/**
- ffa_query_partitions_info - invokes FFA_PARTITION_INFO_GET and saves partitions data
- @part_uuid: Pointer to the partition(s) UUID
- @pcount: Pointer to the number of partitions variable filled when querying
- This function executes the FFA_PARTITION_INFO_GET
- to query the partitions data. Then, it calls ffa_read_partitions_info
- to save the data in the private data structure.
- 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 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;
- }
- ffa_priv_data->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((u32)res.a2, part_uuid);
if (ret) {
ffa_err("failed to read partition(s) data , error (%d)", ret);
ffa_release_rx_buffer();
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();
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 - FFA_PARTITION_INFO_GET handler function
- The passed arguments:
- Mode 1: When getting from the driver the number of
- secure partitions:
- @uuid_str: pointer to the UUID string
- @parts_size: pointer to the variable that contains the number of partitions
The variable will be set by the driver
- @buffer: NULL
- Mode 2: When requesting the driver to return the
- partitions information:
- @uuid_str: pointer to the UUID string
- @parts_size: pointer to the size of the SPs information buffer in bytes
- @buffer: pointer to SPs information buffer
(allocated by the client).
The buffer will be filled by the driver
- This function queries the secure partition data from
- the private data structure. If not found, it invokes 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 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().
- When invoked through a client request, ffa_get_partitions_info should be
- called twice. First call is to get from the driver the number of secure
- partitions (SPs) associated to a particular UUID.
- Then, the caller (client) allocates the buffer to host the SPs data and
- issues a 2nd call. Then, the driver fills the SPs data in the pre-allocated
- buffer.
- To achieve the mechanism described above, ffa_get_partitions_info uses the
- following functions:
ffa_read_partitions_info
ffa_query_partitions_info
- Return:
- @parts_size: When pointing to the number of partitions variable, the number is
- set by the driver.
- When pointing to the partitions information buffer size, the buffer will be
- filled by the driver.
This is confusing, it's better if you use the same unit all the time. Since you below require the size to be an even multiple of sizeof(struct ffa_partition_info) you should perhaps use that unit.
- On success 0 is returned. Otherwise, failure
- */
+static int ffa_get_partitions_info(const char *uuid_str,
u32 *parts_size, struct ffa_partition_info *buffer)
+{
- /*
* fill_data:
* 0: return the SP count
* 1: fill SP data and return it to the caller
* -1: undefined mode
*/
- int fill_data = -1;
In the code below this is treated as a bool, it's never tested for -1, so you could as well make this a bool.
- u32 desc_idx, client_desc_idx;
- struct ffa_partition_uuid part_uuid = {0};
- u32 client_desc_max_cnt;
- u32 parts_found = 0;
- if (!ffa_priv_data->partitions.count || !ffa_priv_data->partitions.descs) {
ffa_err("no partition installed");
return -EINVAL;
- }
- if (!uuid_str) {
ffa_err("no UUID provided");
return -EINVAL;
- }
- if (!parts_size) {
ffa_err("no size/count provided");
return -EINVAL;
- }
- if (uuid_str_to_le_bin(uuid_str, (unsigned char *)&part_uuid)) {
ffa_err("invalid UUID");
return -EINVAL;
- }
- if (!buffer) {
/* Mode 1: getting the number of secure partitions */
fill_data = 0;
ffa_info("Preparing for checking partitions count");
- } else if ((*parts_size >= sizeof(struct ffa_partition_info)) &&
!(*parts_size % sizeof(struct ffa_partition_info))) {
/* Mode 2: retrieving the partitions information */
fill_data = 1;
client_desc_idx = 0;
/*
* number of empty descriptors preallocated by the caller
*/
client_desc_max_cnt = *parts_size / sizeof(struct ffa_partition_info);
ffa_info("Preparing for filling partitions info");
- } else {
ffa_err("invalid function arguments provided");
return -EINVAL;
- }
- ffa_info("Searching partitions using the provided UUID");
- /*
* search in the cached partitions
*/
- for (desc_idx = 0;
desc_idx < ffa_priv_data->partitions.count;
desc_idx++) {
if (ffa_uuid_are_identical(&ffa_priv_data->partitions.descs[desc_idx].sp_uuid,
&part_uuid)) {
ffa_info("Partition ID %x matches the provided UUID",
ffa_priv_data->partitions.descs[desc_idx].info.id);
parts_found++;
if (fill_data) {
/*
* trying to fill the partition info in the input buffer
*/
if (client_desc_idx < client_desc_max_cnt) {
buffer[client_desc_idx++] =
ffa_priv_data->partitions.descs[desc_idx].info;
continue;
}
ffa_err("failed to fill the current descriptor client buffer full");
return -ENOBUFS;
}
}
- }
- if (!parts_found) {
By asking for a UUID never found ffa_query_partitions_info() will be called again, even if the rx/tx buffers needed are not available any longer. Since you populate the cache in ffa_probe() below, perhaps it would be better to only rely on the cache after that point.
Thanks for pointing out this.
As stated in the documentation under "Requirements for clients" paragraph:
Clients must:
- Query SPs in EFI boot time mode using the service UUID. - Unmap RX/TX buffers before EFI runtime mode starts.
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.
So, when the RX/TX buffers are no longer available, we are at EFI runtime. At that level querying is not allowed and ffa_get_partitions_info() is not available. Only direct messaging is available.
At boot time, when an SP is queryed ffa_get_partitions_info() looks at the cached information first. If the UUID is not found, it will communicate with secure world to try getting the information.
By the way, all the previous comments on this v7 patch will be addressed in v8.
Thanks.
int ret;
ffa_info("No partition found. Querying framework ...");
ret = ffa_query_partitions_info(&part_uuid, &parts_found);
if (ret == 0) {
if (!fill_data) {
*parts_size = parts_found;
ffa_info("Number of partition(s) found matching the UUID: %d",
parts_found);
} else {
/*
* If SPs data detected, they are already in the private data
* structure, retry searching SP data again to return them
* to the caller
*/
if (parts_found)
ret = ffa_get_partitions_info(uuid_str, parts_size, buffer);
else
ret = -ENODATA;
}
}
return ret;
- }
- /* partition(s) found */
- if (!fill_data)
*parts_size = parts_found;
- return 0;
+}
+/**
- ffa_cache_partitions_info - Queries and saves all secure partitions data
- This function invokes 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 the
- resident private data structure and we keep the UUID field empty
- (in FF-A 1.0 UUID is not provided by the partition descriptor)
- This function is 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(void) +{
- return ffa_query_partitions_info(NULL, NULL);
+}
+/**
- ffa_msg_send_direct_req - FFA_MSG_SEND_DIRECT_{REQ,RESP} handler function
- @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
- */
+static int ffa_msg_send_direct_req(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;
- if (!ffa_priv_data || !ffa_priv_data->invoke_ffa_fn)
return -EINVAL;
- /* No partition installed */
- if (!ffa_priv_data->partitions.count || !ffa_priv_data->partitions.descs)
return -ENODEV;
This check isn't needed. What if the partition ID is known by other means?
I'm happy to remove this check. I'd like to add a comment explaining what the other means are. Could you please explain what are they ?
- 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);
- }
- ffa_priv_data->invoke_ffa_fn((ffa_value_t){
.a0 = req_mode,
.a1 = PREP_SELF_ENDPOINT_ID(ffa_priv_data->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))
ffa_priv_data->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);
+}
+/**
- __arm_ffa_fn_smc - 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
- Return: void
- */
+void __arm_ffa_fn_smc(ffa_value_t args, ffa_value_t *res) +{
- arm_smccc_1_2_smc(&args, res);
+}
+/**
- ffa_set_smc_conduit - Set the SMC conduit
- This function selects the SMC conduit by setting the driver invoke function
- to SMC assembly function
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_set_smc_conduit(void) +{
- ffa_priv_data->invoke_ffa_fn = __arm_ffa_fn_smc;
- if (!ffa_priv_data->invoke_ffa_fn) {
ffa_err("failure to set the invoke function");
return -EINVAL;
- }
- ffa_info("Conduit is SMC");
- return 0;
+}
+/**
- ffa_set_bus_ops - Set the bus driver operations
- Setting the driver callbacks.
- */
+static void ffa_set_bus_ops(void) +{
- ffa_priv_data->ffa_ops.partition_info_get = ffa_get_partitions_info;
- ffa_priv_data->ffa_ops.sync_send_receive = ffa_msg_send_direct_req;
- ffa_priv_data->ffa_ops.rxtx_unmap = ffa_unmap_rxtx_buffers;
+}
+/**
- ffa_alloc_prvdata - allocate the driver main data structure and sets the device
- @dev: the arm_ffa device
- This function creates the main data structure embedding all the driver data.
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_alloc_prvdata(struct udevice *dev) +{
- if (!dev) {
ffa_err("no udevice found");
return -ENODEV;
- }
- /* The device is registered with the DM. Let's create the driver main data structure*/
- ffa_priv_data = devm_kmalloc(dev, sizeof(struct ffa_prvdata), __GFP_ZERO);
- if (!ffa_priv_data) {
ffa_err("can not allocate the driver main data structure");
return -ENOMEM;
- }
- ffa_priv_data->dev = dev;
- return 0;
+}
+/**
- ffa_probe - The driver probe function
- @dev: the arm_ffa device
- Probing is done at boot time and triggered by the uclass device discovery.
- At probe level the following actions are done:
- setting the conduit
- querying the FF-A framework version
- 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 the resident private data structure.
- The probe will fail if either FF-A framework is not detected or the
- FF-A requests are not behaving correctly. This ensures that the
- driver is not installed and its operations are not exported to the clients.
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_probe(struct udevice *dev) +{
- int ret;
- ret = ffa_alloc_prvdata(dev);
- if (ret != 0)
return ret;
- ffa_set_bus_ops();
- ret = ffa_set_smc_conduit();
- if (ret != 0)
return ret;
- ret = ffa_get_version();
- if (ret != 0)
return ret;
- ret = ffa_get_endpoint_id();
- if (ret != 0)
return ret;
- ret = ffa_get_rxtx_map_features();
- if (ret != 0)
return ret;
- ret = ffa_map_rxtx_buffers();
- if (ret != 0)
return ret;
- ret = ffa_cache_partitions_info();
- if (ret != 0) {
ffa_free_rxtx_buffers();
return ret;
- }
- return 0;
+}
+/**
- ffa_remove - The driver remove function
- @dev: the arm_ffa device
- When the device is about to be removed , unmap the RX/TX buffers and free the memory
- Return:
- 0 on success.
- */
+static int ffa_remove(struct udevice *dev) +{
- ffa_info("removing the device");
- ffa_unmap_rxtx_buffers();
- if (ffa_priv_data->pair.rxbuf || ffa_priv_data->pair.txbuf)
ffa_free_rxtx_buffers();
- return 0;
+}
+/**
- ffa_unbind - The driver unbind function
- @dev: the arm_ffa device
- After the device is removed and memory freed the device is unbound
- Return:
- 0 on success.
- */
+static int ffa_unbind(struct udevice *dev) +{
- ffa_info("unbinding the device , private data already released");
- ffa_priv_data = NULL;
- return 0;
+}
+/**
- ffa_bus_ops_get - bus driver operations getter
- Return:
- This function returns a pointer to the driver operations structure
- */
+const struct ffa_bus_ops *ffa_bus_ops_get(void) +{
- return &ffa_priv_data->ffa_ops;
+}
+/**
- ffa_bus_prvdata_get - bus driver private data getter
- Return:
- This function returns a pointer to the main private data structure
- */
+struct ffa_prvdata **ffa_bus_prvdata_get(void)
Why a pointer to a pointer, isn't "struct ffa_prvdata *" enough?
Because ffa_priv_data is a pointer. ffa_bus_prvdata_get() returns an address of a pointer so the returned type should be struct ffa_prvdata **
Otherwise, a compiler warning:
drivers/firmware/arm-ffa/core.c: In function ‘ffa_bus_prvdata_get’: drivers/firmware/arm-ffa/core.c:1278:9: warning: return from incompatible pointer type [-Wincompatible-pointer-types] return &ffa_priv_data; ^~~~~~~~~~~~~~
+{
- return &ffa_priv_data;
+}
+/**
- ffa_bus_discover - discover FF-A bus and probe the arm_ffa device
- This function makes sure the FF-A bus is discoverable.
- When probing succeeds FF-A discovery is done. The arm_ffa device is ready to use.
- When the bus was already discovered successfully the discovery will not run again.
- Arm FF-A transport is implemented through arm_ffa u-boot device managing the FF-A
- communication.
- All FF-A clients should use the arm_ffa device to use the FF-A transport.
- Return:
- 0 on success. Otherwise, failure
- */
+int ffa_bus_discover(void) +{
- int ret = 0;
- if (!ffa_priv_data)
ret = ffa_device_get();
- return ret;
+}
+/**
- Declaring the arm_ffa driver under UCLASS_FFA
- */
+U_BOOT_DRIVER(arm_ffa) = {
- .name = FFA_DRV_NAME,
- .id = UCLASS_FFA,
- .probe = ffa_probe,
- .remove = ffa_remove,
- .unbind = ffa_unbind,
+}; diff --git a/include/arm_ffa.h b/include/arm_ffa.h new file mode 100644 index 0000000000..b0c8a18926 --- /dev/null +++ b/include/arm_ffa.h @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#ifndef __ARM_FFA_H +#define __ARM_FFA_H
+#include <linux/printk.h>
+/*
- This header is public. It can be used by clients to access
- data structures and definitions they need
- */
+/*
- Macros for displaying logs
- */
+#define ffa_info(fmt, ...) pr_info("[FFA] " fmt "\n", ##__VA_ARGS__) +#define ffa_err(fmt, ...) pr_err("[FFA] " fmt "\n", ##__VA_ARGS__)
+/*
- 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 __packed 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;
+};
Perhaps this has been discussed before. Why is this packed? Is it to allow unaligned access or to be sure that there is not implicitly added padding? The Linux kernel does seem to need it.
When not using __packed the compiler will add paddings. ffa_partition_info structure is used for reading SP information from the secure world.
The issue arises when the non secure world and the secure world have different architectures (Aarch64 vs Aarch32) or different endianess. In these cases, the data will be corrupted.
I think we need to use __packed for all the comms structures.
I'm aware ffa_partition_info in the kernel is not packed. I'm happy to remove __packed from here to align it with Linux.
But please share your thoughts about the padding issues when working with different architecures/endianess.
Thanks.
+/*
- 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 __packed ffa_send_direct_data {
- unsigned long data0; /* w3/x3 */
- unsigned long data1; /* w4/x4 */
- unsigned long data2; /* w5/x5 */
- unsigned long data3; /* w6/x6 */
- unsigned long data4; /* w7/x7 */
+};
Why is this __packed? It seems unnecessary.
Yes, in this specific case packing is not necessary. I'll remove this in v8.
Cheers, Jens
+/**
- struct ffa_bus_ops - The driver operations structure
- @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)(const char *uuid_str,
u32 *parts_size, struct ffa_partition_info *buffer);
- int (*sync_send_receive)(u16 dst_part_id, struct ffa_send_direct_data *msg, bool is_smc64);
- int (*rxtx_unmap)(void);
+};
+/**
- The device driver and the Uclass driver public functions
- */
+/**
- ffa_bus_ops_get - driver operations getter
- */
+const struct ffa_bus_ops *ffa_bus_ops_get(void);
+/**
- ffa_bus_discover - discover FF-A bus and probes the arm_ffa device
- */
+int ffa_bus_discover(void);
+#endif diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 4b2c323452..fb59d4f356 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -4,6 +4,9 @@
- (C) Copyright 2012
- Pavel Herrmann morpheus.ibis@gmail.com
- (C) Copyright 2022 ARM Limited
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#ifndef _DM_UCLASS_ID_H @@ -55,6 +58,7 @@ enum uclass_id { UCLASS_EFI_MEDIA, /* Devices provided by UEFI firmware */ UCLASS_ETH, /* Ethernet device */ UCLASS_ETH_PHY, /* Ethernet PHY device */
- UCLASS_FFA, /* Arm Firmware Framework for Armv8-A */ UCLASS_FIRMWARE, /* Firmware */ UCLASS_FPGA, /* FPGA device */ UCLASS_FUZZING_ENGINE, /* Fuzzing engine */
-- 2.17.1

On Fri, Nov 11, 2022 at 02:36:11PM +0000, Abdellatif El Khlifi wrote:
On Wed, Nov 09, 2022 at 12:51:26PM +0100, Jens Wiklander wrote:
On Mon, Nov 07, 2022 at 07:20:48PM +0000, Abdellatif El Khlifi wrote:
[snip]
+/**
- ffa_msg_send_direct_req - FFA_MSG_SEND_DIRECT_{REQ,RESP} handler function
- @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
- */
+static int ffa_msg_send_direct_req(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;
- if (!ffa_priv_data || !ffa_priv_data->invoke_ffa_fn)
return -EINVAL;
- /* No partition installed */
- if (!ffa_priv_data->partitions.count || !ffa_priv_data->partitions.descs)
return -ENODEV;
This check isn't needed. What if the partition ID is known by other means?
I'm happy to remove this check. I'd like to add a comment explaining what the other means are. Could you please explain what are they ?
In some systems perhaps you have well known partition ids reserved for certain services.
- 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);
- }
- ffa_priv_data->invoke_ffa_fn((ffa_value_t){
.a0 = req_mode,
.a1 = PREP_SELF_ENDPOINT_ID(ffa_priv_data->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))
ffa_priv_data->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);
+}
[snip]
+/**
- ffa_bus_prvdata_get - bus driver private data getter
- Return:
- This function returns a pointer to the main private data structure
- */
+struct ffa_prvdata **ffa_bus_prvdata_get(void)
Why a pointer to a pointer, isn't "struct ffa_prvdata *" enough?
Because ffa_priv_data is a pointer. ffa_bus_prvdata_get() returns an address of a pointer so the returned type should be struct ffa_prvdata **
Otherwise, a compiler warning:
drivers/firmware/arm-ffa/core.c: In function ‘ffa_bus_prvdata_get’: drivers/firmware/arm-ffa/core.c:1278:9: warning: return from incompatible pointer type [-Wincompatible-pointer-types] return &ffa_priv_data; ^~~~~~~~~~~~~~
Why not return ffa_priv_data instead?
+{
- return &ffa_priv_data;
+}
[snip]
+++ b/include/arm_ffa.h
@@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#ifndef __ARM_FFA_H +#define __ARM_FFA_H
+#include <linux/printk.h>
+/*
- This header is public. It can be used by clients to access
- data structures and definitions they need
- */
+/*
- Macros for displaying logs
- */
+#define ffa_info(fmt, ...) pr_info("[FFA] " fmt "\n", ##__VA_ARGS__) +#define ffa_err(fmt, ...) pr_err("[FFA] " fmt "\n", ##__VA_ARGS__)
+/*
- 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 __packed 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;
+};
Perhaps this has been discussed before. Why is this packed? Is it to allow unaligned access or to be sure that there is not implicitly added padding? The Linux kernel does seem to need it.
When not using __packed the compiler will add paddings. ffa_partition_info structure is used for reading SP information from the secure world.
The issue arises when the non secure world and the secure world have different architectures (Aarch64 vs Aarch32) or different endianess. In these cases, the data will be corrupted.
I think we need to use __packed for all the comms structures.
I'm aware ffa_partition_info in the kernel is not packed. I'm happy to remove __packed from here to align it with Linux.
But please share your thoughts about the padding issues when working with different architecures/endianess.
__packed doesn't help with endianess, besides if I remember correctly these are always expected to be little endian. But I guess that could be a problem for another day.
I believe the layout of these structs are designed in a way that a reasonable compiler wouldn't add padding on AArch32 or AArch64. Note that packed does more than just force generation of inefficient code on Arm (unaligned access), it also makes it invalid to take the address of a member inside such a struct.
Cheers, Jens

On Tue, Nov 15, 2022 at 11:32:49AM +0100, Jens Wiklander wrote:
On Fri, Nov 11, 2022 at 02:36:11PM +0000, Abdellatif El Khlifi wrote:
On Wed, Nov 09, 2022 at 12:51:26PM +0100, Jens Wiklander wrote:
On Mon, Nov 07, 2022 at 07:20:48PM +0000, Abdellatif El Khlifi wrote:
[snip]
+/**
- ffa_msg_send_direct_req - FFA_MSG_SEND_DIRECT_{REQ,RESP} handler function
- @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
- */
+static int ffa_msg_send_direct_req(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;
- if (!ffa_priv_data || !ffa_priv_data->invoke_ffa_fn)
return -EINVAL;
- /* No partition installed */
- if (!ffa_priv_data->partitions.count || !ffa_priv_data->partitions.descs)
return -ENODEV;
This check isn't needed. What if the partition ID is known by other means?
I'm happy to remove this check. I'd like to add a comment explaining what the other means are. Could you please explain what are they ?
In some systems perhaps you have well known partition ids reserved for certain services.
I double checked with Achin about this.
FFA_PARTITION_INFO_GET provides the partitions information for all partitions including the reserved ones. The driver will cache the information of all the partitions. Not finding any partition cached before a direct request means: there is no partition in the system. I think we need to keep the check.
- 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);
- }
- ffa_priv_data->invoke_ffa_fn((ffa_value_t){
.a0 = req_mode,
.a1 = PREP_SELF_ENDPOINT_ID(ffa_priv_data->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))
ffa_priv_data->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);
+}
[snip]
+/**
- ffa_bus_prvdata_get - bus driver private data getter
- Return:
- This function returns a pointer to the main private data structure
- */
+struct ffa_prvdata **ffa_bus_prvdata_get(void)
Why a pointer to a pointer, isn't "struct ffa_prvdata *" enough?
Because ffa_priv_data is a pointer. ffa_bus_prvdata_get() returns an address of a pointer so the returned type should be struct ffa_prvdata **
Otherwise, a compiler warning:
drivers/firmware/arm-ffa/core.c: In function ‘ffa_bus_prvdata_get’: drivers/firmware/arm-ffa/core.c:1278:9: warning: return from incompatible pointer type [-Wincompatible-pointer-types] return &ffa_priv_data; ^~~~~~~~~~~~~~
Why not return ffa_priv_data instead?
Thanks, addressed in v8 patchset.
+{
- return &ffa_priv_data;
+}
[snip]
+++ b/include/arm_ffa.h
@@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#ifndef __ARM_FFA_H +#define __ARM_FFA_H
+#include <linux/printk.h>
+/*
- This header is public. It can be used by clients to access
- data structures and definitions they need
- */
+/*
- Macros for displaying logs
- */
+#define ffa_info(fmt, ...) pr_info("[FFA] " fmt "\n", ##__VA_ARGS__) +#define ffa_err(fmt, ...) pr_err("[FFA] " fmt "\n", ##__VA_ARGS__)
+/*
- 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 __packed 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;
+};
Perhaps this has been discussed before. Why is this packed? Is it to allow unaligned access or to be sure that there is not implicitly added padding? The Linux kernel does seem to need it.
When not using __packed the compiler will add paddings. ffa_partition_info structure is used for reading SP information from the secure world.
The issue arises when the non secure world and the secure world have different architectures (Aarch64 vs Aarch32) or different endianess. In these cases, the data will be corrupted.
I think we need to use __packed for all the comms structures.
I'm aware ffa_partition_info in the kernel is not packed. I'm happy to remove __packed from here to align it with Linux.
But please share your thoughts about the padding issues when working with different architecures/endianess.
__packed doesn't help with endianess, besides if I remember correctly these are always expected to be little endian. But I guess that could be a problem for another day.
I believe the layout of these structs are designed in a way that a reasonable compiler wouldn't add padding on AArch32 or AArch64. Note that packed does more than just force generation of inefficient code on Arm (unaligned access), it also makes it invalid to take the address of a member inside such a struct.
Packing removed from ffa_partition_info and ffa_send_direct_data structures in v8 patchset.
Cheers, Jens

On Wed, Nov 09, 2022 at 12:51:26PM +0100, Jens Wiklander wrote:
On Mon, Nov 07, 2022 at 07:20:48PM +0000, Abdellatif El Khlifi wrote:
Add the core driver 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.
In U-Boot FF-A design, FF-A is considered as a discoverable bus. 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:
- partition_info_get
- sync_send_receive
- rxtx_unmap
For more details please refer to the driver documentation [2].
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v7:
- add support for 32-bit direct messaging
- rename be_uuid_str_to_le_bin() to uuid_str_to_le_bin()
- improve the declaration of error handling mapping
- stating in doc/arch/arm64.ffa.rst that EFI runtime is not supported
v6:
- drop use of EFI runtime support (We decided with Linaro to add this later)
- drop discovery from initcalls (discovery will be on demand by FF-A users)
- set the alignment of the RX/TX buffers to the larger translation granule size
- move FF-A RX/TX buffers unmapping at ExitBootServices() to a separate commit
- update the documentation and move it to doc/arch/arm64.ffa.rst
v4:
- add doc/README.ffa.drv
- moving the FF-A driver work to drivers/firmware/arm-ffa
- use less #ifdefs in lib/efi_loader/efi_boottime.c and replace #if defined by #if CONFIG_IS_ENABLED
- improving error handling by mapping the FF-A errors to standard errors and logs
- replacing panics with an error log and returning an error code
- improving features discovery in FFA_FEATURES by introducing rxtx_min_pages private data field
- add ffa_remove and ffa_unbind functions
- improve how the driver behaves when bus discovery is done more than once
v3:
- align the interfaces of the U-Boot FF-A driver with those in the linux FF-A driver
- remove the FF-A helper layer
- make the U-Boot FF-A driver independent from EFI
- provide an optional config that enables copying the driver data to EFI runtime section at ExitBootServices service
- use 64-bit version of FFA_RXTX_MAP, FFA_MSG_SEND_DIRECT_{REQ, RESP}
v2:
- make FF-A bus discoverable using device_{bind, probe} APIs
- remove device tree support
v1:
- introduce FF-A bus driver with device tree support
MAINTAINERS | 7 + doc/arch/arm64.ffa.rst | 218 ++++ doc/arch/index.rst | 1 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/firmware/arm-ffa/Kconfig | 30 + drivers/firmware/arm-ffa/Makefile | 6 + drivers/firmware/arm-ffa/arm-ffa-uclass.c | 16 + drivers/firmware/arm-ffa/arm_ffa_prv.h | 205 ++++ drivers/firmware/arm-ffa/core.c | 1310 +++++++++++++++++++++ include/arm_ffa.h | 93 ++ include/dm/uclass-id.h | 4 + 12 files changed, 1893 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_prv.h create mode 100644 drivers/firmware/arm-ffa/core.c create mode 100644 include/arm_ffa.h
diff --git a/MAINTAINERS b/MAINTAINERS index 1cf99c1393..450b5725ce 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -263,6 +263,13 @@ 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 abdellatif.elkhlifi@arm.com +S: Maintained +F: doc/arch/arm64.ffa.rst +F: drivers/firmware/arm-ffa/ +F: include/arm_ffa.h
ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de M: Fabio Estevam festevam@gmail.com diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst new file mode 100644 index 0000000000..dfcec82e45 --- /dev/null +++ b/doc/arch/arm64.ffa.rst @@ -0,0 +1,218 @@ +.. SPDX-License-Identifier: GPL-2.0+
+Arm FF-A Driver +===============
+Summary +-------
+FF-A stands for Firmware Framework for Arm A-profile processors.
+FF-A specifies interfaces that enable a pair of software sandboxes to +communicate with each other. A sandbox aka partition could +be a VM in the Normal or Secure world, an application in S-EL0, or a +Trusted OS in S-EL1.
+This FF-A driver implements the interfaces to communicate with partitions in +the Secure world aka Secure partitions (SPs).
+The driver 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.
+FF-A driver 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 the FF-A driver supports EFI boot time only.
+Runtime support will be added in future developments.
+FF-A and SMC specifications +-------------------------------------------
+The current implementation of the driver relies on FF-A specification v1.0 +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 driver has been tested with OP-TEE which supports SMC32 calling convention.
+For more details please refer to the FF-A v1.0 spec: +https://documentation-service.arm.com/static/5fb7e8a6ca04df4095c1d65e?token=
+Hypervisors are supported if they are configured to trap SMC calls.
+The FF-A driver uses 64-bit registers as per SMCCCv1.2 specification.
+For more details please refer to the SMC Calling Convention v1.2 spec: +https://documentation-service.arm.com/static/5f8edaeff86e16515cdbe4c6?token=
+Supported hardware +--------------------------------
+Aarch64 plaforms
+Configuration +----------------------
+CONFIG_ARM_FFA_TRANSPORT
- Enables the FF-A bus driver. Turn this on if you want to use FF-A
- communication.
+CONFIG_SANDBOX_FFA
- Enables FF-A Sandbox driver. This emulates the FF-A ABIs handling under
- Sandbox and provides functional tests for FF-A.
+FF-A ABIs under the hood +---------------------------------------
+Invoking an FF-A ABI involves providing to the secure world/hypervisor the +expected arguments from the ABI.
+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 FF-A driver (non-secure world).
+The driver reads the response and processes it accordingly.
+This methodology applies to all the FF-A ABIs in the driver.
+FF-A bus discovery in U-Boot +-------------------------------------------
+When CONFIG_ARM_FFA_TRANSPORT is enabled, the FF-A bus is discovered on +demand by the clients (users).
+Clients can discover the FF-A bus using ffa_bus_discover() API which triggers the +discovery process.
+ffa_bus_discover() creates, binds and probes the arm_ffa device using +device_{bind, probe} APIs.
+The discovery process consists in communicating with secure world (or hypervisor) +and querying specific data.
+The discovery process takes place during the arm_ffa device probing which is +handled by ffa_probe().
+The FF-A bus discovery is successful and the bus is ready for use when these +operations succeed:
+- querying the FF-A framework version +- querying from secure world the U-Boot endpoint ID +- querying from secure world the RX/TX mapping features +- mapping the RX/TX buffers +- querying from secure world all the partitions information
+Discovery failure results in a probing failure and the arm_ffa device is +destroyed.
+Requirements for clients +-------------------------------------
+When using the FF-A bus with EFI, clients must:
+- Query SPs in EFI boot time mode using the service UUID. +- Unmap RX/TX buffers before EFI runtime mode starts.
+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 by the user before EFI runtime mode +starts. The driver provides a bus operation for that: rxtx_unmap()
+If RX/TX buffers created by U-Boot are not unmapped and by +consequence becoming available at EFI runtime, secure world will get confused +about RX/TX buffers ownership (U-Boot vs kernel).
+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 stays the same: SMC32.
+The bus driver layer +------------------------------
+The driver comes on top of the SMCCC layer and is implemented in +drivers/firmware/arm-ffa/core.c
+The driver provides the following features:
+- 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, the driver takes care of how to transport
- that to the secure world/hypervisor using FF-A
+- The driver provides callbacks to be used by clients to access the FF-A bus:
+partition_info_get +sync_send_receive +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
+Using armffa command +-----------------------------------
+armffa is an implementation defined command showcasing how to use the FF-A driver and how to invoke +its operations.
+This provides a guidance to the client developers on how to call the FF-A bus +interfaces.
+Usage:
+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 the arm_ffa device info
+Contributors +------------
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
diff --git a/doc/arch/index.rst b/doc/arch/index.rst index 792d9182c3..8d1ab0ad4e 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/Kconfig b/drivers/Kconfig index 8b6fead351..b06b1ae481 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -6,6 +6,8 @@ source "drivers/core/Kconfig"
source "drivers/adc/Kconfig"
+source "drivers/firmware/arm-ffa/Kconfig"
source "drivers/ata/Kconfig"
source "drivers/axi/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index a1700c819d..b86c1d2a42 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -111,6 +111,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/arm-ffa/Kconfig b/drivers/firmware/arm-ffa/Kconfig new file mode 100644 index 0000000000..e4914b9bc7 --- /dev/null +++ b/drivers/firmware/arm-ffa/Kconfig @@ -0,0 +1,30 @@ +# 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 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.
This driver 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.
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.
For more details about the FF-A driver, 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 0000000000..043a8915be --- /dev/null +++ b/drivers/firmware/arm-ffa/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2022 +# Abdellatif El Khlifi, Arm Limited, abdellatif.elkhlifi@arm.com.
+obj-y += arm-ffa-uclass.o core.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 0000000000..7d9695d289 --- /dev/null +++ b/drivers/firmware/arm-ffa/arm-ffa-uclass.c @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include <common.h> +#include <dm.h> +#include <asm/global_data.h>
+DECLARE_GLOBAL_DATA_PTR;
+UCLASS_DRIVER(ffa) = {
- .name = "ffa",
- .id = UCLASS_FFA,
+}; diff --git a/drivers/firmware/arm-ffa/arm_ffa_prv.h b/drivers/firmware/arm-ffa/arm_ffa_prv.h new file mode 100644 index 0000000000..ad7430ada9 --- /dev/null +++ b/drivers/firmware/arm-ffa/arm_ffa_prv.h @@ -0,0 +1,205 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#ifndef __ARM_FFA_PRV_H +#define __ARM_FFA_PRV_H
+#include <arm_ffa.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/arm-smccc.h>
+/*
- This header is private. It is exclusively used by the FF-A driver
- */
+/* FF-A core driver name */ +#define FFA_DRV_NAME "arm_ffa"
+/* 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,
Strange indentation above.
Thanks, addressed in v8 patchset.
- /* 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
+};
+/* 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)
+/* The FF-A SMC function definitions */
+typedef struct arm_smccc_1_2_regs ffa_value_t; +typedef void (*invoke_ffa_fn_t)(ffa_value_t args, ffa_value_t *res);
+/*
- 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 */
+};
+/**
- 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 - structure hosting 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
- Data structure hosting 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 {
- u64 rxbuf; /* virtual address */
- u64 txbuf; /* virtual address */
- size_t rxtx_min_pages; /* minimum number of pages in each of the RX/TX buffers */
+};
+/**
- 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_partitions - descriptors for all secure partitions
- @count: The number of partitions descriptors
- @descs The partitions descriptors table
- This data structure contains the partitions descriptors table
- */
+struct ffa_partitions {
- u32 count;
- struct ffa_partition_desc *descs; /* virtual address */
+};
+/**
- struct ffa_prvdata - the driver private data structure
- @dev: The arm_ffa device under u-boot driver model
- @ffa_ops: The driver operations structure
- @fwk_version: FF-A framework version
- @id: u-boot endpoint ID
- @partitions: The partitions descriptors structure
- @pair: The RX/TX buffers pair
- @invoke_ffa_fn: The function executing the FF-A function
- The driver data structure hosting all resident data.
- */
+struct ffa_prvdata {
- struct udevice *dev;
- struct ffa_bus_ops ffa_ops;
- u32 fwk_version;
- u16 id;
- struct ffa_partitions partitions;
- struct ffa_rxtxpair pair;
- invoke_ffa_fn_t invoke_ffa_fn;
+};
+/**
- ffa_device_get - create, bind and probe the arm_ffa device
- */
+int ffa_device_get(void);
+/**
- ffa_bus_prvdata_get - bus driver private data getter
- */
+struct ffa_prvdata **ffa_bus_prvdata_get(void);
+#endif diff --git a/drivers/firmware/arm-ffa/core.c b/drivers/firmware/arm-ffa/core.c new file mode 100644 index 0000000000..b955e5187b --- /dev/null +++ b/drivers/firmware/arm-ffa/core.c @@ -0,0 +1,1310 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include "arm_ffa_prv.h" +#include <asm/global_data.h> +#include <common.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <dm/devres.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> +#include <log.h> +#include <malloc.h> +#include <string.h> +#include <uuid.h>
+DECLARE_GLOBAL_DATA_PTR;
+/**
- The device private data structure containing all the
- data read from secure world
- */
+struct ffa_prvdata *ffa_priv_data;
+/* 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,
+};
+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",
},
- },
+};
Indentation above looks strange and inconsistent in some places. This entire approach is in my opinion a bit complicated, no big deal, if no one else complains I suppose it's OK.
Addressed in v8 patchset.
+/**
- ffa_to_std_errno - convert FF-A error code to standard error code
- @ffa_errno: Error code returned by the FF-A ABI
- This function maps 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
- */
+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
- This function maps 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
- */
+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;
- ffa_err("%s", err_msg_map[abi_idx].err_str[err_idx]);
- return 0;
+}
+/*
- Driver core functions
- */
+/**
- ffa_remove_device - removes the arm_ffa device
- @dev: the device to be removed
- This function makes sure the arm_ffa device is removed
- No need to free the kmalloced data when the device is destroyed.
- It's automatically done by devm management by
- device_remove() -> device_free() -> devres_release_probe().
- Return:
- 0 on success. Otherwise, failure
- */
+int ffa_remove_device(struct udevice *dev) +{
- int ret;
- if (!dev) {
ffa_err("no udevice found");
return -ENODEV;
- }
- ret = device_remove(dev, DM_REMOVE_NORMAL);
- if (ret) {
ffa_err("unable to remove. err:%d\n", ret);
return ret;
- }
- ffa_info("device removed and freed");
- ret = device_unbind(dev);
- if (ret) {
ffa_err("unable to unbind. err:%d\n", ret);
return ret;
- }
- ffa_info("device unbound");
- return 0;
+}
+/**
- ffa_device_get - create, bind and probe the arm_ffa device
- This function makes sure the arm_ffa device is
- created, bound to this driver, probed and ready to use.
- Arm FF-A transport is implemented through a single u-boot
- device managing the FF-A bus (arm_ffa).
- Return:
- 0 on success. Otherwise, failure
- */
+int ffa_device_get(void) +{
- int ret;
- struct udevice *dev = NULL;
- ret = device_bind(dm_root(), DM_DRIVER_GET(arm_ffa), FFA_DRV_NAME, NULL, ofnode_null(),
&dev);
- if (ret)
return ret;
- /* The FF-A bus discovery succeeds when probing is successful */
- ret = device_probe(dev);
- if (ret) {
ffa_err("arm_ffa device probing failed");
ffa_remove_device(dev);
return ret;
- }
- return 0;
+}
+/**
- ffa_get_version - FFA_VERSION handler function
- This function implements FFA_VERSION FF-A function
- to get from the secure world the FF-A framework version
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_get_version(void) +{
- u16 major, minor;
- ffa_value_t res = {0};
- int ffa_errno;
- ffa_priv_data->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);
- ffa_info("FF-A driver %d.%d\nFF-A framework %d.%d",
FFA_MAJOR_VERSION, FFA_MINOR_VERSION, major, minor);
- if ((major == FFA_MAJOR_VERSION && minor >= FFA_MINOR_VERSION)) {
ffa_info("Versions are compatible ");
ffa_priv_data->fwk_version = res.a0;
return 0;
- }
- ffa_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
- This function implements 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(void) +{
- ffa_value_t res = {0};
- int ffa_errno;
- ffa_priv_data->invoke_ffa_fn((ffa_value_t){
.a0 = FFA_SMC_32(FFA_ID_GET),
}, &res);
- if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) {
ffa_priv_data->id = GET_SELF_ENDPOINT_ID((u32)res.a2);
ffa_info("endpoint ID is %u", ffa_priv_data->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 - sets the minimum number of pages in each of the RX/TX buffers
- @prop_field: properties field obtained from FFA_FEATURES ABI
- This function sets the minimum number of pages
- in each of the RX/TX buffers in the private data structure
- Return:
- buf_4k_pages points to the returned number of pages
- 0 on success. Otherwise, failure
- */
+static int ffa_set_rxtx_buffers_pages_cnt(u32 prop_field) +{
- if (!ffa_priv_data)
return -EINVAL;
- switch (prop_field) {
- case RXTX_4K:
ffa_priv_data->pair.rxtx_min_pages = 1;
break;
- case RXTX_16K:
ffa_priv_data->pair.rxtx_min_pages = 4;
break;
- case RXTX_64K:
ffa_priv_data->pair.rxtx_min_pages = 16;
break;
- default:
ffa_err("RX/TX buffer size not supported");
return -EINVAL;
- }
- return 0;
+}
+/**
- ffa_get_rxtx_map_features - FFA_FEATURES handler function with FFA_RXTX_MAP argument
- This function implements 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(void) +{
- ffa_value_t res = {0};
- int ffa_errno;
- ffa_priv_data->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(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 - frees the RX/TX buffers
- This function frees the RX/TX buffers
- */
+static void ffa_free_rxtx_buffers(void) +{
- ffa_info("Freeing RX/TX buffers");
- if (ffa_priv_data->pair.rxbuf) {
free((void *)ffa_priv_data->pair.rxbuf);
ffa_priv_data->pair.rxbuf = 0;
- }
- if (ffa_priv_data->pair.txbuf) {
free((void *)ffa_priv_data->pair.txbuf);
ffa_priv_data->pair.txbuf = 0;
- }
+}
+/**
- ffa_alloc_rxtx_buffers - allocates the RX/TX buffers
- This function is 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.
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_alloc_rxtx_buffers(void) +{
- u64 bytes;
- ffa_info("Using %lu 4KB page(s) for RX/TX buffers size",
ffa_priv_data->pair.rxtx_min_pages);
- bytes = ffa_priv_data->pair.rxtx_min_pages * SZ_4K;
- /*
* The alignment of the RX and TX buffers must be equal
* to the larger translation granule size
*/
- ffa_priv_data->pair.rxbuf = (u64)memalign(bytes, bytes);
- if (!ffa_priv_data->pair.rxbuf) {
ffa_err("failure to allocate RX buffer");
return -ENOBUFS;
- }
- ffa_info("RX buffer at virtual address 0x%llx", ffa_priv_data->pair.rxbuf);
- ffa_priv_data->pair.txbuf = (u64)memalign(bytes, bytes);
- if (!ffa_priv_data->pair.txbuf) {
free((void *)ffa_priv_data->pair.rxbuf);
ffa_priv_data->pair.rxbuf = 0;
ffa_err("failure to allocate the TX buffer");
return -ENOBUFS;
- }
- ffa_info("TX buffer at virtual address 0x%llx", ffa_priv_data->pair.txbuf);
- /*
* make sure the buffers are cleared before use
*/
- memset((void *)ffa_priv_data->pair.rxbuf, 0, bytes);
- memset((void *)ffa_priv_data->pair.txbuf, 0, bytes);
- return 0;
+}
+/**
- ffa_map_rxtx_buffers - FFA_RXTX_MAP handler function
- This function implements FFA_RXTX_MAP FF-A function
- to map the RX/TX buffers
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_map_rxtx_buffers(void) +{
- int ret;
- ffa_value_t res = {0};
- int ffa_errno;
- ret = ffa_alloc_rxtx_buffers();
- 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
*/
- ffa_priv_data->invoke_ffa_fn((ffa_value_t){
.a0 = FFA_SMC_64(FFA_RXTX_MAP),
.a1 = ffa_priv_data->pair.txbuf,
.a2 = ffa_priv_data->pair.rxbuf,
.a3 = ffa_priv_data->pair.rxtx_min_pages,
}, &res);
- if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) {
ffa_info("RX/TX buffers mapped");
return 0;
- }
- ffa_errno = res.a2;
- ffa_print_error_log(FFA_RXTX_MAP, ffa_errno);
- ffa_free_rxtx_buffers();
- return ffa_to_std_errno(ffa_errno);
+}
+/**
- ffa_unmap_rxtx_buffers - FFA_RXTX_UNMAP handler function
- This function implements FFA_RXTX_UNMAP FF-A function
- to unmap the RX/TX buffers
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_unmap_rxtx_buffers(void) +{
- ffa_value_t res = {0};
- int ffa_errno;
- ffa_priv_data->invoke_ffa_fn((ffa_value_t){
.a0 = FFA_SMC_32(FFA_RXTX_UNMAP),
.a1 = PREP_SELF_ENDPOINT_ID(ffa_priv_data->id),
}, &res);
- if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) {
ffa_free_rxtx_buffers();
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 - FFA_RX_RELEASE handler function
- This function invokes 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(void) +{
- ffa_value_t res = {0};
- int ffa_errno;
- ffa_priv_data->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 - checks whether two given UUIDs are identical
- @uuid1: first UUID
- @uuid2: second UUID
- This function is used by ffa_read_partitions_info to search
- for a UUID in the partitions descriptors table
- Return:
- 1 when UUIDs match. Otherwise, 0
- */
+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 - reads the data queried by FFA_PARTITION_INFO_GET
and saves it in the private structure
- @count: The number of partitions queried
- @part_uuid: Pointer to the partition(s) UUID
- This function reads the partitions information
- returned by the FFA_PARTITION_INFO_GET and saves it in the private
- data structure.
- Return:
- The private data structure is updated with the partition(s) information
- 0 is returned on success. Otherwise, failure
- */
+static int ffa_read_partitions_info(u32 count, struct ffa_partition_uuid *part_uuid) +{
- if (!count) {
ffa_err("no partition detected");
return -ENODATA;
- }
- ffa_info("Reading partitions data from the RX buffer");
- 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 = ffa_priv_data->pair.rxtx_min_pages * SZ_4K;
if (data_bytes > buf_bytes) {
ffa_err("partitions data size exceeds the RX buffer size:");
ffa_err(" sizes in bytes: data %llu , RX buffer %llu ",
data_bytes,
buf_bytes);
return -ENOMEM;
}
ffa_priv_data->partitions.descs = devm_kmalloc(ffa_priv_data->dev, data_bytes,
__GFP_ZERO);
if (!ffa_priv_data->partitions.descs) {
ffa_err("cannot allocate partitions data buffer");
return -ENOMEM;
}
parts_info = (struct ffa_partition_info *)ffa_priv_data->pair.rxbuf;
for (desc_idx = 0 ; desc_idx < count ; desc_idx++) {
ffa_priv_data->partitions.descs[desc_idx].info =
parts_info[desc_idx];
ffa_info("Partition ID %x : info cached",
ffa_priv_data->partitions.descs[desc_idx].info.id);
}
ffa_priv_data->partitions.count = count;
ffa_info("%d partition(s) found and cached", count);
- } else {
u32 rx_desc_idx, cached_desc_idx;
struct ffa_partition_info *parts_info;
u8 desc_found;
parts_info = (struct ffa_partition_info *)ffa_priv_data->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 < ffa_priv_data->partitions.count;
cached_desc_idx++) {
/*
* save the UUID
*/
if (ffa_priv_data->partitions.descs[cached_desc_idx].info.id ==
parts_info[rx_desc_idx].id) {
ffa_priv_data->partitions.descs[cached_desc_idx].sp_uuid =
*part_uuid;
desc_found = 1;
break;
}
}
if (!desc_found)
return -ENODATA;
}
- }
- return 0;
+}
+/**
- ffa_query_partitions_info - invokes FFA_PARTITION_INFO_GET and saves partitions data
- @part_uuid: Pointer to the partition(s) UUID
- @pcount: Pointer to the number of partitions variable filled when querying
- This function executes the FFA_PARTITION_INFO_GET
- to query the partitions data. Then, it calls ffa_read_partitions_info
- to save the data in the private data structure.
- 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 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;
- }
- ffa_priv_data->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((u32)res.a2, part_uuid);
if (ret) {
ffa_err("failed to read partition(s) data , error (%d)", ret);
ffa_release_rx_buffer();
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();
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 - FFA_PARTITION_INFO_GET handler function
- The passed arguments:
- Mode 1: When getting from the driver the number of
- secure partitions:
- @uuid_str: pointer to the UUID string
- @parts_size: pointer to the variable that contains the number of partitions
The variable will be set by the driver
- @buffer: NULL
- Mode 2: When requesting the driver to return the
- partitions information:
- @uuid_str: pointer to the UUID string
- @parts_size: pointer to the size of the SPs information buffer in bytes
- @buffer: pointer to SPs information buffer
(allocated by the client).
The buffer will be filled by the driver
- This function queries the secure partition data from
- the private data structure. If not found, it invokes 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 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().
- When invoked through a client request, ffa_get_partitions_info should be
- called twice. First call is to get from the driver the number of secure
- partitions (SPs) associated to a particular UUID.
- Then, the caller (client) allocates the buffer to host the SPs data and
- issues a 2nd call. Then, the driver fills the SPs data in the pre-allocated
- buffer.
- To achieve the mechanism described above, ffa_get_partitions_info uses the
- following functions:
ffa_read_partitions_info
ffa_query_partitions_info
- Return:
- @parts_size: When pointing to the number of partitions variable, the number is
- set by the driver.
- When pointing to the partitions information buffer size, the buffer will be
- filled by the driver.
This is confusing, it's better if you use the same unit all the time. Since you below require the size to be an even multiple of sizeof(struct ffa_partition_info) you should perhaps use that unit.
Addressed in v8 patchset.
- On success 0 is returned. Otherwise, failure
- */
+static int ffa_get_partitions_info(const char *uuid_str,
u32 *parts_size, struct ffa_partition_info *buffer)
+{
- /*
* fill_data:
* 0: return the SP count
* 1: fill SP data and return it to the caller
* -1: undefined mode
*/
- int fill_data = -1;
In the code below this is treated as a bool, it's never tested for -1, so you could as well make this a bool.
Addressed in v8 patchset.
- u32 desc_idx, client_desc_idx;
- struct ffa_partition_uuid part_uuid = {0};
- u32 client_desc_max_cnt;
- u32 parts_found = 0;
- if (!ffa_priv_data->partitions.count || !ffa_priv_data->partitions.descs) {
ffa_err("no partition installed");
return -EINVAL;
- }
- if (!uuid_str) {
ffa_err("no UUID provided");
return -EINVAL;
- }
- if (!parts_size) {
ffa_err("no size/count provided");
return -EINVAL;
- }
- if (uuid_str_to_le_bin(uuid_str, (unsigned char *)&part_uuid)) {
ffa_err("invalid UUID");
return -EINVAL;
- }
- if (!buffer) {
/* Mode 1: getting the number of secure partitions */
fill_data = 0;
ffa_info("Preparing for checking partitions count");
- } else if ((*parts_size >= sizeof(struct ffa_partition_info)) &&
!(*parts_size % sizeof(struct ffa_partition_info))) {
/* Mode 2: retrieving the partitions information */
fill_data = 1;
client_desc_idx = 0;
/*
* number of empty descriptors preallocated by the caller
*/
client_desc_max_cnt = *parts_size / sizeof(struct ffa_partition_info);
ffa_info("Preparing for filling partitions info");
- } else {
ffa_err("invalid function arguments provided");
return -EINVAL;
- }
- ffa_info("Searching partitions using the provided UUID");
- /*
* search in the cached partitions
*/
- for (desc_idx = 0;
desc_idx < ffa_priv_data->partitions.count;
desc_idx++) {
if (ffa_uuid_are_identical(&ffa_priv_data->partitions.descs[desc_idx].sp_uuid,
&part_uuid)) {
ffa_info("Partition ID %x matches the provided UUID",
ffa_priv_data->partitions.descs[desc_idx].info.id);
parts_found++;
if (fill_data) {
/*
* trying to fill the partition info in the input buffer
*/
if (client_desc_idx < client_desc_max_cnt) {
buffer[client_desc_idx++] =
ffa_priv_data->partitions.descs[desc_idx].info;
continue;
}
ffa_err("failed to fill the current descriptor client buffer full");
return -ENOBUFS;
}
}
- }
- if (!parts_found) {
By asking for a UUID never found ffa_query_partitions_info() will be called again, even if the rx/tx buffers needed are not available any longer. Since you populate the cache in ffa_probe() below, perhaps it would be better to only rely on the cache after that point.
int ret;
ffa_info("No partition found. Querying framework ...");
ret = ffa_query_partitions_info(&part_uuid, &parts_found);
if (ret == 0) {
if (!fill_data) {
*parts_size = parts_found;
ffa_info("Number of partition(s) found matching the UUID: %d",
parts_found);
} else {
/*
* If SPs data detected, they are already in the private data
* structure, retry searching SP data again to return them
* to the caller
*/
if (parts_found)
ret = ffa_get_partitions_info(uuid_str, parts_size, buffer);
else
ret = -ENODATA;
}
}
return ret;
- }
- /* partition(s) found */
- if (!fill_data)
*parts_size = parts_found;
- return 0;
+}
+/**
- ffa_cache_partitions_info - Queries and saves all secure partitions data
- This function invokes 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 the
- resident private data structure and we keep the UUID field empty
- (in FF-A 1.0 UUID is not provided by the partition descriptor)
- This function is 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(void) +{
- return ffa_query_partitions_info(NULL, NULL);
+}
+/**
- ffa_msg_send_direct_req - FFA_MSG_SEND_DIRECT_{REQ,RESP} handler function
- @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
- */
+static int ffa_msg_send_direct_req(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;
- if (!ffa_priv_data || !ffa_priv_data->invoke_ffa_fn)
return -EINVAL;
- /* No partition installed */
- if (!ffa_priv_data->partitions.count || !ffa_priv_data->partitions.descs)
return -ENODEV;
This check isn't needed. What if the partition ID is known by other means?
- 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);
- }
- ffa_priv_data->invoke_ffa_fn((ffa_value_t){
.a0 = req_mode,
.a1 = PREP_SELF_ENDPOINT_ID(ffa_priv_data->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))
ffa_priv_data->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);
+}
+/**
- __arm_ffa_fn_smc - 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
- Return: void
- */
+void __arm_ffa_fn_smc(ffa_value_t args, ffa_value_t *res) +{
- arm_smccc_1_2_smc(&args, res);
+}
+/**
- ffa_set_smc_conduit - Set the SMC conduit
- This function selects the SMC conduit by setting the driver invoke function
- to SMC assembly function
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_set_smc_conduit(void) +{
- ffa_priv_data->invoke_ffa_fn = __arm_ffa_fn_smc;
- if (!ffa_priv_data->invoke_ffa_fn) {
ffa_err("failure to set the invoke function");
return -EINVAL;
- }
- ffa_info("Conduit is SMC");
- return 0;
+}
+/**
- ffa_set_bus_ops - Set the bus driver operations
- Setting the driver callbacks.
- */
+static void ffa_set_bus_ops(void) +{
- ffa_priv_data->ffa_ops.partition_info_get = ffa_get_partitions_info;
- ffa_priv_data->ffa_ops.sync_send_receive = ffa_msg_send_direct_req;
- ffa_priv_data->ffa_ops.rxtx_unmap = ffa_unmap_rxtx_buffers;
+}
+/**
- ffa_alloc_prvdata - allocate the driver main data structure and sets the device
- @dev: the arm_ffa device
- This function creates the main data structure embedding all the driver data.
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_alloc_prvdata(struct udevice *dev) +{
- if (!dev) {
ffa_err("no udevice found");
return -ENODEV;
- }
- /* The device is registered with the DM. Let's create the driver main data structure*/
- ffa_priv_data = devm_kmalloc(dev, sizeof(struct ffa_prvdata), __GFP_ZERO);
- if (!ffa_priv_data) {
ffa_err("can not allocate the driver main data structure");
return -ENOMEM;
- }
- ffa_priv_data->dev = dev;
- return 0;
+}
+/**
- ffa_probe - The driver probe function
- @dev: the arm_ffa device
- Probing is done at boot time and triggered by the uclass device discovery.
- At probe level the following actions are done:
- setting the conduit
- querying the FF-A framework version
- 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 the resident private data structure.
- The probe will fail if either FF-A framework is not detected or the
- FF-A requests are not behaving correctly. This ensures that the
- driver is not installed and its operations are not exported to the clients.
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_probe(struct udevice *dev) +{
- int ret;
- ret = ffa_alloc_prvdata(dev);
- if (ret != 0)
return ret;
- ffa_set_bus_ops();
- ret = ffa_set_smc_conduit();
- if (ret != 0)
return ret;
- ret = ffa_get_version();
- if (ret != 0)
return ret;
- ret = ffa_get_endpoint_id();
- if (ret != 0)
return ret;
- ret = ffa_get_rxtx_map_features();
- if (ret != 0)
return ret;
- ret = ffa_map_rxtx_buffers();
- if (ret != 0)
return ret;
- ret = ffa_cache_partitions_info();
- if (ret != 0) {
ffa_free_rxtx_buffers();
return ret;
- }
- return 0;
+}
+/**
- ffa_remove - The driver remove function
- @dev: the arm_ffa device
- When the device is about to be removed , unmap the RX/TX buffers and free the memory
- Return:
- 0 on success.
- */
+static int ffa_remove(struct udevice *dev) +{
- ffa_info("removing the device");
- ffa_unmap_rxtx_buffers();
- if (ffa_priv_data->pair.rxbuf || ffa_priv_data->pair.txbuf)
ffa_free_rxtx_buffers();
- return 0;
+}
+/**
- ffa_unbind - The driver unbind function
- @dev: the arm_ffa device
- After the device is removed and memory freed the device is unbound
- Return:
- 0 on success.
- */
+static int ffa_unbind(struct udevice *dev) +{
- ffa_info("unbinding the device , private data already released");
- ffa_priv_data = NULL;
- return 0;
+}
+/**
- ffa_bus_ops_get - bus driver operations getter
- Return:
- This function returns a pointer to the driver operations structure
- */
+const struct ffa_bus_ops *ffa_bus_ops_get(void) +{
- return &ffa_priv_data->ffa_ops;
+}
+/**
- ffa_bus_prvdata_get - bus driver private data getter
- Return:
- This function returns a pointer to the main private data structure
- */
+struct ffa_prvdata **ffa_bus_prvdata_get(void)
Why a pointer to a pointer, isn't "struct ffa_prvdata *" enough?
+{
- return &ffa_priv_data;
+}
+/**
- ffa_bus_discover - discover FF-A bus and probe the arm_ffa device
- This function makes sure the FF-A bus is discoverable.
- When probing succeeds FF-A discovery is done. The arm_ffa device is ready to use.
- When the bus was already discovered successfully the discovery will not run again.
- Arm FF-A transport is implemented through arm_ffa u-boot device managing the FF-A
- communication.
- All FF-A clients should use the arm_ffa device to use the FF-A transport.
- Return:
- 0 on success. Otherwise, failure
- */
+int ffa_bus_discover(void) +{
- int ret = 0;
- if (!ffa_priv_data)
ret = ffa_device_get();
- return ret;
+}
+/**
- Declaring the arm_ffa driver under UCLASS_FFA
- */
+U_BOOT_DRIVER(arm_ffa) = {
- .name = FFA_DRV_NAME,
- .id = UCLASS_FFA,
- .probe = ffa_probe,
- .remove = ffa_remove,
- .unbind = ffa_unbind,
+}; diff --git a/include/arm_ffa.h b/include/arm_ffa.h new file mode 100644 index 0000000000..b0c8a18926 --- /dev/null +++ b/include/arm_ffa.h @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#ifndef __ARM_FFA_H +#define __ARM_FFA_H
+#include <linux/printk.h>
+/*
- This header is public. It can be used by clients to access
- data structures and definitions they need
- */
+/*
- Macros for displaying logs
- */
+#define ffa_info(fmt, ...) pr_info("[FFA] " fmt "\n", ##__VA_ARGS__) +#define ffa_err(fmt, ...) pr_err("[FFA] " fmt "\n", ##__VA_ARGS__)
+/*
- 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 __packed 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;
+};
Perhaps this has been discussed before. Why is this packed? Is it to allow unaligned access or to be sure that there is not implicitly added padding? The Linux kernel does seem to need it.
+/*
- 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 __packed ffa_send_direct_data {
- unsigned long data0; /* w3/x3 */
- unsigned long data1; /* w4/x4 */
- unsigned long data2; /* w5/x5 */
- unsigned long data3; /* w6/x6 */
- unsigned long data4; /* w7/x7 */
+};
Why is this __packed? It seems unnecessary.
Cheers, Jens
+/**
- struct ffa_bus_ops - The driver operations structure
- @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)(const char *uuid_str,
u32 *parts_size, struct ffa_partition_info *buffer);
- int (*sync_send_receive)(u16 dst_part_id, struct ffa_send_direct_data *msg, bool is_smc64);
- int (*rxtx_unmap)(void);
+};
+/**
- The device driver and the Uclass driver public functions
- */
+/**
- ffa_bus_ops_get - driver operations getter
- */
+const struct ffa_bus_ops *ffa_bus_ops_get(void);
+/**
- ffa_bus_discover - discover FF-A bus and probes the arm_ffa device
- */
+int ffa_bus_discover(void);
+#endif diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 4b2c323452..fb59d4f356 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -4,6 +4,9 @@
- (C) Copyright 2012
- Pavel Herrmann morpheus.ibis@gmail.com
- (C) Copyright 2022 ARM Limited
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#ifndef _DM_UCLASS_ID_H @@ -55,6 +58,7 @@ enum uclass_id { UCLASS_EFI_MEDIA, /* Devices provided by UEFI firmware */ UCLASS_ETH, /* Ethernet device */ UCLASS_ETH_PHY, /* Ethernet PHY device */
- UCLASS_FFA, /* Arm Firmware Framework for Armv8-A */ UCLASS_FIRMWARE, /* Firmware */ UCLASS_FPGA, /* FPGA device */ UCLASS_FUZZING_ENGINE, /* Fuzzing engine */
-- 2.17.1

unmap RX/TX buffers at ExitBootServices()
Unmapping the RX/TX buffers created by u-boot is needed before EFI runtime.
At EFI runtime the linux kernel takes care of allocating its own RX/TX buffers and registering them with the secure world.
Secure world should be using the RX/TX buffers created by the kernel. So, RX/TX buffers created by u-boot must be unmapped.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v7: replace debug() by log_err()
lib/efi_loader/efi_boottime.c | 13 +++++++++++++ 1 file changed, 13 insertions(+)
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index a56021559b..b0b61bc812 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -3,6 +3,9 @@ * EFI application boot time services * * Copyright (c) 2016 Alexander Graf + * + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> @@ -23,6 +26,10 @@ #include <asm/setjmp.h> #include <linux/libfdt_env.h>
+#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT) +#include <arm_ffa.h> +#endif + DECLARE_GLOBAL_DATA_PTR;
/* Task priority level */ @@ -2178,6 +2185,12 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL); }
+#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT) + /* unmap FF-A RX/TX buffers */ + if (ffa_bus_ops_get()->rxtx_unmap()) + log_err("Can't unmap FF-A RX/TX buffers\n"); +#endif + /* Patch out unsupported runtime function */ efi_runtime_detach();

Provide armffa command showcasing the use of the FF-A driver
The armffa command allows to query secure partitions data from the secure world and exchanging messages with the partitions using 64-bit FF-A direct messaging.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v7:
* adapt do_ffa_dev_list() following the recent update on uclass_first_device/uclass_next_device functions (they return void now) * set armffa command to use 64-bit direct messaging
v4:
* remove pattern data in do_ffa_msg_send_direct_req
v3:
* use the new driver interfaces (partition_info_get, sync_send_receive) in armffa command
v2:
* replace use of ffa_helper_init_device function by ffa_helper_bus_discover
v1:
* introduce armffa command
MAINTAINERS | 1 + cmd/Kconfig | 10 ++ cmd/Makefile | 2 + cmd/armffa.c | 239 +++++++++++++++++++++++++++++++ drivers/firmware/arm-ffa/Kconfig | 1 + 5 files changed, 253 insertions(+) create mode 100644 cmd/armffa.c
diff --git a/MAINTAINERS b/MAINTAINERS index 450b5725ce..e86ba068ad 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -266,6 +266,7 @@ F: configs/cortina_presidio-asic-pnand_defconfig ARM FF-A M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com S: Maintained +F: cmd/armffa.c F: doc/arch/arm64.ffa.rst F: drivers/firmware/arm-ffa/ F: include/arm_ffa.h diff --git a/cmd/Kconfig b/cmd/Kconfig index 3f6bc70d43..16505361d5 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -918,6 +918,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 Arm FF-A driver + 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 ca9ed1054d..adb4728b65 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -12,6 +12,8 @@ obj-y += panic.o obj-y += version.o
# command + +obj-$(CONFIG_CMD_ARMFFA) += armffa.o obj-$(CONFIG_CMD_ACPI) += acpi.o obj-$(CONFIG_CMD_ADDRMAP) += addrmap.o obj-$(CONFIG_CMD_AES) += aes.o diff --git a/cmd/armffa.c b/cmd/armffa.c new file mode 100644 index 0000000000..964a5e1b34 --- /dev/null +++ b/cmd/armffa.c @@ -0,0 +1,239 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <arm_ffa.h> +#include <asm/io.h> +#include <common.h> +#include <command.h> +#include <dm.h> +#include <mapmem.h> +#include <stdlib.h> + +/** + * do_ffa_get_singular_partition_info - implementation of the getpart subcommand + * @cmdtp: Command Table + * @flag: flags + * @argc: number of arguments + * @argv: arguments + * + * This function queries the secure partition information which the UUID is provided + * as an argument. The function uses the arm_ffa driver partition_info_get operation + * 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_get_singular_partition_info(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + u32 count = 0, size = 0; + int ret; + struct ffa_partition_info *parts_info; + u32 info_idx; + + if (argc != 1) + return -EINVAL; + + /* Mode 1: getting the number of secure partitions */ + ret = ffa_bus_ops_get()->partition_info_get(argv[0], &count, NULL); + if (ret != 0) { + ffa_err("Failure in querying partitions count (error code: %d)", ret); + return ret; + } + + if (!count) { + ffa_info("No secure partition found"); + return ret; + } + + /* + * pre-allocate a buffer to be filled by the driver + * with ffa_partition_info structs + */ + + ffa_info("Pre-allocating %d partition(s) info structures", count); + + parts_info = calloc(count, sizeof(struct ffa_partition_info)); + if (!parts_info) + return -EINVAL; + + size = count * sizeof(struct ffa_partition_info); + + /* + * ask the driver to fill the buffer with the SPs info + */ + + ret = ffa_bus_ops_get()->partition_info_get(argv[0], &size, parts_info); + if (ret != 0) { + ffa_err("Failure in querying partition(s) info (error code: %d)", ret); + free(parts_info); + return ret; + } + + /* + * SPs found , show the partition information + */ + for (info_idx = 0; info_idx < count ; info_idx++) { + ffa_info("Partition: id = 0x%x , exec_ctxt 0x%x , properties 0x%x", + parts_info[info_idx].id, + parts_info[info_idx].exec_ctxt, + parts_info[info_idx].properties); + } + + free(parts_info); + + return 0; +} + +/** + * do_ffa_msg_send_direct_req - implementation of the ping subcommand + * @cmdtp: Command Table + * @flag: flags + * @argc: number of arguments + * @argv: arguments + * + * This function sends data to the secure partition which the ID is provided + * as an argument. The function uses the arm_ffa driver sync_send_receive operation + * to send data. + * + * Return: + * + * CMD_RET_SUCCESS: on success, otherwise failure + */ +int do_ffa_msg_send_direct_req(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; + + if (argc != 1) + return -EINVAL; + + errno = 0; + part_id = strtoul(argv[0], NULL, 16); + + if (errno) { + ffa_err("Invalid partition ID"); + return -EINVAL; + } + + ret = ffa_bus_ops_get()->sync_send_receive(part_id, &msg, 1); + if (ret == 0) { + u8 cnt; + + ffa_info("SP response:\n[LSB]"); + for (cnt = 0; + cnt < sizeof(struct ffa_send_direct_data) / sizeof(u64); + cnt++) + ffa_info("0x%llx", ((u64 *)&msg)[cnt]); + } else { + ffa_err("Sending direct request error (%d)", ret); + } + + return ret; +} + +/** + *do_ffa_dev_list - implementation of the devlist subcommand + * @cmdtp: [in] Command Table + * @flag: flags + * @argc: number of arguments + * @argv: arguments + * + * This function queries the devices belonging to the UCLASS_FFA + * class. Currently, one device is expected to show up: the arm_ffa device + * + * Return: + * + * CMD_RET_SUCCESS: on success, otherwise failure + */ +int do_ffa_dev_list(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct udevice *dev = NULL; + int i; + + ffa_info("arm_ffa uclass entries:"); + + for (i = 0, uclass_first_device(UCLASS_FFA, &dev); + dev; + uclass_next_device(&dev), i++) { + ffa_info("entry %d - instance %08x, ops %08x, plat %08x", + i, + (u32)map_to_sysmem(dev), + (u32)map_to_sysmem(dev->driver->ops), + (u32)map_to_sysmem(dev_get_plat(dev))); + } + + return 0; +} + +static struct cmd_tbl armffa_commands[] = { + U_BOOT_CMD_MKENT(getpart, 1, 1, do_ffa_get_singular_partition_info, "", ""), + U_BOOT_CMD_MKENT(ping, 1, 1, do_ffa_msg_send_direct_req, "", ""), + U_BOOT_CMD_MKENT(devlist, 0, 1, do_ffa_dev_list, "", ""), +}; + +/** + * do_armffa - the armffa command main function + * @cmdtp: Command Table + * @flag: flags + * @argc: number of arguments + * @argv: arguments + * + * This function identifies which armffa subcommand to run. + * Then, it makes sure the arm_ffa device is probed and + * ready for use. + * Then, it runs the subcommand. + * + * Return: + * + * CMD_RET_SUCCESS: on success, otherwise failure + */ +static int do_armffa(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct cmd_tbl *armffa_cmd; + int ret; + + if (argc < 2) + return CMD_RET_USAGE; + + armffa_cmd = find_cmd_tbl(argv[1], armffa_commands, ARRAY_SIZE(armffa_commands)); + + argc -= 2; + argv += 2; + + if (!armffa_cmd || argc > armffa_cmd->maxargs) + return CMD_RET_USAGE; + + ret = ffa_bus_discover(); + if (ret != 0) + return cmd_process_error(cmdtp, ret); + + if (!ffa_bus_ops_get()) + return -EINVAL; + + ret = armffa_cmd->cmd(armffa_cmd, flag, argc, argv); + + return cmd_process_error(armffa_cmd, ret); +} + +U_BOOT_CMD(armffa, 4, 1, do_armffa, + "Arm FF-A operations test command", + "getpart <partition UUID>\n" + " - lists the partition(s) info\n" + "ping <partition ID>\n" + " - sends a data pattern to the specified partition\n" + "devlist\n" + " - displays the arm_ffa device info\n"); diff --git a/drivers/firmware/arm-ffa/Kconfig b/drivers/firmware/arm-ffa/Kconfig index e4914b9bc7..be4df89d23 100644 --- a/drivers/firmware/arm-ffa/Kconfig +++ b/drivers/firmware/arm-ffa/Kconfig @@ -4,6 +4,7 @@ config ARM_FFA_TRANSPORT bool "Enable Arm Firmware Framework for Armv8-A driver" depends on DM && ARM64 select ARM_SMCCC + select CMD_ARMFFA select LIB_UUID select DEVRES help

Provide a Sandbox driver to emulate the FF-A ABIs
The emulated ABIs are those supported by the FF-A core driver and according to FF-A specification v1.0.
The Sandbox driver provides operations allowing the test application to read the status of all the inspected ABIs and perform functional tests based on that.
sandbox driver supports only 64-bit direct messaging.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v7: state that sandbox driver supports only 64-bit direct messaging
v4: align sandbox driver with the new FF-A driver interfaces and new way of error handling
v1: introduce the sandbox driver
MAINTAINERS | 1 + configs/sandbox64_defconfig | 2 + configs/sandbox_defconfig | 2 + doc/arch/sandbox.rst | 1 + drivers/firmware/arm-ffa/Kconfig | 9 +- drivers/firmware/arm-ffa/Makefile | 1 + drivers/firmware/arm-ffa/arm_ffa_prv.h | 15 +- drivers/firmware/arm-ffa/core.c | 24 +- drivers/firmware/arm-ffa/sandbox.c | 659 ++++++++++++++++++ .../firmware/arm-ffa/sandbox_arm_ffa_prv.h | 144 ++++ include/arm_ffa.h | 2 +- include/sandbox_arm_ffa.h | 91 +++ lib/efi_loader/efi_boottime.c | 2 +- 13 files changed, 939 insertions(+), 14 deletions(-) create mode 100644 drivers/firmware/arm-ffa/sandbox.c create mode 100644 drivers/firmware/arm-ffa/sandbox_arm_ffa_prv.h create mode 100644 include/sandbox_arm_ffa.h
diff --git a/MAINTAINERS b/MAINTAINERS index e86ba068ad..1d745b175d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -270,6 +270,7 @@ F: cmd/armffa.c 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 sbabic@denx.de diff --git a/configs/sandbox64_defconfig b/configs/sandbox64_defconfig index af2ce34174..01f3b6456f 100644 --- a/configs/sandbox64_defconfig +++ b/configs/sandbox64_defconfig @@ -256,3 +256,5 @@ CONFIG_TEST_FDTDEC=y CONFIG_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y +CONFIG_ARM_FFA_TRANSPORT=y +CONFIG_SANDBOX_FFA=y \ No newline at end of file diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index f12d0a4f51..f7e8d73f75 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -334,3 +334,5 @@ CONFIG_TEST_FDTDEC=y CONFIG_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y +CONFIG_ARM_FFA_TRANSPORT=y +CONFIG_SANDBOX_FFA=y \ No newline at end of file diff --git a/doc/arch/sandbox.rst b/doc/arch/sandbox.rst index ed66f70f61..9a3d362dfc 100644 --- a/doc/arch/sandbox.rst +++ b/doc/arch/sandbox.rst @@ -203,6 +203,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 be4df89d23..b86f16d778 100644 --- a/drivers/firmware/arm-ffa/Kconfig +++ b/drivers/firmware/arm-ffa/Kconfig @@ -2,8 +2,8 @@
config ARM_FFA_TRANSPORT bool "Enable Arm Firmware Framework for Armv8-A driver" - depends on DM && ARM64 - select ARM_SMCCC + depends on DM && (ARM64 || SANDBOX) + select ARM_SMCCC if !SANDBOX select CMD_ARMFFA select LIB_UUID select DEVRES @@ -29,3 +29,8 @@ config ARM_FFA_TRANSPORT
For more details about the FF-A driver, please refer to doc/arch/arm64.ffa.rst
+config SANDBOX_FFA + bool "FF-A Sandbox driver" + depends on ARM_FFA_TRANSPORT && SANDBOX + help + This emulates the FF-A handling under Sandbox and allows to test the FF-A driver diff --git a/drivers/firmware/arm-ffa/Makefile b/drivers/firmware/arm-ffa/Makefile index 043a8915be..0d21d6b47a 100644 --- a/drivers/firmware/arm-ffa/Makefile +++ b/drivers/firmware/arm-ffa/Makefile @@ -4,3 +4,4 @@ # Abdellatif El Khlifi, Arm Limited, abdellatif.elkhlifi@arm.com.
obj-y += arm-ffa-uclass.o core.o +obj-$(CONFIG_SANDBOX_FFA) += sandbox.o diff --git a/drivers/firmware/arm-ffa/arm_ffa_prv.h b/drivers/firmware/arm-ffa/arm_ffa_prv.h index ad7430ada9..a16536c2be 100644 --- a/drivers/firmware/arm-ffa/arm_ffa_prv.h +++ b/drivers/firmware/arm-ffa/arm_ffa_prv.h @@ -19,6 +19,16 @@ /* FF-A core driver name */ #define FFA_DRV_NAME "arm_ffa"
+/* The FF-A SMC function definitions */ + +#if CONFIG_IS_ENABLED(SANDBOX_FFA) +#include "sandbox_arm_ffa.h" +#else +typedef struct arm_smccc_1_2_regs ffa_value_t; +#endif + +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) @@ -103,11 +113,6 @@ struct ffa_abi_errmap { #define FFA_ERRMAP_COUNT (FFA_LAST_ID - FFA_FIRST_ID + 1) #define FFA_ID_TO_ERRMAP_ID(ffa_id) ((ffa_id) - FFA_FIRST_ID)
-/* The FF-A SMC function definitions */ - -typedef struct arm_smccc_1_2_regs ffa_value_t; -typedef void (*invoke_ffa_fn_t)(ffa_value_t args, ffa_value_t *res); - /* * struct ffa_partition_uuid - 16 bytes UUID transmitted by FFA_PARTITION_INFO_GET * @a1-4: 32-bit words access to the UUID data diff --git a/drivers/firmware/arm-ffa/core.c b/drivers/firmware/arm-ffa/core.c index b955e5187b..4e1535abac 100644 --- a/drivers/firmware/arm-ffa/core.c +++ b/drivers/firmware/arm-ffa/core.c @@ -1069,6 +1069,7 @@ static int ffa_msg_send_direct_req(u16 dst_part_id, struct ffa_send_direct_data return ffa_to_std_errno(ffa_errno); }
+#if !CONFIG_IS_ENABLED(SANDBOX_FFA) /** * __arm_ffa_fn_smc - SMC wrapper * @args: FF-A ABI arguments to be copied to Xn registers @@ -1082,6 +1083,7 @@ void __arm_ffa_fn_smc(ffa_value_t args, ffa_value_t *res) { arm_smccc_1_2_smc(&args, res); } +#endif
/** * ffa_set_smc_conduit - Set the SMC conduit @@ -1095,7 +1097,12 @@ void __arm_ffa_fn_smc(ffa_value_t args, ffa_value_t *res) */ static int ffa_set_smc_conduit(void) { - ffa_priv_data->invoke_ffa_fn = __arm_ffa_fn_smc; +#if CONFIG_IS_ENABLED(SANDBOX_FFA) + ffa_priv_data->invoke_ffa_fn = sandbox_arm_ffa_smccc_smc; + ffa_info("Using SMC emulation"); +#else + ffa_priv_data->invoke_ffa_fn = __arm_ffa_fn_smc; +#endif
if (!ffa_priv_data->invoke_ffa_fn) { ffa_err("failure to set the invoke function"); @@ -1272,15 +1279,16 @@ struct ffa_prvdata **ffa_bus_prvdata_get(void) }
/** - * ffa_bus_discover - discover FF-A bus and probe the arm_ffa device + * ffa_bus_discover - discover FF-A bus and probe arm_ffa and sandbox_arm_ffa devices * * This function makes sure the FF-A bus is discoverable. - * When probing succeeds FF-A discovery is done. The arm_ffa device is ready to use. + * When probing succeeds FF-A discovery is done. The arm_ffa and sandbox_arm_ffa devices + * are ready to use. * * When the bus was already discovered successfully the discovery will not run again. * * Arm FF-A transport is implemented through arm_ffa u-boot device managing the FF-A - * communication. + * communication. In Sandbox mode sandbox_arm_ffa is used to test arm_ffa driver. * All FF-A clients should use the arm_ffa device to use the FF-A transport. * * Return: @@ -1291,9 +1299,15 @@ int ffa_bus_discover(void) { int ret = 0;
- if (!ffa_priv_data) + if (!ffa_priv_data) { ret = ffa_device_get();
+#if CONFIG_IS_ENABLED(SANDBOX_FFA) + if (ret == 0) + ret = sandbox_ffa_device_get(); +#endif + } + return ret; }
diff --git a/drivers/firmware/arm-ffa/sandbox.c b/drivers/firmware/arm-ffa/sandbox.c new file mode 100644 index 0000000000..8e8549441d --- /dev/null +++ b/drivers/firmware/arm-ffa/sandbox.c @@ -0,0 +1,659 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include "sandbox_arm_ffa_prv.h" +#include <asm/global_data.h> +#include <common.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> +#include <mapmem.h> +#include <string.h> + +DECLARE_GLOBAL_DATA_PTR; + +/** + * The device private data structure containing all the emulated secure world data + */ +static struct sandbox_ffa_prvdata sandbox_ffa_priv_data = {0}; + +/* 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_SP2_ID, .exec_ctxt = 0x9587, .properties = 0x45325621 }, + .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_SP3_ID, .exec_ctxt = 0x7687, .properties = 0x23325621 }, + .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, + } + } + +}; + +/* + * Driver functions + */ + +/** + * sandbox_ffa_get_device - probes the sandbox_arm_ffa device + * + * This function makes sure the sandbox_arm_ffa device is probed + * This function makes sure the sandbox_arm_ffa device is + * created, bound to this driver, probed and ready to use. + * + * sandbox_arm_ffa depends on arm_ffa device. This dependency is + * handled by ffa_bus_discover function. arm_ffa is probed first then + * sandbox_arm_ffa. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int sandbox_ffa_device_get(void) +{ + int ret; + + if (sandbox_ffa_priv_data.dev) + return 0; + + ret = device_bind(dm_root(), + DM_DRIVER_GET(sandbox_arm_ffa), + FFA_SANDBOX_DRV_NAME, + NULL, + ofnode_null(), + &sandbox_ffa_priv_data.dev); + if (ret) { + sandbox_ffa_priv_data.dev = NULL; + return ret; + } + + ret = device_probe(sandbox_ffa_priv_data.dev); + if (ret) { + ffa_err("[Sandbox] can not probe the device"); + device_unbind(sandbox_ffa_priv_data.dev); + sandbox_ffa_priv_data.dev = NULL; + return ret; + } + + return 0; +} + +/** + * sandbox_ffa_version - Emulated FFA_VERSION handler function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_VERSION FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_version) +{ + sandbox_ffa_priv_data.fwk_version = FFA_VERSION_1_0; + res->a0 = sandbox_ffa_priv_data.fwk_version; + + /* x1-x7 MBZ */ + memset(FFA_X1X7_MBZ_REG_START, 0, FFA_X1X7_MBZ_CNT * sizeof(unsigned long)); + + return 0; +} + +/** + * sandbox_ffa_id_get - Emulated FFA_ID_GET handler function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_ID_GET FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_id_get) +{ + res->a0 = FFA_SMC_32(FFA_SUCCESS); + res->a1 = 0; + + sandbox_ffa_priv_data.id = NS_PHYS_ENDPOINT_ID; + res->a2 = sandbox_ffa_priv_data.id; + + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, 0, FFA_X3X7_MBZ_CNT * sizeof(unsigned long)); + + return 0; +} + +/** + * sandbox_ffa_features - Emulated FFA_FEATURES handler function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_FEATURES FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_features) +{ + 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(unsigned long)); + } else { + res->a0 = FFA_SMC_32(FFA_ERROR); + res->a2 = FFA_ERR_STAT_NOT_SUPPORTED; + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, + 0, FFA_X3X7_MBZ_CNT * sizeof(unsigned long)); + ffa_err("[Sandbox] FF-A interface 0x%lx not implemented", pargs->a1); + } + + res->a1 = 0; + + return 0; +} + +/** + * sandbox_ffa_partition_info_get - Emulated FFA_PARTITION_INFO_GET handler function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_PARTITION_INFO_GET FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_partition_info_get) +{ + struct ffa_partition_info *rxbuf_desc_info = NULL; + u32 descs_cnt; + u32 descs_size_bytes; + + res->a0 = FFA_SMC_32(FFA_ERROR); + + if (!sandbox_ffa_priv_data.pair.rxbuf) { + res->a2 = FFA_ERR_STAT_DENIED; + goto cleanup; + } + + if (sandbox_ffa_priv_data.pair_info.rxbuf_owned) { + res->a2 = FFA_ERR_STAT_BUSY; + goto cleanup; + } + + if (!sandbox_ffa_priv_data.partitions.descs) { + sandbox_ffa_priv_data.partitions.descs = sandbox_partitions; + sandbox_ffa_priv_data.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 descriptors buffer size */ + if ((sandbox_ffa_priv_data.pair_info.rxtx_buf_size * SZ_4K) < descs_size_bytes) { + res->a2 = FFA_ERR_STAT_NO_MEMORY; + goto cleanup; + } + + rxbuf_desc_info = (struct ffa_partition_info *)sandbox_ffa_priv_data.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++) = + sandbox_ffa_priv_data.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 */ + sandbox_ffa_priv_data.pair_info.rxbuf_owned = 1; + + goto cleanup; + } + + /* + * A UUID is specified. Return the information of all partitions matching + * the UUID + */ + + for (descs_cnt = 0 ; descs_cnt < SANDBOX_PARTITIONS_CNT ; descs_cnt++) + if (pargs->a1 == sandbox_ffa_priv_data.partitions.descs[descs_cnt].sp_uuid.a1 && + pargs->a2 == sandbox_ffa_priv_data.partitions.descs[descs_cnt].sp_uuid.a2 && + pargs->a3 == sandbox_ffa_priv_data.partitions.descs[descs_cnt].sp_uuid.a3 && + pargs->a4 == sandbox_ffa_priv_data.partitions.descs[descs_cnt].sp_uuid.a4) { + *(rxbuf_desc_info++) = + sandbox_ffa_priv_data.partitions.descs[descs_cnt].info; + } + + if (rxbuf_desc_info != ((struct ffa_partition_info *)sandbox_ffa_priv_data.pair.rxbuf)) { + res->a0 = FFA_SMC_32(FFA_SUCCESS); + /* store the partitions count */ + res->a2 = (unsigned long) + (rxbuf_desc_info - (struct ffa_partition_info *) + sandbox_ffa_priv_data.pair.rxbuf); + + /* transfer ownership to the consumer: the non secure world */ + sandbox_ffa_priv_data.pair_info.rxbuf_owned = 1; + } else { + /* Unrecognized UUID */ + res->a2 = FFA_ERR_STAT_INVALID_PARAMETERS; + } + +cleanup: + + ffa_err("[Sandbox] FFA_PARTITION_INFO_GET (%ld)", res->a2); + + res->a1 = 0; + + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, 0, FFA_X3X7_MBZ_CNT * sizeof(unsigned long)); + + return 0; +} + +/** + * sandbox_ffa_rxtx_map - Emulated FFA_RXTX_MAP handler function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_RXTX_MAP FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_rxtx_map) +{ + res->a0 = FFA_SMC_32(FFA_ERROR); + + if (sandbox_ffa_priv_data.pair.txbuf && sandbox_ffa_priv_data.pair.rxbuf) { + res->a2 = FFA_ERR_STAT_DENIED; + goto feedback; + } + + if (pargs->a3 >= RXTX_BUFFERS_MIN_PAGES && pargs->a1 && pargs->a2) { + sandbox_ffa_priv_data.pair.txbuf = pargs->a1; + sandbox_ffa_priv_data.pair.rxbuf = pargs->a2; + sandbox_ffa_priv_data.pair_info.rxtx_buf_size = pargs->a3; + sandbox_ffa_priv_data.pair_info.rxbuf_mapped = 1; + res->a0 = FFA_SMC_32(FFA_SUCCESS); + res->a2 = 0; + goto feedback; + } + + if (!pargs->a1 || !pargs->a2) + res->a2 = FFA_ERR_STAT_INVALID_PARAMETERS; + else + res->a2 = FFA_ERR_STAT_NO_MEMORY; + + ffa_err("[Sandbox] error in FFA_RXTX_MAP arguments (%d)", (int)res->a2); + +feedback: + + res->a1 = 0; + + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, + 0, FFA_X3X7_MBZ_CNT * sizeof(unsigned long)); + + return 0; +} + +/** + * sandbox_ffa_rxtx_unmap - Emulated FFA_RXTX_UNMAP handler function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_RXTX_UNMAP FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_rxtx_unmap) +{ + res->a0 = FFA_SMC_32(FFA_ERROR); + res->a2 = FFA_ERR_STAT_INVALID_PARAMETERS; + + if (GET_NS_PHYS_ENDPOINT_ID(pargs->a1) != sandbox_ffa_priv_data.id) + goto feedback; + + if (sandbox_ffa_priv_data.pair.txbuf && sandbox_ffa_priv_data.pair.rxbuf) { + sandbox_ffa_priv_data.pair.txbuf = 0; + sandbox_ffa_priv_data.pair.rxbuf = 0; + sandbox_ffa_priv_data.pair_info.rxtx_buf_size = 0; + sandbox_ffa_priv_data.pair_info.rxbuf_mapped = 0; + res->a0 = FFA_SMC_32(FFA_SUCCESS); + res->a2 = 0; + goto feedback; + } + + ffa_err("[Sandbox] No buffer pair registered on behalf of the caller"); + +feedback: + + res->a1 = 0; + + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, + 0, FFA_X3X7_MBZ_CNT * sizeof(unsigned long)); + + return 0; +} + +/** + * sandbox_ffa_rx_release - Emulated FFA_RX_RELEASE handler function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_RX_RELEASE FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_rx_release) +{ + if (!sandbox_ffa_priv_data.pair_info.rxbuf_owned) { + res->a0 = FFA_SMC_32(FFA_ERROR); + res->a2 = FFA_ERR_STAT_DENIED; + } else { + sandbox_ffa_priv_data.pair_info.rxbuf_owned = 0; + res->a0 = FFA_SMC_32(FFA_SUCCESS); + res->a2 = 0; + } + + res->a1 = 0; + + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, + 0, FFA_X3X7_MBZ_CNT * sizeof(unsigned long)); + + return 0; +} + +/** + * sandbox_ffa_sp_valid - Checks SP validity + * @part_id: partition ID to check + * + * This is the function searches the input ID in the descriptors table. + * + * Return: + * + * 1 on success (Partition found). Otherwise, failure + */ +static int sandbox_ffa_sp_valid(u16 part_id) +{ + u32 descs_cnt; + + for (descs_cnt = 0 ; descs_cnt < SANDBOX_PARTITIONS_CNT ; descs_cnt++) + if (sandbox_ffa_priv_data.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 function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_MSG_SEND_DIRECT_{REQ,RESP} + * FF-A functions. 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 + */ +SANDBOX_SMC_FFA_ABI(ffa_msg_send_direct_req) +{ + u16 part_id; + + part_id = GET_DST_SP_ID(pargs->a1); + + if ((GET_NS_PHYS_ENDPOINT_ID(pargs->a1) != sandbox_ffa_priv_data.id) || + !sandbox_ffa_sp_valid(part_id) || + pargs->a2) { + res->a0 = FFA_SMC_32(FFA_ERROR); + res->a1 = 0; + res->a2 = FFA_ERR_STAT_INVALID_PARAMETERS; + + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, + 0, FFA_X3X7_MBZ_CNT * sizeof(unsigned long)); + + return 0; + } + + res->a0 = FFA_SMC_64(FFA_MSG_SEND_DIRECT_RESP); + + res->a1 = PREP_SRC_SP_ID(part_id) | + PREP_NS_PHYS_ENDPOINT_ID(sandbox_ffa_priv_data.id); + + res->a2 = 0; + + /* + * return 0xff bytes as a response + */ + res->a3 = 0xffffffffffffffff; + res->a4 = 0xffffffffffffffff; + res->a5 = 0xffffffffffffffff; + res->a6 = 0xffffffffffffffff; + res->a7 = 0xffffffffffffffff; + + return 0; +} + +/** + * sandbox_ffa_get_prv_data - Returns the pointer to FF-A core pivate data + * @func_data: Pointer to the FF-A function arguments container structure + * + * This is the handler that returns the address of the FF-A core pivate data. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int sandbox_ffa_get_prv_data(struct ffa_sandbox_data *func_data) +{ + if (!func_data) + return -EINVAL; + + if (!func_data->data0 || func_data->data0_size != sizeof(struct ffa_prvdata **)) + return -EINVAL; + + if (!func_data->data1 || func_data->data1_size != sizeof(struct sandbox_ffa_prvdata **)) + return -EINVAL; + + *((struct ffa_prvdata **)func_data->data0) = *(ffa_bus_prvdata_get()); + *((struct sandbox_ffa_prvdata **)func_data->data1) = &sandbox_ffa_priv_data; + + return 0; +} + +/** + * sandbox_ffa_get_rxbuf_flags - Reading the mapping/ownership flags + * @queried_func_id: The FF-A function to be queried + * @func_data: Pointer to the FF-A function arguments container structure + * + * This is the handler that queries 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(u32 queried_func_id, struct ffa_sandbox_data *func_data) +{ + 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) = sandbox_ffa_priv_data.pair_info.rxbuf_mapped; + return 0; + case FFA_RX_RELEASE: + *((u8 *)func_data->data0) = sandbox_ffa_priv_data.pair_info.rxbuf_owned; + return 0; + default: + ffa_err("[Sandbox] The querried FF-A interface flag (%d) undefined", + queried_func_id); + return -EINVAL; + } +} + +/** + * sandbox_ffa_query_core_state - The driver dispatcher function + * @queried_func_id: The FF-A function to be queried + * @func_data: Pointer to the FF-A function arguments container structure + * + * Queries the status of FF-A ABI specified in the input argument. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int sandbox_ffa_query_core_state(u32 queried_func_id, struct ffa_sandbox_data *func_data) +{ + switch (queried_func_id) { + case FFA_VERSION: + case FFA_ID_GET: + case FFA_FEATURES: + return sandbox_ffa_get_prv_data(func_data); + case FFA_RXTX_MAP: + case FFA_RXTX_UNMAP: + case FFA_RX_RELEASE: + return sandbox_ffa_get_rxbuf_flags(queried_func_id, func_data); + default: + ffa_err("[Sandbox] The querried FF-A interface (%d) undefined", 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 + * + * Sandbox driver emulates the FF-A ABIs SMC call using this function. + * 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; + + switch (args.a0) { + case FFA_SMC_32(FFA_VERSION): + ret = sandbox_ffa_version(&args, res); + break; + case FFA_SMC_32(FFA_PARTITION_INFO_GET): + ret = sandbox_ffa_partition_info_get(&args, res); + break; + case FFA_SMC_32(FFA_RXTX_UNMAP): + ret = sandbox_ffa_rxtx_unmap(&args, res); + break; + case FFA_SMC_64(FFA_MSG_SEND_DIRECT_REQ): + ret = sandbox_ffa_msg_send_direct_req(&args, res); + break; + case FFA_SMC_32(FFA_ID_GET): + ret = sandbox_ffa_id_get(&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(&args, res); + break; + case FFA_SMC_32(FFA_RX_RELEASE): + ret = sandbox_ffa_rx_release(&args, res); + break; + default: + ffa_err("[Sandbox] Undefined FF-A interface (0x%lx)", args.a0); + } + + if (ret != 0) + ffa_err("[Sandbox] FF-A ABI internal failure (%d)", ret); +} + +/** + * sandbox_ffa_probe - The driver probe function + * @dev: the sandbox_arm_ffa device + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int sandbox_ffa_probe(struct udevice *dev) +{ + return 0; +} + +/** + * sandbox_ffa_remove - The driver remove function + * @dev: the sandbox_arm_ffa device + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int sandbox_ffa_remove(struct udevice *dev) +{ + ffa_info("[Sandbox] removing the device"); + memset(&sandbox_ffa_priv_data, 0, sizeof(sandbox_ffa_priv_data)); + return 0; +} + +/** + * Declaring the sandbox_arm_ffa driver under UCLASS_FFA + */ +U_BOOT_DRIVER(sandbox_arm_ffa) = { + .name = FFA_SANDBOX_DRV_NAME, + .id = UCLASS_FFA, + .probe = sandbox_ffa_probe, + .remove = sandbox_ffa_remove, +}; diff --git a/drivers/firmware/arm-ffa/sandbox_arm_ffa_prv.h b/drivers/firmware/arm-ffa/sandbox_arm_ffa_prv.h new file mode 100644 index 0000000000..4db57f5092 --- /dev/null +++ b/drivers/firmware/arm-ffa/sandbox_arm_ffa_prv.h @@ -0,0 +1,144 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __SANDBOX_ARM_FFA_PRV_H +#define __SANDBOX_ARM_FFA_PRV_H + +#include "arm_ffa_prv.h" +#include <sandbox_arm_ffa.h> + +/* + * This header is private. It is exclusively used by the Sandbox FF-A driver + */ + +/* FF-A core driver name */ +#define FFA_SANDBOX_DRV_NAME "sandbox_arm_ffa" + +/* FF-A ABIs internal error codes (as defined by the spec) */ + +#define FFA_ERR_STAT_NOT_SUPPORTED -1 +#define FFA_ERR_STAT_INVALID_PARAMETERS -2 +#define FFA_ERR_STAT_NO_MEMORY -3 +#define FFA_ERR_STAT_BUSY -4 +#define FFA_ERR_STAT_DENIED -6 + +/* Providing Arm SMCCC declarations to sandbox */ + +#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)) + +/* 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) + +/* secure partitions count */ +#define SANDBOX_PARTITIONS_CNT (4) + +/* 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: the consumer) + * @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 as set by the FF-A core driver + * + * Data structure hosting 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_prvdata - the driver private data structure + * + * @dev: The arm_ffa device under u-boot driver model + * @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 + * @conduit: The selected conduit + * + * The driver data structure hosting all the emulated secure world data. + */ +struct sandbox_ffa_prvdata { + struct udevice *dev; + u32 fwk_version; + u16 id; + struct ffa_partitions partitions; + struct ffa_rxtxpair pair; + struct ffa_rxtxpair_info pair_info; +}; + +#define SANDBOX_SMC_FFA_ABI(ffabi) static int sandbox_##ffabi(ffa_value_t *pargs, ffa_value_t *res) + +#endif diff --git a/include/arm_ffa.h b/include/arm_ffa.h index b0c8a18926..f51e826d47 100644 --- a/include/arm_ffa.h +++ b/include/arm_ffa.h @@ -86,7 +86,7 @@ struct ffa_bus_ops { const struct ffa_bus_ops *ffa_bus_ops_get(void);
/** - * ffa_bus_discover - discover FF-A bus and probes the arm_ffa device + * ffa_bus_discover - discover FF-A bus and probes the arm_ffa and sandbox_arm_ffa devices */ int ffa_bus_discover(void);
diff --git a/include/sandbox_arm_ffa.h b/include/sandbox_arm_ffa.h new file mode 100644 index 0000000000..d5df16f282 --- /dev/null +++ b/include/sandbox_arm_ffa.h @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __SANDBOX_ARM_FFA_H +#define __SANDBOX_ARM_FFA_H + +#include <arm_ffa.h> + +/** + * struct sandbox_smccc_1_2_regs - Arguments for or Results from emulated SMC call + * @a0-a17 argument values from registers 0 to 17 + */ +struct sandbox_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; +}; + +typedef struct sandbox_smccc_1_2_regs ffa_value_t; + +/* UUIDs of services supported by the sandbox driver */ +#define SANDBOX_SERVICE1_UUID "ed32d533-4209-99e6-2d72-cdd998a79cc0" +#define SANDBOX_SERVICE2_UUID "ed32d544-4209-99e6-2d72-cdd998a79cc0" +#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" + +#define SANDBOX_SP_COUNT_PER_VALID_SERVICE 2 + +/** + * struct ffa_sandbox_data - generic data structure used to exchange + * data between test cases and the sandbox driver + * @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 + * + * Using this structure sandbox test cases can pass various types of data with different sizes. + */ +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 driver public functions + */ + +/** + * sandbox_ffa_query_core_state - Queries the status of FF-A ABIs + */ +int sandbox_ffa_query_core_state(u32 queried_func_id, struct ffa_sandbox_data *func_data); + +/** + * sandbox_ffa_get_device - create, bind and probe the sandbox_arm_ffa device + */ +int sandbox_ffa_device_get(void); + +/** + * sandbox_arm_ffa_smccc_smc - FF-A SMC call emulation + */ +void sandbox_arm_ffa_smccc_smc(ffa_value_t args, ffa_value_t *res); + +#endif diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index b0b61bc812..8f18d16173 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -2185,7 +2185,7 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL); }
-#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT) +#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT) && !CONFIG_IS_ENABLED(SANDBOX_FFA) /* unmap FF-A RX/TX buffers */ if (ffa_bus_ops_get()->rxtx_unmap()) log_err("Can't unmap FF-A RX/TX buffers\n");

Add functional test cases for the FF-A core driver
These tests rely on the FF-A Sandbox driver which helps in inspecting the FF-A core driver.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v7: set the tests to use 64-bit direct messaging
v4: align sandbox tests with the new FF-A driver interfaces and new way of error handling
v1: introduce sandbox tests
MAINTAINERS | 1 + test/dm/Makefile | 2 + test/dm/ffa.c | 394 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 397 insertions(+) create mode 100644 test/dm/ffa.c
diff --git a/MAINTAINERS b/MAINTAINERS index 1d745b175d..d87f1865b5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -271,6 +271,7 @@ F: doc/arch/arm64.ffa.rst F: drivers/firmware/arm-ffa/ F: include/arm_ffa.h F: include/sandbox_arm_ffa.h +F: test/dm/ffa.c
ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de diff --git a/test/dm/Makefile b/test/dm/Makefile index cc3cc454f7..9691821f81 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0+ # # Copyright (c) 2013 Google, Inc +# (C) Copyright 2022 ARM Limited
obj-$(CONFIG_UT_DM) += test-dm.o
@@ -83,6 +84,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_SANDBOX_FFA) += 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 0000000000..14b19cf71e --- /dev/null +++ b/test/dm/ffa.c @@ -0,0 +1,394 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Functional tests for UCLASS_FFA class + * + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <console.h> +#include <dm.h> +#include <dm/test.h> +#include "../../drivers/firmware/arm-ffa/sandbox_arm_ffa_prv.h" +#include <sandbox_arm_ffa.h> +#include <test/test.h> +#include <test/ut.h> + +/* Macros */ + +#define LOG_MSG_SZ (100) +#define LOG_CMD_SZ (LOG_MSG_SZ * 2) + +/* Functional tests for the UCLASS_FFA */ + +static int dm_test_ffa_log(struct unit_test_state *uts, char *msg) +{ + char cmd[LOG_CMD_SZ] = {0}; + + console_record_reset(); + + snprintf(cmd, LOG_CMD_SZ, "echo "%s"", msg); + run_command(cmd, 0); + + ut_assert_console_end(); + + return CMD_RET_SUCCESS; +} + +static int check_fwk_version(struct ffa_prvdata *prvdata, struct sandbox_ffa_prvdata *sdx_prvdata, + struct unit_test_state *uts) +{ + if (prvdata->fwk_version != sdx_prvdata->fwk_version) { + char msg[LOG_MSG_SZ] = {0}; + + snprintf(msg, LOG_MSG_SZ, + "[%s]: Error: framework version: core = 0x%x , sandbox = 0x%x", __func__, + prvdata->fwk_version, + sdx_prvdata->fwk_version); + + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + return CMD_RET_SUCCESS; +} + +static int check_endpoint_id(struct ffa_prvdata *prvdata, struct unit_test_state *uts) +{ + if (prvdata->id) { + char msg[LOG_MSG_SZ] = {0}; + + snprintf(msg, LOG_MSG_SZ, + "[%s]: Error: endpoint id: core = 0x%x", __func__, prvdata->id); + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + return CMD_RET_SUCCESS; +} + +static int check_core_dev(struct ffa_prvdata *prvdata, struct unit_test_state *uts) +{ + if (!prvdata->dev) { + char msg[LOG_MSG_SZ] = {0}; + + snprintf(msg, LOG_MSG_SZ, "[%s]: Error: core device NULL", __func__); + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + return CMD_RET_SUCCESS; +} + +static int check_sandbox_dev(struct sandbox_ffa_prvdata *sdx_prvdata, struct unit_test_state *uts) +{ + if (!sdx_prvdata->dev) { + char msg[LOG_MSG_SZ] = {0}; + + snprintf(msg, LOG_MSG_SZ, "[%s]: Error: sandbox device NULL", __func__); + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + return CMD_RET_SUCCESS; +} + +static int check_rxtxbuf(struct ffa_prvdata *prvdata, struct unit_test_state *uts) +{ + if (!prvdata->pair.rxbuf && prvdata->pair.txbuf) { + char msg[LOG_MSG_SZ] = {0}; + + snprintf(msg, LOG_MSG_SZ, "[%s]: Error: rxbuf = 0x%llx txbuf = 0x%llx", __func__, + prvdata->pair.rxbuf, + prvdata->pair.txbuf); + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + return CMD_RET_SUCCESS; +} + +static int check_features(struct ffa_prvdata *prvdata, struct unit_test_state *uts) +{ + char msg[LOG_MSG_SZ] = {0}; + + if (prvdata->pair.rxtx_min_pages != RXTX_4K && + prvdata->pair.rxtx_min_pages != RXTX_16K && + prvdata->pair.rxtx_min_pages != RXTX_64K) { + snprintf(msg, + LOG_MSG_SZ, + "[%s]: Error: FFA_RXTX_MAP features = 0x%lx", + __func__, + prvdata->pair.rxtx_min_pages); + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + + return CMD_RET_SUCCESS; +} + +static int check_rxbuf_mapped_flag(u32 queried_func_id, + u8 rxbuf_mapped, + struct unit_test_state *uts) +{ + char msg[LOG_MSG_SZ] = {0}; + + switch (queried_func_id) { + case FFA_RXTX_MAP: + { + if (rxbuf_mapped) + return CMD_RET_SUCCESS; + break; + } + case FFA_RXTX_UNMAP: + { + if (!rxbuf_mapped) + return CMD_RET_SUCCESS; + break; + } + default: + return CMD_RET_FAILURE; + } + + snprintf(msg, LOG_MSG_SZ, "[%s]: Error: %s mapping issue", __func__, + (queried_func_id == FFA_RXTX_MAP ? "FFA_RXTX_MAP" : "FFA_RXTX_UNMAP")); + dm_test_ffa_log(uts, msg); + + return CMD_RET_FAILURE; +} + +static int check_rxbuf_release_flag(u8 rxbuf_owned, struct unit_test_state *uts) +{ + if (rxbuf_owned) { + char msg[LOG_MSG_SZ] = {0}; + + snprintf(msg, LOG_MSG_SZ, "[%s]: Error: RX buffer not released", __func__); + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + return CMD_RET_SUCCESS; +} + +static int test_ffa_msg_send_direct_req(u16 part_id, struct unit_test_state *uts) +{ + struct ffa_send_direct_data msg = {0}; + u8 cnt; + + ut_assertok(ffa_bus_ops_get()->sync_send_receive(part_id, &msg, 1)); + + for (cnt = 0; cnt < sizeof(struct ffa_send_direct_data) / sizeof(u64); cnt++) + ut_assertok(((u64 *)&msg)[cnt] != 0xffffffffffffffff); + + return CMD_RET_SUCCESS; +} + +static int test_partitions_and_comms(const char *service_uuid, + struct sandbox_ffa_prvdata *sdx_prvdata, + struct unit_test_state *uts) +{ + u32 count = 0, size = 0; + struct ffa_partition_info *parts_info; + u32 info_idx, exp_info_idx; + int ret; + + /* + * get from the driver the count of the SPs matching the UUID + */ + ret = ffa_bus_ops_get()->partition_info_get(service_uuid, &count, NULL); + /* make sure partitions are detected */ + ut_assertok(ret != 0); + ut_assertok(count != SANDBOX_SP_COUNT_PER_VALID_SERVICE); + + /* + * pre-allocate a buffer to be filled by the driver + * with ffa_partition_info structs + */ + + parts_info = calloc(count, sizeof(struct ffa_partition_info)); + ut_assertok(!parts_info); + + size = count * sizeof(struct ffa_partition_info); + + /* + * ask the driver to fill the buffer with the SPs info + */ + ret = ffa_bus_ops_get()->partition_info_get(service_uuid, &size, parts_info); + if (ret != 0) { + free(parts_info); + ut_assertok(ret != 0); + } + + /* + * SPs found , verify the partitions information + */ + + ret = CMD_RET_FAILURE; + + for (info_idx = 0; info_idx < count ; info_idx++) { + for (exp_info_idx = 0; + exp_info_idx < sdx_prvdata->partitions.count; + exp_info_idx++) { + if (parts_info[info_idx].id == + sdx_prvdata->partitions.descs[exp_info_idx].info.id) { + ret = memcmp(&parts_info[info_idx], + &sdx_prvdata->partitions.descs[exp_info_idx] + .info, + sizeof(struct ffa_partition_info)); + if (ret) + free(parts_info); + ut_assertok(ret != 0); + /* send and receive data from the current partition */ + test_ffa_msg_send_direct_req(parts_info[info_idx].id, uts); + } + ret = CMD_RET_SUCCESS; + } + } + + free(parts_info); + + /* Verify expected partitions found in the emulated secure world*/ + ut_assertok(ret != CMD_RET_SUCCESS); + + return CMD_RET_SUCCESS; +} + +static int dm_test_ffa_ack(struct unit_test_state *uts) +{ + struct ffa_prvdata *prvdata = NULL; + struct sandbox_ffa_prvdata *sdx_prvdata = NULL; + struct ffa_sandbox_data func_data = {0}; + u8 rxbuf_flag = 0; + const char *svc1_uuid = SANDBOX_SERVICE1_UUID; + const char *svc2_uuid = SANDBOX_SERVICE2_UUID; + int ret; + + /* test probing FF-A devices */ + ut_assertok(ffa_bus_discover()); + + /* get a pointer to the FF-A core and sandbox drivers private data */ + func_data.data0 = &prvdata; + func_data.data0_size = sizeof(prvdata); + func_data.data1 = &sdx_prvdata; + func_data.data1_size = sizeof(sdx_prvdata); + + ut_assertok(sandbox_ffa_query_core_state(FFA_VERSION, &func_data)); + + /* make sure private data pointers are retrieved */ + ut_assertok(prvdata == 0); + ut_assertok(sdx_prvdata == 0); + + /* make sure dev devices created */ + ut_assertok(check_core_dev(prvdata, uts)); + ut_assertok(check_sandbox_dev(sdx_prvdata, uts)); + + /* test FFA_VERSION */ + ut_assertok(check_fwk_version(prvdata, sdx_prvdata, uts)); + + /* test FFA_ID_GET */ + ut_assertok(check_endpoint_id(prvdata, uts)); + + /* test FFA_FEATURES */ + ut_assertok(check_features(prvdata, uts)); + + /* test core RX/TX buffers */ + ut_assertok(check_rxtxbuf(prvdata, uts)); + + /* test FFA_RXTX_MAP */ + func_data.data0 = &rxbuf_flag; + func_data.data0_size = sizeof(rxbuf_flag); + + rxbuf_flag = 0; + ut_assertok(sandbox_ffa_query_core_state(FFA_RXTX_MAP, &func_data)); + ut_assertok(check_rxbuf_mapped_flag(FFA_RXTX_MAP, rxbuf_flag, uts)); + + /* FFA_PARTITION_INFO_GET / FFA_MSG_SEND_DIRECT_REQ */ + ret = test_partitions_and_comms(svc1_uuid, sdx_prvdata, uts); + ut_assertok(ret != CMD_RET_SUCCESS); + + /* test FFA_RX_RELEASE */ + rxbuf_flag = 1; + ut_assertok(sandbox_ffa_query_core_state(FFA_RX_RELEASE, &func_data)); + ut_assertok(check_rxbuf_release_flag(rxbuf_flag, uts)); + + /* FFA_PARTITION_INFO_GET / FFA_MSG_SEND_DIRECT_REQ */ + ret = test_partitions_and_comms(svc2_uuid, sdx_prvdata, uts); + ut_assertok(ret != CMD_RET_SUCCESS); + + /* test FFA_RX_RELEASE */ + rxbuf_flag = 1; + ut_assertok(sandbox_ffa_query_core_state(FFA_RX_RELEASE, &func_data)); + ut_assertok(check_rxbuf_release_flag(rxbuf_flag, uts)); + + /* test FFA_RXTX_UNMAP */ + ut_assertok(ffa_bus_ops_get()->rxtx_unmap()); + + rxbuf_flag = 1; + ut_assertok(sandbox_ffa_query_core_state(FFA_RXTX_UNMAP, &func_data)); + ut_assertok(check_rxbuf_mapped_flag(FFA_RXTX_UNMAP, rxbuf_flag, uts)); + + return CMD_RET_SUCCESS; +} + +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_prvdata *prvdata = NULL; + struct sandbox_ffa_prvdata *sdx_prvdata = NULL; + struct ffa_sandbox_data func_data = {0}; + 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 = {0}; + int ret; + u32 count = 0; + u16 part_id = 0; + + /* test probing FF-A devices */ + ut_assertok(ffa_bus_discover()); + + /* get a pointer to the FF-A core and sandbox drivers private data */ + func_data.data0 = &prvdata; + func_data.data0_size = sizeof(prvdata); + func_data.data1 = &sdx_prvdata; + func_data.data1_size = sizeof(sdx_prvdata); + + ut_assertok(sandbox_ffa_query_core_state(FFA_VERSION, &func_data)); + + /* make sure private data pointers are retrieved */ + ut_assertok(prvdata == 0); + ut_assertok(sdx_prvdata == 0); + + /* make sure dev devices created */ + ut_assertok(check_core_dev(prvdata, uts)); + ut_assertok(check_sandbox_dev(sdx_prvdata, uts)); + + /* query partitions count using invalid arguments */ + ret = ffa_bus_ops_get()->partition_info_get(unvalid_svc_uuid, NULL, NULL); + ut_assertok(ret != -EINVAL); + + /* query partitions count using an invalid UUID string */ + ret = ffa_bus_ops_get()->partition_info_get(unvalid_svc_uuid_str, &count, NULL); + ut_assertok(ret != -EINVAL); + + /* query partitions count using an invalid UUID (no matching SP) */ + count = 0; + ret = ffa_bus_ops_get()->partition_info_get(unvalid_svc_uuid, &count, NULL); + ut_assertok(count != 0); + + /* query partitions count using a valid UUID */ + count = 0; + ret = ffa_bus_ops_get()->partition_info_get(valid_svc_uuid, &count, NULL); + /* make sure partitions are detected */ + ut_assertok(ret != 0); + ut_assertok(count != SANDBOX_SP_COUNT_PER_VALID_SERVICE); + + /* send data to an invalid partition */ + ret = ffa_bus_ops_get()->sync_send_receive(part_id, &msg, 1); + ut_assertok(ret != -EINVAL); + + /* send data to a valid partition */ + part_id = prvdata->partitions.descs[0].info.id; + ret = ffa_bus_ops_get()->sync_send_receive(part_id, &msg, 1); + ut_assertok(ret != 0); + + return CMD_RET_SUCCESS; +} + +DM_TEST(dm_test_ffa_nack, UT_TESTF_SCAN_FDT | UT_TESTF_CONSOLE_REC);

Add Sandbox test for the armffa command
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v4: drop use of helper APIs
v1: introduce armffa command sandbox test
MAINTAINERS | 1 + test/cmd/Makefile | 2 ++ test/cmd/armffa.c | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+) create mode 100644 test/cmd/armffa.c
diff --git a/MAINTAINERS b/MAINTAINERS index d87f1865b5..729e11a69f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -271,6 +271,7 @@ F: doc/arch/arm64.ffa.rst F: drivers/firmware/arm-ffa/ F: include/arm_ffa.h F: include/sandbox_arm_ffa.h +F: test/cmd/armffa.c F: test/dm/ffa.c
ARM FREESCALE IMX diff --git a/test/cmd/Makefile b/test/cmd/Makefile index 6dd6e81875..4c40aefb3b 100644 --- a/test/cmd/Makefile +++ b/test/cmd/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0+ # # Copyright (c) 2013 Google, Inc +# (C) Copyright 2022 ARM Limited
ifdef CONFIG_HUSH_PARSER obj-$(CONFIG_CONSOLE_RECORD) += test_echo.o @@ -18,5 +19,6 @@ obj-$(CONFIG_CMD_PINMUX) += pinmux.o obj-$(CONFIG_CMD_PWM) += pwm.o ifdef CONFIG_SANDBOX obj-$(CONFIG_CMD_SETEXPR) += setexpr.o +obj-$(CONFIG_SANDBOX_FFA) += armffa.o endif obj-$(CONFIG_CMD_TEMPERATURE) += temperature.o diff --git a/test/cmd/armffa.c b/test/cmd/armffa.c new file mode 100644 index 0000000000..531f82066e --- /dev/null +++ b/test/cmd/armffa.c @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test for armffa command + * + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <dm.h> +#include <dm/test.h> +#include <sandbox_arm_ffa.h> +#include <string.h> +#include <test/test.h> +#include <test/ut.h> + +#define PING_CMD_SIZE 19 + +/* Basic test of 'armffa' command */ +static int dm_test_armffa_cmd(struct unit_test_state *uts) +{ + char ping_cmd[PING_CMD_SIZE] = {0}; + + ut_assertok(ffa_bus_discover()); + + /* armffa getpart <UUID> */ + ut_assertok(run_command("armffa getpart " SANDBOX_SERVICE1_UUID, 0)); + + snprintf(ping_cmd, PING_CMD_SIZE, "armffa ping 0x%x", SANDBOX_SP1_ID); + + /* armffa ping <ID> */ + ut_assertok(run_command(ping_cmd, 0)); + + /* armffa devlist */ + ut_assertok(run_command("armffa devlist", 0)); + + return CMD_RET_SUCCESS; +} + +DM_TEST(dm_test_armffa_cmd, UT_TESTF_SCAN_FDT | UT_TESTF_CONSOLE_REC);

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.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Signed-off-by: Gowtham Suresh Kumar gowtham.sureshkumar@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v7:
* set the MM door bell event to use 64-bit direct messaging * issue a compile time error when one of these macros are not found : FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_OFFSET, FFA_SHARED_MM_BUFFER_ADDR * make mm_sp_svc_uuid static * replace EINVAL with ENOMEM in ffa_discover_mm_sp_id() when calloc() fails * improve use of unmap_sysmem() in ffa_mm_communicate()
v6:
* add FF-A runtime discovery at MM communication level * drop EFI runtime support for FF-A MM communication * revert the changes in include/mm_communication.h for efi_mm_communicate_header and smm_variable_access structures
v4:
* use the new FF-A driver interfaces * discover MM partitions at runtime * copy FF-A driver private data to EFI runtime section at ExitBootServices() * drop use of FFA_ERR_STAT_SUCCESS error code * replace EFI_BUFFER_TOO_SMALL with EFI_OUT_OF_RESOURCES in ffa_mm_communicate(). No need for efi_memcpy_runtime() anymore * revert the error log in mm_communicate() in case of failure * remove packed attribute from efi_mm_communicate_header and smm_variable_communicate_header
v2:
* set default values to 0 for FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_ADDR and MM_SP_UUID_DATA and add warnings
v1:
* introduce FF-A MM communication
include/mm_communication.h | 5 + lib/efi_loader/Kconfig | 14 +- lib/efi_loader/efi_variable_tee.c | 269 +++++++++++++++++++++++++++++- 3 files changed, 282 insertions(+), 6 deletions(-)
diff --git a/include/mm_communication.h b/include/mm_communication.h index e65fbde60d..d409bed777 100644 --- a/include/mm_communication.h +++ b/include/mm_communication.h @@ -6,6 +6,8 @@ * Copyright (c) 2017, Intel Corporation. All rights reserved. * Copyright (C) 2020 Linaro Ltd. sughosh.ganu@linaro.org * Copyright (C) 2020 Linaro Ltd. ilias.apalodimas@linaro.org + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#ifndef _MM_COMMUNICATION_H_ @@ -13,6 +15,9 @@
#include <part_efi.h>
+/* MM service UUID string (big-endian format). This UUID is common across all MM SPs */ +#define MM_SP_UUID "33d532ed-e699-0942-c09c-a798d9cd722d" + /* * Interface to the pseudo Trusted Application (TA), which provides a * communication channel with the Standalone MM (Management Mode) diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index 41756ea539..f0d843d5ca 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -60,13 +60,23 @@ 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" - depends on OPTEE + bool "UEFI variables storage service via the trusted world" + depends on OPTEE && ARM_FFA_TRANSPORT 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 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 dfef18435d..ec5ea4fd63 100644 --- a/lib/efi_loader/efi_variable_tee.c +++ b/lib/efi_loader/efi_variable_tee.c @@ -4,6 +4,8 @@ * * Copyright (C) 2019 Linaro Ltd. sughosh.ganu@linaro.org * Copyright (C) 2019 Linaro Ltd. ilias.apalodimas@linaro.org + * Copyright (C) 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> @@ -15,6 +17,36 @@ #include <malloc.h> #include <mm_communication.h>
+#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) + +#include <arm_ffa.h> +#include <cpu_func.h> +#include <mapmem.h> + +#ifndef FFA_SHARED_MM_BUFFER_SIZE +#error "FFA_SHARED_MM_BUFFER_SIZE must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_SIZE 0 +#endif + +#ifndef FFA_SHARED_MM_BUFFER_OFFSET +#error "FFA_SHARED_MM_BUFFER_OFFSET must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_OFFSET 0 +#endif + +#ifndef FFA_SHARED_MM_BUFFER_ADDR +#error "FFA_SHARED_MM_BUFFER_ADDR must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_ADDR 0 +#endif + +/* MM return codes */ +#define MM_SUCCESS (0) + +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 */ static efi_uintn_t max_payload_size; /* func + data */ @@ -24,6 +56,7 @@ struct mm_connection { u32 session; };
+#if (IS_ENABLED(CONFIG_OPTEE)) /** * get_connection() - Retrieve OP-TEE session for a specific UUID. * @@ -143,13 +176,229 @@ static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize)
return ret; } +#endif + +#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) + +/** + * ffa_notify_mm_sp() - Announce there is data in the shared buffer + * + * Notifies 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 = -1; + + if (!ffa_bus_ops_get()) + return -EINVAL; + + msg.data0 = FFA_SHARED_MM_BUFFER_OFFSET; /* x3 */ + + ret = ffa_bus_ops_get()->sync_send_receive(mm_sp_id, &msg, 1); + if (ret != 0) + return ret; + + sp_event_ret = msg.data0; /* x3 */ + + if (sp_event_ret == MM_SUCCESS) + return 0; + + /* + * Failure to notify the MM SP + */ + + return -EACCES; +} + +/** + * 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, size = 0; + int ret; + struct ffa_partition_info *parts_info; + + if (!ffa_bus_ops_get()) + return -EINVAL; + + /* + * get from the driver the count of the SPs matching the UUID + */ + ret = ffa_bus_ops_get()->partition_info_get(mm_sp_svc_uuid, &count, NULL); + if (ret != 0) { + log_err("EFI: Failure in querying partitions count (error code: %d)\n", ret); + return ret; + } + + if (!count) { + log_info("EFI: No MM partition found\n"); + return ret; + } + + /* + * pre-allocate a buffer to be filled by the driver + * with ffa_partition_info structs + */ + + log_info("EFI: Pre-allocating %d partition(s) info structures\n", count); + + parts_info = calloc(count, sizeof(struct ffa_partition_info)); + if (!parts_info) + return -ENOMEM; + + size = count * sizeof(struct ffa_partition_info); + + /* + * ask the driver to fill the + * buffer with the SPs info + */ + ret = ffa_bus_ops_get()->partition_info_get(mm_sp_svc_uuid, &size, parts_info); + if (ret != 0) { + log_err("EFI: Failure in querying partition(s) info (error code: %d)\n", ret); + free(parts_info); + return ret; + } + + /* + * MM SPs found , use the first one + */ + + mm_sp_id = parts_info[0].id; + + log_info("EFI: MM partition ID 0x%x\n", mm_sp_id); + + free(parts_info); + + return 0; +}
/** - * mm_communicate() - Adjust the cmonnucation buffer to StandAlonneMM and send + * 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 + * + * Issues 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() != 0) { + log_err("EFI: Failure to discover MM partition ID at boot time\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 > FFA_SHARED_MM_BUFFER_SIZE) + return EFI_INVALID_PARAMETER; + + /* Copy the data to the shared buffer */ + + virt_shared_buf = (void *)map_sysmem((phys_addr_t)FFA_SHARED_MM_BUFFER_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; +} +#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) @@ -162,7 +411,12 @@ static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) 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); + ret = ffa_bus_discover(); + if (ret != 0) + ret = optee_mm_communicate(comm_buf, dsize); + else + ret = ffa_mm_communicate(comm_buf, dsize); + if (ret != EFI_SUCCESS) { log_err("%s failed!\n", __func__); return ret; @@ -258,6 +512,13 @@ efi_status_t EFIAPI get_max_payload(efi_uintn_t *size) goto out; } *size = var_payload->size; + + #if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) + if (*size > FFA_SHARED_MM_BUFFER_SIZE) + *size = FFA_SHARED_MM_BUFFER_SIZE - MM_COMMUNICATE_HEADER_SIZE - + MM_VARIABLE_COMMUNICATE_SIZE; + #endif + /* * There seems to be a bug in EDK2 miscalculating the boundaries and * size checks, so deduct 2 more bytes to fulfill this requirement. Fix @@ -697,7 +958,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);
/*

Hi Abdellatif
On Mon, Nov 07, 2022 at 07:20:54PM +0000, Abdellatif El Khlifi wrote:
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.
config EFI_MM_COMM_TEE
- bool "UEFI variables storage service via OP-TEE"
- depends on OPTEE
- bool "UEFI variables storage service via the trusted world"
- depends on OPTEE && ARM_FFA_TRANSPORT
This shouldn't rely on both. It's either OP-TEE or FF-A
+#if (IS_ENABLED(CONFIG_OPTEE))
This separation is a bit problematic. A user can configure bot OP-TEE and FF-A. Those are not mutually exclusive, but for the EFI variables case they are. We need a better way to isolate the compilation choices. Why don't we make ffa_bus_discover() return -1 if FF-A isn't compiled in?
/**
- get_connection() - Retrieve OP-TEE session for a specific UUID.
@@ -143,13 +176,229 @@ static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize)
return ret; } +#endif
+#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
+/**
- ffa_notify_mm_sp() - Announce there is data in the shared buffer
- Notifies 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 = -1;
- if (!ffa_bus_ops_get())
return -EINVAL;
- msg.data0 = FFA_SHARED_MM_BUFFER_OFFSET; /* x3 */
- ret = ffa_bus_ops_get()->sync_send_receive(mm_sp_id, &msg, 1);
- if (ret != 0)
return ret;
- sp_event_ret = msg.data0; /* x3 */
- if (sp_event_ret == MM_SUCCESS)
return 0;
- /*
* Failure to notify the MM SP
*/
- return -EACCES;
+}
+/**
- 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, size = 0;
- int ret;
- struct ffa_partition_info *parts_info;
- if (!ffa_bus_ops_get())
return -EINVAL;
- /*
* get from the driver the count of the SPs matching the UUID
*/
- ret = ffa_bus_ops_get()->partition_info_get(mm_sp_svc_uuid, &count, NULL);
- if (ret != 0) {
log_err("EFI: Failure in querying partitions count (error code: %d)\n", ret);
return ret;
- }
- if (!count) {
log_info("EFI: No MM partition found\n");
return ret;
- }
- /*
* pre-allocate a buffer to be filled by the driver
* with ffa_partition_info structs
*/
- log_info("EFI: Pre-allocating %d partition(s) info structures\n", count);
- parts_info = calloc(count, sizeof(struct ffa_partition_info));
I prefer sizeof(*parts_info). Same goes for all sizeof() calls.
- if (!parts_info)
return -ENOMEM;
- size = count * sizeof(struct ffa_partition_info);
- /*
* ask the driver to fill the
* buffer with the SPs info
*/
- ret = ffa_bus_ops_get()->partition_info_get(mm_sp_svc_uuid, &size, parts_info);
- if (ret != 0) {
if (!ret)
[...]
Thanks /Ilias

On Tue, Nov 15, 2022 at 11:03:51AM +0200, Ilias Apalodimas wrote:
Hi Abdellatif
On Mon, Nov 07, 2022 at 07:20:54PM +0000, Abdellatif El Khlifi wrote:
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.
config EFI_MM_COMM_TEE
- bool "UEFI variables storage service via OP-TEE"
- depends on OPTEE
- bool "UEFI variables storage service via the trusted world"
- depends on OPTEE && ARM_FFA_TRANSPORT
This shouldn't rely on both. It's either OP-TEE or FF-A
Thanks. Addressed in v8 patchset.
+#if (IS_ENABLED(CONFIG_OPTEE))
This separation is a bit problematic. A user can configure bot OP-TEE and FF-A. Those are not mutually exclusive, but for the EFI variables case they are. We need a better way to isolate the compilation choices. Why don't we make ffa_bus_discover() return -1 if FF-A isn't compiled in?
I agree, this is addressed in v8 patchset.
/**
- get_connection() - Retrieve OP-TEE session for a specific UUID.
@@ -143,13 +176,229 @@ static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize)
return ret; } +#endif
+#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
+/**
- ffa_notify_mm_sp() - Announce there is data in the shared buffer
- Notifies 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 = -1;
- if (!ffa_bus_ops_get())
return -EINVAL;
- msg.data0 = FFA_SHARED_MM_BUFFER_OFFSET; /* x3 */
- ret = ffa_bus_ops_get()->sync_send_receive(mm_sp_id, &msg, 1);
- if (ret != 0)
return ret;
- sp_event_ret = msg.data0; /* x3 */
- if (sp_event_ret == MM_SUCCESS)
return 0;
- /*
* Failure to notify the MM SP
*/
- return -EACCES;
+}
+/**
- 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, size = 0;
- int ret;
- struct ffa_partition_info *parts_info;
- if (!ffa_bus_ops_get())
return -EINVAL;
- /*
* get from the driver the count of the SPs matching the UUID
*/
- ret = ffa_bus_ops_get()->partition_info_get(mm_sp_svc_uuid, &count, NULL);
- if (ret != 0) {
log_err("EFI: Failure in querying partitions count (error code: %d)\n", ret);
return ret;
- }
- if (!count) {
log_info("EFI: No MM partition found\n");
return ret;
- }
- /*
* pre-allocate a buffer to be filled by the driver
* with ffa_partition_info structs
*/
- log_info("EFI: Pre-allocating %d partition(s) info structures\n", count);
- parts_info = calloc(count, sizeof(struct ffa_partition_info));
I prefer sizeof(*parts_info). Same goes for all sizeof() calls.
Addressed in v8 patchset.
- if (!parts_info)
return -ENOMEM;
- size = count * sizeof(struct ffa_partition_info);
- /*
* ask the driver to fill the
* buffer with the SPs info
*/
- ret = ffa_bus_ops_get()->partition_info_get(mm_sp_svc_uuid, &size, parts_info);
- if (ret != 0) {
if (!ret)
Addressed in v8 patchset.
[...]
Thanks /Ilias

turn on EFI MM communication
On corstone1000 platform MM communication between u-boot and the secure world (Optee) is done using the FF-A bus.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v7:
* improve the definition of FFA_SHARED_MM_BUFFER_ADDR and FFA_SHARED_MM_BUFFER_OFFSET * update FFA_SHARED_MM_BUFFER_ADDR value
v6:
* corstone-1000: enable optee driver * corstone-1000: remove CONFIG_ARM_FFA_EFI_RUNTIME_MODE from the defconfig
v4:
* corstone-1000: turn on EFI MM communication
configs/corstone1000_defconfig | 4 ++++ include/configs/corstone1000.h | 9 +++++++++ 2 files changed, 13 insertions(+)
diff --git a/configs/corstone1000_defconfig b/configs/corstone1000_defconfig index dddfa27507..d827dd72f1 100644 --- a/configs/corstone1000_defconfig +++ b/configs/corstone1000_defconfig @@ -52,3 +52,7 @@ CONFIG_DM_SERIAL=y CONFIG_USB=y CONFIG_USB_ISP1760=y CONFIG_ERRNO_STR=y +CONFIG_EFI_MM_COMM_TEE=y +CONFIG_TEE=y +CONFIG_OPTEE=y +CONFIG_ARM_FFA_TRANSPORT=y diff --git a/include/configs/corstone1000.h b/include/configs/corstone1000.h index 8e0230c135..0362d29ac2 100644 --- a/include/configs/corstone1000.h +++ b/include/configs/corstone1000.h @@ -14,6 +14,15 @@
#include <linux/sizes.h>
+#define FFA_SHARED_MM_BUFFER_SIZE SZ_4K /* 4 KB */ + +/* + * shared buffer physical address used for communication between + * u-boot and the MM SP + */ +#define FFA_SHARED_MM_BUFFER_ADDR 0x02000000UL +#define FFA_SHARED_MM_BUFFER_OFFSET 0 + #define V2M_BASE 0x80000000
#define CONFIG_PL011_CLOCK 50000000

On Mon, Nov 07, 2022 at 07:20:45PM +0000, Abdellatif El Khlifi wrote:
Adding support for Arm FF-A v1.0 (Arm Firmware Framework for Armv8-A) [7].
FF-A describes interfaces (ABIs) that standardize communication between the Secure World and Normal World. These interfaces enable a pair of software sandboxes to communicate with each other. A sandbox aka partition could be a VM in the Normal or Secure world, an application in S-EL0, or a Trusted OS in S-EL1.
The FF-A transport is implemented as a data bus and a core driver is provided.
This implementation of the specification provides support for Aarch64.
The FF-A driver uses the SMC ABIs defined by the FF-A specification to:
- Discover the presence of secure partitions (SPs) of interest - Access an SP's service through communication protocols e.g. EFI MM communication protocol
The FF-A support provides the following features:
- Being generic by design and can be used by any Arm 64-bit platform - The FF-A core driver - The FF-A bus is discoverable on demand at runtime - The driver provides driver operations to be used by clients to access the FF-A bus - FF-A driver can be compiled and used without EFI - Support for SMCCCv1.2 x0-x17 registers - Support for SMC32 calling convention - Support for 32-bit and 64-bit FF-A direct messaging - A new command called armffa is provided as an example of how to access the FF-A bus - An FF-A Sandbox driver is provided with test cases - Support for FF-A MM communication (compatible with EFI boot time) - Enabling FF-A and MM communication in Corstone1000 platform
For more details about the FF-A core driver please refer to [8].
Please find at [9] an example of the expected boot logs when enabling FF-A support for a platform. In this example the platform is Corstone1000. But it can be any Arm 64-bit platform.
Changelog of the major changes:
v7:
- add support for 32-bit direct messaging (now we have 32-bit and 64-bit support)
- set the MM door bell event to use 64-bit direct messaging
- issue a compile time error when one of these macros are not found : FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_OFFSET, FFA_SHARED_MM_BUFFER_ADDR
- make uuid_str_to_le_bin() implementation similar to uuid_str_to_bin() by using same APIs
Hi Ilias, Jens,
After addressing your current comments for v7 patchset, which patches are you happy with so I could add the Reviewed-by: to the commits logs ?
Cheers
v6: [6]
- remove clearing x0-x17 registers after SMC calls
- drop use of EFI runtime support for FF-A (We decided with Linaro to add this later)
- drop discovery from initcalls (discovery will be on demand by FF-A users)
- add FF-A runtime discovery at MM communication level
- update the documentation and move it to doc/arch/arm64.ffa.rst
v5: [5]
- move changelogs in each commit to the changes section
v4: [4]
- add FF-A support README (doc/README.ffa.drv)
- improving error handling by mapping the FF-A errors to standard errors and logs
- replacing panics with an error log
- align sandbox driver and tests with the new FF-A driver interfaces
and new way of error handling
- use the new FF-A driver interfaces for MM communication
- discover MM partitions at runtime
- copy FF-A driver private data to EFI runtime section at ExitBootServices()
- moving the FF-A driver work to drivers/firmware/arm-ffa
- improving features discovery in FFA_FEATURES
- add remove/unbind functions to the FF-A core device
- improve how the driver behaves when bus discovery is done more than once
- move clearing x0-x17 registers code into a new macro like done in the linux kernel
- enable EFI MM communication for the Corstone1000 platform
v3: [3]
- port x0-x17 registers support from linux kernel as defined by SMCCCv1.2
- align the interfaces of the u-boot FF-A driver with those in the linux FF-A driver
- remove the FF-A helper layer
- make the u-boot FF-A driver independent from EFI
- provide an optional config that enables copying the driver data to EFI runtime section at ExitBootServices service
- use 64-bit version of FFA_RXTX_MAP, FFA_MSG_SEND_DIRECT_{REQ, RESP}
- update armffa command with the new driver interfaces
v2 [2]:
- make FF-A bus discoverable using device_{bind, probe} APIs
- remove device tree support
v1 [1]:
- introduce FF-A bus driver with device tree support
- introduce armffa command
- introduce FF-A Sandbox driver
- add FF-A Sandbox test cases
- introduce FF-A MM communication
Cheers, Abdellatif
[9]: example of boot logs when enabling FF-A
U-Boot 2022.07 (Jul 11 2022 - 13:42:58 +0000) corstone1000 aarch64 ... [FFA] Conduit is SMC [FFA] FF-A driver 1.0 FF-A framework 1.0 [FFA] Versions are compatible [FFA] endpoint ID is 0 [FFA] Using 1 4KB page(s) for RX/TX buffers size [FFA] RX buffer at virtual address 0xfdf18000 [FFA] TX buffer at virtual address 0xfdf1a000 [FFA] RX/TX buffers mapped [FFA] Reading partitions data from the RX buffer [FFA] Partition ID 8001 : info cached [FFA] Partition ID 8002 : info cached [FFA] Partition ID 8003 : info cached [FFA] 3 partition(s) found and cached [FFA] Preparing for checking partitions count [FFA] Searching partitions using the provided UUID [FFA] No partition found. Querying framework ... [FFA] Reading partitions data from the RX buffer [FFA] Number of partition(s) found matching the UUID: 1 EFI: Pre-allocating 1 partition(s) info structures [FFA] Preparing for filling partitions info [FFA] Searching partitions using the provided UUID [FFA] Partition ID 8003 matches the provided UUID EFI: MM partition ID 0x8003 ... Booting /MemoryMapped(0x0,0x88200000,0xf00000) EFI stub: Booting Linux Kernel... EFI stub: Using DTB from configuration table EFI stub: Exiting boot services... [FFA] Freeing RX/TX buffers Booting Linux on physical CPU 0x0000000000 [0x410fd040] Linux version 5.19.9-yocto-standard (oe-user@oe-host) (aarch64-poky-linux-musl-gcc (GCC) 12.2.0, GNU ld (GNU Binutils) 2.39.0.20220819) #1 SMP PREEMPT Wed Sep 21 20:11:18 UTC 2022
Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Achin Gupta achin.gupta@arm.com Cc: Drew Reed Drew.Reed@arm.com Cc: Vishnu Banavath vishnu.banavath@arm.com Cc: Xueliang Zhong Xueliang.Zhong@arm.com
Abdellatif El Khlifi (10): arm64: smccc: add support for SMCCCv1.2 x0-x17 registers lib: uuid: introduce uuid_str_to_le_bin function arm_ffa: introduce Arm FF-A low-level driver arm_ffa: efi: unmap RX/TX buffers arm_ffa: introduce armffa command arm_ffa: introduce the FF-A Sandbox driver arm_ffa: introduce Sandbox test cases for UCLASS_FFA arm_ffa: introduce armffa command Sandbox test arm_ffa: efi: introduce FF-A MM communication arm_ffa: efi: corstone1000: enable MM communication
MAINTAINERS | 11 + arch/arm/cpu/armv8/smccc-call.S | 53 + arch/arm/lib/asm-offsets.c | 14 + cmd/Kconfig | 10 + cmd/Makefile | 2 + cmd/armffa.c | 239 +++ configs/corstone1000_defconfig | 4 + configs/sandbox64_defconfig | 2 + configs/sandbox_defconfig | 2 + doc/arch/arm64.ffa.rst | 218 +++ doc/arch/index.rst | 1 + doc/arch/sandbox.rst | 1 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/firmware/arm-ffa/Kconfig | 36 + drivers/firmware/arm-ffa/Makefile | 7 + drivers/firmware/arm-ffa/arm-ffa-uclass.c | 16 + drivers/firmware/arm-ffa/arm_ffa_prv.h | 210 +++ drivers/firmware/arm-ffa/core.c | 1324 +++++++++++++++++ drivers/firmware/arm-ffa/sandbox.c | 659 ++++++++ .../firmware/arm-ffa/sandbox_arm_ffa_prv.h | 144 ++ include/arm_ffa.h | 93 ++ include/configs/corstone1000.h | 9 + include/dm/uclass-id.h | 4 + include/linux/arm-smccc.h | 43 + include/mm_communication.h | 5 + include/sandbox_arm_ffa.h | 91 ++ include/uuid.h | 8 + lib/efi_loader/Kconfig | 14 +- lib/efi_loader/efi_boottime.c | 13 + lib/efi_loader/efi_variable_tee.c | 269 +++- lib/uuid.c | 46 + test/cmd/Makefile | 2 + test/cmd/armffa.c | 40 + test/dm/Makefile | 2 + test/dm/ffa.c | 394 +++++ 36 files changed, 3983 insertions(+), 6 deletions(-) create mode 100644 cmd/armffa.c 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_prv.h create mode 100644 drivers/firmware/arm-ffa/core.c create mode 100644 drivers/firmware/arm-ffa/sandbox.c create mode 100644 drivers/firmware/arm-ffa/sandbox_arm_ffa_prv.h create mode 100644 include/arm_ffa.h create mode 100644 include/sandbox_arm_ffa.h create mode 100644 test/cmd/armffa.c create mode 100644 test/dm/ffa.c
-- 2.17.1

Adding support for Arm FF-A v1.0 (Arm Firmware Framework for Armv8-A) [A].
FF-A describes interfaces (ABIs) that standardize communication between the Secure World and Normal World. These interfaces enable a pair of software sandboxes to communicate with each other. A sandbox aka partition could be a VM in the Normal or Secure world, an application in S-EL0, or a Trusted OS in S-EL1.
The FF-A transport is implemented as a data bus and a core driver is provided.
This implementation of the specification provides support for Aarch64.
The FF-A driver uses the SMC ABIs defined by the FF-A specification to:
- Discover the presence of secure partitions (SPs) of interest - Access an SP's service through communication protocols e.g. EFI MM communication protocol
The FF-A support provides the following features:
- Being generic by design and can be used by any Arm 64-bit platform - The FF-A core driver - The FF-A bus is discoverable on demand at runtime - The driver provides driver operations to be used by clients to access the FF-A bus - FF-A driver can be compiled and used without EFI - Support for SMCCCv1.2 x0-x17 registers - Support for SMC32 calling convention - Support for 32-bit and 64-bit FF-A direct messaging - A new command called armffa is provided as an example of how to access the FF-A bus - An FF-A Sandbox driver is provided with test cases - Support for FF-A MM communication (compatible with EFI boot time) - Enabling FF-A and MM communication in Corstone1000 platform as a use case
For more details about the FF-A core driver please refer to [B].
Please find at [C] an example of the expected boot logs when enabling FF-A support for a platform. In this example the platform is Corstone1000. But it can be any Arm 64-bit platform.
Changelog of the major changes: ===========================
v8:
* pass the FF-A bus device to the bus operations * isolate the compilation choices between FF-A and OP-TEE * drop OP-TEE configs from Corstone-1000 defconfig * make ffa_get_partitions_info() second argument to be an SP count in both modes
v7: [7]
* add support for 32-bit direct messaging (now we have 32-bit and 64-bit support) * set the MM door bell event to use 64-bit direct messaging * issue a compile time error when one of these macros are not found : FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_OFFSET, FFA_SHARED_MM_BUFFER_ADDR * make uuid_str_to_le_bin() implementation similar to uuid_str_to_bin() by using same APIs
v6: [6]
* remove clearing x0-x17 registers after SMC calls * drop use of EFI runtime support for FF-A (We decided with Linaro to add this later) * drop discovery from initcalls (discovery will be on demand by FF-A users) * add FF-A runtime discovery at MM communication level * update the documentation and move it to doc/arch/arm64.ffa.rst
v5: [5]
* move changelogs in each commit to the changes section
v4: [4]
* add FF-A support README (doc/README.ffa.drv) * improving error handling by mapping the FF-A errors to standard errors and logs * replacing panics with an error log * align sandbox driver and tests with the new FF-A driver interfaces and new way of error handling * use the new FF-A driver interfaces for MM communication * discover MM partitions at runtime * copy FF-A driver private data to EFI runtime section at ExitBootServices() * moving the FF-A driver work to drivers/firmware/arm-ffa * improving features discovery in FFA_FEATURES * add remove/unbind functions to the FF-A core device * improve how the driver behaves when bus discovery is done more than once * move clearing x0-x17 registers code into a new macro like done in the linux kernel * enable EFI MM communication for the Corstone1000 platform
v3: [3]
* port x0-x17 registers support from linux kernel as defined by SMCCCv1.2 * align the interfaces of the u-boot FF-A driver with those in the linux FF-A driver * remove the FF-A helper layer * make the u-boot FF-A driver independent from EFI * provide an optional config that enables copying the driver data to EFI runtime section at ExitBootServices service * use 64-bit version of FFA_RXTX_MAP, FFA_MSG_SEND_DIRECT_{REQ, RESP} * update armffa command with the new driver interfaces
v2 [2]:
* make FF-A bus discoverable using device_{bind, probe} APIs * remove device tree support
v1 [1]:
* introduce FF-A bus driver with device tree support * introduce armffa command * introduce FF-A Sandbox driver * add FF-A Sandbox test cases * introduce FF-A MM communication
Cheers, Abdellatif
List of previous patches:
[1]: https://lore.kernel.org/all/20220329151659.16894-1-abdellatif.elkhlifi@arm.c... [2]: https://lore.kernel.org/all/20220415122803.16666-1-abdellatif.elkhlifi@arm.c... [3]: https://lore.kernel.org/all/20220801172053.20163-1-abdellatif.elkhlifi@arm.c... [4]: https://lore.kernel.org/all/20220926101723.9965-1-abdellatif.elkhlifi@arm.co... [5]: https://lore.kernel.org/all/20220926140827.15125-1-abdellatif.elkhlifi@arm.c... [6]: https://lore.kernel.org/all/20221013103857.614-1-abdellatif.elkhlifi@arm.com... [7]: https://lore.kernel.org/all/20221107192055.21669-1-abdellatif.elkhlifi@arm.c...
More details:
[A]: https://developer.arm.com/documentation/den0077/latest/ [B]: doc/arch/arm64.ffa.rst [C]: example of boot logs when enabling FF-A
``` U-Boot 2022.07 (Jul 11 2022 - 13:42:58 +0000) corstone1000 aarch64 ... [FFA] Conduit is SMC [FFA] FF-A driver 1.0 FF-A framework 1.0 [FFA] Versions are compatible [FFA] endpoint ID is 0 [FFA] Using 1 4KB page(s) for RX/TX buffers size [FFA] RX buffer at virtual address 0xfdf18000 [FFA] TX buffer at virtual address 0xfdf1a000 [FFA] RX/TX buffers mapped [FFA] Reading partitions data from the RX buffer [FFA] Partition ID 8001 : info cached [FFA] Partition ID 8002 : info cached [FFA] Partition ID 8003 : info cached [FFA] 3 partition(s) found and cached [FFA] Preparing for checking partitions count [FFA] Searching partitions using the provided UUID [FFA] No partition found. Querying framework ... [FFA] Reading partitions data from the RX buffer [FFA] Number of partition(s) found matching the UUID: 1 EFI: Pre-allocating 1 partition(s) info structures [FFA] Preparing for filling partitions info [FFA] Searching partitions using the provided UUID [FFA] Partition ID 8003 matches the provided UUID EFI: MM partition ID 0x8003 ... Booting /MemoryMapped(0x0,0x88200000,0xf00000) EFI stub: Booting Linux Kernel... EFI stub: Using DTB from configuration table EFI stub: Exiting boot services... [FFA] Freeing RX/TX buffers Booting Linux on physical CPU 0x0000000000 [0x410fd040] Linux version 5.19.9-yocto-standard (oe-user@oe-host) (aarch64-poky-linux-musl-gcc (GCC) 12.2.0, GNU ld (GNU Binutils) 2.39.0.20220819) #1 SMP PREEMPT Wed Sep 21 20:11:18 UTC 2022 ```
Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Achin Gupta achin.gupta@arm.com Cc: Drew Reed Drew.Reed@arm.com Cc: Vishnu Banavath vishnu.banavath@arm.com Cc: Xueliang Zhong Xueliang.Zhong@arm.com
Abdellatif El Khlifi (10): arm64: smccc: add support for SMCCCv1.2 x0-x17 registers lib: uuid: introduce uuid_str_to_le_bin function arm_ffa: introduce Arm FF-A low-level driver arm_ffa: efi: unmap RX/TX buffers arm_ffa: introduce armffa command arm_ffa: introduce the FF-A Sandbox driver arm_ffa: introduce Sandbox test cases for UCLASS_FFA arm_ffa: introduce armffa command Sandbox test arm_ffa: efi: introduce FF-A MM communication arm_ffa: efi: corstone1000: enable MM communication
MAINTAINERS | 11 + arch/arm/cpu/armv8/smccc-call.S | 53 + arch/arm/lib/asm-offsets.c | 14 + cmd/Kconfig | 10 + cmd/Makefile | 2 + cmd/armffa.c | 237 +++ configs/corstone1000_defconfig | 2 + configs/sandbox64_defconfig | 2 + configs/sandbox_defconfig | 2 + doc/arch/arm64.ffa.rst | 218 +++ doc/arch/index.rst | 1 + doc/arch/sandbox/sandbox.rst | 1 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/firmware/arm-ffa/Kconfig | 36 + drivers/firmware/arm-ffa/Makefile | 7 + drivers/firmware/arm-ffa/arm-ffa-uclass.c | 16 + drivers/firmware/arm-ffa/arm_ffa_prv.h | 205 +++ drivers/firmware/arm-ffa/core.c | 1329 +++++++++++++++++ drivers/firmware/arm-ffa/sandbox.c | 659 ++++++++ .../firmware/arm-ffa/sandbox_arm_ffa_prv.h | 144 ++ include/arm_ffa.h | 97 ++ include/configs/corstone1000.h | 9 + include/dm/uclass-id.h | 4 + include/linux/arm-smccc.h | 43 + include/mm_communication.h | 5 + include/sandbox_arm_ffa.h | 91 ++ include/uuid.h | 8 + lib/efi_loader/Kconfig | 14 +- lib/efi_loader/efi_boottime.c | 13 + lib/efi_loader/efi_variable_tee.c | 294 +++- lib/uuid.c | 46 + test/cmd/Makefile | 2 + test/cmd/armffa.c | 39 + test/dm/Makefile | 2 + test/dm/ffa.c | 392 +++++ 36 files changed, 4005 insertions(+), 6 deletions(-) create mode 100644 cmd/armffa.c 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_prv.h create mode 100644 drivers/firmware/arm-ffa/core.c create mode 100644 drivers/firmware/arm-ffa/sandbox.c create mode 100644 drivers/firmware/arm-ffa/sandbox_arm_ffa_prv.h create mode 100644 include/arm_ffa.h create mode 100644 include/sandbox_arm_ffa.h create mode 100644 test/cmd/armffa.c create mode 100644 test/dm/ffa.c

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 abdellatif.elkhlifi@arm.com Reviewed-by: Jens Wiklander jens.wiklander@linaro.org Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org
---
Changelog: ===============
v7:
* improve indentation of ARM_SMCCC_1_2_REGS_Xn_OFFS
v4:
* rename the commit title and improve description new commit title: the current
v3:
* port x0-x17 registers support from linux kernel as defined by SMCCCv1.2 commit title: arm64: smccc: add Xn registers support used by SMC calls
arch/arm/cpu/armv8/smccc-call.S | 53 +++++++++++++++++++++++++++++++++ arch/arm/lib/asm-offsets.c | 14 +++++++++ include/linux/arm-smccc.h | 43 ++++++++++++++++++++++++++ 3 files changed, 110 insertions(+)
diff --git a/arch/arm/cpu/armv8/smccc-call.S b/arch/arm/cpu/armv8/smccc-call.S index dc92b28777..ec6f299bc9 100644 --- a/arch/arm/cpu/armv8/smccc-call.S +++ b/arch/arm/cpu/armv8/smccc-call.S @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2015, Linaro Limited + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */ #include <linux/linkage.h> #include <linux/arm-smccc.h> @@ -45,3 +47,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 22fd541f9a..db6d7ed234 100644 --- a/arch/arm/lib/asm-offsets.c +++ b/arch/arm/lib/asm-offsets.c @@ -9,6 +9,9 @@ * generate asm statements containing #defines, * compile this file to assembler, and then extract the * #defines from the assembly-language output. + * + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> @@ -117,6 +120,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 e1d09884a1..9105031d55 100644 --- a/include/linux/arm-smccc.h +++ b/include/linux/arm-smccc.h @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* * Copyright (c) 2015, Linaro Limited + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */ #ifndef __LINUX_ARM_SMCCC_H #define __LINUX_ARM_SMCCC_H @@ -70,6 +72,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

convert UUID string to little endian binary data
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v8:
* use simple_strtoull() in uuid_str_to_le_bin() to support 32-bit platforms
v7:
* rename be_uuid_str_to_le_bin() to uuid_str_to_le_bin() * make uuid_str_to_le_bin() implementation similar to uuid_str_to_bin() by using same APIs
v4:
* rename ffa_uuid_str_to_bin to be_uuid_str_to_le_bin and put in a standalone commit (the current)
v3:
* introduce ffa_uuid_str_to_bin (provided by arm_ffa: introduce Arm FF-A low-level driver)
include/uuid.h | 8 ++++++++ lib/uuid.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+)
diff --git a/include/uuid.h b/include/uuid.h index 4a4883d3b5..293a8eb0a5 100644 --- a/include/uuid.h +++ b/include/uuid.h @@ -2,6 +2,8 @@ /* * Copyright (C) 2014 Samsung Electronics * Przemyslaw Marczak p.marczak@samsung.com + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */ #ifndef __UUID_H__ #define __UUID_H__ @@ -44,4 +46,10 @@ 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 - Converts a UUID string to little endian binary data + */ +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 465e1ac38f..d29f561a70 100644 --- a/lib/uuid.c +++ b/lib/uuid.c @@ -1,6 +1,8 @@ // SPDX-License-Identifier: GPL-2.0+ /* * Copyright 2011 Calxeda, Inc. + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> @@ -346,6 +348,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. *

Hi Abdellatif,
On Tue, 22 Nov 2022 at 06:18, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
convert UUID string to little endian binary data
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v8:
- use simple_strtoull() in uuid_str_to_le_bin() to support 32-bit platforms
v7:
- rename be_uuid_str_to_le_bin() to uuid_str_to_le_bin()
- make uuid_str_to_le_bin() implementation similar to uuid_str_to_bin() by using same APIs
v4:
- rename ffa_uuid_str_to_bin to be_uuid_str_to_le_bin and put in a standalone commit (the current)
v3:
- introduce ffa_uuid_str_to_bin (provided by arm_ffa: introduce Arm FF-A low-level driver)
include/uuid.h | 8 ++++++++ lib/uuid.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+)
Can you please add a full function comment in the header and also a test, perhaps in test/lib/uuid.c ?
REgards, Simon

Add the core driver 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.
In U-Boot FF-A design, FF-A is considered as a discoverable bus. 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:
- partition_info_get - sync_send_receive - rxtx_unmap
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 abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v8:
* make ffa_get_partitions_info() second argument to be an SP count in both modes * update ffa_bus_prvdata_get() to return a pointer rather than a pointer address * remove packing from ffa_partition_info and ffa_send_direct_data structures * pass the FF-A bus device to the bus operations
v7:
* add support for 32-bit direct messaging * rename be_uuid_str_to_le_bin() to uuid_str_to_le_bin() * improve the declaration of error handling mapping * stating in doc/arch/arm64.ffa.rst that EFI runtime is not supported
v6:
* drop use of EFI runtime support (We decided with Linaro to add this later) * drop discovery from initcalls (discovery will be on demand by FF-A users) * set the alignment of the RX/TX buffers to the larger translation granule size * move FF-A RX/TX buffers unmapping at ExitBootServices() to a separate commit * update the documentation and move it to doc/arch/arm64.ffa.rst
v4:
* add doc/README.ffa.drv * moving the FF-A driver work to drivers/firmware/arm-ffa * use less #ifdefs in lib/efi_loader/efi_boottime.c and replace #if defined by #if CONFIG_IS_ENABLED * improving error handling by mapping the FF-A errors to standard errors and logs * replacing panics with an error log and returning an error code * improving features discovery in FFA_FEATURES by introducing rxtx_min_pages private data field * add ffa_remove and ffa_unbind functions * improve how the driver behaves when bus discovery is done more than once
v3:
* align the interfaces of the U-Boot FF-A driver with those in the linux FF-A driver * remove the FF-A helper layer * make the U-Boot FF-A driver independent from EFI * provide an optional config that enables copying the driver data to EFI runtime section at ExitBootServices service * use 64-bit version of FFA_RXTX_MAP, FFA_MSG_SEND_DIRECT_{REQ, RESP}
v2:
* make FF-A bus discoverable using device_{bind, probe} APIs * remove device tree support
v1:
* introduce FF-A bus driver with device tree support
MAINTAINERS | 7 + doc/arch/arm64.ffa.rst | 218 ++++ doc/arch/index.rst | 1 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/firmware/arm-ffa/Kconfig | 30 + drivers/firmware/arm-ffa/Makefile | 6 + drivers/firmware/arm-ffa/arm-ffa-uclass.c | 16 + drivers/firmware/arm-ffa/arm_ffa_prv.h | 200 ++++ drivers/firmware/arm-ffa/core.c | 1315 +++++++++++++++++++++ include/arm_ffa.h | 97 ++ include/dm/uclass-id.h | 4 + 12 files changed, 1897 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_prv.h create mode 100644 drivers/firmware/arm-ffa/core.c create mode 100644 include/arm_ffa.h
diff --git a/MAINTAINERS b/MAINTAINERS index 97b2f69f65..dcd32cf83a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -263,6 +263,13 @@ 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 abdellatif.elkhlifi@arm.com +S: Maintained +F: doc/arch/arm64.ffa.rst +F: drivers/firmware/arm-ffa/ +F: include/arm_ffa.h + ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de M: Fabio Estevam festevam@gmail.com diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst new file mode 100644 index 0000000000..dfcec82e45 --- /dev/null +++ b/doc/arch/arm64.ffa.rst @@ -0,0 +1,218 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +Arm FF-A Driver +=============== + +Summary +------- + +FF-A stands for Firmware Framework for Arm A-profile processors. + +FF-A specifies interfaces that enable a pair of software sandboxes to +communicate with each other. A sandbox aka partition could +be a VM in the Normal or Secure world, an application in S-EL0, or a +Trusted OS in S-EL1. + +This FF-A driver implements the interfaces to communicate with partitions in +the Secure world aka Secure partitions (SPs). + +The driver 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. + +FF-A driver 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 the FF-A driver supports EFI boot time only. + +Runtime support will be added in future developments. + +FF-A and SMC specifications +------------------------------------------- + +The current implementation of the driver relies on FF-A specification v1.0 +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 driver has been tested with OP-TEE which supports SMC32 calling convention. + +For more details please refer to the FF-A v1.0 spec: +https://documentation-service.arm.com/static/5fb7e8a6ca04df4095c1d65e?token= + +Hypervisors are supported if they are configured to trap SMC calls. + +The FF-A driver uses 64-bit registers as per SMCCCv1.2 specification. + +For more details please refer to the SMC Calling Convention v1.2 spec: +https://documentation-service.arm.com/static/5f8edaeff86e16515cdbe4c6?token= + +Supported hardware +-------------------------------- + +Aarch64 plaforms + +Configuration +---------------------- + +CONFIG_ARM_FFA_TRANSPORT + Enables the FF-A bus driver. Turn this on if you want to use FF-A + communication. + +CONFIG_SANDBOX_FFA + Enables FF-A Sandbox driver. This emulates the FF-A ABIs handling under + Sandbox and provides functional tests for FF-A. + +FF-A ABIs under the hood +--------------------------------------- + +Invoking an FF-A ABI involves providing to the secure world/hypervisor the +expected arguments from the ABI. + +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 FF-A driver (non-secure world). + +The driver reads the response and processes it accordingly. + +This methodology applies to all the FF-A ABIs in the driver. + +FF-A bus discovery in U-Boot +------------------------------------------- + +When CONFIG_ARM_FFA_TRANSPORT is enabled, the FF-A bus is discovered on +demand by the clients (users). + +Clients can discover the FF-A bus using ffa_bus_discover() API which triggers the +discovery process. + +ffa_bus_discover() creates, binds and probes the arm_ffa device using +device_{bind, probe} APIs. + +The discovery process consists in communicating with secure world (or hypervisor) +and querying specific data. + +The discovery process takes place during the arm_ffa device probing which is +handled by ffa_probe(). + +The FF-A bus discovery is successful and the bus is ready for use when these +operations succeed: + +- querying the FF-A framework version +- querying from secure world the U-Boot endpoint ID +- querying from secure world the RX/TX mapping features +- mapping the RX/TX buffers +- querying from secure world all the partitions information + +Discovery failure results in a probing failure and the arm_ffa device is +destroyed. + +Requirements for clients +------------------------------------- + +When using the FF-A bus with EFI, clients must: + +- Query SPs in EFI boot time mode using the service UUID. +- Unmap RX/TX buffers before EFI runtime mode starts. + +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 by the user before EFI runtime mode +starts. The driver provides a bus operation for that: rxtx_unmap() + +If RX/TX buffers created by U-Boot are not unmapped and by +consequence becoming available at EFI runtime, secure world will get confused +about RX/TX buffers ownership (U-Boot vs kernel). + +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 stays the same: SMC32. + +The bus driver layer +------------------------------ + +The driver comes on top of the SMCCC layer and is implemented in +drivers/firmware/arm-ffa/core.c + +The driver provides the following features: + +- 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, the driver takes care of how to transport + that to the secure world/hypervisor using FF-A + +- The driver provides callbacks to be used by clients to access the FF-A bus: + +partition_info_get +sync_send_receive +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 + +Using armffa command +----------------------------------- + +armffa is an implementation defined command showcasing how to use the FF-A driver and how to invoke +its operations. + +This provides a guidance to the client developers on how to call the FF-A bus +interfaces. + +Usage: + +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 the arm_ffa device info + +Contributors +------------ + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com diff --git a/doc/arch/index.rst b/doc/arch/index.rst index b3e85f9bf3..cf1cfc9287 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/Kconfig b/drivers/Kconfig index 75ac149d31..ff75b7c3f8 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -6,6 +6,8 @@ source "drivers/core/Kconfig"
source "drivers/adc/Kconfig"
+source "drivers/firmware/arm-ffa/Kconfig" + source "drivers/ata/Kconfig"
source "drivers/axi/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index ac2d83af4e..d23009577e 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -112,6 +112,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/arm-ffa/Kconfig b/drivers/firmware/arm-ffa/Kconfig new file mode 100644 index 0000000000..e4914b9bc7 --- /dev/null +++ b/drivers/firmware/arm-ffa/Kconfig @@ -0,0 +1,30 @@ +# 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 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. + + This driver 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. + 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. + + For more details about the FF-A driver, 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 0000000000..043a8915be --- /dev/null +++ b/drivers/firmware/arm-ffa/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2022 +# Abdellatif El Khlifi, Arm Limited, abdellatif.elkhlifi@arm.com. + +obj-y += arm-ffa-uclass.o core.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 0000000000..7d9695d289 --- /dev/null +++ b/drivers/firmware/arm-ffa/arm-ffa-uclass.c @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <dm.h> +#include <asm/global_data.h> + +DECLARE_GLOBAL_DATA_PTR; + +UCLASS_DRIVER(ffa) = { + .name = "ffa", + .id = UCLASS_FFA, +}; diff --git a/drivers/firmware/arm-ffa/arm_ffa_prv.h b/drivers/firmware/arm-ffa/arm_ffa_prv.h new file mode 100644 index 0000000000..4eea7dc036 --- /dev/null +++ b/drivers/firmware/arm-ffa/arm_ffa_prv.h @@ -0,0 +1,200 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __ARM_FFA_PRV_H +#define __ARM_FFA_PRV_H + +#include <arm_ffa.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/arm-smccc.h> + +/* + * This header is private. It is exclusively used by the FF-A driver + */ + +/* FF-A core driver name */ +#define FFA_DRV_NAME "arm_ffa" + +/* 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 +}; + +/* 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) + +/* The FF-A SMC function definitions */ + +typedef struct arm_smccc_1_2_regs ffa_value_t; +typedef void (*invoke_ffa_fn_t)(ffa_value_t args, ffa_value_t *res); + +/* + * 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 */ +}; + +/** + * 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 - structure hosting 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 + * + * Data structure hosting 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 { + u64 rxbuf; /* virtual address */ + u64 txbuf; /* virtual address */ + size_t rxtx_min_pages; /* minimum number of pages in each of the RX/TX buffers */ +}; + +/** + * 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_partitions - descriptors for all secure partitions + * @count: The number of partitions descriptors + * @descs The partitions descriptors table + * + * This data structure contains the partitions descriptors table + */ +struct ffa_partitions { + u32 count; + struct ffa_partition_desc *descs; /* virtual address */ +}; + +/** + * struct ffa_prvdata - the driver private data structure + * + * @dev: The arm_ffa device under u-boot driver model + * @ffa_ops: The driver operations structure + * @fwk_version: FF-A framework version + * @id: u-boot endpoint ID + * @partitions: The partitions descriptors structure + * @pair: The RX/TX buffers pair + * @invoke_ffa_fn: The function executing the FF-A function + * + * The driver data structure hosting all resident data. + */ +struct ffa_prvdata { + struct udevice *dev; + struct ffa_bus_ops ffa_ops; + u32 fwk_version; + u16 id; + struct ffa_partitions partitions; + struct ffa_rxtxpair pair; + invoke_ffa_fn_t invoke_ffa_fn; +}; + +/** + * ffa_bus_prvdata_get - bus driver private data getter + */ +struct ffa_prvdata *ffa_bus_prvdata_get(void); + +#endif diff --git a/drivers/firmware/arm-ffa/core.c b/drivers/firmware/arm-ffa/core.c new file mode 100644 index 0000000000..0b1f8e6a07 --- /dev/null +++ b/drivers/firmware/arm-ffa/core.c @@ -0,0 +1,1315 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include "arm_ffa_prv.h" +#include <asm/global_data.h> +#include <common.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <dm/devres.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> +#include <log.h> +#include <malloc.h> +#include <string.h> +#include <uuid.h> + +DECLARE_GLOBAL_DATA_PTR; + +/** + * The device private data structure containing all the + * data read from secure world + */ +struct ffa_prvdata *ffa_priv_data; + +/* 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, +}; + +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 + * + * This function maps 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 + */ +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 + * + * This function maps 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 + */ +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; + + ffa_err("%s", err_msg_map[abi_idx].err_str[err_idx]); + + return 0; +} + +/* + * Driver core functions + */ + +/** + * ffa_remove_device - removes the arm_ffa device + * @dev: the device to be removed + * + * This function makes sure the arm_ffa device is removed + * No need to free the kmalloced data when the device is destroyed. + * It's automatically done by devm management by + * device_remove() -> device_free() -> devres_release_probe(). + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int ffa_remove_device(struct udevice *dev) +{ + int ret; + + if (!dev) { + ffa_err("no udevice found"); + return -ENODEV; + } + + ret = device_remove(dev, DM_REMOVE_NORMAL); + if (ret) { + ffa_err("unable to remove. err:%d\n", ret); + return ret; + } + + ffa_info("device removed and freed"); + + ret = device_unbind(dev); + if (ret) { + ffa_err("unable to unbind. err:%d\n", ret); + return ret; + } + + ffa_info("device unbound"); + + return 0; +} + +/** + * ffa_device_get - create, bind and probe the arm_ffa device + * @pdev: the address of a device pointer (to be filled when the arm_ffa bus device is created + * successfully) + * + * This function makes sure the arm_ffa device is + * created, bound to this driver, probed and ready to use. + * Arm FF-A transport is implemented through a single U-Boot + * device managing the FF-A bus (arm_ffa). + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int ffa_device_get(struct udevice **pdev) +{ + int ret; + struct udevice *dev = NULL; + + ret = device_bind(dm_root(), DM_DRIVER_GET(arm_ffa), FFA_DRV_NAME, NULL, ofnode_null(), + &dev); + if (ret) + return ret; + + /* The FF-A bus discovery succeeds when probing is successful */ + ret = device_probe(dev); + if (ret) { + ffa_err("arm_ffa device probing failed"); + ffa_remove_device(dev); + return ret; + } + + if (pdev) + *pdev = dev; + + return 0; +} + +/** + * ffa_get_version - FFA_VERSION handler function + * + * This function implements FFA_VERSION FF-A function + * to get from the secure world the FF-A framework version + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_get_version(void) +{ + u16 major, minor; + ffa_value_t res = {0}; + int ffa_errno; + + ffa_priv_data->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); + + ffa_info("FF-A driver %d.%d\nFF-A framework %d.%d", + FFA_MAJOR_VERSION, FFA_MINOR_VERSION, major, minor); + + if ((major == FFA_MAJOR_VERSION && minor >= FFA_MINOR_VERSION)) { + ffa_info("Versions are compatible "); + + ffa_priv_data->fwk_version = res.a0; + + return 0; + } + + ffa_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 + * + * This function implements 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(void) +{ + ffa_value_t res = {0}; + int ffa_errno; + + ffa_priv_data->invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_32(FFA_ID_GET), + }, &res); + + if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) { + ffa_priv_data->id = GET_SELF_ENDPOINT_ID((u32)res.a2); + ffa_info("endpoint ID is %u", ffa_priv_data->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 - sets the minimum number of pages in each of the RX/TX buffers + * @prop_field: properties field obtained from FFA_FEATURES ABI + * + * This function sets the minimum number of pages + * in each of the RX/TX buffers in the private data structure + * + * Return: + * + * buf_4k_pages points to the returned number of pages + * 0 on success. Otherwise, failure + */ +static int ffa_set_rxtx_buffers_pages_cnt(u32 prop_field) +{ + if (!ffa_priv_data) + return -EINVAL; + + switch (prop_field) { + case RXTX_4K: + ffa_priv_data->pair.rxtx_min_pages = 1; + break; + case RXTX_16K: + ffa_priv_data->pair.rxtx_min_pages = 4; + break; + case RXTX_64K: + ffa_priv_data->pair.rxtx_min_pages = 16; + break; + default: + ffa_err("RX/TX buffer size not supported"); + return -EINVAL; + } + + return 0; +} + +/** + * ffa_get_rxtx_map_features - FFA_FEATURES handler function with FFA_RXTX_MAP argument + * + * This function implements 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(void) +{ + ffa_value_t res = {0}; + int ffa_errno; + + ffa_priv_data->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(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 - frees the RX/TX buffers + * + * This function frees the RX/TX buffers + * + */ +static void ffa_free_rxtx_buffers(void) +{ + ffa_info("Freeing RX/TX buffers"); + + if (ffa_priv_data->pair.rxbuf) { + free((void *)ffa_priv_data->pair.rxbuf); + ffa_priv_data->pair.rxbuf = 0; + } + + if (ffa_priv_data->pair.txbuf) { + free((void *)ffa_priv_data->pair.txbuf); + ffa_priv_data->pair.txbuf = 0; + } +} + +/** + * ffa_alloc_rxtx_buffers - allocates the RX/TX buffers + * + * This function is 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. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_alloc_rxtx_buffers(void) +{ + u64 bytes; + + ffa_info("Using %lu 4KB page(s) for RX/TX buffers size", + ffa_priv_data->pair.rxtx_min_pages); + + bytes = ffa_priv_data->pair.rxtx_min_pages * SZ_4K; + + /* + * The alignment of the RX and TX buffers must be equal + * to the larger translation granule size + */ + + ffa_priv_data->pair.rxbuf = (u64)memalign(bytes, bytes); + if (!ffa_priv_data->pair.rxbuf) { + ffa_err("failure to allocate RX buffer"); + return -ENOBUFS; + } + + ffa_info("RX buffer at virtual address 0x%llx", ffa_priv_data->pair.rxbuf); + + ffa_priv_data->pair.txbuf = (u64)memalign(bytes, bytes); + if (!ffa_priv_data->pair.txbuf) { + free((void *)ffa_priv_data->pair.rxbuf); + ffa_priv_data->pair.rxbuf = 0; + ffa_err("failure to allocate the TX buffer"); + return -ENOBUFS; + } + + ffa_info("TX buffer at virtual address 0x%llx", ffa_priv_data->pair.txbuf); + + /* + * make sure the buffers are cleared before use + */ + memset((void *)ffa_priv_data->pair.rxbuf, 0, bytes); + memset((void *)ffa_priv_data->pair.txbuf, 0, bytes); + + return 0; +} + +/** + * ffa_map_rxtx_buffers - FFA_RXTX_MAP handler function + * + * This function implements FFA_RXTX_MAP FF-A function + * to map the RX/TX buffers + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_map_rxtx_buffers(void) +{ + int ret; + ffa_value_t res = {0}; + int ffa_errno; + + ret = ffa_alloc_rxtx_buffers(); + 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 + */ + + ffa_priv_data->invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_64(FFA_RXTX_MAP), + .a1 = ffa_priv_data->pair.txbuf, + .a2 = ffa_priv_data->pair.rxbuf, + .a3 = ffa_priv_data->pair.rxtx_min_pages, + }, &res); + + if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) { + ffa_info("RX/TX buffers mapped"); + return 0; + } + + ffa_errno = res.a2; + ffa_print_error_log(FFA_RXTX_MAP, ffa_errno); + + ffa_free_rxtx_buffers(); + + return ffa_to_std_errno(ffa_errno); +} + +/** + * ffa_unmap_rxtx_buffers - 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 + */ +static int ffa_unmap_rxtx_buffers(struct udevice *dev) +{ + ffa_value_t res = {0}; + int ffa_errno; + + ffa_priv_data->invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_32(FFA_RXTX_UNMAP), + .a1 = PREP_SELF_ENDPOINT_ID(ffa_priv_data->id), + }, &res); + + if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) { + ffa_free_rxtx_buffers(); + 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 - FFA_RX_RELEASE handler function + * + * This function invokes 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(void) +{ + ffa_value_t res = {0}; + int ffa_errno; + + ffa_priv_data->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 - checks whether two given UUIDs are identical + * @uuid1: first UUID + * @uuid2: second UUID + * + * This function is used by ffa_read_partitions_info to search + * for a UUID in the partitions descriptors table + * + * Return: + * + * 1 when UUIDs match. Otherwise, 0 + */ +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 - reads the data queried by FFA_PARTITION_INFO_GET + * and saves it in the private structure + * @count: The number of partitions queried + * @part_uuid: Pointer to the partition(s) UUID + * + * This function reads the partitions information + * returned by the FFA_PARTITION_INFO_GET and saves it in the private + * data structure. + * + * Return: + * + * The private data structure is updated with the partition(s) information + * 0 is returned on success. Otherwise, failure + */ +static int ffa_read_partitions_info(u32 count, struct ffa_partition_uuid *part_uuid) +{ + if (!count) { + ffa_err("no partition detected"); + return -ENODATA; + } + + ffa_info("Reading partitions data from the RX buffer"); + + 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 = ffa_priv_data->pair.rxtx_min_pages * SZ_4K; + + if (data_bytes > buf_bytes) { + ffa_err("partitions data size exceeds the RX buffer size:"); + ffa_err(" sizes in bytes: data %llu , RX buffer %llu ", + data_bytes, + buf_bytes); + + return -ENOMEM; + } + + ffa_priv_data->partitions.descs = devm_kmalloc(ffa_priv_data->dev, data_bytes, + __GFP_ZERO); + if (!ffa_priv_data->partitions.descs) { + ffa_err("cannot allocate partitions data buffer"); + return -ENOMEM; + } + + parts_info = (struct ffa_partition_info *)ffa_priv_data->pair.rxbuf; + + for (desc_idx = 0 ; desc_idx < count ; desc_idx++) { + ffa_priv_data->partitions.descs[desc_idx].info = + parts_info[desc_idx]; + + ffa_info("Partition ID %x : info cached", + ffa_priv_data->partitions.descs[desc_idx].info.id); + } + + ffa_priv_data->partitions.count = count; + + ffa_info("%d partition(s) found and cached", count); + + } else { + u32 rx_desc_idx, cached_desc_idx; + struct ffa_partition_info *parts_info; + u8 desc_found; + + parts_info = (struct ffa_partition_info *)ffa_priv_data->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 < ffa_priv_data->partitions.count; + cached_desc_idx++) { + /* + * save the UUID + */ + if (ffa_priv_data->partitions.descs[cached_desc_idx].info.id == + parts_info[rx_desc_idx].id) { + ffa_priv_data->partitions.descs[cached_desc_idx].sp_uuid = + *part_uuid; + + desc_found = 1; + break; + } + } + + if (!desc_found) + return -ENODATA; + } + } + + return 0; +} + +/** + * ffa_query_partitions_info - invokes FFA_PARTITION_INFO_GET and saves partitions data + * + * @part_uuid: Pointer to the partition(s) UUID + * @pcount: Pointer to the number of partitions variable filled when querying + * + * This function executes the FFA_PARTITION_INFO_GET + * to query the partitions data. Then, it calls ffa_read_partitions_info + * to save the data in the private data structure. + * + * 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 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; + } + + ffa_priv_data->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((u32)res.a2, part_uuid); + if (ret) { + ffa_err("failed to read partition(s) data , error (%d)", ret); + ffa_release_rx_buffer(); + 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(); + + 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 - FFA_PARTITION_INFO_GET handler function + * + * The passed arguments: + * Mode 1: When getting from the driver the number of + * secure partitions: + * @uuid_str: pointer to the UUID string + * @sp_count: pointer to the variable that contains the number of partitions + * The variable will be set by the driver + * @buffer: NULL + * + * Mode 2: When requesting the driver to return the + * partitions information: + * @dev: The arm_ffa bus device + * @uuid_str: pointer to the UUID string + * @sp_count: pointer to the variable that contains the number of empty partition descriptors + * The variable will be read by the driver + * @buffer: pointer to SPs information buffer + * (allocated by the client and contains empty @sp_count descriptors). + * The buffer will be filled by the driver + * + * This function queries the secure partition data from + * the private data structure. If not found, it invokes 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 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(). + * + * When invoked through a client request, ffa_get_partitions_info should be + * called twice. First call is to get from the driver the number of secure + * partitions (SPs) associated to a particular UUID. + * Then, the caller (client) allocates the buffer to host the SPs data and + * issues a 2nd call. Then, the driver fills the SPs data in the pre-allocated + * buffer. + * + * To achieve the mechanism described above, ffa_get_partitions_info uses the + * following functions: + * ffa_read_partitions_info + * ffa_query_partitions_info + * + * Return: + * + * @sp_count: When pointing to the number of partitions variable, the number is + * set by the driver. + * When pointing to the partitions information buffer size, the buffer will be + * filled by the driver. + * + * On success 0 is returned. Otherwise, failure + */ +static int ffa_get_partitions_info(struct udevice *dev, const char *uuid_str, + u32 *sp_count, struct ffa_partition_info *buffer) +{ + /* + * fill_data: + * 0: return the SP count + * 1: fill SP data and return it to the caller + */ + bool fill_data = 0; + u32 desc_idx, client_desc_idx; + struct ffa_partition_uuid part_uuid = {0}; + u32 sp_found = 0; + + if (!ffa_priv_data->partitions.count || !ffa_priv_data->partitions.descs) { + ffa_err("no partition installed"); + return -EINVAL; + } + + if (!uuid_str) { + ffa_err("no UUID provided"); + return -EINVAL; + } + + if (!sp_count) { + ffa_err("no size/count provided"); + return -EINVAL; + } + + if (uuid_str_to_le_bin(uuid_str, (unsigned char *)&part_uuid)) { + ffa_err("invalid UUID"); + return -EINVAL; + } + + if (!buffer) { + /* Mode 1: getting the number of secure partitions */ + + fill_data = 0; + + ffa_info("Preparing for checking partitions count"); + + } else if (*sp_count) { + /* Mode 2: retrieving the partitions information */ + + fill_data = 1; + + client_desc_idx = 0; + + ffa_info("Preparing for filling partitions info"); + + } else { + ffa_err("invalid function arguments provided"); + return -EINVAL; + } + + ffa_info("Searching partitions using the provided UUID"); + + /* + * search in the cached partitions + */ + for (desc_idx = 0; + desc_idx < ffa_priv_data->partitions.count; + desc_idx++) { + if (ffa_uuid_are_identical(&ffa_priv_data->partitions.descs[desc_idx].sp_uuid, + &part_uuid)) { + ffa_info("Partition ID %x matches the provided UUID", + ffa_priv_data->partitions.descs[desc_idx].info.id); + + sp_found++; + + if (fill_data) { + /* + * trying to fill the partition info in the input buffer + */ + + if (client_desc_idx < *sp_count) { + buffer[client_desc_idx++] = + ffa_priv_data->partitions.descs[desc_idx].info; + continue; + } + + ffa_err("failed to fill the current descriptor client buffer full"); + return -ENOBUFS; + } + } + } + + if (!sp_found) { + int ret; + + ffa_info("No partition found. Querying framework ..."); + + ret = ffa_query_partitions_info(&part_uuid, &sp_found); + + if (ret == 0) { + if (!fill_data) { + *sp_count = sp_found; + + ffa_info("Number of partition(s) found matching the UUID: %d", + sp_found); + } else { + /* + * If SPs data detected, they are already in the private data + * structure, retry searching SP data again to return them + * to the caller + */ + if (sp_found) + ret = ffa_get_partitions_info(dev, uuid_str, sp_count, + buffer); + else + ret = -ENODATA; + } + } + + return ret; + } + + /* partition(s) found */ + if (!fill_data) + *sp_count = sp_found; + + return 0; +} + +/** + * ffa_cache_partitions_info - Queries and saves all secure partitions data + * + * This function invokes 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 the + * resident private data structure and we keep the UUID field empty + * (in FF-A 1.0 UUID is not provided by the partition descriptor) + * + * This function is 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(void) +{ + return ffa_query_partitions_info(NULL, NULL); +} + +/** + * ffa_msg_send_direct_req - 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 + */ +static int ffa_msg_send_direct_req(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; + + if (!ffa_priv_data || !ffa_priv_data->invoke_ffa_fn) + return -EINVAL; + + /* No partition installed */ + if (!ffa_priv_data->partitions.count || !ffa_priv_data->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); + } + + ffa_priv_data->invoke_ffa_fn((ffa_value_t){ + .a0 = req_mode, + .a1 = PREP_SELF_ENDPOINT_ID(ffa_priv_data->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)) + ffa_priv_data->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); +} + +/** + * __arm_ffa_fn_smc - 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 + * + * Return: void + */ +void __arm_ffa_fn_smc(ffa_value_t args, ffa_value_t *res) +{ + arm_smccc_1_2_smc(&args, res); +} + +/** + * ffa_set_smc_conduit - Set the SMC conduit + * + * This function selects the SMC conduit by setting the driver invoke function + * to SMC assembly function + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_set_smc_conduit(void) +{ + ffa_priv_data->invoke_ffa_fn = __arm_ffa_fn_smc; + + if (!ffa_priv_data->invoke_ffa_fn) { + ffa_err("failure to set the invoke function"); + return -EINVAL; + } + + ffa_info("Conduit is SMC"); + + return 0; +} + +/** + * ffa_set_bus_ops - Set the bus driver operations + * + * Setting the driver callbacks. + * + */ +static void ffa_set_bus_ops(void) +{ + ffa_priv_data->ffa_ops.partition_info_get = ffa_get_partitions_info; + ffa_priv_data->ffa_ops.sync_send_receive = ffa_msg_send_direct_req; + ffa_priv_data->ffa_ops.rxtx_unmap = ffa_unmap_rxtx_buffers; +} + +/** + * ffa_alloc_prvdata - allocate the driver main data structure and sets the device + * @dev: the arm_ffa device + * + * This function creates the main data structure embedding all the driver data. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_alloc_prvdata(struct udevice *dev) +{ + if (!dev) { + ffa_err("no udevice found"); + return -ENODEV; + } + + /* The device is registered with the DM. Let's create the driver main data structure*/ + + ffa_priv_data = devm_kmalloc(dev, sizeof(struct ffa_prvdata), __GFP_ZERO); + if (!ffa_priv_data) { + ffa_err("can not allocate the driver main data structure"); + return -ENOMEM; + } + + ffa_priv_data->dev = dev; + + return 0; +} + +/** + * ffa_probe - The driver probe function + * @dev: the arm_ffa device + * + * Probing is done at boot time and triggered by the uclass device discovery. + * At probe level the following actions are done: + * - setting the conduit + * - querying the FF-A framework version + * - 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 the resident private data structure. + * + * The probe will fail if either FF-A framework is not detected or the + * FF-A requests are not behaving correctly. This ensures that the + * driver is not installed and its operations are not exported to the clients. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_probe(struct udevice *dev) +{ + int ret; + + ret = ffa_alloc_prvdata(dev); + if (ret != 0) + return ret; + + ffa_set_bus_ops(); + + ret = ffa_set_smc_conduit(); + if (ret != 0) + return ret; + + ret = ffa_get_version(); + if (ret != 0) + return ret; + + ret = ffa_get_endpoint_id(); + if (ret != 0) + return ret; + + ret = ffa_get_rxtx_map_features(); + if (ret != 0) + return ret; + + ret = ffa_map_rxtx_buffers(); + if (ret != 0) + return ret; + + ret = ffa_cache_partitions_info(); + if (ret != 0) { + ffa_free_rxtx_buffers(); + return ret; + } + + return 0; +} + +/** + * ffa_remove - The driver remove function + * @dev: the arm_ffa device + * When the device is about to be removed , unmap the RX/TX buffers and free the memory + * Return: + * + * 0 on success. + */ +static int ffa_remove(struct udevice *dev) +{ + ffa_info("removing the device"); + + ffa_unmap_rxtx_buffers(dev); + + if (ffa_priv_data->pair.rxbuf || ffa_priv_data->pair.txbuf) + ffa_free_rxtx_buffers(); + + return 0; +} + +/** + * ffa_unbind - The driver unbind function + * @dev: the arm_ffa device + * After the device is removed and memory freed the device is unbound + * Return: + * + * 0 on success. + */ +static int ffa_unbind(struct udevice *dev) +{ + ffa_info("unbinding the device , private data already released"); + + ffa_priv_data = NULL; + + return 0; +} + +/** + * ffa_bus_ops_get - bus driver operations getter + * + * Return: + * This function returns a pointer to the driver operations structure + */ +const struct ffa_bus_ops *ffa_bus_ops_get(void) +{ + return &ffa_priv_data->ffa_ops; +} + +/** + * ffa_bus_prvdata_get - bus driver private data getter + * + * Return: + * This function returns a pointer to the main private data structure + */ +struct ffa_prvdata *ffa_bus_prvdata_get(void) +{ + return ffa_priv_data; +} + +/** + * ffa_bus_discover - discover FF-A bus and probe arm_ffa device + * @pdev: the address of a device pointer (to be filled when the arm_ffa bus device is created + * successfully) + * + * This function makes sure the FF-A bus is discoverable. + * When probing succeeds FF-A discovery is done. The arm_ffa device is ready to use. + * + * When the bus was already discovered successfully the discovery will not run again. + * + * Arm FF-A transport is implemented through arm_ffa u-boot device managing the FF-A + * communication. + * All FF-A clients should use the arm_ffa device to use the FF-A transport. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int ffa_bus_discover(struct udevice **pdev) +{ + int ret = 0; + + if (!ffa_priv_data) { + ret = ffa_device_get(pdev); + + return ret; +} + +/** + * Declaring the arm_ffa driver under UCLASS_FFA + */ + +U_BOOT_DRIVER(arm_ffa) = { + .name = FFA_DRV_NAME, + .id = UCLASS_FFA, + .probe = ffa_probe, + .remove = ffa_remove, + .unbind = ffa_unbind, +}; diff --git a/include/arm_ffa.h b/include/arm_ffa.h new file mode 100644 index 0000000000..74b16174c2 --- /dev/null +++ b/include/arm_ffa.h @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __ARM_FFA_H +#define __ARM_FFA_H + +#include <linux/printk.h> + +/* + * This header is public. It can be used by clients to access + * data structures and definitions they need + */ + +/* + * Macros for displaying logs + */ + +#define ffa_info(fmt, ...) pr_info("[FFA] " fmt "\n", ##__VA_ARGS__) +#define ffa_err(fmt, ...) pr_err("[FFA] " fmt "\n", ##__VA_ARGS__) + +/* + * 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_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 { + unsigned long data0; /* w3/x3 */ + unsigned long data1; /* w4/x4 */ + unsigned long data2; /* w5/x5 */ + unsigned long data3; /* w6/x6 */ + unsigned long data4; /* w7/x7 */ +}; + +struct udevice; + +/** + * struct ffa_bus_ops - The driver operations structure + * @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_info *buffer); + 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); +}; + +/** + * The device driver and the Uclass driver public functions + */ + +/** + * ffa_bus_ops_get - driver operations getter + */ +const struct ffa_bus_ops *ffa_bus_ops_get(void); + +/** + * ffa_bus_discover - discover FF-A bus and probes the arm_ffa device + */ +int ffa_bus_discover(struct udevice **pdev); + +#endif diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 376f741cc2..fa08a66ac8 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -4,6 +4,9 @@ * * (C) Copyright 2012 * Pavel Herrmann morpheus.ibis@gmail.com + * + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#ifndef _DM_UCLASS_ID_H @@ -55,6 +58,7 @@ enum uclass_id { UCLASS_EFI_MEDIA, /* Devices provided by UEFI firmware */ UCLASS_ETH, /* Ethernet device */ UCLASS_ETH_PHY, /* Ethernet PHY device */ + UCLASS_FFA, /* Arm Firmware Framework for Armv8-A */ UCLASS_FIRMWARE, /* Firmware */ UCLASS_FPGA, /* FPGA device */ UCLASS_FUZZING_ENGINE, /* Fuzzing engine */

should be called 'priov' and should beHi Abdellatif,
On Tue, 22 Nov 2022 at 06:18, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
Add the core driver 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.
In U-Boot FF-A design, FF-A is considered as a discoverable bus. 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:
- partition_info_get
- sync_send_receive
- rxtx_unmap
For more details please refer to the driver documentation [2].
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v8:
- make ffa_get_partitions_info() second argument to be an SP count in both modes
- update ffa_bus_prvdata_get() to return a pointer rather than a pointer address
- remove packing from ffa_partition_info and ffa_send_direct_data structures
- pass the FF-A bus device to the bus operations
v7:
- add support for 32-bit direct messaging
- rename be_uuid_str_to_le_bin() to uuid_str_to_le_bin()
- improve the declaration of error handling mapping
- stating in doc/arch/arm64.ffa.rst that EFI runtime is not supported
v6:
- drop use of EFI runtime support (We decided with Linaro to add this later)
- drop discovery from initcalls (discovery will be on demand by FF-A users)
- set the alignment of the RX/TX buffers to the larger translation granule size
- move FF-A RX/TX buffers unmapping at ExitBootServices() to a separate commit
- update the documentation and move it to doc/arch/arm64.ffa.rst
v4:
- add doc/README.ffa.drv
- moving the FF-A driver work to drivers/firmware/arm-ffa
- use less #ifdefs in lib/efi_loader/efi_boottime.c and replace #if defined by #if CONFIG_IS_ENABLED
- improving error handling by mapping the FF-A errors to standard errors and logs
- replacing panics with an error log and returning an error code
- improving features discovery in FFA_FEATURES by introducing rxtx_min_pages private data field
- add ffa_remove and ffa_unbind functions
- improve how the driver behaves when bus discovery is done more than once
v3:
- align the interfaces of the U-Boot FF-A driver with those in the linux FF-A driver
- remove the FF-A helper layer
- make the U-Boot FF-A driver independent from EFI
- provide an optional config that enables copying the driver data to EFI runtime section at ExitBootServices service
- use 64-bit version of FFA_RXTX_MAP, FFA_MSG_SEND_DIRECT_{REQ, RESP}
v2:
- make FF-A bus discoverable using device_{bind, probe} APIs
- remove device tree support
v1:
- introduce FF-A bus driver with device tree support
MAINTAINERS | 7 + doc/arch/arm64.ffa.rst | 218 ++++ doc/arch/index.rst | 1 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/firmware/arm-ffa/Kconfig | 30 + drivers/firmware/arm-ffa/Makefile | 6 + drivers/firmware/arm-ffa/arm-ffa-uclass.c | 16 + drivers/firmware/arm-ffa/arm_ffa_prv.h | 200 ++++ drivers/firmware/arm-ffa/core.c | 1315 +++++++++++++++++++++ include/arm_ffa.h | 97 ++ include/dm/uclass-id.h | 4 + 12 files changed, 1897 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_prv.h create mode 100644 drivers/firmware/arm-ffa/core.c create mode 100644 include/arm_ffa.h
This looks mostly OK to me. I have a few comments below. [..]
diff --git a/drivers/firmware/arm-ffa/arm_ffa_prv.h b/drivers/firmware/arm-ffa/arm_ffa_prv.h new file mode 100644 index 0000000000..4eea7dc036 --- /dev/null +++ b/drivers/firmware/arm-ffa/arm_ffa_prv.h @@ -0,0 +1,200 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#ifndef __ARM_FFA_PRV_H +#define __ARM_FFA_PRV_H
+#include <arm_ffa.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/arm-smccc.h>
+/*
- This header is private. It is exclusively used by the FF-A driver
- */
/* ...*/
is the single-line comment style
+/* FF-A core driver name */ +#define FFA_DRV_NAME "arm_ffa"
+/* 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,
Can you use lower-case hex consistently?
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*/
not: spaces before */
+};
+enum ffa_abi_errcode {
NOT_SUPPORTED = 1,
INVALID_PARAMETERS,
NO_MEMORY,
BUSY,
INTERRUPTED,
DENIED,
RETRY,
ABORTED,
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)
+/* The FF-A SMC function definitions */
+typedef struct arm_smccc_1_2_regs ffa_value_t; +typedef void (*invoke_ffa_fn_t)(ffa_value_t args, ffa_value_t *res);
that needs a comment
[..]
+/**
- struct ffa_rxtxpair - structure hosting 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
- Data structure hosting the virtual addresses of the mapped RX/TX buffers
Just a nit but it catches my eye. We know this is a struct. You can just say "Hosts the ..."
[..]
diff --git a/drivers/firmware/arm-ffa/core.c b/drivers/firmware/arm-ffa/core.c new file mode 100644 index 0000000000..0b1f8e6a07 --- /dev/null +++ b/drivers/firmware/arm-ffa/core.c @@ -0,0 +1,1315 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include "arm_ffa_prv.h" +#include <asm/global_data.h> +#include <common.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <dm/devres.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> +#include <log.h> +#include <malloc.h> +#include <string.h> +#include <uuid.h>
See this:
https://u-boot.readthedocs.io/en/latest/develop/codingstyle.html?highlight=s...
[..]
+/*
- Driver core functions
- */
+/**
- ffa_remove_device - removes the arm_ffa device
- @dev: the device to be removed
- This function makes sure the arm_ffa device is removed
- No need to free the kmalloced data when the device is destroyed.
- It's automatically done by devm management by
- device_remove() -> device_free() -> devres_release_probe().
- Return:
- 0 on success. Otherwise, failure
- */
+int ffa_remove_device(struct udevice *dev) +{
int ret;
if (!dev) {
ffa_err("no udevice found");
return -ENODEV;
}
ret = device_remove(dev, DM_REMOVE_NORMAL);
if (ret) {
ffa_err("unable to remove. err:%d\n", ret);
return ret;
}
ffa_info("device removed and freed");
ret = device_unbind(dev);
if (ret) {
ffa_err("unable to unbind. err:%d\n", ret);
return ret;
}
ffa_info("device unbound");
return 0;
+}
Do we need this function? We should not be unbinding devices. Even removing them should be done elsewhere if needed. But why?
+/**
- ffa_device_get - create, bind and probe the arm_ffa device
- @pdev: the address of a device pointer (to be filled when the arm_ffa bus device is created
successfully)
- This function makes sure the arm_ffa device is
- created, bound to this driver, probed and ready to use.
- Arm FF-A transport is implemented through a single U-Boot
- device managing the FF-A bus (arm_ffa).
- Return:
- 0 on success. Otherwise, failure
- */
+int ffa_device_get(struct udevice **pdev) +{
int ret;
struct udevice *dev = NULL;
ret = device_bind(dm_root(), DM_DRIVER_GET(arm_ffa), FFA_DRV_NAME, NULL, ofnode_null(),
&dev);
Please add a DT binding. Even if only temporary, we need something for this. [..]
+static int ffa_get_version(void) +{
u16 major, minor;
ffa_value_t res = {0};
int ffa_errno;
ffa_priv_data->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);
ffa_info("FF-A driver %d.%d\nFF-A framework %d.%d",
FFA_MAJOR_VERSION, FFA_MINOR_VERSION, major, minor);
if ((major == FFA_MAJOR_VERSION && minor >= FFA_MINOR_VERSION)) {
nit: Drop extra brackets
ffa_info("Versions are compatible ");
ffa_priv_data->fwk_version = res.a0;
return 0;
}
ffa_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
I believe these should have () at the end, so:
ffa_get_endpoint_id() - FFA_ID_GET handler function
- This function implements FFA_ID_GET FF-A function
- to get from the secure world u-boot endpoint ID
- Return:
- 0 on success. Otherwise, failure
- */
[..]
+/**
- ffa_free_rxtx_buffers - frees the RX/TX buffers
- This function frees the RX/TX buffers
- */
+static void ffa_free_rxtx_buffers(void) +{
ffa_info("Freeing RX/TX buffers");
if (ffa_priv_data->pair.rxbuf) {
free((void *)ffa_priv_data->pair.rxbuf);
You can't cast an address to a pointer in this way. Should use map_sysmem() or store a pointer.
ffa_priv_data->pair.rxbuf = 0;
}
if (ffa_priv_data->pair.txbuf) {
free((void *)ffa_priv_data->pair.txbuf);
ffa_priv_data->pair.txbuf = 0;
}
+} +[..]
/*
* make sure the buffers are cleared before use
*/
memset((void *)ffa_priv_data->pair.rxbuf, 0, bytes);
memset((void *)ffa_priv_data->pair.txbuf, 0, bytes);
Yes these should be pointers everywhere, since you are casting all the time.
return 0;
+}
[..]
+/**
- ffa_read_partitions_info - reads the data queried by FFA_PARTITION_INFO_GET
and saves it in the private structure
Can you fit all these titles on one line, like:
ffa_read_partitions_info() - Read partition information
<insert longer description here?>
- @count: The number of partitions queried
- @part_uuid: Pointer to the partition(s) UUID
- This function reads the partitions information
- returned by the FFA_PARTITION_INFO_GET and saves it in the private
- data structure.
- Return:
- The private data structure is updated with the partition(s) information
- 0 is returned on success. Otherwise, failure
- */
[..] [..]
+static int ffa_msg_send_direct_req(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;
if (!ffa_priv_data || !ffa_priv_data->invoke_ffa_fn)
return -EINVAL;
ffa_priv_data should be called 'priv' and attached to the device with device_get_priv() [..]
+/**
- ffa_probe - The driver probe function
- @dev: the arm_ffa device
- Probing is done at boot time and triggered by the uclass device discovery.
- At probe level the following actions are done:
- setting the conduit
- querying the FF-A framework version
- 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 the resident private data structure.
- The probe will fail if either FF-A framework is not detected or the
- FF-A requests are not behaving correctly. This ensures that the
- driver is not installed and its operations are not exported to the clients.
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_probe(struct udevice *dev) +{
int ret;
ret = ffa_alloc_prvdata(dev);
don't do this, see above.
[..]
diff --git a/include/arm_ffa.h b/include/arm_ffa.h new file mode 100644 index 0000000000..74b16174c2 --- /dev/null +++ b/include/arm_ffa.h @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#ifndef __ARM_FFA_H +#define __ARM_FFA_H
+#include <linux/printk.h>
+/*
- This header is public. It can be used by clients to access
- data structures and definitions they need
- */
+/*
- Macros for displaying logs
- */
+#define ffa_info(fmt, ...) pr_info("[FFA] " fmt "\n", ##__VA_ARGS__) +#define ffa_err(fmt, ...) pr_err("[FFA] " fmt "\n", ##__VA_ARGS__)
You could use log_info(), log_err()
+/*
- 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_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 {
unsigned long data0; /* w3/x3 */
unsigned long data1; /* w4/x4 */
unsigned long data2; /* w5/x5 */
unsigned long data3; /* w6/x6 */
unsigned long data4; /* w7/x7 */
+};
+struct udevice;
+/**
- struct ffa_bus_ops - The driver operations structure
- @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_info *buffer);
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);
+};
Shouldn't this be the .ops member in your driver?
+/**
- The device driver and the Uclass driver public functions
- */
+/**
- ffa_bus_ops_get - driver operations getter
- */
+const struct ffa_bus_ops *ffa_bus_ops_get(void);
See how this is done in other uclasses, e.g. spi_get_ops()
Regards, SImon

On Tue, Nov 22, 2022 at 07:09:16PM -0700, Simon Glass wrote:
should be called 'priov' and should beHi Abdellatif,
On Tue, 22 Nov 2022 at 06:18, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
Add the core driver 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.
In U-Boot FF-A design, FF-A is considered as a discoverable bus. 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:
- partition_info_get
- sync_send_receive
- rxtx_unmap
For more details please refer to the driver documentation [2].
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v8:
- make ffa_get_partitions_info() second argument to be an SP count in both modes
- update ffa_bus_prvdata_get() to return a pointer rather than a pointer address
- remove packing from ffa_partition_info and ffa_send_direct_data structures
- pass the FF-A bus device to the bus operations
v7:
- add support for 32-bit direct messaging
- rename be_uuid_str_to_le_bin() to uuid_str_to_le_bin()
- improve the declaration of error handling mapping
- stating in doc/arch/arm64.ffa.rst that EFI runtime is not supported
v6:
- drop use of EFI runtime support (We decided with Linaro to add this later)
- drop discovery from initcalls (discovery will be on demand by FF-A users)
- set the alignment of the RX/TX buffers to the larger translation granule size
- move FF-A RX/TX buffers unmapping at ExitBootServices() to a separate commit
- update the documentation and move it to doc/arch/arm64.ffa.rst
v4:
- add doc/README.ffa.drv
- moving the FF-A driver work to drivers/firmware/arm-ffa
- use less #ifdefs in lib/efi_loader/efi_boottime.c and replace #if defined by #if CONFIG_IS_ENABLED
- improving error handling by mapping the FF-A errors to standard errors and logs
- replacing panics with an error log and returning an error code
- improving features discovery in FFA_FEATURES by introducing rxtx_min_pages private data field
- add ffa_remove and ffa_unbind functions
- improve how the driver behaves when bus discovery is done more than once
v3:
- align the interfaces of the U-Boot FF-A driver with those in the linux FF-A driver
- remove the FF-A helper layer
- make the U-Boot FF-A driver independent from EFI
- provide an optional config that enables copying the driver data to EFI runtime section at ExitBootServices service
- use 64-bit version of FFA_RXTX_MAP, FFA_MSG_SEND_DIRECT_{REQ, RESP}
v2:
- make FF-A bus discoverable using device_{bind, probe} APIs
- remove device tree support
v1:
- introduce FF-A bus driver with device tree support
MAINTAINERS | 7 + doc/arch/arm64.ffa.rst | 218 ++++ doc/arch/index.rst | 1 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/firmware/arm-ffa/Kconfig | 30 + drivers/firmware/arm-ffa/Makefile | 6 + drivers/firmware/arm-ffa/arm-ffa-uclass.c | 16 + drivers/firmware/arm-ffa/arm_ffa_prv.h | 200 ++++ drivers/firmware/arm-ffa/core.c | 1315 +++++++++++++++++++++ include/arm_ffa.h | 97 ++ include/dm/uclass-id.h | 4 + 12 files changed, 1897 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_prv.h create mode 100644 drivers/firmware/arm-ffa/core.c create mode 100644 include/arm_ffa.h
This looks mostly OK to me. I have a few comments below. [..]
diff --git a/drivers/firmware/arm-ffa/arm_ffa_prv.h b/drivers/firmware/arm-ffa/arm_ffa_prv.h new file mode 100644 index 0000000000..4eea7dc036 --- /dev/null +++ b/drivers/firmware/arm-ffa/arm_ffa_prv.h @@ -0,0 +1,200 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#ifndef __ARM_FFA_PRV_H +#define __ARM_FFA_PRV_H
+#include <arm_ffa.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/arm-smccc.h>
+/*
- This header is private. It is exclusively used by the FF-A driver
- */
/* ...*/
is the single-line comment style
+/* FF-A core driver name */ +#define FFA_DRV_NAME "arm_ffa"
+/* 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,
Can you use lower-case hex consistently?
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*/
not: spaces before */
+};
+enum ffa_abi_errcode {
NOT_SUPPORTED = 1,
INVALID_PARAMETERS,
NO_MEMORY,
BUSY,
INTERRUPTED,
DENIED,
RETRY,
ABORTED,
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)
+/* The FF-A SMC function definitions */
+typedef struct arm_smccc_1_2_regs ffa_value_t; +typedef void (*invoke_ffa_fn_t)(ffa_value_t args, ffa_value_t *res);
that needs a comment
[..]
+/**
- struct ffa_rxtxpair - structure hosting 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
- Data structure hosting the virtual addresses of the mapped RX/TX buffers
Just a nit but it catches my eye. We know this is a struct. You can just say "Hosts the ..."
[..]
diff --git a/drivers/firmware/arm-ffa/core.c b/drivers/firmware/arm-ffa/core.c new file mode 100644 index 0000000000..0b1f8e6a07 --- /dev/null +++ b/drivers/firmware/arm-ffa/core.c @@ -0,0 +1,1315 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include "arm_ffa_prv.h" +#include <asm/global_data.h> +#include <common.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <dm/devres.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> +#include <log.h> +#include <malloc.h> +#include <string.h> +#include <uuid.h>
See this:
https://u-boot.readthedocs.io/en/latest/develop/codingstyle.html?highlight=s...
[..]
+/*
- Driver core functions
- */
+/**
- ffa_remove_device - removes the arm_ffa device
- @dev: the device to be removed
- This function makes sure the arm_ffa device is removed
- No need to free the kmalloced data when the device is destroyed.
- It's automatically done by devm management by
- device_remove() -> device_free() -> devres_release_probe().
- Return:
- 0 on success. Otherwise, failure
- */
+int ffa_remove_device(struct udevice *dev) +{
int ret;
if (!dev) {
ffa_err("no udevice found");
return -ENODEV;
}
ret = device_remove(dev, DM_REMOVE_NORMAL);
if (ret) {
ffa_err("unable to remove. err:%d\n", ret);
return ret;
}
ffa_info("device removed and freed");
ret = device_unbind(dev);
if (ret) {
ffa_err("unable to unbind. err:%d\n", ret);
return ret;
}
ffa_info("device unbound");
return 0;
+}
Do we need this function? We should not be unbinding devices. Even removing them should be done elsewhere if needed. But why?
+/**
- ffa_device_get - create, bind and probe the arm_ffa device
- @pdev: the address of a device pointer (to be filled when the arm_ffa bus device is created
successfully)
- This function makes sure the arm_ffa device is
- created, bound to this driver, probed and ready to use.
- Arm FF-A transport is implemented through a single U-Boot
- device managing the FF-A bus (arm_ffa).
- Return:
- 0 on success. Otherwise, failure
- */
+int ffa_device_get(struct udevice **pdev) +{
int ret;
struct udevice *dev = NULL;
ret = device_bind(dm_root(), DM_DRIVER_GET(arm_ffa), FFA_DRV_NAME, NULL, ofnode_null(),
&dev);
Please add a DT binding. Even if only temporary, we need something for this.
Thanks for the feedback. I'm happy to address all the comments.
Regarding DT binding and FF-A discovery. We agreed with Linaro and Rob Herring about the following:
- DT is only for what we failed to make discoverable. For hardware, we're stuck with it. We shouldn't repeat that for software interfaces. This approach is already applied in the FF-A kernel driver which comes with no DT support and discovers the bus with bus_register() API [1].
- FF-A bus in U-Boot is used on demand by clients. We don't want to set it up if no client is using it. For example, the EFI MM client tries to discover the FF-A bus, if it succeeds it uses it. Otherwise, it uses OP-TEE protocol [2]. EFI MM client tries to discover the FF-A bus by calling ffa_bus_discover() which is a discovery API provided by the FF-A core driver.
Here is an overview about what ffa_bus_discover() does :
client -> ffa_bus_discover() -> ffa_device_get() -> { device_bind() , device_probe() }
device_probe() -> ffa_probe() -> { at this stage the FF-A driver tries to discover the bus by executing FF-A discovery ABIs }
Let's see what Ilias and Rob think about FF-A discovery and DT support.
Cheers Abdellatif
[1]: https://elixir.bootlin.com/linux/v6.1-rc4/source/drivers/firmware/arm_ffa/bu... [2]: https://lore.kernel.org/all/20221122131751.22747-10-abdellatif.elkhlifi@arm....
[..]
+static int ffa_get_version(void) +{
u16 major, minor;
ffa_value_t res = {0};
int ffa_errno;
ffa_priv_data->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);
ffa_info("FF-A driver %d.%d\nFF-A framework %d.%d",
FFA_MAJOR_VERSION, FFA_MINOR_VERSION, major, minor);
if ((major == FFA_MAJOR_VERSION && minor >= FFA_MINOR_VERSION)) {
nit: Drop extra brackets
ffa_info("Versions are compatible ");
ffa_priv_data->fwk_version = res.a0;
return 0;
}
ffa_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
I believe these should have () at the end, so:
ffa_get_endpoint_id() - FFA_ID_GET handler function
- This function implements FFA_ID_GET FF-A function
- to get from the secure world u-boot endpoint ID
- Return:
- 0 on success. Otherwise, failure
- */
[..]
+/**
- ffa_free_rxtx_buffers - frees the RX/TX buffers
- This function frees the RX/TX buffers
- */
+static void ffa_free_rxtx_buffers(void) +{
ffa_info("Freeing RX/TX buffers");
if (ffa_priv_data->pair.rxbuf) {
free((void *)ffa_priv_data->pair.rxbuf);
You can't cast an address to a pointer in this way. Should use map_sysmem() or store a pointer.
ffa_priv_data->pair.rxbuf = 0;
}
if (ffa_priv_data->pair.txbuf) {
free((void *)ffa_priv_data->pair.txbuf);
ffa_priv_data->pair.txbuf = 0;
}
+} +[..]
/*
* make sure the buffers are cleared before use
*/
memset((void *)ffa_priv_data->pair.rxbuf, 0, bytes);
memset((void *)ffa_priv_data->pair.txbuf, 0, bytes);
Yes these should be pointers everywhere, since you are casting all the time.
return 0;
+}
[..]
+/**
- ffa_read_partitions_info - reads the data queried by FFA_PARTITION_INFO_GET
and saves it in the private structure
Can you fit all these titles on one line, like:
ffa_read_partitions_info() - Read partition information
<insert longer description here?>
- @count: The number of partitions queried
- @part_uuid: Pointer to the partition(s) UUID
- This function reads the partitions information
- returned by the FFA_PARTITION_INFO_GET and saves it in the private
- data structure.
- Return:
- The private data structure is updated with the partition(s) information
- 0 is returned on success. Otherwise, failure
- */
[..] [..]
+static int ffa_msg_send_direct_req(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;
if (!ffa_priv_data || !ffa_priv_data->invoke_ffa_fn)
return -EINVAL;
ffa_priv_data should be called 'priv' and attached to the device with device_get_priv() [..]
+/**
- ffa_probe - The driver probe function
- @dev: the arm_ffa device
- Probing is done at boot time and triggered by the uclass device discovery.
- At probe level the following actions are done:
- setting the conduit
- querying the FF-A framework version
- 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 the resident private data structure.
- The probe will fail if either FF-A framework is not detected or the
- FF-A requests are not behaving correctly. This ensures that the
- driver is not installed and its operations are not exported to the clients.
- Return:
- 0 on success. Otherwise, failure
- */
+static int ffa_probe(struct udevice *dev) +{
int ret;
ret = ffa_alloc_prvdata(dev);
don't do this, see above.
[..]
diff --git a/include/arm_ffa.h b/include/arm_ffa.h new file mode 100644 index 0000000000..74b16174c2 --- /dev/null +++ b/include/arm_ffa.h @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#ifndef __ARM_FFA_H +#define __ARM_FFA_H
+#include <linux/printk.h>
+/*
- This header is public. It can be used by clients to access
- data structures and definitions they need
- */
+/*
- Macros for displaying logs
- */
+#define ffa_info(fmt, ...) pr_info("[FFA] " fmt "\n", ##__VA_ARGS__) +#define ffa_err(fmt, ...) pr_err("[FFA] " fmt "\n", ##__VA_ARGS__)
You could use log_info(), log_err()
+/*
- 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_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 {
unsigned long data0; /* w3/x3 */
unsigned long data1; /* w4/x4 */
unsigned long data2; /* w5/x5 */
unsigned long data3; /* w6/x6 */
unsigned long data4; /* w7/x7 */
+};
+struct udevice;
+/**
- struct ffa_bus_ops - The driver operations structure
- @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_info *buffer);
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);
+};
Shouldn't this be the .ops member in your driver?
+/**
- The device driver and the Uclass driver public functions
- */
+/**
- ffa_bus_ops_get - driver operations getter
- */
+const struct ffa_bus_ops *ffa_bus_ops_get(void);
See how this is done in other uclasses, e.g. spi_get_ops()
Regards, SImon

Hi Abdellatif,
On Thu, 24 Nov 2022 at 06:21, Abdellatif El Khlifi < abdellatif.elkhlifi@arm.com> wrote:
On Tue, Nov 22, 2022 at 07:09:16PM -0700, Simon Glass wrote:
should be called 'priov' and should beHi Abdellatif,
[..]
+/**
- ffa_device_get - create, bind and probe the arm_ffa device
- @pdev: the address of a device pointer (to be filled when the
arm_ffa bus device is created
successfully)
- This function makes sure the arm_ffa device is
- created, bound to this driver, probed and ready to use.
- Arm FF-A transport is implemented through a single U-Boot
- device managing the FF-A bus (arm_ffa).
- Return:
- 0 on success. Otherwise, failure
- */
+int ffa_device_get(struct udevice **pdev) +{
int ret;
struct udevice *dev = NULL;
ret = device_bind(dm_root(), DM_DRIVER_GET(arm_ffa),
FFA_DRV_NAME, NULL, ofnode_null(),
&dev);
Please add a DT binding. Even if only temporary, we need something for
this.
Thanks for the feedback. I'm happy to address all the comments.
Regarding DT binding and FF-A discovery. We agreed with Linaro and Rob
Herring
about the following:
- DT is only for what we failed to make discoverable. For hardware, we're
stuck
with it. We shouldn't repeat that for software interfaces. This
approach is
already applied in the FF-A kernel driver which comes with no DT
support and
discovers the bus with bus_register() API [1].
This may be the UEFI view, but it is not how U-Boot works. This is not something we are 'stuck' with. It is how we define what is present on a device. This is how the PCI bus works in U-Boot. It is best practice in U-Boot to use the device tree to make this things visible and configurable. Unlike with Linux there is no other way to provide configuration needed by these devices.
- FF-A bus in U-Boot is used on demand by clients. We don't want to set
it up
if no client is using it. For example, the EFI MM client tries to
discover the
FF-A bus, if it succeeds it uses it. Otherwise, it uses OP-TEE protocol
[2].
EFI MM client tries to discover the FF-A bus by calling
ffa_bus_discover()
which is a discovery API provided by the FF-A core driver.
You are talking about probing, I think. Lazy init is build into U-Boot and works fine. We don't need to invent another way to do it. Probe the device if it is needed, but bind it always.
Here is an overview about what ffa_bus_discover() does :
client -> ffa_bus_discover() -> ffa_device_get() -> { device_bind() ,
device_probe() }
device_probe() -> ffa_probe() -> { at this stage the FF-A driver tries to
discover the bus by executing FF-A discovery ABIs }
Yes it needs a bit of clean-up, to be honest. You need to add a device pointer to the calls as well.
Let's see what Ilias and Rob think about FF-A discovery and DT support.
Cheers Abdellatif
[1]:
https://elixir.bootlin.com/linux/v6.1-rc4/source/drivers/firmware/arm_ffa/bu...
[2]:
https://lore.kernel.org/all/20221122131751.22747-10-abdellatif.elkhlifi@arm....
Regards, SImon

On Fri, Nov 25, 2022 at 3:18 PM Simon Glass sjg@chromium.org wrote:
Hi Abdellatif,
On Thu, 24 Nov 2022 at 06:21, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
On Tue, Nov 22, 2022 at 07:09:16PM -0700, Simon Glass wrote:
should be called 'priov' and should beHi Abdellatif,
[..]
+/**
- ffa_device_get - create, bind and probe the arm_ffa device
- @pdev: the address of a device pointer (to be filled when the arm_ffa bus device is created
successfully)
- This function makes sure the arm_ffa device is
- created, bound to this driver, probed and ready to use.
- Arm FF-A transport is implemented through a single U-Boot
- device managing the FF-A bus (arm_ffa).
- Return:
- 0 on success. Otherwise, failure
- */
+int ffa_device_get(struct udevice **pdev) +{
int ret;
struct udevice *dev = NULL;
ret = device_bind(dm_root(), DM_DRIVER_GET(arm_ffa), FFA_DRV_NAME, NULL, ofnode_null(),
&dev);
Please add a DT binding. Even if only temporary, we need something for this.
Thanks for the feedback. I'm happy to address all the comments.
Regarding DT binding and FF-A discovery. We agreed with Linaro and Rob Herring about the following:
- DT is only for what we failed to make discoverable. For hardware, we're stuck with it. We shouldn't repeat that for software interfaces. This approach is already applied in the FF-A kernel driver which comes with no DT support and discovers the bus with bus_register() API [1].
This may be the UEFI view, but it is not how U-Boot works. This is not something we are 'stuck' with. It is how we define what is present on a device. This is how the PCI bus works in U-Boot. It is best practice in U-Boot to use the device tree to make this things visible and configurable. Unlike with Linux there is no other way to provide configuration needed by these devices.
Where do you get UEFI out of this?
It is the discoverability of hardware that is fixed (and we are stuck with). We can't change hardware. The disoverability may be PCI VID/PID, USB device descriptors, or nothing. We only use DT when those are not sufficient. For a software interface, there is no reason to make them non-discoverable as the interface can be fixed (at least for new things like FF-A).
Rob

Hi all
On Mon, 28 Nov 2022 at 18:22, Rob Herring robh@kernel.org wrote:
On Fri, Nov 25, 2022 at 3:18 PM Simon Glass sjg@chromium.org wrote:
Hi Abdellatif,
On Thu, 24 Nov 2022 at 06:21, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
On Tue, Nov 22, 2022 at 07:09:16PM -0700, Simon Glass wrote:
should be called 'priov' and should beHi Abdellatif,
[..]
+/**
- ffa_device_get - create, bind and probe the arm_ffa device
- @pdev: the address of a device pointer (to be filled when the arm_ffa bus device is created
successfully)
- This function makes sure the arm_ffa device is
- created, bound to this driver, probed and ready to use.
- Arm FF-A transport is implemented through a single U-Boot
- device managing the FF-A bus (arm_ffa).
- Return:
- 0 on success. Otherwise, failure
- */
+int ffa_device_get(struct udevice **pdev) +{
int ret;
struct udevice *dev = NULL;
ret = device_bind(dm_root(), DM_DRIVER_GET(arm_ffa), FFA_DRV_NAME, NULL, ofnode_null(),
&dev);
Please add a DT binding. Even if only temporary, we need something for this.
Thanks for the feedback. I'm happy to address all the comments.
Regarding DT binding and FF-A discovery. We agreed with Linaro and Rob Herring about the following:
- DT is only for what we failed to make discoverable. For hardware, we're stuck with it. We shouldn't repeat that for software interfaces. This approach is already applied in the FF-A kernel driver which comes with no DT support and discovers the bus with bus_register() API [1].
This may be the UEFI view, but it is not how U-Boot works. This is not something we are 'stuck' with. It is how we define what is present on a device. This is how the PCI bus works in U-Boot. It is best practice in U-Boot to use the device tree to make this things visible and configurable. Unlike with Linux there is no other way to provide configuration needed by these devices.
Where do you get UEFI out of this?
It is the discoverability of hardware that is fixed (and we are stuck with). We can't change hardware. The disoverability may be PCI VID/PID, USB device descriptors, or nothing. We only use DT when those are not sufficient. For a software interface, there is no reason to make them non-discoverable as the interface can be fixed (at least for new things like FF-A).
I'll agree with Rob here. In fact the first version of the patchset *did* have this as a DT node. We explicitly asked Abdellatif to change this, so u-boot and the linux kernel can have an identical approach in discovering FF-A
Regards /Ilias
Rob

Hi Rob,
On Tue, 29 Nov 2022 at 05:22, Rob Herring robh@kernel.org wrote:
On Fri, Nov 25, 2022 at 3:18 PM Simon Glass sjg@chromium.org wrote:
Hi Abdellatif,
On Thu, 24 Nov 2022 at 06:21, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
On Tue, Nov 22, 2022 at 07:09:16PM -0700, Simon Glass wrote:
should be called 'priov' and should beHi Abdellatif,
[..]
+/**
- ffa_device_get - create, bind and probe the arm_ffa device
- @pdev: the address of a device pointer (to be filled when the arm_ffa bus device is created
successfully)
- This function makes sure the arm_ffa device is
- created, bound to this driver, probed and ready to use.
- Arm FF-A transport is implemented through a single U-Boot
- device managing the FF-A bus (arm_ffa).
- Return:
- 0 on success. Otherwise, failure
- */
+int ffa_device_get(struct udevice **pdev) +{
int ret;
struct udevice *dev = NULL;
ret = device_bind(dm_root(), DM_DRIVER_GET(arm_ffa), FFA_DRV_NAME, NULL, ofnode_null(),
&dev);
Please add a DT binding. Even if only temporary, we need something for this.
Thanks for the feedback. I'm happy to address all the comments.
Regarding DT binding and FF-A discovery. We agreed with Linaro and Rob Herring about the following:
- DT is only for what we failed to make discoverable. For hardware, we're stuck with it. We shouldn't repeat that for software interfaces. This approach is already applied in the FF-A kernel driver which comes with no DT support and discovers the bus with bus_register() API [1].
This may be the UEFI view, but it is not how U-Boot works. This is not something we are 'stuck' with. It is how we define what is present on a device. This is how the PCI bus works in U-Boot. It is best practice in U-Boot to use the device tree to make this things visible and configurable. Unlike with Linux there is no other way to provide configuration needed by these devices.
Where do you get UEFI out of this?
I assume it was UEFI as there was no discussion about this in U-Boot. Which firmware project was consulted about this?
It is the discoverability of hardware that is fixed (and we are stuck with). We can't change hardware. The disoverability may be PCI VID/PID, USB device descriptors, or nothing. We only use DT when those are not sufficient. For a software interface, there is no reason to make them non-discoverable as the interface can be fixed (at least for new things like FF-A).
Here I am talking about the controller itself, the top-level node in the device tree. For PCI this is a device tree node and it should be the same here. So I am not saying that the devices on the bus need to be in the device tree (that can be optional, but may be useful in some situations where it is status and known). We need something like:
ff-a { compatible = "something"; };
I don't know what mechanism is actually used to communicate with it, but that will be enough to get the top-level driver started.
If Linux does not want to use the node, that it another thing, but I respectfully request that U-Boot's needs be considered more carefully. I'd also like to see more willingness to accommodate open-source software in these designs.
Regards, Simon

On Sun, Dec 4, 2022 at 1:22 PM Simon Glass sjg@chromium.org wrote:
Hi Rob,
On Tue, 29 Nov 2022 at 05:22, Rob Herring robh@kernel.org wrote:
On Fri, Nov 25, 2022 at 3:18 PM Simon Glass sjg@chromium.org wrote:
Hi Abdellatif,
On Thu, 24 Nov 2022 at 06:21, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
On Tue, Nov 22, 2022 at 07:09:16PM -0700, Simon Glass wrote:
should be called 'priov' and should beHi Abdellatif,
[..]
+/**
- ffa_device_get - create, bind and probe the arm_ffa device
- @pdev: the address of a device pointer (to be filled when the arm_ffa bus device is created
successfully)
- This function makes sure the arm_ffa device is
- created, bound to this driver, probed and ready to use.
- Arm FF-A transport is implemented through a single U-Boot
- device managing the FF-A bus (arm_ffa).
- Return:
- 0 on success. Otherwise, failure
- */
+int ffa_device_get(struct udevice **pdev) +{
int ret;
struct udevice *dev = NULL;
ret = device_bind(dm_root(), DM_DRIVER_GET(arm_ffa), FFA_DRV_NAME, NULL, ofnode_null(),
&dev);
Please add a DT binding. Even if only temporary, we need something for this.
Thanks for the feedback. I'm happy to address all the comments.
Regarding DT binding and FF-A discovery. We agreed with Linaro and Rob Herring about the following:
- DT is only for what we failed to make discoverable. For hardware, we're stuck with it. We shouldn't repeat that for software interfaces. This approach is already applied in the FF-A kernel driver which comes with no DT support and discovers the bus with bus_register() API [1].
This may be the UEFI view, but it is not how U-Boot works. This is not something we are 'stuck' with. It is how we define what is present on a device. This is how the PCI bus works in U-Boot. It is best practice in U-Boot to use the device tree to make this things visible and configurable. Unlike with Linux there is no other way to provide configuration needed by these devices.
Where do you get UEFI out of this?
I assume it was UEFI as there was no discussion about this in U-Boot. Which firmware project was consulted about this?
It is the discoverability of hardware that is fixed (and we are stuck with). We can't change hardware. The disoverability may be PCI VID/PID, USB device descriptors, or nothing. We only use DT when those are not sufficient. For a software interface, there is no reason to make them non-discoverable as the interface can be fixed (at least for new things like FF-A).
Here I am talking about the controller itself, the top-level node in the device tree. For PCI this is a device tree node and it should be the same here. So I am not saying that the devices on the bus need to be in the device tree (that can be optional, but may be useful in some situations where it is status and known).
Sure, the PCI host bridges are not discoverable, have a bunch of resources, and do need to be in DT. The downstream devices only do if they have extra resources such as when a device is soldered down on a board rather than a standard slot.
We need something like:
ff-a { compatible = "something"; };
I don't know what mechanism is actually used to communicate with it, but that will be enough to get the top-level driver started.
There's discovery of FF-A itself and then discovery of FF-A features (e.g. partitions). Both of those are discoverable without DT. The first is done by checking the SMCCC version, then checking for FF-A presence and features. Putting this into DT is redundant. Worse, what if they disagree?
If Linux does not want to use the node, that it another thing, but I respectfully request that U-Boot's needs be considered more carefully.
It's not really a big deal for just one compatible. It's the next 10 firmware things Arm comes up with. Or the let's add one property at a time binding (mis)design that happens once we have a binding.
I'd also like to see more willingness to accommodate open-source software in these designs.
I'm not sure what you are asking for here. Are you talking about FF-A and Arm firmware interfaces itself, the DT binding for it, or something else? I'd agree on the first part. I only saw this when the binding landed on my plate. For bindings themselves, the firehose is there. I can't pick out what you or others care and don't care about. I try to steer common things to the devicetree-spec list, but there's not that many things that come up really.
Rob

On Mon, Dec 05, 2022 at 09:49:30AM -0600, Rob Herring wrote:
On Sun, Dec 4, 2022 at 1:22 PM Simon Glass sjg@chromium.org wrote:
Hi Rob,
On Tue, 29 Nov 2022 at 05:22, Rob Herring robh@kernel.org wrote:
On Fri, Nov 25, 2022 at 3:18 PM Simon Glass sjg@chromium.org wrote:
Hi Abdellatif,
On Thu, 24 Nov 2022 at 06:21, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
On Tue, Nov 22, 2022 at 07:09:16PM -0700, Simon Glass wrote:
should be called 'priov' and should beHi Abdellatif,
[..]
> +/** > + * ffa_device_get - create, bind and probe the arm_ffa device > + * @pdev: the address of a device pointer (to be filled when the arm_ffa bus device is created > + * successfully) > + * > + * This function makes sure the arm_ffa device is > + * created, bound to this driver, probed and ready to use. > + * Arm FF-A transport is implemented through a single U-Boot > + * device managing the FF-A bus (arm_ffa). > + * > + * Return: > + * > + * 0 on success. Otherwise, failure > + */ > +int ffa_device_get(struct udevice **pdev) > +{ > + int ret; > + struct udevice *dev = NULL; > + > + ret = device_bind(dm_root(), DM_DRIVER_GET(arm_ffa), FFA_DRV_NAME, NULL, ofnode_null(), > + &dev);
Please add a DT binding. Even if only temporary, we need something for this.
Thanks for the feedback. I'm happy to address all the comments.
Regarding DT binding and FF-A discovery. We agreed with Linaro and Rob Herring about the following:
- DT is only for what we failed to make discoverable. For hardware, we're stuck with it. We shouldn't repeat that for software interfaces. This approach is already applied in the FF-A kernel driver which comes with no DT support and discovers the bus with bus_register() API [1].
This may be the UEFI view, but it is not how U-Boot works. This is not something we are 'stuck' with. It is how we define what is present on a device. This is how the PCI bus works in U-Boot. It is best practice in U-Boot to use the device tree to make this things visible and configurable. Unlike with Linux there is no other way to provide configuration needed by these devices.
Where do you get UEFI out of this?
I assume it was UEFI as there was no discussion about this in U-Boot. Which firmware project was consulted about this?
It is the discoverability of hardware that is fixed (and we are stuck with). We can't change hardware. The disoverability may be PCI VID/PID, USB device descriptors, or nothing. We only use DT when those are not sufficient. For a software interface, there is no reason to make them non-discoverable as the interface can be fixed (at least for new things like FF-A).
Here I am talking about the controller itself, the top-level node in the device tree. For PCI this is a device tree node and it should be the same here. So I am not saying that the devices on the bus need to be in the device tree (that can be optional, but may be useful in some situations where it is status and known).
Sure, the PCI host bridges are not discoverable, have a bunch of resources, and do need to be in DT. The downstream devices only do if they have extra resources such as when a device is soldered down on a board rather than a standard slot.
We need something like:
ff-a { compatible = "something"; };
I don't know what mechanism is actually used to communicate with it, but that will be enough to get the top-level driver started.
There's discovery of FF-A itself and then discovery of FF-A features (e.g. partitions). Both of those are discoverable without DT. The first is done by checking the SMCCC version, then checking for FF-A presence and features. Putting this into DT is redundant. Worse, what if they disagree?
Hi Simon,
Do you agree with Rob, Ilias and myself that it makes more sense FF-A bus is discovered without a DT node and following the same approach as Linux ? (FF-A bus doesn't have a HW controller and is a purely SW bus, no configuration/description needed at DT level).
Your suggestions are always welcome.
cheers
If Linux does not want to use the node, that it another thing, but I respectfully request that U-Boot's needs be considered more carefully.
It's not really a big deal for just one compatible. It's the next 10 firmware things Arm comes up with. Or the let's add one property at a time binding (mis)design that happens once we have a binding.
I'd also like to see more willingness to accommodate open-source software in these designs.
I'm not sure what you are asking for here. Are you talking about FF-A and Arm firmware interfaces itself, the DT binding for it, or something else? I'd agree on the first part. I only saw this when the binding landed on my plate. For bindings themselves, the firehose is there. I can't pick out what you or others care and don't care about. I try to steer common things to the devicetree-spec list, but there's not that many things that come up really.
Rob

Hi Abdellatif,
On Mon, 19 Dec 2022 at 04:12, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
On Mon, Dec 05, 2022 at 09:49:30AM -0600, Rob Herring wrote:
On Sun, Dec 4, 2022 at 1:22 PM Simon Glass sjg@chromium.org wrote:
Hi Rob,
On Tue, 29 Nov 2022 at 05:22, Rob Herring robh@kernel.org wrote:
On Fri, Nov 25, 2022 at 3:18 PM Simon Glass sjg@chromium.org wrote:
Hi Abdellatif,
On Thu, 24 Nov 2022 at 06:21, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
On Tue, Nov 22, 2022 at 07:09:16PM -0700, Simon Glass wrote: > should be called 'priov' and should beHi Abdellatif, >
[..]
> > +/** > > + * ffa_device_get - create, bind and probe the arm_ffa device > > + * @pdev: the address of a device pointer (to be filled when the arm_ffa bus device is created > > + * successfully) > > + * > > + * This function makes sure the arm_ffa device is > > + * created, bound to this driver, probed and ready to use. > > + * Arm FF-A transport is implemented through a single U-Boot > > + * device managing the FF-A bus (arm_ffa). > > + * > > + * Return: > > + * > > + * 0 on success. Otherwise, failure > > + */ > > +int ffa_device_get(struct udevice **pdev) > > +{ > > + int ret; > > + struct udevice *dev = NULL; > > + > > + ret = device_bind(dm_root(), DM_DRIVER_GET(arm_ffa), FFA_DRV_NAME, NULL, ofnode_null(), > > + &dev); > > Please add a DT binding. Even if only temporary, we need something for this.
Thanks for the feedback. I'm happy to address all the comments.
Regarding DT binding and FF-A discovery. We agreed with Linaro and Rob Herring about the following:
- DT is only for what we failed to make discoverable. For hardware, we're stuck with it. We shouldn't repeat that for software interfaces. This approach is already applied in the FF-A kernel driver which comes with no DT support and discovers the bus with bus_register() API [1].
This may be the UEFI view, but it is not how U-Boot works. This is not something we are 'stuck' with. It is how we define what is present on a device. This is how the PCI bus works in U-Boot. It is best practice in U-Boot to use the device tree to make this things visible and configurable. Unlike with Linux there is no other way to provide configuration needed by these devices.
Where do you get UEFI out of this?
I assume it was UEFI as there was no discussion about this in U-Boot. Which firmware project was consulted about this?
It is the discoverability of hardware that is fixed (and we are stuck with). We can't change hardware. The disoverability may be PCI VID/PID, USB device descriptors, or nothing. We only use DT when those are not sufficient. For a software interface, there is no reason to make them non-discoverable as the interface can be fixed (at least for new things like FF-A).
Here I am talking about the controller itself, the top-level node in the device tree. For PCI this is a device tree node and it should be the same here. So I am not saying that the devices on the bus need to be in the device tree (that can be optional, but may be useful in some situations where it is status and known).
Sure, the PCI host bridges are not discoverable, have a bunch of resources, and do need to be in DT. The downstream devices only do if they have extra resources such as when a device is soldered down on a board rather than a standard slot.
We need something like:
ff-a { compatible = "something"; };
I don't know what mechanism is actually used to communicate with it, but that will be enough to get the top-level driver started.
There's discovery of FF-A itself and then discovery of FF-A features (e.g. partitions). Both of those are discoverable without DT. The first is done by checking the SMCCC version, then checking for FF-A presence and features. Putting this into DT is redundant. Worse, what if they disagree?
Hi Simon,
Do you agree with Rob, Ilias and myself that it makes more sense FF-A bus is discovered without a DT node and following the same approach as Linux ? (FF-A bus doesn't have a HW controller and is a purely SW bus, no configuration/description needed at DT level).
Your suggestions are always welcome.
I'm sorry I don't agree with that. It does need a compatible string, like PCI has. You can just add it in U-Boot if Linux won't accept the binding.
Rob, if you are here I think the U-Boot DM bindings seem to have stalled. What is needed to get these in?
Regards, Simon

On Mon, Dec 19, 2022 at 1:21 PM Simon Glass sjg@chromium.org wrote:
Hi Abdellatif,
On Mon, 19 Dec 2022 at 04:12, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
On Mon, Dec 05, 2022 at 09:49:30AM -0600, Rob Herring wrote:
On Sun, Dec 4, 2022 at 1:22 PM Simon Glass sjg@chromium.org wrote:
Hi Rob,
On Tue, 29 Nov 2022 at 05:22, Rob Herring robh@kernel.org wrote:
On Fri, Nov 25, 2022 at 3:18 PM Simon Glass sjg@chromium.org wrote:
Hi Abdellatif,
On Thu, 24 Nov 2022 at 06:21, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote: > > On Tue, Nov 22, 2022 at 07:09:16PM -0700, Simon Glass wrote: > > should be called 'priov' and should beHi Abdellatif, > >
[..]
> > > +/** > > > + * ffa_device_get - create, bind and probe the arm_ffa device > > > + * @pdev: the address of a device pointer (to be filled when the arm_ffa bus device is created > > > + * successfully) > > > + * > > > + * This function makes sure the arm_ffa device is > > > + * created, bound to this driver, probed and ready to use. > > > + * Arm FF-A transport is implemented through a single U-Boot > > > + * device managing the FF-A bus (arm_ffa). > > > + * > > > + * Return: > > > + * > > > + * 0 on success. Otherwise, failure > > > + */ > > > +int ffa_device_get(struct udevice **pdev) > > > +{ > > > + int ret; > > > + struct udevice *dev = NULL; > > > + > > > + ret = device_bind(dm_root(), DM_DRIVER_GET(arm_ffa), FFA_DRV_NAME, NULL, ofnode_null(), > > > + &dev); > > > > Please add a DT binding. Even if only temporary, we need something for this. > > Thanks for the feedback. I'm happy to address all the comments. > > Regarding DT binding and FF-A discovery. We agreed with Linaro and Rob Herring > about the following: > > - DT is only for what we failed to make discoverable. For hardware, we're stuck > with it. We shouldn't repeat that for software interfaces. This approach is > already applied in the FF-A kernel driver which comes with no DT support and > discovers the bus with bus_register() API [1].
This may be the UEFI view, but it is not how U-Boot works. This is not something we are 'stuck' with. It is how we define what is present on a device. This is how the PCI bus works in U-Boot. It is best practice in U-Boot to use the device tree to make this things visible and configurable. Unlike with Linux there is no other way to provide configuration needed by these devices.
Where do you get UEFI out of this?
I assume it was UEFI as there was no discussion about this in U-Boot. Which firmware project was consulted about this?
It is the discoverability of hardware that is fixed (and we are stuck with). We can't change hardware. The disoverability may be PCI VID/PID, USB device descriptors, or nothing. We only use DT when those are not sufficient. For a software interface, there is no reason to make them non-discoverable as the interface can be fixed (at least for new things like FF-A).
Here I am talking about the controller itself, the top-level node in the device tree. For PCI this is a device tree node and it should be the same here. So I am not saying that the devices on the bus need to be in the device tree (that can be optional, but may be useful in some situations where it is status and known).
Sure, the PCI host bridges are not discoverable, have a bunch of resources, and do need to be in DT. The downstream devices only do if they have extra resources such as when a device is soldered down on a board rather than a standard slot.
We need something like:
ff-a { compatible = "something"; };
I don't know what mechanism is actually used to communicate with it, but that will be enough to get the top-level driver started.
There's discovery of FF-A itself and then discovery of FF-A features (e.g. partitions). Both of those are discoverable without DT. The first is done by checking the SMCCC version, then checking for FF-A presence and features. Putting this into DT is redundant. Worse, what if they disagree?
Hi Simon,
Do you agree with Rob, Ilias and myself that it makes more sense FF-A bus is discovered without a DT node and following the same approach as Linux ? (FF-A bus doesn't have a HW controller and is a purely SW bus, no configuration/description needed at DT level).
Your suggestions are always welcome.
I'm sorry I don't agree with that. It does need a compatible string, like PCI has. You can just add it in U-Boot if Linux won't accept the binding.
It's not like PCI as the host side of PCI has non-discoverable resources.
This all could have been designed better, but hindsight is 20/20 and things evolved step by step. There are a bunch of firmware services that are all behind SMCCC. The first (upstream) was PSCI. IIRC, SMCCC was invented a bit after that, but generalized PSCI for other services. Since then more have been added. More services get added one by one and yes we added bindings for them. Because what's one more... But that really needs to stop. We're stuck with h/w that's not discoverable, there's zero reason to do that with s/w interfaces. If we could redo everything, we'd have a node for SMCCC and that's it unless there's h/w resources provided to the rest of DT. But we can't, so SMCCC is discovered by the presence of PSCI.
Rob

Hi Rob,
On Wed, 11 Jan 2023 at 19:10, Rob Herring robh@kernel.org wrote:
On Mon, Dec 19, 2022 at 1:21 PM Simon Glass sjg@chromium.org wrote:
Hi Abdellatif,
On Mon, 19 Dec 2022 at 04:12, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
On Mon, Dec 05, 2022 at 09:49:30AM -0600, Rob Herring wrote:
On Sun, Dec 4, 2022 at 1:22 PM Simon Glass sjg@chromium.org wrote:
Hi Rob,
On Tue, 29 Nov 2022 at 05:22, Rob Herring robh@kernel.org wrote:
On Fri, Nov 25, 2022 at 3:18 PM Simon Glass sjg@chromium.org wrote: > > Hi Abdellatif, > > On Thu, 24 Nov 2022 at 06:21, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote: > > > > On Tue, Nov 22, 2022 at 07:09:16PM -0700, Simon Glass wrote: > > > should be called 'priov' and should beHi Abdellatif, > > > > > [..] > > > > > +/** > > > > + * ffa_device_get - create, bind and probe the arm_ffa device > > > > + * @pdev: the address of a device pointer (to be filled when the arm_ffa bus device is created > > > > + * successfully) > > > > + * > > > > + * This function makes sure the arm_ffa device is > > > > + * created, bound to this driver, probed and ready to use. > > > > + * Arm FF-A transport is implemented through a single U-Boot > > > > + * device managing the FF-A bus (arm_ffa). > > > > + * > > > > + * Return: > > > > + * > > > > + * 0 on success. Otherwise, failure > > > > + */ > > > > +int ffa_device_get(struct udevice **pdev) > > > > +{ > > > > + int ret; > > > > + struct udevice *dev = NULL; > > > > + > > > > + ret = device_bind(dm_root(), DM_DRIVER_GET(arm_ffa), FFA_DRV_NAME, NULL, ofnode_null(), > > > > + &dev); > > > > > > Please add a DT binding. Even if only temporary, we need something for this. > > > > Thanks for the feedback. I'm happy to address all the comments. > > > > Regarding DT binding and FF-A discovery. We agreed with Linaro and Rob Herring > > about the following: > > > > - DT is only for what we failed to make discoverable. For hardware, we're stuck > > with it. We shouldn't repeat that for software interfaces. This approach is > > already applied in the FF-A kernel driver which comes with no DT support and > > discovers the bus with bus_register() API [1]. > > This may be the UEFI view, but it is not how U-Boot works. This is not something we are 'stuck' with. It is how we define what is present on a device. This is how the PCI bus works in U-Boot. It is best practice in U-Boot to use the device tree to make this things visible and configurable. Unlike with Linux there is no other way to provide configuration needed by these devices.
Where do you get UEFI out of this?
I assume it was UEFI as there was no discussion about this in U-Boot. Which firmware project was consulted about this?
It is the discoverability of hardware that is fixed (and we are stuck with). We can't change hardware. The disoverability may be PCI VID/PID, USB device descriptors, or nothing. We only use DT when those are not sufficient. For a software interface, there is no reason to make them non-discoverable as the interface can be fixed (at least for new things like FF-A).
Here I am talking about the controller itself, the top-level node in the device tree. For PCI this is a device tree node and it should be the same here. So I am not saying that the devices on the bus need to be in the device tree (that can be optional, but may be useful in some situations where it is status and known).
Sure, the PCI host bridges are not discoverable, have a bunch of resources, and do need to be in DT. The downstream devices only do if they have extra resources such as when a device is soldered down on a board rather than a standard slot.
We need something like:
ff-a { compatible = "something"; };
I don't know what mechanism is actually used to communicate with it, but that will be enough to get the top-level driver started.
There's discovery of FF-A itself and then discovery of FF-A features (e.g. partitions). Both of those are discoverable without DT. The first is done by checking the SMCCC version, then checking for FF-A presence and features. Putting this into DT is redundant. Worse, what if they disagree?
Hi Simon,
Do you agree with Rob, Ilias and myself that it makes more sense FF-A bus is discovered without a DT node and following the same approach as Linux ? (FF-A bus doesn't have a HW controller and is a purely SW bus, no configuration/description needed at DT level).
Your suggestions are always welcome.
I'm sorry I don't agree with that. It does need a compatible string, like PCI has. You can just add it in U-Boot if Linux won't accept the binding.
It's not like PCI as the host side of PCI has non-discoverable resources.
OK I see. It is certainly an edge case.
This all could have been designed better, but hindsight is 20/20 and things evolved step by step. There are a bunch of firmware services that are all behind SMCCC. The first (upstream) was PSCI. IIRC, SMCCC was invented a bit after that, but generalized PSCI for other services. Since then more have been added. More services get added one by one and yes we added bindings for them. Because what's one more... But that really needs to stop. We're stuck with h/w that's not discoverable, there's zero reason to do that with s/w interfaces. If we could redo everything, we'd have a node for SMCCC and that's it unless there's h/w resources provided to the rest of DT. But we can't, so SMCCC is discovered by the presence of PSCI.
I understand the background here, but if we don't take a stand on this, this sort of thing will continue. Just because something works in Linux does not mean that the binding (or lack of it) is good.
The reasons to do this are: - avoids needing to manually call device_bind() - avoids extra plumbing in U-Boot - provides visibility into what is in the system, by looking at the DT, like documentation - DT is how devices are bound in U-Boot
You can see the problem if you look at ffa_device_get(). It is called from ffa_bus_discover() which is a new addition into the board_init list. We are trying to remove this list and certainly don't want new things added!!
We don't need to change this in the Linux implementation, just add a top-level DT node for U-Boot. I don't understand why that is such a big problem?
Regards, Simon

On Thu, Jan 12, 2023 at 04:43:32PM -0700, Simon Glass wrote:
Hi Rob,
On Wed, 11 Jan 2023 at 19:10, Rob Herring robh@kernel.org wrote:
On Mon, Dec 19, 2022 at 1:21 PM Simon Glass sjg@chromium.org wrote:
Hi Abdellatif,
On Mon, 19 Dec 2022 at 04:12, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
On Mon, Dec 05, 2022 at 09:49:30AM -0600, Rob Herring wrote:
On Sun, Dec 4, 2022 at 1:22 PM Simon Glass sjg@chromium.org wrote:
Hi Rob,
On Tue, 29 Nov 2022 at 05:22, Rob Herring robh@kernel.org wrote: > > On Fri, Nov 25, 2022 at 3:18 PM Simon Glass sjg@chromium.org wrote: > > > > Hi Abdellatif, > > > > On Thu, 24 Nov 2022 at 06:21, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote: > > > > > > On Tue, Nov 22, 2022 at 07:09:16PM -0700, Simon Glass wrote: > > > > should be called 'priov' and should beHi Abdellatif, > > > > > > > > [..] > > > > > > > +/** > > > > > + * ffa_device_get - create, bind and probe the arm_ffa device > > > > > + * @pdev: the address of a device pointer (to be filled when the arm_ffa bus device is created > > > > > + * successfully) > > > > > + * > > > > > + * This function makes sure the arm_ffa device is > > > > > + * created, bound to this driver, probed and ready to use. > > > > > + * Arm FF-A transport is implemented through a single U-Boot > > > > > + * device managing the FF-A bus (arm_ffa). > > > > > + * > > > > > + * Return: > > > > > + * > > > > > + * 0 on success. Otherwise, failure > > > > > + */ > > > > > +int ffa_device_get(struct udevice **pdev) > > > > > +{ > > > > > + int ret; > > > > > + struct udevice *dev = NULL; > > > > > + > > > > > + ret = device_bind(dm_root(), DM_DRIVER_GET(arm_ffa), FFA_DRV_NAME, NULL, ofnode_null(), > > > > > + &dev); > > > > > > > > Please add a DT binding. Even if only temporary, we need something for this. > > > > > > Thanks for the feedback. I'm happy to address all the comments. > > > > > > Regarding DT binding and FF-A discovery. We agreed with Linaro and Rob Herring > > > about the following: > > > > > > - DT is only for what we failed to make discoverable. For hardware, we're stuck > > > with it. We shouldn't repeat that for software interfaces. This approach is > > > already applied in the FF-A kernel driver which comes with no DT support and > > > discovers the bus with bus_register() API [1]. > > > > This may be the UEFI view, but it is not how U-Boot works. This is not something we are 'stuck' with. It is how we define what is present on a device. This is how the PCI bus works in U-Boot. It is best practice in U-Boot to use the device tree to make this things visible and configurable. Unlike with Linux there is no other way to provide configuration needed by these devices. > > Where do you get UEFI out of this?
I assume it was UEFI as there was no discussion about this in U-Boot. Which firmware project was consulted about this?
> > It is the discoverability of hardware that is fixed (and we are stuck > with). We can't change hardware. The disoverability may be PCI > VID/PID, USB device descriptors, or nothing. We only use DT when those > are not sufficient. For a software interface, there is no reason to > make them non-discoverable as the interface can be fixed (at least for > new things like FF-A).
Here I am talking about the controller itself, the top-level node in the device tree. For PCI this is a device tree node and it should be the same here. So I am not saying that the devices on the bus need to be in the device tree (that can be optional, but may be useful in some situations where it is status and known).
Sure, the PCI host bridges are not discoverable, have a bunch of resources, and do need to be in DT. The downstream devices only do if they have extra resources such as when a device is soldered down on a board rather than a standard slot.
We need something like:
ff-a { compatible = "something"; };
I don't know what mechanism is actually used to communicate with it, but that will be enough to get the top-level driver started.
There's discovery of FF-A itself and then discovery of FF-A features (e.g. partitions). Both of those are discoverable without DT. The first is done by checking the SMCCC version, then checking for FF-A presence and features. Putting this into DT is redundant. Worse, what if they disagree?
Hi Simon,
Do you agree with Rob, Ilias and myself that it makes more sense FF-A bus is discovered without a DT node and following the same approach as Linux ? (FF-A bus doesn't have a HW controller and is a purely SW bus, no configuration/description needed at DT level).
Your suggestions are always welcome.
I'm sorry I don't agree with that. It does need a compatible string, like PCI has. You can just add it in U-Boot if Linux won't accept the binding.
It's not like PCI as the host side of PCI has non-discoverable resources.
OK I see. It is certainly an edge case.
This all could have been designed better, but hindsight is 20/20 and things evolved step by step. There are a bunch of firmware services that are all behind SMCCC. The first (upstream) was PSCI. IIRC, SMCCC was invented a bit after that, but generalized PSCI for other services. Since then more have been added. More services get added one by one and yes we added bindings for them. Because what's one more... But that really needs to stop. We're stuck with h/w that's not discoverable, there's zero reason to do that with s/w interfaces. If we could redo everything, we'd have a node for SMCCC and that's it unless there's h/w resources provided to the rest of DT. But we can't, so SMCCC is discovered by the presence of PSCI.
I understand the background here, but if we don't take a stand on this, this sort of thing will continue. Just because something works in Linux does not mean that the binding (or lack of it) is good.
The reasons to do this are:
- avoids needing to manually call device_bind()
- avoids extra plumbing in U-Boot
- provides visibility into what is in the system, by looking at the
DT, like documentation
- DT is how devices are bound in U-Boot
You can see the problem if you look at ffa_device_get(). It is called from ffa_bus_discover() which is a new addition into the board_init list. We are trying to remove this list and certainly don't want new things added!!
Hi Simon,
As stated in the v8 patchset cover letter [1] and readme [2], the FF-A bus is discoverable on demand at runtime.
Clients (such as EFI) can discover the FF-A bus using ffa_bus_discover() API which triggers the discovery process.
We no longer use the board_init list to discover the FF-A bus.
Please refer to the v8 patchset for the review.
Cheers
[1]: https://lore.kernel.org/all/20221122131751.22747-1-abdellatif.elkhlifi@arm.c... [2]: https://lore.kernel.org/all/20221122131751.22747-4-abdellatif.elkhlifi@arm.c...
We don't need to change this in the Linux implementation, just add a top-level DT node for U-Boot. I don't understand why that is such a big problem?
Regards, Simon

Hi Abdellatif,
On Fri, 13 Jan 2023 at 03:44, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
On Thu, Jan 12, 2023 at 04:43:32PM -0700, Simon Glass wrote:
Hi Rob,
On Wed, 11 Jan 2023 at 19:10, Rob Herring robh@kernel.org wrote:
On Mon, Dec 19, 2022 at 1:21 PM Simon Glass sjg@chromium.org wrote:
Hi Abdellatif,
On Mon, 19 Dec 2022 at 04:12, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
On Mon, Dec 05, 2022 at 09:49:30AM -0600, Rob Herring wrote:
On Sun, Dec 4, 2022 at 1:22 PM Simon Glass sjg@chromium.org wrote: > > Hi Rob, > > On Tue, 29 Nov 2022 at 05:22, Rob Herring robh@kernel.org wrote: > > > > On Fri, Nov 25, 2022 at 3:18 PM Simon Glass sjg@chromium.org wrote: > > > > > > Hi Abdellatif, > > > > > > On Thu, 24 Nov 2022 at 06:21, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote: > > > > > > > > On Tue, Nov 22, 2022 at 07:09:16PM -0700, Simon Glass wrote: > > > > > should be called 'priov' and should beHi Abdellatif, > > > > > > > > > > > [..] > > > > > > > > > +/** > > > > > > + * ffa_device_get - create, bind and probe the arm_ffa device > > > > > > + * @pdev: the address of a device pointer (to be filled when the arm_ffa bus device is created > > > > > > + * successfully) > > > > > > + * > > > > > > + * This function makes sure the arm_ffa device is > > > > > > + * created, bound to this driver, probed and ready to use. > > > > > > + * Arm FF-A transport is implemented through a single U-Boot > > > > > > + * device managing the FF-A bus (arm_ffa). > > > > > > + * > > > > > > + * Return: > > > > > > + * > > > > > > + * 0 on success. Otherwise, failure > > > > > > + */ > > > > > > +int ffa_device_get(struct udevice **pdev) > > > > > > +{ > > > > > > + int ret; > > > > > > + struct udevice *dev = NULL; > > > > > > + > > > > > > + ret = device_bind(dm_root(), DM_DRIVER_GET(arm_ffa), FFA_DRV_NAME, NULL, ofnode_null(), > > > > > > + &dev); > > > > > > > > > > Please add a DT binding. Even if only temporary, we need something for this. > > > > > > > > Thanks for the feedback. I'm happy to address all the comments. > > > > > > > > Regarding DT binding and FF-A discovery. We agreed with Linaro and Rob Herring > > > > about the following: > > > > > > > > - DT is only for what we failed to make discoverable. For hardware, we're stuck > > > > with it. We shouldn't repeat that for software interfaces. This approach is > > > > already applied in the FF-A kernel driver which comes with no DT support and > > > > discovers the bus with bus_register() API [1]. > > > > > > This may be the UEFI view, but it is not how U-Boot works. This is not something we are 'stuck' with. It is how we define what is present on a device. This is how the PCI bus works in U-Boot. It is best practice in U-Boot to use the device tree to make this things visible and configurable. Unlike with Linux there is no other way to provide configuration needed by these devices. > > > > Where do you get UEFI out of this? > > I assume it was UEFI as there was no discussion about this in U-Boot. > Which firmware project was consulted about this? > > > > > It is the discoverability of hardware that is fixed (and we are stuck > > with). We can't change hardware. The disoverability may be PCI > > VID/PID, USB device descriptors, or nothing. We only use DT when those > > are not sufficient. For a software interface, there is no reason to > > make them non-discoverable as the interface can be fixed (at least for > > new things like FF-A). > > Here I am talking about the controller itself, the top-level node in > the device tree. For PCI this is a device tree node and it should be > the same here. So I am not saying that the devices on the bus need to > be in the device tree (that can be optional, but may be useful in some > situations where it is status and known).
Sure, the PCI host bridges are not discoverable, have a bunch of resources, and do need to be in DT. The downstream devices only do if they have extra resources such as when a device is soldered down on a board rather than a standard slot.
> We need something like: > > ff-a { > compatible = "something"; > }; > > I don't know what mechanism is actually used to communicate with it, > but that will be enough to get the top-level driver started.
There's discovery of FF-A itself and then discovery of FF-A features (e.g. partitions). Both of those are discoverable without DT. The first is done by checking the SMCCC version, then checking for FF-A presence and features. Putting this into DT is redundant. Worse, what if they disagree?
Hi Simon,
Do you agree with Rob, Ilias and myself that it makes more sense FF-A bus is discovered without a DT node and following the same approach as Linux ? (FF-A bus doesn't have a HW controller and is a purely SW bus, no configuration/description needed at DT level).
Your suggestions are always welcome.
I'm sorry I don't agree with that. It does need a compatible string, like PCI has. You can just add it in U-Boot if Linux won't accept the binding.
It's not like PCI as the host side of PCI has non-discoverable resources.
OK I see. It is certainly an edge case.
This all could have been designed better, but hindsight is 20/20 and things evolved step by step. There are a bunch of firmware services that are all behind SMCCC. The first (upstream) was PSCI. IIRC, SMCCC was invented a bit after that, but generalized PSCI for other services. Since then more have been added. More services get added one by one and yes we added bindings for them. Because what's one more... But that really needs to stop. We're stuck with h/w that's not discoverable, there's zero reason to do that with s/w interfaces. If we could redo everything, we'd have a node for SMCCC and that's it unless there's h/w resources provided to the rest of DT. But we can't, so SMCCC is discovered by the presence of PSCI.
I understand the background here, but if we don't take a stand on this, this sort of thing will continue. Just because something works in Linux does not mean that the binding (or lack of it) is good.
The reasons to do this are:
- avoids needing to manually call device_bind()
- avoids extra plumbing in U-Boot
- provides visibility into what is in the system, by looking at the
DT, like documentation
- DT is how devices are bound in U-Boot
You can see the problem if you look at ffa_device_get(). It is called from ffa_bus_discover() which is a new addition into the board_init list. We are trying to remove this list and certainly don't want new things added!!
Hi Simon,
As stated in the v8 patchset cover letter [1] and readme [2], the FF-A bus is discoverable on demand at runtime.
Clients (such as EFI) can discover the FF-A bus using ffa_bus_discover() API which triggers the discovery process.
We no longer use the board_init list to discover the FF-A bus.
That's because you have moved it to the command - everything that wants to use this has to call ffa_bus_discover()...!
Please refer to the v8 patchset for the review.
It still needs a DT node and the binding needs to happen with driver model. Please put that in. See also how the sandbox emulation is done for I2C, SPI, USB, PCI and every other bus, with something in the DT.
We have dm_scan_other() which can pick up other buses but that is just one function and we don't support multiple versions of it. We could add that I suppose, but as I say, where does it end?
You can create a u-boot-ffa.dtsi file with the appropriate node and include that in any board or SoC that uses this feature. Sandbox can have its emulation node also. You will know that everything is good when you can boot U-Boot, type 'dm tree' and see your device in there without running any other commands.
Regards, Simon
Cheers
We don't need to change this in the Linux implementation, just add a top-level DT node for U-Boot. I don't understand why that is such a big problem?
Regards, Simon

On Fri, Jan 13, 2023 at 11:00:28AM -0700, Simon Glass wrote:
Hi Abdellatif,
On Fri, 13 Jan 2023 at 03:44, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
On Thu, Jan 12, 2023 at 04:43:32PM -0700, Simon Glass wrote:
Hi Rob,
On Wed, 11 Jan 2023 at 19:10, Rob Herring robh@kernel.org wrote:
On Mon, Dec 19, 2022 at 1:21 PM Simon Glass sjg@chromium.org wrote:
Hi Abdellatif,
On Mon, 19 Dec 2022 at 04:12, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
On Mon, Dec 05, 2022 at 09:49:30AM -0600, Rob Herring wrote: > On Sun, Dec 4, 2022 at 1:22 PM Simon Glass sjg@chromium.org wrote: > > > > Hi Rob, > > > > On Tue, 29 Nov 2022 at 05:22, Rob Herring robh@kernel.org wrote: > > > > > > On Fri, Nov 25, 2022 at 3:18 PM Simon Glass sjg@chromium.org wrote: > > > > > > > > Hi Abdellatif, > > > > > > > > On Thu, 24 Nov 2022 at 06:21, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote: > > > > > > > > > > On Tue, Nov 22, 2022 at 07:09:16PM -0700, Simon Glass wrote: > > > > > > should be called 'priov' and should beHi Abdellatif, > > > > > > > > > > > > > > [..] > > > > > > > > > > > +/** > > > > > > > + * ffa_device_get - create, bind and probe the arm_ffa device > > > > > > > + * @pdev: the address of a device pointer (to be filled when the arm_ffa bus device is created > > > > > > > + * successfully) > > > > > > > + * > > > > > > > + * This function makes sure the arm_ffa device is > > > > > > > + * created, bound to this driver, probed and ready to use. > > > > > > > + * Arm FF-A transport is implemented through a single U-Boot > > > > > > > + * device managing the FF-A bus (arm_ffa). > > > > > > > + * > > > > > > > + * Return: > > > > > > > + * > > > > > > > + * 0 on success. Otherwise, failure > > > > > > > + */ > > > > > > > +int ffa_device_get(struct udevice **pdev) > > > > > > > +{ > > > > > > > + int ret; > > > > > > > + struct udevice *dev = NULL; > > > > > > > + > > > > > > > + ret = device_bind(dm_root(), DM_DRIVER_GET(arm_ffa), FFA_DRV_NAME, NULL, ofnode_null(), > > > > > > > + &dev); > > > > > > > > > > > > Please add a DT binding. Even if only temporary, we need something for this. > > > > > > > > > > Thanks for the feedback. I'm happy to address all the comments. > > > > > > > > > > Regarding DT binding and FF-A discovery. We agreed with Linaro and Rob Herring > > > > > about the following: > > > > > > > > > > - DT is only for what we failed to make discoverable. For hardware, we're stuck > > > > > with it. We shouldn't repeat that for software interfaces. This approach is > > > > > already applied in the FF-A kernel driver which comes with no DT support and > > > > > discovers the bus with bus_register() API [1]. > > > > > > > > This may be the UEFI view, but it is not how U-Boot works. This is not something we are 'stuck' with. It is how we define what is present on a device. This is how the PCI bus works in U-Boot. It is best practice in U-Boot to use the device tree to make this things visible and configurable. Unlike with Linux there is no other way to provide configuration needed by these devices. > > > > > > Where do you get UEFI out of this? > > > > I assume it was UEFI as there was no discussion about this in U-Boot. > > Which firmware project was consulted about this? > > > > > > > > It is the discoverability of hardware that is fixed (and we are stuck > > > with). We can't change hardware. The disoverability may be PCI > > > VID/PID, USB device descriptors, or nothing. We only use DT when those > > > are not sufficient. For a software interface, there is no reason to > > > make them non-discoverable as the interface can be fixed (at least for > > > new things like FF-A). > > > > Here I am talking about the controller itself, the top-level node in > > the device tree. For PCI this is a device tree node and it should be > > the same here. So I am not saying that the devices on the bus need to > > be in the device tree (that can be optional, but may be useful in some > > situations where it is status and known). > > Sure, the PCI host bridges are not discoverable, have a bunch of > resources, and do need to be in DT. The downstream devices only do if > they have extra resources such as when a device is soldered down on a > board rather than a standard slot. > > > We need something like: > > > > ff-a { > > compatible = "something"; > > }; > > > > I don't know what mechanism is actually used to communicate with it, > > but that will be enough to get the top-level driver started. > > There's discovery of FF-A itself and then discovery of FF-A features > (e.g. partitions). Both of those are discoverable without DT. The > first is done by checking the SMCCC version, then checking for FF-A > presence and features. Putting this into DT is redundant. Worse, what > if they disagree?
Hi Simon,
Do you agree with Rob, Ilias and myself that it makes more sense FF-A bus is discovered without a DT node and following the same approach as Linux ? (FF-A bus doesn't have a HW controller and is a purely SW bus, no configuration/description needed at DT level).
Your suggestions are always welcome.
I'm sorry I don't agree with that. It does need a compatible string, like PCI has. You can just add it in U-Boot if Linux won't accept the binding.
It's not like PCI as the host side of PCI has non-discoverable resources.
OK I see. It is certainly an edge case.
This all could have been designed better, but hindsight is 20/20 and things evolved step by step. There are a bunch of firmware services that are all behind SMCCC. The first (upstream) was PSCI. IIRC, SMCCC was invented a bit after that, but generalized PSCI for other services. Since then more have been added. More services get added one by one and yes we added bindings for them. Because what's one more... But that really needs to stop. We're stuck with h/w that's not discoverable, there's zero reason to do that with s/w interfaces. If we could redo everything, we'd have a node for SMCCC and that's it unless there's h/w resources provided to the rest of DT. But we can't, so SMCCC is discovered by the presence of PSCI.
I understand the background here, but if we don't take a stand on this, this sort of thing will continue. Just because something works in Linux does not mean that the binding (or lack of it) is good.
The reasons to do this are:
- avoids needing to manually call device_bind()
- avoids extra plumbing in U-Boot
- provides visibility into what is in the system, by looking at the
DT, like documentation
- DT is how devices are bound in U-Boot
You can see the problem if you look at ffa_device_get(). It is called from ffa_bus_discover() which is a new addition into the board_init list. We are trying to remove this list and certainly don't want new things added!!
Hi Simon,
As stated in the v8 patchset cover letter [1] and readme [2], the FF-A bus is discoverable on demand at runtime.
Clients (such as EFI) can discover the FF-A bus using ffa_bus_discover() API which triggers the discovery process.
We no longer use the board_init list to discover the FF-A bus.
That's because you have moved it to the command - everything that wants to use this has to call ffa_bus_discover()...!
Please refer to the v8 patchset for the review.
It still needs a DT node and the binding needs to happen with driver model. Please put that in. See also how the sandbox emulation is done for I2C, SPI, USB, PCI and every other bus, with something in the DT.
We have dm_scan_other() which can pick up other buses but that is just one function and we don't support multiple versions of it. We could add that I suppose, but as I say, where does it end?
You can create a u-boot-ffa.dtsi file with the appropriate node and include that in any board or SoC that uses this feature. Sandbox can have its emulation node also. You will know that everything is good when you can boot U-Boot, type 'dm tree' and see your device in there without running any other commands.
Hi Tom,
Any thoughts about this topic ? (discovering FF-A bus manually like done in Linux without DT node vs using a DT node)
Cheers
Regards, Simon
Cheers
We don't need to change this in the Linux implementation, just add a top-level DT node for U-Boot. I don't understand why that is such a big problem?
Regards, Simon

On Mon, Jan 16, 2023 at 01:23:53PM +0000, Abdellatif El Khlifi wrote:
On Fri, Jan 13, 2023 at 11:00:28AM -0700, Simon Glass wrote:
Hi Abdellatif,
On Fri, 13 Jan 2023 at 03:44, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
On Thu, Jan 12, 2023 at 04:43:32PM -0700, Simon Glass wrote:
Hi Rob,
On Wed, 11 Jan 2023 at 19:10, Rob Herring robh@kernel.org wrote:
On Mon, Dec 19, 2022 at 1:21 PM Simon Glass sjg@chromium.org wrote:
Hi Abdellatif,
On Mon, 19 Dec 2022 at 04:12, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote: > > On Mon, Dec 05, 2022 at 09:49:30AM -0600, Rob Herring wrote: > > On Sun, Dec 4, 2022 at 1:22 PM Simon Glass sjg@chromium.org wrote: > > > > > > Hi Rob, > > > > > > On Tue, 29 Nov 2022 at 05:22, Rob Herring robh@kernel.org wrote: > > > > > > > > On Fri, Nov 25, 2022 at 3:18 PM Simon Glass sjg@chromium.org wrote: > > > > > > > > > > Hi Abdellatif, > > > > > > > > > > On Thu, 24 Nov 2022 at 06:21, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote: > > > > > > > > > > > > On Tue, Nov 22, 2022 at 07:09:16PM -0700, Simon Glass wrote: > > > > > > > should be called 'priov' and should beHi Abdellatif, > > > > > > > > > > > > > > > > > [..] > > > > > > > > > > > > > +/** > > > > > > > > + * ffa_device_get - create, bind and probe the arm_ffa device > > > > > > > > + * @pdev: the address of a device pointer (to be filled when the arm_ffa bus device is created > > > > > > > > + * successfully) > > > > > > > > + * > > > > > > > > + * This function makes sure the arm_ffa device is > > > > > > > > + * created, bound to this driver, probed and ready to use. > > > > > > > > + * Arm FF-A transport is implemented through a single U-Boot > > > > > > > > + * device managing the FF-A bus (arm_ffa). > > > > > > > > + * > > > > > > > > + * Return: > > > > > > > > + * > > > > > > > > + * 0 on success. Otherwise, failure > > > > > > > > + */ > > > > > > > > +int ffa_device_get(struct udevice **pdev) > > > > > > > > +{ > > > > > > > > + int ret; > > > > > > > > + struct udevice *dev = NULL; > > > > > > > > + > > > > > > > > + ret = device_bind(dm_root(), DM_DRIVER_GET(arm_ffa), FFA_DRV_NAME, NULL, ofnode_null(), > > > > > > > > + &dev); > > > > > > > > > > > > > > Please add a DT binding. Even if only temporary, we need something for this. > > > > > > > > > > > > Thanks for the feedback. I'm happy to address all the comments. > > > > > > > > > > > > Regarding DT binding and FF-A discovery. We agreed with Linaro and Rob Herring > > > > > > about the following: > > > > > > > > > > > > - DT is only for what we failed to make discoverable. For hardware, we're stuck > > > > > > with it. We shouldn't repeat that for software interfaces. This approach is > > > > > > already applied in the FF-A kernel driver which comes with no DT support and > > > > > > discovers the bus with bus_register() API [1]. > > > > > > > > > > This may be the UEFI view, but it is not how U-Boot works. This is not something we are 'stuck' with. It is how we define what is present on a device. This is how the PCI bus works in U-Boot. It is best practice in U-Boot to use the device tree to make this things visible and configurable. Unlike with Linux there is no other way to provide configuration needed by these devices. > > > > > > > > Where do you get UEFI out of this? > > > > > > I assume it was UEFI as there was no discussion about this in U-Boot. > > > Which firmware project was consulted about this? > > > > > > > > > > > It is the discoverability of hardware that is fixed (and we are stuck > > > > with). We can't change hardware. The disoverability may be PCI > > > > VID/PID, USB device descriptors, or nothing. We only use DT when those > > > > are not sufficient. For a software interface, there is no reason to > > > > make them non-discoverable as the interface can be fixed (at least for > > > > new things like FF-A). > > > > > > Here I am talking about the controller itself, the top-level node in > > > the device tree. For PCI this is a device tree node and it should be > > > the same here. So I am not saying that the devices on the bus need to > > > be in the device tree (that can be optional, but may be useful in some > > > situations where it is status and known). > > > > Sure, the PCI host bridges are not discoverable, have a bunch of > > resources, and do need to be in DT. The downstream devices only do if > > they have extra resources such as when a device is soldered down on a > > board rather than a standard slot. > > > > > We need something like: > > > > > > ff-a { > > > compatible = "something"; > > > }; > > > > > > I don't know what mechanism is actually used to communicate with it, > > > but that will be enough to get the top-level driver started. > > > > There's discovery of FF-A itself and then discovery of FF-A features > > (e.g. partitions). Both of those are discoverable without DT. The > > first is done by checking the SMCCC version, then checking for FF-A > > presence and features. Putting this into DT is redundant. Worse, what > > if they disagree? > > Hi Simon, > > Do you agree with Rob, Ilias and myself that it makes more sense > FF-A bus is discovered without a DT node and following the same approach as > Linux ? (FF-A bus doesn't have a HW controller and is a purely SW bus, > no configuration/description needed at DT level). > > Your suggestions are always welcome.
I'm sorry I don't agree with that. It does need a compatible string, like PCI has. You can just add it in U-Boot if Linux won't accept the binding.
It's not like PCI as the host side of PCI has non-discoverable resources.
OK I see. It is certainly an edge case.
This all could have been designed better, but hindsight is 20/20 and things evolved step by step. There are a bunch of firmware services that are all behind SMCCC. The first (upstream) was PSCI. IIRC, SMCCC was invented a bit after that, but generalized PSCI for other services. Since then more have been added. More services get added one by one and yes we added bindings for them. Because what's one more... But that really needs to stop. We're stuck with h/w that's not discoverable, there's zero reason to do that with s/w interfaces. If we could redo everything, we'd have a node for SMCCC and that's it unless there's h/w resources provided to the rest of DT. But we can't, so SMCCC is discovered by the presence of PSCI.
I understand the background here, but if we don't take a stand on this, this sort of thing will continue. Just because something works in Linux does not mean that the binding (or lack of it) is good.
The reasons to do this are:
- avoids needing to manually call device_bind()
- avoids extra plumbing in U-Boot
- provides visibility into what is in the system, by looking at the
DT, like documentation
- DT is how devices are bound in U-Boot
You can see the problem if you look at ffa_device_get(). It is called from ffa_bus_discover() which is a new addition into the board_init list. We are trying to remove this list and certainly don't want new things added!!
Hi Simon,
As stated in the v8 patchset cover letter [1] and readme [2], the FF-A bus is discoverable on demand at runtime.
Clients (such as EFI) can discover the FF-A bus using ffa_bus_discover() API which triggers the discovery process.
We no longer use the board_init list to discover the FF-A bus.
That's because you have moved it to the command - everything that wants to use this has to call ffa_bus_discover()...!
Please refer to the v8 patchset for the review.
It still needs a DT node and the binding needs to happen with driver model. Please put that in. See also how the sandbox emulation is done for I2C, SPI, USB, PCI and every other bus, with something in the DT.
We have dm_scan_other() which can pick up other buses but that is just one function and we don't support multiple versions of it. We could add that I suppose, but as I say, where does it end?
You can create a u-boot-ffa.dtsi file with the appropriate node and include that in any board or SoC that uses this feature. Sandbox can have its emulation node also. You will know that everything is good when you can boot U-Boot, type 'dm tree' and see your device in there without running any other commands.
Hi Tom,
Any thoughts about this topic ? (discovering FF-A bus manually like done in Linux without DT node vs using a DT node)
Yes, I think Simon's point is right. Without a DT node, it's a bunch of one-off special code to discover something that we could get from just having a node. If you want to avoid doing this, you need to show - the patchset, using a DT node - the patchset, without using a DT node, but instead something that addresses Simon's feedback.
And then demonstrate that the 2nd approach is cleaner than the first.

On Tue, Jan 17, 2023 at 8:04 AM Tom Rini trini@konsulko.com wrote:
On Mon, Jan 16, 2023 at 01:23:53PM +0000, Abdellatif El Khlifi wrote:
On Fri, Jan 13, 2023 at 11:00:28AM -0700, Simon Glass wrote:
Hi Abdellatif,
On Fri, 13 Jan 2023 at 03:44, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
On Thu, Jan 12, 2023 at 04:43:32PM -0700, Simon Glass wrote:
Hi Rob,
On Wed, 11 Jan 2023 at 19:10, Rob Herring robh@kernel.org wrote:
On Mon, Dec 19, 2022 at 1:21 PM Simon Glass sjg@chromium.org wrote: > > Hi Abdellatif, > > On Mon, 19 Dec 2022 at 04:12, Abdellatif El Khlifi > abdellatif.elkhlifi@arm.com wrote: > > > > On Mon, Dec 05, 2022 at 09:49:30AM -0600, Rob Herring wrote: > > > On Sun, Dec 4, 2022 at 1:22 PM Simon Glass sjg@chromium.org wrote: > > > > > > > > Hi Rob, > > > > > > > > On Tue, 29 Nov 2022 at 05:22, Rob Herring robh@kernel.org wrote: > > > > > > > > > > On Fri, Nov 25, 2022 at 3:18 PM Simon Glass sjg@chromium.org wrote: > > > > > > > > > > > > Hi Abdellatif, > > > > > > > > > > > > On Thu, 24 Nov 2022 at 06:21, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote: > > > > > > > > > > > > > > On Tue, Nov 22, 2022 at 07:09:16PM -0700, Simon Glass wrote: > > > > > > > > should be called 'priov' and should beHi Abdellatif, > > > > > > > > > > > > > > > > > > > > [..] > > > > > > > > > > > > > > > +/** > > > > > > > > > + * ffa_device_get - create, bind and probe the arm_ffa device > > > > > > > > > + * @pdev: the address of a device pointer (to be filled when the arm_ffa bus device is created > > > > > > > > > + * successfully) > > > > > > > > > + * > > > > > > > > > + * This function makes sure the arm_ffa device is > > > > > > > > > + * created, bound to this driver, probed and ready to use. > > > > > > > > > + * Arm FF-A transport is implemented through a single U-Boot > > > > > > > > > + * device managing the FF-A bus (arm_ffa). > > > > > > > > > + * > > > > > > > > > + * Return: > > > > > > > > > + * > > > > > > > > > + * 0 on success. Otherwise, failure > > > > > > > > > + */ > > > > > > > > > +int ffa_device_get(struct udevice **pdev) > > > > > > > > > +{ > > > > > > > > > + int ret; > > > > > > > > > + struct udevice *dev = NULL; > > > > > > > > > + > > > > > > > > > + ret = device_bind(dm_root(), DM_DRIVER_GET(arm_ffa), FFA_DRV_NAME, NULL, ofnode_null(), > > > > > > > > > + &dev); > > > > > > > > > > > > > > > > Please add a DT binding. Even if only temporary, we need something for this. > > > > > > > > > > > > > > Thanks for the feedback. I'm happy to address all the comments. > > > > > > > > > > > > > > Regarding DT binding and FF-A discovery. We agreed with Linaro and Rob Herring > > > > > > > about the following: > > > > > > > > > > > > > > - DT is only for what we failed to make discoverable. For hardware, we're stuck > > > > > > > with it. We shouldn't repeat that for software interfaces. This approach is > > > > > > > already applied in the FF-A kernel driver which comes with no DT support and > > > > > > > discovers the bus with bus_register() API [1]. > > > > > > > > > > > > This may be the UEFI view, but it is not how U-Boot works. This is not something we are 'stuck' with. It is how we define what is present on a device. This is how the PCI bus works in U-Boot. It is best practice in U-Boot to use the device tree to make this things visible and configurable. Unlike with Linux there is no other way to provide configuration needed by these devices. > > > > > > > > > > Where do you get UEFI out of this? > > > > > > > > I assume it was UEFI as there was no discussion about this in U-Boot. > > > > Which firmware project was consulted about this? > > > > > > > > > > > > > > It is the discoverability of hardware that is fixed (and we are stuck > > > > > with). We can't change hardware. The disoverability may be PCI > > > > > VID/PID, USB device descriptors, or nothing. We only use DT when those > > > > > are not sufficient. For a software interface, there is no reason to > > > > > make them non-discoverable as the interface can be fixed (at least for > > > > > new things like FF-A). > > > > > > > > Here I am talking about the controller itself, the top-level node in > > > > the device tree. For PCI this is a device tree node and it should be > > > > the same here. So I am not saying that the devices on the bus need to > > > > be in the device tree (that can be optional, but may be useful in some > > > > situations where it is status and known). > > > > > > Sure, the PCI host bridges are not discoverable, have a bunch of > > > resources, and do need to be in DT. The downstream devices only do if > > > they have extra resources such as when a device is soldered down on a > > > board rather than a standard slot. > > > > > > > We need something like: > > > > > > > > ff-a { > > > > compatible = "something"; > > > > }; > > > > > > > > I don't know what mechanism is actually used to communicate with it, > > > > but that will be enough to get the top-level driver started. > > > > > > There's discovery of FF-A itself and then discovery of FF-A features > > > (e.g. partitions). Both of those are discoverable without DT. The > > > first is done by checking the SMCCC version, then checking for FF-A > > > presence and features. Putting this into DT is redundant. Worse, what > > > if they disagree? > > > > Hi Simon, > > > > Do you agree with Rob, Ilias and myself that it makes more sense > > FF-A bus is discovered without a DT node and following the same approach as > > Linux ? (FF-A bus doesn't have a HW controller and is a purely SW bus, > > no configuration/description needed at DT level). > > > > Your suggestions are always welcome. > > I'm sorry I don't agree with that. It does need a compatible string, > like PCI has. You can just add it in U-Boot if Linux won't accept the > binding.
It's not like PCI as the host side of PCI has non-discoverable resources.
OK I see. It is certainly an edge case.
This all could have been designed better, but hindsight is 20/20 and things evolved step by step. There are a bunch of firmware services that are all behind SMCCC. The first (upstream) was PSCI. IIRC, SMCCC was invented a bit after that, but generalized PSCI for other services. Since then more have been added. More services get added one by one and yes we added bindings for them. Because what's one more... But that really needs to stop. We're stuck with h/w that's not discoverable, there's zero reason to do that with s/w interfaces. If we could redo everything, we'd have a node for SMCCC and that's it unless there's h/w resources provided to the rest of DT. But we can't, so SMCCC is discovered by the presence of PSCI.
I understand the background here, but if we don't take a stand on this, this sort of thing will continue. Just because something works in Linux does not mean that the binding (or lack of it) is good.
The reasons to do this are:
- avoids needing to manually call device_bind()
- avoids extra plumbing in U-Boot
- provides visibility into what is in the system, by looking at the
DT, like documentation
- DT is how devices are bound in U-Boot
You can see the problem if you look at ffa_device_get(). It is called from ffa_bus_discover() which is a new addition into the board_init list. We are trying to remove this list and certainly don't want new things added!!
Hi Simon,
As stated in the v8 patchset cover letter [1] and readme [2], the FF-A bus is discoverable on demand at runtime.
Clients (such as EFI) can discover the FF-A bus using ffa_bus_discover() API which triggers the discovery process.
We no longer use the board_init list to discover the FF-A bus.
That's because you have moved it to the command - everything that wants to use this has to call ffa_bus_discover()...!
Please refer to the v8 patchset for the review.
It still needs a DT node and the binding needs to happen with driver model. Please put that in. See also how the sandbox emulation is done for I2C, SPI, USB, PCI and every other bus, with something in the DT.
We have dm_scan_other() which can pick up other buses but that is just one function and we don't support multiple versions of it. We could add that I suppose, but as I say, where does it end?
You can create a u-boot-ffa.dtsi file with the appropriate node and include that in any board or SoC that uses this feature. Sandbox can have its emulation node also. You will know that everything is good when you can boot U-Boot, type 'dm tree' and see your device in there without running any other commands.
Hi Tom,
Any thoughts about this topic ? (discovering FF-A bus manually like done in Linux without DT node vs using a DT node)
Yes, I think Simon's point is right. Without a DT node, it's a bunch of one-off special code to discover something that we could get from just having a node.
DT simply is not always a nice 1:1 with what the client wants for drivers. It's not an abstraction layer for the OS. I see it tried All The Time. We have the same problem on things like CPUFreq because there is often not a h/w block, but a scattered collection of bits of h/w the driver has to control and still needs platform specific knowledge.
If you want to avoid doing this, you need to show
- the patchset, using a DT node
That would be the PSCI driver which has a node.
- the patchset, without using a DT node, but instead something that addresses Simon's feedback.
That would be the SMCCC TRNG driver which has no node.
Perhaps if there was an 'SMCCC bus' which PSCI, TRNG, FF-A, etc. all sat on, then that bus code can discover every sub-function and create devices for them. Not sure, I'm not really familiar with the current DM stuff.
Rob

On Thu, Jan 12, 2023 at 5:43 PM Simon Glass sjg@chromium.org wrote:
Hi Rob,
On Wed, 11 Jan 2023 at 19:10, Rob Herring robh@kernel.org wrote:
On Mon, Dec 19, 2022 at 1:21 PM Simon Glass sjg@chromium.org wrote:
Hi Abdellatif,
On Mon, 19 Dec 2022 at 04:12, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
On Mon, Dec 05, 2022 at 09:49:30AM -0600, Rob Herring wrote:
On Sun, Dec 4, 2022 at 1:22 PM Simon Glass sjg@chromium.org wrote:
Hi Rob,
On Tue, 29 Nov 2022 at 05:22, Rob Herring robh@kernel.org wrote: > > On Fri, Nov 25, 2022 at 3:18 PM Simon Glass sjg@chromium.org wrote: > > > > Hi Abdellatif, > > > > On Thu, 24 Nov 2022 at 06:21, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote: > > > > > > On Tue, Nov 22, 2022 at 07:09:16PM -0700, Simon Glass wrote: > > > > should be called 'priov' and should beHi Abdellatif, > > > > > > > > [..] > > > > > > > +/** > > > > > + * ffa_device_get - create, bind and probe the arm_ffa device > > > > > + * @pdev: the address of a device pointer (to be filled when the arm_ffa bus device is created > > > > > + * successfully) > > > > > + * > > > > > + * This function makes sure the arm_ffa device is > > > > > + * created, bound to this driver, probed and ready to use. > > > > > + * Arm FF-A transport is implemented through a single U-Boot > > > > > + * device managing the FF-A bus (arm_ffa). > > > > > + * > > > > > + * Return: > > > > > + * > > > > > + * 0 on success. Otherwise, failure > > > > > + */ > > > > > +int ffa_device_get(struct udevice **pdev) > > > > > +{ > > > > > + int ret; > > > > > + struct udevice *dev = NULL; > > > > > + > > > > > + ret = device_bind(dm_root(), DM_DRIVER_GET(arm_ffa), FFA_DRV_NAME, NULL, ofnode_null(), > > > > > + &dev); > > > > > > > > Please add a DT binding. Even if only temporary, we need something for this. > > > > > > Thanks for the feedback. I'm happy to address all the comments. > > > > > > Regarding DT binding and FF-A discovery. We agreed with Linaro and Rob Herring > > > about the following: > > > > > > - DT is only for what we failed to make discoverable. For hardware, we're stuck > > > with it. We shouldn't repeat that for software interfaces. This approach is > > > already applied in the FF-A kernel driver which comes with no DT support and > > > discovers the bus with bus_register() API [1]. > > > > This may be the UEFI view, but it is not how U-Boot works. This is not something we are 'stuck' with. It is how we define what is present on a device. This is how the PCI bus works in U-Boot. It is best practice in U-Boot to use the device tree to make this things visible and configurable. Unlike with Linux there is no other way to provide configuration needed by these devices. > > Where do you get UEFI out of this?
I assume it was UEFI as there was no discussion about this in U-Boot. Which firmware project was consulted about this?
> > It is the discoverability of hardware that is fixed (and we are stuck > with). We can't change hardware. The disoverability may be PCI > VID/PID, USB device descriptors, or nothing. We only use DT when those > are not sufficient. For a software interface, there is no reason to > make them non-discoverable as the interface can be fixed (at least for > new things like FF-A).
Here I am talking about the controller itself, the top-level node in the device tree. For PCI this is a device tree node and it should be the same here. So I am not saying that the devices on the bus need to be in the device tree (that can be optional, but may be useful in some situations where it is status and known).
Sure, the PCI host bridges are not discoverable, have a bunch of resources, and do need to be in DT. The downstream devices only do if they have extra resources such as when a device is soldered down on a board rather than a standard slot.
We need something like:
ff-a { compatible = "something"; };
I don't know what mechanism is actually used to communicate with it, but that will be enough to get the top-level driver started.
There's discovery of FF-A itself and then discovery of FF-A features (e.g. partitions). Both of those are discoverable without DT. The first is done by checking the SMCCC version, then checking for FF-A presence and features. Putting this into DT is redundant. Worse, what if they disagree?
Hi Simon,
Do you agree with Rob, Ilias and myself that it makes more sense FF-A bus is discovered without a DT node and following the same approach as Linux ? (FF-A bus doesn't have a HW controller and is a purely SW bus, no configuration/description needed at DT level).
Your suggestions are always welcome.
I'm sorry I don't agree with that. It does need a compatible string, like PCI has. You can just add it in U-Boot if Linux won't accept the binding.
It's not like PCI as the host side of PCI has non-discoverable resources.
OK I see. It is certainly an edge case.
This all could have been designed better, but hindsight is 20/20 and things evolved step by step. There are a bunch of firmware services that are all behind SMCCC. The first (upstream) was PSCI. IIRC, SMCCC was invented a bit after that, but generalized PSCI for other services. Since then more have been added. More services get added one by one and yes we added bindings for them. Because what's one more... But that really needs to stop. We're stuck with h/w that's not discoverable, there's zero reason to do that with s/w interfaces. If we could redo everything, we'd have a node for SMCCC and that's it unless there's h/w resources provided to the rest of DT. But we can't, so SMCCC is discovered by the presence of PSCI.
I understand the background here, but if we don't take a stand on this, this sort of thing will continue.
I agree completely (but I think what you mean for 'this' is different). Using DT for s/w features has to stop.
Every new SMCCC firmware interface we get a new binding for it. FF-A originally had a bunch of crap in the binding. So did Op-tee. Why does each new firmware feature need a DT node? As software people, can't we design software interfaces which are discoverable on their own rather than using a hardware description for discovering them? The only part we can't discover is whether we have SMCCC or not, but we already have that in DT with the PSCI node because if we have PSCI, we have SMCCC.
It is the same problem with that UEFI SPI protocol binding on the list last week.
Just because something works in Linux does not mean that the binding (or lack of it) is good.
The reasons to do this are:
- avoids needing to manually call device_bind()
- avoids extra plumbing in U-Boot
- provides visibility into what is in the system, by looking at the
DT, like documentation
- DT is how devices are bound in U-Boot
You can see the problem if you look at ffa_device_get(). It is called from ffa_bus_discover() which is a new addition into the board_init list. We are trying to remove this list and certainly don't want new things added!!
That seems less than ideal. How do you init PSCI? Or u-boot doesn't touch it? The low-level code for both should be shared if not already.
We don't need to change this in the Linux implementation, just add a top-level DT node for U-Boot. I don't understand why that is such a big problem?
It's a line in the sand. Yeah, 1 compatible is not a big deal, but then it is an invitation to add to the binding and add a compatible for the next SMCCC firmware feature.
Rob

On Tue, Jan 17, 2023 at 08:51:51PM -0600, Rob Herring wrote:
On Thu, Jan 12, 2023 at 5:43 PM Simon Glass sjg@chromium.org wrote:
Hi Rob,
On Wed, 11 Jan 2023 at 19:10, Rob Herring robh@kernel.org wrote:
On Mon, Dec 19, 2022 at 1:21 PM Simon Glass sjg@chromium.org wrote:
Hi Abdellatif,
On Mon, 19 Dec 2022 at 04:12, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
On Mon, Dec 05, 2022 at 09:49:30AM -0600, Rob Herring wrote:
On Sun, Dec 4, 2022 at 1:22 PM Simon Glass sjg@chromium.org wrote: > > Hi Rob, > > On Tue, 29 Nov 2022 at 05:22, Rob Herring robh@kernel.org wrote: > > > > On Fri, Nov 25, 2022 at 3:18 PM Simon Glass sjg@chromium.org wrote: > > > > > > Hi Abdellatif, > > > > > > On Thu, 24 Nov 2022 at 06:21, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote: > > > > > > > > On Tue, Nov 22, 2022 at 07:09:16PM -0700, Simon Glass wrote: > > > > > should be called 'priov' and should beHi Abdellatif, > > > > > > > > > > > [..] > > > > > > > > > +/** > > > > > > + * ffa_device_get - create, bind and probe the arm_ffa device > > > > > > + * @pdev: the address of a device pointer (to be filled when the arm_ffa bus device is created > > > > > > + * successfully) > > > > > > + * > > > > > > + * This function makes sure the arm_ffa device is > > > > > > + * created, bound to this driver, probed and ready to use. > > > > > > + * Arm FF-A transport is implemented through a single U-Boot > > > > > > + * device managing the FF-A bus (arm_ffa). > > > > > > + * > > > > > > + * Return: > > > > > > + * > > > > > > + * 0 on success. Otherwise, failure > > > > > > + */ > > > > > > +int ffa_device_get(struct udevice **pdev) > > > > > > +{ > > > > > > + int ret; > > > > > > + struct udevice *dev = NULL; > > > > > > + > > > > > > + ret = device_bind(dm_root(), DM_DRIVER_GET(arm_ffa), FFA_DRV_NAME, NULL, ofnode_null(), > > > > > > + &dev); > > > > > > > > > > Please add a DT binding. Even if only temporary, we need something for this. > > > > > > > > Thanks for the feedback. I'm happy to address all the comments. > > > > > > > > Regarding DT binding and FF-A discovery. We agreed with Linaro and Rob Herring > > > > about the following: > > > > > > > > - DT is only for what we failed to make discoverable. For hardware, we're stuck > > > > with it. We shouldn't repeat that for software interfaces. This approach is > > > > already applied in the FF-A kernel driver which comes with no DT support and > > > > discovers the bus with bus_register() API [1]. > > > > > > This may be the UEFI view, but it is not how U-Boot works. This is not something we are 'stuck' with. It is how we define what is present on a device. This is how the PCI bus works in U-Boot. It is best practice in U-Boot to use the device tree to make this things visible and configurable. Unlike with Linux there is no other way to provide configuration needed by these devices. > > > > Where do you get UEFI out of this? > > I assume it was UEFI as there was no discussion about this in U-Boot. > Which firmware project was consulted about this? > > > > > It is the discoverability of hardware that is fixed (and we are stuck > > with). We can't change hardware. The disoverability may be PCI > > VID/PID, USB device descriptors, or nothing. We only use DT when those > > are not sufficient. For a software interface, there is no reason to > > make them non-discoverable as the interface can be fixed (at least for > > new things like FF-A). > > Here I am talking about the controller itself, the top-level node in > the device tree. For PCI this is a device tree node and it should be > the same here. So I am not saying that the devices on the bus need to > be in the device tree (that can be optional, but may be useful in some > situations where it is status and known).
Sure, the PCI host bridges are not discoverable, have a bunch of resources, and do need to be in DT. The downstream devices only do if they have extra resources such as when a device is soldered down on a board rather than a standard slot.
> We need something like: > > ff-a { > compatible = "something"; > }; > > I don't know what mechanism is actually used to communicate with it, > but that will be enough to get the top-level driver started.
There's discovery of FF-A itself and then discovery of FF-A features (e.g. partitions). Both of those are discoverable without DT. The first is done by checking the SMCCC version, then checking for FF-A presence and features. Putting this into DT is redundant. Worse, what if they disagree?
Hi Simon,
Do you agree with Rob, Ilias and myself that it makes more sense FF-A bus is discovered without a DT node and following the same approach as Linux ? (FF-A bus doesn't have a HW controller and is a purely SW bus, no configuration/description needed at DT level).
Your suggestions are always welcome.
I'm sorry I don't agree with that. It does need a compatible string, like PCI has. You can just add it in U-Boot if Linux won't accept the binding.
It's not like PCI as the host side of PCI has non-discoverable resources.
OK I see. It is certainly an edge case.
This all could have been designed better, but hindsight is 20/20 and things evolved step by step. There are a bunch of firmware services that are all behind SMCCC. The first (upstream) was PSCI. IIRC, SMCCC was invented a bit after that, but generalized PSCI for other services. Since then more have been added. More services get added one by one and yes we added bindings for them. Because what's one more... But that really needs to stop. We're stuck with h/w that's not discoverable, there's zero reason to do that with s/w interfaces. If we could redo everything, we'd have a node for SMCCC and that's it unless there's h/w resources provided to the rest of DT. But we can't, so SMCCC is discovered by the presence of PSCI.
I understand the background here, but if we don't take a stand on this, this sort of thing will continue.
I agree completely (but I think what you mean for 'this' is different). Using DT for s/w features has to stop.
Every new SMCCC firmware interface we get a new binding for it. FF-A originally had a bunch of crap in the binding. So did Op-tee. Why does each new firmware feature need a DT node? As software people, can't we design software interfaces which are discoverable on their own rather than using a hardware description for discovering them? The only part we can't discover is whether we have SMCCC or not, but we already have that in DT with the PSCI node because if we have PSCI, we have SMCCC.
It is the same problem with that UEFI SPI protocol binding on the list last week.
I guess the problem comes down to, can we have one discovery method that everyone shares, or do we have to let everyone invent a new discovery method every time? FF-A, Op-tee, U-Boot, coreboot, barebox (and everyone else I'm unintentionally forgetting) could just discover these things via device tree. Or, we could all write our own code to perform the discovery. And when RISC-V comes along with similar functionality, we could probe their device tree and see they've implemented the same concept, but a little differently, but still have the discovery portion be in the device tree. To which it sounds like your answer is "not in the device tree".
Just because something works in Linux does not mean that the binding (or lack of it) is good.
The reasons to do this are:
- avoids needing to manually call device_bind()
- avoids extra plumbing in U-Boot
- provides visibility into what is in the system, by looking at the
DT, like documentation
- DT is how devices are bound in U-Boot
You can see the problem if you look at ffa_device_get(). It is called from ffa_bus_discover() which is a new addition into the board_init list. We are trying to remove this list and certainly don't want new things added!!
That seems less than ideal. How do you init PSCI? Or u-boot doesn't touch it? The low-level code for both should be shared if not already.
We don't need to change this in the Linux implementation, just add a top-level DT node for U-Boot. I don't understand why that is such a big problem?
It's a line in the sand. Yeah, 1 compatible is not a big deal, but then it is an invitation to add to the binding and add a compatible for the next SMCCC firmware feature.
Well, I suppose then that this is not the line in the sand we have to have a larger debate about. For this, we'll just follow along with what everyone else has agreed on re bindings, and that just leaves Simon's other feedback about sandbox testing, etc.

On Wed, Jan 18, 2023 at 12:49 PM Tom Rini trini@konsulko.com wrote:
I guess the problem comes down to, can we have one discovery method that everyone shares, or do we have to let everyone invent a new discovery method every time?
No one needs to invent any discovery method every time if the firmware specification provides one and as Rob mentioned many times in the thread, all new firmware specification must provide one and we are trying to make sure that is the case with all new specs from Arm.
FF-A, Op-tee, U-Boot, coreboot, barebox (and everyone else I'm unintentionally forgetting) could just discover these things via device tree.
I leave that to the individual projects to decide and agree but fundamentally if the specification provides a way to discover, not sure why we are even discussing an alternative method here.
Or, we could all write our own code to perform the discovery.
For what reason ? I can understand if there is no discovery mechanism but that's not the case in $subject.
And when RISC-V comes along with similar functionality, we could probe their device tree and see they've implemented the same concept, but a little differently, but still have the discovery portion be in the device tree. To which it sounds like your answer is "not in the device tree".
I see U-boot seem to have made a decision to create DT node for each and everything that needs to be added to DM which seems bit unfortunate but I don't understand the history/motive/background for it but I respect the decision if it is already made.
These firmware interfaces are standard on all Arm platforms and can be discovered based on PSCI/SMCCC. Not using the same and use DT node needs unnecessary addition of DT nodes for all the f/w i/f on all the platforms that need the support when one can be just discovered.
Sorry for the sudden appearance on this thread, I was avoiding getting into this but thought I will at least express my opinion and also the way the firmware specifications from Arm is expected to be evolved from now on. With that I will leave it to you and other U-boot maintainers and the community in general to decide the right course in this case.

On Wed, Jan 18, 2023 at 01:46:54PM +0000, Sudeep Holla wrote:
On Wed, Jan 18, 2023 at 12:49 PM Tom Rini trini@konsulko.com wrote:
I guess the problem comes down to, can we have one discovery method that everyone shares, or do we have to let everyone invent a new discovery method every time?
No one needs to invent any discovery method every time if the firmware specification provides one and as Rob mentioned many times in the thread, all new firmware specification must provide one and we are trying to make sure that is the case with all new specs from Arm.
FF-A, Op-tee, U-Boot, coreboot, barebox (and everyone else I'm unintentionally forgetting) could just discover these things via device tree.
I leave that to the individual projects to decide and agree but fundamentally if the specification provides a way to discover, not sure why we are even discussing an alternative method here.
Or, we could all write our own code to perform the discovery.
For what reason ? I can understand if there is no discovery mechanism but that's not the case in $subject.
And when RISC-V comes along with similar functionality, we could probe their device tree and see they've implemented the same concept, but a little differently, but still have the discovery portion be in the device tree. To which it sounds like your answer is "not in the device tree".
I see U-boot seem to have made a decision to create DT node for each and everything that needs to be added to DM which seems bit unfortunate but I don't understand the history/motive/background for it but I respect the decision if it is already made.
These firmware interfaces are standard on all Arm platforms and can be discovered based on PSCI/SMCCC. Not using the same and use DT node needs unnecessary addition of DT nodes for all the f/w i/f on all the platforms that need the support when one can be just discovered.
Sorry for the sudden appearance on this thread, I was avoiding getting into this but thought I will at least express my opinion and also the way the firmware specifications from Arm is expected to be evolved from now on. With that I will leave it to you and other U-boot maintainers and the community in general to decide the right course in this case.
To be clear, if the position is that "this is what everyone else will use, really" then yes, we'll follow this in U-Boot.

On Wed, Jan 18, 2023 at 08:59:32AM -0500, Tom Rini wrote:
To be clear, if the position is that "this is what everyone else will use, really" then yes, we'll follow this in U-Boot.
Well, that's difficult question to provide an assertive answer TBH. I don't know all the projects using FF-A for example. I wasn't even aware of U-Boot plans for it even being part of the same company. So lots of things happen outside the company, in different community like this and mainly without even Arm or interested party's involvement. I guess you understand what I am trying to say here.
I or even Arm as a company can only provide suggestions when requested or when interested developers like me happen to stumble across discussions like this. I can never give you assurance that "everyone else will follow our suggestion" unfortunately :(.

On Wed, Jan 18, 2023 at 08:59:32AM -0500, Tom Rini wrote:
On Wed, Jan 18, 2023 at 01:46:54PM +0000, Sudeep Holla wrote:
On Wed, Jan 18, 2023 at 12:49 PM Tom Rini trini@konsulko.com wrote:
I guess the problem comes down to, can we have one discovery method that everyone shares, or do we have to let everyone invent a new discovery method every time?
No one needs to invent any discovery method every time if the firmware specification provides one and as Rob mentioned many times in the thread, all new firmware specification must provide one and we are trying to make sure that is the case with all new specs from Arm.
FF-A, Op-tee, U-Boot, coreboot, barebox (and everyone else I'm unintentionally forgetting) could just discover these things via device tree.
I leave that to the individual projects to decide and agree but fundamentally if the specification provides a way to discover, not sure why we are even discussing an alternative method here.
Or, we could all write our own code to perform the discovery.
For what reason ? I can understand if there is no discovery mechanism but that's not the case in $subject.
And when RISC-V comes along with similar functionality, we could probe their device tree and see they've implemented the same concept, but a little differently, but still have the discovery portion be in the device tree. To which it sounds like your answer is "not in the device tree".
I see U-boot seem to have made a decision to create DT node for each and everything that needs to be added to DM which seems bit unfortunate but I don't understand the history/motive/background for it but I respect the decision if it is already made.
These firmware interfaces are standard on all Arm platforms and can be discovered based on PSCI/SMCCC. Not using the same and use DT node needs unnecessary addition of DT nodes for all the f/w i/f on all the platforms that need the support when one can be just discovered.
Sorry for the sudden appearance on this thread, I was avoiding getting into this but thought I will at least express my opinion and also the way the firmware specifications from Arm is expected to be evolved from now on. With that I will leave it to you and other U-boot maintainers and the community in general to decide the right course in this case.
To be clear, if the position is that "this is what everyone else will use, really" then yes, we'll follow this in U-Boot.
Hi Simon, Tom,
The FF-A transport is a SW bus and is not associated to any HW peripheral or undiscoverable base address.
There is only 1 way of discovering the FF-A bus and it's through the FF-A SW interfaces. The FF-A spec [1] describes this in details.
Discovering means gathering information about the FF-A framework such as: the FF-A version, supported features, secure partitions number and attributes.
Please refer to the following paragraphs for more details: [2], [3], [4], [5]
The core driver provided by this patchset implements the Setup and discovery interfaces in addition to direct messaging.
The driver provides ffa_bus_discover() API that allows to discover the FF-A bus as described by the spec and in the FF-A driver readme [6].
We expect and highly recommend FF-A users to always discover the FF-A bus using ffa_bus_discover() API.
A use case is provided which is the EFI MM communication [7].
ffa_bus_discover() does the following:
- creates, binds and probes the arm_ffa device - at probe level, discovery FF-A interfaces are called to try to discover the FF-A framework - when all discovery interfaces succeed, probing is successful and FF-A bus is ready to use - if one of the discovery interfaces fails, the arm_ffa device is removed from the DM and FF-A bus can not be used
Cheers Abdellatif
[1]: FF-A spec version 1.0, https://developer.arm.com/documentation/den0077/latest/
[2] 2.8 Partition identification and discovery
All FF-A components can discover the identities and properties of other partitions through the FFA_PARTITION_INFO_GET interface. Once discovered, the IDs must be used in the messaging interfaces to identify the target of a message.
[3] 4.2.2.2 Buffer discovery and setup
This version of the Framework enables discovery and setup of RX/TX buffer pairs between FF-A components as follows.
...
2. An endpoint could allocate the buffer pair and use the FFA_RXTX_MAP interface to map it with the Hypervisor or SPM as applicable.
[4] 4.2.2.3 Buffer attributes
An endpoint must discover the minimum size and alignment boundary for the RX/TX buffers by passing the function ID of the FFA_RXTX_MAP ABI as input in the FFA_FEATURES interface (see 8.2 FFA_FEATURES).
[5] 6.1.1 Common compliance requirements
- It must be possible for the FF-A components at an FF-A instance to discover the presence and version number of their Framework implementations through the FFA_VERSION interface (see 8.1 FFA_VERSION).
- All FF-A components must implement support for Setup and discovery interfaces described in Chapter 8 Setup and discovery interfaces. These interfaces are as follows.
FFA_VERSION. FFA_FEATURES. FFA_RX_RELEASE. FFA_RXTX_MAP. FFA_RXTX_UNMAP. FFA_PARTITION_INFO_GET. FFA_ID_GET.
- It must be possible for an FF-A component, at the lower EL at an FF-A instance to use the FFA_FEATURES interface (see 8.2 FFA_FEATURES) to discover if an FF-A interface is implemented by the FF-A component at the higher EL.
[6] FF-A support readme: https://lore.kernel.org/all/20221122131751.22747-4-abdellatif.elkhlifi@arm.c... [7] FF-A MM comms: https://lore.kernel.org/all/20221122131751.22747-10-abdellatif.elkhlifi@arm....
-- Tom

On Thu, Jan 19, 2023 at 04:31:57PM +0000, Abdellatif El Khlifi wrote:
On Wed, Jan 18, 2023 at 08:59:32AM -0500, Tom Rini wrote:
On Wed, Jan 18, 2023 at 01:46:54PM +0000, Sudeep Holla wrote:
On Wed, Jan 18, 2023 at 12:49 PM Tom Rini trini@konsulko.com wrote:
I guess the problem comes down to, can we have one discovery method that everyone shares, or do we have to let everyone invent a new discovery method every time?
No one needs to invent any discovery method every time if the firmware specification provides one and as Rob mentioned many times in the thread, all new firmware specification must provide one and we are trying to make sure that is the case with all new specs from Arm.
FF-A, Op-tee, U-Boot, coreboot, barebox (and everyone else I'm unintentionally forgetting) could just discover these things via device tree.
I leave that to the individual projects to decide and agree but fundamentally if the specification provides a way to discover, not sure why we are even discussing an alternative method here.
Or, we could all write our own code to perform the discovery.
For what reason ? I can understand if there is no discovery mechanism but that's not the case in $subject.
And when RISC-V comes along with similar functionality, we could probe their device tree and see they've implemented the same concept, but a little differently, but still have the discovery portion be in the device tree. To which it sounds like your answer is "not in the device tree".
I see U-boot seem to have made a decision to create DT node for each and everything that needs to be added to DM which seems bit unfortunate but I don't understand the history/motive/background for it but I respect the decision if it is already made.
These firmware interfaces are standard on all Arm platforms and can be discovered based on PSCI/SMCCC. Not using the same and use DT node needs unnecessary addition of DT nodes for all the f/w i/f on all the platforms that need the support when one can be just discovered.
Sorry for the sudden appearance on this thread, I was avoiding getting into this but thought I will at least express my opinion and also the way the firmware specifications from Arm is expected to be evolved from now on. With that I will leave it to you and other U-boot maintainers and the community in general to decide the right course in this case.
To be clear, if the position is that "this is what everyone else will use, really" then yes, we'll follow this in U-Boot.
Hi Simon, Tom,
The FF-A transport is a SW bus and is not associated to any HW peripheral or undiscoverable base address.
There is only 1 way of discovering the FF-A bus and it's through the FF-A SW interfaces. The FF-A spec [1] describes this in details.
Discovering means gathering information about the FF-A framework such as: the FF-A version, supported features, secure partitions number and attributes.
Please refer to the following paragraphs for more details: [2], [3], [4], [5]
The core driver provided by this patchset implements the Setup and discovery interfaces in addition to direct messaging.
The driver provides ffa_bus_discover() API that allows to discover the FF-A bus as described by the spec and in the FF-A driver readme [6].
We expect and highly recommend FF-A users to always discover the FF-A bus using ffa_bus_discover() API.
A use case is provided which is the EFI MM communication [7].
ffa_bus_discover() does the following:
- creates, binds and probes the arm_ffa device
- at probe level, discovery FF-A interfaces are called to try to discover the FF-A framework
- when all discovery interfaces succeed, probing is successful and FF-A bus is ready to use
- if one of the discovery interfaces fails, the arm_ffa device is removed from the DM and FF-A bus can not be used
Cheers Abdellatif
[1]: FF-A spec version 1.0, https://developer.arm.com/documentation/den0077/latest/
[2] 2.8 Partition identification and discovery
All FF-A components can discover the identities and properties of other partitions through the FFA_PARTITION_INFO_GET interface. Once discovered, the IDs must be used in the messaging interfaces to identify the target of a message.
[3] 4.2.2.2 Buffer discovery and setup
This version of the Framework enables discovery and setup of RX/TX buffer pairs between FF-A components as follows. ... 2. An endpoint could allocate the buffer pair and use the FFA_RXTX_MAP interface to map it with the Hypervisor or SPM as applicable.
[4] 4.2.2.3 Buffer attributes
An endpoint must discover the minimum size and alignment boundary for the RX/TX buffers by passing the function ID of the FFA_RXTX_MAP ABI as input in the FFA_FEATURES interface (see 8.2 FFA_FEATURES).
[5] 6.1.1 Common compliance requirements
- It must be possible for the FF-A components at an FF-A instance to discover the presence and version number of their Framework implementations through the FFA_VERSION interface (see 8.1 FFA_VERSION). - All FF-A components must implement support for Setup and discovery interfaces described in Chapter 8 Setup and discovery interfaces. These interfaces are as follows. FFA_VERSION. FFA_FEATURES. FFA_RX_RELEASE. FFA_RXTX_MAP. FFA_RXTX_UNMAP. FFA_PARTITION_INFO_GET. FFA_ID_GET. - It must be possible for an FF-A component, at the lower EL at an FF-A instance to use the FFA_FEATURES interface (see 8.2 FFA_FEATURES) to discover if an FF-A interface is implemented by the FF-A component at the higher EL.
[6] FF-A support readme: https://lore.kernel.org/all/20221122131751.22747-4-abdellatif.elkhlifi@arm.c... [7] FF-A MM comms: https://lore.kernel.org/all/20221122131751.22747-10-abdellatif.elkhlifi@arm....
Since I'm apparently being unclear, let me try again. Yes, fine, for U-Boot, we'll go ahead and accept patches that implement this spec, and not require device tree modifications.
But as also I believe been agreed on, this doesn't prevent some other architecture / group from coming along and claiming it has a new unique approach to this problem and so has to re-invent the discoverable software bus wheel. Nor does it prevent some group from inventing a different software defined bus and discovery method and not re-using any of this work, because it too is special and unique and somehow different enough.

Hi Abdellatif,
On Thu, 19 Jan 2023 at 09:32, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
On Wed, Jan 18, 2023 at 08:59:32AM -0500, Tom Rini wrote:
On Wed, Jan 18, 2023 at 01:46:54PM +0000, Sudeep Holla wrote:
On Wed, Jan 18, 2023 at 12:49 PM Tom Rini trini@konsulko.com wrote:
I guess the problem comes down to, can we have one discovery method that everyone shares, or do we have to let everyone invent a new discovery method every time?
No one needs to invent any discovery method every time if the firmware specification provides one and as Rob mentioned many times in the thread, all new firmware specification must provide one and we are trying to make sure that is the case with all new specs from Arm.
FF-A, Op-tee, U-Boot, coreboot, barebox (and everyone else I'm unintentionally forgetting) could just discover these things via device tree.
I leave that to the individual projects to decide and agree but fundamentally if the specification provides a way to discover, not sure why we are even discussing an alternative method here.
Or, we could all write our own code to perform the discovery.
For what reason ? I can understand if there is no discovery mechanism but that's not the case in $subject.
And when RISC-V comes along with similar functionality, we could probe their device tree and see they've implemented the same concept, but a little differently, but still have the discovery portion be in the device tree. To which it sounds like your answer is "not in the device tree".
I see U-boot seem to have made a decision to create DT node for each and everything that needs to be added to DM which seems bit unfortunate but I don't understand the history/motive/background for it but I respect the decision if it is already made.
These firmware interfaces are standard on all Arm platforms and can be discovered based on PSCI/SMCCC. Not using the same and use DT node needs unnecessary addition of DT nodes for all the f/w i/f on all the platforms that need the support when one can be just discovered.
Sorry for the sudden appearance on this thread, I was avoiding getting into this but thought I will at least express my opinion and also the way the firmware specifications from Arm is expected to be evolved from now on. With that I will leave it to you and other U-boot maintainers and the community in general to decide the right course in this case.
To be clear, if the position is that "this is what everyone else will use, really" then yes, we'll follow this in U-Boot.
Hi Simon, Tom,
The FF-A transport is a SW bus and is not associated to any HW peripheral or undiscoverable base address.
There is only 1 way of discovering the FF-A bus and it's through the FF-A SW interfaces. The FF-A spec [1] describes this in details.
Can you add a DT node for the 'FF-A SW interfaces' and attach some sort of top-level driver to that? Perhaps simple-bus, or your own thing? You don't need to add compatible strings for subnodes (devices that are discoverable within that).
If you don't want to submit the compatible string to Linux, I will do it. If it has to have a 'u-boot,' prefix then so be it, but I don't see why that is necessary, since Linux can ignore it if it likes.
We have been talking about this for far too long, IMO. Would you like me to send a patch? It is something like this:
ff-a { compatible = "arm,ff-a"; };
Discovering means gathering information about the FF-A framework such as: the FF-A version, supported features, secure partitions number and attributes.
Please refer to the following paragraphs for more details: [2], [3], [4], [5]
The core driver provided by this patchset implements the Setup and discovery interfaces in addition to direct messaging.
The driver provides ffa_bus_discover() API that allows to discover the FF-A bus as described by the spec and in the FF-A driver readme [6].
We expect and highly recommend FF-A users to always discover the FF-A bus using ffa_bus_discover() API.
A use case is provided which is the EFI MM communication [7].
ffa_bus_discover() does the following:
- creates, binds and probes the arm_ffa device
- at probe level, discovery FF-A interfaces are called to try to discover the FF-A framework
- when all discovery interfaces succeed, probing is successful and FF-A bus is ready to use
- if one of the discovery interfaces fails, the arm_ffa device is removed from the DM and FF-A bus can not be used
This is not how things are supposed to work in U-Boot. Please read the documentation which is here:
https://u-boot.readthedocs.io/en/latest/develop/driver-model/index.html
So referencing above:
1, No, the binding of the ff-a device should happen automatically from the DT 2. probing ff-a causes the other devices to be bound (as with PCI, USB, every other bus in U-Boot) 3. Yes 4. No, you must not unbind it. It just sits there unprobed and cannot be used. We might want to have a command that looks at what is wrong with it. Probing the device should produce an error, as with every other device in U-Boot
I am happy to discuss this on a call if we really cannot do this by email. Or I can send a patch...but please read the documentation and avoid adding special cases for this interface.
Regards, Simon
Cheers Abdellatif
[1]: FF-A spec version 1.0, https://developer.arm.com/documentation/den0077/latest/
[2] 2.8 Partition identification and discovery
All FF-A components can discover the identities and properties of other partitions through the FFA_PARTITION_INFO_GET interface. Once discovered, the IDs must be used in the messaging interfaces to identify the target of a message.
[3] 4.2.2.2 Buffer discovery and setup
This version of the Framework enables discovery and setup of RX/TX buffer pairs between FF-A components as follows. ... 2. An endpoint could allocate the buffer pair and use the FFA_RXTX_MAP interface to map it with the Hypervisor or SPM as applicable.
[4] 4.2.2.3 Buffer attributes
An endpoint must discover the minimum size and alignment boundary for the RX/TX buffers by passing the function ID of the FFA_RXTX_MAP ABI as input in the FFA_FEATURES interface (see 8.2 FFA_FEATURES).
[5] 6.1.1 Common compliance requirements
- It must be possible for the FF-A components at an FF-A instance to discover the presence and version number of their Framework implementations through the FFA_VERSION interface (see 8.1 FFA_VERSION). - All FF-A components must implement support for Setup and discovery interfaces described in Chapter 8 Setup and discovery interfaces. These interfaces are as follows. FFA_VERSION. FFA_FEATURES. FFA_RX_RELEASE. FFA_RXTX_MAP. FFA_RXTX_UNMAP. FFA_PARTITION_INFO_GET. FFA_ID_GET. - It must be possible for an FF-A component, at the lower EL at an FF-A instance to use the FFA_FEATURES interface (see 8.2 FFA_FEATURES) to discover if an FF-A interface is implemented by the FF-A component at the higher EL.
[6] FF-A support readme: https://lore.kernel.org/all/20221122131751.22747-4-abdellatif.elkhlifi@arm.c... [7] FF-A MM comms: https://lore.kernel.org/all/20221122131751.22747-10-abdellatif.elkhlifi@arm....
-- Tom

On Thu, Jan 19, 2023 at 09:41:12AM -0700, Simon Glass wrote:
Hi Abdellatif,
On Thu, 19 Jan 2023 at 09:32, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
On Wed, Jan 18, 2023 at 08:59:32AM -0500, Tom Rini wrote:
On Wed, Jan 18, 2023 at 01:46:54PM +0000, Sudeep Holla wrote:
On Wed, Jan 18, 2023 at 12:49 PM Tom Rini trini@konsulko.com wrote:
I guess the problem comes down to, can we have one discovery method that everyone shares, or do we have to let everyone invent a new discovery method every time?
No one needs to invent any discovery method every time if the firmware specification provides one and as Rob mentioned many times in the thread, all new firmware specification must provide one and we are trying to make sure that is the case with all new specs from Arm.
FF-A, Op-tee, U-Boot, coreboot, barebox (and everyone else I'm unintentionally forgetting) could just discover these things via device tree.
I leave that to the individual projects to decide and agree but fundamentally if the specification provides a way to discover, not sure why we are even discussing an alternative method here.
Or, we could all write our own code to perform the discovery.
For what reason ? I can understand if there is no discovery mechanism but that's not the case in $subject.
And when RISC-V comes along with similar functionality, we could probe their device tree and see they've implemented the same concept, but a little differently, but still have the discovery portion be in the device tree. To which it sounds like your answer is "not in the device tree".
I see U-boot seem to have made a decision to create DT node for each and everything that needs to be added to DM which seems bit unfortunate but I don't understand the history/motive/background for it but I respect the decision if it is already made.
These firmware interfaces are standard on all Arm platforms and can be discovered based on PSCI/SMCCC. Not using the same and use DT node needs unnecessary addition of DT nodes for all the f/w i/f on all the platforms that need the support when one can be just discovered.
Sorry for the sudden appearance on this thread, I was avoiding getting into this but thought I will at least express my opinion and also the way the firmware specifications from Arm is expected to be evolved from now on. With that I will leave it to you and other U-boot maintainers and the community in general to decide the right course in this case.
To be clear, if the position is that "this is what everyone else will use, really" then yes, we'll follow this in U-Boot.
Hi Simon, Tom,
The FF-A transport is a SW bus and is not associated to any HW peripheral or undiscoverable base address.
There is only 1 way of discovering the FF-A bus and it's through the FF-A SW interfaces. The FF-A spec [1] describes this in details.
Can you add a DT node for the 'FF-A SW interfaces' and attach some sort of top-level driver to that? Perhaps simple-bus, or your own thing? You don't need to add compatible strings for subnodes (devices that are discoverable within that).
If you don't want to submit the compatible string to Linux, I will do it. If it has to have a 'u-boot,' prefix then so be it, but I don't see why that is necessary, since Linux can ignore it if it likes.
We have been talking about this for far too long, IMO. Would you like me to send a patch? It is something like this:
ff-a { compatible = "arm,ff-a"; };
No, we don't need a DT node here. Everyone else is insisting that we can solve the problems without it. So, lets go ahead and prove it. The approach they're describing can be integrated without a device tree node, in to the rest of the framework we have.

Hi Simon,
(sorry we just crossed the emails)
On Thu, Jan 19, 2023 at 09:41:12AM -0700, Simon Glass wrote:
Can you add a DT node for the 'FF-A SW interfaces' and attach some sort of top-level driver to that? Perhaps simple-bus, or your own thing? You don't need to add compatible strings for subnodes (devices that are discoverable within that).
Thanks for putting this nicely. I just wrote the same thing probably in not so simpler way. But I agree with you as Abdellatif last email talks more around sub-nodes or child device nodes (devices that are discoverable within that)
If you don't want to submit the compatible string to Linux, I will do it. If it has to have a 'u-boot,' prefix then so be it, but I don't see why that is necessary, since Linux can ignore it if it likes.
We have been talking about this for far too long, IMO. Would you like me to send a patch? It is something like this:
ff-a { compatible = "arm,ff-a"; };
Makes sense if DT node is the only way. It should be as simple as this and presence of this must not imply presence of FF-A feature on the platform. The driver must check using FFA_VERSION
Discovering means gathering information about the FF-A framework such as: the FF-A version, supported features, secure partitions number and attributes.
Please refer to the following paragraphs for more details: [2], [3], [4], [5]
The core driver provided by this patchset implements the Setup and discovery interfaces in addition to direct messaging.
The driver provides ffa_bus_discover() API that allows to discover the FF-A bus as described by the spec and in the FF-A driver readme [6].
We expect and highly recommend FF-A users to always discover the FF-A bus using ffa_bus_discover() API.
A use case is provided which is the EFI MM communication [7].
ffa_bus_discover() does the following:
- creates, binds and probes the arm_ffa device
- at probe level, discovery FF-A interfaces are called to try to discover the FF-A framework
- when all discovery interfaces succeed, probing is successful and FF-A bus is ready to use
- if one of the discovery interfaces fails, the arm_ffa device is removed from the DM and FF-A bus can not be used
This is not how things are supposed to work in U-Boot. Please read the documentation which is here:
https://u-boot.readthedocs.io/en/latest/develop/driver-model/index.html
So referencing above:
1, No, the binding of the ff-a device should happen automatically from the DT 2. probing ff-a causes the other devices to be bound (as with PCI, USB, every other bus in U-Boot) 3. Yes 4. No, you must not unbind it. It just sits there unprobed and cannot be used. We might want to have a command that looks at what is wrong with it. Probing the device should produce an error, as with every other device in U-Boot
Agreed on all points(assuming DT way here). Especially the last point, there is no point is rolling back if one partion/device initialisation fails or is not bound.
-- Regards, Sudeep

On Thu, Jan 19, 2023 at 04:56:09PM +0000, Sudeep Holla wrote:
Hi Simon,
(sorry we just crossed the emails)
On Thu, Jan 19, 2023 at 09:41:12AM -0700, Simon Glass wrote:
Can you add a DT node for the 'FF-A SW interfaces' and attach some sort of top-level driver to that? Perhaps simple-bus, or your own thing? You don't need to add compatible strings for subnodes (devices that are discoverable within that).
Thanks for putting this nicely. I just wrote the same thing probably in not so simpler way. But I agree with you as Abdellatif last email talks more around sub-nodes or child device nodes (devices that are discoverable within that)
Hi Sudeep,
A gentle reminder about what has been said in my last reply:
I didn't talk about sub-nodes or child nodes. The FF-A driver in u-boot doesn't do that.
In this patchset, only 1 device is created: The FF-A bus device (arm_ffa).
The discovery done by the driver queries all the discovery interfaces described in Chapter 8 of the spec, not just FFA_VERSION.
The discovery interfaces are queried in this order:
FFA_VERSION. FFA_ID_GET. FFA_FEATURES. FFA_PARTITION_INFO_GET.
Setup interfaces are used during discovery as well.
If one of these interfaces fail, the discovery fails.
The discovery from this patchset point of view is the discovery process involving the interfaces above.
Cheers
If you don't want to submit the compatible string to Linux, I will do it. If it has to have a 'u-boot,' prefix then so be it, but I don't see why that is necessary, since Linux can ignore it if it likes.
We have been talking about this for far too long, IMO. Would you like me to send a patch? It is something like this:
ff-a { compatible = "arm,ff-a"; };
Makes sense if DT node is the only way. It should be as simple as this and presence of this must not imply presence of FF-A feature on the platform. The driver must check using FFA_VERSION
Discovering means gathering information about the FF-A framework such as: the FF-A version, supported features, secure partitions number and attributes.
Please refer to the following paragraphs for more details: [2], [3], [4], [5]
The core driver provided by this patchset implements the Setup and discovery interfaces in addition to direct messaging.
The driver provides ffa_bus_discover() API that allows to discover the FF-A bus as described by the spec and in the FF-A driver readme [6].
We expect and highly recommend FF-A users to always discover the FF-A bus using ffa_bus_discover() API.
A use case is provided which is the EFI MM communication [7].
ffa_bus_discover() does the following:
- creates, binds and probes the arm_ffa device
- at probe level, discovery FF-A interfaces are called to try to discover the FF-A framework
- when all discovery interfaces succeed, probing is successful and FF-A bus is ready to use
- if one of the discovery interfaces fails, the arm_ffa device is removed from the DM and FF-A bus can not be used
This is not how things are supposed to work in U-Boot. Please read the documentation which is here:
https://u-boot.readthedocs.io/en/latest/develop/driver-model/index.html
So referencing above:
1, No, the binding of the ff-a device should happen automatically from the DT 2. probing ff-a causes the other devices to be bound (as with PCI, USB, every other bus in U-Boot) 3. Yes 4. No, you must not unbind it. It just sits there unprobed and cannot be used. We might want to have a command that looks at what is wrong with it. Probing the device should produce an error, as with every other device in U-Boot
Agreed on all points(assuming DT way here). Especially the last point, there is no point is rolling back if one partion/device initialisation fails or is not bound.
-- Regards, Sudeep

On Thu, Jan 19, 2023 at 10:41 AM Simon Glass sjg@chromium.org wrote:
Hi Abdellatif,
On Thu, 19 Jan 2023 at 09:32, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
On Wed, Jan 18, 2023 at 08:59:32AM -0500, Tom Rini wrote:
On Wed, Jan 18, 2023 at 01:46:54PM +0000, Sudeep Holla wrote:
On Wed, Jan 18, 2023 at 12:49 PM Tom Rini trini@konsulko.com wrote:
I guess the problem comes down to, can we have one discovery method that everyone shares, or do we have to let everyone invent a new discovery method every time?
No one needs to invent any discovery method every time if the firmware specification provides one and as Rob mentioned many times in the thread, all new firmware specification must provide one and we are trying to make sure that is the case with all new specs from Arm.
FF-A, Op-tee, U-Boot, coreboot, barebox (and everyone else I'm unintentionally forgetting) could just discover these things via device tree.
I leave that to the individual projects to decide and agree but fundamentally if the specification provides a way to discover, not sure why we are even discussing an alternative method here.
Or, we could all write our own code to perform the discovery.
For what reason ? I can understand if there is no discovery mechanism but that's not the case in $subject.
And when RISC-V comes along with similar functionality, we could probe their device tree and see they've implemented the same concept, but a little differently, but still have the discovery portion be in the device tree. To which it sounds like your answer is "not in the device tree".
I see U-boot seem to have made a decision to create DT node for each and everything that needs to be added to DM which seems bit unfortunate but I don't understand the history/motive/background for it but I respect the decision if it is already made.
These firmware interfaces are standard on all Arm platforms and can be discovered based on PSCI/SMCCC. Not using the same and use DT node needs unnecessary addition of DT nodes for all the f/w i/f on all the platforms that need the support when one can be just discovered.
Sorry for the sudden appearance on this thread, I was avoiding getting into this but thought I will at least express my opinion and also the way the firmware specifications from Arm is expected to be evolved from now on. With that I will leave it to you and other U-boot maintainers and the community in general to decide the right course in this case.
To be clear, if the position is that "this is what everyone else will use, really" then yes, we'll follow this in U-Boot.
Hi Simon, Tom,
The FF-A transport is a SW bus and is not associated to any HW peripheral or undiscoverable base address.
There is only 1 way of discovering the FF-A bus and it's through the FF-A SW interfaces. The FF-A spec [1] describes this in details.
Can you add a DT node for the 'FF-A SW interfaces' and attach some sort of top-level driver to that? Perhaps simple-bus, or your own thing? You don't need to add compatible strings for subnodes (devices that are discoverable within that).
We already have that. It's just called 'arm,psci'. FF-A is not the top-level thing. SMCCC is. That's unfortunately called PSCI in DT because SMCCC grew out of PSCI. Evolution is ugly...
It's like this:
SMCCC +--PSCI +--TRNG +--FF-A +--SCMI (sometimes) +--OP-TEE +--...Whatever Arm comes up with next...
Rob

On Thu, Jan 19, 2023 at 12:11:34PM -0600, Rob Herring wrote:
On Thu, Jan 19, 2023 at 10:41 AM Simon Glass sjg@chromium.org wrote:
Can you add a DT node for the 'FF-A SW interfaces' and attach some sort of top-level driver to that? Perhaps simple-bus, or your own thing? You don't need to add compatible strings for subnodes (devices that are discoverable within that).
We already have that. It's just called 'arm,psci'. FF-A is not the top-level thing. SMCCC is. That's unfortunately called PSCI in DT because SMCCC grew out of PSCI. Evolution is ugly...
It's like this:
SMCCC +--PSCI +--TRNG +--FF-A +--SCMI (sometimes) +--OP-TEE +--...Whatever Arm comes up with next...
Thanks Rob for the nice description.
Hi Simon,
Though SMCCC and PSCI are kind of swapped in reality like Rob mentioned while referring to the ugly evolution, we are trying to stick with PSCI node and discovery other features dynamically in the kernel. Hope the reasons for not defining the extra unnecessary bindings for FF-A is clear now.
There are couple of other bindings IIRC, one being for the OPTEE for example which again could have been avoid if we had better idea on how these SMCCC and its users would have evolved.
-- Regards, Sudeep

Hi Rob,
On Thu, 19 Jan 2023 at 11:11, Rob Herring robh@kernel.org wrote:
On Thu, Jan 19, 2023 at 10:41 AM Simon Glass sjg@chromium.org wrote:
Hi Abdellatif,
On Thu, 19 Jan 2023 at 09:32, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
On Wed, Jan 18, 2023 at 08:59:32AM -0500, Tom Rini wrote:
On Wed, Jan 18, 2023 at 01:46:54PM +0000, Sudeep Holla wrote:
On Wed, Jan 18, 2023 at 12:49 PM Tom Rini trini@konsulko.com wrote:
I guess the problem comes down to, can we have one discovery method that everyone shares, or do we have to let everyone invent a new discovery method every time?
No one needs to invent any discovery method every time if the firmware specification provides one and as Rob mentioned many times in the thread, all new firmware specification must provide one and we are trying to make sure that is the case with all new specs from Arm.
FF-A, Op-tee, U-Boot, coreboot, barebox (and everyone else I'm unintentionally forgetting) could just discover these things via device tree.
I leave that to the individual projects to decide and agree but fundamentally if the specification provides a way to discover, not sure why we are even discussing an alternative method here.
Or, we could all write our own code to perform the discovery.
For what reason ? I can understand if there is no discovery mechanism but that's not the case in $subject.
And when RISC-V comes along with similar functionality, we could probe their device tree and see they've implemented the same concept, but a little differently, but still have the discovery portion be in the device tree. To which it sounds like your answer is "not in the device tree".
I see U-boot seem to have made a decision to create DT node for each and everything that needs to be added to DM which seems bit unfortunate but I don't understand the history/motive/background for it but I respect the decision if it is already made.
These firmware interfaces are standard on all Arm platforms and can be discovered based on PSCI/SMCCC. Not using the same and use DT node needs unnecessary addition of DT nodes for all the f/w i/f on all the platforms that need the support when one can be just discovered.
Sorry for the sudden appearance on this thread, I was avoiding getting into this but thought I will at least express my opinion and also the way the firmware specifications from Arm is expected to be evolved from now on. With that I will leave it to you and other U-boot maintainers and the community in general to decide the right course in this case.
To be clear, if the position is that "this is what everyone else will use, really" then yes, we'll follow this in U-Boot.
Hi Simon, Tom,
The FF-A transport is a SW bus and is not associated to any HW peripheral or undiscoverable base address.
There is only 1 way of discovering the FF-A bus and it's through the FF-A SW interfaces. The FF-A spec [1] describes this in details.
Can you add a DT node for the 'FF-A SW interfaces' and attach some sort of top-level driver to that? Perhaps simple-bus, or your own thing? You don't need to add compatible strings for subnodes (devices that are discoverable within that).
We already have that. It's just called 'arm,psci'. FF-A is not the top-level thing. SMCCC is. That's unfortunately called PSCI in DT because SMCCC grew out of PSCI. Evolution is ugly...
It's like this:
SMCCC +--PSCI +--TRNG +--FF-A +--SCMI (sometimes) +--OP-TEE +--...Whatever Arm comes up with next...
OK well that sounds OK.
So what is the problem here? We have an SMCCC top-level thing in the DT and everything else can be bound from that, right? Are people on this thread not aware of this...or am I still missing something?
Can you point to the SMCCC driver in U-Boot? Is this bind_smccc_features(), i.w.c. it looks like it does what I want...why does this thread exist?
So confused...:-)
Regards, Simon

On Fri, Jan 20, 2023 at 4:04 PM Simon Glass sjg@chromium.org wrote:
Hi Rob,
On Thu, 19 Jan 2023 at 11:11, Rob Herring robh@kernel.org wrote:
On Thu, Jan 19, 2023 at 10:41 AM Simon Glass sjg@chromium.org wrote:
Hi Abdellatif,
On Thu, 19 Jan 2023 at 09:32, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
On Wed, Jan 18, 2023 at 08:59:32AM -0500, Tom Rini wrote:
On Wed, Jan 18, 2023 at 01:46:54PM +0000, Sudeep Holla wrote:
On Wed, Jan 18, 2023 at 12:49 PM Tom Rini trini@konsulko.com wrote:
> > I guess the problem comes down to, can we have one discovery method that > everyone shares, or do we have to let everyone invent a new discovery > method every time?
No one needs to invent any discovery method every time if the firmware specification provides one and as Rob mentioned many times in the thread, all new firmware specification must provide one and we are trying to make sure that is the case with all new specs from Arm.
> FF-A, Op-tee, U-Boot, coreboot, barebox (and > everyone else I'm unintentionally forgetting) could just discover these > things via device tree.
I leave that to the individual projects to decide and agree but fundamentally if the specification provides a way to discover, not sure why we are even discussing an alternative method here.
> Or, we could all write our own code to perform > the discovery.
For what reason ? I can understand if there is no discovery mechanism but that's not the case in $subject.
> And when RISC-V comes along with similar functionality, > we could probe their device tree and see they've implemented the same > concept, but a little differently, but still have the discovery portion > be in the device tree. To which it sounds like your answer is "not in > the device tree". >
I see U-boot seem to have made a decision to create DT node for each and everything that needs to be added to DM which seems bit unfortunate but I don't understand the history/motive/background for it but I respect the decision if it is already made.
These firmware interfaces are standard on all Arm platforms and can be discovered based on PSCI/SMCCC. Not using the same and use DT node needs unnecessary addition of DT nodes for all the f/w i/f on all the platforms that need the support when one can be just discovered.
Sorry for the sudden appearance on this thread, I was avoiding getting into this but thought I will at least express my opinion and also the way the firmware specifications from Arm is expected to be evolved from now on. With that I will leave it to you and other U-boot maintainers and the community in general to decide the right course in this case.
To be clear, if the position is that "this is what everyone else will use, really" then yes, we'll follow this in U-Boot.
Hi Simon, Tom,
The FF-A transport is a SW bus and is not associated to any HW peripheral or undiscoverable base address.
There is only 1 way of discovering the FF-A bus and it's through the FF-A SW interfaces. The FF-A spec [1] describes this in details.
Can you add a DT node for the 'FF-A SW interfaces' and attach some sort of top-level driver to that? Perhaps simple-bus, or your own thing? You don't need to add compatible strings for subnodes (devices that are discoverable within that).
We already have that. It's just called 'arm,psci'. FF-A is not the top-level thing. SMCCC is. That's unfortunately called PSCI in DT because SMCCC grew out of PSCI. Evolution is ugly...
It's like this:
SMCCC +--PSCI +--TRNG +--FF-A +--SCMI (sometimes) +--OP-TEE +--...Whatever Arm comes up with next...
OK well that sounds OK.
So what is the problem here? We have an SMCCC top-level thing in the DT and everything else can be bound from that, right? Are people on this thread not aware of this...or am I still missing something?
Can you point to the SMCCC driver in U-Boot? Is this bind_smccc_features(), i.w.c. it looks like it does what I want...why does this thread exist?
I imagine the u-boot structure for all this has evolved like the bindings where each feature was developed independently. From my brief look at it, initialization of all the above features would need to be reworked to work as described.
Rob

Hi Rob,
On Mon, 23 Jan 2023 at 08:13, Rob Herring robh@kernel.org wrote:
On Fri, Jan 20, 2023 at 4:04 PM Simon Glass sjg@chromium.org wrote:
Hi Rob,
On Thu, 19 Jan 2023 at 11:11, Rob Herring robh@kernel.org wrote:
On Thu, Jan 19, 2023 at 10:41 AM Simon Glass sjg@chromium.org wrote:
Hi Abdellatif,
On Thu, 19 Jan 2023 at 09:32, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
On Wed, Jan 18, 2023 at 08:59:32AM -0500, Tom Rini wrote:
On Wed, Jan 18, 2023 at 01:46:54PM +0000, Sudeep Holla wrote: > On Wed, Jan 18, 2023 at 12:49 PM Tom Rini trini@konsulko.com wrote: > > > > > I guess the problem comes down to, can we have one discovery method that > > everyone shares, or do we have to let everyone invent a new discovery > > method every time? > > > No one needs to invent any discovery method every time if the firmware > specification > provides one and as Rob mentioned many times in the thread, all new firmware > specification must provide one and we are trying to make sure that is > the case with all new > specs from Arm. > > > > FF-A, Op-tee, U-Boot, coreboot, barebox (and > > everyone else I'm unintentionally forgetting) could just discover these > > things via device tree. > > > I leave that to the individual projects to decide and agree but > fundamentally if > the specification provides a way to discover, not sure why we are even > discussing > an alternative method here. > > > > Or, we could all write our own code to perform > > the discovery. > > > For what reason ? I can understand if there is no discovery mechanism but > that's not the > case in $subject. > > > > And when RISC-V comes along with similar functionality, > > we could probe their device tree and see they've implemented the same > > concept, but a little differently, but still have the discovery portion > > be in the device tree. To which it sounds like your answer is "not in > > the device tree". > > > > I see U-boot seem to have made a decision to create DT node for each and > everything > that needs to be added to DM which seems bit unfortunate but I don't > understand the > history/motive/background for it but I respect the decision if it is > already made. > > These firmware interfaces are standard on all Arm platforms and can be > discovered > based on PSCI/SMCCC. Not using the same and use DT node needs unnecessary > addition of DT nodes for all the f/w i/f on all the platforms that need the > support when > one can be just discovered. > > Sorry for the sudden appearance on this thread, I was avoiding getting into > this but thought > I will at least express my opinion and also the way the firmware > specifications from Arm is > expected to be evolved from now on. With that I will leave it to you and > other U-boot > maintainers and the community in general to decide the right course in this > case.
To be clear, if the position is that "this is what everyone else will use, really" then yes, we'll follow this in U-Boot.
Hi Simon, Tom,
The FF-A transport is a SW bus and is not associated to any HW peripheral or undiscoverable base address.
There is only 1 way of discovering the FF-A bus and it's through the FF-A SW interfaces. The FF-A spec [1] describes this in details.
Can you add a DT node for the 'FF-A SW interfaces' and attach some sort of top-level driver to that? Perhaps simple-bus, or your own thing? You don't need to add compatible strings for subnodes (devices that are discoverable within that).
We already have that. It's just called 'arm,psci'. FF-A is not the top-level thing. SMCCC is. That's unfortunately called PSCI in DT because SMCCC grew out of PSCI. Evolution is ugly...
It's like this:
SMCCC +--PSCI +--TRNG +--FF-A +--SCMI (sometimes) +--OP-TEE +--...Whatever Arm comes up with next...
OK well that sounds OK.
So what is the problem here? We have an SMCCC top-level thing in the DT and everything else can be bound from that, right? Are people on this thread not aware of this...or am I still missing something?
Can you point to the SMCCC driver in U-Boot? Is this bind_smccc_features(), i.w.c. it looks like it does what I want...why does this thread exist?
I imagine the u-boot structure for all this has evolved like the bindings where each feature was developed independently. From my brief look at it, initialization of all the above features would need to be reworked to work as described.
OK, then perhaps this is making more sense to me now.
Abdellatif, can you please look at the above? We should have one top-level node in the DT and have that driver bind the child devices.
Regards, Simon

On Mon, Jan 23, 2023 at 09:32:28AM -0700, Simon Glass wrote:
Hi Rob,
On Mon, 23 Jan 2023 at 08:13, Rob Herring robh@kernel.org wrote:
On Fri, Jan 20, 2023 at 4:04 PM Simon Glass sjg@chromium.org wrote:
Hi Rob,
On Thu, 19 Jan 2023 at 11:11, Rob Herring robh@kernel.org wrote:
On Thu, Jan 19, 2023 at 10:41 AM Simon Glass sjg@chromium.org wrote:
Hi Abdellatif,
On Thu, 19 Jan 2023 at 09:32, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
On Wed, Jan 18, 2023 at 08:59:32AM -0500, Tom Rini wrote: > On Wed, Jan 18, 2023 at 01:46:54PM +0000, Sudeep Holla wrote: > > On Wed, Jan 18, 2023 at 12:49 PM Tom Rini trini@konsulko.com wrote: > > > > > > > > I guess the problem comes down to, can we have one discovery method that > > > everyone shares, or do we have to let everyone invent a new discovery > > > method every time? > > > > > > No one needs to invent any discovery method every time if the firmware > > specification > > provides one and as Rob mentioned many times in the thread, all new firmware > > specification must provide one and we are trying to make sure that is > > the case with all new > > specs from Arm. > > > > > > > FF-A, Op-tee, U-Boot, coreboot, barebox (and > > > everyone else I'm unintentionally forgetting) could just discover these > > > things via device tree. > > > > > > I leave that to the individual projects to decide and agree but > > fundamentally if > > the specification provides a way to discover, not sure why we are even > > discussing > > an alternative method here. > > > > > > > Or, we could all write our own code to perform > > > the discovery. > > > > > > For what reason ? I can understand if there is no discovery mechanism but > > that's not the > > case in $subject. > > > > > > > And when RISC-V comes along with similar functionality, > > > we could probe their device tree and see they've implemented the same > > > concept, but a little differently, but still have the discovery portion > > > be in the device tree. To which it sounds like your answer is "not in > > > the device tree". > > > > > > > I see U-boot seem to have made a decision to create DT node for each and > > everything > > that needs to be added to DM which seems bit unfortunate but I don't > > understand the > > history/motive/background for it but I respect the decision if it is > > already made. > > > > These firmware interfaces are standard on all Arm platforms and can be > > discovered > > based on PSCI/SMCCC. Not using the same and use DT node needs unnecessary > > addition of DT nodes for all the f/w i/f on all the platforms that need the > > support when > > one can be just discovered. > > > > Sorry for the sudden appearance on this thread, I was avoiding getting into > > this but thought > > I will at least express my opinion and also the way the firmware > > specifications from Arm is > > expected to be evolved from now on. With that I will leave it to you and > > other U-boot > > maintainers and the community in general to decide the right course in this > > case. > > To be clear, if the position is that "this is what everyone else will > use, really" then yes, we'll follow this in U-Boot.
Hi Simon, Tom,
The FF-A transport is a SW bus and is not associated to any HW peripheral or undiscoverable base address.
There is only 1 way of discovering the FF-A bus and it's through the FF-A SW interfaces. The FF-A spec [1] describes this in details.
Can you add a DT node for the 'FF-A SW interfaces' and attach some sort of top-level driver to that? Perhaps simple-bus, or your own thing? You don't need to add compatible strings for subnodes (devices that are discoverable within that).
We already have that. It's just called 'arm,psci'. FF-A is not the top-level thing. SMCCC is. That's unfortunately called PSCI in DT because SMCCC grew out of PSCI. Evolution is ugly...
It's like this:
SMCCC +--PSCI +--TRNG +--FF-A +--SCMI (sometimes) +--OP-TEE +--...Whatever Arm comes up with next...
OK well that sounds OK.
So what is the problem here? We have an SMCCC top-level thing in the DT and everything else can be bound from that, right? Are people on this thread not aware of this...or am I still missing something?
Can you point to the SMCCC driver in U-Boot? Is this bind_smccc_features(), i.w.c. it looks like it does what I want...why does this thread exist?
I imagine the u-boot structure for all this has evolved like the bindings where each feature was developed independently. From my brief look at it, initialization of all the above features would need to be reworked to work as described.
OK, then perhaps this is making more sense to me now.
Abdellatif, can you please look at the above? We should have one top-level node in the DT and have that driver bind the child devices.
Regards, Simon
Hi Simon, Rob,
I'd like to suggest the creation of a root node called arm_smccc as shown below.
This node is the parent of all nodes using SMC calls like FF-A, PSCI, optee, ...
The first child to be integrated in the arm_smccc root node is FF-A. Hopefully, in the future the other features such as PSCI could be integrated under arm_smccc as well.
Th root node looks like this:
firmware { arm_smccc { compatible = "arm,smccc-1.2"; arm_ffa { compatible = "arm,ffa"; method = "smc"; };
psci { compatible = "arm,psci"; method = "smc"; };
optee { compatible = "linaro,optee-tz"; method = "smc"; };
}; };
What do you think guys ?
Kind regards, Abdellatif

Hi Abdellatif,
On Tue, 24 Jan 2023 at 08:56, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
On Mon, Jan 23, 2023 at 09:32:28AM -0700, Simon Glass wrote:
Hi Rob,
On Mon, 23 Jan 2023 at 08:13, Rob Herring robh@kernel.org wrote:
On Fri, Jan 20, 2023 at 4:04 PM Simon Glass sjg@chromium.org wrote:
Hi Rob,
On Thu, 19 Jan 2023 at 11:11, Rob Herring robh@kernel.org wrote:
On Thu, Jan 19, 2023 at 10:41 AM Simon Glass sjg@chromium.org wrote:
Hi Abdellatif,
On Thu, 19 Jan 2023 at 09:32, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote: > > On Wed, Jan 18, 2023 at 08:59:32AM -0500, Tom Rini wrote: > > On Wed, Jan 18, 2023 at 01:46:54PM +0000, Sudeep Holla wrote: > > > On Wed, Jan 18, 2023 at 12:49 PM Tom Rini trini@konsulko.com wrote: > > > > > > > > > > > I guess the problem comes down to, can we have one discovery method that > > > > everyone shares, or do we have to let everyone invent a new discovery > > > > method every time? > > > > > > > > > No one needs to invent any discovery method every time if the firmware > > > specification > > > provides one and as Rob mentioned many times in the thread, all new firmware > > > specification must provide one and we are trying to make sure that is > > > the case with all new > > > specs from Arm. > > > > > > > > > > FF-A, Op-tee, U-Boot, coreboot, barebox (and > > > > everyone else I'm unintentionally forgetting) could just discover these > > > > things via device tree. > > > > > > > > > I leave that to the individual projects to decide and agree but > > > fundamentally if > > > the specification provides a way to discover, not sure why we are even > > > discussing > > > an alternative method here. > > > > > > > > > > Or, we could all write our own code to perform > > > > the discovery. > > > > > > > > > For what reason ? I can understand if there is no discovery mechanism but > > > that's not the > > > case in $subject. > > > > > > > > > > And when RISC-V comes along with similar functionality, > > > > we could probe their device tree and see they've implemented the same > > > > concept, but a little differently, but still have the discovery portion > > > > be in the device tree. To which it sounds like your answer is "not in > > > > the device tree". > > > > > > > > > > I see U-boot seem to have made a decision to create DT node for each and > > > everything > > > that needs to be added to DM which seems bit unfortunate but I don't > > > understand the > > > history/motive/background for it but I respect the decision if it is > > > already made. > > > > > > These firmware interfaces are standard on all Arm platforms and can be > > > discovered > > > based on PSCI/SMCCC. Not using the same and use DT node needs unnecessary > > > addition of DT nodes for all the f/w i/f on all the platforms that need the > > > support when > > > one can be just discovered. > > > > > > Sorry for the sudden appearance on this thread, I was avoiding getting into > > > this but thought > > > I will at least express my opinion and also the way the firmware > > > specifications from Arm is > > > expected to be evolved from now on. With that I will leave it to you and > > > other U-boot > > > maintainers and the community in general to decide the right course in this > > > case. > > > > To be clear, if the position is that "this is what everyone else will > > use, really" then yes, we'll follow this in U-Boot. > > Hi Simon, Tom, > > The FF-A transport is a SW bus and is not associated to any HW peripheral or > undiscoverable base address. > > There is only 1 way of discovering the FF-A bus and it's through the FF-A SW > interfaces. The FF-A spec [1] describes this in details.
Can you add a DT node for the 'FF-A SW interfaces' and attach some sort of top-level driver to that? Perhaps simple-bus, or your own thing? You don't need to add compatible strings for subnodes (devices that are discoverable within that).
We already have that. It's just called 'arm,psci'. FF-A is not the top-level thing. SMCCC is. That's unfortunately called PSCI in DT because SMCCC grew out of PSCI. Evolution is ugly...
It's like this:
SMCCC +--PSCI +--TRNG +--FF-A +--SCMI (sometimes) +--OP-TEE +--...Whatever Arm comes up with next...
OK well that sounds OK.
So what is the problem here? We have an SMCCC top-level thing in the DT and everything else can be bound from that, right? Are people on this thread not aware of this...or am I still missing something?
Can you point to the SMCCC driver in U-Boot? Is this bind_smccc_features(), i.w.c. it looks like it does what I want...why does this thread exist?
I imagine the u-boot structure for all this has evolved like the bindings where each feature was developed independently. From my brief look at it, initialization of all the above features would need to be reworked to work as described.
OK, then perhaps this is making more sense to me now.
Abdellatif, can you please look at the above? We should have one top-level node in the DT and have that driver bind the child devices.
Regards, Simon
Hi Simon, Rob,
I'd like to suggest the creation of a root node called arm_smccc as shown below.
This node is the parent of all nodes using SMC calls like FF-A, PSCI, optee, ...
The first child to be integrated in the arm_smccc root node is FF-A. Hopefully, in the future the other features such as PSCI could be integrated under arm_smccc as well.
Th root node looks like this:
firmware { arm_smccc { compatible = "arm,smccc-1.2"; arm_ffa { compatible = "arm,ffa"; method = "smc"; }; psci { compatible = "arm,psci"; method = "smc"; }; optee { compatible = "linaro,optee-tz"; method = "smc"; }; }; };
What do you think guys ?
LGTM, thanks.
Regards, Simon

On Tue, Jan 24, 2023 at 03:56:19PM +0000, Abdellatif El Khlifi wrote:
I'd like to suggest the creation of a root node called arm_smccc as shown below.
Not sure if U-Boot already supports the existing binding of PSCI and OPTEE.
This node is the parent of all nodes using SMC calls like FF-A, PSCI, optee, ...
Well if you want to start with the clean slate for all new platforms, then it is a good way. But you don't need any of the child nodes as they can all be queried and discovered.
The first child to be integrated in the arm_smccc root node is FF-A. Hopefully, in the future the other features such as PSCI could be integrated under arm_smccc as well.
You don't need FF-A or any other node if the presence of SMCCC node indicates the support of SMCCC v1.1+(need to go back and check to confirm this). You can query the presence of all the feature. There is no need to have a node for each of the feature supported. That was the whole discussion of this thread IIUC.
-- Regards, Sudeep

On Wed, Jan 25, 2023 at 07:48:17AM +0000, Sudeep Holla wrote:
On Tue, Jan 24, 2023 at 03:56:19PM +0000, Abdellatif El Khlifi wrote:
I'd like to suggest the creation of a root node called arm_smccc as shown below.
Not sure if U-Boot already supports the existing binding of PSCI and OPTEE.
This node is the parent of all nodes using SMC calls like FF-A, PSCI, optee, ...
Well if you want to start with the clean slate for all new platforms, then it is a good way. But you don't need any of the child nodes as they can all be queried and discovered.
The first child to be integrated in the arm_smccc root node is FF-A. Hopefully, in the future the other features such as PSCI could be integrated under arm_smccc as well.
You don't need FF-A or any other node if the presence of SMCCC node indicates the support of SMCCC v1.1+(need to go back and check to confirm this). You can query the presence of all the feature. There is no need to have a node for each of the feature supported. That was the whole discussion of this thread IIUC.
-- Regards, Sudeep
Hi Simon, Rob, Sudeep
I'm tweaking my previous suggestion regarding the creation of a new arm_smccc root node.
This node is the parent of all SW features using SMC calls like FF-A, PSCI, optee, ...
However, no child node is created in the DT. The SW features are seen as architecture features and can be discovered by querying through SMC calls.
So, when probed the arm_smccc device driver tries to discover the SW features (FF-A, PSCI, optee, ...)
by calling xxx_discover() API for each feature ( e.g: ffa_discover() ).
The xxx_discover() creates U-Boot device(s) for the feature and set arm_smccc as the parent.
"dm tree" command should show:
SMCCC +--PSCI +--TRNG +--FF-A +--SCMI (sometimes) +--OP-TEE +--...Whatever Arm comes up with next...
As Sudeep suggested, we only need this node:
firmware { arm_smccc { compatible = "arm,smccc-1.2"; }; };
What do you think guys ? Are you happy with this approach ?
Kind regards, Abdellatif

On Wed, Jan 25, 2023 at 10:55:11AM +0000, Abdellatif El Khlifi wrote:
Hi Simon, Rob, Sudeep
I'm tweaking my previous suggestion regarding the creation of a new arm_smccc root node..
This node is the parent of all SW features using SMC calls like FF-A, PSCI, optee, ...
However, no child node is created in the DT. The SW features are seen as architecture features and can be discovered by querying through SMC calls.
So, when probed the arm_smccc device driver tries to discover the SW features (FF-A, PSCI, optee, ...) by calling xxx_discover() API for each feature ( e.g: ffa_discover() ).
The xxx_discover() creates U-Boot device(s) for the feature and set arm_smccc as the parent.
"dm tree" command should show:
SMCCC +--PSCI +--TRNG +--FF-A +--SCMI (sometimes) +--OP-TEE +--...Whatever Arm comes up with next...
As Sudeep suggested, we only need this node:
firmware { arm_smccc { compatible = "arm,smccc-1.2"; }; };
What do you think guys ? Are you happy with this approach ?
This looks good to me. No objections binding per se.
However if you look at Rob's earlier comments, due to the way all these firmware specs evolved and hence the corresponding binding, though PSCI uses SMCCC like all other specs in the above list/tree, the PSCI binding was introduced along with its implementation in the kernel. Also the fact that until SMCCC v1.1, there was no version query feature in SMCCC and PSCI_FEATURE was used instead. It is handled in the same way in the kernel today.
Not sure if that is the reason Rob suggested just using PSCI node like we do in the kernel today.
As I mentioned I am not against your suggestion as it would be the best way to start if we were doing it today, but we have legacy to handle. Let us see what is Rob's opinion. If we decide not to use PSCI like in the kernel and add this new SMCCC, we may have to handle that in the kernel and also preferably add checks in DTC to not have PSCI node if SMCCC is present or something on that lines.
-- Regards, Sudeep

On Tue, Jan 24, 2023 at 9:56 AM Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
On Mon, Jan 23, 2023 at 09:32:28AM -0700, Simon Glass wrote:
Hi Rob,
On Mon, 23 Jan 2023 at 08:13, Rob Herring robh@kernel.org wrote:
On Fri, Jan 20, 2023 at 4:04 PM Simon Glass sjg@chromium.org wrote:
Hi Rob,
On Thu, 19 Jan 2023 at 11:11, Rob Herring robh@kernel.org wrote:
On Thu, Jan 19, 2023 at 10:41 AM Simon Glass sjg@chromium.org wrote:
Hi Abdellatif,
On Thu, 19 Jan 2023 at 09:32, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote: > > On Wed, Jan 18, 2023 at 08:59:32AM -0500, Tom Rini wrote: > > On Wed, Jan 18, 2023 at 01:46:54PM +0000, Sudeep Holla wrote: > > > On Wed, Jan 18, 2023 at 12:49 PM Tom Rini trini@konsulko.com wrote: > > > > > > > > > > > I guess the problem comes down to, can we have one discovery method that > > > > everyone shares, or do we have to let everyone invent a new discovery > > > > method every time? > > > > > > > > > No one needs to invent any discovery method every time if the firmware > > > specification > > > provides one and as Rob mentioned many times in the thread, all new firmware > > > specification must provide one and we are trying to make sure that is > > > the case with all new > > > specs from Arm. > > > > > > > > > > FF-A, Op-tee, U-Boot, coreboot, barebox (and > > > > everyone else I'm unintentionally forgetting) could just discover these > > > > things via device tree. > > > > > > > > > I leave that to the individual projects to decide and agree but > > > fundamentally if > > > the specification provides a way to discover, not sure why we are even > > > discussing > > > an alternative method here. > > > > > > > > > > Or, we could all write our own code to perform > > > > the discovery. > > > > > > > > > For what reason ? I can understand if there is no discovery mechanism but > > > that's not the > > > case in $subject. > > > > > > > > > > And when RISC-V comes along with similar functionality, > > > > we could probe their device tree and see they've implemented the same > > > > concept, but a little differently, but still have the discovery portion > > > > be in the device tree. To which it sounds like your answer is "not in > > > > the device tree". > > > > > > > > > > I see U-boot seem to have made a decision to create DT node for each and > > > everything > > > that needs to be added to DM which seems bit unfortunate but I don't > > > understand the > > > history/motive/background for it but I respect the decision if it is > > > already made. > > > > > > These firmware interfaces are standard on all Arm platforms and can be > > > discovered > > > based on PSCI/SMCCC. Not using the same and use DT node needs unnecessary > > > addition of DT nodes for all the f/w i/f on all the platforms that need the > > > support when > > > one can be just discovered. > > > > > > Sorry for the sudden appearance on this thread, I was avoiding getting into > > > this but thought > > > I will at least express my opinion and also the way the firmware > > > specifications from Arm is > > > expected to be evolved from now on. With that I will leave it to you and > > > other U-boot > > > maintainers and the community in general to decide the right course in this > > > case. > > > > To be clear, if the position is that "this is what everyone else will > > use, really" then yes, we'll follow this in U-Boot. > > Hi Simon, Tom, > > The FF-A transport is a SW bus and is not associated to any HW peripheral or > undiscoverable base address. > > There is only 1 way of discovering the FF-A bus and it's through the FF-A SW > interfaces. The FF-A spec [1] describes this in details.
Can you add a DT node for the 'FF-A SW interfaces' and attach some sort of top-level driver to that? Perhaps simple-bus, or your own thing? You don't need to add compatible strings for subnodes (devices that are discoverable within that).
We already have that. It's just called 'arm,psci'. FF-A is not the top-level thing. SMCCC is. That's unfortunately called PSCI in DT because SMCCC grew out of PSCI. Evolution is ugly...
It's like this:
SMCCC +--PSCI +--TRNG +--FF-A +--SCMI (sometimes) +--OP-TEE +--...Whatever Arm comes up with next...
OK well that sounds OK.
So what is the problem here? We have an SMCCC top-level thing in the DT and everything else can be bound from that, right? Are people on this thread not aware of this...or am I still missing something?
Can you point to the SMCCC driver in U-Boot? Is this bind_smccc_features(), i.w.c. it looks like it does what I want...why does this thread exist?
I imagine the u-boot structure for all this has evolved like the bindings where each feature was developed independently. From my brief look at it, initialization of all the above features would need to be reworked to work as described.
OK, then perhaps this is making more sense to me now.
Abdellatif, can you please look at the above? We should have one top-level node in the DT and have that driver bind the child devices.
Regards, Simon
Hi Simon, Rob,
I'd like to suggest the creation of a root node called arm_smccc as shown below.
This node is the parent of all nodes using SMC calls like FF-A, PSCI, optee, ...
The first child to be integrated in the arm_smccc root node is FF-A. Hopefully, in the future the other features such as PSCI could be integrated under arm_smccc as well.
So now instead of 1 binding being added, you are proposing 2 new bindings ("arm,smccc-1.2" and "arm,ffa")?
Not only that, we'd have to support both the old way for all the existing platforms and the new way.
Th root node looks like this:
firmware { arm_smccc { compatible = "arm,smccc-1.2";
Stop here. That's all that needs to be in the DT. The rest is discoverable. If we could redesign everything, then this is what we would have. A node saying we have SMCCC. But we can't just redesign everything. However, we already have such a node with the PSCI node because PSCI implies SMCCC.
What u-boot needs is a driver for "arm,smccc-1.2" that then instantiates FFA, PSCI, TRNG, etc. without DT. Except that it would match on "arm,psci" instead of "arm,smccc-1.2".
Rob

On Wed, Jan 25, 2023 at 10:00:58AM -0600, Rob Herring wrote:
On Tue, Jan 24, 2023 at 9:56 AM Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
On Mon, Jan 23, 2023 at 09:32:28AM -0700, Simon Glass wrote:
Hi Rob,
On Mon, 23 Jan 2023 at 08:13, Rob Herring robh@kernel.org wrote:
On Fri, Jan 20, 2023 at 4:04 PM Simon Glass sjg@chromium.org wrote:
Hi Rob,
On Thu, 19 Jan 2023 at 11:11, Rob Herring robh@kernel.org wrote:
On Thu, Jan 19, 2023 at 10:41 AM Simon Glass sjg@chromium.org wrote: > > Hi Abdellatif, > > On Thu, 19 Jan 2023 at 09:32, Abdellatif El Khlifi > abdellatif.elkhlifi@arm.com wrote: > > > > On Wed, Jan 18, 2023 at 08:59:32AM -0500, Tom Rini wrote: > > > On Wed, Jan 18, 2023 at 01:46:54PM +0000, Sudeep Holla wrote: > > > > On Wed, Jan 18, 2023 at 12:49 PM Tom Rini trini@konsulko.com wrote: > > > > > > > > > > > > > > I guess the problem comes down to, can we have one discovery method that > > > > > everyone shares, or do we have to let everyone invent a new discovery > > > > > method every time? > > > > > > > > > > > > No one needs to invent any discovery method every time if the firmware > > > > specification > > > > provides one and as Rob mentioned many times in the thread, all new firmware > > > > specification must provide one and we are trying to make sure that is > > > > the case with all new > > > > specs from Arm. > > > > > > > > > > > > > FF-A, Op-tee, U-Boot, coreboot, barebox (and > > > > > everyone else I'm unintentionally forgetting) could just discover these > > > > > things via device tree. > > > > > > > > > > > > I leave that to the individual projects to decide and agree but > > > > fundamentally if > > > > the specification provides a way to discover, not sure why we are even > > > > discussing > > > > an alternative method here. > > > > > > > > > > > > > Or, we could all write our own code to perform > > > > > the discovery. > > > > > > > > > > > > For what reason ? I can understand if there is no discovery mechanism but > > > > that's not the > > > > case in $subject. > > > > > > > > > > > > > And when RISC-V comes along with similar functionality, > > > > > we could probe their device tree and see they've implemented the same > > > > > concept, but a little differently, but still have the discovery portion > > > > > be in the device tree. To which it sounds like your answer is "not in > > > > > the device tree". > > > > > > > > > > > > > I see U-boot seem to have made a decision to create DT node for each and > > > > everything > > > > that needs to be added to DM which seems bit unfortunate but I don't > > > > understand the > > > > history/motive/background for it but I respect the decision if it is > > > > already made. > > > > > > > > These firmware interfaces are standard on all Arm platforms and can be > > > > discovered > > > > based on PSCI/SMCCC. Not using the same and use DT node needs unnecessary > > > > addition of DT nodes for all the f/w i/f on all the platforms that need the > > > > support when > > > > one can be just discovered. > > > > > > > > Sorry for the sudden appearance on this thread, I was avoiding getting into > > > > this but thought > > > > I will at least express my opinion and also the way the firmware > > > > specifications from Arm is > > > > expected to be evolved from now on. With that I will leave it to you and > > > > other U-boot > > > > maintainers and the community in general to decide the right course in this > > > > case. > > > > > > To be clear, if the position is that "this is what everyone else will > > > use, really" then yes, we'll follow this in U-Boot. > > > > Hi Simon, Tom, > > > > The FF-A transport is a SW bus and is not associated to any HW peripheral or > > undiscoverable base address. > > > > There is only 1 way of discovering the FF-A bus and it's through the FF-A SW > > interfaces. The FF-A spec [1] describes this in details. > > Can you add a DT node for the 'FF-A SW interfaces' and attach some > sort of top-level driver to that? Perhaps simple-bus, or your own > thing? You don't need to add compatible strings for subnodes (devices > that are discoverable within that).
We already have that. It's just called 'arm,psci'. FF-A is not the top-level thing. SMCCC is. That's unfortunately called PSCI in DT because SMCCC grew out of PSCI. Evolution is ugly...
It's like this:
SMCCC +--PSCI +--TRNG +--FF-A +--SCMI (sometimes) +--OP-TEE +--...Whatever Arm comes up with next...
OK well that sounds OK.
So what is the problem here? We have an SMCCC top-level thing in the DT and everything else can be bound from that, right? Are people on this thread not aware of this...or am I still missing something?
Can you point to the SMCCC driver in U-Boot? Is this bind_smccc_features(), i.w.c. it looks like it does what I want...why does this thread exist?
I imagine the u-boot structure for all this has evolved like the bindings where each feature was developed independently. From my brief look at it, initialization of all the above features would need to be reworked to work as described.
OK, then perhaps this is making more sense to me now.
Abdellatif, can you please look at the above? We should have one top-level node in the DT and have that driver bind the child devices.
Regards, Simon
Hi Simon, Rob,
I'd like to suggest the creation of a root node called arm_smccc as shown below.
This node is the parent of all nodes using SMC calls like FF-A, PSCI, optee, ...
The first child to be integrated in the arm_smccc root node is FF-A. Hopefully, in the future the other features such as PSCI could be integrated under arm_smccc as well.
So now instead of 1 binding being added, you are proposing 2 new bindings ("arm,smccc-1.2" and "arm,ffa")?
Not only that, we'd have to support both the old way for all the existing platforms and the new way.
Th root node looks like this:
firmware { arm_smccc { compatible = "arm,smccc-1.2";
Stop here. That's all that needs to be in the DT. The rest is discoverable. If we could redesign everything, then this is what we would have. A node saying we have SMCCC. But we can't just redesign everything. However, we already have such a node with the PSCI node because PSCI implies SMCCC.
What u-boot needs is a driver for "arm,smccc-1.2" that then instantiates FFA, PSCI, TRNG, etc. without DT. Except that it would match on "arm,psci" instead of "arm,smccc-1.2".
Rob
Thanks Rob. I already suggested something similar in a newer message, please have a look [1]. But the approach I'm suggesting is without using a PSCI node anymore. Hopefully, the community would be interested in updating the older discovery designs to use the one and only DT node: arm_smccc.
When probed the arm_smccc device driver tries to discover the SW features (FF-A, PSCI, optee, ...) by calling xxx_discover() API for each feature.
firmware { arm_smccc { compatible = "arm,smccc-1.2"; }; };
As a proof of concept, we can provide a first example with FF-A. Other SW features (e.g: PSCI, optee, future ones, ...) can be added later on and replace the older way of discovery.
Future SW features using SMC can be discovered by arm_smccc as well. We can document this approach in U-Boot/Linux so future developments will follow this approach.
[1]: https://lore.kernel.org/all/20230125105511.GA31193@e121910.cambridge.arm.com...
Kind regards, Abdellatif

On Wed, Jan 25, 2023 at 04:44:34PM +0000, Abdellatif El Khlifi wrote:
[...]
Future SW features using SMC can be discovered by arm_smccc as well. We can document this approach in U-Boot/Linux so future developments will follow this approach.
OK as discussed in private, you must not need any new binding. Also looking (a very quick glance) at the U-Boot and in particular
"Commit 2fbe47b7e771 ("firmware: psci: bind arm smccc features when discovered")"
It looks like you have most of what you need already. Etienne has already added mechanism for SMCCC discovery. You just need to extend things from there if you need to support other features using SMCCC and discoverable via SMCCC. Hope that helps.

Adding support for Arm FF-A v1.0 (Arm Firmware Framework for Armv8-A) [A].
FF-A describes interfaces (ABIs) that standardize communication between the Secure World and Normal World. These interfaces enable a pair of software sandboxes to communicate with each other. A sandbox aka partition could be a VM in the Normal or Secure world, an application in S-EL0, or a Trusted OS in S-EL1.
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.
=> dm tree
Class Index Probed Driver Name ----------------------------------------------------------- ... firmware 0 [ + ] psci |-- psci ffa 0 [ ] arm_ffa | `-- arm_ffa ...
Clients are able to probe then use the FF-A bus by calling the DM class searching APIs (e.g: uclass_get_device_by_name).
This implementation of the specification provides support for Aarch64.
The FF-A driver uses the SMC ABIs defined by the FF-A specification to:
- Discover the presence of secure partitions (SPs) of interest - Access an SP's service through communication protocols e.g. EFI MM communication protocol
The FF-A support provides the following features:
- Being generic by design and can be used by any Arm 64-bit platform - The FF-A core driver - The driver provides driver operations to be used by clients to access the FF-A bus - FF-A driver can be compiled and used without EFI - Support for SMCCCv1.2 x0-x17 registers - Support for SMC32 calling convention - Support for 32-bit and 64-bit FF-A direct messaging - A new command called armffa is provided as an example of how to access the FF-A bus - An FF-A Sandbox driver is provided with test cases - Support for FF-A MM communication (compatible with EFI boot time) - Enabling FF-A and MM communication in Corstone1000 platform as a use case
For more details about the FF-A core driver please refer to [B] and refer to [C] for how to use the armffa command.
Please find at [D] an example of the expected boot logs when enabling FF-A support for a platform. In this example the platform is Corstone1000. But it can be any Arm 64-bit platform.
Changelog of the major changes: ===========================
v9:
* integrate the FF-A bus discovery in the DM and use ARM_SMCCC_FEATURES for binding * align FF-A sandbox driver with FF-A discovery through DM * use DM class APIs to probe and interact with the FF-A bus (in FF-A MM comms, armffa command, sandbox tests) * add documentation for the armffa command: doc/usage/cmd/armffa.rst * introduce testcase for uuid_str_to_le_bin
v8: [8]
* pass the FF-A bus device to the bus operations * isolate the compilation choices between FF-A and OP-TEE * drop OP-TEE configs from Corstone-1000 defconfig * make ffa_get_partitions_info() second argument to be an SP count in both modes
v7: [7]
* add support for 32-bit direct messaging (now we have 32-bit and 64-bit support) * set the MM door bell event to use 64-bit direct messaging * issue a compile time error when one of these macros are not found : FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_OFFSET, FFA_SHARED_MM_BUFFER_ADDR * make uuid_str_to_le_bin() implementation similar to uuid_str_to_bin() by using same APIs
v6: [6]
* remove clearing x0-x17 registers after SMC calls * drop use of EFI runtime support for FF-A (We decided with Linaro to add this later) * drop discovery from initcalls (discovery will be on demand by FF-A users) * add FF-A runtime discovery at MM communication level * update the documentation and move it to doc/arch/arm64.ffa.rst
v5: [5]
* move changelogs in each commit to the changes section
v4: [4]
* add FF-A support README (doc/README.ffa.drv) * improving error handling by mapping the FF-A errors to standard errors and logs * replacing panics with an error log * align sandbox driver and tests with the new FF-A driver interfaces and new way of error handling * use the new FF-A driver interfaces for MM communication * discover MM partitions at runtime * copy FF-A driver private data to EFI runtime section at ExitBootServices() * moving the FF-A driver work to drivers/firmware/arm-ffa * improving features discovery in FFA_FEATURES * add remove/unbind functions to the FF-A core device * improve how the driver behaves when bus discovery is done more than once * move clearing x0-x17 registers code into a new macro like done in the linux kernel * enable EFI MM communication for the Corstone1000 platform
v3: [3]
* port x0-x17 registers support from linux kernel as defined by SMCCCv1.2 * align the interfaces of the u-boot FF-A driver with those in the linux FF-A driver * remove the FF-A helper layer * make the u-boot FF-A driver independent from EFI * provide an optional config that enables copying the driver data to EFI runtime section at ExitBootServices service * use 64-bit version of FFA_RXTX_MAP, FFA_MSG_SEND_DIRECT_{REQ, RESP} * update armffa command with the new driver interfaces
v2 [2]:
* make FF-A bus discoverable using device_{bind, probe} APIs * remove device tree support
v1 [1]:
* introduce FF-A bus driver with device tree support * introduce armffa command * introduce FF-A Sandbox driver * add FF-A Sandbox test cases * introduce FF-A MM communication
Cheers, Abdellatif
List of previous patches:
[1]: https://lore.kernel.org/all/20220329151659.16894-1-abdellatif.elkhlifi@arm.c... [2]: https://lore.kernel.org/all/20220415122803.16666-1-abdellatif.elkhlifi@arm.c... [3]: https://lore.kernel.org/all/20220801172053.20163-1-abdellatif.elkhlifi@arm.c... [4]: https://lore.kernel.org/all/20220926101723.9965-1-abdellatif.elkhlifi@arm.co... [5]: https://lore.kernel.org/all/20220926140827.15125-1-abdellatif.elkhlifi@arm.c... [6]: https://lore.kernel.org/all/20221013103857.614-1-abdellatif.elkhlifi@arm.com... [7]: https://lore.kernel.org/all/20221107192055.21669-1-abdellatif.elkhlifi@arm.c... [8]: https://lore.kernel.org/all/20221122131751.22747-1-abdellatif.elkhlifi@arm.c...
More details:
[A]: https://developer.arm.com/documentation/den0077/latest/ [B]: doc/arch/arm64.ffa.rst [C]: doc/usage/cmd/armffa.rst [D]: example of boot logs when enabling FF-A
``` U-Boot 2023.01 (Mar 07 2023 - 11:05:21 +0000) corstone1000 aarch64
DRAM: 2 GiB [FFA] trying FF-A framework discovery [FFA] Conduit is SMC [FFA] FF-A driver 1.0 FF-A framework 1.0 [FFA] Versions are compatible Core: 18 devices, 12 uclasses, devicetree: separate MMC: Loading Environment from nowhere... OK ... Hit any key to stop autoboot: 0 Loading kernel from 0x083EE000 to memory ... ... [FFA] endpoint ID is 0 [FFA] Using 1 4KB page(s) for RX/TX buffers size [FFA] RX buffer at virtual address 00000000fdf4e000 [FFA] TX buffer at virtual address 00000000fdf50000 [FFA] RX/TX buffers mapped [FFA] Reading partitions data from the RX buffer [FFA] Partition ID 8001 : info cached [FFA] Partition ID 8002 : info cached [FFA] Partition ID 8003 : info cached [FFA] 3 partition(s) found and cached [FFA] Preparing for checking partitions count [FFA] Searching partitions using the provided UUID [FFA] No partition found. Querying framework ... [FFA] Reading partitions data from the RX buffer [FFA] Number of partition(s) matching the UUID: 1 EFI: Pre-allocating 1 partition(s) info structures [FFA] Preparing for filling partitions info [FFA] Searching partitions using the provided UUID [FFA] Partition ID 8003 matches the provided UUID EFI: MM partition ID 0x8003 EFI: Corstone1000: Capsule shared buffer at 0x80000000 , size 8192 pages Booting /MemoryMapped(0x0,0x88200000,0xf00000) EFI stub: Booting Linux Kernel... EFI stub: Using DTB from configuration table EFI stub: Exiting boot services... [FFA] removing the device [FFA] unmapping RX/TX buffers [FFA] Freeing RX/TX buffers Booting Linux on physical CPU 0x0000000000 [0x411fd040] 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 efi: EFI v2.100 by Das U-Boot ... ```
Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Rob Herring robh@kernel.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Achin Gupta achin.gupta@arm.com Cc: Drew Reed Drew.Reed@arm.com Cc: Xueliang Zhong Xueliang.Zhong@arm.com
Abdellatif El Khlifi (10): arm64: smccc: add support for SMCCCv1.2 x0-x17 registers lib: uuid: introduce uuid_str_to_le_bin function lib: uuid: introduce testcase for uuid_str_to_le_bin arm_ffa: introduce Arm FF-A low-level driver arm_ffa: introduce armffa command arm_ffa: introduce the FF-A Sandbox driver arm_ffa: introduce Sandbox test cases for UCLASS_FFA arm_ffa: introduce armffa command Sandbox test arm_ffa: efi: introduce FF-A MM communication arm_ffa: efi: corstone1000: enable MM communication
MAINTAINERS | 17 + arch/arm/cpu/armv8/smccc-call.S | 57 +- arch/arm/lib/asm-offsets.c | 16 + arch/sandbox/dts/sandbox.dtsi | 4 + arch/sandbox/dts/test.dts | 4 + cmd/Kconfig | 10 + cmd/Makefile | 2 + cmd/armffa.c | 264 ++++ configs/corstone1000_defconfig | 2 + configs/sandbox64_defconfig | 2 + configs/sandbox_defconfig | 2 + doc/arch/arm64.ffa.rst | 286 ++++ doc/arch/index.rst | 1 + doc/arch/sandbox/sandbox.rst | 1 + doc/usage/cmd/armffa.rst | 118 ++ doc/usage/index.rst | 1 + drivers/Makefile | 1 + drivers/firmware/Kconfig | 1 + drivers/firmware/arm-ffa/Kconfig | 39 + drivers/firmware/arm-ffa/Makefile | 9 + drivers/firmware/arm-ffa/arm-ffa-uclass.c | 33 + drivers/firmware/arm-ffa/arm_ffa_priv.h | 209 +++ drivers/firmware/arm-ffa/core.c | 1283 +++++++++++++++++ drivers/firmware/arm-ffa/sandbox.c | 610 ++++++++ .../firmware/arm-ffa/sandbox_arm_ffa_priv.h | 129 ++ include/arm_ffa.h | 89 ++ include/configs/corstone1000.h | 15 +- include/dm/uclass-id.h | 6 + include/linux/arm-smccc.h | 45 + include/mm_communication.h | 6 + include/sandbox_arm_ffa.h | 124 ++ include/uuid.h | 15 + lib/efi_loader/Kconfig | 14 +- lib/efi_loader/efi_variable_tee.c | 317 +++- lib/uuid.c | 48 + test/cmd/Makefile | 2 + test/cmd/armffa.c | 39 + test/dm/Makefile | 2 + test/dm/ffa.c | 380 +++++ test/lib/Makefile | 1 + test/lib/uuid.c | 44 + 41 files changed, 4239 insertions(+), 9 deletions(-) create mode 100644 cmd/armffa.c create mode 100644 doc/arch/arm64.ffa.rst create mode 100644 doc/usage/cmd/armffa.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_priv.h create mode 100644 drivers/firmware/arm-ffa/core.c create mode 100644 drivers/firmware/arm-ffa/sandbox.c create mode 100644 drivers/firmware/arm-ffa/sandbox_arm_ffa_priv.h create mode 100644 include/arm_ffa.h create mode 100644 include/sandbox_arm_ffa.h create mode 100644 test/cmd/armffa.c create mode 100644 test/dm/ffa.c create mode 100644 test/lib/uuid.c

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 abdellatif.elkhlifi@arm.com Reviewed-by: Jens Wiklander jens.wiklander@linaro.org Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org
--- Changelog: ===============
v9:
* update the copyright string
v7:
* improve indentation of ARM_SMCCC_1_2_REGS_Xn_OFFS
v4:
* rename the commit title and improve description new commit title: the current
v3:
* port x0-x17 registers support from linux kernel as defined by SMCCCv1.2 commit title: arm64: smccc: add Xn registers support used by SMC calls
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 dc92b28777..93f66d3366 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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com +*/ #include <linux/linkage.h> #include <linux/arm-smccc.h> #include <generated/asm-offsets.h> @@ -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 6de0ce9152..181a8ac4c2 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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> @@ -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 e1d09884a1..f44e9e8f93 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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */ #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

convert UUID string to little endian binary data
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
--- Changelog: ===============
v9:
* add a full function prototype description in uuid.h
v8:
* use simple_strtoull() in uuid_str_to_le_bin() to support 32-bit platforms
v7:
* rename be_uuid_str_to_le_bin() to uuid_str_to_le_bin() * make uuid_str_to_le_bin() implementation similar to uuid_str_to_bin() by using same APIs
v4:
* rename ffa_uuid_str_to_bin to be_uuid_str_to_le_bin and put in a standalone commit (the current)
v3:
* introduce ffa_uuid_str_to_bin (provided by arm_ffa: introduce Arm FF-A low-level driver)
include/uuid.h | 15 +++++++++++++++ lib/uuid.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+)
diff --git a/include/uuid.h b/include/uuid.h index 4a4883d3b5..89b93e642b 100644 --- a/include/uuid.h +++ b/include/uuid.h @@ -2,6 +2,10 @@ /* * Copyright (C) 2014 Samsung Electronics * Przemyslaw Marczak p.marczak@samsung.com + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */ #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 465e1ac38f..a72011e564 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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> @@ -346,6 +350,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. *

On Fri, 10 Mar 2023 at 06:10, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
convert UUID string to little endian binary data
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v9:
- add a full function prototype description in uuid.h
v8:
- use simple_strtoull() in uuid_str_to_le_bin() to support 32-bit platforms
v7:
- rename be_uuid_str_to_le_bin() to uuid_str_to_le_bin()
- make uuid_str_to_le_bin() implementation similar to uuid_str_to_bin() by using same APIs
v4:
- rename ffa_uuid_str_to_bin to be_uuid_str_to_le_bin and put in a standalone commit (the current)
v3:
- introduce ffa_uuid_str_to_bin (provided by arm_ffa: introduce Arm FF-A low-level driver)
include/uuid.h | 15 +++++++++++++++ lib/uuid.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+)
Reviewed-by: Simon Glass sjg@chromium.org

provide a test case
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Simon Glass sjg@chromium.org --- MAINTAINERS | 5 +++++ test/lib/Makefile | 1 + test/lib/uuid.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+) create mode 100644 test/lib/uuid.c
diff --git a/MAINTAINERS b/MAINTAINERS index e29c16cf01..58370c5fbd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1596,3 +1596,8 @@ S: Maintained F: arch/arm/dts/ls1021a-twr-u-boot.dtsi F: drivers/crypto/fsl/ F: include/fsl_sec.h + +UUID testing +M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com +S: Maintained +F: test/lib/uuid.c diff --git a/test/lib/Makefile b/test/lib/Makefile index 7e7922fe3b..db6d88c065 100644 --- a/test/lib/Makefile +++ b/test/lib/Makefile @@ -21,6 +21,7 @@ obj-$(CONFIG_UT_LIB_RSA) += rsa.o obj-$(CONFIG_AES) += test_aes.o obj-$(CONFIG_GETOPT) += getopt.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 0000000000..6f24939de4 --- /dev/null +++ b/test/lib/uuid.c @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Functional tests for UCLASS_FFA class + * + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <uuid.h> +#include <test/lib.h> +#include <test/test.h> +#include <test/ut.h> + +/* 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}; + int cnt; + + ut_assertok(uuid_str_to_le_bin(uuid_str, ret_uuid_bin)); + + for (cnt = 0; cnt < UUID_SIZE; cnt++) + ut_asserteq(ref_uuid_bin[cnt], ret_uuid_bin[cnt]); + + return 0; +} + +LIB_TEST(lib_test_uuid_to_le, 0);

Add the core driver 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_get_device_by_name).
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:
- partition_info_get - sync_send_receive - rxtx_unmap
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 abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
--- Changelog: ===============
v9:
* integrate the FF-A bus discovery in the DM and use ARM_SMCCC_FEATURES for binding
v8:
* make ffa_get_partitions_info() second argument to be an SP count in both modes * update ffa_bus_prvdata_get() to return a pointer rather than a pointer address * remove packing from ffa_partition_info and ffa_send_direct_data structures * pass the FF-A bus device to the bus operations
v7:
* add support for 32-bit direct messaging * rename be_uuid_str_to_le_bin() to uuid_str_to_le_bin() * improve the declaration of error handling mapping * stating in doc/arch/arm64.ffa.rst that EFI runtime is not supported
v6:
* drop use of EFI runtime support (We decided with Linaro to add this later) * drop discovery from initcalls (discovery will be on demand by FF-A users) * set the alignment of the RX/TX buffers to the larger translation granule size * move FF-A RX/TX buffers unmapping at ExitBootServices() to a separate commit * update the documentation and move it to doc/arch/arm64.ffa.rst
v4:
* add doc/README.ffa.drv * moving the FF-A driver work to drivers/firmware/arm-ffa * use less #ifdefs in lib/efi_loader/efi_boottime.c and replace #if defined by #if CONFIG_IS_ENABLED * improving error handling by mapping the FF-A errors to standard errors and logs * replacing panics with an error log and returning an error code * improving features discovery in FFA_FEATURES by introducing rxtx_min_pages private data field * add ffa_remove and ffa_unbind functions * improve how the driver behaves when bus discovery is done more than once
v3:
* align the interfaces of the U-Boot FF-A driver with those in the linux FF-A driver * remove the FF-A helper layer * make the U-Boot FF-A driver independent from EFI * provide an optional config that enables copying the driver data to EFI runtime section at ExitBootServices service * use 64-bit version of FFA_RXTX_MAP, FFA_MSG_SEND_DIRECT_{REQ, RESP}
v2:
* make FF-A bus discoverable using device_{bind, probe} APIs * remove device tree support
v1:
* introduce FF-A bus driver with device tree support
MAINTAINERS | 7 + doc/arch/arm64.ffa.rst | 282 +++++ doc/arch/index.rst | 1 + drivers/Makefile | 1 + drivers/firmware/Kconfig | 1 + drivers/firmware/arm-ffa/Kconfig | 33 + drivers/firmware/arm-ffa/Makefile | 8 + drivers/firmware/arm-ffa/arm-ffa-uclass.c | 33 + drivers/firmware/arm-ffa/arm_ffa_priv.h | 209 ++++ drivers/firmware/arm-ffa/core.c | 1249 +++++++++++++++++++++ include/arm_ffa.h | 88 ++ include/dm/uclass-id.h | 6 + 12 files changed, 1918 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_priv.h create mode 100644 drivers/firmware/arm-ffa/core.c create mode 100644 include/arm_ffa.h
diff --git a/MAINTAINERS b/MAINTAINERS index 58370c5fbd..1dfa23c1f0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -266,6 +266,13 @@ 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 abdellatif.elkhlifi@arm.com +S: Maintained +F: doc/arch/arm64.ffa.rst +F: drivers/firmware/arm-ffa/ +F: include/arm_ffa.h + ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de M: Fabio Estevam festevam@gmail.com diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst new file mode 100644 index 0000000000..8fad9ef3d0 --- /dev/null +++ b/doc/arch/arm64.ffa.rst @@ -0,0 +1,282 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +Arm FF-A Driver +=============== + +Summary +------- + +FF-A stands for Firmware Framework for Arm A-profile processors. + +FF-A specifies interfaces that enable a pair of software sandboxes to +communicate with each other. A sandbox aka partition could +be a VM in the Normal or Secure world, an application in S-EL0, or a +Trusted OS in S-EL1. + +This FF-A driver implements the interfaces to communicate with partitions in +the Secure world aka Secure partitions (SPs). + +The driver 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. + +FF-A driver 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 the FF-A driver supports EFI boot time only. + +Runtime support will be added in future developments. + +FF-A and SMC specifications +------------------------------------------- + +The current implementation of the driver relies on FF-A specification v1.0 +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 driver has been tested with OP-TEE which supports SMC32 calling convention. + +For more details please refer to the FF-A v1.0 spec: +https://documentation-service.arm.com/static/5fb7e8a6ca04df4095c1d65e?token= + +Hypervisors are supported if they are configured to trap SMC calls. + +The FF-A driver uses 64-bit registers as per SMCCCv1.2 specification. + +For more details please refer to the SMC Calling Convention v1.2 spec: +https://documentation-service.arm.com/static/5f8edaeff86e16515cdbe4c6?token= + +Supported hardware +-------------------------------- + +Aarch64 plaforms + +Configuration +---------------------- + +CONFIG_ARM_FFA_TRANSPORT + Enables the FF-A bus driver. Turn this on if you want to use FF-A + communication. + +FF-A ABIs under the hood +--------------------------------------- + +Invoking an FF-A ABI involves providing to the secure world/hypervisor the +expected arguments from the ABI. + +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 FF-A driver (non-secure world). + +The driver reads the response and processes it accordingly. + +This methodology applies to all the FF-A ABIs in the driver. + +FF-A bus discovery in U-Boot +------------------------------------------- + +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 ffa_bus_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 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 the DM class searching APIs +(e.g: uclass_get_device_by_name). Please refer to the armffa command implementation +as an example of how to probe and interact with the FF-A driver. + +When calling uclass_get_device_by_name() for example, the FF-A driver is probed +and ffa_probe() is called which performs the following: + + - allocating private data (priv) with devres + - updating priv with discovery information + - 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. + +FF-A device destruction +------------------------- + +When the FF-A device is removed by the DM, RX/TX buffers are automatically +unmapped and freed. Same happens when the device is unbound before being +removed first. + +For example, at EFI efi_exit_boot_services() active devices are automatically removed +by dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL). + +By consequence, the FF-A RX/TX are unmapped automatically. + +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 rxtx_unmap() and this is done +automatically at efi_exit_boot_services(). + +If RX/TX buffers created by U-Boot are not unmapped and by consequence becoming +available at EFI runtime, secure world will get confused about RX/TX buffers +ownership (U-Boot vs kernel). + +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. + +The bus driver layer +------------------------------ + +The driver comes on top of the SMCCC layer and is implemented in +drivers/firmware/arm-ffa/core.c + +The driver provides the following features: + +- 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, the driver takes care of how to transport + that to the secure world/hypervisor using FF-A + +- The driver provides callbacks to be used by clients to access the FF-A bus: + + - partition_info_get + - sync_send_receive + - 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 + +The armffa command +----------------------------------- + +armffa is an implementation defined command showcasing how to use the FF-A driver and how to invoke +its operations. + +Please refer the command documentation at doc/usage/cmd/armffa.rst + +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 (Mar 07 2023 - 11:05:21 +0000) corstone1000 aarch64 + + DRAM: 2 GiB + [FFA] trying FF-A framework discovery + [FFA] Conduit is SMC + [FFA] FF-A driver 1.0 + FF-A framework 1.0 + [FFA] Versions are compatible + Core: 18 devices, 12 uclasses, devicetree: separate + MMC: + Loading Environment from nowhere... OK + ... + Hit any key to stop autoboot: 0 + Loading kernel from 0x083EE000 to memory ... + ... + [FFA] endpoint ID is 0 + [FFA] Using 1 4KB page(s) for RX/TX buffers size + [FFA] RX buffer at virtual address 00000000fdf4e000 + [FFA] TX buffer at virtual address 00000000fdf50000 + [FFA] RX/TX buffers mapped + [FFA] Reading partitions data from the RX buffer + [FFA] Partition ID 8001 : info cached + [FFA] Partition ID 8002 : info cached + [FFA] Partition ID 8003 : info cached + [FFA] 3 partition(s) found and cached + [FFA] Preparing for checking partitions count + [FFA] Searching partitions using the provided UUID + [FFA] No partition found. Querying framework ... + [FFA] Reading partitions data from the RX buffer + [FFA] Number of partition(s) matching the UUID: 1 + EFI: Pre-allocating 1 partition(s) info structures + [FFA] Preparing for filling partitions info + [FFA] Searching partitions using the provided UUID + [FFA] Partition ID 8003 matches the provided UUID + EFI: MM partition ID 0x8003 + EFI: Corstone1000: Capsule shared buffer at 0x80000000 , size 8192 pages + Booting /MemoryMapped(0x0,0x88200000,0xf00000) + EFI stub: Booting Linux Kernel... + EFI stub: Using DTB from configuration table + EFI stub: Exiting boot services... + [FFA] removing the device + [FFA] unmapping RX/TX buffers + [FFA] Freeing RX/TX buffers + Booting Linux on physical CPU 0x0000000000 [0x411fd040] + 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 + efi: EFI v2.100 by Das U-Boot + ... + +Contributors +------------ + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com diff --git a/doc/arch/index.rst b/doc/arch/index.rst index b8da4b8c8e..2f916f4026 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 58be410135..885fdef4dc 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -112,6 +112,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 eae1c8ddc9..8789b1ea14 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 0000000000..e5b2912201 --- /dev/null +++ b/drivers/firmware/arm-ffa/Kconfig @@ -0,0 +1,33 @@ +# 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. + + This driver 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). + This FF-A driver takes care of all the interactions between Normal world + and Secure World. + + For more details about the FF-A driver, 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 0000000000..c8d83b4bc9 --- /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 open-source-office@arm.com +# +# Authors: +# Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + +obj-y += arm-ffa-uclass.o core.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 0000000000..bc4fe68d57 --- /dev/null +++ b/drivers/firmware/arm-ffa/arm-ffa-uclass.c @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <dm.h> +#include <asm/global_data.h> + +DECLARE_GLOBAL_DATA_PTR; + +/** + * ffa_bus_get_ops() - bus driver operations getter + * @dev: the arm_ffa device + * Returns a pointer to the FF-A driver ops field. + * Return: + * The ops pointer on success, NULL on failure. + */ +const struct ffa_bus_ops *ffa_bus_get_ops(struct udevice *dev) +{ + if (!dev) + return NULL; + + return device_get_ops(dev); +} + +UCLASS_DRIVER(ffa) = { + .name = "ffa", + .id = UCLASS_FFA, +}; diff --git a/drivers/firmware/arm-ffa/arm_ffa_priv.h b/drivers/firmware/arm-ffa/arm_ffa_priv.h new file mode 100644 index 0000000000..51df7d1c64 --- /dev/null +++ b/drivers/firmware/arm-ffa/arm_ffa_priv.h @@ -0,0 +1,209 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __ARM_FFA_PRV_H +#define __ARM_FFA_PRV_H + +#include <arm_ffa.h> +#include <mapmem.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> + +/* This header is exclusively used by the FF-A core driver, sandbox driver and sandbox tests */ + +/* FF-A core driver name */ +#define FFA_DRV_NAME "arm_ffa" + +/* The FF-A SMC function definitions */ + +#if CONFIG_IS_ENABLED(SANDBOX_FFA) +#include "sandbox_arm_ffa.h" +#else +/* CONFIG_ARM64 */ +#include <linux/arm-smccc.h> +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 +}; + +/* 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) + +/* + * 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 */ +}; + +/** + * 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 - 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_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_discovery_info - discovery information + * @fwk_version: FF-A framework version + * @invoke_ffa_fn: The function executing the FF-A function (ABI) + */ +struct ffa_discovery_info { + u32 fwk_version; + invoke_ffa_fn_t invoke_ffa_fn; +}; + +/** + * struct ffa_priv - the driver private data structure + * + * @dscvry_info: Initial information discovered + * @ops: The driver operations structure + * @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. + * The data is dynamically allocated, managed by devres + * and registered with the DM. + */ +struct ffa_priv { + struct ffa_discovery_info dscvry_info; + struct ffa_bus_ops ops; + u16 id; + struct ffa_partitions partitions; + struct ffa_rxtxpair pair; +}; +#endif diff --git a/drivers/firmware/arm-ffa/core.c b/drivers/firmware/arm-ffa/core.c new file mode 100644 index 0000000000..2210f5343c --- /dev/null +++ b/drivers/firmware/arm-ffa/core.c @@ -0,0 +1,1249 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <dm.h> +#include <log.h> +#include <malloc.h> +#include <string.h> +#include <uuid.h> +#include <asm/global_data.h> +#include <dm/device-internal.h> +#include <dm/devres.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> +#include "arm_ffa_priv.h" + +DECLARE_GLOBAL_DATA_PTR; + +/* FF-A discovery information */ +struct ffa_discovery_info dscvry_info; + +/* The function that sets the SMC conduit */ +static int ffa_set_smc_conduit(void); + +/* 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, +}; + +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 + * + * This function maps 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 + */ +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 + * + * This function maps 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 + */ +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("[FFA] %s\n", err_msg_map[abi_idx].err_str[err_idx]); + + return 0; +} + +/* Driver core functions */ + +/** + * ffa_get_version() - FFA_VERSION handler function + * + * This function implements 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 + */ +static int ffa_get_version(void) +{ + u16 major, minor; + ffa_value_t res = {0}; + int ffa_errno; + + if (!dscvry_info.invoke_ffa_fn) + return -EINVAL; + + dscvry_info.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("[FFA] 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("[FFA] Versions are compatible\n"); + + dscvry_info.fwk_version = res.a0; + + return 0; + } + + log_err("[FFA] 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 + * + * This function implements 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 *priv = dev_get_priv(dev); + + priv->dscvry_info.invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_32(FFA_ID_GET), + }, &res); + + if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) { + priv->id = GET_SELF_ENDPOINT_ID((u32)res.a2); + log_info("[FFA] endpoint ID is %u\n", 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() - sets 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 + * + * This function sets the minimum number of pages + * in each of the RX/TX buffers in the private data structure + * + * Return: + * + * buf_4k_pages points to 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 *priv = dev_get_priv(dev); + + switch (prop_field) { + case RXTX_4K: + priv->pair.rxtx_min_pages = 1; + break; + case RXTX_16K: + priv->pair.rxtx_min_pages = 4; + break; + case RXTX_64K: + priv->pair.rxtx_min_pages = 16; + break; + default: + log_err("[FFA] RX/TX buffer size not supported\n"); + return -EINVAL; + } + + return 0; +} + +/** + * ffa_get_rxtx_map_features() - FFA_FEATURES handler function with FFA_RXTX_MAP argument + * @dev: The FF-A bus device + * + * This function implements 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(struct udevice *dev) +{ + ffa_value_t res = {0}; + int ffa_errno; + struct ffa_priv *priv = dev_get_priv(dev); + + priv->dscvry_info.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() - frees the RX/TX buffers + * @dev: The FF-A bus device + * + * This function frees the RX/TX buffers + */ +static void ffa_free_rxtx_buffers(struct udevice *dev) +{ + struct ffa_priv *priv = dev_get_priv(dev); + + log_info("[FFA] Freeing RX/TX buffers\n"); + + if (priv->pair.rxbuf) { + free(priv->pair.rxbuf); + priv->pair.rxbuf = NULL; + } + + if (priv->pair.txbuf) { + free(priv->pair.txbuf); + priv->pair.txbuf = NULL; + } +} + +/** + * ffa_alloc_rxtx_buffers() - allocates the RX/TX buffers + * @dev: The FF-A bus device + * + * This function is 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. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_alloc_rxtx_buffers(struct udevice *dev) +{ + u64 bytes; + struct ffa_priv *priv = dev_get_priv(dev); + + log_info("[FFA] Using %lu 4KB page(s) for RX/TX buffers size\n", + priv->pair.rxtx_min_pages); + + bytes = 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 + */ + + priv->pair.rxbuf = memalign(bytes, bytes); + if (!priv->pair.rxbuf) { + log_err("[FFA] failure to allocate RX buffer\n"); + return -ENOBUFS; + } + + log_info("[FFA] RX buffer at virtual address %p\n", priv->pair.rxbuf); + + priv->pair.txbuf = memalign(bytes, bytes); + if (!priv->pair.txbuf) { + free(priv->pair.rxbuf); + priv->pair.rxbuf = NULL; + log_err("[FFA] failure to allocate the TX buffer\n"); + return -ENOBUFS; + } + + log_info("[FFA] TX buffer at virtual address %p\n", priv->pair.txbuf); + + /* Make sure the buffers are cleared before use */ + memset(priv->pair.rxbuf, 0, bytes); + memset(priv->pair.txbuf, 0, bytes); + + return 0; +} + +/** + * ffa_map_rxtx_buffers() - FFA_RXTX_MAP handler function + * @dev: The FF-A bus device + * + * This function implements FFA_RXTX_MAP FF-A function + * to map the RX/TX buffers + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_map_rxtx_buffers(struct udevice *dev) +{ + int ret; + ffa_value_t res = {0}; + int ffa_errno; + struct ffa_priv *priv = dev_get_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 + */ + + priv->dscvry_info.invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_64(FFA_RXTX_MAP), + .a1 = map_to_sysmem(priv->pair.txbuf), + .a2 = map_to_sysmem(priv->pair.rxbuf), + .a3 = priv->pair.rxtx_min_pages, + }, &res); + + if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) { + log_info("[FFA] 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() - 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 + */ +static int ffa_unmap_rxtx_buffers(struct udevice *dev) +{ + ffa_value_t res = {0}; + int ffa_errno; + struct ffa_priv *priv = NULL; + + if (!dev) + return -ENODEV; + + log_info("[FFA] unmapping RX/TX buffers\n"); + + priv = dev_get_priv(dev); + + priv->dscvry_info.invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_32(FFA_RXTX_UNMAP), + .a1 = PREP_SELF_ENDPOINT_ID(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() - FFA_RX_RELEASE handler function + * @dev: The FF-A bus device + * + * This function invokes 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(struct udevice *dev) +{ + ffa_value_t res = {0}; + int ffa_errno; + struct ffa_priv *priv = dev_get_priv(dev); + + priv->dscvry_info.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() - checks whether two given UUIDs are identical + * @uuid1: first UUID + * @uuid2: second UUID + * + * This function is used by ffa_read_partitions_info to search + * for a UUID in the partitions descriptors table + * + * Return: + * + * 1 when UUIDs match. Otherwise, 0 + */ +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() - reads queried partition data + * @dev: The arm_ffa bus device + * @count: The number of partitions queried + * @part_uuid: Pointer to the partition(s) UUID + * + * This function reads the partitions information + * returned by the FFA_PARTITION_INFO_GET and saves it in the private + * data structure. + * + * Return: + * + * The private data structure 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 *priv = dev_get_priv(dev); + + if (!count) { + log_err("[FFA] no partition detected\n"); + return -ENODATA; + } + + log_info("[FFA] Reading 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 = priv->pair.rxtx_min_pages * SZ_4K; + + if (data_bytes > buf_bytes) { + log_err("[FFA] partitions data size exceeds the RX buffer size:\n"); + log_err("[FFA] sizes in bytes: data %llu , RX buffer %llu\n", + data_bytes, + buf_bytes); + + return -ENOMEM; + } + + priv->partitions.descs = devm_kmalloc(dev, data_bytes, __GFP_ZERO); + if (!priv->partitions.descs) { + log_err("[FFA] cannot allocate partitions data buffer\n"); + return -ENOMEM; + } + + parts_info = priv->pair.rxbuf; + + for (desc_idx = 0 ; desc_idx < count ; desc_idx++) { + priv->partitions.descs[desc_idx].info = + parts_info[desc_idx]; + + log_info("[FFA] Partition ID %x : info cached\n", + priv->partitions.descs[desc_idx].info.id); + } + + priv->partitions.count = count; + + log_info("[FFA] %d 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 = 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 < priv->partitions.count; + cached_desc_idx++) { + /* Save the UUID */ + if (priv->partitions.descs[cached_desc_idx].info.id == + parts_info[rx_desc_idx].id) { + 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() - invokes FFA_PARTITION_INFO_GET and saves partitions data + * @dev: The arm_ffa bus device + * @part_uuid: Pointer to the partition(s) UUID + * @pcount: Pointer to the number of partitions variable filled when querying + * + * This function executes the FFA_PARTITION_INFO_GET + * to query the partitions data. Then, it calls ffa_read_partitions_info + * to save the data in the private data structure. + * + * 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; + struct ffa_priv *priv = dev_get_priv(dev); + + /* + * 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; + } + + priv->dscvry_info.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("[FFA] failed reading SP(s) data , err (%d)\n", ret); + ffa_release_rx_buffer(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(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() - FFA_PARTITION_INFO_GET handler function + * + * The passed arguments: + * Mode 1: When getting from the driver the number of + * secure partitions: + * @uuid_str: pointer to the UUID string + * @sp_count: pointer to the variable that contains the number of partitions + * The variable will be set by the driver + * @buffer: NULL + * + * Mode 2: When requesting the driver to return the + * partitions information: + * @dev: The arm_ffa bus device + * @uuid_str: pointer to the UUID string + * @sp_count: pointer to the variable that contains the number of empty partition descriptors + * The variable will be read by the driver + * @buffer: pointer to SPs information buffer + * (allocated by the client and contains empty @sp_count descriptors). + * The buffer will be filled by the driver + * + * This function queries the secure partition data from + * the private data structure. If not found, it invokes 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 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(). + * + * When invoked through a client request, ffa_get_partitions_info should be + * called twice. First call is to get from the driver the number of secure + * partitions (SPs) associated to a particular UUID. + * Then, the caller (client) allocates the buffer to host the SPs data and + * issues a 2nd call. Then, the driver fills the SPs data in the pre-allocated + * buffer. + * + * To achieve the mechanism described above, ffa_get_partitions_info uses the + * following functions: + * ffa_read_partitions_info + * ffa_query_partitions_info + * + * Return: + * + * @sp_count: When pointing to the number of partitions variable, the number is + * set by the driver. + * When pointing to the partitions information buffer size, the buffer will be + * filled by the driver. + * + * On success 0 is returned. Otherwise, failure + */ +static int ffa_get_partitions_info(struct udevice *dev, const char *uuid_str, + u32 *sp_count, struct ffa_partition_info *buffer) +{ + /* + * fill_data: + * 0: return the SP count + * 1: fill SP data and return it to the caller + */ + bool fill_data = 0; + u32 desc_idx, client_desc_idx; + struct ffa_partition_uuid part_uuid = {0}; + u32 sp_found = 0; + struct ffa_priv *priv = NULL; + + if (!dev) + return -ENODEV; + + priv = dev_get_priv(dev); + + if (!priv->partitions.count || !priv->partitions.descs) { + log_err("[FFA] no partition installed\n"); + return -EINVAL; + } + + if (!uuid_str) { + log_err("[FFA] no UUID provided\n"); + return -EINVAL; + } + + if (!sp_count) { + log_err("[FFA] no size/count provided\n"); + return -EINVAL; + } + + if (uuid_str_to_le_bin(uuid_str, (unsigned char *)&part_uuid)) { + log_err("[FFA] invalid UUID\n"); + return -EINVAL; + } + + if (!buffer) { + /* Mode 1: getting the number of secure partitions */ + + fill_data = 0; + + log_info("[FFA] Preparing for checking partitions count\n"); + + } else if (*sp_count) { + /* Mode 2: retrieving the partitions information */ + + fill_data = 1; + + client_desc_idx = 0; + + log_info("[FFA] Preparing for filling partitions info\n"); + + } else { + log_err("[FFA] invalid function arguments provided\n"); + return -EINVAL; + } + + log_info("[FFA] Searching partitions using the provided UUID\n"); + + /* Search in the cached partitions */ + for (desc_idx = 0; + desc_idx < priv->partitions.count; + desc_idx++) { + if (ffa_uuid_are_identical(&priv->partitions.descs[desc_idx].sp_uuid, + &part_uuid)) { + log_info("[FFA] Partition ID %x matches the provided UUID\n", + priv->partitions.descs[desc_idx].info.id); + + sp_found++; + + if (fill_data) { + /* Trying to fill the partition info in the input buffer */ + + if (client_desc_idx < *sp_count) { + buffer[client_desc_idx++] = + priv->partitions.descs[desc_idx].info; + continue; + } + + log_err("[FFA] failed to fill client descriptor, buffer full\n"); + return -ENOBUFS; + } + } + } + + if (!sp_found) { + int ret; + + log_info("[FFA] No partition found. Querying framework ...\n"); + + ret = ffa_query_partitions_info(dev, &part_uuid, &sp_found); + + if (ret == 0) { + if (!fill_data) { + *sp_count = sp_found; + + log_info("[FFA] Number of partition(s) matching the UUID: %d\n", + sp_found); + } else { + /* + * If SPs data detected, they are already in the private data + * structure, retry searching SP data again to return them + * to the caller + */ + if (sp_found) + ret = ffa_get_partitions_info(dev, uuid_str, sp_count, + buffer); + else + ret = -ENODATA; + } + } + + return ret; + } + + /* Partition(s) found */ + if (!fill_data) + *sp_count = sp_found; + + return 0; +} + +/** + * ffa_cache_partitions_info() - Queries and saves all secure partitions data + * @dev: The arm_ffa bus device + * + * This function invokes 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 the + * resident private data structure and we keep the UUID field empty + * (in FF-A 1.0 UUID is not provided by the partition descriptor) + * + * This function is 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() - 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 + */ +static int ffa_msg_send_direct_req(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 *priv = NULL; + + if (!dev) + return -ENODEV; + + priv = dev_get_priv(dev); + + if (!priv || !priv->dscvry_info.invoke_ffa_fn) + return -EINVAL; + + /* No partition installed */ + if (!priv->partitions.count || !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); + } + + priv->dscvry_info.invoke_ffa_fn((ffa_value_t){ + .a0 = req_mode, + .a1 = PREP_SELF_ENDPOINT_ID(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)) + priv->dscvry_info.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); +} + +/** + * ffa_try_discovery() - performs FF-A discovery + * Tries 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. + */ +bool ffa_try_discovery(void) +{ + int ret; + + log_info("[FFA] trying FF-A framework discovery\n"); + + ret = ffa_set_smc_conduit(); + if (ret) + return false; + + ret = ffa_get_version(); + if (ret) + return false; + + return true; +} + +/** + * __arm_ffa_fn_smc() - 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 __arm_ffa_fn_smc(ffa_value_t args, ffa_value_t *res) +{ + arm_smccc_1_2_smc(&args, res); +} + +/** + * ffa_bus_is_supported() - FF-A discovery callback + * @invoke_fn: legacy SMC invoke function (not used) + * + * This function performs FF-A discovery by calling ffa_try_discovery(). + * 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 ffa_bus_is_supported(void (*invoke_fn)(unsigned long a0, unsigned long a1, + unsigned long a2, unsigned long a3, + unsigned long a4, unsigned long a5, + unsigned long a6, unsigned long a7, + struct arm_smccc_res *res)) +{ + return ffa_try_discovery(); +} + +/* Registering the FF-A driver as an SMCCC feature driver */ + +ARM_SMCCC_FEATURE_DRIVER(arm_ffa) = { + .driver_name = FFA_DRV_NAME, + .is_supported = ffa_bus_is_supported, +}; + +/** + * ffa_set_smc_conduit() - Set the SMC conduit + * + * This function selects the SMC conduit by setting the driver invoke function + * to SMC assembly function + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_set_smc_conduit(void) +{ + dscvry_info.invoke_ffa_fn = __arm_ffa_fn_smc; + + log_info("[FFA] Conduit is SMC\n"); + + return 0; +} + +/** + * ffa_devm_alloc_priv() - allocate FF-A driver private data + * @dev: The FF-A bus device + * + * This function dynamically allocates with devres the private data structure + * which contains all the FF-A data. Then, register the structure with the DM. + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_devm_alloc_priv(struct udevice *dev) +{ + struct ffa_priv *priv = dev_get_priv(dev); + + if (!priv) { + priv = devm_kmalloc(dev, sizeof(struct ffa_priv), __GFP_ZERO); + if (!priv) { + log_err("[FFA] can not allocate FF-A main data structure\n"); + return -ENOMEM; + } + dev_set_priv(dev, priv); + } + + return 0; +} + +/** + * ffa_probe() - The driver probe function + * @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: + * - allocating private data (priv) with devres + * - updating priv with discovery information + * - 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 the private data structure (priv). + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_probe(struct udevice *dev) +{ + int ret; + struct ffa_priv *priv = NULL; + + ret = ffa_devm_alloc_priv(dev); + if (ret) + return ret; + + /* The data is dynamically allocated, managed by devres */ + priv = dev_get_priv(dev); + + priv->dscvry_info = dscvry_info; + + ret = ffa_get_endpoint_id(dev); + if (ret) + return ret; + + ret = ffa_get_rxtx_map_features(dev); + if (ret) + return ret; + + ret = ffa_map_rxtx_buffers(dev); + if (ret) + return ret; + + ret = ffa_cache_partitions_info(dev); + if (ret) { + ffa_unmap_rxtx_buffers(dev); + return ret; + } + + return 0; +} + +/** + * ffa_remove() - The driver remove function + * @dev: the arm_ffa device + * Making sure the RX/TX buffers are unmapped and freed when the device is removed. + * No need to free the private data structure because devres takes care of that. + * Return: + * + * 0 on success. + */ +static int ffa_remove(struct udevice *dev) +{ + log_info("[FFA] removing the device\n"); + + ffa_unmap_rxtx_buffers(dev); + dev_set_priv(dev, NULL); + + return 0; +} + +/** + * ffa_unbind() - The driver unbind function + * @dev: the arm_ffa device + * Making sure the RX/TX buffers are unmapped and freed when the device is unbound. + * No need to free the private data structure because devres takes care of that. + * Return: + * + * 0 on success. + */ +static int ffa_unbind(struct udevice *dev) +{ + struct ffa_priv *priv = dev_get_priv(dev); + + log_info("[FFA] unbinding the device\n"); + + if (priv) + ffa_unmap_rxtx_buffers(dev); + + return 0; +} + +/* FF-A driver operations */ + +static const struct ffa_bus_ops ffa_ops = { + .partition_info_get = ffa_get_partitions_info, + .sync_send_receive = ffa_msg_send_direct_req, + .rxtx_unmap = ffa_unmap_rxtx_buffers, +}; + +/* 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, + .probe = ffa_probe, + .remove = ffa_remove, + .unbind = ffa_unbind, + .ops = &ffa_ops, +}; diff --git a/include/arm_ffa.h b/include/arm_ffa.h new file mode 100644 index 0000000000..69f3c51080 --- /dev/null +++ b/include/arm_ffa.h @@ -0,0 +1,88 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __ARM_FFA_H +#define __ARM_FFA_H + +#include <linux/printk.h> + +/* + * 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_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 { + unsigned long data0; /* w3/x3 */ + unsigned long data1; /* w4/x4 */ + unsigned long data2; /* w5/x5 */ + unsigned long data3; /* w6/x6 */ + unsigned long data4; /* w7/x7 */ +}; + +struct udevice; + +/** + * struct ffa_bus_ops - The driver operations structure + * @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_info *buffer); + 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); +}; + +/* The device driver and the Uclass driver public functions */ + +/** + * ffa_bus_get_ops() - bus driver operations getter + * @dev: the arm_ffa device + * Returns a pointer to the FF-A driver ops field. + * Return: + * The ops pointer on success, NULL on failure. + */ +const struct ffa_bus_ops *ffa_bus_get_ops(struct udevice *dev); +#endif diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 33e43c20db..df77c7da58 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -4,6 +4,11 @@ * * (C) Copyright 2012 * Pavel Herrmann morpheus.ibis@gmail.com + * + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#ifndef _DM_UCLASS_ID_H @@ -55,6 +60,7 @@ enum uclass_id { UCLASS_EFI_MEDIA, /* Devices provided by UEFI firmware */ UCLASS_ETH, /* Ethernet device */ UCLASS_ETH_PHY, /* Ethernet PHY device */ + UCLASS_FFA, /* Arm Firmware Framework for Armv8-A */ UCLASS_FIRMWARE, /* Firmware */ UCLASS_FPGA, /* FPGA device */ UCLASS_FUZZING_ENGINE, /* Fuzzing engine */

Provide armffa command showcasing the use of the FF-A driver
armffa is a command showcasing how to use the FF-A driver 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.
For more details please refer to the command documentation [1].
[1]: doc/usage/cmd/armffa.rst
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
--- Changelog: ===============
v9:
* remove manual FF-A discovery and use DM * use DM class APIs to probe and interact with the FF-A bus * add doc/usage/cmd/armffa.rst
v8:
* update partition_info_get() second argument to be an SP count * pass NULL device pointer to the FF-A bus discovery and operations
v7:
* adapt do_ffa_dev_list() following the recent update on uclass_first_device/uclass_next_device functions (they return void now) * set armffa command to use 64-bit direct messaging
v4:
* remove pattern data in do_ffa_msg_send_direct_req
v3:
* use the new driver interfaces (partition_info_get, sync_send_receive) in armffa command
v2:
* replace use of ffa_helper_init_device function by ffa_helper_bus_discover
v1:
* introduce armffa command
MAINTAINERS | 2 + cmd/Kconfig | 10 ++ cmd/Makefile | 2 + cmd/armffa.c | 264 +++++++++++++++++++++++++++++++ doc/usage/cmd/armffa.rst | 118 ++++++++++++++ doc/usage/index.rst | 1 + drivers/firmware/arm-ffa/Kconfig | 1 + 7 files changed, 398 insertions(+) create mode 100644 cmd/armffa.c create mode 100644 doc/usage/cmd/armffa.rst
diff --git a/MAINTAINERS b/MAINTAINERS index 1dfa23c1f0..18e9c2ce99 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -269,7 +269,9 @@ F: configs/cortina_presidio-asic-pnand_defconfig ARM FF-A M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com S: Maintained +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
diff --git a/cmd/Kconfig b/cmd/Kconfig index a3512836c1..f24c52def4 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -934,6 +934,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 Arm FF-A driver + 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 2d8bb4fc05..a59ab55ad0 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -12,6 +12,8 @@ obj-y += panic.o obj-y += version.o
# command + +obj-$(CONFIG_CMD_ARMFFA) += armffa.o obj-$(CONFIG_CMD_ACPI) += acpi.o obj-$(CONFIG_CMD_ADDRMAP) += addrmap.o obj-$(CONFIG_CMD_AES) += aes.o diff --git a/cmd/armffa.c b/cmd/armffa.c new file mode 100644 index 0000000000..f6c017542d --- /dev/null +++ b/cmd/armffa.c @@ -0,0 +1,264 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <arm_ffa.h> +#include <command.h> +#include <dm.h> +#include <mapmem.h> +#include <stdlib.h> +#include <asm/io.h> + +/** + * do_ffa_getpart() - implementation of the getpart subcommand + * @cmdtp: Command Table + * @flag: flags + * @argc: number of arguments + * @argv: arguments + * + * This function queries the secure partition information which the 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_info *parts_info; + u32 info_idx; + struct udevice *dev = NULL; + struct ffa_bus_ops *ffa_ops = NULL; + + if (argc != 1) + return -EINVAL; + + uclass_get_device_by_name(UCLASS_FFA, "arm_ffa", &dev); + if (!dev) { + log_err("[FFA] Cannot find FF-A bus device\n"); + return -ENODEV; + } + + ffa_ops = (struct ffa_bus_ops *)ffa_bus_get_ops(dev); + if (!ffa_ops) { + log_err("[FFA] Invalid FF-A ops\n"); + return -EINVAL; + } + + /* Mode 1: getting the number of secure partitions */ + ret = ffa_ops->partition_info_get(dev, argv[0], &count, NULL); + if (ret != 0) { + log_err("[FFA] Failure in querying partitions count (error code: %d)\n", ret); + return ret; + } + + if (!count) { + log_info("[FFA] No secure partition found\n"); + return ret; + } + + /* + * Pre-allocate a buffer to be filled by the driver + * with ffa_partition_info structs + */ + + log_info("[FFA] Pre-allocating %d partition(s) info structures\n", count); + + parts_info = calloc(count, sizeof(struct ffa_partition_info)); + if (!parts_info) + return -EINVAL; + + /* Ask the driver to fill the buffer with the SPs info */ + + ret = ffa_ops->partition_info_get(dev, argv[0], &count, parts_info); + if (ret != 0) { + log_err("[FFA] Failure in querying partition(s) info (error code: %d)\n", ret); + free(parts_info); + return ret; + } + + /* SPs found , show the partition information */ + for (info_idx = 0; info_idx < count ; info_idx++) { + log_info("[FFA] Partition: id = 0x%x , exec_ctxt 0x%x , properties 0x%x\n", + parts_info[info_idx].id, + parts_info[info_idx].exec_ctxt, + parts_info[info_idx].properties); + } + + free(parts_info); + + return 0; +} + +/** + * do_ffa_ping() - implementation of the ping subcommand + * @cmdtp: Command Table + * @flag: flags + * @argc: number of arguments + * @argv: arguments + * + * This function sends data to the secure partition which the ID is provided + * as an argument. The function uses 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 + */ +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 = NULL; + struct ffa_bus_ops *ffa_ops = NULL; + + if (argc != 1) + return -EINVAL; + + errno = 0; + part_id = strtoul(argv[0], NULL, 16); + + if (errno) { + log_err("[FFA] Invalid partition ID\n"); + return -EINVAL; + } + + uclass_get_device_by_name(UCLASS_FFA, "arm_ffa", &dev); + if (!dev) { + log_err("[FFA] Cannot find FF-A bus device\n"); + return -ENODEV; + } + + ffa_ops = (struct ffa_bus_ops *)ffa_bus_get_ops(dev); + if (!ffa_ops) { + log_err("[FFA] Invalid FF-A ops\n"); + return -EINVAL; + } + + ret = ffa_ops->sync_send_receive(dev, part_id, &msg, 1); + if (!ret) { + u8 cnt; + + log_info("[FFA] SP response:\n[LSB]\n"); + for (cnt = 0; + cnt < sizeof(struct ffa_send_direct_data) / sizeof(u64); + cnt++) + log_info("[FFA] 0x%llx\n", ((u64 *)&msg)[cnt]); + } else { + log_err("[FFA] Sending direct request error (%d)\n", ret); + } + + return ret; +} + +/** + *do_ffa_devlist() - implementation of the devlist subcommand + * @cmdtp: [in] Command Table + * @flag: flags + * @argc: number of arguments + * @argv: arguments + * + * This function queries the devices belonging to the UCLASS_FFA + * class. + * + * Return: + * + * CMD_RET_SUCCESS: on success, otherwise failure + */ +int do_ffa_devlist(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct udevice *dev = NULL; + int i; + + log_info("[FFA] FF-A uclass entries:\n"); + + for (i = 0, uclass_first_device(UCLASS_FFA, &dev); + dev; + uclass_next_device(&dev), i++) { + log_info("[FFA] entry %d - instance %08x, ops %08x, plat %08x\n", + i, + (u32)map_to_sysmem(dev), + (u32)map_to_sysmem(dev->driver->ops), + (u32)map_to_sysmem(dev_get_plat(dev))); + } + + return 0; +} + +static struct cmd_tbl armffa_commands[] = { + U_BOOT_CMD_MKENT(getpart, 1, 1, do_ffa_getpart, "", ""), + U_BOOT_CMD_MKENT(ping, 1, 1, do_ffa_ping, "", ""), + U_BOOT_CMD_MKENT(devlist, 0, 1, do_ffa_devlist, "", ""), +}; + +/** + * do_armffa() - the armffa command main function + * @cmdtp: Command Table + * @flag: flags + * @argc: number of arguments + * @argv: arguments + * + * This function identifies which armffa subcommand to run. + * Then, it makes sure the arm_ffa device is probed and + * ready for use. + * Then, it runs the subcommand. + * + * Return: + * + * CMD_RET_SUCCESS: on success, otherwise failure + */ +static int do_armffa(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct cmd_tbl *armffa_cmd; + int ret; + + if (argc < 2) + return CMD_RET_USAGE; + + armffa_cmd = find_cmd_tbl(argv[1], armffa_commands, ARRAY_SIZE(armffa_commands)); + + argc -= 2; + argv += 2; + + if (!armffa_cmd || argc > armffa_cmd->maxargs) + return CMD_RET_USAGE; + + if (IS_ENABLED(CONFIG_SANDBOX_FFA)) { + struct udevice *sdx_dev = NULL; + /* Probe the FF-A sandbox driver, then bind the FF-A bus driver */ + uclass_get_device_by_name(UCLASS_FFA, "sandbox_arm_ffa", &sdx_dev); + if (!sdx_dev) { + log_err("[FFA] Cannot find FF-A sandbox device\n"); + return -ENODEV; + } + } + + ret = armffa_cmd->cmd(armffa_cmd, flag, argc, argv); + + return cmd_process_error(armffa_cmd, ret); +} + +U_BOOT_CMD(armffa, 4, 1, do_armffa, + "Arm FF-A operations test command", + "getpart <partition UUID>\n" + " - lists the partition(s) info\n" + "ping <partition ID>\n" + " - sends a data pattern to the specified partition\n" + "devlist\n" + " - displays instance info of FF-A devices (the bus and its associated sandbox\n"); diff --git a/doc/usage/cmd/armffa.rst b/doc/usage/cmd/armffa.rst new file mode 100644 index 0000000000..91f89971d1 --- /dev/null +++ b/doc/usage/cmd/armffa.rst @@ -0,0 +1,118 @@ +.. SPDX-License-Identifier: GPL-2.0+: + +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 instance info of FF-A devices (the bus and its associated sandbox) + +Description +----------- + +armffa is a command showcasing how to use the FF-A driver 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 with debug logs enabled. + +* ping + +:: + + corstone1000# armffa ping 0x8003 + [FFA] endpoint ID is 0 + [FFA] Using 1 4KB page(s) for RX/TX buffers size + [FFA] RX buffer at virtual address 00000000fdf48000 + [FFA] TX buffer at virtual address 00000000fdf4a000 + [FFA] RX/TX buffers mapped + [FFA] Reading partitions data from the RX buffer + [FFA] Partition ID 8001 : info cached + [FFA] Partition ID 8002 : info cached + [FFA] Partition ID 8003 : info cached + [FFA] 3 partition(s) found and cached + [FFA] SP response: + [LSB] + [FFA] 0xfffffffe + [FFA] 0x0 + [FFA] 0x0 + [FFA] 0x0 + [FFA] 0x0 + +* ping (failure case) + +:: + + corstone1000# armffa ping 0x1234 + [FFA] Sending direct request error (-22) + Command 'ping' failed: Error -22 + +* getpart + +:: + + corstone1000# armffa getpart 33d532ed-e699-0942-c09c-a798d9cd722d + [FFA] Preparing for checking partitions count + [FFA] Searching partitions using the provided UUID + [FFA] No partition found. Querying framework ... + [FFA] Reading partitions data from the RX buffer + [FFA] Number of partition(s) matching the UUID: 1 + [FFA] Pre-allocating 1 partition(s) info structures + [FFA] Preparing for filling partitions info + [FFA] Searching partitions using the provided UUID + [FFA] Partition ID 8003 matches the provided UUID + [FFA] Partition: id = 0x8003 , exec_ctxt 0x1 , properties 0x3 + +* getpart (failure case) + +:: + + corstone1000# armffa getpart ed32d533-4209-99e6-2d72-cdd998a79cc0 + [FFA] Preparing for checking partitions count + [FFA] Searching partitions using the provided UUID + [FFA] No partition found. Querying framework ... + [FFA] INVALID_PARAMETERS: Unrecognized UUID + [FFA] Failure in querying partitions count (error code: -22) + Command 'getpart' failed: Error -22 + +* devlist + +:: + + corstone1000# armffa devlist + [FFA] FF-A uclass entries: + [FFA] entry 0 - instance fdf40c50, ops fffc0408, plat 00000000 + +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 and a negative error code on failure. diff --git a/doc/usage/index.rst b/doc/usage/index.rst index 840c20c934..f1b9335cb1 100644 --- a/doc/usage/index.rst +++ b/doc/usage/index.rst @@ -21,6 +21,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 e5b2912201..2cfd7ef5fc 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 select ARM_SMCCC select ARM_SMCCC_FEATURES + imply CMD_ARMFFA select LIB_UUID select DEVRES help

Hi Abdellatif,
On Fri, 10 Mar 2023 at 06:10, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
Provide armffa command showcasing the use of the FF-A driver
armffa is a command showcasing how to use the FF-A driver 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.
For more details please refer to the command documentation [1].
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v9:
- remove manual FF-A discovery and use DM
- use DM class APIs to probe and interact with the FF-A bus
- add doc/usage/cmd/armffa.rst
v8:
- update partition_info_get() second argument to be an SP count
- pass NULL device pointer to the FF-A bus discovery and operations
v7:
- adapt do_ffa_dev_list() following the recent update on uclass_first_device/uclass_next_device functions (they return void now)
- set armffa command to use 64-bit direct messaging
v4:
- remove pattern data in do_ffa_msg_send_direct_req
v3:
- use the new driver interfaces (partition_info_get, sync_send_receive) in armffa command
v2:
- replace use of ffa_helper_init_device function by
ffa_helper_bus_discover
v1:
- introduce armffa command
MAINTAINERS | 2 + cmd/Kconfig | 10 ++ cmd/Makefile | 2 + cmd/armffa.c | 264 +++++++++++++++++++++++++++++++ doc/usage/cmd/armffa.rst | 118 ++++++++++++++ doc/usage/index.rst | 1 +
+Heinrich Schuchardt for docs
drivers/firmware/arm-ffa/Kconfig | 1 + 7 files changed, 398 insertions(+) create mode 100644 cmd/armffa.c create mode 100644 doc/usage/cmd/armffa.rst
diff --git a/MAINTAINERS b/MAINTAINERS index 1dfa23c1f0..18e9c2ce99 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -269,7 +269,9 @@ F: configs/cortina_presidio-asic-pnand_defconfig ARM FF-A M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com S: Maintained +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
diff --git a/cmd/Kconfig b/cmd/Kconfig index a3512836c1..f24c52def4 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -934,6 +934,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 Arm FF-A driver
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 2d8bb4fc05..a59ab55ad0 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -12,6 +12,8 @@ obj-y += panic.o obj-y += version.o
# command
+obj-$(CONFIG_CMD_ARMFFA) += armffa.o obj-$(CONFIG_CMD_ACPI) += acpi.o obj-$(CONFIG_CMD_ADDRMAP) += addrmap.o obj-$(CONFIG_CMD_AES) += aes.o diff --git a/cmd/armffa.c b/cmd/armffa.c new file mode 100644 index 0000000000..f6c017542d --- /dev/null +++ b/cmd/armffa.c @@ -0,0 +1,264 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
- Authors:
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include <common.h> +#include <arm_ffa.h> +#include <command.h> +#include <dm.h> +#include <mapmem.h> +#include <stdlib.h> +#include <asm/io.h>
+/**
- do_ffa_getpart() - implementation of the getpart subcommand
- @cmdtp: Command Table
- @flag: flags
- @argc: number of arguments
- @argv: arguments
- This function queries the secure partition information which the 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_info *parts_info;
u32 info_idx;
struct udevice *dev = NULL;
struct ffa_bus_ops *ffa_ops = NULL;
if (argc != 1)
return -EINVAL;
uclass_get_device_by_name(UCLASS_FFA, "arm_ffa", &dev);
uclass_first_device()
The name is most likely to have a hyphen than an underscore due to devicetree conventions. Anyway, there should be only one.
if (!dev) {
log_err("[FFA] Cannot find FF-A bus device\n");
return -ENODEV;
}
ffa_ops = (struct ffa_bus_ops *)ffa_bus_get_ops(dev);unl
if (!ffa_ops) {
log_err("[FFA] Invalid FF-A ops\n");
return -EINVAL;
}
/* Mode 1: getting the number of secure partitions */
ret = ffa_ops->partition_info_get(dev, argv[0], &count, NULL);
These should be uclass operations in armffa.h, i.e. they implement the uclass. See how other things do this, e.g. p2sb.h is a really simple example. Then you implement the callers in armffa-uclass.c rather than accessing the ops here.
if (ret != 0) {
if (ret)
Please fix below
log_err("[FFA] Failure in querying partitions count (error code: %d)\n", ret);
return ret;
}
if (!count) {
log_info("[FFA] No secure partition found\n");
return ret;
}
/*
* Pre-allocate a buffer to be filled by the driver
* with ffa_partition_info structs
*/
log_info("[FFA] Pre-allocating %d partition(s) info structures\n", count);
parts_info = calloc(count, sizeof(struct ffa_partition_info));
if (!parts_info)
return -EINVAL;
/* Ask the driver to fill the buffer with the SPs info */
ret = ffa_ops->partition_info_get(dev, argv[0], &count, parts_info);
here again the operation
if (ret != 0) {
log_err("[FFA] Failure in querying partition(s) info (error code: %d)\n", ret);
free(parts_info);
return ret;
}
/* SPs found , show the partition information */
for (info_idx = 0; info_idx < count ; info_idx++) {
log_info("[FFA] Partition: id = 0x%x , exec_ctxt 0x%x , properties 0x%x\n",
parts_info[info_idx].id,
parts_info[info_idx].exec_ctxt,
parts_info[info_idx].properties);
}
free(parts_info);
return 0;
+}
+/**
- do_ffa_ping() - implementation of the ping subcommand
- @cmdtp: Command Table
- @flag: flags
- @argc: number of arguments
- @argv: arguments
- This function sends data to the secure partition which the ID is provided
- as an argument. The function uses 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
- */
+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 = NULL;
struct ffa_bus_ops *ffa_ops = NULL;
if (argc != 1)
return -EINVAL;
errno = 0;
part_id = strtoul(argv[0], NULL, 16);
if (errno) {
log_err("[FFA] Invalid partition ID\n");
return -EINVAL;
}
uclass_get_device_by_name(UCLASS_FFA, "arm_ffa", &dev);
if (!dev) {
log_err("[FFA] Cannot find FF-A bus device\n");
return -ENODEV;
}
ffa_ops = (struct ffa_bus_ops *)ffa_bus_get_ops(dev);
if (!ffa_ops) {
log_err("[FFA] Invalid FF-A ops\n");
return -EINVAL;
}
ret = ffa_ops->sync_send_receive(dev, part_id, &msg, 1);
if (!ret) {
u8 cnt;
log_info("[FFA] SP response:\n[LSB]\n");
for (cnt = 0;
cnt < sizeof(struct ffa_send_direct_data) / sizeof(u64);
cnt++)
log_info("[FFA] 0x%llx\n", ((u64 *)&msg)[cnt]);
} else {
log_err("[FFA] Sending direct request error (%d)\n", ret);
}
return ret;
+}
+/**
- *do_ffa_devlist() - implementation of the devlist subcommand
- @cmdtp: [in] Command Table
- @flag: flags
- @argc: number of arguments
- @argv: arguments
- This function queries the devices belonging to the UCLASS_FFA
- class.
- Return:
- CMD_RET_SUCCESS: on success, otherwise failure
- */
+int do_ffa_devlist(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{
struct udevice *dev = NULL;
int i;
log_info("[FFA] FF-A uclass entries:\n");
for (i = 0, uclass_first_device(UCLASS_FFA, &dev);
dev;
uclass_next_device(&dev), i++) {
uclass_foreacah_dev_probe(dUCLASS_FFA, dev) {
log_info("[FFA] entry %d - instance %08x, ops %08x, plat %08x\n",
i,
(u32)map_to_sysmem(dev),
(u32)map_to_sysmem(dev->driver->ops),
(u32)map_to_sysmem(dev_get_plat(dev)));
}
return 0;
+}
+static struct cmd_tbl armffa_commands[] = {
U_BOOT_CMD_MKENT(getpart, 1, 1, do_ffa_getpart, "", ""),
U_BOOT_CMD_MKENT(ping, 1, 1, do_ffa_ping, "", ""),
U_BOOT_CMD_MKENT(devlist, 0, 1, do_ffa_devlist, "", ""),
+};
+/**
- do_armffa() - the armffa command main function
- @cmdtp: Command Table
- @flag: flags
- @argc: number of arguments
- @argv: arguments
- This function identifies which armffa subcommand to run.
- Then, it makes sure the arm_ffa device is probed and
- ready for use.
- Then, it runs the subcommand.
- Return:
- CMD_RET_SUCCESS: on success, otherwise failure
- */
+static int do_armffa(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{
struct cmd_tbl *armffa_cmd;
int ret;
if (argc < 2)
return CMD_RET_USAGE;
armffa_cmd = find_cmd_tbl(argv[1], armffa_commands, ARRAY_SIZE(armffa_commands));
argc -= 2;
argv += 2;
if (!armffa_cmd || argc > armffa_cmd->maxargs)
return CMD_RET_USAGE;
if (IS_ENABLED(CONFIG_SANDBOX_FFA)) {
No, this needs to be bound automatically. Add it to test.dts and let sandbox bind it.
struct udevice *sdx_dev = NULL;
/* Probe the FF-A sandbox driver, then bind the FF-A bus driver */
uclass_get_device_by_name(UCLASS_FFA, "sandbox_arm_ffa", &sdx_dev);
if (!sdx_dev) {
log_err("[FFA] Cannot find FF-A sandbox device\n");
return -ENODEV;
}
}
ret = armffa_cmd->cmd(armffa_cmd, flag, argc, argv);
return cmd_process_error(armffa_cmd, ret);
+}
[..]
Regards, Simon

Provide a Sandbox driver to emulate the FF-A ABIs
The emulated ABIs are those supported by the FF-A core driver and according to FF-A specification v1.0.
The Sandbox driver provides operations allowing the test application to read the status of all the inspected ABIs and perform functional tests based on that.
sandbox driver supports only 64-bit direct messaging.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
--- Changelog: ===============
v9: align FF-A sandbox driver with FF-A discovery through DM
v8: update ffa_bus_prvdata_get() to return a pointer rather than a pointer address
v7: state that sandbox driver supports only 64-bit direct messaging
v4: align sandbox driver with the new FF-A driver interfaces and new way of error handling
v1: introduce the sandbox driver
MAINTAINERS | 1 + arch/sandbox/dts/sandbox.dtsi | 4 + arch/sandbox/dts/test.dts | 4 + configs/sandbox64_defconfig | 2 + configs/sandbox_defconfig | 2 + doc/arch/arm64.ffa.rst | 4 + doc/arch/sandbox/sandbox.rst | 1 + drivers/firmware/arm-ffa/Kconfig | 11 +- drivers/firmware/arm-ffa/Makefile | 1 + drivers/firmware/arm-ffa/core.c | 36 +- drivers/firmware/arm-ffa/sandbox.c | 610 ++++++++++++++++++ .../firmware/arm-ffa/sandbox_arm_ffa_priv.h | 129 ++++ include/arm_ffa.h | 5 +- include/sandbox_arm_ffa.h | 124 ++++ 14 files changed, 928 insertions(+), 6 deletions(-) create mode 100644 drivers/firmware/arm-ffa/sandbox.c create mode 100644 drivers/firmware/arm-ffa/sandbox_arm_ffa_priv.h create mode 100644 include/sandbox_arm_ffa.h
diff --git a/MAINTAINERS b/MAINTAINERS index 18e9c2ce99..2b9d33e964 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -274,6 +274,7 @@ F: doc/arch/arm64.ffa.rst F: doc/usage/cmd/armffa.rst F: drivers/firmware/arm-ffa/ F: include/arm_ffa.h +F: include/sandbox_arm_ffa.h
ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de diff --git a/arch/sandbox/dts/sandbox.dtsi b/arch/sandbox/dts/sandbox.dtsi index 30a305c4d2..059c273277 100644 --- a/arch/sandbox/dts/sandbox.dtsi +++ b/arch/sandbox/dts/sandbox.dtsi @@ -445,6 +445,10 @@ thermal { compatible = "sandbox,thermal"; }; + + sandbox_arm_ffa { + compatible = "sandbox,arm_ffa"; + }; };
&cros_ec { diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index d72d7a567a..11dc6ed0d9 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -1802,6 +1802,10 @@ compatible = "u-boot,fwu-mdata-gpt"; fwu-mdata-store = <&mmc0>; }; + + sandbox_arm_ffa { + compatible = "sandbox,arm_ffa"; + }; };
#include "sandbox_pmic.dtsi" diff --git a/configs/sandbox64_defconfig b/configs/sandbox64_defconfig index ccbc18aad0..35d4676cf7 100644 --- a/configs/sandbox64_defconfig +++ b/configs/sandbox64_defconfig @@ -259,3 +259,5 @@ CONFIG_FWU_MULTI_BANK_UPDATE=y CONFIG_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y +CONFIG_ARM_FFA_TRANSPORT=y +CONFIG_SANDBOX_FFA=y diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index a0fbdad20a..8aab8dda31 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -336,3 +336,5 @@ CONFIG_TEST_FDTDEC=y CONFIG_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y +CONFIG_ARM_FFA_TRANSPORT=y +CONFIG_SANDBOX_FFA=y \ No newline at end of file diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst index 8fad9ef3d0..555ee9a6ae 100644 --- a/doc/arch/arm64.ffa.rst +++ b/doc/arch/arm64.ffa.rst @@ -64,6 +64,10 @@ CONFIG_ARM_FFA_TRANSPORT Enables the FF-A bus driver. Turn this on if you want to use FF-A communication.
+CONFIG_SANDBOX_FFA + Enables FF-A Sandbox driver. This emulates the FF-A ABIs handling under + Sandbox and provides functional tests for FF-A. + FF-A ABIs under the hood ---------------------------------------
diff --git a/doc/arch/sandbox/sandbox.rst b/doc/arch/sandbox/sandbox.rst index cd7f8a2cb0..c5df372e00 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 2cfd7ef5fc..b5430eb6f4 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 imply CMD_ARMFFA select LIB_UUID select DEVRES @@ -32,3 +32,8 @@ config ARM_FFA_TRANSPORT
For more details about the FF-A driver, please refer to doc/arch/arm64.ffa.rst
+config SANDBOX_FFA + bool "FF-A Sandbox driver" + depends on ARM_FFA_TRANSPORT && SANDBOX + help + This emulates the FF-A handling under Sandbox and allows to test the FF-A driver diff --git a/drivers/firmware/arm-ffa/Makefile b/drivers/firmware/arm-ffa/Makefile index c8d83b4bc9..d22c1ba609 100644 --- a/drivers/firmware/arm-ffa/Makefile +++ b/drivers/firmware/arm-ffa/Makefile @@ -6,3 +6,4 @@ # Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
obj-y += arm-ffa-uclass.o core.o +obj-$(CONFIG_SANDBOX_FFA) += sandbox.o diff --git a/drivers/firmware/arm-ffa/core.c b/drivers/firmware/arm-ffa/core.c index 2210f5343c..0d2e6ff0d4 100644 --- a/drivers/firmware/arm-ffa/core.c +++ b/drivers/firmware/arm-ffa/core.c @@ -1042,6 +1042,7 @@ bool ffa_try_discovery(void) return true; }
+#if !CONFIG_IS_ENABLED(SANDBOX_FFA) /** * __arm_ffa_fn_smc() - SMC wrapper * @args: FF-A ABI arguments to be copied to Xn registers @@ -1069,6 +1070,7 @@ void __arm_ffa_fn_smc(ffa_value_t args, ffa_value_t *res) * The FF-A driver supports the SMCCCv1.2 extended input/output registers. * So, the legacy SMC invocation is not used. * + * In Sandbox mode sandbox_arm_ffa is used to test arm_ffa driver. * Return: * * 0 on success. Otherwise, failure @@ -1088,6 +1090,30 @@ ARM_SMCCC_FEATURE_DRIVER(arm_ffa) = { .driver_name = FFA_DRV_NAME, .is_supported = ffa_bus_is_supported, }; +#else +/* SANDBOX_FFA */ + +/** + * ffa_bind() - The driver bind function + * @dev: the arm_ffa device + * When using sandbox tries to discover the emulated FF-A bus. + * Return: + * + * 0 on success. + */ +static int ffa_bind(struct udevice *dev) +{ + bool ret; + + log_info("[FFA] binding the device\n"); + + ret = ffa_try_discovery(); + if (ret) + return 0; + else + return -ENODEV; +} +#endif
/** * ffa_set_smc_conduit() - Set the SMC conduit @@ -1101,7 +1127,12 @@ ARM_SMCCC_FEATURE_DRIVER(arm_ffa) = { */ static int ffa_set_smc_conduit(void) { - dscvry_info.invoke_ffa_fn = __arm_ffa_fn_smc; +#if CONFIG_IS_ENABLED(SANDBOX_FFA) + dscvry_info.invoke_ffa_fn = sandbox_arm_ffa_smccc_smc; + log_info("[FFA] Using SMC emulation\n"); +#else + dscvry_info.invoke_ffa_fn = __arm_ffa_fn_smc; +#endif
log_info("[FFA] Conduit is SMC\n");
@@ -1246,4 +1277,7 @@ U_BOOT_DRIVER(arm_ffa) = { .remove = ffa_remove, .unbind = ffa_unbind, .ops = &ffa_ops, +#if CONFIG_IS_ENABLED(SANDBOX_FFA) + .bind = ffa_bind, +#endif }; diff --git a/drivers/firmware/arm-ffa/sandbox.c b/drivers/firmware/arm-ffa/sandbox.c new file mode 100644 index 0000000000..84c2fc929f --- /dev/null +++ b/drivers/firmware/arm-ffa/sandbox.c @@ -0,0 +1,610 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <dm.h> +#include <mapmem.h> +#include <string.h> +#include <asm/global_data.h> +#include <dm/device-internal.h> +#include <dm/lists.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> +#include "sandbox_arm_ffa_priv.h" + +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_SP2_ID, .exec_ctxt = 0x9587, .properties = 0x45325621 }, + .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_SP3_ID, .exec_ctxt = 0x7687, .properties = 0x23325621 }, + .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, + } + } + +}; + +/* Driver functions */ + +/** + * sandbox_ffa_version() - Emulated FFA_VERSION handler function + * @dev: the sandbox FF-A device + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_VERSION FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_version) +{ + struct sandbox_ffa_priv *priv = dev_get_priv(dev); + + 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(unsigned long)); + + return 0; +} + +/** + * sandbox_ffa_id_get() - Emulated FFA_ID_GET handler function + * @dev: the sandbox FF-A device + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_ID_GET FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_id_get) +{ + struct sandbox_ffa_priv *priv = dev_get_priv(dev); + + 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(unsigned long)); + + return 0; +} + +/** + * sandbox_ffa_features() - Emulated FFA_FEATURES handler function + * @dev: the sandbox FF-A device + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_FEATURES FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_features) +{ + 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(unsigned long)); + } else { + res->a0 = FFA_SMC_32(FFA_ERROR); + res->a2 = FFA_ERR_STAT_NOT_SUPPORTED; + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, + 0, FFA_X3X7_MBZ_CNT * sizeof(unsigned long)); + log_err("[FFA] [Sandbox] FF-A interface 0x%lx not implemented\n", pargs->a1); + } + + res->a1 = 0; + + return 0; +} + +/** + * sandbox_ffa_partition_info_get() - Emulated FFA_PARTITION_INFO_GET handler function + * @dev: the sandbox FF-A device + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_PARTITION_INFO_GET FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_partition_info_get) +{ + struct ffa_partition_info *rxbuf_desc_info = NULL; + u32 descs_cnt; + u32 descs_size_bytes; + struct sandbox_ffa_priv *priv = dev_get_priv(dev); + + res->a0 = FFA_SMC_32(FFA_ERROR); + + if (!priv->pair.rxbuf) { + res->a2 = FFA_ERR_STAT_DENIED; + goto cleanup; + } + + if (priv->pair_info.rxbuf_owned) { + res->a2 = FFA_ERR_STAT_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 descriptors buffer size */ + if ((priv->pair_info.rxtx_buf_size * SZ_4K) < descs_size_bytes) { + res->a2 = FFA_ERR_STAT_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; + + goto cleanup; + } + + /* A UUID is specified. Return the information of all partitions 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 = (unsigned long) + (rxbuf_desc_info - (struct ffa_partition_info *)priv->pair.rxbuf); + + /* Transfer ownership to the consumer: the non secure world */ + priv->pair_info.rxbuf_owned = 1; + } else { + /* Unrecognized UUID */ + res->a2 = FFA_ERR_STAT_INVALID_PARAMETERS; + } + +cleanup: + + log_err("[FFA] [Sandbox] 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(unsigned long)); + + return 0; +} + +/** + * sandbox_ffa_rxtx_map() - Emulated FFA_RXTX_MAP handler function + * @dev: the sandbox FF-A device + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_RXTX_MAP FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_rxtx_map) +{ + struct sandbox_ffa_priv *priv = dev_get_priv(dev); + + res->a0 = FFA_SMC_32(FFA_ERROR); + + if (priv->pair.txbuf && priv->pair.rxbuf) { + res->a2 = FFA_ERR_STAT_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; + goto feedback; + } + + if (!pargs->a1 || !pargs->a2) + res->a2 = FFA_ERR_STAT_INVALID_PARAMETERS; + else + res->a2 = FFA_ERR_STAT_NO_MEMORY; + + log_err("[FFA] [Sandbox] 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(unsigned long)); + + return 0; +} + +/** + * sandbox_ffa_rxtx_unmap() - Emulated FFA_RXTX_UNMAP handler function + * @dev: the sandbox FF-A device + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_RXTX_UNMAP FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_rxtx_unmap) +{ + struct sandbox_ffa_priv *priv = dev_get_priv(dev); + + res->a0 = FFA_SMC_32(FFA_ERROR); + res->a2 = FFA_ERR_STAT_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; + goto feedback; + } + + log_err("[FFA] [Sandbox] 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(unsigned long)); + + return 0; +} + +/** + * sandbox_ffa_rx_release() - Emulated FFA_RX_RELEASE handler function + * @dev: the sandbox FF-A device + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_RX_RELEASE FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_rx_release) +{ + struct sandbox_ffa_priv *priv = dev_get_priv(dev); + + if (!priv->pair_info.rxbuf_owned) { + res->a0 = FFA_SMC_32(FFA_ERROR); + res->a2 = FFA_ERR_STAT_DENIED; + } else { + priv->pair_info.rxbuf_owned = 0; + res->a0 = FFA_SMC_32(FFA_SUCCESS); + res->a2 = 0; + } + + res->a1 = 0; + + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, + 0, FFA_X3X7_MBZ_CNT * sizeof(unsigned long)); + + return 0; +} + +/** + * sandbox_ffa_sp_valid() - Checks SP validity + * @dev: the sandbox_arm_ffa device + * @part_id: partition ID to check + * + * This is the function searches the input ID in the descriptors table. + * + * Return: + * + * 1 on success (Partition found). Otherwise, failure + */ +static int sandbox_ffa_sp_valid(struct udevice *dev, u16 part_id) +{ + u32 descs_cnt; + struct sandbox_ffa_priv *priv = dev_get_priv(dev); + + 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 function + * @dev: the sandbox FF-A device + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_MSG_SEND_DIRECT_{REQ,RESP} + * FF-A functions. 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 + */ +SANDBOX_SMC_FFA_ABI(ffa_msg_send_direct_req) +{ + u16 part_id; + struct sandbox_ffa_priv *priv = dev_get_priv(dev); + + part_id = GET_DST_SP_ID(pargs->a1); + + if ((GET_NS_PHYS_ENDPOINT_ID(pargs->a1) != priv->id) || + !sandbox_ffa_sp_valid(dev, part_id) || pargs->a2) { + res->a0 = FFA_SMC_32(FFA_ERROR); + res->a1 = 0; + res->a2 = FFA_ERR_STAT_INVALID_PARAMETERS; + + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, + 0, FFA_X3X7_MBZ_CNT * sizeof(unsigned long)); + + return 0; + } + + 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 = 0xffffffffffffffff; + res->a4 = 0xffffffffffffffff; + res->a5 = 0xffffffffffffffff; + res->a6 = 0xffffffffffffffff; + res->a7 = 0xffffffffffffffff; + + return 0; +} + +/** + * sandbox_ffa_get_rxbuf_flags() - Reading the mapping/ownership flags + * @dev: the sandbox_arm_ffa device + * @queried_func_id: The FF-A function to be queried + * @func_data: Pointer to the FF-A function arguments container structure + * + * This is the handler that queries 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 *dev, u32 queried_func_id, + struct ffa_sandbox_data *func_data) +{ + struct sandbox_ffa_priv *priv = dev_get_priv(dev); + + 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("[FFA] [Sandbox] The querried FF-A interface flag (%d) undefined\n", + queried_func_id); + return -EINVAL; + } +} + +/** + * sandbox_ffa_query_core_state() - The driver dispatcher function + * @dev: the sandbox_arm_ffa device + * @queried_func_id: The FF-A function to be queried + * @func_data: Pointer to the FF-A function arguments container structure + * + * Queries the status of FF-A ABI specified in the input argument. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int sandbox_ffa_query_core_state(struct udevice *dev, u32 queried_func_id, + struct ffa_sandbox_data *func_data) +{ + switch (queried_func_id) { + case FFA_RXTX_MAP: + case FFA_RXTX_UNMAP: + case FFA_RX_RELEASE: + return sandbox_ffa_get_rxbuf_flags(dev, queried_func_id, func_data); + default: + log_err("[FFA] [Sandbox] 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 + * + * Sandbox driver emulates the FF-A ABIs SMC call using this function. + * 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 *dev = NULL; + + uclass_get_device_by_name(UCLASS_FFA, "sandbox_arm_ffa", &dev); + if (!dev) { + log_err("[FFA] [Sandbox] Cannot find FF-A sandbox device\n"); + return; + } + + switch (args.a0) { + case FFA_SMC_32(FFA_VERSION): + ret = sandbox_ffa_version(dev, &args, res); + break; + case FFA_SMC_32(FFA_PARTITION_INFO_GET): + ret = sandbox_ffa_partition_info_get(dev, &args, res); + break; + case FFA_SMC_32(FFA_RXTX_UNMAP): + ret = sandbox_ffa_rxtx_unmap(dev, &args, res); + break; + case FFA_SMC_64(FFA_MSG_SEND_DIRECT_REQ): + ret = sandbox_ffa_msg_send_direct_req(dev, &args, res); + break; + case FFA_SMC_32(FFA_ID_GET): + ret = sandbox_ffa_id_get(dev, &args, res); + break; + case FFA_SMC_32(FFA_FEATURES): + ret = sandbox_ffa_features(dev, &args, res); + break; + case FFA_SMC_64(FFA_RXTX_MAP): + ret = sandbox_ffa_rxtx_map(dev, &args, res); + break; + case FFA_SMC_32(FFA_RX_RELEASE): + ret = sandbox_ffa_rx_release(dev, &args, res); + break; + default: + log_err("[FFA] [Sandbox] Undefined FF-A interface (0x%lx)\n", args.a0); + } + + if (ret != 0) + log_err("[FFA] [Sandbox] FF-A ABI internal failure (%d)\n", ret); +} + +/** + * sandbox_ffa_probe() - The driver probe function + * @dev: the sandbox_arm_ffa device + * + * Binds the FF-A bus driver and sets the sandbox device as the FF-A bus device parent + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int sandbox_ffa_probe(struct udevice *dev) +{ + struct udevice *child_dev = NULL; + int ret; + + ret = device_bind_driver(dev, FFA_DRV_NAME, FFA_DRV_NAME, &child_dev); + if (ret) { + pr_err("%s was not bound: %d, aborting\n", FFA_DRV_NAME, ret); + return -ENODEV; + } + + dev_set_parent_plat(child_dev, dev_get_plat(dev)); + + return 0; +} + +static const struct udevice_id sandbox_ffa_id[] = { + { "sandbox,arm_ffa", 0 }, + { }, +}; + +/* Declaring the sandbox_arm_ffa driver under UCLASS_FFA */ +U_BOOT_DRIVER(sandbox_arm_ffa) = { + .name = FFA_SANDBOX_DRV_NAME, + .of_match = sandbox_ffa_id, + .id = UCLASS_FFA, + .probe = sandbox_ffa_probe, + .priv_auto = sizeof(struct sandbox_ffa_priv), +}; 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 0000000000..c35d80de16 --- /dev/null +++ b/drivers/firmware/arm-ffa/sandbox_arm_ffa_priv.h @@ -0,0 +1,129 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __SANDBOX_ARM_FFA_PRV_H +#define __SANDBOX_ARM_FFA_PRV_H + +#include <sandbox_arm_ffa.h> +#include "arm_ffa_priv.h" + +/* This header is exclusively used by the Sandbox FF-A driver and sandbox tests */ + +/* FF-A core driver name */ +#define FFA_SANDBOX_DRV_NAME "sandbox_arm_ffa" + +/* FF-A ABIs internal error codes (as defined by the spec) */ + +#define FFA_ERR_STAT_NOT_SUPPORTED -1 +#define FFA_ERR_STAT_INVALID_PARAMETERS -2 +#define FFA_ERR_STAT_NO_MEMORY -3 +#define FFA_ERR_STAT_BUSY -4 +#define FFA_ERR_STAT_DENIED -6 + +/* 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 secure partitions emulated by the FF-A sandbox driver */ +#define SANDBOX_PARTITIONS_CNT (4) + +/* Binary data of services UUIDs emulated by the FF-A sandbox driver */ + +/* 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: the consumer) + * @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 as set by the FF-A core driver + * + * Data structure hosting 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_priv - the driver private data structure + * + * @dev: The arm_ffa device under u-boot driver model + * @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 + * @conduit: The selected conduit + * + * The driver data structure hosting all the emulated secure world data. + */ +struct sandbox_ffa_priv { + struct udevice *dev; + u32 fwk_version; + u16 id; + struct ffa_partitions partitions; + struct ffa_rxtxpair pair; + struct ffa_rxtxpair_info pair_info; +}; + +#define SANDBOX_SMC_FFA_ABI(ffabi) static int sandbox_##ffabi(struct udevice *dev, \ + ffa_value_t *pargs, ffa_value_t *res) + +#endif diff --git a/include/arm_ffa.h b/include/arm_ffa.h index 69f3c51080..70a9eefae6 100644 --- a/include/arm_ffa.h +++ b/include/arm_ffa.h @@ -12,8 +12,9 @@ #include <linux/printk.h>
/* - * This header is public. It can be used by clients to access - * data structures and definitions they need + * This header is used to access FF-A data structures + * and definitions. The header is used by the FF-A core driver, clients, + * sandbox driver, sandbox tests, armffa command */
/* diff --git a/include/sandbox_arm_ffa.h b/include/sandbox_arm_ffa.h new file mode 100644 index 0000000000..3d99b6b8fa --- /dev/null +++ b/include/sandbox_arm_ffa.h @@ -0,0 +1,124 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __SANDBOX_ARM_FFA_H +#define __SANDBOX_ARM_FFA_H + +#include <arm_ffa.h> + +/* + * This header provides the sandbox driver declarations + * needed by FF-A driver, armffa command and sandbox tests + */ + +/* Providing Arm SMCCC declarations to sandbox */ + +#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)) + +/** + * struct sandbox_smccc_1_2_regs - Arguments for or Results from emulated SMC call + * @a0-a17 argument values from registers 0 to 17 + */ +struct sandbox_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; +}; + +typedef struct sandbox_smccc_1_2_regs ffa_value_t; + +/* UUIDs strings of services emulated by the FF-A sandbox driver */ +#define SANDBOX_SERVICE1_UUID "ed32d533-4209-99e6-2d72-cdd998a79cc0" +#define SANDBOX_SERVICE2_UUID "ed32d544-4209-99e6-2d72-cdd998a79cc0" + +/* IDs of secure partitions (SPs) emulated by the FF-A sandbox driver */ +#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 - generic data structure used to exchange + * data between test cases and the sandbox driver + * @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 + * + * Using this structure sandbox test cases can pass various types of data with different sizes. + */ +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 driver public functions */ + +/** + * sandbox_ffa_query_core_state() - The driver dispatcher function + * @dev: the sandbox_arm_ffa device + * @queried_func_id: The FF-A function to be queried + * @func_data: Pointer to the FF-A function arguments container structure + * Return: + * 0 on success. Otherwise, failure + */ +int sandbox_ffa_query_core_state(struct udevice *dev, u32 queried_func_id, + struct ffa_sandbox_data *func_data); + +/** + * sandbox_arm_ffa_smccc_smc() - FF-A SMC call emulation + * @args: the SMC call arguments + * @res: the SMC call returned data + * 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); + +#endif

Hi Abdellatif,
On Fri, 10 Mar 2023 at 06:10, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
Provide a Sandbox driver to emulate the FF-A ABIs
The emulated ABIs are those supported by the FF-A core driver and according to FF-A specification v1.0.
The Sandbox driver provides operations allowing the test application to read the status of all the inspected ABIs and perform functional tests based on that.
sandbox driver supports only 64-bit direct messaging.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v9: align FF-A sandbox driver with FF-A discovery through DM
v8: update ffa_bus_prvdata_get() to return a pointer rather than a pointer address
v7: state that sandbox driver supports only 64-bit direct messaging
v4: align sandbox driver with the new FF-A driver interfaces and new way of error handling
v1: introduce the sandbox driver
MAINTAINERS | 1 + arch/sandbox/dts/sandbox.dtsi | 4 + arch/sandbox/dts/test.dts | 4 + configs/sandbox64_defconfig | 2 + configs/sandbox_defconfig | 2 + doc/arch/arm64.ffa.rst | 4 + doc/arch/sandbox/sandbox.rst | 1 + drivers/firmware/arm-ffa/Kconfig | 11 +- drivers/firmware/arm-ffa/Makefile | 1 + drivers/firmware/arm-ffa/core.c | 36 +- drivers/firmware/arm-ffa/sandbox.c | 610 ++++++++++++++++++ .../firmware/arm-ffa/sandbox_arm_ffa_priv.h | 129 ++++ include/arm_ffa.h | 5 +- include/sandbox_arm_ffa.h | 124 ++++ 14 files changed, 928 insertions(+), 6 deletions(-) create mode 100644 drivers/firmware/arm-ffa/sandbox.c create mode 100644 drivers/firmware/arm-ffa/sandbox_arm_ffa_priv.h create mode 100644 include/sandbox_arm_ffa.h
Could you use 80 columns where possible? There seem to be a lot of things that extend beyond that without much of a reason.
Also you can use 'ulong' instead of 'unsigned long'. It is less verbose and a U-Boot standard.
diff --git a/MAINTAINERS b/MAINTAINERS index 18e9c2ce99..2b9d33e964 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -274,6 +274,7 @@ F: doc/arch/arm64.ffa.rst F: doc/usage/cmd/armffa.rst F: drivers/firmware/arm-ffa/ F: include/arm_ffa.h +F: include/sandbox_arm_ffa.h
ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de diff --git a/arch/sandbox/dts/sandbox.dtsi b/arch/sandbox/dts/sandbox.dtsi index 30a305c4d2..059c273277 100644 --- a/arch/sandbox/dts/sandbox.dtsi +++ b/arch/sandbox/dts/sandbox.dtsi @@ -445,6 +445,10 @@ thermal { compatible = "sandbox,thermal"; };
sandbox_arm_ffa {
compatible = "sandbox,arm_ffa";
};
};
&cros_ec { diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index d72d7a567a..11dc6ed0d9 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -1802,6 +1802,10 @@ compatible = "u-boot,fwu-mdata-gpt"; fwu-mdata-store = <&mmc0>; };
sandbox_arm_ffa {
compatible = "sandbox,arm_ffa";
};
I see that you have this, so the driver should bind automatically.
Is the problem that you are trying to bind the sandbox emulator? Again, you can actually add that to the DT. See how this works for i2c, SPI, PCI, for example.
};
#include "sandbox_pmic.dtsi" diff --git a/configs/sandbox64_defconfig b/configs/sandbox64_defconfig index ccbc18aad0..35d4676cf7 100644 --- a/configs/sandbox64_defconfig +++ b/configs/sandbox64_defconfig @@ -259,3 +259,5 @@ CONFIG_FWU_MULTI_BANK_UPDATE=y CONFIG_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y +CONFIG_ARM_FFA_TRANSPORT=y +CONFIG_SANDBOX_FFA=y diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index a0fbdad20a..8aab8dda31 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -336,3 +336,5 @@ CONFIG_TEST_FDTDEC=y CONFIG_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y +CONFIG_ARM_FFA_TRANSPORT=y +CONFIG_SANDBOX_FFA=y \ No newline at end of file diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst index 8fad9ef3d0..555ee9a6ae 100644 --- a/doc/arch/arm64.ffa.rst +++ b/doc/arch/arm64.ffa.rst @@ -64,6 +64,10 @@ CONFIG_ARM_FFA_TRANSPORT Enables the FF-A bus driver. Turn this on if you want to use FF-A communication.
+CONFIG_SANDBOX_FFA
- Enables FF-A Sandbox driver. This emulates the FF-A ABIs handling under
- Sandbox and provides functional tests for FF-A.
OK that is why I am confused. Please don't call this a driver. It is an emulator. When you have an emulator and a driver for the same thing it gets confusing.
FF-A ABIs under the hood
diff --git a/doc/arch/sandbox/sandbox.rst b/doc/arch/sandbox/sandbox.rst index cd7f8a2cb0..c5df372e00 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 2cfd7ef5fc..b5430eb6f4 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 imply CMD_ARMFFA select LIB_UUID select DEVRES
@@ -32,3 +32,8 @@ config ARM_FFA_TRANSPORT
For more details about the FF-A driver, please refer to doc/arch/arm64.ffa.rst
+config SANDBOX_FFA
bool "FF-A Sandbox driver"
depends on ARM_FFA_TRANSPORT && SANDBOX
help
This emulates the FF-A handling under Sandbox and allows to test the FF-A driver
diff --git a/drivers/firmware/arm-ffa/Makefile b/drivers/firmware/arm-ffa/Makefile index c8d83b4bc9..d22c1ba609 100644 --- a/drivers/firmware/arm-ffa/Makefile +++ b/drivers/firmware/arm-ffa/Makefile @@ -6,3 +6,4 @@ # Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
obj-y += arm-ffa-uclass.o core.o +obj-$(CONFIG_SANDBOX_FFA) += sandbox.o diff --git a/drivers/firmware/arm-ffa/core.c b/drivers/firmware/arm-ffa/core.c index 2210f5343c..0d2e6ff0d4 100644 --- a/drivers/firmware/arm-ffa/core.c +++ b/drivers/firmware/arm-ffa/core.c @@ -1042,6 +1042,7 @@ bool ffa_try_discovery(void) return true; }
+#if !CONFIG_IS_ENABLED(SANDBOX_FFA)
We should not need #ifdefs here. The sandbox driver is just another driver, just like there is the ARM driver. We select the correct one using the devicetree and everything just works.
/**
- __arm_ffa_fn_smc() - SMC wrapper
- @args: FF-A ABI arguments to be copied to Xn registers
@@ -1069,6 +1070,7 @@ void __arm_ffa_fn_smc(ffa_value_t args, ffa_value_t *res)
- The FF-A driver supports the SMCCCv1.2 extended input/output registers.
- So, the legacy SMC invocation is not used.
- In Sandbox mode sandbox_arm_ffa is used to test arm_ffa driver.
- Return:
- 0 on success. Otherwise, failure
@@ -1088,6 +1090,30 @@ ARM_SMCCC_FEATURE_DRIVER(arm_ffa) = { .driver_name = FFA_DRV_NAME, .is_supported = ffa_bus_is_supported, }; +#else +/* SANDBOX_FFA */
+/**
- ffa_bind() - The driver bind function
- @dev: the arm_ffa device
- When using sandbox tries to discover the emulated FF-A bus.
- Return:
- 0 on success.
- */
+static int ffa_bind(struct udevice *dev)
I don't think this is binding anything. How about ffa_get_dev() ?
+{
bool ret;
log_info("[FFA] binding the device\n");
ret = ffa_try_discovery();
if (ret)
return 0;
else
return -ENODEV;
+} +#endif
/**
- ffa_set_smc_conduit() - Set the SMC conduit
@@ -1101,7 +1127,12 @@ ARM_SMCCC_FEATURE_DRIVER(arm_ffa) = { */ static int ffa_set_smc_conduit(void) {
dscvry_info.invoke_ffa_fn = __arm_ffa_fn_smc;
+#if CONFIG_IS_ENABLED(SANDBOX_FFA)
dscvry_info.invoke_ffa_fn = sandbox_arm_ffa_smccc_smc;
log_info("[FFA] Using SMC emulation\n");
+#else
dscvry_info.invoke_ffa_fn = __arm_ffa_fn_smc;
+#endif
This needs to be reworked to go through the uclass to the correct driver. Basically you need an ARM driver and a sandbox driver (which attaches to the emulator).
There should not be #idefs in this sort of code...driver model should handle it. See other uclasses for examples.
log_info("[FFA] Conduit is SMC\n");
@@ -1246,4 +1277,7 @@ U_BOOT_DRIVER(arm_ffa) = { .remove = ffa_remove, .unbind = ffa_unbind, .ops = &ffa_ops, +#if CONFIG_IS_ENABLED(SANDBOX_FFA)
.bind = ffa_bind,
Can drop this
+#endif }; diff --git a/drivers/firmware/arm-ffa/sandbox.c b/drivers/firmware/arm-ffa/sandbox.c
sandbox_emul.c pehaps?
I am pretty sure this is an emulator
new file mode 100644 index 0000000000..84c2fc929f --- /dev/null +++ b/drivers/firmware/arm-ffa/sandbox.c @@ -0,0 +1,610 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
- Authors:
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include <common.h> +#include <dm.h> +#include <mapmem.h> +#include <string.h> +#include <asm/global_data.h> +#include <dm/device-internal.h> +#include <dm/lists.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> +#include "sandbox_arm_ffa_priv.h"
+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_SP2_ID, .exec_ctxt = 0x9587, .properties = 0x45325621 },
.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_SP3_ID, .exec_ctxt = 0x7687, .properties = 0x23325621 },
.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,
}
}
+};
+/* Driver functions */
+/**
- sandbox_ffa_version() - Emulated FFA_VERSION handler function
- @dev: the sandbox FF-A device
- @{a0-a7} , res: The SMC call arguments and return structure.
- This is the function that emulates FFA_VERSION FF-A function.
- Return:
- 0 on success. Otherwise, failure
- */
+SANDBOX_SMC_FFA_ABI(ffa_version)
Instead of the macro can you write this out in full? It defeats ctags, etc.
+{
struct sandbox_ffa_priv *priv = dev_get_priv(dev);
priv->fwk_version = FFA_VERSION_1_0;
res->a0 = priv->fwk_version;
Where is res defined?
/* x1-x7 MBZ */
memset(FFA_X1X7_MBZ_REG_START, 0, FFA_X1X7_MBZ_CNT * sizeof(unsigned long));
return 0;
+}
+/**
- sandbox_ffa_id_get() - Emulated FFA_ID_GET handler function
- @dev: the sandbox FF-A device
- @{a0-a7} , res: The SMC call arguments and return structure.
- This is the function that emulates FFA_ID_GET FF-A function.
s/This is the function that e/E/g
- Return:
- 0 on success. Otherwise, failure
- */
+SANDBOX_SMC_FFA_ABI(ffa_id_get) +{
struct sandbox_ffa_priv *priv = dev_get_priv(dev);
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(unsigned long));
return 0;
+}
+/**
- sandbox_ffa_features() - Emulated FFA_FEATURES handler function
- @dev: the sandbox FF-A device
- @{a0-a7} , res: The SMC call arguments and return structure.
- This is the function that emulates FFA_FEATURES FF-A function.
- Return:
- 0 on success. Otherwise, failure
- */
+SANDBOX_SMC_FFA_ABI(ffa_features) +{
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(unsigned long));
} else {
res->a0 = FFA_SMC_32(FFA_ERROR);
res->a2 = FFA_ERR_STAT_NOT_SUPPORTED;
/* x3-x7 MBZ */
memset(FFA_X3_MBZ_REG_START,
0, FFA_X3X7_MBZ_CNT * sizeof(unsigned long));
log_err("[FFA] [Sandbox] FF-A interface 0x%lx not implemented\n", pargs->a1);
return -ENOSYS
}
res->a1 = 0;
return 0;
+}
[..]
+SANDBOX_SMC_FFA_ABI(ffa_msg_send_direct_req) +{
u16 part_id;
struct sandbox_ffa_priv *priv = dev_get_priv(dev);
part_id = GET_DST_SP_ID(pargs->a1);
if ((GET_NS_PHYS_ENDPOINT_ID(pargs->a1) != priv->id) ||
drop extra brackets
!sandbox_ffa_sp_valid(dev, part_id) || pargs->a2) {
res->a0 = FFA_SMC_32(FFA_ERROR);
res->a1 = 0;
res->a2 = FFA_ERR_STAT_INVALID_PARAMETERS;
/* x3-x7 MBZ */
memset(FFA_X3_MBZ_REG_START,
0, FFA_X3X7_MBZ_CNT * sizeof(unsigned long));
return 0;
}
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 = 0xffffffffffffffff;
res->a4 = 0xffffffffffffffff;
res->a5 = 0xffffffffffffffff;
res->a6 = 0xffffffffffffffff;
res->a7 = 0xffffffffffffffff;
Would -1UL work?
return 0;
+}
[..]
+static int sandbox_ffa_probe(struct udevice *dev) +{
struct udevice *child_dev = NULL;
int ret;
ret = device_bind_driver(dev, FFA_DRV_NAME, FFA_DRV_NAME, &child_dev);
Is this binding the emulator? Add it to the DT instead.
If you put
.post_bind = dm_scan_fdt_dev()
in your uclass (like spi-uclass.c does) then it will binding child-node devices automatically. Then you can make your emulator a child node.
if (ret) {
pr_err("%s was not bound: %d, aborting\n", FFA_DRV_NAME, ret);
return -ENODEV;
}
dev_set_parent_plat(child_dev, dev_get_plat(dev));
return 0;
+}
+static const struct udevice_id sandbox_ffa_id[] = {
{ "sandbox,arm_ffa", 0 },
{ },
+};
+/* Declaring the sandbox_arm_ffa driver under UCLASS_FFA */ +U_BOOT_DRIVER(sandbox_arm_ffa) = {
.name = FFA_SANDBOX_DRV_NAME,
.of_match = sandbox_ffa_id,
.id = UCLASS_FFA,
.probe = sandbox_ffa_probe,
.priv_auto = sizeof(struct sandbox_ffa_priv),
+}; 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 0000000000..c35d80de16 --- /dev/null +++ b/drivers/firmware/arm-ffa/sandbox_arm_ffa_priv.h @@ -0,0 +1,129 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
- Authors:
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#ifndef __SANDBOX_ARM_FFA_PRV_H +#define __SANDBOX_ARM_FFA_PRV_H
+#include <sandbox_arm_ffa.h> +#include "arm_ffa_priv.h"
+/* This header is exclusively used by the Sandbox FF-A driver and sandbox tests */
+/* FF-A core driver name */ +#define FFA_SANDBOX_DRV_NAME "sandbox_arm_ffa"
+/* FF-A ABIs internal error codes (as defined by the spec) */
+#define FFA_ERR_STAT_NOT_SUPPORTED -1 +#define FFA_ERR_STAT_INVALID_PARAMETERS -2 +#define FFA_ERR_STAT_NO_MEMORY -3 +#define FFA_ERR_STAT_BUSY -4 +#define FFA_ERR_STAT_DENIED -6
It's a convention to put negative values in brackets just in case of operator-precedence issues. Or you could use an enum.
+/* 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 secure partitions emulated by the FF-A sandbox driver */ +#define SANDBOX_PARTITIONS_CNT (4)
+/* Binary data of services UUIDs emulated by the FF-A sandbox driver */
+/* 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: the consumer)
- @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 as set by the FF-A core driver
- Data structure hosting 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_priv - the driver private data structure
- @dev: The arm_ffa device under u-boot driver model
- @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
- @conduit: The selected conduit
- The driver data structure hosting all the emulated secure world data.
- */
+struct sandbox_ffa_priv {
struct udevice *dev;
u32 fwk_version;
u16 id;
struct ffa_partitions partitions;
struct ffa_rxtxpair pair;
struct ffa_rxtxpair_info pair_info;
+};
+#define SANDBOX_SMC_FFA_ABI(ffabi) static int sandbox_##ffabi(struct udevice *dev, \
ffa_value_t *pargs, ffa_value_t *res)
drop that
[..]
Regards, Simon

On Fri, Mar 10, 2023 at 12:49:57PM -0800, Simon Glass wrote:
Hi Abdellatif,
On Fri, 10 Mar 2023 at 06:10, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
Provide a Sandbox driver to emulate the FF-A ABIs
The emulated ABIs are those supported by the FF-A core driver and according to FF-A specification v1.0.
The Sandbox driver provides operations allowing the test application to read the status of all the inspected ABIs and perform functional tests based on that.
sandbox driver supports only 64-bit direct messaging.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v9: align FF-A sandbox driver with FF-A discovery through DM
v8: update ffa_bus_prvdata_get() to return a pointer rather than a pointer address
v7: state that sandbox driver supports only 64-bit direct messaging
v4: align sandbox driver with the new FF-A driver interfaces and new way of error handling
v1: introduce the sandbox driver
MAINTAINERS | 1 + arch/sandbox/dts/sandbox.dtsi | 4 + arch/sandbox/dts/test.dts | 4 + configs/sandbox64_defconfig | 2 + configs/sandbox_defconfig | 2 + doc/arch/arm64.ffa.rst | 4 + doc/arch/sandbox/sandbox.rst | 1 + drivers/firmware/arm-ffa/Kconfig | 11 +- drivers/firmware/arm-ffa/Makefile | 1 + drivers/firmware/arm-ffa/core.c | 36 +- drivers/firmware/arm-ffa/sandbox.c | 610 ++++++++++++++++++ .../firmware/arm-ffa/sandbox_arm_ffa_priv.h | 129 ++++ include/arm_ffa.h | 5 +- include/sandbox_arm_ffa.h | 124 ++++ 14 files changed, 928 insertions(+), 6 deletions(-) create mode 100644 drivers/firmware/arm-ffa/sandbox.c create mode 100644 drivers/firmware/arm-ffa/sandbox_arm_ffa_priv.h create mode 100644 include/sandbox_arm_ffa.h
Could you use 80 columns where possible? There seem to be a lot of things that extend beyond that without much of a reason.
Also you can use 'ulong' instead of 'unsigned long'. It is less verbose and a U-Boot standard.
diff --git a/MAINTAINERS b/MAINTAINERS index 18e9c2ce99..2b9d33e964 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -274,6 +274,7 @@ F: doc/arch/arm64.ffa.rst F: doc/usage/cmd/armffa.rst F: drivers/firmware/arm-ffa/ F: include/arm_ffa.h +F: include/sandbox_arm_ffa.h
ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de diff --git a/arch/sandbox/dts/sandbox.dtsi b/arch/sandbox/dts/sandbox.dtsi index 30a305c4d2..059c273277 100644 --- a/arch/sandbox/dts/sandbox.dtsi +++ b/arch/sandbox/dts/sandbox.dtsi @@ -445,6 +445,10 @@ thermal { compatible = "sandbox,thermal"; };
sandbox_arm_ffa {
compatible = "sandbox,arm_ffa";
};
};
&cros_ec { diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index d72d7a567a..11dc6ed0d9 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -1802,6 +1802,10 @@ compatible = "u-boot,fwu-mdata-gpt"; fwu-mdata-store = <&mmc0>; };
sandbox_arm_ffa {
compatible = "sandbox,arm_ffa";
};
I see that you have this, so the driver should bind automatically.
Is the problem that you are trying to bind the sandbox emulator? Again, you can actually add that to the DT. See how this works for i2c, SPI, PCI, for example.
};
#include "sandbox_pmic.dtsi" diff --git a/configs/sandbox64_defconfig b/configs/sandbox64_defconfig index ccbc18aad0..35d4676cf7 100644 --- a/configs/sandbox64_defconfig +++ b/configs/sandbox64_defconfig @@ -259,3 +259,5 @@ CONFIG_FWU_MULTI_BANK_UPDATE=y CONFIG_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y +CONFIG_ARM_FFA_TRANSPORT=y +CONFIG_SANDBOX_FFA=y diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index a0fbdad20a..8aab8dda31 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -336,3 +336,5 @@ CONFIG_TEST_FDTDEC=y CONFIG_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y +CONFIG_ARM_FFA_TRANSPORT=y +CONFIG_SANDBOX_FFA=y \ No newline at end of file diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst index 8fad9ef3d0..555ee9a6ae 100644 --- a/doc/arch/arm64.ffa.rst +++ b/doc/arch/arm64.ffa.rst @@ -64,6 +64,10 @@ CONFIG_ARM_FFA_TRANSPORT Enables the FF-A bus driver. Turn this on if you want to use FF-A communication.
+CONFIG_SANDBOX_FFA
- Enables FF-A Sandbox driver. This emulates the FF-A ABIs handling under
- Sandbox and provides functional tests for FF-A.
OK that is why I am confused. Please don't call this a driver. It is an emulator. When you have an emulator and a driver for the same thing it gets confusing.
FF-A ABIs under the hood
diff --git a/doc/arch/sandbox/sandbox.rst b/doc/arch/sandbox/sandbox.rst index cd7f8a2cb0..c5df372e00 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 2cfd7ef5fc..b5430eb6f4 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 imply CMD_ARMFFA select LIB_UUID select DEVRES
@@ -32,3 +32,8 @@ config ARM_FFA_TRANSPORT
For more details about the FF-A driver, please refer to doc/arch/arm64.ffa.rst
+config SANDBOX_FFA
bool "FF-A Sandbox driver"
depends on ARM_FFA_TRANSPORT && SANDBOX
help
This emulates the FF-A handling under Sandbox and allows to test the FF-A driver
diff --git a/drivers/firmware/arm-ffa/Makefile b/drivers/firmware/arm-ffa/Makefile index c8d83b4bc9..d22c1ba609 100644 --- a/drivers/firmware/arm-ffa/Makefile +++ b/drivers/firmware/arm-ffa/Makefile @@ -6,3 +6,4 @@ # Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
obj-y += arm-ffa-uclass.o core.o +obj-$(CONFIG_SANDBOX_FFA) += sandbox.o diff --git a/drivers/firmware/arm-ffa/core.c b/drivers/firmware/arm-ffa/core.c index 2210f5343c..0d2e6ff0d4 100644 --- a/drivers/firmware/arm-ffa/core.c +++ b/drivers/firmware/arm-ffa/core.c @@ -1042,6 +1042,7 @@ bool ffa_try_discovery(void) return true; }
+#if !CONFIG_IS_ENABLED(SANDBOX_FFA)
We should not need #ifdefs here. The sandbox driver is just another driver, just like there is the ARM driver. We select the correct one using the devicetree and everything just works.
/**
- __arm_ffa_fn_smc() - SMC wrapper
- @args: FF-A ABI arguments to be copied to Xn registers
@@ -1069,6 +1070,7 @@ void __arm_ffa_fn_smc(ffa_value_t args, ffa_value_t *res)
- The FF-A driver supports the SMCCCv1.2 extended input/output registers.
- So, the legacy SMC invocation is not used.
- In Sandbox mode sandbox_arm_ffa is used to test arm_ffa driver.
- Return:
- 0 on success. Otherwise, failure
@@ -1088,6 +1090,30 @@ ARM_SMCCC_FEATURE_DRIVER(arm_ffa) = { .driver_name = FFA_DRV_NAME, .is_supported = ffa_bus_is_supported, }; +#else +/* SANDBOX_FFA */
+/**
- ffa_bind() - The driver bind function
- @dev: the arm_ffa device
- When using sandbox tries to discover the emulated FF-A bus.
- Return:
- 0 on success.
- */
+static int ffa_bind(struct udevice *dev)
I don't think this is binding anything. How about ffa_get_dev() ?
+{
bool ret;
log_info("[FFA] binding the device\n");
ret = ffa_try_discovery();
if (ret)
return 0;
else
return -ENODEV;
+} +#endif
/**
- ffa_set_smc_conduit() - Set the SMC conduit
@@ -1101,7 +1127,12 @@ ARM_SMCCC_FEATURE_DRIVER(arm_ffa) = { */ static int ffa_set_smc_conduit(void) {
dscvry_info.invoke_ffa_fn = __arm_ffa_fn_smc;
+#if CONFIG_IS_ENABLED(SANDBOX_FFA)
dscvry_info.invoke_ffa_fn = sandbox_arm_ffa_smccc_smc;
log_info("[FFA] Using SMC emulation\n");
+#else
dscvry_info.invoke_ffa_fn = __arm_ffa_fn_smc;
+#endif
This needs to be reworked to go through the uclass to the correct driver. Basically you need an ARM driver and a sandbox driver (which attaches to the emulator).
There should not be #idefs in this sort of code...driver model should handle it. See other uclasses for examples.
log_info("[FFA] Conduit is SMC\n");
@@ -1246,4 +1277,7 @@ U_BOOT_DRIVER(arm_ffa) = { .remove = ffa_remove, .unbind = ffa_unbind, .ops = &ffa_ops, +#if CONFIG_IS_ENABLED(SANDBOX_FFA)
.bind = ffa_bind,
Can drop this
+#endif }; diff --git a/drivers/firmware/arm-ffa/sandbox.c b/drivers/firmware/arm-ffa/sandbox.c
sandbox_emul.c pehaps?
I am pretty sure this is an emulator
new file mode 100644 index 0000000000..84c2fc929f --- /dev/null +++ b/drivers/firmware/arm-ffa/sandbox.c @@ -0,0 +1,610 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
- Authors:
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include <common.h> +#include <dm.h> +#include <mapmem.h> +#include <string.h> +#include <asm/global_data.h> +#include <dm/device-internal.h> +#include <dm/lists.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> +#include "sandbox_arm_ffa_priv.h"
+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_SP2_ID, .exec_ctxt = 0x9587, .properties = 0x45325621 },
.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_SP3_ID, .exec_ctxt = 0x7687, .properties = 0x23325621 },
.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,
}
}
+};
+/* Driver functions */
+/**
- sandbox_ffa_version() - Emulated FFA_VERSION handler function
- @dev: the sandbox FF-A device
- @{a0-a7} , res: The SMC call arguments and return structure.
- This is the function that emulates FFA_VERSION FF-A function.
- Return:
- 0 on success. Otherwise, failure
- */
+SANDBOX_SMC_FFA_ABI(ffa_version)
Instead of the macro can you write this out in full? It defeats ctags, etc.
+{
struct sandbox_ffa_priv *priv = dev_get_priv(dev);
priv->fwk_version = FFA_VERSION_1_0;
res->a0 = priv->fwk_version;
Where is res defined?
/* x1-x7 MBZ */
memset(FFA_X1X7_MBZ_REG_START, 0, FFA_X1X7_MBZ_CNT * sizeof(unsigned long));
return 0;
+}
+/**
- sandbox_ffa_id_get() - Emulated FFA_ID_GET handler function
- @dev: the sandbox FF-A device
- @{a0-a7} , res: The SMC call arguments and return structure.
- This is the function that emulates FFA_ID_GET FF-A function.
s/This is the function that e/E/g
- Return:
- 0 on success. Otherwise, failure
- */
+SANDBOX_SMC_FFA_ABI(ffa_id_get) +{
struct sandbox_ffa_priv *priv = dev_get_priv(dev);
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(unsigned long));
return 0;
+}
+/**
- sandbox_ffa_features() - Emulated FFA_FEATURES handler function
- @dev: the sandbox FF-A device
- @{a0-a7} , res: The SMC call arguments and return structure.
- This is the function that emulates FFA_FEATURES FF-A function.
- Return:
- 0 on success. Otherwise, failure
- */
+SANDBOX_SMC_FFA_ABI(ffa_features) +{
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(unsigned long));
} else {
res->a0 = FFA_SMC_32(FFA_ERROR);
res->a2 = FFA_ERR_STAT_NOT_SUPPORTED;
/* x3-x7 MBZ */
memset(FFA_X3_MBZ_REG_START,
0, FFA_X3X7_MBZ_CNT * sizeof(unsigned long));
log_err("[FFA] [Sandbox] FF-A interface 0x%lx not implemented\n", pargs->a1);
return -ENOSYS
}
res->a1 = 0;
return 0;
+}
[..]
+SANDBOX_SMC_FFA_ABI(ffa_msg_send_direct_req) +{
u16 part_id;
struct sandbox_ffa_priv *priv = dev_get_priv(dev);
part_id = GET_DST_SP_ID(pargs->a1);
if ((GET_NS_PHYS_ENDPOINT_ID(pargs->a1) != priv->id) ||
drop extra brackets
!sandbox_ffa_sp_valid(dev, part_id) || pargs->a2) {
res->a0 = FFA_SMC_32(FFA_ERROR);
res->a1 = 0;
res->a2 = FFA_ERR_STAT_INVALID_PARAMETERS;
/* x3-x7 MBZ */
memset(FFA_X3_MBZ_REG_START,
0, FFA_X3X7_MBZ_CNT * sizeof(unsigned long));
return 0;
}
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 = 0xffffffffffffffff;
res->a4 = 0xffffffffffffffff;
res->a5 = 0xffffffffffffffff;
res->a6 = 0xffffffffffffffff;
res->a7 = 0xffffffffffffffff;
Would -1UL work?
return 0;
+}
[..]
+static int sandbox_ffa_probe(struct udevice *dev) +{
struct udevice *child_dev = NULL;
int ret;
ret = device_bind_driver(dev, FFA_DRV_NAME, FFA_DRV_NAME, &child_dev);
Is this binding the emulator? Add it to the DT instead.
If you put
.post_bind = dm_scan_fdt_dev()
in your uclass (like spi-uclass.c does) then it will binding child-node devices automatically. Then you can make your emulator a child node.
Hi Simon,
Thanks for the comments. I'm happy to address them.
I'd like to explain the following points:
1/ The FF-A core driver is the driver that interacts with secure world. The driver is implemented in drivers/firmware/arm-ffa/core.c
The FF-A sandbox driver is the driver used to emulate the FF-A side of the secure world and interacts with the core driver. The sandbox driver is implemented in drivers/firmware/arm-ffa/sandbox.c
If you prefer we call it FF-A sandbox emulator, please let me know. I'm calling it sandbox driver based on what the checkpatch script is suggesting.
2/ As we agreed with Rob, in the patchset v9 , FF-A is seen as an architecture feature and is discovered by software. The root device tree node is the PSCI device and the architecture features are children of the PSCI device.
When the PSCI driver is probed, it tries to discover the architecture features (including FF-A). When the FF-A discovery succeeds, PSCI creates the FF-A device as a child of PSCI device, and binds the FF-A core driver with the FF-A core device.
This is done here [1] using device_bind_driver().
=> dm tree
Class Index Probed Driver Name ----------------------------------------------------------- ... firmware 0 [ + ] psci |-- psci ffa 0 [ ] arm_ffa | `-- arm_ffa
Since PSCI is not supported in sandbox, we can not use PSCI driver to discover the emulated FF-A framework. In place of the PSCI driver, FF-A discovery and binding is done in the FF-A sandbox driver at probe level (same way as PSCI). The FF-A core device is bound and set as a child of the FF-A sandbox device. This garantees the same behaviour as the real use case.
Is it OK to keep it like done for the PSCI please ? ( using device_bind_driver() )
3/ FF-A core device and FF-A sandbox device both belong to UCLASS_FFA.
In a real use case, there is 1 FF-A device (arm_ffa). However, when using sandbox there are 2 devices:
FF-A core device (arm_ffa) FF-A sandbox device (sandbox_arm_ffa)
So, there is a good reason to use uclass_get_device_by_name() which garantees that we probe the right device. In sandbox mode, the sandbox FF-A device should be probed first since it is responsible of discovering and binding the FF-A core device. This is as close as possible to what PSCI driver does.
4/ In a real use case, we only need to use the PSCI device tree node as parent. In sandbox mode, we need to use the FF-A sandbox device tree node as parent. 5/ I'm happy to update the core driver operations as you suggested, thanks for the comments. 6/ I'm happy to rename the devices to arm-ffa and sandbox-arm-ffa
[1]: https://github.com/u-boot/u-boot/blob/master/drivers/firmware/psci.c#L158
Cheers Abdellatif
if (ret) {
pr_err("%s was not bound: %d, aborting\n", FFA_DRV_NAME, ret);
return -ENODEV;
}
dev_set_parent_plat(child_dev, dev_get_plat(dev));
return 0;
+}
+static const struct udevice_id sandbox_ffa_id[] = {
{ "sandbox,arm_ffa", 0 },
{ },
+};
+/* Declaring the sandbox_arm_ffa driver under UCLASS_FFA */ +U_BOOT_DRIVER(sandbox_arm_ffa) = {
.name = FFA_SANDBOX_DRV_NAME,
.of_match = sandbox_ffa_id,
.id = UCLASS_FFA,
.probe = sandbox_ffa_probe,
.priv_auto = sizeof(struct sandbox_ffa_priv),
+}; 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 0000000000..c35d80de16 --- /dev/null +++ b/drivers/firmware/arm-ffa/sandbox_arm_ffa_priv.h @@ -0,0 +1,129 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
- Authors:
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#ifndef __SANDBOX_ARM_FFA_PRV_H +#define __SANDBOX_ARM_FFA_PRV_H
+#include <sandbox_arm_ffa.h> +#include "arm_ffa_priv.h"
+/* This header is exclusively used by the Sandbox FF-A driver and sandbox tests */
+/* FF-A core driver name */ +#define FFA_SANDBOX_DRV_NAME "sandbox_arm_ffa"
+/* FF-A ABIs internal error codes (as defined by the spec) */
+#define FFA_ERR_STAT_NOT_SUPPORTED -1 +#define FFA_ERR_STAT_INVALID_PARAMETERS -2 +#define FFA_ERR_STAT_NO_MEMORY -3 +#define FFA_ERR_STAT_BUSY -4 +#define FFA_ERR_STAT_DENIED -6
It's a convention to put negative values in brackets just in case of operator-precedence issues. Or you could use an enum.
+/* 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 secure partitions emulated by the FF-A sandbox driver */ +#define SANDBOX_PARTITIONS_CNT (4)
+/* Binary data of services UUIDs emulated by the FF-A sandbox driver */
+/* 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: the consumer)
- @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 as set by the FF-A core driver
- Data structure hosting 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_priv - the driver private data structure
- @dev: The arm_ffa device under u-boot driver model
- @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
- @conduit: The selected conduit
- The driver data structure hosting all the emulated secure world data.
- */
+struct sandbox_ffa_priv {
struct udevice *dev;
u32 fwk_version;
u16 id;
struct ffa_partitions partitions;
struct ffa_rxtxpair pair;
struct ffa_rxtxpair_info pair_info;
+};
+#define SANDBOX_SMC_FFA_ABI(ffabi) static int sandbox_##ffabi(struct udevice *dev, \
ffa_value_t *pargs, ffa_value_t *res)
drop that
[..]
Regards, Simon

Hi Simon,
Hi Abdellatif,
On Fri, 10 Mar 2023 at 06:10, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
Provide a Sandbox driver to emulate the FF-A ABIs
The emulated ABIs are those supported by the FF-A core driver and according to FF-A specification v1.0.
The Sandbox driver provides operations allowing the test application to read the status of all the inspected ABIs and perform functional tests based on that.
sandbox driver supports only 64-bit direct messaging.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v9: align FF-A sandbox driver with FF-A discovery through DM
v8: update ffa_bus_prvdata_get() to return a pointer rather than a pointer address
v7: state that sandbox driver supports only 64-bit direct messaging
v4: align sandbox driver with the new FF-A driver interfaces and new way of error handling
v1: introduce the sandbox driver
MAINTAINERS | 1 + arch/sandbox/dts/sandbox.dtsi | 4 + arch/sandbox/dts/test.dts | 4 + configs/sandbox64_defconfig | 2 + configs/sandbox_defconfig | 2 + doc/arch/arm64.ffa.rst | 4 + doc/arch/sandbox/sandbox.rst | 1 + drivers/firmware/arm-ffa/Kconfig | 11 +- drivers/firmware/arm-ffa/Makefile | 1 + drivers/firmware/arm-ffa/core.c | 36 +- drivers/firmware/arm-ffa/sandbox.c | 610 ++++++++++++++++++ .../firmware/arm-ffa/sandbox_arm_ffa_priv.h | 129 ++++ include/arm_ffa.h | 5 +- include/sandbox_arm_ffa.h | 124 ++++ 14 files changed, 928 insertions(+), 6 deletions(-) create mode 100644 drivers/firmware/arm-ffa/sandbox.c create mode 100644 drivers/firmware/arm-ffa/sandbox_arm_ffa_priv.h create mode 100644 include/sandbox_arm_ffa.h
Could you use 80 columns where possible? There seem to be a lot of things that extend beyond that without much of a reason.
Also you can use 'ulong' instead of 'unsigned long'. It is less verbose and a U-Boot standard.
diff --git a/MAINTAINERS b/MAINTAINERS index 18e9c2ce99..2b9d33e964 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -274,6 +274,7 @@ F: doc/arch/arm64.ffa.rst F: doc/usage/cmd/armffa.rst F: drivers/firmware/arm-ffa/ F: include/arm_ffa.h +F: include/sandbox_arm_ffa.h
ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de diff --git a/arch/sandbox/dts/sandbox.dtsi b/arch/sandbox/dts/sandbox.dtsi index 30a305c4d2..059c273277 100644 --- a/arch/sandbox/dts/sandbox.dtsi +++ b/arch/sandbox/dts/sandbox.dtsi @@ -445,6 +445,10 @@ thermal { compatible = "sandbox,thermal"; };
sandbox_arm_ffa {
compatible = "sandbox,arm_ffa";
};
};
&cros_ec { diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index d72d7a567a..11dc6ed0d9 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -1802,6 +1802,10 @@ compatible = "u-boot,fwu-mdata-gpt"; fwu-mdata-store = <&mmc0>; };
sandbox_arm_ffa {
compatible = "sandbox,arm_ffa";
};
I see that you have this, so the driver should bind automatically.
Is the problem that you are trying to bind the sandbox emulator? Again, you can actually add that to the DT. See how this works for i2c, SPI, PCI, for example.
};
#include "sandbox_pmic.dtsi" diff --git a/configs/sandbox64_defconfig b/configs/sandbox64_defconfig index ccbc18aad0..35d4676cf7 100644 --- a/configs/sandbox64_defconfig +++ b/configs/sandbox64_defconfig @@ -259,3 +259,5 @@ CONFIG_FWU_MULTI_BANK_UPDATE=y CONFIG_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y +CONFIG_ARM_FFA_TRANSPORT=y +CONFIG_SANDBOX_FFA=y diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index a0fbdad20a..8aab8dda31 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -336,3 +336,5 @@ CONFIG_TEST_FDTDEC=y CONFIG_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y +CONFIG_ARM_FFA_TRANSPORT=y +CONFIG_SANDBOX_FFA=y \ No newline at end of file diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst index 8fad9ef3d0..555ee9a6ae 100644 --- a/doc/arch/arm64.ffa.rst +++ b/doc/arch/arm64.ffa.rst @@ -64,6 +64,10 @@ CONFIG_ARM_FFA_TRANSPORT Enables the FF-A bus driver. Turn this on if you want to use FF-A communication.
+CONFIG_SANDBOX_FFA
- Enables FF-A Sandbox driver. This emulates the FF-A ABIs handling under
- Sandbox and provides functional tests for FF-A.
OK that is why I am confused. Please don't call this a driver. It is an emulator. When you have an emulator and a driver for the same thing it gets confusing.
FF-A ABIs under the hood
diff --git a/doc/arch/sandbox/sandbox.rst b/doc/arch/sandbox/sandbox.rst index cd7f8a2cb0..c5df372e00 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 2cfd7ef5fc..b5430eb6f4 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 imply CMD_ARMFFA select LIB_UUID select DEVRES
@@ -32,3 +32,8 @@ config ARM_FFA_TRANSPORT
For more details about the FF-A driver, please refer to doc/arch/arm64.ffa.rst
+config SANDBOX_FFA
bool "FF-A Sandbox driver"
depends on ARM_FFA_TRANSPORT && SANDBOX
help
This emulates the FF-A handling under Sandbox and allows to test the FF-A driver
diff --git a/drivers/firmware/arm-ffa/Makefile b/drivers/firmware/arm-ffa/Makefile index c8d83b4bc9..d22c1ba609 100644 --- a/drivers/firmware/arm-ffa/Makefile +++ b/drivers/firmware/arm-ffa/Makefile @@ -6,3 +6,4 @@ # Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
obj-y += arm-ffa-uclass.o core.o +obj-$(CONFIG_SANDBOX_FFA) += sandbox.o diff --git a/drivers/firmware/arm-ffa/core.c b/drivers/firmware/arm-ffa/core.c index 2210f5343c..0d2e6ff0d4 100644 --- a/drivers/firmware/arm-ffa/core.c +++ b/drivers/firmware/arm-ffa/core.c @@ -1042,6 +1042,7 @@ bool ffa_try_discovery(void) return true; }
+#if !CONFIG_IS_ENABLED(SANDBOX_FFA)
We should not need #ifdefs here. The sandbox driver is just another driver, just like there is the ARM driver. We select the correct one using the devicetree and everything just works.
The FF-A core driver (core.c) has Arm specific code that is not available when running sandbox. So we still need to use these ifdefs in core.c to prevent the Arm specific code from building under sandbox. For example these are Arm specific:
arm_smccc_1_2_smc() struct arm_smccc_res ARM_SMCCC_FEATURE_DRIVER The SMC conduit. In sandbox mode the core driver should use the emulated conduit implemented by sandbox_arm_ffa_smccc_smc()
/**
- __arm_ffa_fn_smc() - SMC wrapper
- @args: FF-A ABI arguments to be copied to Xn registers
@@ -1069,6 +1070,7 @@ void __arm_ffa_fn_smc(ffa_value_t args, ffa_value_t *res)
- The FF-A driver supports the SMCCCv1.2 extended input/output registers.
- So, the legacy SMC invocation is not used.
- In Sandbox mode sandbox_arm_ffa is used to test arm_ffa driver.
- Return:
- 0 on success. Otherwise, failure
@@ -1088,6 +1090,30 @@ ARM_SMCCC_FEATURE_DRIVER(arm_ffa) = { .driver_name = FFA_DRV_NAME, .is_supported = ffa_bus_is_supported, }; +#else +/* SANDBOX_FFA */
+/**
- ffa_bind() - The driver bind function
- @dev: the arm_ffa device
- When using sandbox tries to discover the emulated FF-A bus.
- Return:
- 0 on success.
- */
+static int ffa_bind(struct udevice *dev)
I don't think this is binding anything. How about ffa_get_dev() ?
In sandbox mode, when the FF-A sandbox driver (emulates the secure world) is probed, it calls device_bind_driver() to bind the FF-A core driver (a similar approach to the PSCI way). ffa_bind() is needed in sandbox mode so the FF-A discovery is setup properly.
In an Arm platform, the PSCI driver calls the FF-A discovery callback ffa_bus_is_supported() which tries to discover FF-A. When discovery is successful, PSCI driver binds the FF-A core device by calling device_bind_driver()
The core driver needs to work on both Arm and sandbox. The sandbox FF-A driver plays the role of a secure world emulator with which the FF-A core driver exchanges data.
+{
bool ret;
log_info("[FFA] binding the device\n");
ret = ffa_try_discovery();
if (ret)
return 0;
else
return -ENODEV;
+} +#endif
/**
- ffa_set_smc_conduit() - Set the SMC conduit
@@ -1101,7 +1127,12 @@ ARM_SMCCC_FEATURE_DRIVER(arm_ffa) = { */ static int ffa_set_smc_conduit(void) {
dscvry_info.invoke_ffa_fn = __arm_ffa_fn_smc;
+#if CONFIG_IS_ENABLED(SANDBOX_FFA)
dscvry_info.invoke_ffa_fn = sandbox_arm_ffa_smccc_smc;
log_info("[FFA] Using SMC emulation\n");
+#else
dscvry_info.invoke_ffa_fn = __arm_ffa_fn_smc;
+#endif
This needs to be reworked to go through the uclass to the correct driver. Basically you need an ARM driver and a sandbox driver (which attaches to the emulator).
There should not be #idefs in this sort of code...driver model should handle it. See other uclasses for examples.
In sandbox we are testing the FF-A core driver. The core driver has some Arm specific code which explains why we need the ifdefs. In Arm the SMC conduit is implemented by __arm_ffa_fn_smc() which ends up calling Arm instructions. When building the core driver with sandbox, the SMC conduit is set to the emulated version of the SMC calls implemented by sandbox_arm_ffa_smccc_smc()
I'm happy to rename the FF-A sandbox driver to the FF-A sandbox emulator if that clears the doubts. However, the emulator is still a driver and bound to a sandbox device (the parent of the core).
Cheers, Abdellatif
log_info("[FFA] Conduit is SMC\n");
@@ -1246,4 +1277,7 @@ U_BOOT_DRIVER(arm_ffa) = { .remove = ffa_remove, .unbind = ffa_unbind, .ops = &ffa_ops, +#if CONFIG_IS_ENABLED(SANDBOX_FFA)
.bind = ffa_bind,
Can drop this
+#endif }; diff --git a/drivers/firmware/arm-ffa/sandbox.c b/drivers/firmware/arm-ffa/sandbox.c
sandbox_emul.c pehaps?
I am pretty sure this is an emulator
new file mode 100644 index 0000000000..84c2fc929f --- /dev/null +++ b/drivers/firmware/arm-ffa/sandbox.c @@ -0,0 +1,610 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
- Authors:
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include <common.h> +#include <dm.h> +#include <mapmem.h> +#include <string.h> +#include <asm/global_data.h> +#include <dm/device-internal.h> +#include <dm/lists.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> +#include "sandbox_arm_ffa_priv.h"
+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_SP2_ID, .exec_ctxt = 0x9587, .properties = 0x45325621 },
.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_SP3_ID, .exec_ctxt = 0x7687, .properties = 0x23325621 },
.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,
}
}
+};
+/* Driver functions */
+/**
- sandbox_ffa_version() - Emulated FFA_VERSION handler function
- @dev: the sandbox FF-A device
- @{a0-a7} , res: The SMC call arguments and return structure.
- This is the function that emulates FFA_VERSION FF-A function.
- Return:
- 0 on success. Otherwise, failure
- */
+SANDBOX_SMC_FFA_ABI(ffa_version)
Instead of the macro can you write this out in full? It defeats ctags, etc.
+{
struct sandbox_ffa_priv *priv = dev_get_priv(dev);
priv->fwk_version = FFA_VERSION_1_0;
res->a0 = priv->fwk_version;
Where is res defined?
/* x1-x7 MBZ */
memset(FFA_X1X7_MBZ_REG_START, 0, FFA_X1X7_MBZ_CNT * sizeof(unsigned long));
return 0;
+}
+/**
- sandbox_ffa_id_get() - Emulated FFA_ID_GET handler function
- @dev: the sandbox FF-A device
- @{a0-a7} , res: The SMC call arguments and return structure.
- This is the function that emulates FFA_ID_GET FF-A function.
s/This is the function that e/E/g
- Return:
- 0 on success. Otherwise, failure
- */
+SANDBOX_SMC_FFA_ABI(ffa_id_get) +{
struct sandbox_ffa_priv *priv = dev_get_priv(dev);
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(unsigned long));
return 0;
+}
+/**
- sandbox_ffa_features() - Emulated FFA_FEATURES handler function
- @dev: the sandbox FF-A device
- @{a0-a7} , res: The SMC call arguments and return structure.
- This is the function that emulates FFA_FEATURES FF-A function.
- Return:
- 0 on success. Otherwise, failure
- */
+SANDBOX_SMC_FFA_ABI(ffa_features) +{
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(unsigned long));
} else {
res->a0 = FFA_SMC_32(FFA_ERROR);
res->a2 = FFA_ERR_STAT_NOT_SUPPORTED;
/* x3-x7 MBZ */
memset(FFA_X3_MBZ_REG_START,
0, FFA_X3X7_MBZ_CNT * sizeof(unsigned long));
log_err("[FFA] [Sandbox] FF-A interface 0x%lx not implemented\n", pargs->a1);
return -ENOSYS
}
res->a1 = 0;
return 0;
+}
[..]
+SANDBOX_SMC_FFA_ABI(ffa_msg_send_direct_req) +{
u16 part_id;
struct sandbox_ffa_priv *priv = dev_get_priv(dev);
part_id = GET_DST_SP_ID(pargs->a1);
if ((GET_NS_PHYS_ENDPOINT_ID(pargs->a1) != priv->id) ||
drop extra brackets
!sandbox_ffa_sp_valid(dev, part_id) || pargs->a2) {
res->a0 = FFA_SMC_32(FFA_ERROR);
res->a1 = 0;
res->a2 = FFA_ERR_STAT_INVALID_PARAMETERS;
/* x3-x7 MBZ */
memset(FFA_X3_MBZ_REG_START,
0, FFA_X3X7_MBZ_CNT * sizeof(unsigned long));
return 0;
}
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 = 0xffffffffffffffff;
res->a4 = 0xffffffffffffffff;
res->a5 = 0xffffffffffffffff;
res->a6 = 0xffffffffffffffff;
res->a7 = 0xffffffffffffffff;
Would -1UL work?
return 0;
+}
[..]
+static int sandbox_ffa_probe(struct udevice *dev) +{
struct udevice *child_dev = NULL;
int ret;
ret = device_bind_driver(dev, FFA_DRV_NAME, FFA_DRV_NAME, &child_dev);
Is this binding the emulator? Add it to the DT instead.
If you put
.post_bind = dm_scan_fdt_dev()
in your uclass (like spi-uclass.c does) then it will binding child-node devices automatically. Then you can make your emulator a child node.
if (ret) {
pr_err("%s was not bound: %d, aborting\n", FFA_DRV_NAME, ret);
return -ENODEV;
}
dev_set_parent_plat(child_dev, dev_get_plat(dev));
return 0;
+}
+static const struct udevice_id sandbox_ffa_id[] = {
{ "sandbox,arm_ffa", 0 },
{ },
+};
+/* Declaring the sandbox_arm_ffa driver under UCLASS_FFA */ +U_BOOT_DRIVER(sandbox_arm_ffa) = {
.name = FFA_SANDBOX_DRV_NAME,
.of_match = sandbox_ffa_id,
.id = UCLASS_FFA,
.probe = sandbox_ffa_probe,
.priv_auto = sizeof(struct sandbox_ffa_priv),
+}; 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 0000000000..c35d80de16 --- /dev/null +++ b/drivers/firmware/arm-ffa/sandbox_arm_ffa_priv.h @@ -0,0 +1,129 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
- Authors:
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#ifndef __SANDBOX_ARM_FFA_PRV_H +#define __SANDBOX_ARM_FFA_PRV_H
+#include <sandbox_arm_ffa.h> +#include "arm_ffa_priv.h"
+/* This header is exclusively used by the Sandbox FF-A driver and sandbox tests */
+/* FF-A core driver name */ +#define FFA_SANDBOX_DRV_NAME "sandbox_arm_ffa"
+/* FF-A ABIs internal error codes (as defined by the spec) */
+#define FFA_ERR_STAT_NOT_SUPPORTED -1 +#define FFA_ERR_STAT_INVALID_PARAMETERS -2 +#define FFA_ERR_STAT_NO_MEMORY -3 +#define FFA_ERR_STAT_BUSY -4 +#define FFA_ERR_STAT_DENIED -6
It's a convention to put negative values in brackets just in case of operator-precedence issues. Or you could use an enum.
+/* 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 secure partitions emulated by the FF-A sandbox driver */ +#define SANDBOX_PARTITIONS_CNT (4)
+/* Binary data of services UUIDs emulated by the FF-A sandbox driver */
+/* 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: the consumer)
- @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 as set by the FF-A core driver
- Data structure hosting 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_priv - the driver private data structure
- @dev: The arm_ffa device under u-boot driver model
- @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
- @conduit: The selected conduit
- The driver data structure hosting all the emulated secure world data.
- */
+struct sandbox_ffa_priv {
struct udevice *dev;
u32 fwk_version;
u16 id;
struct ffa_partitions partitions;
struct ffa_rxtxpair pair;
struct ffa_rxtxpair_info pair_info;
+};
+#define SANDBOX_SMC_FFA_ABI(ffabi) static int sandbox_##ffabi(struct udevice *dev, \
ffa_value_t *pargs, ffa_value_t *res)
drop that
[..]
Regards, Simon

Hi Abdellatif,
On Tue, 14 Mar 2023 at 11:59, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
Hi Simon,
Hi Abdellatif,
On Fri, 10 Mar 2023 at 06:10, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
Provide a Sandbox driver to emulate the FF-A ABIs
The emulated ABIs are those supported by the FF-A core driver and according to FF-A specification v1.0.
The Sandbox driver provides operations allowing the test application to read the status of all the inspected ABIs and perform functional tests based on that.
sandbox driver supports only 64-bit direct messaging.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v9: align FF-A sandbox driver with FF-A discovery through DM
v8: update ffa_bus_prvdata_get() to return a pointer rather than a pointer address
v7: state that sandbox driver supports only 64-bit direct messaging
v4: align sandbox driver with the new FF-A driver interfaces and new way of error handling
v1: introduce the sandbox driver
MAINTAINERS | 1 + arch/sandbox/dts/sandbox.dtsi | 4 + arch/sandbox/dts/test.dts | 4 + configs/sandbox64_defconfig | 2 + configs/sandbox_defconfig | 2 + doc/arch/arm64.ffa.rst | 4 + doc/arch/sandbox/sandbox.rst | 1 + drivers/firmware/arm-ffa/Kconfig | 11 +- drivers/firmware/arm-ffa/Makefile | 1 + drivers/firmware/arm-ffa/core.c | 36 +- drivers/firmware/arm-ffa/sandbox.c | 610 ++++++++++++++++++ .../firmware/arm-ffa/sandbox_arm_ffa_priv.h | 129 ++++ include/arm_ffa.h | 5 +- include/sandbox_arm_ffa.h | 124 ++++ 14 files changed, 928 insertions(+), 6 deletions(-) create mode 100644 drivers/firmware/arm-ffa/sandbox.c create mode 100644 drivers/firmware/arm-ffa/sandbox_arm_ffa_priv.h create mode 100644 include/sandbox_arm_ffa.h
Could you use 80 columns where possible? There seem to be a lot of things that extend beyond that without much of a reason.
Also you can use 'ulong' instead of 'unsigned long'. It is less verbose and a U-Boot standard.
diff --git a/MAINTAINERS b/MAINTAINERS index 18e9c2ce99..2b9d33e964 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -274,6 +274,7 @@ F: doc/arch/arm64.ffa.rst F: doc/usage/cmd/armffa.rst F: drivers/firmware/arm-ffa/ F: include/arm_ffa.h +F: include/sandbox_arm_ffa.h
ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de diff --git a/arch/sandbox/dts/sandbox.dtsi b/arch/sandbox/dts/sandbox.dtsi index 30a305c4d2..059c273277 100644 --- a/arch/sandbox/dts/sandbox.dtsi +++ b/arch/sandbox/dts/sandbox.dtsi @@ -445,6 +445,10 @@ thermal { compatible = "sandbox,thermal"; };
sandbox_arm_ffa {
compatible = "sandbox,arm_ffa";
};
};
&cros_ec { diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index d72d7a567a..11dc6ed0d9 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -1802,6 +1802,10 @@ compatible = "u-boot,fwu-mdata-gpt"; fwu-mdata-store = <&mmc0>; };
sandbox_arm_ffa {
compatible = "sandbox,arm_ffa";
};
I see that you have this, so the driver should bind automatically.
Is the problem that you are trying to bind the sandbox emulator? Again, you can actually add that to the DT. See how this works for i2c, SPI, PCI, for example.
};
#include "sandbox_pmic.dtsi" diff --git a/configs/sandbox64_defconfig b/configs/sandbox64_defconfig index ccbc18aad0..35d4676cf7 100644 --- a/configs/sandbox64_defconfig +++ b/configs/sandbox64_defconfig @@ -259,3 +259,5 @@ CONFIG_FWU_MULTI_BANK_UPDATE=y CONFIG_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y +CONFIG_ARM_FFA_TRANSPORT=y +CONFIG_SANDBOX_FFA=y diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index a0fbdad20a..8aab8dda31 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -336,3 +336,5 @@ CONFIG_TEST_FDTDEC=y CONFIG_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y +CONFIG_ARM_FFA_TRANSPORT=y +CONFIG_SANDBOX_FFA=y \ No newline at end of file diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst index 8fad9ef3d0..555ee9a6ae 100644 --- a/doc/arch/arm64.ffa.rst +++ b/doc/arch/arm64.ffa.rst @@ -64,6 +64,10 @@ CONFIG_ARM_FFA_TRANSPORT Enables the FF-A bus driver. Turn this on if you want to use FF-A communication.
+CONFIG_SANDBOX_FFA
- Enables FF-A Sandbox driver. This emulates the FF-A ABIs handling under
- Sandbox and provides functional tests for FF-A.
OK that is why I am confused. Please don't call this a driver. It is an emulator. When you have an emulator and a driver for the same thing it gets confusing.
FF-A ABIs under the hood
diff --git a/doc/arch/sandbox/sandbox.rst b/doc/arch/sandbox/sandbox.rst index cd7f8a2cb0..c5df372e00 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 2cfd7ef5fc..b5430eb6f4 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 imply CMD_ARMFFA select LIB_UUID select DEVRES
@@ -32,3 +32,8 @@ config ARM_FFA_TRANSPORT
For more details about the FF-A driver, please refer to doc/arch/arm64.ffa.rst
+config SANDBOX_FFA
bool "FF-A Sandbox driver"
depends on ARM_FFA_TRANSPORT && SANDBOX
help
This emulates the FF-A handling under Sandbox and allows to test the FF-A driver
diff --git a/drivers/firmware/arm-ffa/Makefile b/drivers/firmware/arm-ffa/Makefile index c8d83b4bc9..d22c1ba609 100644 --- a/drivers/firmware/arm-ffa/Makefile +++ b/drivers/firmware/arm-ffa/Makefile @@ -6,3 +6,4 @@ # Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
obj-y += arm-ffa-uclass.o core.o +obj-$(CONFIG_SANDBOX_FFA) += sandbox.o diff --git a/drivers/firmware/arm-ffa/core.c b/drivers/firmware/arm-ffa/core.c index 2210f5343c..0d2e6ff0d4 100644 --- a/drivers/firmware/arm-ffa/core.c +++ b/drivers/firmware/arm-ffa/core.c @@ -1042,6 +1042,7 @@ bool ffa_try_discovery(void) return true; }
+#if !CONFIG_IS_ENABLED(SANDBOX_FFA)
We should not need #ifdefs here. The sandbox driver is just another driver, just like there is the ARM driver. We select the correct one using the devicetree and everything just works.
The FF-A core driver (core.c) has Arm specific code that is not available when running sandbox. So we still need to use these ifdefs in core.c to prevent the Arm specific code from building under sandbox. For example these are Arm specific:
arm_smccc_1_2_smc() struct arm_smccc_res ARM_SMCCC_FEATURE_DRIVER The SMC conduit. In sandbox mode the core driver should use the emulated conduit implemented by sandbox_arm_ffa_smccc_smc()
/**
- __arm_ffa_fn_smc() - SMC wrapper
- @args: FF-A ABI arguments to be copied to Xn registers
@@ -1069,6 +1070,7 @@ void __arm_ffa_fn_smc(ffa_value_t args, ffa_value_t *res)
- The FF-A driver supports the SMCCCv1.2 extended input/output registers.
- So, the legacy SMC invocation is not used.
- In Sandbox mode sandbox_arm_ffa is used to test arm_ffa driver.
- Return:
- 0 on success. Otherwise, failure
@@ -1088,6 +1090,30 @@ ARM_SMCCC_FEATURE_DRIVER(arm_ffa) = { .driver_name = FFA_DRV_NAME, .is_supported = ffa_bus_is_supported, }; +#else +/* SANDBOX_FFA */
+/**
- ffa_bind() - The driver bind function
- @dev: the arm_ffa device
- When using sandbox tries to discover the emulated FF-A bus.
- Return:
- 0 on success.
- */
+static int ffa_bind(struct udevice *dev)
I don't think this is binding anything. How about ffa_get_dev() ?
In sandbox mode, when the FF-A sandbox driver (emulates the secure world) is probed, it calls device_bind_driver() to bind the FF-A core driver (a similar approach to the PSCI way). ffa_bind() is needed in sandbox mode so the FF-A discovery is setup properly.
In an Arm platform, the PSCI driver calls the FF-A discovery callback ffa_bus_is_supported() which tries to discover FF-A. When discovery is successful, PSCI driver binds the FF-A core device by calling device_bind_driver()
The core driver needs to work on both Arm and sandbox. The sandbox FF-A driver plays the role of a secure world emulator with which the FF-A core driver exchanges data.
I think there is some sort of fundamental misunderstanding of driver model. If you put the methods in the uclass then you won't need any #ifdefs. What am I missing?
Yes, the emulator should be called an emulator, since it is confusing if you call it a driver.
+{
bool ret;
log_info("[FFA] binding the device\n");
ret = ffa_try_discovery();
if (ret)
return 0;
else
return -ENODEV;
+} +#endif
/**
- ffa_set_smc_conduit() - Set the SMC conduit
@@ -1101,7 +1127,12 @@ ARM_SMCCC_FEATURE_DRIVER(arm_ffa) = { */ static int ffa_set_smc_conduit(void) {
dscvry_info.invoke_ffa_fn = __arm_ffa_fn_smc;
+#if CONFIG_IS_ENABLED(SANDBOX_FFA)
dscvry_info.invoke_ffa_fn = sandbox_arm_ffa_smccc_smc;
log_info("[FFA] Using SMC emulation\n");
+#else
dscvry_info.invoke_ffa_fn = __arm_ffa_fn_smc;
+#endif
This needs to be reworked to go through the uclass to the correct driver. Basically you need an ARM driver and a sandbox driver (which attaches to the emulator).
There should not be #idefs in this sort of code...driver model should handle it. See other uclasses for examples.
In sandbox we are testing the FF-A core driver. The core driver has some Arm specific code which explains why we need the ifdefs. In Arm the SMC conduit is implemented by __arm_ffa_fn_smc() which ends up calling Arm instructions. When building the core driver with sandbox, the SMC conduit is set to the emulated version of the SMC calls implemented by sandbox_arm_ffa_smccc_smc()
I'm happy to rename the FF-A sandbox driver to the FF-A sandbox emulator if that clears the doubts. However, the emulator is still a driver and bound to a sandbox device (the parent of the core).
Cheers, Abdellatif
log_info("[FFA] Conduit is SMC\n");
@@ -1246,4 +1277,7 @@ U_BOOT_DRIVER(arm_ffa) = { .remove = ffa_remove, .unbind = ffa_unbind, .ops = &ffa_ops, +#if CONFIG_IS_ENABLED(SANDBOX_FFA)
.bind = ffa_bind,
Can drop this
+#endif }; diff --git a/drivers/firmware/arm-ffa/sandbox.c b/drivers/firmware/arm-ffa/sandbox.c
sandbox_emul.c pehaps?
I am pretty sure this is an emulator
new file mode 100644 index 0000000000..84c2fc929f --- /dev/null +++ b/drivers/firmware/arm-ffa/sandbox.c @@ -0,0 +1,610 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
- Authors:
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include <common.h> +#include <dm.h> +#include <mapmem.h> +#include <string.h> +#include <asm/global_data.h> +#include <dm/device-internal.h> +#include <dm/lists.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> +#include "sandbox_arm_ffa_priv.h"
+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_SP2_ID, .exec_ctxt = 0x9587, .properties = 0x45325621 },
.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_SP3_ID, .exec_ctxt = 0x7687, .properties = 0x23325621 },
.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,
}
}
+};
+/* Driver functions */
+/**
- sandbox_ffa_version() - Emulated FFA_VERSION handler function
- @dev: the sandbox FF-A device
- @{a0-a7} , res: The SMC call arguments and return structure.
- This is the function that emulates FFA_VERSION FF-A function.
- Return:
- 0 on success. Otherwise, failure
- */
+SANDBOX_SMC_FFA_ABI(ffa_version)
Instead of the macro can you write this out in full? It defeats ctags, etc.
+{
struct sandbox_ffa_priv *priv = dev_get_priv(dev);
priv->fwk_version = FFA_VERSION_1_0;
res->a0 = priv->fwk_version;
Where is res defined?
/* x1-x7 MBZ */
memset(FFA_X1X7_MBZ_REG_START, 0, FFA_X1X7_MBZ_CNT * sizeof(unsigned long));
return 0;
+}
+/**
- sandbox_ffa_id_get() - Emulated FFA_ID_GET handler function
- @dev: the sandbox FF-A device
- @{a0-a7} , res: The SMC call arguments and return structure.
- This is the function that emulates FFA_ID_GET FF-A function.
s/This is the function that e/E/g
- Return:
- 0 on success. Otherwise, failure
- */
+SANDBOX_SMC_FFA_ABI(ffa_id_get) +{
struct sandbox_ffa_priv *priv = dev_get_priv(dev);
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(unsigned long));
return 0;
+}
+/**
- sandbox_ffa_features() - Emulated FFA_FEATURES handler function
- @dev: the sandbox FF-A device
- @{a0-a7} , res: The SMC call arguments and return structure.
- This is the function that emulates FFA_FEATURES FF-A function.
- Return:
- 0 on success. Otherwise, failure
- */
+SANDBOX_SMC_FFA_ABI(ffa_features) +{
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(unsigned long));
} else {
res->a0 = FFA_SMC_32(FFA_ERROR);
res->a2 = FFA_ERR_STAT_NOT_SUPPORTED;
/* x3-x7 MBZ */
memset(FFA_X3_MBZ_REG_START,
0, FFA_X3X7_MBZ_CNT * sizeof(unsigned long));
log_err("[FFA] [Sandbox] FF-A interface 0x%lx not implemented\n", pargs->a1);
return -ENOSYS
}
res->a1 = 0;
return 0;
+}
[..]
+SANDBOX_SMC_FFA_ABI(ffa_msg_send_direct_req) +{
u16 part_id;
struct sandbox_ffa_priv *priv = dev_get_priv(dev);
part_id = GET_DST_SP_ID(pargs->a1);
if ((GET_NS_PHYS_ENDPOINT_ID(pargs->a1) != priv->id) ||
drop extra brackets
!sandbox_ffa_sp_valid(dev, part_id) || pargs->a2) {
res->a0 = FFA_SMC_32(FFA_ERROR);
res->a1 = 0;
res->a2 = FFA_ERR_STAT_INVALID_PARAMETERS;
/* x3-x7 MBZ */
memset(FFA_X3_MBZ_REG_START,
0, FFA_X3X7_MBZ_CNT * sizeof(unsigned long));
return 0;
}
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 = 0xffffffffffffffff;
res->a4 = 0xffffffffffffffff;
res->a5 = 0xffffffffffffffff;
res->a6 = 0xffffffffffffffff;
res->a7 = 0xffffffffffffffff;
Would -1UL work?
return 0;
+}
[..]
+static int sandbox_ffa_probe(struct udevice *dev) +{
struct udevice *child_dev = NULL;
int ret;
ret = device_bind_driver(dev, FFA_DRV_NAME, FFA_DRV_NAME, &child_dev);
Is this binding the emulator? Add it to the DT instead.
If you put
.post_bind = dm_scan_fdt_dev()
in your uclass (like spi-uclass.c does) then it will binding child-node devices automatically. Then you can make your emulator a child node.
if (ret) {
pr_err("%s was not bound: %d, aborting\n", FFA_DRV_NAME, ret);
return -ENODEV;
}
dev_set_parent_plat(child_dev, dev_get_plat(dev));
return 0;
+}
+static const struct udevice_id sandbox_ffa_id[] = {
{ "sandbox,arm_ffa", 0 },
{ },
+};
+/* Declaring the sandbox_arm_ffa driver under UCLASS_FFA */ +U_BOOT_DRIVER(sandbox_arm_ffa) = {
.name = FFA_SANDBOX_DRV_NAME,
.of_match = sandbox_ffa_id,
.id = UCLASS_FFA,
.probe = sandbox_ffa_probe,
.priv_auto = sizeof(struct sandbox_ffa_priv),
+}; 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 0000000000..c35d80de16 --- /dev/null +++ b/drivers/firmware/arm-ffa/sandbox_arm_ffa_priv.h @@ -0,0 +1,129 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
- Authors:
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#ifndef __SANDBOX_ARM_FFA_PRV_H +#define __SANDBOX_ARM_FFA_PRV_H
+#include <sandbox_arm_ffa.h> +#include "arm_ffa_priv.h"
+/* This header is exclusively used by the Sandbox FF-A driver and sandbox tests */
+/* FF-A core driver name */ +#define FFA_SANDBOX_DRV_NAME "sandbox_arm_ffa"
+/* FF-A ABIs internal error codes (as defined by the spec) */
+#define FFA_ERR_STAT_NOT_SUPPORTED -1 +#define FFA_ERR_STAT_INVALID_PARAMETERS -2 +#define FFA_ERR_STAT_NO_MEMORY -3 +#define FFA_ERR_STAT_BUSY -4 +#define FFA_ERR_STAT_DENIED -6
It's a convention to put negative values in brackets just in case of operator-precedence issues. Or you could use an enum.
+/* 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 secure partitions emulated by the FF-A sandbox driver */ +#define SANDBOX_PARTITIONS_CNT (4)
+/* Binary data of services UUIDs emulated by the FF-A sandbox driver */
+/* 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: the consumer)
- @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 as set by the FF-A core driver
- Data structure hosting 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_priv - the driver private data structure
- @dev: The arm_ffa device under u-boot driver model
- @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
- @conduit: The selected conduit
- The driver data structure hosting all the emulated secure world data.
- */
+struct sandbox_ffa_priv {
struct udevice *dev;
u32 fwk_version;
u16 id;
struct ffa_partitions partitions;
struct ffa_rxtxpair pair;
struct ffa_rxtxpair_info pair_info;
+};
+#define SANDBOX_SMC_FFA_ABI(ffabi) static int sandbox_##ffabi(struct udevice *dev, \
ffa_value_t *pargs, ffa_value_t *res)
drop that
[..]
Regards, Simon

Add functional test cases for the FF-A core driver
These tests rely on the FF-A Sandbox driver which helps in inspecting the FF-A core driver.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
--- Changelog: ===============
v9: align FF-A sandbox tests with FF-A discovery through DM
v8:
* update partition_info_get() second argument to be an SP count * pass NULL device pointer to the FF-A bus discovery and operations
v7: set the tests to use 64-bit direct messaging
v4: align sandbox tests with the new FF-A driver interfaces and new way of error handling
v1: introduce sandbox tests
MAINTAINERS | 1 + test/dm/Makefile | 2 + test/dm/ffa.c | 380 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 383 insertions(+) create mode 100644 test/dm/ffa.c
diff --git a/MAINTAINERS b/MAINTAINERS index 2b9d33e964..6939b832e4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -275,6 +275,7 @@ F: doc/usage/cmd/armffa.rst F: drivers/firmware/arm-ffa/ F: include/arm_ffa.h F: include/sandbox_arm_ffa.h +F: test/dm/ffa.c
ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de diff --git a/test/dm/Makefile b/test/dm/Makefile index 7a79b6e1a2..b554b170db 100644 --- a/test/dm/Makefile +++ b/test/dm/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 open-source-office@arm.com
obj-$(CONFIG_UT_DM) += test-dm.o
@@ -85,6 +86,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_SANDBOX_FFA) += 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 0000000000..d978feda72 --- /dev/null +++ b/test/dm/ffa.c @@ -0,0 +1,380 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Functional tests for UCLASS_FFA class + * + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <console.h> +#include <dm.h> +#include <sandbox_arm_ffa.h> +#include "../../drivers/firmware/arm-ffa/sandbox_arm_ffa_priv.h" +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +/* Macros */ + +#define LOG_MSG_SZ (100) +#define LOG_CMD_SZ (LOG_MSG_SZ * 2) + +/* Functional tests for the UCLASS_FFA */ + +static int dm_test_ffa_log(struct unit_test_state *uts, char *msg) +{ + char cmd[LOG_CMD_SZ] = {0}; + + console_record_reset(); + + snprintf(cmd, LOG_CMD_SZ, "echo "%s"", msg); + run_command(cmd, 0); + + ut_assert_console_end(); + + return 0; +} + +static int check_fwk_version(struct ffa_priv *priv, struct sandbox_ffa_priv *sdx_priv, + struct unit_test_state *uts) +{ + if (priv->dscvry_info.fwk_version != sdx_priv->fwk_version) { + char msg[LOG_MSG_SZ] = {0}; + + snprintf(msg, LOG_MSG_SZ, + "[%s]: Error: framework version: core = 0x%x , sandbox = 0x%x", __func__, + priv->dscvry_info.fwk_version, + sdx_priv->fwk_version); + + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + return 0; +} + +static int check_endpoint_id(struct ffa_priv *priv, struct unit_test_state *uts) +{ + if (priv->id) { + char msg[LOG_MSG_SZ] = {0}; + + snprintf(msg, LOG_MSG_SZ, + "[%s]: Error: endpoint id: core = 0x%x", __func__, priv->id); + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + return 0; +} + +static int check_rxtxbuf(struct ffa_priv *priv, struct unit_test_state *uts) +{ + if (!priv->pair.rxbuf && priv->pair.txbuf) { + char msg[LOG_MSG_SZ] = {0}; + + snprintf(msg, LOG_MSG_SZ, "[%s]: Error: rxbuf = %p txbuf = %p", __func__, + priv->pair.rxbuf, + priv->pair.txbuf); + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + return 0; +} + +static int check_features(struct ffa_priv *priv, struct unit_test_state *uts) +{ + char msg[LOG_MSG_SZ] = {0}; + + if (priv->pair.rxtx_min_pages != RXTX_4K && + priv->pair.rxtx_min_pages != RXTX_16K && + priv->pair.rxtx_min_pages != RXTX_64K) { + snprintf(msg, + LOG_MSG_SZ, + "[%s]: Error: FFA_RXTX_MAP features = 0x%lx", + __func__, + priv->pair.rxtx_min_pages); + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + + return 0; +} + +static int check_rxbuf_mapped_flag(u32 queried_func_id, + u8 rxbuf_mapped, + struct unit_test_state *uts) +{ + char msg[LOG_MSG_SZ] = {0}; + + switch (queried_func_id) { + case FFA_RXTX_MAP: + { + if (rxbuf_mapped) + return 0; + break; + } + case FFA_RXTX_UNMAP: + { + if (!rxbuf_mapped) + return 0; + break; + } + default: + return CMD_RET_FAILURE; + } + + snprintf(msg, LOG_MSG_SZ, "[%s]: Error: %s mapping issue", __func__, + (queried_func_id == FFA_RXTX_MAP ? "FFA_RXTX_MAP" : "FFA_RXTX_UNMAP")); + dm_test_ffa_log(uts, msg); + + return CMD_RET_FAILURE; +} + +static int check_rxbuf_release_flag(u8 rxbuf_owned, struct unit_test_state *uts) +{ + if (rxbuf_owned) { + char msg[LOG_MSG_SZ] = {0}; + + snprintf(msg, LOG_MSG_SZ, "[%s]: Error: RX buffer not released", __func__); + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + return 0; +} + +static int test_ffa_msg_send_direct_req(u16 part_id, struct unit_test_state *uts) +{ + struct ffa_send_direct_data msg = {0}; + u8 cnt; + struct udevice *ffa_dev = NULL; + struct ffa_bus_ops *ffa_ops = NULL; + + uclass_get_device_by_name(UCLASS_FFA, "arm_ffa", &ffa_dev); + ut_assertok(!ffa_dev); + + ffa_ops = (struct ffa_bus_ops *)ffa_bus_get_ops(ffa_dev); + ut_assertok(!ffa_ops); + + ut_assertok(ffa_ops->sync_send_receive(ffa_dev, part_id, &msg, 1)); + + for (cnt = 0; cnt < sizeof(struct ffa_send_direct_data) / sizeof(u64); cnt++) + ut_assertok(((u64 *)&msg)[cnt] != 0xffffffffffffffff); + + return 0; +} + +static int test_partitions_and_comms(const char *service_uuid, + struct sandbox_ffa_priv *sdx_priv, + struct unit_test_state *uts) +{ + u32 count = 0; + struct ffa_partition_info *parts_info; + u32 info_idx, exp_info_idx; + int ret; + struct udevice *ffa_dev = NULL; + struct ffa_bus_ops *ffa_ops = NULL; + + uclass_get_device_by_name(UCLASS_FFA, "arm_ffa", &ffa_dev); + ut_assertok(!ffa_dev); + + ffa_ops = (struct ffa_bus_ops *)ffa_bus_get_ops(ffa_dev); + ut_assertok(!ffa_ops); + + /* Get from the driver the count of the SPs matching the UUID */ + ret = ffa_ops->partition_info_get(ffa_dev, service_uuid, &count, NULL); + /* Make sure partitions are detected */ + ut_assertok(ret != 0); + ut_assertok(count != SANDBOX_SP_COUNT_PER_VALID_SERVICE); + + /* Pre-allocate a buffer to be filled by the driver with ffa_partition_info structs */ + + parts_info = calloc(count, sizeof(struct ffa_partition_info)); + ut_assertok(!parts_info); + + /* Ask the driver to fill the buffer with the SPs info */ + ret = ffa_ops->partition_info_get(ffa_dev, service_uuid, &count, parts_info); + if (ret != 0) { + free(parts_info); + ut_assertok(ret != 0); + } + + /* SPs found , verify the partitions information */ + + ret = CMD_RET_FAILURE; + + for (info_idx = 0; info_idx < count ; info_idx++) { + for (exp_info_idx = 0; + exp_info_idx < sdx_priv->partitions.count; + exp_info_idx++) { + if (parts_info[info_idx].id == + sdx_priv->partitions.descs[exp_info_idx].info.id) { + ret = memcmp(&parts_info[info_idx], + &sdx_priv->partitions.descs[exp_info_idx] + .info, + sizeof(struct ffa_partition_info)); + if (ret) + free(parts_info); + ut_assertok(ret != 0); + /* Send and receive data from the current partition */ + test_ffa_msg_send_direct_req(parts_info[info_idx].id, uts); + } + ret = 0; + } + } + + free(parts_info); + + /* Verify expected partitions found in the emulated secure world */ + ut_assertok(ret != 0); + + return 0; +} + +static int dm_test_ffa_ack(struct unit_test_state *uts) +{ + struct ffa_priv *priv = NULL; + struct sandbox_ffa_priv *sdx_priv = NULL; + struct ffa_sandbox_data func_data = {0}; + u8 rxbuf_flag = 0; + const char *svc1_uuid = SANDBOX_SERVICE1_UUID; + const char *svc2_uuid = SANDBOX_SERVICE2_UUID; + int ret; + struct udevice *ffa_dev = NULL, *sdx_dev = NULL; + struct ffa_bus_ops *ffa_ops = NULL; + + /* Test probing the FF-A sandbox driver, then binding the FF-A bus driver */ + uclass_get_device_by_name(UCLASS_FFA, "sandbox_arm_ffa", &sdx_dev); + ut_assertok(!sdx_dev); + + /* Test probing the FF-A bus driver */ + uclass_get_device_by_name(UCLASS_FFA, "arm_ffa", &ffa_dev); + ut_assertok(!ffa_dev); + + ffa_ops = (struct ffa_bus_ops *)ffa_bus_get_ops(ffa_dev); + ut_assertok(!ffa_ops); + + /* Get a pointer to the FF-A core and sandbox drivers private data */ + priv = dev_get_priv(ffa_dev); + sdx_priv = dev_get_priv(sdx_dev); + + /* Make sure private data pointers are retrieved */ + ut_assertok(priv == 0); + ut_assertok(sdx_priv == 0); + + /* Test FFA_VERSION */ + ut_assertok(check_fwk_version(priv, sdx_priv, uts)); + + /* Test FFA_ID_GET */ + ut_assertok(check_endpoint_id(priv, uts)); + + /* Test FFA_FEATURES */ + ut_assertok(check_features(priv, uts)); + + /* Test core RX/TX buffers */ + ut_assertok(check_rxtxbuf(priv, uts)); + + /* Test FFA_RXTX_MAP */ + func_data.data0 = &rxbuf_flag; + func_data.data0_size = sizeof(rxbuf_flag); + + rxbuf_flag = 0; + ut_assertok(sandbox_ffa_query_core_state(sdx_dev, FFA_RXTX_MAP, &func_data)); + ut_assertok(check_rxbuf_mapped_flag(FFA_RXTX_MAP, rxbuf_flag, uts)); + + /* FFA_PARTITION_INFO_GET / FFA_MSG_SEND_DIRECT_REQ */ + ret = test_partitions_and_comms(svc1_uuid, sdx_priv, uts); + ut_assertok(ret != 0); + + /* Test FFA_RX_RELEASE */ + rxbuf_flag = 1; + ut_assertok(sandbox_ffa_query_core_state(sdx_dev, FFA_RX_RELEASE, &func_data)); + ut_assertok(check_rxbuf_release_flag(rxbuf_flag, uts)); + + /* FFA_PARTITION_INFO_GET / FFA_MSG_SEND_DIRECT_REQ */ + ret = test_partitions_and_comms(svc2_uuid, sdx_priv, uts); + ut_assertok(ret != 0); + + /* Test FFA_RX_RELEASE */ + rxbuf_flag = 1; + ut_assertok(sandbox_ffa_query_core_state(sdx_dev, FFA_RX_RELEASE, &func_data)); + ut_assertok(check_rxbuf_release_flag(rxbuf_flag, uts)); + + /* Test FFA_RXTX_UNMAP */ + ut_assertok(ffa_ops->rxtx_unmap(ffa_dev)); + + rxbuf_flag = 1; + ut_assertok(sandbox_ffa_query_core_state(sdx_dev, FFA_RXTX_UNMAP, &func_data)); + ut_assertok(check_rxbuf_mapped_flag(FFA_RXTX_UNMAP, 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 *priv = NULL; + struct sandbox_ffa_priv *sdx_priv = NULL; + 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 = {0}; + int ret; + u32 count = 0; + u16 part_id = 0; + struct udevice *ffa_dev = NULL, *sdx_dev = NULL; + struct ffa_bus_ops *ffa_ops = NULL; + + /* Test probing the FF-A sandbox driver, then binding the FF-A bus driver */ + uclass_get_device_by_name(UCLASS_FFA, "sandbox_arm_ffa", &sdx_dev); + ut_assertok(!sdx_dev); + + /* Test probing the FF-A bus driver */ + uclass_get_device_by_name(UCLASS_FFA, "arm_ffa", &ffa_dev); + ut_assertok(!ffa_dev); + + ffa_ops = (struct ffa_bus_ops *)ffa_bus_get_ops(ffa_dev); + ut_assertok(!ffa_ops); + + /* Get a pointer to the FF-A core and sandbox drivers private data */ + priv = dev_get_priv(ffa_dev); + sdx_priv = dev_get_priv(sdx_dev); + + /* Make sure private data pointers are retrieved */ + ut_assertok(priv == 0); + ut_assertok(sdx_priv == 0); + + /* Query partitions count using invalid arguments */ + ret = ffa_ops->partition_info_get(ffa_dev, unvalid_svc_uuid, NULL, NULL); + ut_assertok(ret != -EINVAL); + + /* Query partitions count using an invalid UUID string */ + ret = ffa_ops->partition_info_get(ffa_dev, unvalid_svc_uuid_str, &count, NULL); + ut_assertok(ret != -EINVAL); + + /* Query partitions count using an invalid UUID (no matching SP) */ + count = 0; + ret = ffa_ops->partition_info_get(ffa_dev, unvalid_svc_uuid, &count, NULL); + ut_assertok(count != 0); + + /* Query partitions count using a valid UUID */ + count = 0; + ret = ffa_ops->partition_info_get(ffa_dev, valid_svc_uuid, &count, NULL); + /* Make sure partitions are detected */ + ut_assertok(ret != 0); + ut_assertok(count != SANDBOX_SP_COUNT_PER_VALID_SERVICE); + + /* Send data to an invalid partition */ + ret = ffa_ops->sync_send_receive(ffa_dev, part_id, &msg, 1); + ut_assertok(ret != -EINVAL); + + /* Send data to a valid partition */ + part_id = priv->partitions.descs[0].info.id; + ret = ffa_ops->sync_send_receive(ffa_dev, part_id, &msg, 1); + ut_assertok(ret != 0); + + return 0; +} + +DM_TEST(dm_test_ffa_nack, UT_TESTF_SCAN_FDT | UT_TESTF_CONSOLE_REC);

Hi Abdellatif,
On Fri, 10 Mar 2023 at 06:10, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
Add functional test cases for the FF-A core driver
These tests rely on the FF-A Sandbox driver which helps in inspecting the FF-A core driver.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v9: align FF-A sandbox tests with FF-A discovery through DM
v8:
- update partition_info_get() second argument to be an SP count
- pass NULL device pointer to the FF-A bus discovery and operations
v7: set the tests to use 64-bit direct messaging
v4: align sandbox tests with the new FF-A driver interfaces and new way of error handling
v1: introduce sandbox tests
MAINTAINERS | 1 + test/dm/Makefile | 2 + test/dm/ffa.c | 380 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 383 insertions(+) create mode 100644 test/dm/ffa.c
diff --git a/MAINTAINERS b/MAINTAINERS index 2b9d33e964..6939b832e4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -275,6 +275,7 @@ F: doc/usage/cmd/armffa.rst F: drivers/firmware/arm-ffa/ F: include/arm_ffa.h F: include/sandbox_arm_ffa.h +F: test/dm/ffa.c
ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de diff --git a/test/dm/Makefile b/test/dm/Makefile index 7a79b6e1a2..b554b170db 100644 --- a/test/dm/Makefile +++ b/test/dm/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 open-source-office@arm.com
obj-$(CONFIG_UT_DM) += test-dm.o
@@ -85,6 +86,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_SANDBOX_FFA) += 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 0000000000..d978feda72 --- /dev/null +++ b/test/dm/ffa.c @@ -0,0 +1,380 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Functional tests for UCLASS_FFA class
- Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
- Authors:
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include <common.h> +#include <console.h> +#include <dm.h> +#include <sandbox_arm_ffa.h> +#include "../../drivers/firmware/arm-ffa/sandbox_arm_ffa_priv.h"
I suppose this is OK, but you could put is in arch/sandbox/include/asm/ perhaps?
+#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h>
+/* Macros */
+#define LOG_MSG_SZ (100) +#define LOG_CMD_SZ (LOG_MSG_SZ * 2)
+/* Functional tests for the UCLASS_FFA */
+static int dm_test_ffa_log(struct unit_test_state *uts, char *msg) +{
char cmd[LOG_CMD_SZ] = {0};
console_record_reset();
snprintf(cmd, LOG_CMD_SZ, "echo \"%s\"", msg);
run_command(cmd, 0);
ut_assert_console_end();
return 0;
+}
+static int check_fwk_version(struct ffa_priv *priv, struct sandbox_ffa_priv *sdx_priv,
struct unit_test_state *uts)
+{
if (priv->dscvry_info.fwk_version != sdx_priv->fwk_version) {
char msg[LOG_MSG_SZ] = {0};
snprintf(msg, LOG_MSG_SZ,
"[%s]: Error: framework version: core = 0x%x , sandbox = 0x%x", __func__,
priv->dscvry_info.fwk_version,
sdx_priv->fwk_version);
dm_test_ffa_log(uts, msg);
return CMD_RET_FAILURE;
}
return 0;
+}
+static int check_endpoint_id(struct ffa_priv *priv, struct unit_test_state *uts) +{
if (priv->id) {
char msg[LOG_MSG_SZ] = {0};
snprintf(msg, LOG_MSG_SZ,
"[%s]: Error: endpoint id: core = 0x%x", __func__, priv->id);
dm_test_ffa_log(uts, msg);
return CMD_RET_FAILURE;
}
return 0;
+}
+static int check_rxtxbuf(struct ffa_priv *priv, struct unit_test_state *uts) +{
if (!priv->pair.rxbuf && priv->pair.txbuf) {
char msg[LOG_MSG_SZ] = {0};
snprintf(msg, LOG_MSG_SZ, "[%s]: Error: rxbuf = %p txbuf = %p", __func__,
priv->pair.rxbuf,
priv->pair.txbuf);
dm_test_ffa_log(uts, msg);
return CMD_RET_FAILURE;
}
return 0;
+}
+static int check_features(struct ffa_priv *priv, struct unit_test_state *uts) +{
char msg[LOG_MSG_SZ] = {0};
if (priv->pair.rxtx_min_pages != RXTX_4K &&
priv->pair.rxtx_min_pages != RXTX_16K &&
priv->pair.rxtx_min_pages != RXTX_64K) {
snprintf(msg,
LOG_MSG_SZ,
"[%s]: Error: FFA_RXTX_MAP features = 0x%lx",
__func__,
priv->pair.rxtx_min_pages);
dm_test_ffa_log(uts, msg);
return CMD_RET_FAILURE;
}
return 0;
+}
+static int check_rxbuf_mapped_flag(u32 queried_func_id,
u8 rxbuf_mapped,
struct unit_test_state *uts)
+{
char msg[LOG_MSG_SZ] = {0};
switch (queried_func_id) {
case FFA_RXTX_MAP:
{
Code style is sometimes a bit off. There should not be a {} around this block, for example.
if (rxbuf_mapped)
return 0;
break;
}
case FFA_RXTX_UNMAP:
{
if (!rxbuf_mapped)
return 0;
break;
}
default:
return CMD_RET_FAILURE;
}
snprintf(msg, LOG_MSG_SZ, "[%s]: Error: %s mapping issue", __func__,
(queried_func_id == FFA_RXTX_MAP ? "FFA_RXTX_MAP" : "FFA_RXTX_UNMAP"));
dm_test_ffa_log(uts, msg);
return CMD_RET_FAILURE;
+}
+static int check_rxbuf_release_flag(u8 rxbuf_owned, struct unit_test_state *uts) +{
if (rxbuf_owned) {
char msg[LOG_MSG_SZ] = {0};
Can you drop the assignment?
snprintf(msg, LOG_MSG_SZ, "[%s]: Error: RX buffer not released", __func__);
dm_test_ffa_log(uts, msg);
return CMD_RET_FAILURE;
}
return 0;
+}
+static int test_ffa_msg_send_direct_req(u16 part_id, struct unit_test_state *uts) +{
struct ffa_send_direct_data msg = {0};
u8 cnt;
struct udevice *ffa_dev = NULL;
struct ffa_bus_ops *ffa_ops = NULL;
Unnecessary assignments aagin. Please look through and clean these up.
uclass_get_device_by_name(UCLASS_FFA, "arm_ffa", &ffa_dev);
ut_assertok(!ffa_dev);
ffa_ops = (struct ffa_bus_ops *)ffa_bus_get_ops(ffa_dev);
ut_assertok(!ffa_ops);
As mentioned earlier the operations should be in the uclass, rather than having one uclass operation that returns the operations. I do wonder which uclass you are copying, to end up with this?
ut_assertok(ffa_ops->sync_send_receive(ffa_dev, part_id, &msg, 1));
for (cnt = 0; cnt < sizeof(struct ffa_send_direct_data) / sizeof(u64); cnt++)
ut_assertok(((u64 *)&msg)[cnt] != 0xffffffffffffffff);
return 0;
+}
+static int test_partitions_and_comms(const char *service_uuid,
struct sandbox_ffa_priv *sdx_priv,
struct unit_test_state *uts)
+{
u32 count = 0;
struct ffa_partition_info *parts_info;
u32 info_idx, exp_info_idx;
int ret;
struct udevice *ffa_dev = NULL;
struct ffa_bus_ops *ffa_ops = NULL;
uclass_get_device_by_name(UCLASS_FFA, "arm_ffa", &ffa_dev);
ut_assertok(!ffa_dev);
ut_assertnonnull(ffa_dev)
Also, please just use 'dev' as there is only one in this file
[..]
/* FFA_PARTITION_INFO_GET / FFA_MSG_SEND_DIRECT_REQ */
ret = test_partitions_and_comms(svc2_uuid, sdx_priv, uts);
ut_assertok(ret != 0);
ut_assertok() is intended to check that something is 0, meaning no error. It is confusing to use it for other things.
Here I think you want to say:
ut_assertok(ret)
?
If you want to assert something special, use ut_assert()
/* Test FFA_RX_RELEASE */
rxbuf_flag = 1;
ut_assertok(sandbox_ffa_query_core_state(sdx_dev, FFA_RX_RELEASE, &func_data));
ut_assertok(check_rxbuf_release_flag(rxbuf_flag, uts));
/* Test FFA_RXTX_UNMAP */
ut_assertok(ffa_ops->rxtx_unmap(ffa_dev));
rxbuf_flag = 1;
ut_assertok(sandbox_ffa_query_core_state(sdx_dev, FFA_RXTX_UNMAP, &func_data));
ut_assertok(check_rxbuf_mapped_flag(FFA_RXTX_UNMAP, 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 *priv = NULL;
struct sandbox_ffa_priv *sdx_priv = NULL;
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 = {0};
int ret;
u32 count = 0;
u16 part_id = 0;
struct udevice *ffa_dev = NULL, *sdx_dev = NULL;
struct ffa_bus_ops *ffa_ops = NULL;
/* Test probing the FF-A sandbox driver, then binding the FF-A bus driver */
uclass_get_device_by_name(UCLASS_FFA, "sandbox_arm_ffa", &sdx_dev);
ut_assertok(!sdx_dev);
/* Test probing the FF-A bus driver */
uclass_get_device_by_name(UCLASS_FFA, "arm_ffa", &ffa_dev);
ut_assertok(!ffa_dev);
ffa_ops = (struct ffa_bus_ops *)ffa_bus_get_ops(ffa_dev);
ut_assertok(!ffa_ops);
/* Get a pointer to the FF-A core and sandbox drivers private data */
priv = dev_get_priv(ffa_dev);
sdx_priv = dev_get_priv(sdx_dev);
/* Make sure private data pointers are retrieved */
ut_assertok(priv == 0);
ut_assertok(sdx_priv == 0);
Those two cannot fail :-)
/* Query partitions count using invalid arguments */
ret = ffa_ops->partition_info_get(ffa_dev, unvalid_svc_uuid, NULL, NULL);
ut_assertok(ret != -EINVAL);
I think you are using ut_assertok() instead of ut_assert(). Please switch.
This one should be
ut_asserteq(-EINVAL, ret)
/* Query partitions count using an invalid UUID string */
ret = ffa_ops->partition_info_get(ffa_dev, unvalid_svc_uuid_str, &count, NULL);
ut_assertok(ret != -EINVAL);
/* Query partitions count using an invalid UUID (no matching SP) */
count = 0;
ret = ffa_ops->partition_info_get(ffa_dev, unvalid_svc_uuid, &count, NULL);
ut_assertok(count != 0);
/* Query partitions count using a valid UUID */
count = 0;
ret = ffa_ops->partition_info_get(ffa_dev, valid_svc_uuid, &count, NULL);
/* Make sure partitions are detected */
ut_assertok(ret != 0);
ut_assertok(count != SANDBOX_SP_COUNT_PER_VALID_SERVICE);
/* Send data to an invalid partition */
ret = ffa_ops->sync_send_receive(ffa_dev, part_id, &msg, 1);
ut_assertok(ret != -EINVAL);
/* Send data to a valid partition */
part_id = priv->partitions.descs[0].info.id;
ret = ffa_ops->sync_send_receive(ffa_dev, part_id, &msg, 1);
ut_assertok(ret != 0);
return 0;
+}
+DM_TEST(dm_test_ffa_nack, UT_TESTF_SCAN_FDT | UT_TESTF_CONSOLE_REC);
2.25.1
This all looks find once you tidy up the nits.
Regards, Simon

Add Sandbox test for the armffa command
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
--- Changelog: ===============
v9: align the test with FF-A discovery through DM
v4: drop use of helper APIs
v1: introduce armffa command sandbox test
MAINTAINERS | 1 + test/cmd/Makefile | 2 ++ test/cmd/armffa.c | 39 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+) create mode 100644 test/cmd/armffa.c
diff --git a/MAINTAINERS b/MAINTAINERS index 6939b832e4..b36fb41668 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -275,6 +275,7 @@ F: doc/usage/cmd/armffa.rst F: drivers/firmware/arm-ffa/ F: include/arm_ffa.h F: include/sandbox_arm_ffa.h +F: test/cmd/armffa.c F: test/dm/ffa.c
ARM FREESCALE IMX diff --git a/test/cmd/Makefile b/test/cmd/Makefile index 2ffde8703a..e74da56a29 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 open-source-office@arm.com
ifdef CONFIG_HUSH_PARSER obj-$(CONFIG_CONSOLE_RECORD) += test_echo.o @@ -19,6 +20,7 @@ obj-$(CONFIG_CMD_PWM) += pwm.o obj-$(CONFIG_CMD_SEAMA) += seama.o ifdef CONFIG_SANDBOX obj-$(CONFIG_CMD_SETEXPR) += setexpr.o +obj-$(CONFIG_SANDBOX_FFA) += 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 0000000000..73c895f182 --- /dev/null +++ b/test/cmd/armffa.c @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test for armffa command + * + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <sandbox_arm_ffa.h> +#include <string.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +#define PING_CMD_SIZE 19 + +/* Basic test of 'armffa' command */ +static int dm_test_armffa_cmd(struct unit_test_state *uts) +{ + char ping_cmd[PING_CMD_SIZE] = {0}; + + /* armffa getpart <UUID> */ + ut_assertok(run_command("armffa getpart " SANDBOX_SERVICE1_UUID, 0)); + + snprintf(ping_cmd, PING_CMD_SIZE, "armffa ping 0x%x", SANDBOX_SP1_ID); + + /* armffa ping <ID> */ + ut_assertok(run_command(ping_cmd, 0)); + + /* armffa devlist */ + ut_assertok(run_command("armffa devlist", 0)); + + return CMD_RET_SUCCESS; +} + +DM_TEST(dm_test_armffa_cmd, UT_TESTF_SCAN_FDT | UT_TESTF_CONSOLE_REC);

Hi Abdellatif,
On Fri, 10 Mar 2023 at 06:11, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
Add Sandbox test for the armffa command
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v9: align the test with FF-A discovery through DM
v4: drop use of helper APIs
v1: introduce armffa command sandbox test
MAINTAINERS | 1 + test/cmd/Makefile | 2 ++ test/cmd/armffa.c | 39 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+) create mode 100644 test/cmd/armffa.c
Reviewed-by: Simon Glass sjg@chromium.org
diff --git a/MAINTAINERS b/MAINTAINERS index 6939b832e4..b36fb41668 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -275,6 +275,7 @@ F: doc/usage/cmd/armffa.rst F: drivers/firmware/arm-ffa/ F: include/arm_ffa.h F: include/sandbox_arm_ffa.h +F: test/cmd/armffa.c F: test/dm/ffa.c
ARM FREESCALE IMX diff --git a/test/cmd/Makefile b/test/cmd/Makefile index 2ffde8703a..e74da56a29 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 open-source-office@arm.com
ifdef CONFIG_HUSH_PARSER obj-$(CONFIG_CONSOLE_RECORD) += test_echo.o @@ -19,6 +20,7 @@ obj-$(CONFIG_CMD_PWM) += pwm.o obj-$(CONFIG_CMD_SEAMA) += seama.o ifdef CONFIG_SANDBOX obj-$(CONFIG_CMD_SETEXPR) += setexpr.o +obj-$(CONFIG_SANDBOX_FFA) += 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 0000000000..73c895f182 --- /dev/null +++ b/test/cmd/armffa.c @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Test for armffa command
- Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
- Authors:
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include <common.h> +#include <sandbox_arm_ffa.h> +#include <string.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h>
+#define PING_CMD_SIZE 19
+/* Basic test of 'armffa' command */ +static int dm_test_armffa_cmd(struct unit_test_state *uts) +{
char ping_cmd[PING_CMD_SIZE] = {0};
/* armffa getpart <UUID> */
ut_assertok(run_command("armffa getpart " SANDBOX_SERVICE1_UUID, 0));
snprintf(ping_cmd, PING_CMD_SIZE, "armffa ping 0x%x", SANDBOX_SP1_ID);
/* armffa ping <ID> */
ut_assertok(run_command(ping_cmd, 0));
/* armffa devlist */
ut_assertok(run_command("armffa devlist", 0));
return CMD_RET_SUCCESS;
return 0
(this isn't even a command, it is a test)
+}
+DM_TEST(dm_test_armffa_cmd, UT_TESTF_SCAN_FDT | UT_TESTF_CONSOLE_REC);
2.25.1
Regards, Simon

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.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Signed-off-by: Gowtham Suresh Kumar gowtham.sureshkumar@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
--- Changelog: ===============
v9: align how FF-A is used with FF-A discovery through DM
v8:
* isolate the compilation choices between FF-A and OP-TEE * update partition_info_get() second argument to be an SP count * pass NULL device pointer to the FF-A bus discovery and operations
v7:
* set the MM door bell event to use 64-bit direct messaging * issue a compile time error when one of these macros are not found : FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_OFFSET, FFA_SHARED_MM_BUFFER_ADDR * make mm_sp_svc_uuid static * replace EINVAL with ENOMEM in ffa_discover_mm_sp_id() when calloc() fails * improve use of unmap_sysmem() in ffa_mm_communicate()
v6:
* add FF-A runtime discovery at MM communication level * drop EFI runtime support for FF-A MM communication * revert the changes in include/mm_communication.h for efi_mm_communicate_header and smm_variable_access structures
v4:
* use the new FF-A driver interfaces * discover MM partitions at runtime * copy FF-A driver private data to EFI runtime section at ExitBootServices() * drop use of FFA_ERR_STAT_SUCCESS error code * replace EFI_BUFFER_TOO_SMALL with EFI_OUT_OF_RESOURCES in ffa_mm_communicate(). No need for efi_memcpy_runtime() anymore * revert the error log in mm_communicate() in case of failure * remove packed attribute from efi_mm_communicate_header and smm_variable_communicate_header
v2:
* set default values to 0 for FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_ADDR and MM_SP_UUID_DATA and add warnings
v1:
* introduce FF-A MM communication
include/mm_communication.h | 6 + lib/efi_loader/Kconfig | 14 +- lib/efi_loader/efi_variable_tee.c | 317 +++++++++++++++++++++++++++++- 3 files changed, 331 insertions(+), 6 deletions(-)
diff --git a/include/mm_communication.h b/include/mm_communication.h index e65fbde60d..87509fec3f 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. sughosh.ganu@linaro.org * Copyright (C) 2020 Linaro Ltd. ilias.apalodimas@linaro.org + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#ifndef _MM_COMMUNICATION_H_ @@ -13,6 +16,9 @@
#include <part_efi.h>
+/* MM service UUID string (big-endian format). This UUID is common across all MM SPs */ +#define MM_SP_UUID "33d532ed-e699-0942-c09c-a798d9cd722d" + /* * Interface to the pseudo Trusted Application (TA), which provides a * communication channel with the Standalone MM (Management Mode) diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index c5835e6ef6..08a6b84101 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -55,13 +55,23 @@ 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" - depends on OPTEE + bool "UEFI variables storage service via the trusted world" + depends on OPTEE || ARM_FFA_TRANSPORT 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 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 dfef18435d..86207f1ff2 100644 --- a/lib/efi_loader/efi_variable_tee.c +++ b/lib/efi_loader/efi_variable_tee.c @@ -4,9 +4,14 @@ * * Copyright (C) 2019 Linaro Ltd. sughosh.ganu@linaro.org * Copyright (C) 2019 Linaro Ltd. ilias.apalodimas@linaro.org + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> +#include <dm.h> #include <efi.h> #include <efi_api.h> #include <efi_loader.h> @@ -15,6 +20,36 @@ #include <malloc.h> #include <mm_communication.h>
+#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) + +#include <arm_ffa.h> +#include <cpu_func.h> +#include <mapmem.h> + +#ifndef FFA_SHARED_MM_BUFFER_SIZE +#error "FFA_SHARED_MM_BUFFER_SIZE must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_SIZE 0 +#endif + +#ifndef FFA_SHARED_MM_BUFFER_OFFSET +#error "FFA_SHARED_MM_BUFFER_OFFSET must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_OFFSET 0 +#endif + +#ifndef FFA_SHARED_MM_BUFFER_ADDR +#error "FFA_SHARED_MM_BUFFER_ADDR must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_ADDR 0 +#endif + +/* MM return codes */ +#define MM_SUCCESS (0) + +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 */ static efi_uintn_t max_payload_size; /* func + data */ @@ -24,6 +59,7 @@ struct mm_connection { u32 session; };
+#if (IS_ENABLED(CONFIG_OPTEE)) /** * get_connection() - Retrieve OP-TEE session for a specific UUID. * @@ -143,13 +179,269 @@ static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize)
return ret; } +#endif + +#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) + +/** + * ffa_notify_mm_sp() - Announce there is data in the shared buffer + * + * Notifies 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 = -1; + struct udevice *ffa_dev = NULL; + struct ffa_bus_ops *ffa_ops = NULL; + + uclass_get_device_by_name(UCLASS_FFA, "arm_ffa", &ffa_dev); + if (!ffa_dev) { + log_err("EFI: Cannot find FF-A bus device, notify MM SP failure\n"); + return -ENODEV; + } + + ffa_ops = (struct ffa_bus_ops *)ffa_bus_get_ops(ffa_dev); + if (!ffa_ops) { + log_err("EFI: Invalid FF-A ops, notify MM SP failure\n"); + return -EINVAL; + } + + msg.data0 = FFA_SHARED_MM_BUFFER_OFFSET; /* x3 */ + + ret = ffa_ops->sync_send_receive(ffa_dev, mm_sp_id, &msg, 1); + if (ret != 0) + return ret; + + sp_event_ret = msg.data0; /* x3 */ + + if (sp_event_ret == MM_SUCCESS) + return 0; + + /* + * Failure to notify the MM SP + */ + + return -EACCES; +} + +/** + * 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_info *parts_info; + struct udevice *ffa_dev = NULL; + struct ffa_bus_ops *ffa_ops = NULL; + + uclass_get_device_by_name(UCLASS_FFA, "arm_ffa", &ffa_dev); + if (!ffa_dev) { + log_err("EFI: Cannot find FF-A bus device, MM SP discovery failure\n"); + return -ENODEV; + } + + ffa_ops = (struct ffa_bus_ops *)ffa_bus_get_ops(ffa_dev); + if (!ffa_ops) { + log_err("EFI: Invalid FF-A ops, MM SP discovery failure\n"); + return -EINVAL; + } + + /* Get from the driver the count of the SPs matching the UUID */ + ret = ffa_ops->partition_info_get(ffa_dev, mm_sp_svc_uuid, &count, NULL); + if (ret != 0) { + log_err("EFI: Failure in querying SPs count (%d), MM SP discovery failure\n", ret); + return ret; + } + + if (!count) { + log_info("EFI: No MM partition found\n"); + return ret; + } + + /* Pre-allocate a buffer to be filled by the driver with ffa_partition_info structs */ + + log_info("EFI: Pre-allocating %d partition(s) info structures\n", count); + + parts_info = calloc(count, sizeof(*parts_info)); + if (!parts_info) + return -ENOMEM; + + /* Ask the driver to fill the buffer with the SPs info */ + ret = ffa_ops->partition_info_get(ffa_dev, mm_sp_svc_uuid, &count, parts_info); + if (ret) { + log_err("EFI: Failure in querying SPs info (%d), MM SP discovery failure\n", ret); + free(parts_info); + return ret; + } + + /* MM SPs found , use the first one */ + + mm_sp_id = parts_info[0].id; + + log_info("EFI: MM partition ID 0x%x\n", mm_sp_id); + + free(parts_info); + + 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 + * + * Issues 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() != 0) { + 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 > FFA_SHARED_MM_BUFFER_SIZE) + return EFI_INVALID_PARAMETER; + + /* Copy the data to the shared buffer */ + + virt_shared_buf = map_sysmem((phys_addr_t)FFA_SHARED_MM_BUFFER_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; +} +#endif + +/** + * select_ffa_mm_comms() - checks FF-A support availability + * + * Making sure FF-A is compiled in. If that's the case try to make sure + * the FF-A bus is probed successfully which means FF-A communication + * with secure world works and ready to be used. + * + * Return: + * + * 0: FF-A ready for use. Otherwise, failure + */ +static efi_status_t select_ffa_mm_comms(void) +{ + efi_status_t ret = EFI_UNSUPPORTED; + + if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) { + struct udevice *dev = NULL; + + uclass_get_device_by_name(UCLASS_FFA, "arm_ffa", &dev); + if (!dev) { + log_err("EFI: Cannot find FF-A bus device, cannot select FF-A comms\n"); + ret = EFI_NOT_READY; + } else { + ret = EFI_SUCCESS; + } + } + + return ret; +}
/** - * mm_communicate() - Adjust the cmonnucation buffer to StandAlonneMM and send + * 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) @@ -162,7 +454,17 @@ static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) 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); + ret = select_ffa_mm_comms(); + if (ret != EFI_SUCCESS) { +#if (IS_ENABLED(CONFIG_OPTEE)) + ret = optee_mm_communicate(comm_buf, dsize); +#endif + } else { +#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) + ret = ffa_mm_communicate(comm_buf, dsize); +#endif + } + if (ret != EFI_SUCCESS) { log_err("%s failed!\n", __func__); return ret; @@ -258,6 +560,13 @@ efi_status_t EFIAPI get_max_payload(efi_uintn_t *size) goto out; } *size = var_payload->size; + + #if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) + if (*size > FFA_SHARED_MM_BUFFER_SIZE) + *size = FFA_SHARED_MM_BUFFER_SIZE - MM_COMMUNICATE_HEADER_SIZE - + MM_VARIABLE_COMMUNICATE_SIZE; + #endif + /* * There seems to be a bug in EDK2 miscalculating the boundaries and * size checks, so deduct 2 more bytes to fulfill this requirement. Fix @@ -697,7 +1006,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);
/*

Hi Abdellatif,
On Fri, 10 Mar 2023 at 06:11, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
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.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Signed-off-by: Gowtham Suresh Kumar gowtham.sureshkumar@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v9: align how FF-A is used with FF-A discovery through DM
v8:
- isolate the compilation choices between FF-A and OP-TEE
- update partition_info_get() second argument to be an SP count
- pass NULL device pointer to the FF-A bus discovery and operations
v7:
- set the MM door bell event to use 64-bit direct messaging
- issue a compile time error when one of these macros are not found : FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_OFFSET, FFA_SHARED_MM_BUFFER_ADDR
- make mm_sp_svc_uuid static
- replace EINVAL with ENOMEM in ffa_discover_mm_sp_id() when calloc() fails
- improve use of unmap_sysmem() in ffa_mm_communicate()
v6:
- add FF-A runtime discovery at MM communication level
- drop EFI runtime support for FF-A MM communication
- revert the changes in include/mm_communication.h for efi_mm_communicate_header and smm_variable_access structures
v4:
- use the new FF-A driver interfaces
- discover MM partitions at runtime
- copy FF-A driver private data to EFI runtime section at ExitBootServices()
- drop use of FFA_ERR_STAT_SUCCESS error code
- replace EFI_BUFFER_TOO_SMALL with EFI_OUT_OF_RESOURCES in ffa_mm_communicate(). No need for efi_memcpy_runtime() anymore
- revert the error log in mm_communicate() in case of failure
- remove packed attribute from efi_mm_communicate_header and smm_variable_communicate_header
v2:
- set default values to 0 for FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_ADDR and MM_SP_UUID_DATA and add warnings
v1:
- introduce FF-A MM communication
include/mm_communication.h | 6 + lib/efi_loader/Kconfig | 14 +- lib/efi_loader/efi_variable_tee.c | 317 +++++++++++++++++++++++++++++- 3 files changed, 331 insertions(+), 6 deletions(-)
diff --git a/include/mm_communication.h b/include/mm_communication.h index e65fbde60d..87509fec3f 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. sughosh.ganu@linaro.org
- Copyright (C) 2020 Linaro Ltd. ilias.apalodimas@linaro.org
- Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
- Authors:
*/
Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
#ifndef _MM_COMMUNICATION_H_ @@ -13,6 +16,9 @@
#include <part_efi.h>
+/* MM service UUID string (big-endian format). This UUID is common across all MM SPs */ +#define MM_SP_UUID "33d532ed-e699-0942-c09c-a798d9cd722d"
/*
- Interface to the pseudo Trusted Application (TA), which provides a
- communication channel with the Standalone MM (Management Mode)
diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index c5835e6ef6..08a6b84101 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -55,13 +55,23 @@ 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"
depends on OPTEE
bool "UEFI variables storage service via the trusted world"
depends on OPTEE || ARM_FFA_TRANSPORT 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 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 dfef18435d..86207f1ff2 100644 --- a/lib/efi_loader/efi_variable_tee.c +++ b/lib/efi_loader/efi_variable_tee.c @@ -4,9 +4,14 @@
- Copyright (C) 2019 Linaro Ltd. sughosh.ganu@linaro.org
- Copyright (C) 2019 Linaro Ltd. ilias.apalodimas@linaro.org
- Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
- Authors:
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#include <common.h> +#include <dm.h> #include <efi.h> #include <efi_api.h> #include <efi_loader.h> @@ -15,6 +20,36 @@ #include <malloc.h> #include <mm_communication.h>
+#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
Please avoid excluding code unless you need to
+#include <arm_ffa.h> +#include <cpu_func.h> +#include <mapmem.h>
+#ifndef FFA_SHARED_MM_BUFFER_SIZE +#error "FFA_SHARED_MM_BUFFER_SIZE must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_SIZE 0
Shouldn't that info be in the DT?
+#endif
+#ifndef FFA_SHARED_MM_BUFFER_OFFSET +#error "FFA_SHARED_MM_BUFFER_OFFSET must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_OFFSET 0 +#endif
+#ifndef FFA_SHARED_MM_BUFFER_ADDR +#error "FFA_SHARED_MM_BUFFER_ADDR must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_ADDR 0 +#endif
+/* MM return codes */ +#define MM_SUCCESS (0)
+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 */ static efi_uintn_t max_payload_size; /* func + data */ @@ -24,6 +59,7 @@ struct mm_connection { u32 session; };
+#if (IS_ENABLED(CONFIG_OPTEE))
same here
/**
- get_connection() - Retrieve OP-TEE session for a specific UUID.
@@ -143,13 +179,269 @@ static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize)
return ret;
} +#endif
+#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
and here
+/**
- ffa_notify_mm_sp() - Announce there is data in the shared buffer
- Notifies 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 = -1;
struct udevice *ffa_dev = NULL;
struct ffa_bus_ops *ffa_ops = NULL;
uclass_get_device_by_name(UCLASS_FFA, "arm_ffa", &ffa_dev);
uclass_first_device()
if (!ffa_dev) {
log_err("EFI: Cannot find FF-A bus device, notify MM SP failure\n");
return -ENODEV;
}
ffa_ops = (struct ffa_bus_ops *)ffa_bus_get_ops(ffa_dev);
if (!ffa_ops) {
log_err("EFI: Invalid FF-A ops, notify MM SP failure\n");
return -EINVAL;
}
msg.data0 = FFA_SHARED_MM_BUFFER_OFFSET; /* x3 */
ret = ffa_ops->sync_send_receive(ffa_dev, mm_sp_id, &msg, 1);
if (ret != 0)
return ret;
sp_event_ret = msg.data0; /* x3 */
if (sp_event_ret == MM_SUCCESS)
return 0;
/*
* Failure to notify the MM SP
*/
/* Failure ... */
return -EACCES;
+}
+/**
- 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_info *parts_info;
struct udevice *ffa_dev = NULL;
struct ffa_bus_ops *ffa_ops = NULL;
uclass_get_device_by_name(UCLASS_FFA, "arm_ffa", &ffa_dev);
if (!ffa_dev) {
log_err("EFI: Cannot find FF-A bus device, MM SP discovery failure\n");
return -ENODEV;
}
ffa_ops = (struct ffa_bus_ops *)ffa_bus_get_ops(ffa_dev);
if (!ffa_ops) {
log_err("EFI: Invalid FF-A ops, MM SP discovery failure\n");
return -EINVAL;
}
/* Get from the driver the count of the SPs matching the UUID */
ret = ffa_ops->partition_info_get(ffa_dev, mm_sp_svc_uuid, &count, NULL);
if (ret != 0) {
log_err("EFI: Failure in querying SPs count (%d), MM SP discovery failure\n", ret);
return ret;
}
if (!count) {
log_info("EFI: No MM partition found\n");
return ret;
}
/* Pre-allocate a buffer to be filled by the driver with ffa_partition_info structs */
log_info("EFI: Pre-allocating %d partition(s) info structures\n", count);
parts_info = calloc(count, sizeof(*parts_info));
if (!parts_info)
return -ENOMEM;
/* Ask the driver to fill the buffer with the SPs info */
ret = ffa_ops->partition_info_get(ffa_dev, mm_sp_svc_uuid, &count, parts_info);
if (ret) {
log_err("EFI: Failure in querying SPs info (%d), MM SP discovery failure\n", ret);
free(parts_info);
return ret;
}
/* MM SPs found , use the first one */
mm_sp_id = parts_info[0].id;
log_info("EFI: MM partition ID 0x%x\n", mm_sp_id);
free(parts_info);
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
- Issues 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() != 0) {
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 > FFA_SHARED_MM_BUFFER_SIZE)
return EFI_INVALID_PARAMETER;
/* Copy the data to the shared buffer */
virt_shared_buf = map_sysmem((phys_addr_t)FFA_SHARED_MM_BUFFER_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();
indent, I think?
#endif
/* Announce there is data in the shared buffer */
ffa_ret = ffa_notify_mm_sp();
switch (ffa_ret) {
case 0:
{
Don't we normally put the { at the end of the previous line?
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;
+} +#endif
+/**
- select_ffa_mm_comms() - checks FF-A support availability
- Making sure FF-A is compiled in. If that's the case try to make sure
- the FF-A bus is probed successfully which means FF-A communication
- with secure world works and ready to be used.
- Return:
- 0: FF-A ready for use. Otherwise, failure
- */
+static efi_status_t select_ffa_mm_comms(void) +{
efi_status_t ret = EFI_UNSUPPORTED;
if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) {
struct udevice *dev = NULL;
uclass_get_device_by_name(UCLASS_FFA, "arm_ffa", &dev);
if (!dev) {
log_err("EFI: Cannot find FF-A bus device, cannot select FF-A comms\n");
ret = EFI_NOT_READY;
} else {
ret = EFI_SUCCESS;
}
}
return ret;
+}
/**
- mm_communicate() - Adjust the cmonnucation buffer to StandAlonneMM and send
speeding
- mm_communicate() - Adjust the communication buffer to the MM SP and send
- it to OP-TEE
- @comm_buf: locally allocted communcation buffer
spelling
- @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 (u8 *comm_buf, efi_uintn_t dsize) @@ -162,7 +454,17 @@ static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) mm_hdr = (struct efi_mm_communicate_header *)comm_buf; var_hdr = (struct smm_variable_communicate_header *)mm_hdr->data;
smm_var_comms_hdr would be a lot shorter!
ret = optee_mm_communicate(comm_buf, dsize);
ret = select_ffa_mm_comms();
if (ret != EFI_SUCCESS) {
+#if (IS_ENABLED(CONFIG_OPTEE))
ret = optee_mm_communicate(comm_buf, dsize);
+#endif
} else {
+#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
ret = ffa_mm_communicate(comm_buf, dsize);
+#endif
}
if (ret != EFI_SUCCESS) { log_err("%s failed!\n", __func__); return ret;
@@ -258,6 +560,13 @@ efi_status_t EFIAPI get_max_payload(efi_uintn_t *size) goto out; } *size = var_payload->size;
#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
if (*size > FFA_SHARED_MM_BUFFER_SIZE)
*size = FFA_SHARED_MM_BUFFER_SIZE - MM_COMMUNICATE_HEADER_SIZE -
MM_VARIABLE_COMMUNICATE_SIZE;
#endif
/* * There seems to be a bug in EDK2 miscalculating the boundaries and * size checks, so deduct 2 more bytes to fulfill this requirement. Fix
@@ -697,7 +1006,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); /*
-- 2.25.1
Regards, Simonn

On Fri, Mar 10, 2023 at 12:50:01PM -0800, Simon Glass wrote:
Hi Simon,
Hi Abdellatif,
On Fri, 10 Mar 2023 at 06:11, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
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.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Signed-off-by: Gowtham Suresh Kumar gowtham.sureshkumar@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v9: align how FF-A is used with FF-A discovery through DM
v8:
- isolate the compilation choices between FF-A and OP-TEE
- update partition_info_get() second argument to be an SP count
- pass NULL device pointer to the FF-A bus discovery and operations
v7:
- set the MM door bell event to use 64-bit direct messaging
- issue a compile time error when one of these macros are not found : FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_OFFSET, FFA_SHARED_MM_BUFFER_ADDR
- make mm_sp_svc_uuid static
- replace EINVAL with ENOMEM in ffa_discover_mm_sp_id() when calloc() fails
- improve use of unmap_sysmem() in ffa_mm_communicate()
v6:
- add FF-A runtime discovery at MM communication level
- drop EFI runtime support for FF-A MM communication
- revert the changes in include/mm_communication.h for efi_mm_communicate_header and smm_variable_access structures
v4:
- use the new FF-A driver interfaces
- discover MM partitions at runtime
- copy FF-A driver private data to EFI runtime section at ExitBootServices()
- drop use of FFA_ERR_STAT_SUCCESS error code
- replace EFI_BUFFER_TOO_SMALL with EFI_OUT_OF_RESOURCES in ffa_mm_communicate(). No need for efi_memcpy_runtime() anymore
- revert the error log in mm_communicate() in case of failure
- remove packed attribute from efi_mm_communicate_header and smm_variable_communicate_header
v2:
- set default values to 0 for FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_ADDR and MM_SP_UUID_DATA and add warnings
v1:
- introduce FF-A MM communication
include/mm_communication.h | 6 + lib/efi_loader/Kconfig | 14 +- lib/efi_loader/efi_variable_tee.c | 317 +++++++++++++++++++++++++++++- 3 files changed, 331 insertions(+), 6 deletions(-)
diff --git a/include/mm_communication.h b/include/mm_communication.h index e65fbde60d..87509fec3f 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. sughosh.ganu@linaro.org
- Copyright (C) 2020 Linaro Ltd. ilias.apalodimas@linaro.org
- Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
- Authors:
*/
Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
#ifndef _MM_COMMUNICATION_H_ @@ -13,6 +16,9 @@
#include <part_efi.h>
+/* MM service UUID string (big-endian format). This UUID is common across all MM SPs */ +#define MM_SP_UUID "33d532ed-e699-0942-c09c-a798d9cd722d"
/*
- Interface to the pseudo Trusted Application (TA), which provides a
- communication channel with the Standalone MM (Management Mode)
diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index c5835e6ef6..08a6b84101 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -55,13 +55,23 @@ 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"
depends on OPTEE
bool "UEFI variables storage service via the trusted world"
depends on OPTEE || ARM_FFA_TRANSPORT 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 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 dfef18435d..86207f1ff2 100644 --- a/lib/efi_loader/efi_variable_tee.c +++ b/lib/efi_loader/efi_variable_tee.c @@ -4,9 +4,14 @@
- Copyright (C) 2019 Linaro Ltd. sughosh.ganu@linaro.org
- Copyright (C) 2019 Linaro Ltd. ilias.apalodimas@linaro.org
- Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
- Authors:
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#include <common.h> +#include <dm.h> #include <efi.h> #include <efi_api.h> #include <efi_loader.h> @@ -15,6 +20,36 @@ #include <malloc.h> #include <mm_communication.h>
+#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
Please avoid excluding code unless you need to
All FF-A code in this file needs to be excluded from the build when FF-A is off. IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT) is needed.
Ilias could provide more details about the Optee ifdefs (which are needed as well).
+#include <arm_ffa.h> +#include <cpu_func.h> +#include <mapmem.h>
+#ifndef FFA_SHARED_MM_BUFFER_SIZE +#error "FFA_SHARED_MM_BUFFER_SIZE must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_SIZE 0
Shouldn't that info be in the DT?
The shared MM buffer is a software feature and belongs to the MM protocol (a higher level than FF-A). It doesn't make sense to describe this in the DT.
+#endif
+#ifndef FFA_SHARED_MM_BUFFER_OFFSET +#error "FFA_SHARED_MM_BUFFER_OFFSET must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_OFFSET 0 +#endif
+#ifndef FFA_SHARED_MM_BUFFER_ADDR +#error "FFA_SHARED_MM_BUFFER_ADDR must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_ADDR 0 +#endif
+/* MM return codes */ +#define MM_SUCCESS (0)
+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 */ static efi_uintn_t max_payload_size; /* func + data */ @@ -24,6 +59,7 @@ struct mm_connection { u32 session; };
+#if (IS_ENABLED(CONFIG_OPTEE))
same here
Optee code in this file is only valid when CONFIG_OPTEE is turned on. We don't need to compile a code that's not used.
/**
- get_connection() - Retrieve OP-TEE session for a specific UUID.
@@ -143,13 +179,269 @@ static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize)
return ret;
} +#endif
+#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
and here
+/**
- ffa_notify_mm_sp() - Announce there is data in the shared buffer
- Notifies 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 = -1;
struct udevice *ffa_dev = NULL;
struct ffa_bus_ops *ffa_ops = NULL;
uclass_get_device_by_name(UCLASS_FFA, "arm_ffa", &ffa_dev);
uclass_first_device()
if (!ffa_dev) {
log_err("EFI: Cannot find FF-A bus device, notify MM SP failure\n");
return -ENODEV;
}
ffa_ops = (struct ffa_bus_ops *)ffa_bus_get_ops(ffa_dev);
if (!ffa_ops) {
log_err("EFI: Invalid FF-A ops, notify MM SP failure\n");
return -EINVAL;
}
msg.data0 = FFA_SHARED_MM_BUFFER_OFFSET; /* x3 */
ret = ffa_ops->sync_send_receive(ffa_dev, mm_sp_id, &msg, 1);
if (ret != 0)
return ret;
sp_event_ret = msg.data0; /* x3 */
if (sp_event_ret == MM_SUCCESS)
return 0;
/*
* Failure to notify the MM SP
*/
/* Failure ... */
return -EACCES;
+}
+/**
- 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_info *parts_info;
struct udevice *ffa_dev = NULL;
struct ffa_bus_ops *ffa_ops = NULL;
uclass_get_device_by_name(UCLASS_FFA, "arm_ffa", &ffa_dev);
if (!ffa_dev) {
log_err("EFI: Cannot find FF-A bus device, MM SP discovery failure\n");
return -ENODEV;
}
ffa_ops = (struct ffa_bus_ops *)ffa_bus_get_ops(ffa_dev);
if (!ffa_ops) {
log_err("EFI: Invalid FF-A ops, MM SP discovery failure\n");
return -EINVAL;
}
/* Get from the driver the count of the SPs matching the UUID */
ret = ffa_ops->partition_info_get(ffa_dev, mm_sp_svc_uuid, &count, NULL);
if (ret != 0) {
log_err("EFI: Failure in querying SPs count (%d), MM SP discovery failure\n", ret);
return ret;
}
if (!count) {
log_info("EFI: No MM partition found\n");
return ret;
}
/* Pre-allocate a buffer to be filled by the driver with ffa_partition_info structs */
log_info("EFI: Pre-allocating %d partition(s) info structures\n", count);
parts_info = calloc(count, sizeof(*parts_info));
if (!parts_info)
return -ENOMEM;
/* Ask the driver to fill the buffer with the SPs info */
ret = ffa_ops->partition_info_get(ffa_dev, mm_sp_svc_uuid, &count, parts_info);
if (ret) {
log_err("EFI: Failure in querying SPs info (%d), MM SP discovery failure\n", ret);
free(parts_info);
return ret;
}
/* MM SPs found , use the first one */
mm_sp_id = parts_info[0].id;
log_info("EFI: MM partition ID 0x%x\n", mm_sp_id);
free(parts_info);
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
- Issues 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() != 0) {
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 > FFA_SHARED_MM_BUFFER_SIZE)
return EFI_INVALID_PARAMETER;
/* Copy the data to the shared buffer */
virt_shared_buf = map_sysmem((phys_addr_t)FFA_SHARED_MM_BUFFER_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();
indent, I think?
#endif
/* Announce there is data in the shared buffer */
ffa_ret = ffa_notify_mm_sp();
switch (ffa_ret) {
case 0:
{
Don't we normally put the { at the end of the previous line?
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;
+} +#endif
+/**
- select_ffa_mm_comms() - checks FF-A support availability
- Making sure FF-A is compiled in. If that's the case try to make sure
- the FF-A bus is probed successfully which means FF-A communication
- with secure world works and ready to be used.
- Return:
- 0: FF-A ready for use. Otherwise, failure
- */
+static efi_status_t select_ffa_mm_comms(void) +{
efi_status_t ret = EFI_UNSUPPORTED;
if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) {
struct udevice *dev = NULL;
uclass_get_device_by_name(UCLASS_FFA, "arm_ffa", &dev);
if (!dev) {
log_err("EFI: Cannot find FF-A bus device, cannot select FF-A comms\n");
ret = EFI_NOT_READY;
} else {
ret = EFI_SUCCESS;
}
}
return ret;
+}
/**
- mm_communicate() - Adjust the cmonnucation buffer to StandAlonneMM and send
speeding
- mm_communicate() - Adjust the communication buffer to the MM SP and send
- it to OP-TEE
- @comm_buf: locally allocted communcation buffer
spelling
- @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 (u8 *comm_buf, efi_uintn_t dsize) @@ -162,7 +454,17 @@ static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) mm_hdr = (struct efi_mm_communicate_header *)comm_buf; var_hdr = (struct smm_variable_communicate_header *)mm_hdr->data;
smm_var_comms_hdr would be a lot shorter!
Sorry, it's beyond the scope of the FF-A patchset. The FF-A patchset is not providing the smm_variable_communicate_header structure.
ret = optee_mm_communicate(comm_buf, dsize);
ret = select_ffa_mm_comms();
if (ret != EFI_SUCCESS) {
+#if (IS_ENABLED(CONFIG_OPTEE))
ret = optee_mm_communicate(comm_buf, dsize);
+#endif
} else {
+#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
ret = ffa_mm_communicate(comm_buf, dsize);
+#endif
}
if (ret != EFI_SUCCESS) { log_err("%s failed!\n", __func__); return ret;
@@ -258,6 +560,13 @@ efi_status_t EFIAPI get_max_payload(efi_uintn_t *size) goto out; } *size = var_payload->size;
#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
if (*size > FFA_SHARED_MM_BUFFER_SIZE)
*size = FFA_SHARED_MM_BUFFER_SIZE - MM_COMMUNICATE_HEADER_SIZE -
MM_VARIABLE_COMMUNICATE_SIZE;
#endif
/* * There seems to be a bug in EDK2 miscalculating the boundaries and * size checks, so deduct 2 more bytes to fulfill this requirement. Fix
@@ -697,7 +1006,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); /*
-- 2.25.1
Regards, Simonn

turn on EFI MM communication
On corstone1000 platform MM communication between u-boot and the secure world (Optee) is done using the FF-A bus.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
--- Changelog: ===============
v9: update copyright string
v8:
* drop OP-TEE configs from Corstone-1000 defconfig
v7:
* improve the definition of FFA_SHARED_MM_BUFFER_ADDR and FFA_SHARED_MM_BUFFER_OFFSET * update FFA_SHARED_MM_BUFFER_ADDR value
v6:
* corstone-1000: enable optee driver * corstone-1000: remove CONFIG_ARM_FFA_EFI_RUNTIME_MODE from the defconfig
v4:
* corstone-1000: turn on EFI MM communication
configs/corstone1000_defconfig | 2 ++ include/configs/corstone1000.h | 15 +++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-)
diff --git a/configs/corstone1000_defconfig b/configs/corstone1000_defconfig index 74250c7524..a7bfcbb2b9 100644 --- a/configs/corstone1000_defconfig +++ b/configs/corstone1000_defconfig @@ -52,3 +52,5 @@ CONFIG_DM_SERIAL=y CONFIG_USB=y CONFIG_USB_ISP1760=y CONFIG_ERRNO_STR=y +CONFIG_EFI_MM_COMM_TEE=y +CONFIG_ARM_FFA_TRANSPORT=y diff --git a/include/configs/corstone1000.h b/include/configs/corstone1000.h index 3347c11792..4ef1f05e40 100644 --- a/include/configs/corstone1000.h +++ b/include/configs/corstone1000.h @@ -1,9 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0+ */ /* - * (C) Copyright 2022 ARM Limited * (C) Copyright 2022 Linaro * Rui Miguel Silva rui.silva@linaro.org - * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com * * Configuration for Corstone1000. Parts were derived from other ARM * configurations. @@ -14,6 +16,15 @@
#include <linux/sizes.h>
+#define FFA_SHARED_MM_BUFFER_SIZE SZ_4K /* 4 KB */ + +/* + * shared buffer physical address used for communication between + * u-boot and the MM SP + */ +#define FFA_SHARED_MM_BUFFER_ADDR 0x02000000UL +#define FFA_SHARED_MM_BUFFER_OFFSET 0 + #define V2M_BASE 0x80000000
#define CFG_PL011_CLOCK 50000000

Hi Abdellatif,
On Fri, 10 Mar 2023 at 06:12, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
Adding support for Arm FF-A v1.0 (Arm Firmware Framework for Armv8-A) [A].
FF-A describes interfaces (ABIs) that standardize communication between the Secure World and Normal World. These interfaces enable a pair of software sandboxes to communicate with each other. A sandbox aka partition could be a VM in the Normal or Secure world, an application in S-EL0, or a Trusted OS in S-EL1.
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.
=> dm tree
Class Index Probed Driver Name
... firmware 0 [ + ] psci |-- psci ffa 0 [ ] arm_ffa | `-- arm_ffa ...
Clients are able to probe then use the FF-A bus by calling the DM class searching APIs (e.g: uclass_get_device_by_name).
This implementation of the specification provides support for Aarch64.
The FF-A driver uses the SMC ABIs defined by the FF-A specification to:
- Discover the presence of secure partitions (SPs) of interest - Access an SP's service through communication protocols e.g. EFI MM communication protocol
The FF-A support provides the following features:
- Being generic by design and can be used by any Arm 64-bit platform - The FF-A core driver - The driver provides driver operations to be used by clients to access the FF-A bus - FF-A driver can be compiled and used without EFI - Support for SMCCCv1.2 x0-x17 registers - Support for SMC32 calling convention - Support for 32-bit and 64-bit FF-A direct messaging - A new command called armffa is provided as an example of how to access the FF-A bus - An FF-A Sandbox driver is provided with test cases - Support for FF-A MM communication (compatible with EFI boot time) - Enabling FF-A and MM communication in Corstone1000 platform as a use case
For more details about the FF-A core driver please refer to [B] and refer to [C] for how to use the armffa command.
Please find at [D] an example of the expected boot logs when enabling FF-A support for a platform. In this example the platform is Corstone1000. But it can be any Arm 64-bit platform.
Changelog of the major changes:
v9:
- integrate the FF-A bus discovery in the DM and use ARM_SMCCC_FEATURES for binding
- align FF-A sandbox driver with FF-A discovery through DM
- use DM class APIs to probe and interact with the FF-A bus (in FF-A MM comms, armffa command, sandbox tests)
- add documentation for the armffa command: doc/usage/cmd/armffa.rst
- introduce testcase for uuid_str_to_le_bin
This version looks a lot better. Things I noticed:
- fix up uclass to use operations - tidy up emulator binding to use DT
Both should be fairly easy.
Other than that I just have code-style nits.
Regards, Simon

Adding support for Arm FF-A v1.0 (Arm Firmware Framework for Armv8-A) [A].
FF-A describes interfaces (ABIs) that standardize communication between the Secure World and Normal World. These interfaces enable a pair of software sandboxes to communicate with each other. A sandbox aka partition could be a VM in the Normal or Secure world, an application in S-EL0, or a Trusted OS in S-EL1.
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.
=> dm tree
Class Index Probed Driver Name ----------------------------------------------------------- ... firmware 0 [ + ] psci |-- psci ffa 0 [ ] arm_ffa | `-- arm_ffa ...
Clients are able to probe then use the FF-A bus by calling the DM class searching APIs (e.g: uclass_get_device_by_name).
This implementation of the specification provides support for Aarch64.
The FF-A driver uses the SMC ABIs defined by the FF-A specification to:
- Discover the presence of secure partitions (SPs) of interest - Access an SP's service through communication protocols e.g. EFI MM communication protocol
The FF-A support provides the following features:
- Being generic by design and can be used by any Arm 64-bit platform - A Uclass driver providing generic FF-A methods and driver operations - An Arm FF-A device driver - FF-A support can be compiled and used without EFI - Support for SMCCCv1.2 x0-x17 registers - Support for SMC32 calling convention - Support for 32-bit and 64-bit FF-A direct messaging - A new command called armffa is provided as an example of how to access the FF-A bus - Support for FF-A MM communication (compatible with EFI boot time) - Enabling FF-A and MM communication in Corstone1000 platform as a use case - A sandbox emulator for Arm FF-A - An FF-A sandbox device driver - Sandbox FF-A test cases
For more details about the FF-A core driver please refer to [B] and refer to [C] for how to use the armffa command.
Please find at [D] an example of the expected boot logs when enabling FF-A support for a platform. In this example the platform is Corstone1000. But it can be any Arm 64-bit platform.
Changelog of the major changes: ===========================
v10:
* provide the FF-A driver operations through the Uclass (arm-ffa-uclass.c) * move the generic FF-A methods to the Uclass * keep Arm specific methods in the Arm driver (arm-ffa.c renamed from core.c) * split the FF-A sandbox support into an emulator (ffa-emul-uclass.c) and a driver (sandbox_ffa.c) * use the FF-A driver Uclass operations by clients (armffa command, tests, MM comms) * use uclass_first_device to search and probe the FF-A device (whether it is on Arm or on sandbox) * address nits
v9: [9]
* integrate the FF-A bus discovery in the DM and use ARM_SMCCC_FEATURES for binding * align FF-A sandbox driver with FF-A discovery through DM * use DM class APIs to probe and interact with the FF-A bus (in FF-A MM comms, armffa command, sandbox tests) * add documentation for the armffa command: doc/usage/cmd/armffa.rst * introduce testcase for uuid_str_to_le_bin
v8: [8]
* pass the FF-A bus device to the bus operations * isolate the compilation choices between FF-A and OP-TEE * drop OP-TEE configs from Corstone-1000 defconfig * make ffa_get_partitions_info() second argument to be an SP count in both modes
v7: [7]
* add support for 32-bit direct messaging (now we have 32-bit and 64-bit support) * set the MM door bell event to use 64-bit direct messaging * issue a compile time error when one of these macros are not found : FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_OFFSET, FFA_SHARED_MM_BUFFER_ADDR * make uuid_str_to_le_bin() implementation similar to uuid_str_to_bin() by using same APIs
v6: [6]
* remove clearing x0-x17 registers after SMC calls * drop use of EFI runtime support for FF-A (We decided with Linaro to add this later) * drop discovery from initcalls (discovery will be on demand by FF-A users) * add FF-A runtime discovery at MM communication level * update the documentation and move it to doc/arch/arm64.ffa.rst
v5: [5]
* move changelogs in each commit to the changes section
v4: [4]
* add FF-A support README (doc/README.ffa.drv) * improving error handling by mapping the FF-A errors to standard errors and logs * replacing panics with an error log * align sandbox driver and tests with the new FF-A driver interfaces and new way of error handling * use the new FF-A driver interfaces for MM communication * discover MM partitions at runtime * copy FF-A driver private data to EFI runtime section at ExitBootServices() * moving the FF-A driver work to drivers/firmware/arm-ffa * improving features discovery in FFA_FEATURES * add remove/unbind functions to the FF-A core device * improve how the driver behaves when bus discovery is done more than once * move clearing x0-x17 registers code into a new macro like done in the linux kernel * enable EFI MM communication for the Corstone1000 platform
v3: [3]
* port x0-x17 registers support from linux kernel as defined by SMCCCv1.2 * align the interfaces of the u-boot FF-A driver with those in the linux FF-A driver * remove the FF-A helper layer * make the u-boot FF-A driver independent from EFI * provide an optional config that enables copying the driver data to EFI runtime section at ExitBootServices service * use 64-bit version of FFA_RXTX_MAP, FFA_MSG_SEND_DIRECT_{REQ, RESP} * update armffa command with the new driver interfaces
v2 [2]:
* make FF-A bus discoverable using device_{bind, probe} APIs * remove device tree support
v1 [1]:
* introduce FF-A bus driver with device tree support * introduce armffa command * introduce FF-A Sandbox driver * add FF-A Sandbox test cases * introduce FF-A MM communication
Cheers, Abdellatif
List of previous patches:
[1]: https://lore.kernel.org/all/20220329151659.16894-1-abdellatif.elkhlifi@arm.c... [2]: https://lore.kernel.org/all/20220415122803.16666-1-abdellatif.elkhlifi@arm.c... [3]: https://lore.kernel.org/all/20220801172053.20163-1-abdellatif.elkhlifi@arm.c... [4]: https://lore.kernel.org/all/20220926101723.9965-1-abdellatif.elkhlifi@arm.co... [5]: https://lore.kernel.org/all/20220926140827.15125-1-abdellatif.elkhlifi@arm.c... [6]: https://lore.kernel.org/all/20221013103857.614-1-abdellatif.elkhlifi@arm.com... [7]: https://lore.kernel.org/all/20221107192055.21669-1-abdellatif.elkhlifi@arm.c... [8]: https://lore.kernel.org/all/20221122131751.22747-1-abdellatif.elkhlifi@arm.c... [9]: https://lore.kernel.org/all/20230310141016.137986-1-abdellatif.elkhlifi@arm....
More details:
[A]: https://developer.arm.com/documentation/den0077/latest/ [B]: doc/arch/arm64.ffa.rst [C]: doc/usage/cmd/armffa.rst [D]: example of boot logs when enabling FF-A
``` U-Boot 2023.01 (Mar 27 2023 - 13:48:33 +0000) corstone1000 aarch64
DRAM: 2 GiB [FFA] trying FF-A framework discovery [FFA] Using Arm SMC for FF-A conduit [FFA] FF-A driver 1.0 FF-A framework 1.0 [FFA] Versions are compatible ... Hit any key to stop autoboot: 0 ... [FFA] endpoint ID is 0 [FFA] Using 1 4KB page(s) for RX/TX buffers size [FFA] RX buffer at virtual address 00000000fdf4f000 [FFA] TX buffer at virtual address 00000000fdf51000 [FFA] RX/TX buffers mapped [FFA] Reading partitions data from the RX buffer [FFA] Partition ID 8001 : info cached [FFA] Partition ID 8002 : info cached [FFA] Partition ID 8003 : info cached [FFA] 3 partition(s) found and cached [FFA] Preparing for checking partitions count [FFA] Searching partitions using the provided UUID [FFA] No partition found. Querying framework ... [FFA] Reading partitions data from the RX buffer [FFA] Number of partition(s) matching the UUID: 1 EFI: Pre-allocating 1 partition(s) info structures [FFA] Preparing for filling partitions info [FFA] Searching partitions using the provided UUID [FFA] Partition ID 8003 matches the provided UUID EFI: MM partition ID 0x8003 EFI: Corstone1000: Capsule shared buffer at 0x80000000 , size 8192 pages Booting /MemoryMapped(0x0,0x88200000,0xf00000) EFI stub: Booting Linux Kernel... EFI stub: Using DTB from configuration table EFI stub: Exiting boot services... [FFA] removing the device [FFA] unmapping RX/TX buffers [FFA] Freeing RX/TX buffers Booting Linux on physical CPU 0x0000000000 [0x411fd040] 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 ```
Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Rob Herring robh@kernel.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Achin Gupta achin.gupta@arm.com Cc: Drew Reed Drew.Reed@arm.com Cc: Xueliang Zhong Xueliang.Zhong@arm.com
Abdellatif El Khlifi (10): arm64: smccc: add support for SMCCCv1.2 x0-x17 registers lib: uuid: introduce uuid_str_to_le_bin function lib: uuid: introduce testcase for uuid_str_to_le_bin arm_ffa: introduce Arm FF-A support arm_ffa: introduce armffa command arm_ffa: introduce sandbox FF-A support arm_ffa: introduce sandbox test cases for UCLASS_FFA arm_ffa: introduce armffa command Sandbox test arm_ffa: efi: introduce FF-A MM communication arm_ffa: efi: corstone1000: enable MM communication
MAINTAINERS | 18 + arch/arm/cpu/armv8/smccc-call.S | 57 +- arch/arm/lib/asm-offsets.c | 16 + arch/sandbox/dts/sandbox.dtsi | 8 + arch/sandbox/dts/test.dts | 8 + arch/sandbox/include/asm/sandbox_arm_ffa.h | 68 + .../include/asm/sandbox_arm_ffa_priv.h | 133 ++ cmd/Kconfig | 10 + cmd/Makefile | 2 + cmd/armffa.c | 238 ++++ configs/corstone1000_defconfig | 2 + configs/sandbox64_defconfig | 1 + configs/sandbox_defconfig | 1 + doc/arch/arm64.ffa.rst | 292 ++++ doc/arch/index.rst | 1 + doc/arch/sandbox/sandbox.rst | 1 + doc/usage/cmd/armffa.rst | 107 ++ doc/usage/index.rst | 1 + drivers/Makefile | 1 + drivers/firmware/Kconfig | 1 + drivers/firmware/arm-ffa/Kconfig | 42 + drivers/firmware/arm-ffa/Makefile | 16 + drivers/firmware/arm-ffa/arm-ffa-uclass.c | 1258 +++++++++++++++++ drivers/firmware/arm-ffa/arm-ffa.c | 148 ++ drivers/firmware/arm-ffa/ffa-emul-uclass.c | 732 ++++++++++ drivers/firmware/arm-ffa/sandbox_ffa.c | 108 ++ include/arm_ffa.h | 269 ++++ include/arm_ffa_priv.h | 254 ++++ include/configs/corstone1000.h | 15 +- include/dm/uclass-id.h | 7 + include/linux/arm-smccc.h | 45 + include/mm_communication.h | 6 + include/uuid.h | 15 + lib/efi_loader/Kconfig | 14 +- lib/efi_loader/efi_variable_tee.c | 300 +++- lib/uuid.c | 48 + test/cmd/Makefile | 2 + test/cmd/armffa.c | 39 + test/dm/Makefile | 2 + test/dm/ffa.c | 357 +++++ test/lib/Makefile | 1 + test/lib/uuid.c | 44 + 42 files changed, 4679 insertions(+), 9 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 cmd/armffa.c create mode 100644 doc/arch/arm64.ffa.rst create mode 100644 doc/usage/cmd/armffa.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/ffa-emul-uclass.c create mode 100644 drivers/firmware/arm-ffa/sandbox_ffa.c create mode 100644 include/arm_ffa.h create mode 100644 include/arm_ffa_priv.h create mode 100644 test/cmd/armffa.c create mode 100644 test/dm/ffa.c create mode 100644 test/lib/uuid.c

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 abdellatif.elkhlifi@arm.com Reviewed-by: Jens Wiklander jens.wiklander@linaro.org Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org
--- Changelog: ===============
v9:
* update the copyright string
v7:
* improve indentation of ARM_SMCCC_1_2_REGS_Xn_OFFS
v4:
* rename the commit title and improve description new commit title: the current
v3:
* port x0-x17 registers support from linux kernel as defined by SMCCCv1.2 commit title: arm64: smccc: add Xn registers support used by SMC calls
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 dc92b28777..93f66d3366 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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com +*/ #include <linux/linkage.h> #include <linux/arm-smccc.h> #include <generated/asm-offsets.h> @@ -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 6de0ce9152..181a8ac4c2 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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> @@ -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 e1d09884a1..f44e9e8f93 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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */ #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

On Wed, 29 Mar 2023 at 05:12, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
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
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Reviewed-by: Jens Wiklander jens.wiklander@linaro.org Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org
Changelog:
v9:
- update the copyright string
v7:
- improve indentation of ARM_SMCCC_1_2_REGS_Xn_OFFS
v4:
- rename the commit title and improve description new commit title: the current
v3:
- port x0-x17 registers support from linux kernel as defined by SMCCCv1.2 commit title: arm64: smccc: add Xn registers support used by SMC calls
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(-)
Reviewed-by: Simon Glass sjg@chromium.org

convert UUID string to little endian binary data
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
--- Changelog: ===============
v9:
* add a full function prototype description in uuid.h
v8:
* use simple_strtoull() in uuid_str_to_le_bin() to support 32-bit platforms
v7:
* rename be_uuid_str_to_le_bin() to uuid_str_to_le_bin() * make uuid_str_to_le_bin() implementation similar to uuid_str_to_bin() by using same APIs
v4:
* rename ffa_uuid_str_to_bin to be_uuid_str_to_le_bin and put in a standalone commit (the current)
v3:
* introduce ffa_uuid_str_to_bin (provided by arm_ffa: introduce Arm FF-A low-level driver)
include/uuid.h | 15 +++++++++++++++ lib/uuid.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+)
diff --git a/include/uuid.h b/include/uuid.h index 4a4883d3b5..89b93e642b 100644 --- a/include/uuid.h +++ b/include/uuid.h @@ -2,6 +2,10 @@ /* * Copyright (C) 2014 Samsung Electronics * Przemyslaw Marczak p.marczak@samsung.com + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */ #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 96e1af3c8b..45f325d964 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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> @@ -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. *

provide a test case
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Simon Glass sjg@chromium.org --- MAINTAINERS | 5 +++++ test/lib/Makefile | 1 + test/lib/uuid.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+) create mode 100644 test/lib/uuid.c
diff --git a/MAINTAINERS b/MAINTAINERS index 91d40ea4b6..dc3af17e61 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1601,3 +1601,8 @@ S: Maintained F: arch/arm/dts/ls1021a-twr-u-boot.dtsi F: drivers/crypto/fsl/ F: include/fsl_sec.h + +UUID testing +M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com +S: Maintained +F: test/lib/uuid.c diff --git a/test/lib/Makefile b/test/lib/Makefile index e0bd9e04e8..e75a263e6a 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 0000000000..6f24939de4 --- /dev/null +++ b/test/lib/uuid.c @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Functional tests for UCLASS_FFA class + * + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <uuid.h> +#include <test/lib.h> +#include <test/test.h> +#include <test/ut.h> + +/* 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}; + int cnt; + + ut_assertok(uuid_str_to_le_bin(uuid_str, ret_uuid_bin)); + + for (cnt = 0; cnt < UUID_SIZE; cnt++) + ut_asserteq(ref_uuid_bin[cnt], ret_uuid_bin[cnt]); + + return 0; +} + +LIB_TEST(lib_test_uuid_to_le, 0);

On Wed, 29 Mar 2023 at 05:12, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
provide a test case
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Simon Glass sjg@chromium.org
MAINTAINERS | 5 +++++ test/lib/Makefile | 1 + test/lib/uuid.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+) create mode 100644 test/lib/uuid.c
Still this is missing a change log. Please can you check your process. Do you use patman?
Can you use ut_asserteq_mem()? If not, please add a comment as to why that doesn't work, so it is clear.
Regards, SImon

On Thu, Mar 30, 2023 at 09:02:18AM +1300, Simon Glass wrote:
On Wed, 29 Mar 2023 at 05:12, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
provide a test case
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Simon Glass sjg@chromium.org
MAINTAINERS | 5 +++++ test/lib/Makefile | 1 + test/lib/uuid.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+) create mode 100644 test/lib/uuid.c
Still this is missing a change log. Please can you check your process.
This commit has been introduced in v9 patchset. No change made in the v10. In v11 I'll add a changelog reflecting the use of ut_asserteq_mem().
Do you use patman?
No, I'm planning to learn about it.
Can you use ut_asserteq_mem()? If not, please add a comment as to why that doesn't work, so it is clear.
Thanks. It works and will be included in v11. Could you please provide a feedback for the other commits so I can address everything in one go in patchset v11 ?
Cheers Abdellatif
Regards, SImon

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 abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
--- Changelog: ===============
v10:
* provide the driver operations through the Uclass * move the generic FF-A methods to the Uclass * keep Arm specific methods in the Arm driver (arm-ffa.c) * rename core.c to arm-ffa.c * address nits
v9:
* integrate the FF-A bus discovery in the DM and use ARM_SMCCC_FEATURES for binding
v8:
* make ffa_get_partitions_info() second argument to be an SP count in both modes * update ffa_bus_prvdata_get() to return a pointer rather than a pointer address * remove packing from ffa_partition_info and ffa_send_direct_data structures * pass the FF-A bus device to the bus operations
v7:
* add support for 32-bit direct messaging * rename be_uuid_str_to_le_bin() to uuid_str_to_le_bin() * improve the declaration of error handling mapping * stating in doc/arch/arm64.ffa.rst that EFI runtime is not supported
v6:
* drop use of EFI runtime support (We decided with Linaro to add this later) * drop discovery from initcalls (discovery will be on demand by FF-A users) * set the alignment of the RX/TX buffers to the larger translation granule size * move FF-A RX/TX buffers unmapping at ExitBootServices() to a separate commit * update the documentation and move it to doc/arch/arm64.ffa.rst
v4:
* add doc/README.ffa.drv * moving the FF-A driver work to drivers/firmware/arm-ffa * use less #ifdefs in lib/efi_loader/efi_boottime.c and replace #if defined by #if CONFIG_IS_ENABLED * improving error handling by mapping the FF-A errors to standard errors and logs * replacing panics with an error log and returning an error code * improving features discovery in FFA_FEATURES by introducing rxtx_min_pages private data field * add ffa_remove and ffa_unbind functions * improve how the driver behaves when bus discovery is done more than once
v3:
* align the interfaces of the U-Boot FF-A driver with those in the linux FF-A driver * remove the FF-A helper layer * make the U-Boot FF-A driver independent from EFI * provide an optional config that enables copying the driver data to EFI runtime section at ExitBootServices service * use 64-bit version of FFA_RXTX_MAP, FFA_MSG_SEND_DIRECT_{REQ, RESP}
v2:
* make FF-A bus discoverable using device_{bind, probe} APIs * remove device tree support
v1:
* introduce FF-A bus driver with device tree support
MAINTAINERS | 8 + doc/arch/arm64.ffa.rst | 279 ++++ 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 | 1258 +++++++++++++++++ drivers/firmware/arm-ffa/arm-ffa.c | 148 ++ .../firmware/arm-ffa/sandbox_arm_ffa_priv.h | 14 + include/arm_ffa.h | 269 ++++ include/arm_ffa_priv.h | 254 ++++ include/dm/uclass-id.h | 6 + 13 files changed, 2283 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 dc3af17e61..62c30184bb 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 abdellatif.elkhlifi@arm.com +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 sbabic@denx.de M: Fabio Estevam festevam@gmail.com diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst new file mode 100644 index 0000000000..ddf6435402 --- /dev/null +++ b/doc/arch/arm64.ffa.rst @@ -0,0 +1,279 @@ +.. 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 sandboxes to +communicate with each other. A sandbox aka 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 is 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 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 +specification v1.0 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. + +For more details please refer to the FF-A v1.0 spec: +https://documentation-service.arm.com/static/5fb7e8a6ca04df4095c1d65e?token= + +Hypervisors are supported if they are configured to trap SMC calls. + +The FF-A support uses 64-bit registers as per SMCCCv1.2 specification. + +For more details please refer to the SMC Calling Convention v1.2 spec: +https://documentation-service.arm.com/static/5f8edaeff86e16515cdbe4c6?token= + +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 ffa_bus_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(). +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: + + - allocating private data (priv) with devres + - updating priv with discovery information + - 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. + +FF-A device destruction +------------------------- + +When the FF-A device is removed by the DM, RX/TX buffers are automatically +unmapped and freed. Same happens when the device is unbound before being +removed first. + +For example, at EFI efi_exit_boot_services() active devices are automatically removed +by dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL). + +By consequence, the FF-A RX/TX are unmapped automatically. + +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 rxtx_unmap() and this is done +automatically at efi_exit_boot_services(). + +If RX/TX buffers created by U-Boot are not unmapped and by consequence becoming +available at EFI runtime, secure world will get confused about RX/TX buffers +ownership (U-Boot vs kernel). + +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. + +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 (Mar 07 2023 - 11:05:21 +0000) corstone1000 aarch64 + + DRAM: 2 GiB + [FFA] trying FF-A framework discovery + [FFA] Conduit is SMC + [FFA] FF-A driver 1.0 + FF-A framework 1.0 + [FFA] Versions are compatible + Core: 18 devices, 12 uclasses, devicetree: separate + MMC: + Loading Environment from nowhere... OK + ... + Hit any key to stop autoboot: 0 + Loading kernel from 0x083EE000 to memory ... + ... + [FFA] endpoint ID is 0 + [FFA] Using 1 4KB page(s) for RX/TX buffers size + [FFA] RX buffer at virtual address 00000000fdf4e000 + [FFA] TX buffer at virtual address 00000000fdf50000 + [FFA] RX/TX buffers mapped + [FFA] Reading partitions data from the RX buffer + [FFA] Partition ID 8001 : info cached + [FFA] Partition ID 8002 : info cached + [FFA] Partition ID 8003 : info cached + [FFA] 3 partition(s) found and cached + [FFA] Preparing for checking partitions count + [FFA] Searching partitions using the provided UUID + [FFA] No partition found. Querying framework ... + [FFA] Reading partitions data from the RX buffer + [FFA] Number of partition(s) matching the UUID: 1 + EFI: Pre-allocating 1 partition(s) info structures + [FFA] Preparing for filling partitions info + [FFA] Searching partitions using the provided UUID + [FFA] Partition ID 8003 matches the provided UUID + EFI: MM partition ID 0x8003 + EFI: Corstone1000: Capsule shared buffer at 0x80000000 , size 8192 pages + Booting /MemoryMapped(0x0,0x88200000,0xf00000) + EFI stub: Booting Linux Kernel... + EFI stub: Using DTB from configuration table + EFI stub: Exiting boot services... + [FFA] removing the device + [FFA] unmapping RX/TX buffers + [FFA] Freeing RX/TX buffers + Booting Linux on physical CPU 0x0000000000 [0x411fd040] + 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 + efi: EFI v2.100 by Das U-Boot + ... + +Contributors +------------ + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com diff --git a/doc/arch/index.rst b/doc/arch/index.rst index b8da4b8c8e..2f916f4026 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 58be410135..885fdef4dc 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -112,6 +112,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 eae1c8ddc9..8789b1ea14 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 0000000000..9200c8028b --- /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 0000000000..11b1766285 --- /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 open-source-office@arm.com +# +# Authors: +# Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + +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 0000000000..c387c26323 --- /dev/null +++ b/drivers/firmware/arm-ffa/arm-ffa-uclass.c @@ -0,0 +1,1258 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ +#include <common.h> +#include <arm_ffa.h> +#include <arm_ffa_priv.h> +#include <dm.h> +#include <log.h> +#include <malloc.h> +#include <string.h> +#include <uuid.h> +#include <asm/global_data.h> +#include <dm/device-internal.h> +#include <dm/devres.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* FF-A discovery information */ +struct ffa_discovery_info dscvry_info; + +/* 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 + * + * This function maps 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 + * + * This function maps 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("[FFA] %s\n", err_msg_map[abi_idx].err_str[err_idx]); + + return 0; +} + +/* FF-A ABIs implementation (U-Boot side) */ + +/** + * ffa_get_version_hdlr() - FFA_VERSION handler function + * + * This function implements 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 + */ +static int ffa_get_version_hdlr(void) +{ + u16 major, minor; + ffa_value_t res = {0}; + int ffa_errno; + + if (!dscvry_info.invoke_ffa_fn) + return -EINVAL; + + dscvry_info.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("[FFA] 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("[FFA] Versions are compatible\n"); + + dscvry_info.fwk_version = res.a0; + + return 0; + } + + log_err("[FFA] 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 + * + * This function implements 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 *priv = dev_get_priv(dev); + + priv->dscvry_info.invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_32(FFA_ID_GET), + }, &res); + + if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) { + priv->id = GET_SELF_ENDPOINT_ID((u32)res.a2); + log_info("[FFA] endpoint ID is %u\n", 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() - sets 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 + * + * This function sets the minimum number of pages + * in each of the RX/TX buffers in the private data structure + * + * Return: + * + * buf_4k_pages points to 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 *priv = dev_get_priv(dev); + + switch (prop_field) { + case RXTX_4K: + priv->pair.rxtx_min_pages = 1; + break; + case RXTX_16K: + priv->pair.rxtx_min_pages = 4; + break; + case RXTX_64K: + priv->pair.rxtx_min_pages = 16; + break; + default: + log_err("[FFA] 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 + * + * This function implements 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; + struct ffa_priv *priv = dev_get_priv(dev); + + priv->dscvry_info.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() - frees the RX/TX buffers + * @dev: The FF-A bus device + * + * This function frees the RX/TX buffers + */ +static void ffa_free_rxtx_buffers(struct udevice *dev) +{ + struct ffa_priv *priv = dev_get_priv(dev); + + log_info("[FFA] Freeing RX/TX buffers\n"); + + if (priv->pair.rxbuf) { + free(priv->pair.rxbuf); + priv->pair.rxbuf = NULL; + } + + if (priv->pair.txbuf) { + free(priv->pair.txbuf); + priv->pair.txbuf = NULL; + } +} + +/** + * ffa_alloc_rxtx_buffers() - allocates the RX/TX buffers + * @dev: The FF-A bus device + * + * This function is 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. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_alloc_rxtx_buffers(struct udevice *dev) +{ + u64 bytes; + struct ffa_priv *priv = dev_get_priv(dev); + + log_info("[FFA] Using %lu 4KB page(s) for RX/TX buffers size\n", + priv->pair.rxtx_min_pages); + + bytes = 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 + */ + + priv->pair.rxbuf = memalign(bytes, bytes); + if (!priv->pair.rxbuf) { + log_err("[FFA] failure to allocate RX buffer\n"); + return -ENOBUFS; + } + + log_info("[FFA] RX buffer at virtual address %p\n", priv->pair.rxbuf); + + priv->pair.txbuf = memalign(bytes, bytes); + if (!priv->pair.txbuf) { + free(priv->pair.rxbuf); + priv->pair.rxbuf = NULL; + log_err("[FFA] failure to allocate the TX buffer\n"); + return -ENOBUFS; + } + + log_info("[FFA] TX buffer at virtual address %p\n", priv->pair.txbuf); + + /* Make sure the buffers are cleared before use */ + memset(priv->pair.rxbuf, 0, bytes); + memset(priv->pair.txbuf, 0, bytes); + + return 0; +} + +/** + * ffa_map_rxtx_buffers_hdlr() - FFA_RXTX_MAP handler function + * @dev: The FF-A bus device + * + * This function implements 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 *priv = dev_get_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 + */ + + priv->dscvry_info.invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_64(FFA_RXTX_MAP), + .a1 = map_to_sysmem(priv->pair.txbuf), + .a2 = map_to_sysmem(priv->pair.rxbuf), + .a3 = priv->pair.rxtx_min_pages, + }, &res); + + if (res.a0 == FFA_SMC_32(FFA_SUCCESS)) { + log_info("[FFA] 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 + * + * 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_value_t res = {0}; + int ffa_errno; + struct ffa_priv *priv = NULL; + + if (!dev) + return -ENODEV; + + log_info("[FFA] unmapping RX/TX buffers\n"); + + priv = dev_get_priv(dev); + + priv->dscvry_info.invoke_ffa_fn((ffa_value_t){ + .a0 = FFA_SMC_32(FFA_RXTX_UNMAP), + .a1 = PREP_SELF_ENDPOINT_ID(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 + * + * This function invokes 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; + struct ffa_priv *priv = dev_get_priv(dev); + + priv->dscvry_info.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() - checks whether two given UUIDs are identical + * @uuid1: first UUID + * @uuid2: second UUID + * + * This function is 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() - reads queried partition data + * @dev: The FF-A bus device + * @count: The number of partitions queried + * @part_uuid: Pointer to the partition(s) UUID + * + * This function reads the partitions information + * returned by the FFA_PARTITION_INFO_GET and saves it in the private + * data structure. + * + * Return: + * + * The private data structure 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 *priv = dev_get_priv(dev); + + if (!count) { + log_err("[FFA] no partition detected\n"); + return -ENODATA; + } + + log_info("[FFA] Reading 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 = priv->pair.rxtx_min_pages * SZ_4K; + + if (data_bytes > buf_bytes) { + log_err("[FFA] partitions data size exceeds the RX buffer size:\n"); + log_err("[FFA] sizes in bytes: data %llu , RX buffer %llu\n", + data_bytes, + buf_bytes); + + return -ENOMEM; + } + + priv->partitions.descs = devm_kmalloc(dev, data_bytes, __GFP_ZERO); + if (!priv->partitions.descs) { + log_err("[FFA] cannot allocate partitions data buffer\n"); + return -ENOMEM; + } + + parts_info = priv->pair.rxbuf; + + for (desc_idx = 0 ; desc_idx < count ; desc_idx++) { + priv->partitions.descs[desc_idx].info = + parts_info[desc_idx]; + + log_info("[FFA] Partition ID %x : info cached\n", + priv->partitions.descs[desc_idx].info.id); + } + + priv->partitions.count = count; + + log_info("[FFA] %d 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 = 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 < priv->partitions.count; + cached_desc_idx++) { + /* Save the UUID */ + if (priv->partitions.descs[cached_desc_idx].info.id == + parts_info[rx_desc_idx].id) { + 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() - invokes FFA_PARTITION_INFO_GET and saves 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 + * + * This function executes the FFA_PARTITION_INFO_GET + * to query the partitions data. Then, it calls ffa_read_partitions_info + * to save the data in the private data structure. + * + * 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; + struct ffa_priv *priv = dev_get_priv(dev); + + /* + * 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; + } + + priv->dscvry_info.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("[FFA] 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 + * + * The passed arguments: + * Mode 1: When getting from the driver the number of + * secure partitions: + * @uuid_str: pointer to the UUID string + * @sp_count: pointer to the variable that contains the number of partitions + * The variable will be set by the driver + * @buffer: NULL + * + * Mode 2: When requesting the driver to return the + * partitions information: + * @dev: The FF-A bus device + * @uuid_str: pointer to the UUID string + * @sp_count: pointer to the variable that contains the number of empty partition descriptors + * The variable will be read by the driver + * @buffer: pointer to SPs information buffer + * (allocated by the client and contains empty @sp_count descriptors). + * The buffer will be filled by the driver + * + * This function queries the secure partition data from + * the private data structure. If not found, it invokes 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(). + * + * When invoked through a client request, ffa_get_partitions_info_hdlr() should be + * called twice. First call is to get from the driver the number of secure + * partitions (SPs) associated to a particular UUID. + * Then, the caller (client) allocates the buffer to host the SPs data and + * issues a 2nd call. Then, the driver fills the SPs data in the pre-allocated + * buffer. + * + * To achieve the mechanism described above, ffa_get_partitions_info_hdlr() uses the + * following functions: + * ffa_read_partitions_info + * ffa_query_partitions_info + * + * Return: + * + * @sp_count: When pointing to the number of partitions variable, the number is + * set by the driver. + * When pointing to the partitions information buffer size, the buffer will be + * filled by the driver. + * + * 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_info *buffer) +{ + /* + * fill_data: + * 0: return the SP count + * 1: fill SP data and return it to the caller + */ + bool fill_data = 0; + u32 desc_idx, client_desc_idx; + struct ffa_partition_uuid part_uuid = {0}; + u32 sp_found = 0; + struct ffa_priv *priv = NULL; + + if (!dev) + return -ENODEV; + + priv = dev_get_priv(dev); + + if (!priv->partitions.count || !priv->partitions.descs) { + log_err("[FFA] no partition installed\n"); + return -EINVAL; + } + + if (!uuid_str) { + log_err("[FFA] no UUID provided\n"); + return -EINVAL; + } + + if (!sp_count) { + log_err("[FFA] no size/count provided\n"); + return -EINVAL; + } + + if (uuid_str_to_le_bin(uuid_str, (unsigned char *)&part_uuid)) { + log_err("[FFA] invalid UUID\n"); + return -EINVAL; + } + + if (!buffer) { + /* Mode 1: getting the number of secure partitions */ + + fill_data = 0; + + log_info("[FFA] Preparing for checking partitions count\n"); + + } else if (*sp_count) { + /* Mode 2: retrieving the partitions information */ + + fill_data = 1; + + client_desc_idx = 0; + + log_info("[FFA] Preparing for filling partitions info\n"); + + } else { + log_err("[FFA] invalid function arguments provided\n"); + return -EINVAL; + } + + log_info("[FFA] Searching partitions using the provided UUID\n"); + + /* Search in the cached partitions */ + for (desc_idx = 0; + desc_idx < priv->partitions.count; + desc_idx++) { + if (ffa_uuid_are_identical(&priv->partitions.descs[desc_idx].sp_uuid, + &part_uuid)) { + log_info("[FFA] Partition ID %x matches the provided UUID\n", + priv->partitions.descs[desc_idx].info.id); + + sp_found++; + + if (fill_data) { + /* Trying to fill the partition info in the input buffer */ + + if (client_desc_idx < *sp_count) { + buffer[client_desc_idx++] = + priv->partitions.descs[desc_idx].info; + continue; + } + + log_err("[FFA] failed to fill client descriptor, buffer full\n"); + return -ENOBUFS; + } + } + } + + if (!sp_found) { + int ret; + + log_info("[FFA] No partition found. Querying framework ...\n"); + + ret = ffa_query_partitions_info(dev, &part_uuid, &sp_found); + + if (ret == 0) { + if (!fill_data) { + *sp_count = sp_found; + + log_info("[FFA] Number of partition(s) matching the UUID: %d\n", + sp_found); + } else { + /* + * If SPs data detected, they are already in the private data + * structure, retry searching SP data again to return them + * to the caller + */ + if (sp_found) + ret = ffa_get_partitions_info_hdlr(dev, uuid_str, sp_count, + buffer); + else + ret = -ENODATA; + } + } + + return ret; + } + + /* Partition(s) found */ + if (!fill_data) + *sp_count = sp_found; + + return 0; +} + +/** + * ffa_cache_partitions_info() - Queries and saves all secure partitions data + * @dev: The FF-A bus device + * + * This function invokes 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 the + * resident private data structure and we keep the UUID field empty + * (in FF-A 1.0 UUID is not provided by the partition descriptor) + * + * This function is 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 + * + * 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_value_t res = {0}; + int ffa_errno; + u64 req_mode, resp_mode; + struct ffa_priv *priv = NULL; + + if (!dev) + return -ENODEV; + + priv = dev_get_priv(dev); + + if (!priv || !priv->dscvry_info.invoke_ffa_fn) + return -EINVAL; + + /* No partition installed */ + if (!priv->partitions.count || !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); + } + + priv->dscvry_info.invoke_ffa_fn((ffa_value_t){ + .a0 = req_mode, + .a1 = PREP_SELF_ENDPOINT_ID(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)) + priv->dscvry_info.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 + * + * The passed arguments: + * Mode 1: When getting from the driver the number of + * secure partitions: + * @uuid_str: pointer to the UUID string + * @sp_count: pointer to the variable that contains the number of partitions + * The variable will be set by the driver + * @buffer: NULL + * + * Mode 2: When requesting the driver to return the + * partitions information: + * @dev: The FF-A bus device + * @uuid_str: pointer to the UUID string + * @sp_count: pointer to the variable that contains the number of empty partition descriptors + * The variable will be read by the driver + * @buffer: pointer to SPs information buffer + * (allocated by the client and contains empty @sp_count descriptors). + * The buffer will be filled by the driver + * + * Driver operation for FFA_PARTITION_INFO_GET. + * Please see ffa_get_partitions_info_hdlr() description for more details. + * + * Return: + * + * @sp_count: When pointing to the number of partitions variable, the number is + * set by the driver. + * When pointing to the partitions information buffer size, the buffer will be + * filled by the driver. + * + * 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_info *buffer) +{ + 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, buffer); +} + +/** + * 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); +} + +/* Implementation of FF-A bus discovery and initial setup */ + +/** + * ffa_try_discovery() - performs FF-A discovery + * Tries 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. + */ +bool ffa_try_discovery(void) +{ + int ret; + + log_info("[FFA] trying FF-A framework discovery\n"); + + ret = ffa_set_smc_conduit(); + if (ret) + return false; + + ret = ffa_get_version_hdlr(); + if (ret) + return false; + + return true; +} + +/** + * ffa_devm_alloc_priv() - allocate FF-A driver private data + * @dev: the FF-A bus device (arm_ffa) + * @priv: Pointer to the FF-A bus private data + * + * This function dynamically allocates with devres the private data structure + * which contains all the FF-A data. + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_devm_alloc_priv(struct udevice *dev, struct ffa_priv **priv) +{ + if (!priv) + return -EINVAL; + + *priv = devm_kmalloc(dev, sizeof(struct ffa_priv), __GFP_ZERO); + if (!(*priv)) { + log_err("[FFA] can not allocate FF-A main data structure\n"); + return -ENOMEM; + } + + return 0; +} + +/** + * 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: + * - allocating private data (priv) with devres + * - updating priv with discovery information + * - 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 the private data structure (priv). + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int ffa_do_probe(struct udevice *dev) +{ + int ret; + struct ffa_priv *priv; + + ret = ffa_devm_alloc_priv(dev, &priv); + if (ret) + return ret; + + /* register the structure with the DM */ + dev_set_priv(dev, priv); + + priv->dscvry_info = dscvry_info; + + 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; +} + +/** + * ffa_do_remove() - FF-A cleanup at device removal + * @dev: the FF-A device + * Making sure the RX/TX buffers are unmapped and freed when the device is removed. + * No need to free the private data structure because devres takes care of that. + * Return: + * + * 0 on success. + */ +int ffa_do_remove(struct udevice *dev) +{ + log_info("[FFA] removing the device\n"); + + ffa_unmap_rxtx_buffers_hdlr(dev); + dev_set_priv(dev, NULL); + + return 0; +} + +/** + * ffa_unbind() - FF-A cleanup at device unbinding + * @dev: the FF-A device + * Making sure the RX/TX buffers are unmapped and freed when the device is unbound. + * No need to free the private data structure because devres takes care of that. + * Return: + * + * 0 on success. + */ +int ffa_do_unbind(struct udevice *dev) +{ + struct ffa_priv *priv = dev_get_priv(dev); + + log_info("[FFA] unbinding the device\n"); + + if (priv) + ffa_unmap_rxtx_buffers_hdlr(dev); + + return 0; +} + +UCLASS_DRIVER(ffa) = { + .name = "ffa", + .id = UCLASS_FFA, +}; diff --git a/drivers/firmware/arm-ffa/arm-ffa.c b/drivers/firmware/arm-ffa/arm-ffa.c new file mode 100644 index 0000000000..cf19926e52 --- /dev/null +++ b/drivers/firmware/arm-ffa/arm-ffa.c @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <arm_ffa.h> +#include <arm_ffa_priv.h> +#include <dm.h> +#include <log.h> +#include <asm/global_data.h> +#include <dm/device-internal.h> +#include <linux/errno.h> + +DECLARE_GLOBAL_DATA_PTR; + +/** + * ffa_bus_is_supported() - FF-A discovery callback + * @invoke_fn: legacy SMC invoke function (not used) + * + * This function performs FF-A discovery by calling ffa_try_discovery(). + * 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 ffa_bus_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 ffa_try_discovery(); +} + +/** + * __arm_ffa_fn_smc() - 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 __arm_ffa_fn_smc(ffa_value_t args, ffa_value_t *res) +{ + arm_smccc_1_2_smc(&args, res); +} + +/** + * ffa_set_smc_conduit() - Set the SMC conduit + * + * Selects the SMC conduit by setting the FF-A ABI invoke function + * to the function executing the SMC call. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int ffa_set_smc_conduit(void) +{ + dscvry_info.invoke_ffa_fn = __arm_ffa_fn_smc; + log_info("[FFA] Using Arm SMC for FF-A conduit\n"); + + return 0; +} + +/** + * ffa_probe() - The Arm FF-A driver probe function + * @dev: the FF-A bus device (arm_ffa) + * + * Probing is done through ffa_do_probe() + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int ffa_probe(struct udevice *dev) +{ + return ffa_do_probe(dev); +} + +/** + * ffa_remove() - The driver remove function + * @dev: the arm_ffa device + * + * FF-A cleanup. + * + * Return: + * + * 0 on success. + */ +static int ffa_remove(struct udevice *dev) +{ + return ffa_do_remove(dev); +} + +/** + * ffa_unbind() - The driver unbind function + * @dev: the arm_ffa device + * + * FF-A cleanup. + * + * Return: + * + * 0 on success. + */ +static int ffa_unbind(struct udevice *dev) +{ + return ffa_do_unbind(dev); +} + +/* 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 = ffa_bus_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, + .probe = ffa_probe, + .remove = ffa_remove, + .unbind = ffa_unbind, + .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 0000000000..4338f9c9b1 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#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 0000000000..e327f786c3 --- /dev/null +++ b/include/arm_ffa.h @@ -0,0 +1,269 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __ARM_FFA_H +#define __ARM_FFA_H + +#include <linux/printk.h> + +/* + * 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_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 */ +}; + +/* FF-A discovery information */ +extern struct ffa_discovery_info dscvry_info; + +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_info *buffer); + 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_info *buffer); + +/** + * ffa_get_partitions_info_hdlr() - FFA_PARTITION_INFO_GET handler function + * + * The passed arguments: + * Mode 1: When getting from the driver the number of + * secure partitions: + * @uuid_str: pointer to the UUID string + * @sp_count: pointer to the variable that contains the number of partitions + * The variable will be set by the driver + * @buffer: NULL + * + * Mode 2: When requesting the driver to return the + * partitions information: + * @dev: The arm_ffa bus device + * @uuid_str: pointer to the UUID string + * @sp_count: pointer to the variable that contains the number of empty partition descriptors + * The variable will be read by the driver + * @buffer: pointer to SPs information buffer + * (allocated by the client and contains empty @sp_count descriptors). + * The buffer will be filled by the driver + * + * This function queries the secure partition data from + * the private data structure. If not found, it invokes 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(). + * + * When invoked through a client request, ffa_get_partitions_info_hdlr() should be + * called twice. First call is to get from the driver the number of secure + * partitions (SPs) associated to a particular UUID. + * Then, the caller (client) allocates the buffer to host the SPs data and + * issues a 2nd call. Then, the driver fills the SPs data in the pre-allocated + * buffer. + * + * To achieve the mechanism described above, ffa_get_partitions_info_hdlr() uses the + * following functions: + * ffa_read_partitions_info + * ffa_query_partitions_info + * + * Return: + * + * @sp_count: When pointing to the number of partitions variable, the number is + * set by the driver. + * When pointing to the partitions information buffer size, the buffer will be + * filled by the driver. + * + * 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_info *buffer); + +struct ffa_priv; + +/** + * 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: + * - allocating private data (priv) with devres + * - updating priv with discovery information + * - 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 the private data structure (priv). + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int ffa_do_probe(struct udevice *dev); + +/** + * ffa_do_remove() - FF-A cleanup at device removal + * @dev: the FF-A device + * Making sure the RX/TX buffers are unmapped and freed when the device is removed. + * No need to free the private data structure because devres takes care of that. + * Return: + * + * 0 on success. + */ +int ffa_do_remove(struct udevice *dev); + +/** + * ffa_unbind() - FF-A cleanup at device unbinding + * @dev: the FF-A device + * Making sure the RX/TX buffers are unmapped and freed when the device is unbound. + * No need to free the private data structure because devres takes care of that. + * Return: + * + * 0 on success. + */ +int ffa_do_unbind(struct udevice *dev); + +/** + * ffa_set_smc_conduit() - Set the SMC conduit + * + * Selects the SMC conduit by setting the FF-A ABI invoke function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int ffa_set_smc_conduit(void); + +/** + * ffa_try_discovery() - performs FF-A discovery + * Tries 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. + */ +bool ffa_try_discovery(void); + +#endif diff --git a/include/arm_ffa_priv.h b/include/arm_ffa_priv.h new file mode 100644 index 0000000000..a7b60b1aa2 --- /dev/null +++ b/include/arm_ffa_priv.h @@ -0,0 +1,254 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __ARM_FFA_PRV_H +#define __ARM_FFA_PRV_H + +#include <mapmem.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> + +/* This header is exclusively used by the FF-A Uclass and FF-A Arm driver */ + +/* 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 <linux/arm-smccc.h> +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) + +/* + * 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 */ +}; + +/** + * 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 - 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_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_discovery_info - discovery information + * @fwk_version: FF-A framework version + * @invoke_ffa_fn: The function executing the FF-A function (ABI) + */ +struct ffa_discovery_info { + u32 fwk_version; + invoke_ffa_fn_t invoke_ffa_fn; +}; + +/** + * struct ffa_priv - the driver private data structure + * + * @dscvry_info: Initial information discovered + * @ops: The driver operations structure + * @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. + * The data is dynamically allocated, managed by devres + * and registered with the DM. + */ +struct ffa_priv { + struct ffa_discovery_info dscvry_info; + u16 id; + struct ffa_partitions partitions; + struct ffa_rxtxpair pair; +}; +#endif diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 33e43c20db..df77c7da58 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -4,6 +4,11 @@ * * (C) Copyright 2012 * Pavel Herrmann morpheus.ibis@gmail.com + * + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#ifndef _DM_UCLASS_ID_H @@ -55,6 +60,7 @@ enum uclass_id { UCLASS_EFI_MEDIA, /* Devices provided by UEFI firmware */ UCLASS_ETH, /* Ethernet device */ UCLASS_ETH_PHY, /* Ethernet PHY device */ + UCLASS_FFA, /* Arm Firmware Framework for Armv8-A */ UCLASS_FIRMWARE, /* Firmware */ UCLASS_FPGA, /* FPGA device */ UCLASS_FUZZING_ENGINE, /* Fuzzing engine */

Hi Abdellatif,
On Wed, 29 Mar 2023 at 05:12, Abdellatif El Khlifi < abdellatif.elkhlifi@arm.com> wrote:
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].
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
Changelog:
v10:
- provide the driver operations through the Uclass
- move the generic FF-A methods to the Uclass
- keep Arm specific methods in the Arm driver (arm-ffa.c)
- rename core.c to arm-ffa.c
- address nits
The functions at the bottom like ffa_do_remove() should be changed to uclass methods.
E.g.
.pre_remove = ffa_do_remove, .pre_probe = ffa_do_probe .post_bind = ffa_do_unbind
You can make them static and they don't need to called from elsewhere. It is automatic.
- no need to check for !dev since this is the caller's responsibiliy - no need to check for !priv since this cannot happen
It looks like you are using 'priv' as per-device uclass data. So instead, please add something like:
per_device_auto = sizeof(struct ffa_priv)
in your uclass driver, then use dev_get_uclass_priv() instead of dev_get_priv().
You should use the var name uc_priv instead of priv, so it is clear that this relates to the uclass (i.e. is common across all devices in that uclass)
You should almost never need to set the priv data, since driver model handles this.
Regards, Simon

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].
[1]: doc/usage/cmd/armffa.rst
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
--- Changelog: ===============
v10:
* use the FF-A driver Uclass operations * use uclass_first_device() * address nits
v9:
* remove manual FF-A discovery and use DM * use DM class APIs to probe and interact with the FF-A bus * add doc/usage/cmd/armffa.rst
v8:
* update partition_info_get() second argument to be an SP count * pass NULL device pointer to the FF-A bus discovery and operations
v7:
* adapt do_ffa_dev_list() following the recent update on uclass_first_device/uclass_next_device functions (they return void now) * set armffa command to use 64-bit direct messaging
v4:
* remove pattern data in do_ffa_msg_send_direct_req
v3:
* use the new driver interfaces (partition_info_get, sync_send_receive) in armffa command
v2:
* replace use of ffa_helper_init_device function by ffa_helper_bus_discover
v1:
* introduce armffa command
MAINTAINERS | 2 + cmd/Kconfig | 10 ++ cmd/Makefile | 2 + cmd/armffa.c | 238 +++++++++++++++++++++++++++++++ doc/arch/arm64.ffa.rst | 7 + doc/usage/cmd/armffa.rst | 107 ++++++++++++++ doc/usage/index.rst | 1 + drivers/firmware/arm-ffa/Kconfig | 1 + 8 files changed, 368 insertions(+) create mode 100644 cmd/armffa.c create mode 100644 doc/usage/cmd/armffa.rst
diff --git a/MAINTAINERS b/MAINTAINERS index 62c30184bb..add208e4ef 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -269,7 +269,9 @@ F: configs/cortina_presidio-asic-pnand_defconfig ARM FF-A M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com S: Maintained +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: include/sandbox_arm_ffa.h diff --git a/cmd/Kconfig b/cmd/Kconfig index ba5ec69293..b814a20d8a 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -934,6 +934,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 d95833b2de..a1eb45f881 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -12,6 +12,8 @@ obj-y += panic.o obj-y += version.o
# command + +obj-$(CONFIG_CMD_ARMFFA) += armffa.o obj-$(CONFIG_CMD_ACPI) += acpi.o obj-$(CONFIG_CMD_ADDRMAP) += addrmap.o obj-$(CONFIG_CMD_AES) += aes.o diff --git a/cmd/armffa.c b/cmd/armffa.c new file mode 100644 index 0000000000..d983a23bbc --- /dev/null +++ b/cmd/armffa.c @@ -0,0 +1,238 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ +#include <common.h> +#include <arm_ffa.h> +#include <command.h> +#include <dm.h> +#include <mapmem.h> +#include <stdlib.h> +#include <asm/io.h> + +/** + * do_ffa_getpart() - implementation of the getpart subcommand + * @cmdtp: Command Table + * @flag: flags + * @argc: number of arguments + * @argv: arguments + * + * This function queries the secure partition information which the 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_info *parts_info; + u32 info_idx; + struct udevice *dev = NULL; + + if (argc != 1) + return -EINVAL; + + uclass_first_device(UCLASS_FFA, &dev); + if (!dev) { + log_err("[FFA][CMD] Cannot find FF-A bus device\n"); + return -ENODEV; + } + + /* Mode 1: getting the number of secure partitions */ + ret = ffa_partition_info_get(dev, argv[0], &count, NULL); + if (ret) { + log_err("[FFA][CMD] Failure in querying partitions count (error code: %d)\n", ret); + return ret; + } + + if (!count) { + log_info("[FFA][CMD] No secure partition found\n"); + return ret; + } + + /* + * Pre-allocate a buffer to be filled by the driver + * with ffa_partition_info structs + */ + + log_info("[FFA][CMD] Pre-allocating %d partition(s) info structures\n", count); + + parts_info = calloc(count, sizeof(struct ffa_partition_info)); + if (!parts_info) + return -EINVAL; + + /* Ask the driver to fill the buffer with the SPs info */ + + ret = ffa_partition_info_get(dev, argv[0], &count, parts_info); + if (ret) { + log_err("[FFA][CMD] Failure in querying partition(s) info (error code: %d)\n", ret); + free(parts_info); + return ret; + } + + /* SPs found , show the partition information */ + for (info_idx = 0; info_idx < count ; info_idx++) { + log_info("[FFA][CMD] Partition: id = 0x%x , exec_ctxt 0x%x , properties 0x%x\n", + parts_info[info_idx].id, + parts_info[info_idx].exec_ctxt, + parts_info[info_idx].properties); + } + + free(parts_info); + + return 0; +} + +/** + * do_ffa_ping() - implementation of the ping subcommand + * @cmdtp: Command Table + * @flag: flags + * @argc: number of arguments + * @argv: arguments + * + * This function sends data to the secure partition which the ID is provided + * as an argument. The function uses 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 + */ +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 = NULL; + + if (argc != 1) + return -EINVAL; + + errno = 0; + part_id = strtoul(argv[0], NULL, 16); + + if (errno) { + log_err("[FFA][CMD] Invalid partition ID\n"); + return -EINVAL; + } + + uclass_first_device(UCLASS_FFA, &dev); + if (!dev) { + log_err("[FFA][CMD] Cannot find FF-A bus device\n"); + return -ENODEV; + } + + ret = ffa_sync_send_receive(dev, part_id, &msg, 1); + if (!ret) { + u8 cnt; + + log_info("[FFA][CMD] SP response:\n[LSB]\n"); + for (cnt = 0; + cnt < sizeof(struct ffa_send_direct_data) / sizeof(u64); + cnt++) + log_info("[FFA][CMD] 0x%llx\n", ((u64 *)&msg)[cnt]); + } else { + log_err("[FFA][CMD] Sending direct request error (%d)\n", ret); + } + + return ret; +} + +/** + *do_ffa_devlist() - implementation of the devlist subcommand + * @cmdtp: [in] Command Table + * @flag: flags + * @argc: number of arguments + * @argv: arguments + * + * This function queries the device belonging to the UCLASS_FFA + * class. + * + * Return: + * + * CMD_RET_SUCCESS: on success, otherwise failure + */ +int do_ffa_devlist(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct udevice *dev = NULL; + + uclass_first_device(UCLASS_FFA, &dev); + if (!dev) { + log_err("[FFA][CMD] Cannot find FF-A bus device\n"); + return -ENODEV; + } + + log_info("[FFA][CMD] device name %s, dev %08x, driver name %s, ops %08x\n", + dev->name, + (u32)map_to_sysmem(dev), + dev->driver->name, + (u32)map_to_sysmem(dev->driver->ops)); + + return 0; +} + +static struct cmd_tbl armffa_commands[] = { + U_BOOT_CMD_MKENT(getpart, 1, 1, do_ffa_getpart, "", ""), + U_BOOT_CMD_MKENT(ping, 1, 1, do_ffa_ping, "", ""), + U_BOOT_CMD_MKENT(devlist, 0, 1, do_ffa_devlist, "", ""), +}; + +/** + * do_armffa() - the armffa command main function + * @cmdtp: Command Table + * @flag: flags + * @argc: number of arguments + * @argv: arguments + * + * This function identifies which armffa subcommand to run. + * Then, it makes sure the arm_ffa device is probed and + * ready for use. + * Then, it runs the subcommand. + * + * Return: + * + * CMD_RET_SUCCESS: on success, otherwise failure + */ +static int do_armffa(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct cmd_tbl *armffa_cmd; + int ret; + + if (argc < 2) + return CMD_RET_USAGE; + + armffa_cmd = find_cmd_tbl(argv[1], armffa_commands, ARRAY_SIZE(armffa_commands)); + + argc -= 2; + argv += 2; + + if (!armffa_cmd || argc > armffa_cmd->maxargs) + return CMD_RET_USAGE; + + ret = armffa_cmd->cmd(armffa_cmd, flag, argc, argv); + + return cmd_process_error(armffa_cmd, ret); +} + +U_BOOT_CMD(armffa, 4, 1, do_armffa, + "Arm FF-A operations test command", + "getpart <partition UUID>\n" + " - lists the partition(s) info\n" + "ping <partition ID>\n" + " - sends a data pattern to the specified partition\n" + "devlist\n" + " - displays information about the FF-A device/driver\n"); diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst index ddf6435402..5fedb0c255 100644 --- a/doc/arch/arm64.ffa.rst +++ b/doc/arch/arm64.ffa.rst @@ -218,6 +218,13 @@ The following features are provided:
- FF-A bus can be compiled and used without EFI
+The armffa command +----------------------------------- + +armffa is an implementation defined 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.rst + 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 0000000000..9bf59e393b --- /dev/null +++ b/doc/usage/cmd/armffa.rst @@ -0,0 +1,107 @@ +.. SPDX-License-Identifier: GPL-2.0+: + +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 with debug logs enabled. + +* ping + +:: + + corstone1000# armffa ping 0x8003 + [FFA][CMD] SP response: + [LSB] + [FFA][CMD] 0xfffffffe + [FFA][CMD] 0x0 + [FFA][CMD] 0x0 + [FFA][CMD] 0x0 + [FFA][CMD] 0x0 + +* ping (failure case) + +:: + + corstone1000# armffa ping 0x8009 + [FFA][CMD] Sending direct request error (-22) + Command 'ping' failed: Error -22 + +* getpart + +:: + + corstone1000# armffa getpart 33d532ed-e699-0942-c09c-a798d9cd722d + [FFA] Preparing for checking partitions count + [FFA] Searching partitions using the provided UUID + [FFA] No partition found. Querying framework ... + [FFA] Reading partitions data from the RX buffer + [FFA] Number of partition(s) matching the UUID: 1 + [FFA][CMD] Pre-allocating 1 partition(s) info structures + [FFA] Preparing for filling partitions info + [FFA] Searching partitions using the provided UUID + [FFA] Partition ID 8003 matches the provided UUID + [FFA][CMD] Partition: id = 0x8003 , exec_ctxt 0x1 , properties 0x3 + +* getpart (failure case) + +:: + + corstone1000# armffa getpart 33d532ed-e699-0942-c09c-a798d9cd7228 + [FFA] Preparing for checking partitions count + [FFA] Searching partitions using the provided UUID + [FFA] No partition found. Querying framework ... + [FFA] INVALID_PARAMETERS: Unrecognized UUID + [FFA][CMD] Failure in querying partitions count (error code: -22) + Command 'getpart' failed: Error -22 + +* devlist + +:: + + corstone1000# armffa devlist + [FFA][CMD] device name arm_ffa, dev fdf41c30, driver name arm_ffa, ops fffc0fc8 + +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 and a negative error code on failure. diff --git a/doc/usage/index.rst b/doc/usage/index.rst index bc85e1d49a..df107fb710 100644 --- a/doc/usage/index.rst +++ b/doc/usage/index.rst @@ -21,6 +21,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 9200c8028b..a7d5392859 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 select ARM_SMCCC select ARM_SMCCC_FEATURES + imply CMD_ARMFFA select LIB_UUID select DEVRES help

Hi Abdellatif,
On Wed, 29 Mar 2023 at 05:12, Abdellatif El Khlifi < abdellatif.elkhlifi@arm.com> wrote:
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].
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
Changelog:
v10:
- use the FF-A driver Uclass operations
- use uclass_first_device()
- address nits
v9:
- remove manual FF-A discovery and use DM
- use DM class APIs to probe and interact with the FF-A bus
- add doc/usage/cmd/armffa.rst
v8:
- update partition_info_get() second argument to be an SP count
- pass NULL device pointer to the FF-A bus discovery and operations
v7:
- adapt do_ffa_dev_list() following the recent update on uclass_first_device/uclass_next_device functions (they return void now)
- set armffa command to use 64-bit direct messaging
v4:
- remove pattern data in do_ffa_msg_send_direct_req
v3:
- use the new driver interfaces (partition_info_get, sync_send_receive) in armffa command
v2:
- replace use of ffa_helper_init_device function by
ffa_helper_bus_discover
v1:
- introduce armffa command
MAINTAINERS | 2 + cmd/Kconfig | 10 ++ cmd/Makefile | 2 + cmd/armffa.c | 238 +++++++++++++++++++++++++++++++ doc/arch/arm64.ffa.rst | 7 + doc/usage/cmd/armffa.rst | 107 ++++++++++++++ doc/usage/index.rst | 1 + drivers/firmware/arm-ffa/Kconfig | 1 + 8 files changed, 368 insertions(+) create mode 100644 cmd/armffa.c create mode 100644 doc/usage/cmd/armffa.rst
diff --git a/MAINTAINERS b/MAINTAINERS index 62c30184bb..add208e4ef 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -269,7 +269,9 @@ F: configs/cortina_presidio-asic-pnand_defconfig ARM FF-A M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com S: Maintained +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: include/sandbox_arm_ffa.h diff --git a/cmd/Kconfig b/cmd/Kconfig index ba5ec69293..b814a20d8a 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -934,6 +934,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 d95833b2de..a1eb45f881 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -12,6 +12,8 @@ obj-y += panic.o obj-y += version.o
# command
+obj-$(CONFIG_CMD_ARMFFA) += armffa.o obj-$(CONFIG_CMD_ACPI) += acpi.o obj-$(CONFIG_CMD_ADDRMAP) += addrmap.o obj-$(CONFIG_CMD_AES) += aes.o diff --git a/cmd/armffa.c b/cmd/armffa.c new file mode 100644 index 0000000000..d983a23bbc --- /dev/null +++ b/cmd/armffa.c @@ -0,0 +1,238 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright 2022-2023 Arm Limited and/or its affiliates <
open-source-office@arm.com>
- Authors:
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include <common.h> +#include <arm_ffa.h> +#include <command.h> +#include <dm.h> +#include <mapmem.h> +#include <stdlib.h> +#include <asm/io.h>
+/**
- do_ffa_getpart() - implementation of the getpart subcommand
- @cmdtp: Command Table
- @flag: flags
- @argc: number of arguments
- @argv: arguments
- This function queries the secure partition information which the UUID
is provided
s/This function queries/Query/
We know it is a function so try to be brief and use the imperative mood like you do in commit messages.
- 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_info *parts_info;
u32 info_idx;
struct udevice *dev = NULL;
if (argc != 1)
return -EINVAL;
Since this is a command handler, you must return CMD_RET_USAGE here. Please fix globally.
uclass_first_device(UCLASS_FFA, &dev);
if (!dev) {
ret = uclass_first_device_err(...) if (ret) {
(please fix globally)
log_err("[FFA][CMD] Cannot find FF-A bus device\n");
return -ENODEV;
CMD_RET_FAILURE - please fix throughout. Often it is easier to put all this code (after arg checking) in a separate function which returns an error code, then have the do_... function check that and either return 0 or print a message and return CMD_RET_FAILURE.
}
/* Mode 1: getting the number of secure partitions */
ret = ffa_partition_info_get(dev, argv[0], &count, NULL);
if (ret) {
log_err("[FFA][CMD] Failure in querying partitions count
(error code: %d)\n", ret);
%dE gives you a nice error name if you want it.
Do you think you need the [FFA][CMD] stuff? Just the message should be enough.
return ret;
}
if (!count) {
log_info("[FFA][CMD] No secure partition found\n");
return ret;
}
/*
* Pre-allocate a buffer to be filled by the driver
* with ffa_partition_info structs
*/
log_info("[FFA][CMD] Pre-allocating %d partition(s) info
structures\n", count);
parts_info = calloc(count, sizeof(struct ffa_partition_info));
Just use a local variable and avoid the allocation.
if (!parts_info)
return -EINVAL;
/* Ask the driver to fill the buffer with the SPs info */
ret = ffa_partition_info_get(dev, argv[0], &count, parts_info);
if (ret) {
log_err("[FFA][CMD] Failure in querying partition(s) info
(error code: %d)\n", ret);
free(parts_info);
return ret;
}
/* SPs found , show the partition information */
for (info_idx = 0; info_idx < count ; info_idx++) {
We generally use 'i' for loops as it is shorter.
log_info("[FFA][CMD] Partition: id = 0x%x , exec_ctxt
0x%x , properties 0x%x\n",
You don't need the 0x. Generally hex is used for everything in U-Boot. If you feel there is ambiguity, use %#x
parts_info[info_idx].id,
parts_info[info_idx].exec_ctxt,
parts_info[info_idx].properties);
}
free(parts_info);
return 0;
+}
+/**
- do_ffa_ping() - implementation of the ping subcommand
- @cmdtp: Command Table
- @flag: flags
- @argc: number of arguments
- @argv: arguments
- This function sends data to the secure partition which the ID is
provided
partition for which ?
- as an argument. The function uses 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
- */
+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 = NULL;
if (argc != 1)
return -EINVAL;
errno = 0;
part_id = strtoul(argv[0], NULL, 16);
if (errno) {
log_err("[FFA][CMD] Invalid partition ID\n");
return -EINVAL;
}
uclass_first_device(UCLASS_FFA, &dev);
if (!dev) {
log_err("[FFA][CMD] Cannot find FF-A bus device\n");
return -ENODEV;
}
ret = ffa_sync_send_receive(dev, part_id, &msg, 1);
if (!ret) {
u8 cnt;
log_info("[FFA][CMD] SP response:\n[LSB]\n");
for (cnt = 0;
cnt < sizeof(struct ffa_send_direct_data) /
sizeof(u64);
cnt++)
log_info("[FFA][CMD] 0x%llx\n", ((u64
*)&msg)[cnt]);
} else {
log_err("[FFA][CMD] Sending direct request error (%d)\n",
ret);
}
return ret;
+}
+/**
- *do_ffa_devlist() - implementation of the devlist subcommand
- @cmdtp: [in] Command Table
- @flag: flags
- @argc: number of arguments
- @argv: arguments
- This function queries the device belonging to the UCLASS_FFA
- class.
- Return:
- CMD_RET_SUCCESS: on success, otherwise failure
- */
+int do_ffa_devlist(struct cmd_tbl *cmdtp, int flag, int argc, char
*const argv[])
+{
struct udevice *dev = NULL;
uclass_first_device(UCLASS_FFA, &dev);
if (!dev) {
log_err("[FFA][CMD] Cannot find FF-A bus device\n");
return -ENODEV;
}
log_info("[FFA][CMD] device name %s, dev %08x, driver name %s,
ops %08x\n",
dev->name,
(u32)map_to_sysmem(dev),
use %p as this is a pointer
dev->driver->name,
(u32)map_to_sysmem(dev->driver->ops));
Use %p as this is a pointer
return 0;
+}
+static struct cmd_tbl armffa_commands[] = {
U_BOOT_CMD_MKENT(getpart, 1, 1, do_ffa_getpart, "", ""),
U_BOOT_CMD_MKENT(ping, 1, 1, do_ffa_ping, "", ""),
U_BOOT_CMD_MKENT(devlist, 0, 1, do_ffa_devlist, "", ""),
+};
+/**
- do_armffa() - the armffa command main function
- @cmdtp: Command Table
- @flag: flags
- @argc: number of arguments
- @argv: arguments
- This function identifies which armffa subcommand to run.
- Then, it makes sure the arm_ffa device is probed and
- ready for use.
- Then, it runs the subcommand.
- Return:
- CMD_RET_SUCCESS: on success, otherwise failure
- */
+static int do_armffa(struct cmd_tbl *cmdtp, int flag, int argc, char
*const argv[])
You can use U_BOOT_CMD_WITH_SUBCMDS and drop this function
+{
struct cmd_tbl *armffa_cmd;
int ret;
if (argc < 2)
return CMD_RET_USAGE;
armffa_cmd = find_cmd_tbl(argv[1], armffa_commands,
ARRAY_SIZE(armffa_commands));
argc -= 2;
argv += 2;
if (!armffa_cmd || argc > armffa_cmd->maxargs)
return CMD_RET_USAGE;
ret = armffa_cmd->cmd(armffa_cmd, flag, argc, argv);
return cmd_process_error(armffa_cmd, ret);
+}
+U_BOOT_CMD(armffa, 4, 1, do_armffa,
"Arm FF-A operations test command",
"getpart <partition UUID>\n"
" - lists the partition(s) info\n"
"ping <partition ID>\n"
" - sends a data pattern to the specified partition\n"
"devlist\n"
" - displays information about the FF-A device/driver\n");
diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst index ddf6435402..5fedb0c255 100644 --- a/doc/arch/arm64.ffa.rst +++ b/doc/arch/arm64.ffa.rst @@ -218,6 +218,13 @@ The following features are provided:
- FF-A bus can be compiled and used without EFI
+The armffa command +-----------------------------------
+armffa is an implementation defined 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.rst
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 0000000000..9bf59e393b --- /dev/null +++ b/doc/usage/cmd/armffa.rst @@ -0,0 +1,107 @@ +.. SPDX-License-Identifier: GPL-2.0+:
+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 with debug logs
enabled.
+* ping
+::
- corstone1000# armffa ping 0x8003
- [FFA][CMD] SP response:
- [LSB]
- [FFA][CMD] 0xfffffffe
- [FFA][CMD] 0x0
- [FFA][CMD] 0x0
- [FFA][CMD] 0x0
- [FFA][CMD] 0x0
+* ping (failure case)
+::
- corstone1000# armffa ping 0x8009
- [FFA][CMD] Sending direct request error (-22)
- Command 'ping' failed: Error -22
+* getpart
+::
- corstone1000# armffa getpart 33d532ed-e699-0942-c09c-a798d9cd722d
- [FFA] Preparing for checking partitions count
- [FFA] Searching partitions using the provided UUID
- [FFA] No partition found. Querying framework ...
- [FFA] Reading partitions data from the RX buffer
- [FFA] Number of partition(s) matching the UUID: 1
- [FFA][CMD] Pre-allocating 1 partition(s) info structures
- [FFA] Preparing for filling partitions info
- [FFA] Searching partitions using the provided UUID
- [FFA] Partition ID 8003 matches the provided UUID
- [FFA][CMD] Partition: id = 0x8003 , exec_ctxt 0x1 , properties 0x3
To me this [FFA] stuff is redundant and it looks awful. Please drop it.
+* getpart (failure case)
+::
- corstone1000# armffa getpart 33d532ed-e699-0942-c09c-a798d9cd7228
- [FFA] Preparing for checking partitions count
- [FFA] Searching partitions using the provided UUID
- [FFA] No partition found. Querying framework ...
- [FFA] INVALID_PARAMETERS: Unrecognized UUID
- [FFA][CMD] Failure in querying partitions count (error code: -22)
- Command 'getpart' failed: Error -22
+* devlist
+::
- corstone1000# armffa devlist
- [FFA][CMD] device name arm_ffa, dev fdf41c30, driver name arm_ffa,
ops fffc0fc8
+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 and a negative error code on
failure.
diff --git a/doc/usage/index.rst b/doc/usage/index.rst index bc85e1d49a..df107fb710 100644 --- a/doc/usage/index.rst +++ b/doc/usage/index.rst @@ -21,6 +21,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 9200c8028b..a7d5392859 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 select ARM_SMCCC select ARM_SMCCC_FEATURES
imply CMD_ARMFFA select LIB_UUID select DEVRES help
-- 2.25.1
Regards, SImon

On Sun, Apr 02, 2023 at 02:40:34PM +1200, Simon Glass wrote:
Hi Abdellatif,
On Wed, 29 Mar 2023 at 05:12, Abdellatif El Khlifi < abdellatif.elkhlifi@arm.com> wrote:
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].
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
Changelog:
v10:
- use the FF-A driver Uclass operations
- use uclass_first_device()
- address nits
v9:
- remove manual FF-A discovery and use DM
- use DM class APIs to probe and interact with the FF-A bus
- add doc/usage/cmd/armffa.rst
v8:
- update partition_info_get() second argument to be an SP count
- pass NULL device pointer to the FF-A bus discovery and operations
v7:
- adapt do_ffa_dev_list() following the recent update on uclass_first_device/uclass_next_device functions (they return void now)
- set armffa command to use 64-bit direct messaging
v4:
- remove pattern data in do_ffa_msg_send_direct_req
v3:
- use the new driver interfaces (partition_info_get, sync_send_receive) in armffa command
v2:
- replace use of ffa_helper_init_device function by
ffa_helper_bus_discover
v1:
- introduce armffa command
MAINTAINERS | 2 + cmd/Kconfig | 10 ++ cmd/Makefile | 2 + cmd/armffa.c | 238 +++++++++++++++++++++++++++++++ doc/arch/arm64.ffa.rst | 7 + doc/usage/cmd/armffa.rst | 107 ++++++++++++++ doc/usage/index.rst | 1 + drivers/firmware/arm-ffa/Kconfig | 1 + 8 files changed, 368 insertions(+) create mode 100644 cmd/armffa.c create mode 100644 doc/usage/cmd/armffa.rst
diff --git a/MAINTAINERS b/MAINTAINERS index 62c30184bb..add208e4ef 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -269,7 +269,9 @@ F: configs/cortina_presidio-asic-pnand_defconfig ARM FF-A M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com S: Maintained +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: include/sandbox_arm_ffa.h diff --git a/cmd/Kconfig b/cmd/Kconfig index ba5ec69293..b814a20d8a 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -934,6 +934,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 d95833b2de..a1eb45f881 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -12,6 +12,8 @@ obj-y += panic.o obj-y += version.o
# command
+obj-$(CONFIG_CMD_ARMFFA) += armffa.o obj-$(CONFIG_CMD_ACPI) += acpi.o obj-$(CONFIG_CMD_ADDRMAP) += addrmap.o obj-$(CONFIG_CMD_AES) += aes.o diff --git a/cmd/armffa.c b/cmd/armffa.c new file mode 100644 index 0000000000..d983a23bbc --- /dev/null +++ b/cmd/armffa.c @@ -0,0 +1,238 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright 2022-2023 Arm Limited and/or its affiliates <
open-source-office@arm.com>
- Authors:
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include <common.h> +#include <arm_ffa.h> +#include <command.h> +#include <dm.h> +#include <mapmem.h> +#include <stdlib.h> +#include <asm/io.h>
+/**
- do_ffa_getpart() - implementation of the getpart subcommand
- @cmdtp: Command Table
- @flag: flags
- @argc: number of arguments
- @argv: arguments
- This function queries the secure partition information which the UUID
is provided
s/This function queries/Query/
We know it is a function so try to be brief and use the imperative mood like you do in commit messages.
- 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_info *parts_info;
u32 info_idx;
struct udevice *dev = NULL;
if (argc != 1)
return -EINVAL;
Since this is a command handler, you must return CMD_RET_USAGE here. Please fix globally.
uclass_first_device(UCLASS_FFA, &dev);
if (!dev) {
ret = uclass_first_device_err(...) if (ret) {
(please fix globally)
log_err("[FFA][CMD] Cannot find FF-A bus device\n");
return -ENODEV;
CMD_RET_FAILURE - please fix throughout. Often it is easier to put all this code (after arg checking) in a separate function which returns an error code, then have the do_... function check that and either return 0 or print a message and return CMD_RET_FAILURE.
}
/* Mode 1: getting the number of secure partitions */
ret = ffa_partition_info_get(dev, argv[0], &count, NULL);
if (ret) {
log_err("[FFA][CMD] Failure in querying partitions count
(error code: %d)\n", ret);
%dE gives you a nice error name if you want it.
Do you think you need the [FFA][CMD] stuff? Just the message should be enough.
return ret;
}
if (!count) {
log_info("[FFA][CMD] No secure partition found\n");
return ret;
}
/*
* Pre-allocate a buffer to be filled by the driver
* with ffa_partition_info structs
*/
log_info("[FFA][CMD] Pre-allocating %d partition(s) info
structures\n", count);
parts_info = calloc(count, sizeof(struct ffa_partition_info));
Just use a local variable and avoid the allocation.
The number of partitions can't be known in advance. A dynamic allocation is needed in this case
if (!parts_info)
return -EINVAL;
/* Ask the driver to fill the buffer with the SPs info */
ret = ffa_partition_info_get(dev, argv[0], &count, parts_info);
if (ret) {
log_err("[FFA][CMD] Failure in querying partition(s) info
(error code: %d)\n", ret);
free(parts_info);
return ret;
}
/* SPs found , show the partition information */
for (info_idx = 0; info_idx < count ; info_idx++) {
We generally use 'i' for loops as it is shorter.
log_info("[FFA][CMD] Partition: id = 0x%x , exec_ctxt
0x%x , properties 0x%x\n",
You don't need the 0x. Generally hex is used for everything in U-Boot. If you feel there is ambiguity, use %#x
parts_info[info_idx].id,
parts_info[info_idx].exec_ctxt,
parts_info[info_idx].properties);
}
free(parts_info);
return 0;
+}
+/**
- do_ffa_ping() - implementation of the ping subcommand
- @cmdtp: Command Table
- @flag: flags
- @argc: number of arguments
- @argv: arguments
- This function sends data to the secure partition which the ID is
provided
partition for which ?
The secure partition (aka SP) resides in the secure world and is discovered at runtime. The purpose of the partition is providing secure services (e.g: firmware update, reading EFI variables, ...)
Cheers, Abdellatif
- as an argument. The function uses 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
- */
+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 = NULL;
if (argc != 1)
return -EINVAL;
errno = 0;
part_id = strtoul(argv[0], NULL, 16);
if (errno) {
log_err("[FFA][CMD] Invalid partition ID\n");
return -EINVAL;
}
uclass_first_device(UCLASS_FFA, &dev);
if (!dev) {
log_err("[FFA][CMD] Cannot find FF-A bus device\n");
return -ENODEV;
}
ret = ffa_sync_send_receive(dev, part_id, &msg, 1);
if (!ret) {
u8 cnt;
log_info("[FFA][CMD] SP response:\n[LSB]\n");
for (cnt = 0;
cnt < sizeof(struct ffa_send_direct_data) /
sizeof(u64);
cnt++)
log_info("[FFA][CMD] 0x%llx\n", ((u64
*)&msg)[cnt]);
} else {
log_err("[FFA][CMD] Sending direct request error (%d)\n",
ret);
}
return ret;
+}
+/**
- *do_ffa_devlist() - implementation of the devlist subcommand
- @cmdtp: [in] Command Table
- @flag: flags
- @argc: number of arguments
- @argv: arguments
- This function queries the device belonging to the UCLASS_FFA
- class.
- Return:
- CMD_RET_SUCCESS: on success, otherwise failure
- */
+int do_ffa_devlist(struct cmd_tbl *cmdtp, int flag, int argc, char
*const argv[])
+{
struct udevice *dev = NULL;
uclass_first_device(UCLASS_FFA, &dev);
if (!dev) {
log_err("[FFA][CMD] Cannot find FF-A bus device\n");
return -ENODEV;
}
log_info("[FFA][CMD] device name %s, dev %08x, driver name %s,
ops %08x\n",
dev->name,
(u32)map_to_sysmem(dev),
use %p as this is a pointer
dev->driver->name,
(u32)map_to_sysmem(dev->driver->ops));
Use %p as this is a pointer
return 0;
+}
+static struct cmd_tbl armffa_commands[] = {
U_BOOT_CMD_MKENT(getpart, 1, 1, do_ffa_getpart, "", ""),
U_BOOT_CMD_MKENT(ping, 1, 1, do_ffa_ping, "", ""),
U_BOOT_CMD_MKENT(devlist, 0, 1, do_ffa_devlist, "", ""),
+};
+/**
- do_armffa() - the armffa command main function
- @cmdtp: Command Table
- @flag: flags
- @argc: number of arguments
- @argv: arguments
- This function identifies which armffa subcommand to run.
- Then, it makes sure the arm_ffa device is probed and
- ready for use.
- Then, it runs the subcommand.
- Return:
- CMD_RET_SUCCESS: on success, otherwise failure
- */
+static int do_armffa(struct cmd_tbl *cmdtp, int flag, int argc, char
*const argv[])
You can use U_BOOT_CMD_WITH_SUBCMDS and drop this function
+{
struct cmd_tbl *armffa_cmd;
int ret;
if (argc < 2)
return CMD_RET_USAGE;
armffa_cmd = find_cmd_tbl(argv[1], armffa_commands,
ARRAY_SIZE(armffa_commands));
argc -= 2;
argv += 2;
if (!armffa_cmd || argc > armffa_cmd->maxargs)
return CMD_RET_USAGE;
ret = armffa_cmd->cmd(armffa_cmd, flag, argc, argv);
return cmd_process_error(armffa_cmd, ret);
+}
+U_BOOT_CMD(armffa, 4, 1, do_armffa,
"Arm FF-A operations test command",
"getpart <partition UUID>\n"
" - lists the partition(s) info\n"
"ping <partition ID>\n"
" - sends a data pattern to the specified partition\n"
"devlist\n"
" - displays information about the FF-A device/driver\n");
diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst index ddf6435402..5fedb0c255 100644 --- a/doc/arch/arm64.ffa.rst +++ b/doc/arch/arm64.ffa.rst @@ -218,6 +218,13 @@ The following features are provided:
- FF-A bus can be compiled and used without EFI
+The armffa command +-----------------------------------
+armffa is an implementation defined 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.rst
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 0000000000..9bf59e393b --- /dev/null +++ b/doc/usage/cmd/armffa.rst @@ -0,0 +1,107 @@ +.. SPDX-License-Identifier: GPL-2.0+:
+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 with debug logs
enabled.
+* ping
+::
- corstone1000# armffa ping 0x8003
- [FFA][CMD] SP response:
- [LSB]
- [FFA][CMD] 0xfffffffe
- [FFA][CMD] 0x0
- [FFA][CMD] 0x0
- [FFA][CMD] 0x0
- [FFA][CMD] 0x0
+* ping (failure case)
+::
- corstone1000# armffa ping 0x8009
- [FFA][CMD] Sending direct request error (-22)
- Command 'ping' failed: Error -22
+* getpart
+::
- corstone1000# armffa getpart 33d532ed-e699-0942-c09c-a798d9cd722d
- [FFA] Preparing for checking partitions count
- [FFA] Searching partitions using the provided UUID
- [FFA] No partition found. Querying framework ...
- [FFA] Reading partitions data from the RX buffer
- [FFA] Number of partition(s) matching the UUID: 1
- [FFA][CMD] Pre-allocating 1 partition(s) info structures
- [FFA] Preparing for filling partitions info
- [FFA] Searching partitions using the provided UUID
- [FFA] Partition ID 8003 matches the provided UUID
- [FFA][CMD] Partition: id = 0x8003 , exec_ctxt 0x1 , properties 0x3
To me this [FFA] stuff is redundant and it looks awful. Please drop it.
+* getpart (failure case)
+::
- corstone1000# armffa getpart 33d532ed-e699-0942-c09c-a798d9cd7228
- [FFA] Preparing for checking partitions count
- [FFA] Searching partitions using the provided UUID
- [FFA] No partition found. Querying framework ...
- [FFA] INVALID_PARAMETERS: Unrecognized UUID
- [FFA][CMD] Failure in querying partitions count (error code: -22)
- Command 'getpart' failed: Error -22
+* devlist
+::
- corstone1000# armffa devlist
- [FFA][CMD] device name arm_ffa, dev fdf41c30, driver name arm_ffa,
ops fffc0fc8
+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 and a negative error code on
failure.
diff --git a/doc/usage/index.rst b/doc/usage/index.rst index bc85e1d49a..df107fb710 100644 --- a/doc/usage/index.rst +++ b/doc/usage/index.rst @@ -21,6 +21,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 9200c8028b..a7d5392859 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 select ARM_SMCCC select ARM_SMCCC_FEATURES
imply CMD_ARMFFA select LIB_UUID select DEVRES help
-- 2.25.1
Regards, SImon

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 abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
--- Changelog: ===============
v10:
* split the FF-A sandbox support into an emulator and a driver * read FFA_VERSION and FFA_PARTITION_INFO_GET state using sandbox_ffa_query_core_state() * drop CONFIG_SANDBOX_FFA config * address nits
v9: align FF-A sandbox driver with FF-A discovery through DM
v8: update ffa_bus_prvdata_get() to return a pointer rather than a pointer address
v7: state that sandbox driver supports only 64-bit direct messaging
v4: align sandbox driver with the new FF-A driver interfaces and new way of error handling
v1: introduce the sandbox driver
MAINTAINERS | 3 +- arch/sandbox/dts/sandbox.dtsi | 8 + arch/sandbox/dts/test.dts | 8 + arch/sandbox/include/asm/sandbox_arm_ffa.h | 68 ++ .../include/asm/sandbox_arm_ffa_priv.h | 133 ++++ configs/sandbox64_defconfig | 1 + configs/sandbox_defconfig | 1 + doc/arch/arm64.ffa.rst | 7 +- 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 | 732 ++++++++++++++++++ .../firmware/arm-ffa/sandbox_arm_ffa_priv.h | 14 - drivers/firmware/arm-ffa/sandbox_ffa.c | 108 +++ include/dm/uclass-id.h | 1 + 15 files changed, 1087 insertions(+), 21 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 add208e4ef..b8019517ba 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -269,12 +269,13 @@ F: configs/cortina_presidio-asic-pnand_defconfig ARM FF-A M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com 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: include/sandbox_arm_ffa.h
ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de diff --git a/arch/sandbox/dts/sandbox.dtsi b/arch/sandbox/dts/sandbox.dtsi index 30a305c4d2..ac713800d1 100644 --- a/arch/sandbox/dts/sandbox.dtsi +++ b/arch/sandbox/dts/sandbox.dtsi @@ -445,6 +445,14 @@ 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 d72d7a567a..bd42906159 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -1802,6 +1802,14 @@ compatible = "u-boot,fwu-mdata-gpt"; fwu-mdata-store = <&mmc0>; }; + + 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 0000000000..3652830e4e --- /dev/null +++ b/arch/sandbox/include/asm/sandbox_arm_ffa.h @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __SANDBOX_ARM_FFA_H +#define __SANDBOX_ARM_FFA_H + +#include <arm_ffa.h> + +/* + * 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_ffa_query_core_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 + * Return: + * 0 on success. Otherwise, failure + */ +int sandbox_ffa_query_core_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 0000000000..8fbfa3760d --- /dev/null +++ b/arch/sandbox/include/asm/sandbox_arm_ffa_priv.h @@ -0,0 +1,133 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __SANDBOX_ARM_FFA_PRV_H +#define __SANDBOX_ARM_FFA_PRV_H + +#include <arm_ffa_priv.h> + +/* 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; +}; + +/** + * struct ffa_emul_ops - Operations for the FF-A emulator + * @invoke_ffa_fn: callback for the emulated SMC call + * + * Provides all the operations supported by the FF-A emulator. + */ +struct ffa_emul_ops { + void (*invoke_ffa_fn)(ffa_value_t args, ffa_value_t *res); +}; + +#define ffa_emul_get_ops(dev) ((struct ffa_emul_ops *)(dev)->driver->ops) + +/** + * 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 b7737814af..89c17e4042 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 ac1e8bbbef..26ef035877 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -337,3 +337,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 5fedb0c255..457a0e112e 100644 --- a/doc/arch/arm64.ffa.rst +++ b/doc/arch/arm64.ffa.rst @@ -33,7 +33,11 @@ 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 driver providing Arm specific methods and reusing the Uclass 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 ------------------------------------------- @@ -69,6 +73,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 --------------------------------------- diff --git a/doc/arch/sandbox/sandbox.rst b/doc/arch/sandbox/sandbox.rst index cd7f8a2cb0..c5df372e00 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 a7d5392859..d75f8b53fd 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 imply CMD_ARMFFA select LIB_UUID select DEVRES @@ -33,5 +33,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 11b1766285..318123a7f4 100644 --- a/drivers/firmware/arm-ffa/Makefile +++ b/drivers/firmware/arm-ffa/Makefile @@ -5,4 +5,12 @@ # Authors: # Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
-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 0000000000..ba4d3c9f3f --- /dev/null +++ b/drivers/firmware/arm-ffa/ffa-emul-uclass.c @@ -0,0 +1,732 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ +#include <common.h> +#include <dm.h> +#include <mapmem.h> +#include <string.h> +#include <asm/global_data.h> +#include <asm/sandbox_arm_ffa.h> +#include <asm/sandbox_arm_ffa_priv.h> +#include <dm/device-internal.h> +#include <dm/lists.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> + +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_SP2_ID, .exec_ctxt = 0x9587, + .properties = 0x45325621 }, + .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_SP3_ID, .exec_ctxt = 0x7687, + .properties = 0x23325621 }, + .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 + * @dev: The sandbox FF-A emulator device + * @pargs: The SMC call input arguments a0-a7 + * @res: The SMC return data + * + * Emulates FFA_VERSION FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ + +static int sandbox_ffa_version(struct udevice *dev, ffa_value_t *pargs, ffa_value_t *res) +{ + struct sandbox_ffa_emul *priv = dev_get_priv(dev); + + 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 + * @dev: The sandbox FF-A emulator device + * @pargs: The SMC call input arguments a0-a7 + * @res: The SMC return data + * + * Emulates FFA_ID_GET FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int sandbox_ffa_id_get(struct udevice *dev, ffa_value_t *pargs, ffa_value_t *res) +{ + struct sandbox_ffa_emul *priv = dev_get_priv(dev); + + 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 + * + * Emulates 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("[FFA][Sandbox][Emul] FF-A interface 0x%lx not implemented\n", + pargs->a1); + + return ffa_to_std_errmap[NOT_SUPPORTED]; +} + +/** + * sandbox_ffa_partition_info_get() - Emulated FFA_PARTITION_INFO_GET handler + * @dev: The sandbox FF-A emulator device + * @pargs: The SMC call input arguments a0-a7 + * @res: The SMC return data + * + * Emulates FFA_PARTITION_INFO_GET FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int sandbox_ffa_partition_info_get(struct udevice *dev, 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(dev); + + 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][Sandbox][Emul] 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 + * @dev: The sandbox FF-A emulator device + * @pargs: The SMC call input arguments a0-a7 + * @res: The SMC return data + * + * Emulates FFA_RXTX_MAP FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int sandbox_ffa_rxtx_map(struct udevice *dev, ffa_value_t *pargs, ffa_value_t *res) +{ + int ret; + struct sandbox_ffa_emul *priv = dev_get_priv(dev); + + 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("[FFA][Sandbox][Emul] 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 + * @dev: The sandbox FF-A emulator device + * @pargs: The SMC call input arguments a0-a7 + * @res: The SMC return data + * + * Emulates FFA_RXTX_UNMAP FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int sandbox_ffa_rxtx_unmap(struct udevice *dev, ffa_value_t *pargs, ffa_value_t *res) +{ + int ret; + struct sandbox_ffa_emul *priv = dev_get_priv(dev); + + 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("[FFA][Sandbox][Emul] 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 + * @dev: The sandbox FF-A emulator device + * @pargs: The SMC call input arguments a0-a7 + * @res: The SMC return data + * + * Emulates FFA_RX_RELEASE FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int sandbox_ffa_rx_release(struct udevice *dev, ffa_value_t *pargs, ffa_value_t *res) +{ + int ret; + struct sandbox_ffa_emul *priv = dev_get_priv(dev); + + 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() - Checks SP validity + * @dev: The sandbox FF-A emulator device + * @part_id: partition ID to check + * + * Searches the input ID in the descriptors table. + * + * Return: + * + * 1 on success (Partition found). Otherwise, failure + */ +static int sandbox_ffa_sp_valid(struct udevice *dev, u16 part_id) +{ + u32 descs_cnt; + struct sandbox_ffa_emul *priv = dev_get_priv(dev); + + 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 + * @dev: The sandbox FF-A emulator device + * @pargs: The SMC call input arguments a0-a7 + * @res: The SMC return data + * + * Emulates 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 *dev, + ffa_value_t *pargs, ffa_value_t *res) +{ + u16 part_id; + struct sandbox_ffa_emul *priv = dev_get_priv(dev); + + part_id = GET_DST_SP_ID(pargs->a1); + + if (GET_NS_PHYS_ENDPOINT_ID(pargs->a1) != priv->id || + !sandbox_ffa_sp_valid(dev, 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() - Reading the mapping/ownership flags + * @dev: 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 + * + * Queries 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 *dev, u32 queried_func_id, + struct ffa_sandbox_data *func_data) +{ + struct sandbox_ffa_emul *priv = dev_get_priv(dev); + + 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("[FFA][Sandbox][Emul] The querried FF-A interface flag (%d) undefined\n", + queried_func_id); + return -EINVAL; + } +} + +/** + * sandbox_ffa_get_fwk_version() - Return the FFA framework version + * @dev: 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 *dev, struct ffa_sandbox_data *func_data) +{ + struct sandbox_ffa_emul *priv = dev_get_priv(dev); + + 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 + * @dev: 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 *dev, struct ffa_sandbox_data *func_data) +{ + struct sandbox_ffa_emul *priv = dev_get_priv(dev); + + 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_ffa_query_core_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 + * + * Queries the status of FF-A ABI specified in the input argument. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int sandbox_ffa_query_core_state(u32 queried_func_id, + struct ffa_sandbox_data *func_data) +{ + struct udevice *dev; + + uclass_first_device(UCLASS_FFA_EMUL, &dev); + if (!dev) { + log_err("[FFA][SANDBOX][Emul] Cannot find FF-A emulator during querying state\n"); + return -ENODEV; + } + + switch (queried_func_id) { + case FFA_RXTX_MAP: + case FFA_RXTX_UNMAP: + case FFA_RX_RELEASE: + return sandbox_ffa_get_rxbuf_flags(dev, queried_func_id, func_data); + case FFA_VERSION: + return sandbox_ffa_get_fwk_version(dev, func_data); + case FFA_PARTITION_INFO_GET: + return sandbox_ffa_get_parts(dev, func_data); + default: + log_err("[FFA][Sandbox][Emul] 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 + * + * Emulates 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 *dev; + + uclass_first_device(UCLASS_FFA_EMUL, &dev); + if (!dev) { + log_err("[FFA][SANDBOX][Emul] Cannot find FF-A emulator during SMC emulation\n"); + return; + } + + switch (args.a0) { + case FFA_SMC_32(FFA_VERSION): + ret = sandbox_ffa_version(dev, &args, res); + break; + case FFA_SMC_32(FFA_PARTITION_INFO_GET): + ret = sandbox_ffa_partition_info_get(dev, &args, res); + break; + case FFA_SMC_32(FFA_RXTX_UNMAP): + ret = sandbox_ffa_rxtx_unmap(dev, &args, res); + break; + case FFA_SMC_64(FFA_MSG_SEND_DIRECT_REQ): + ret = sandbox_ffa_msg_send_direct_req(dev, &args, res); + break; + case FFA_SMC_32(FFA_ID_GET): + ret = sandbox_ffa_id_get(dev, &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(dev, &args, res); + break; + case FFA_SMC_32(FFA_RX_RELEASE): + ret = sandbox_ffa_rx_release(dev, &args, res); + break; + default: + log_err("[FFA][Sandbox][Emul] Undefined FF-A interface (0x%lx)\n", + args.a0); + } + + if (ret != 0) + log_err("[FFA][Sandbox][Emul] FF-A ABI internal failure (%d)\n", ret); +} + +/** + * 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) + * + * Searches 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) +{ + if (!emulp) + return -EINVAL; + + *emulp = NULL; + + uclass_first_device(UCLASS_FFA_EMUL, emulp); + if (!(*emulp)) { + log_err("[FFA][SANDBOX][Emul] Cannot find FF-A emulator\n"); + return -ENODEV; + } + + log_info("[FFA][Sandbox][Emul] FF-A emulator found\n"); + + return 0; +} + +UCLASS_DRIVER(ffa_emul) = { + .name = "ffa_emul", + .id = UCLASS_FFA_EMUL, +}; + +/* Sandbox Arm FF-A emulator operations */ + +static const struct ffa_emul_ops sandbox_ffa_emul_ops = { + .invoke_ffa_fn = sandbox_arm_ffa_smccc_smc, +}; + +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, + .ops = &sandbox_ffa_emul_ops, + .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 4338f9c9b1..0000000000 --- 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 open-source-office@arm.com - * - * Authors: - * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com - */ - -#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 0000000000..bb150fd5cd --- /dev/null +++ b/drivers/firmware/arm-ffa/sandbox_ffa.c @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ +#include <common.h> +#include <arm_ffa.h> +#include <dm.h> +#include <log.h> +#include <asm/global_data.h> +#include <asm/sandbox_arm_ffa_priv.h> +#include <dm/device-internal.h> +#include <linux/errno.h> + +DECLARE_GLOBAL_DATA_PTR; + +/** + * ffa_set_smc_conduit() - Set the SMC conduit + * + * Selects the SMC conduit by setting the FF-A ABI invoke function. + * The function emulating the SMC call is provided by the FF-A emulator. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int ffa_set_smc_conduit(void) +{ + struct udevice *emul; + int ret; + + ret = ffa_emul_find(NULL, &emul); + if (ret) { + log_err("[FFA][Sandbox] Cannot find FF-A emulator, SMC emulation failure\n"); + return -ENODEV; + } + + if (!ffa_emul_get_ops(emul)->invoke_ffa_fn) { + log_err("[FFA][Sandbox] Cannot get FF-A emulator ops, SMC emulation failure\n"); + return -ENOSYS; + } + + dscvry_info.invoke_ffa_fn = ffa_emul_get_ops(emul)->invoke_ffa_fn; + log_info("[FFA][Sandbox] Using emulated Arm SMC for FF-A conduit\n"); + + return 0; +} + +/** + * sandbox_ffa_probe() - The sandbox FF-A driver probe function + * @dev: the sandbox Arm FF-A bus device (sandbox-arm-ffa) + * + * Probing is done through ffa_do_probe() + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int sandbox_ffa_probe(struct udevice *dev) +{ + return ffa_do_probe(dev); +} + +/** + * sandbox_ffa_bind() - The sandbox FF-A driver bind function + * @dev: the sandbox-arm-ffa device + * Tries to discover the emulated FF-A bus. + * Return: + * + * 0 on success. + */ +static int sandbox_ffa_bind(struct udevice *dev) +{ + bool ret; + + log_info("[FFA][Sandbox] binding the device\n"); + + ret = ffa_try_discovery(); + if (ret) + return 0; + else + return -ENODEV; +} + +/* 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, + .probe = sandbox_ffa_probe, + .bind = sandbox_ffa_bind, + .ops = &sandbox_ffa_ops, +}; diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index df77c7da58..4658411935 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -61,6 +61,7 @@ enum uclass_id { UCLASS_ETH, /* Ethernet device */ UCLASS_ETH_PHY, /* Ethernet PHY device */ 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 */

Hi Abdellatif,
On Wed, 29 Mar 2023 at 05:12, Abdellatif El Khlifi < abdellatif.elkhlifi@arm.com> wrote:
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 abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
Changelog:
v10:
- split the FF-A sandbox support into an emulator and a driver
- read FFA_VERSION and FFA_PARTITION_INFO_GET state using sandbox_ffa_query_core_state()
- drop CONFIG_SANDBOX_FFA config
- address nits
v9: align FF-A sandbox driver with FF-A discovery through DM
v8: update ffa_bus_prvdata_get() to return a pointer rather than a pointer address
v7: state that sandbox driver supports only 64-bit direct messaging
v4: align sandbox driver with the new FF-A driver interfaces and new way of error handling
v1: introduce the sandbox driver
MAINTAINERS | 3 +- arch/sandbox/dts/sandbox.dtsi | 8 + arch/sandbox/dts/test.dts | 8 + arch/sandbox/include/asm/sandbox_arm_ffa.h | 68 ++ .../include/asm/sandbox_arm_ffa_priv.h | 133 ++++ configs/sandbox64_defconfig | 1 + configs/sandbox_defconfig | 1 + doc/arch/arm64.ffa.rst | 7 +- 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 | 732 ++++++++++++++++++ .../firmware/arm-ffa/sandbox_arm_ffa_priv.h | 14 - drivers/firmware/arm-ffa/sandbox_ffa.c | 108 +++ include/dm/uclass-id.h | 1 + 15 files changed, 1087 insertions(+), 21 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
[..]
+/**
- sandbox_ffa_get_fwk_version() - Return the FFA framework version
- @dev: 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 *dev, struct
ffa_sandbox_data *func_data)
+{
struct sandbox_ffa_emul *priv = dev_get_priv(dev);
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
- @dev: 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 *dev, struct
ffa_sandbox_data *func_data)
+{
struct sandbox_ffa_emul *priv = dev_get_priv(dev);
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_ffa_query_core_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
- Queries the status of FF-A ABI specified in the input argument.
- Return:
- 0 on success. Otherwise, failure
- */
+int sandbox_ffa_query_core_state(u32 queried_func_id,
struct ffa_sandbox_data *func_data)
+{
struct udevice *dev;
Please can you use 'emul' for the emulator so it is separate from the device?
uclass_first_device(UCLASS_FFA_EMUL, &dev);
if (!dev) {
log_err("[FFA][SANDBOX][Emul] Cannot find FF-A emulator
during querying state\n");
return -ENODEV;
}
How about storing the emulator somewhere? You could have:
struct sandbox_ffa_priv { struct udevice *emul; };
.priv = sizeof(sandbox_ffa_priv)
switch (queried_func_id) {
case FFA_RXTX_MAP:
case FFA_RXTX_UNMAP:
case FFA_RX_RELEASE:
return sandbox_ffa_get_rxbuf_flags(dev, queried_func_id,
func_data);
case FFA_VERSION:
return sandbox_ffa_get_fwk_version(dev, func_data);
case FFA_PARTITION_INFO_GET:
return sandbox_ffa_get_parts(dev, func_data);
default:
log_err("[FFA][Sandbox][Emul] 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
- Emulates 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 *dev;
uclass_first_device(UCLASS_FFA_EMUL, &dev);
if (!dev) {
log_err("[FFA][SANDBOX][Emul] Cannot find FF-A emulator
during SMC emulation\n");
return;
}
switch (args.a0) {
case FFA_SMC_32(FFA_VERSION):
ret = sandbox_ffa_version(dev, &args, res);
break;
case FFA_SMC_32(FFA_PARTITION_INFO_GET):
ret = sandbox_ffa_partition_info_get(dev, &args, res);
break;
case FFA_SMC_32(FFA_RXTX_UNMAP):
ret = sandbox_ffa_rxtx_unmap(dev, &args, res);
break;
case FFA_SMC_64(FFA_MSG_SEND_DIRECT_REQ):
ret = sandbox_ffa_msg_send_direct_req(dev, &args, res);
break;
case FFA_SMC_32(FFA_ID_GET):
ret = sandbox_ffa_id_get(dev, &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(dev, &args, res);
break;
case FFA_SMC_32(FFA_RX_RELEASE):
ret = sandbox_ffa_rx_release(dev, &args, res);
break;
default:
log_err("[FFA][Sandbox][Emul] Undefined FF-A interface
(0x%lx)\n",
args.a0);
}
if (ret != 0)
log_err("[FFA][Sandbox][Emul] FF-A ABI internal failure
(%d)\n", ret);
+}
+/**
- 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)
- Searches 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) +{
if (!emulp)
return -EINVAL;
*emulp = NULL;
uclass_first_device(UCLASS_FFA_EMUL, emulp);
if (!(*emulp)) {
log_err("[FFA][SANDBOX][Emul] Cannot find FF-A
emulator\n");
return -ENODEV;
}
log_info("[FFA][Sandbox][Emul] FF-A emulator found\n");
return 0;
+}
+UCLASS_DRIVER(ffa_emul) = {
.name = "ffa_emul",
.id = UCLASS_FFA_EMUL,
+};
+/* Sandbox Arm FF-A emulator operations */
+static const struct ffa_emul_ops sandbox_ffa_emul_ops = {
.invoke_ffa_fn = sandbox_arm_ffa_smccc_smc,
+};
+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,
.ops = &sandbox_ffa_emul_ops,
.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 4338f9c9b1..0000000000 --- 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 <
open-source-office@arm.com>
- Authors:
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
-#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 0000000000..bb150fd5cd --- /dev/null +++ b/drivers/firmware/arm-ffa/sandbox_ffa.c @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright 2022-2023 Arm Limited and/or its affiliates <
open-source-office@arm.com>
- Authors:
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include <common.h> +#include <arm_ffa.h> +#include <dm.h> +#include <log.h> +#include <asm/global_data.h> +#include <asm/sandbox_arm_ffa_priv.h> +#include <dm/device-internal.h> +#include <linux/errno.h>
+DECLARE_GLOBAL_DATA_PTR;
+/**
- ffa_set_smc_conduit() - Set the SMC conduit
- Selects the SMC conduit by setting the FF-A ABI invoke function.
- The function emulating the SMC call is provided by the FF-A emulator.
- Return:
- 0 on success. Otherwise, failure
- */
+int ffa_set_smc_conduit(void) +{
struct udevice *emul;
int ret;
ret = ffa_emul_find(NULL, &emul);
if (ret) {
log_err("[FFA][Sandbox] Cannot find FF-A emulator, SMC
emulation failure\n");
return -ENODEV;
}
if (!ffa_emul_get_ops(emul)->invoke_ffa_fn) {
log_err("[FFA][Sandbox] Cannot get FF-A emulator ops, SMC
emulation failure\n");
return -ENOSYS;
}
dscvry_info.invoke_ffa_fn = ffa_emul_get_ops(emul)->invoke_ffa_fn;
log_info("[FFA][Sandbox] Using emulated Arm SMC for FF-A
conduit\n");
return 0;
+}
+/**
- sandbox_ffa_probe() - The sandbox FF-A driver probe function
- @dev: the sandbox Arm FF-A bus device (sandbox-arm-ffa)
- Probing is done through ffa_do_probe()
- Return:
- 0 on success. Otherwise, failure
- */
+static int sandbox_ffa_probe(struct udevice *dev) +{
return ffa_do_probe(dev);
Move to a uclass pre_probe() method.
+}
Drop this. You can add it in the uclass as a child_pre_probe member.
+/**
- sandbox_ffa_bind() - The sandbox FF-A driver bind function
- @dev: the sandbox-arm-ffa device
- Tries to discover the emulated FF-A bus.
- Return:
- 0 on success.
- */
+static int sandbox_ffa_bind(struct udevice *dev) +{
bool ret;
log_info("[FFA][Sandbox] binding the device\n");
ret = ffa_try_discovery();
The code for this is immediately above. Just do that here, or make the above function static and rename it.
if (ret)
return 0;
else
return -ENODEV;
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,
.probe = sandbox_ffa_probe,
.bind = sandbox_ffa_bind,
.ops = &sandbox_ffa_ops,
+}; diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index df77c7da58..4658411935 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -61,6 +61,7 @@ enum uclass_id { UCLASS_ETH, /* Ethernet device */ UCLASS_ETH_PHY, /* Ethernet PHY device */ 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 */
-- 2.25.1
Regards, Simon

On Sun, Apr 02, 2023 at 02:41:01PM +1200, Simon Glass wrote:
Hi Abdellatif,
On Wed, 29 Mar 2023 at 05:12, Abdellatif El Khlifi < abdellatif.elkhlifi@arm.com> wrote:
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 abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
Changelog:
v10:
- split the FF-A sandbox support into an emulator and a driver
- read FFA_VERSION and FFA_PARTITION_INFO_GET state using sandbox_ffa_query_core_state()
- drop CONFIG_SANDBOX_FFA config
- address nits
v9: align FF-A sandbox driver with FF-A discovery through DM
v8: update ffa_bus_prvdata_get() to return a pointer rather than a pointer address
v7: state that sandbox driver supports only 64-bit direct messaging
v4: align sandbox driver with the new FF-A driver interfaces and new way of error handling
v1: introduce the sandbox driver
MAINTAINERS | 3 +- arch/sandbox/dts/sandbox.dtsi | 8 + arch/sandbox/dts/test.dts | 8 + arch/sandbox/include/asm/sandbox_arm_ffa.h | 68 ++ .../include/asm/sandbox_arm_ffa_priv.h | 133 ++++ configs/sandbox64_defconfig | 1 + configs/sandbox_defconfig | 1 + doc/arch/arm64.ffa.rst | 7 +- 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 | 732 ++++++++++++++++++ .../firmware/arm-ffa/sandbox_arm_ffa_priv.h | 14 - drivers/firmware/arm-ffa/sandbox_ffa.c | 108 +++ include/dm/uclass-id.h | 1 + 15 files changed, 1087 insertions(+), 21 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
[..]
+/**
- sandbox_ffa_get_fwk_version() - Return the FFA framework version
- @dev: 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 *dev, struct
ffa_sandbox_data *func_data)
+{
struct sandbox_ffa_emul *priv = dev_get_priv(dev);
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
- @dev: 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 *dev, struct
ffa_sandbox_data *func_data)
+{
struct sandbox_ffa_emul *priv = dev_get_priv(dev);
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_ffa_query_core_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
- Queries the status of FF-A ABI specified in the input argument.
- Return:
- 0 on success. Otherwise, failure
- */
+int sandbox_ffa_query_core_state(u32 queried_func_id,
struct ffa_sandbox_data *func_data)
+{
struct udevice *dev;
Please can you use 'emul' for the emulator so it is separate from the device?
uclass_first_device(UCLASS_FFA_EMUL, &dev);
if (!dev) {
log_err("[FFA][SANDBOX][Emul] Cannot find FF-A emulator
during querying state\n");
return -ENODEV;
}
How about storing the emulator somewhere? You could have:
struct sandbox_ffa_priv { struct udevice *emul; };
.priv = sizeof(sandbox_ffa_priv)
in patchset v11 the sandbox emulator pointer is stored in the FF-A device uc_priv (struct ffa_priv)
Cheers, Abdellatif
switch (queried_func_id) {
case FFA_RXTX_MAP:
case FFA_RXTX_UNMAP:
case FFA_RX_RELEASE:
return sandbox_ffa_get_rxbuf_flags(dev, queried_func_id,
func_data);
case FFA_VERSION:
return sandbox_ffa_get_fwk_version(dev, func_data);
case FFA_PARTITION_INFO_GET:
return sandbox_ffa_get_parts(dev, func_data);
default:
log_err("[FFA][Sandbox][Emul] 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
- Emulates 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 *dev;
uclass_first_device(UCLASS_FFA_EMUL, &dev);
if (!dev) {
log_err("[FFA][SANDBOX][Emul] Cannot find FF-A emulator
during SMC emulation\n");
return;
}
switch (args.a0) {
case FFA_SMC_32(FFA_VERSION):
ret = sandbox_ffa_version(dev, &args, res);
break;
case FFA_SMC_32(FFA_PARTITION_INFO_GET):
ret = sandbox_ffa_partition_info_get(dev, &args, res);
break;
case FFA_SMC_32(FFA_RXTX_UNMAP):
ret = sandbox_ffa_rxtx_unmap(dev, &args, res);
break;
case FFA_SMC_64(FFA_MSG_SEND_DIRECT_REQ):
ret = sandbox_ffa_msg_send_direct_req(dev, &args, res);
break;
case FFA_SMC_32(FFA_ID_GET):
ret = sandbox_ffa_id_get(dev, &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(dev, &args, res);
break;
case FFA_SMC_32(FFA_RX_RELEASE):
ret = sandbox_ffa_rx_release(dev, &args, res);
break;
default:
log_err("[FFA][Sandbox][Emul] Undefined FF-A interface
(0x%lx)\n",
args.a0);
}
if (ret != 0)
log_err("[FFA][Sandbox][Emul] FF-A ABI internal failure
(%d)\n", ret);
+}
+/**
- 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)
- Searches 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) +{
if (!emulp)
return -EINVAL;
*emulp = NULL;
uclass_first_device(UCLASS_FFA_EMUL, emulp);
if (!(*emulp)) {
log_err("[FFA][SANDBOX][Emul] Cannot find FF-A
emulator\n");
return -ENODEV;
}
log_info("[FFA][Sandbox][Emul] FF-A emulator found\n");
return 0;
+}
+UCLASS_DRIVER(ffa_emul) = {
.name = "ffa_emul",
.id = UCLASS_FFA_EMUL,
+};
+/* Sandbox Arm FF-A emulator operations */
+static const struct ffa_emul_ops sandbox_ffa_emul_ops = {
.invoke_ffa_fn = sandbox_arm_ffa_smccc_smc,
+};
+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,
.ops = &sandbox_ffa_emul_ops,
.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 4338f9c9b1..0000000000 --- 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 <
open-source-office@arm.com>
- Authors:
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
-#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 0000000000..bb150fd5cd --- /dev/null +++ b/drivers/firmware/arm-ffa/sandbox_ffa.c @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright 2022-2023 Arm Limited and/or its affiliates <
open-source-office@arm.com>
- Authors:
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include <common.h> +#include <arm_ffa.h> +#include <dm.h> +#include <log.h> +#include <asm/global_data.h> +#include <asm/sandbox_arm_ffa_priv.h> +#include <dm/device-internal.h> +#include <linux/errno.h>
+DECLARE_GLOBAL_DATA_PTR;
+/**
- ffa_set_smc_conduit() - Set the SMC conduit
- Selects the SMC conduit by setting the FF-A ABI invoke function.
- The function emulating the SMC call is provided by the FF-A emulator.
- Return:
- 0 on success. Otherwise, failure
- */
+int ffa_set_smc_conduit(void) +{
struct udevice *emul;
int ret;
ret = ffa_emul_find(NULL, &emul);
if (ret) {
log_err("[FFA][Sandbox] Cannot find FF-A emulator, SMC
emulation failure\n");
return -ENODEV;
}
if (!ffa_emul_get_ops(emul)->invoke_ffa_fn) {
log_err("[FFA][Sandbox] Cannot get FF-A emulator ops, SMC
emulation failure\n");
return -ENOSYS;
}
dscvry_info.invoke_ffa_fn = ffa_emul_get_ops(emul)->invoke_ffa_fn;
log_info("[FFA][Sandbox] Using emulated Arm SMC for FF-A
conduit\n");
return 0;
+}
+/**
- sandbox_ffa_probe() - The sandbox FF-A driver probe function
- @dev: the sandbox Arm FF-A bus device (sandbox-arm-ffa)
- Probing is done through ffa_do_probe()
- Return:
- 0 on success. Otherwise, failure
- */
+static int sandbox_ffa_probe(struct udevice *dev) +{
return ffa_do_probe(dev);
Move to a uclass pre_probe() method.
+}
Drop this. You can add it in the uclass as a child_pre_probe member.
+/**
- sandbox_ffa_bind() - The sandbox FF-A driver bind function
- @dev: the sandbox-arm-ffa device
- Tries to discover the emulated FF-A bus.
- Return:
- 0 on success.
- */
+static int sandbox_ffa_bind(struct udevice *dev) +{
bool ret;
log_info("[FFA][Sandbox] binding the device\n");
ret = ffa_try_discovery();
The code for this is immediately above. Just do that here, or make the above function static and rename it.
if (ret)
return 0;
else
return -ENODEV;
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,
.probe = sandbox_ffa_probe,
.bind = sandbox_ffa_bind,
.ops = &sandbox_ffa_ops,
+}; diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index df77c7da58..4658411935 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -61,6 +61,7 @@ enum uclass_id { UCLASS_ETH, /* Ethernet device */ UCLASS_ETH_PHY, /* Ethernet PHY device */ 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 */
-- 2.25.1
Regards, Simon

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 abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
--- Changelog: ===============
v10:
* use the FF-A driver Uclass operations * use uclass_first_device() * replace CONFIG_SANDBOX_FFA with CONFIG_ARM_FFA_TRANSPORT * address nits
v9: align FF-A sandbox tests with FF-A discovery through DM
v8:
* update partition_info_get() second argument to be an SP count * pass NULL device pointer to the FF-A bus discovery and operations
v7: set the tests to use 64-bit direct messaging
v4: align sandbox tests with the new FF-A driver interfaces and new way of error handling
v1: introduce sandbox tests
MAINTAINERS | 1 + doc/arch/arm64.ffa.rst | 1 + test/dm/Makefile | 2 + test/dm/ffa.c | 357 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 361 insertions(+) create mode 100644 test/dm/ffa.c
diff --git a/MAINTAINERS b/MAINTAINERS index b8019517ba..8f698b3f20 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -276,6 +276,7 @@ F: doc/arch/arm64.ffa.rst F: doc/usage/cmd/armffa.rst F: drivers/firmware/arm-ffa/ F: include/arm_ffa.h +F: test/dm/ffa.c
ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst index 457a0e112e..5b1450f579 100644 --- a/doc/arch/arm64.ffa.rst +++ b/doc/arch/arm64.ffa.rst @@ -38,6 +38,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 7a79b6e1a2..45563bdfb4 100644 --- a/test/dm/Makefile +++ b/test/dm/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 open-source-office@arm.com
obj-$(CONFIG_UT_DM) += test-dm.o
@@ -85,6 +86,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 0000000000..d493597521 --- /dev/null +++ b/test/dm/ffa.c @@ -0,0 +1,357 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Functional tests for UCLASS_FFA class + * + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <console.h> +#include <dm.h> +#include <asm/sandbox_arm_ffa.h> +#include <asm/sandbox_arm_ffa_priv.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +/* Macros */ + +#define LOG_MSG_SZ (100) +#define LOG_CMD_SZ (LOG_MSG_SZ * 2) + +/* Functional tests for the UCLASS_FFA */ + +static int dm_test_ffa_log(struct unit_test_state *uts, char *msg) +{ + char cmd[LOG_CMD_SZ] = {0}; + + console_record_reset(); + + snprintf(cmd, LOG_CMD_SZ, "echo "%s"", msg); + run_command(cmd, 0); + + ut_assert_console_end(); + + return 0; +} + +static int check_fwk_version(struct ffa_priv *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_ffa_query_core_state(FFA_VERSION, &func_data)); + + if (priv->dscvry_info.fwk_version != fwk_version) { + char msg[LOG_MSG_SZ] = {0}; + + snprintf(msg, LOG_MSG_SZ, + "[%s]: Error: framework version: core = 0x%x , sandbox = 0x%x", __func__, + priv->dscvry_info.fwk_version, + fwk_version); + + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + return 0; +} + +static int check_endpoint_id(struct ffa_priv *priv, struct unit_test_state *uts) +{ + if (priv->id) { + char msg[LOG_MSG_SZ] = {0}; + + snprintf(msg, LOG_MSG_SZ, + "[%s]: Error: endpoint id: core = 0x%x", __func__, priv->id); + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + return 0; +} + +static int check_rxtxbuf(struct ffa_priv *priv, struct unit_test_state *uts) +{ + if (!priv->pair.rxbuf || !priv->pair.txbuf) { + char msg[LOG_MSG_SZ] = {0}; + + snprintf(msg, LOG_MSG_SZ, "[%s]: Error: rxbuf = %p txbuf = %p", __func__, + priv->pair.rxbuf, + priv->pair.txbuf); + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + return 0; +} + +static int check_features(struct ffa_priv *priv, struct unit_test_state *uts) +{ + char msg[LOG_MSG_SZ] = {0}; + + if (priv->pair.rxtx_min_pages != RXTX_4K && + priv->pair.rxtx_min_pages != RXTX_16K && + priv->pair.rxtx_min_pages != RXTX_64K) { + snprintf(msg, + LOG_MSG_SZ, + "[%s]: Error: FFA_RXTX_MAP features = 0x%lx", + __func__, + priv->pair.rxtx_min_pages); + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + + return 0; +} + +static int check_rxbuf_mapped_flag(u32 queried_func_id, + u8 rxbuf_mapped, + struct unit_test_state *uts) +{ + char msg[LOG_MSG_SZ] = {0}; + + switch (queried_func_id) { + case FFA_RXTX_MAP: + if (rxbuf_mapped) + return 0; + break; + case FFA_RXTX_UNMAP: + if (!rxbuf_mapped) + return 0; + break; + default: + return CMD_RET_FAILURE; + } + + snprintf(msg, LOG_MSG_SZ, "[%s]: Error: %s mapping issue", __func__, + (queried_func_id == FFA_RXTX_MAP ? "FFA_RXTX_MAP" : "FFA_RXTX_UNMAP")); + dm_test_ffa_log(uts, msg); + + return CMD_RET_FAILURE; +} + +static int check_rxbuf_release_flag(u8 rxbuf_owned, struct unit_test_state *uts) +{ + if (rxbuf_owned) { + char msg[LOG_MSG_SZ]; + + snprintf(msg, LOG_MSG_SZ, "[%s]: Error: RX buffer not released", __func__); + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + 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; + + uclass_first_device(UCLASS_FFA, &dev); + ut_assertnonnull(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) +{ + u32 count; + struct ffa_partition_info *parts_info; + u32 info_idx, exp_info_idx; + int ret; + struct udevice *dev; + struct ffa_sandbox_data func_data; + struct ffa_partitions *partitions; + + uclass_first_device(UCLASS_FFA, &dev); + ut_assertnonnull(dev); + + /* Get from the driver the count of the SPs matching the UUID */ + ret = ffa_partition_info_get(dev, service_uuid, &count, NULL); + /* Make sure partitions are detected */ + ut_assertok(ret); + ut_asserteq(SANDBOX_SP_COUNT_PER_VALID_SERVICE, count); + + /* Pre-allocate a buffer to be filled by the driver with ffa_partition_info structs */ + + parts_info = calloc(count, sizeof(struct ffa_partition_info)); + ut_assertnonnull(parts_info); + + /* Ask the driver to fill the buffer with the SPs info */ + ret = ffa_partition_info_get(dev, service_uuid, &count, parts_info); + if (ret != 0) { + free(parts_info); + ut_assertok(ret); + } + + /* SPs found , verify the partitions information */ + + func_data.data0 = &partitions; + func_data.data0_size = sizeof(struct ffa_partitions *); + ut_assertok(sandbox_ffa_query_core_state(FFA_PARTITION_INFO_GET, &func_data)); + + ret = CMD_RET_FAILURE; + + for (info_idx = 0; info_idx < count ; info_idx++) { + for (exp_info_idx = 0; + exp_info_idx < partitions->count; + exp_info_idx++) { + if (parts_info[info_idx].id == + partitions->descs[exp_info_idx].info.id) { + ret = memcmp(&parts_info[info_idx], + &partitions->descs[exp_info_idx] + .info, + sizeof(struct ffa_partition_info)); + if (ret) + free(parts_info); + ut_assertok(ret); + /* Send and receive data from the current partition */ + test_ffa_msg_send_direct_req(parts_info[info_idx].id, uts); + } + ret = 0; + } + } + + free(parts_info); + + /* Verify expected partitions found in the emulated secure world */ + ut_assertok(ret); + + return 0; +} + +static int dm_test_ffa_ack(struct unit_test_state *uts) +{ + struct ffa_priv *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; + int ret; + struct udevice *dev; + + /* Test probing the sandbox FF-A bus */ + uclass_first_device(UCLASS_FFA, &dev); + ut_assertnonnull(dev); + + /* Get a pointer to the sandbox FF-A bus private data */ + priv = dev_get_priv(dev); + + /* Make sure the private data pointer is retrieved */ + ut_assertnonnull(priv); + + /* Test FFA_VERSION */ + ut_assertok(check_fwk_version(priv, uts)); + + /* Test FFA_ID_GET */ + ut_assertok(check_endpoint_id(priv, uts)); + + /* Test FFA_FEATURES */ + ut_assertok(check_features(priv, uts)); + + /* Test core RX/TX buffers */ + ut_assertok(check_rxtxbuf(priv, uts)); + + /* Test FFA_RXTX_MAP */ + func_data.data0 = &rxbuf_flag; + func_data.data0_size = sizeof(rxbuf_flag); + + rxbuf_flag = 0; + ut_assertok(sandbox_ffa_query_core_state(FFA_RXTX_MAP, &func_data)); + ut_assertok(check_rxbuf_mapped_flag(FFA_RXTX_MAP, rxbuf_flag, uts)); + + /* FFA_PARTITION_INFO_GET / FFA_MSG_SEND_DIRECT_REQ */ + ret = test_partitions_and_comms(svc1_uuid, uts); + ut_assertok(ret); + + /* Test FFA_RX_RELEASE */ + rxbuf_flag = 1; + ut_assertok(sandbox_ffa_query_core_state(FFA_RX_RELEASE, &func_data)); + ut_assertok(check_rxbuf_release_flag(rxbuf_flag, uts)); + + /* FFA_PARTITION_INFO_GET / FFA_MSG_SEND_DIRECT_REQ */ + ret = test_partitions_and_comms(svc2_uuid, uts); + ut_assertok(ret); + + /* Test FFA_RX_RELEASE */ + rxbuf_flag = 1; + ut_assertok(sandbox_ffa_query_core_state(FFA_RX_RELEASE, &func_data)); + ut_assertok(check_rxbuf_release_flag(rxbuf_flag, uts)); + + /* Test FFA_RXTX_UNMAP */ + ut_assertok(ffa_rxtx_unmap(dev)); + + rxbuf_flag = 1; + ut_assertok(sandbox_ffa_query_core_state(FFA_RXTX_UNMAP, &func_data)); + ut_assertok(check_rxbuf_mapped_flag(FFA_RXTX_UNMAP, 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 *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 = 0; + u16 part_id = 0; + struct udevice *dev; + + /* Test probing the sandbox FF-A bus */ + uclass_first_device(UCLASS_FFA, &dev); + ut_assertnonnull(dev); + + /* Get a pointer to the sandbox FF-A bus private data */ + priv = dev_get_priv(dev); + + /* Make sure the private data pointer is retrieved */ + ut_assertnonnull(priv); + + /* Query partitions count using invalid arguments */ + ret = ffa_partition_info_get(dev, unvalid_svc_uuid, NULL, NULL); + ut_asserteq(-EINVAL, ret); + + /* Query partitions count using an invalid UUID string */ + ret = ffa_partition_info_get(dev, unvalid_svc_uuid_str, &count, NULL); + 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, NULL); + ut_assertok(count); + + /* Query partitions count using a valid UUID */ + count = 0; + ret = ffa_partition_info_get(dev, valid_svc_uuid, &count, NULL); + /* Make sure partitions are detected */ + ut_assertok(ret); + ut_asserteq(SANDBOX_SP_COUNT_PER_VALID_SERVICE, count); + + /* 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 = priv->partitions.descs[0].info.id; + ret = ffa_sync_send_receive(dev, part_id, &msg, 1); + ut_assertok(ret); + + return 0; +} + +DM_TEST(dm_test_ffa_nack, UT_TESTF_SCAN_FDT | UT_TESTF_CONSOLE_REC);

Hi Abdellatif,
On Wed, 29 Mar 2023 at 05:12, Abdellatif El Khlifi < abdellatif.elkhlifi@arm.com> wrote:
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 abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
Changelog:
v10:
- use the FF-A driver Uclass operations
- use uclass_first_device()
- replace CONFIG_SANDBOX_FFA with CONFIG_ARM_FFA_TRANSPORT
- address nits
v9: align FF-A sandbox tests with FF-A discovery through DM
v8:
- update partition_info_get() second argument to be an SP count
- pass NULL device pointer to the FF-A bus discovery and operations
v7: set the tests to use 64-bit direct messaging
v4: align sandbox tests with the new FF-A driver interfaces and new way of error handling
v1: introduce sandbox tests
MAINTAINERS | 1 + doc/arch/arm64.ffa.rst | 1 + test/dm/Makefile | 2 + test/dm/ffa.c | 357 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 361 insertions(+) create mode 100644 test/dm/ffa.c
diff --git a/MAINTAINERS b/MAINTAINERS index b8019517ba..8f698b3f20 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -276,6 +276,7 @@ F: doc/arch/arm64.ffa.rst F: doc/usage/cmd/armffa.rst F: drivers/firmware/arm-ffa/ F: include/arm_ffa.h +F: test/dm/ffa.c
ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst index 457a0e112e..5b1450f579 100644 --- a/doc/arch/arm64.ffa.rst +++ b/doc/arch/arm64.ffa.rst @@ -38,6 +38,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 7a79b6e1a2..45563bdfb4 100644 --- a/test/dm/Makefile +++ b/test/dm/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 <
open-source-office@arm.com>
obj-$(CONFIG_UT_DM) += test-dm.o
@@ -85,6 +86,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 0000000000..d493597521 --- /dev/null +++ b/test/dm/ffa.c @@ -0,0 +1,357 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Functional tests for UCLASS_FFA class
- Copyright 2022-2023 Arm Limited and/or its affiliates <
open-source-office@arm.com>
- Authors:
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include <common.h> +#include <console.h> +#include <dm.h> +#include <asm/sandbox_arm_ffa.h> +#include <asm/sandbox_arm_ffa_priv.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h>
+/* Macros */
+#define LOG_MSG_SZ (100) +#define LOG_CMD_SZ (LOG_MSG_SZ * 2)
+/* Functional tests for the UCLASS_FFA */
+static int dm_test_ffa_log(struct unit_test_state *uts, char *msg) +{
char cmd[LOG_CMD_SZ] = {0};
console_record_reset();
snprintf(cmd, LOG_CMD_SZ, "echo \"%s\"", msg);
run_command(cmd, 0);
ut_assert_console_end();
return 0;
+}
+static int check_fwk_version(struct ffa_priv *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_ffa_query_core_state(FFA_VERSION,
&func_data));
if (priv->dscvry_info.fwk_version != fwk_version) {
char msg[LOG_MSG_SZ] = {0};
snprintf(msg, LOG_MSG_SZ,
"[%s]: Error: framework version: core = 0x%x ,
sandbox = 0x%x", __func__,
priv->dscvry_info.fwk_version,
fwk_version);
dm_test_ffa_log(uts, msg);
return CMD_RET_FAILURE;
This is not a command. You should use ut_assert(false) here, perhaps? Please fix throughout.
}
return 0;
+}
+static int check_endpoint_id(struct ffa_priv *priv, struct
unit_test_state *uts)
+{
if (priv->id) {
char msg[LOG_MSG_SZ] = {0};
snprintf(msg, LOG_MSG_SZ,
"[%s]: Error: endpoint id: core = 0x%x",
__func__, priv->id);
dm_test_ffa_log(uts, msg);
return CMD_RET_FAILURE;
}
return 0;
+}
+static int check_rxtxbuf(struct ffa_priv *priv, struct unit_test_state
*uts)
+{
if (!priv->pair.rxbuf || !priv->pair.txbuf) {
char msg[LOG_MSG_SZ] = {0};
snprintf(msg, LOG_MSG_SZ, "[%s]: Error: rxbuf = %p txbuf
= %p", __func__,
priv->pair.rxbuf,
priv->pair.txbuf);
dm_test_ffa_log(uts, msg);
return CMD_RET_FAILURE;
}
return 0;
+}
+static int check_features(struct ffa_priv *priv, struct unit_test_state
*uts)
+{
char msg[LOG_MSG_SZ] = {0};
if (priv->pair.rxtx_min_pages != RXTX_4K &&
priv->pair.rxtx_min_pages != RXTX_16K &&
priv->pair.rxtx_min_pages != RXTX_64K) {
snprintf(msg,
LOG_MSG_SZ,
"[%s]: Error: FFA_RXTX_MAP features = 0x%lx",
__func__,
priv->pair.rxtx_min_pages);
dm_test_ffa_log(uts, msg);
return CMD_RET_FAILURE;
It's not clear to me why you are printing these messages? If should be enough to use ut_assert() as it shows you where the error happened, in a backtrace. Perhaps I am missing something?
}
return 0;
+}
+static int check_rxbuf_mapped_flag(u32 queried_func_id,
u8 rxbuf_mapped,
struct unit_test_state *uts)
+{
char msg[LOG_MSG_SZ] = {0};
switch (queried_func_id) {
case FFA_RXTX_MAP:
if (rxbuf_mapped)
return 0;
break;
case FFA_RXTX_UNMAP:
if (!rxbuf_mapped)
return 0;
break;
default:
return CMD_RET_FAILURE;
}
snprintf(msg, LOG_MSG_SZ, "[%s]: Error: %s mapping issue",
__func__,
(queried_func_id == FFA_RXTX_MAP ? "FFA_RXTX_MAP" :
"FFA_RXTX_UNMAP"));
dm_test_ffa_log(uts, msg);
return CMD_RET_FAILURE;
+}
+static int check_rxbuf_release_flag(u8 rxbuf_owned, struct
unit_test_state *uts)
+{
if (rxbuf_owned) {
char msg[LOG_MSG_SZ];
snprintf(msg, LOG_MSG_SZ, "[%s]: Error: RX buffer not
released", __func__);
dm_test_ffa_log(uts, msg);
return CMD_RET_FAILURE;
}
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;
uclass_first_device(UCLASS_FFA, &dev);
ut_assertnonnull(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)
+{
u32 count;
struct ffa_partition_info *parts_info;
u32 info_idx, exp_info_idx;
int ret;
struct udevice *dev;
struct ffa_sandbox_data func_data;
struct ffa_partitions *partitions;
uclass_first_device(UCLASS_FFA, &dev);
ut_assertnonnull(dev);
ut_assertok(uclass_first_device_err(...
/* Get from the driver the count of the SPs matching the UUID */
ret = ffa_partition_info_get(dev, service_uuid, &count, NULL);
/* Make sure partitions are detected */
ut_assertok(ret);
ut_asserteq(SANDBOX_SP_COUNT_PER_VALID_SERVICE, count);
/* Pre-allocate a buffer to be filled by the driver with
ffa_partition_info structs */
parts_info = calloc(count, sizeof(struct ffa_partition_info));
ut_assertnonnull(parts_info);
Use a local var
/* Ask the driver to fill the buffer with the SPs info */
ret = ffa_partition_info_get(dev, service_uuid, &count,
parts_info);
if (ret != 0) {
free(parts_info);
ut_assertok(ret);
}
/* SPs found , verify the partitions information */
func_data.data0 = &partitions;
func_data.data0_size = sizeof(struct ffa_partitions *);
ut_assertok(sandbox_ffa_query_core_state(FFA_PARTITION_INFO_GET,
&func_data));
ret = CMD_RET_FAILURE;
for (info_idx = 0; info_idx < count ; info_idx++) {
for (exp_info_idx = 0;
exp_info_idx < partitions->count;
exp_info_idx++) {
if (parts_info[info_idx].id ==
partitions->descs[exp_info_idx].info.id) {
ret = memcmp(&parts_info[info_idx],
&partitions->descs[exp_info_idx]
.info,
sizeof(struct
ffa_partition_info));
ut_asserteq_mem(). Please take a look at the functions available.
if (ret)
free(parts_info);
ut_assertok(ret);
/* Send and receive data from the current
partition */
test_ffa_msg_send_direct_req(parts_info[info_idx].id, uts);
}
ret = 0;
}
}
free(parts_info);
/* Verify expected partitions found in the emulated secure world
*/
ut_assertok(ret);
return 0;
+}
+static int dm_test_ffa_ack(struct unit_test_state *uts) +{
struct ffa_priv *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;
int ret;
struct udevice *dev;
/* Test probing the sandbox FF-A bus */
uclass_first_device(UCLASS_FFA, &dev);
ut_assertnonnull(dev);
/* Get a pointer to the sandbox FF-A bus private data */
priv = dev_get_priv(dev);
I think you want dev_get_uclass_priv() here, based on my othe rcomment
/* Make sure the private data pointer is retrieved */
ut_assertnonnull(priv);
/* Test FFA_VERSION */
[..]
Regards, Simon

Add Sandbox test for the armffa command
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
--- Changelog: ===============
v10:
* replace CMD_RET_SUCCESS with 0 * replace CONFIG_SANDBOX_FFA with CONFIG_ARM_FFA_TRANSPORT
v9: align the test with FF-A discovery through DM
v4: drop use of helper APIs
v1: introduce armffa command sandbox test
MAINTAINERS | 1 + test/cmd/Makefile | 2 ++ test/cmd/armffa.c | 39 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+) create mode 100644 test/cmd/armffa.c
diff --git a/MAINTAINERS b/MAINTAINERS index 8f698b3f20..9019370d71 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -276,6 +276,7 @@ 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/test/cmd/Makefile b/test/cmd/Makefile index 7848f348bc..8d0ec038cd 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 open-source-office@arm.com
ifdef CONFIG_HUSH_PARSER obj-$(CONFIG_CONSOLE_RECORD) += test_echo.o @@ -20,6 +21,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 0000000000..a9768dbd2c --- /dev/null +++ b/test/cmd/armffa.c @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test for armffa command + * + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <string.h> +#include <asm/sandbox_arm_ffa.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +#define PING_CMD_SIZE 19 + +/* Basic test of 'armffa' command */ +static int dm_test_armffa_cmd(struct unit_test_state *uts) +{ + char ping_cmd[PING_CMD_SIZE] = {0}; + + /* armffa getpart <UUID> */ + ut_assertok(run_command("armffa getpart " SANDBOX_SERVICE1_UUID, 0)); + + snprintf(ping_cmd, PING_CMD_SIZE, "armffa ping 0x%x", SANDBOX_SP1_ID); + + /* armffa ping <ID> */ + ut_assertok(run_command(ping_cmd, 0)); + + /* 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);

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.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Signed-off-by: Gowtham Suresh Kumar gowtham.sureshkumar@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
--- Changelog: ===============
v10:
* use the FF-A driver Uclass operations * use uclass_first_device() * addressing nits
v9: align how FF-A is used with FF-A discovery through DM
v8:
* isolate the compilation choices between FF-A and OP-TEE * update partition_info_get() second argument to be an SP count * pass NULL device pointer to the FF-A bus discovery and operations
v7:
* set the MM door bell event to use 64-bit direct messaging * issue a compile time error when one of these macros are not found : FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_OFFSET, FFA_SHARED_MM_BUFFER_ADDR * make mm_sp_svc_uuid static * replace EINVAL with ENOMEM in ffa_discover_mm_sp_id() when calloc() fails * improve use of unmap_sysmem() in ffa_mm_communicate()
v6:
* add FF-A runtime discovery at MM communication level * drop EFI runtime support for FF-A MM communication * revert the changes in include/mm_communication.h for efi_mm_communicate_header and smm_variable_access structures
v4:
* use the new FF-A driver interfaces * discover MM partitions at runtime * copy FF-A driver private data to EFI runtime section at ExitBootServices() * drop use of FFA_ERR_STAT_SUCCESS error code * replace EFI_BUFFER_TOO_SMALL with EFI_OUT_OF_RESOURCES in ffa_mm_communicate(). No need for efi_memcpy_runtime() anymore * revert the error log in mm_communicate() in case of failure * remove packed attribute from efi_mm_communicate_header and smm_variable_communicate_header
v2:
* set default values to 0 for FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_ADDR and MM_SP_UUID_DATA and add warnings
v1:
* introduce FF-A MM communication
include/mm_communication.h | 6 + lib/efi_loader/Kconfig | 14 +- lib/efi_loader/efi_variable_tee.c | 300 +++++++++++++++++++++++++++++- 3 files changed, 314 insertions(+), 6 deletions(-)
diff --git a/include/mm_communication.h b/include/mm_communication.h index e65fbde60d..87509fec3f 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. sughosh.ganu@linaro.org * Copyright (C) 2020 Linaro Ltd. ilias.apalodimas@linaro.org + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#ifndef _MM_COMMUNICATION_H_ @@ -13,6 +16,9 @@
#include <part_efi.h>
+/* MM service UUID string (big-endian format). This UUID is common across all MM SPs */ +#define MM_SP_UUID "33d532ed-e699-0942-c09c-a798d9cd722d" + /* * Interface to the pseudo Trusted Application (TA), which provides a * communication channel with the Standalone MM (Management Mode) diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index c5835e6ef6..08a6b84101 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -55,13 +55,23 @@ 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" - depends on OPTEE + bool "UEFI variables storage service via the trusted world" + depends on OPTEE || ARM_FFA_TRANSPORT 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 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 dfef18435d..a3060b4e7e 100644 --- a/lib/efi_loader/efi_variable_tee.c +++ b/lib/efi_loader/efi_variable_tee.c @@ -4,9 +4,14 @@ * * Copyright (C) 2019 Linaro Ltd. sughosh.ganu@linaro.org * Copyright (C) 2019 Linaro Ltd. ilias.apalodimas@linaro.org + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> +#include <dm.h> #include <efi.h> #include <efi_api.h> #include <efi_loader.h> @@ -15,6 +20,36 @@ #include <malloc.h> #include <mm_communication.h>
+#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) + +#include <arm_ffa.h> +#include <cpu_func.h> +#include <mapmem.h> + +#ifndef FFA_SHARED_MM_BUFFER_SIZE +#error "FFA_SHARED_MM_BUFFER_SIZE must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_SIZE 0 +#endif + +#ifndef FFA_SHARED_MM_BUFFER_OFFSET +#error "FFA_SHARED_MM_BUFFER_OFFSET must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_OFFSET 0 +#endif + +#ifndef FFA_SHARED_MM_BUFFER_ADDR +#error "FFA_SHARED_MM_BUFFER_ADDR must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_ADDR 0 +#endif + +/* MM return codes */ +#define MM_SUCCESS (0) + +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 */ static efi_uintn_t max_payload_size; /* func + data */ @@ -24,6 +59,7 @@ struct mm_connection { u32 session; };
+#if (IS_ENABLED(CONFIG_OPTEE)) /** * get_connection() - Retrieve OP-TEE session for a specific UUID. * @@ -143,13 +179,252 @@ static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize)
return ret; } +#endif + +#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) + +/** + * ffa_notify_mm_sp() - Announce there is data in the shared buffer + * + * Notifies 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 = -1; + struct udevice *dev; + + uclass_first_device(UCLASS_FFA, &dev); + if (!dev) { + log_err("EFI: Cannot find FF-A bus device, notify MM SP failure\n"); + return -ENODEV; + } + + msg.data0 = FFA_SHARED_MM_BUFFER_OFFSET; /* x3 */ + + ret = ffa_sync_send_receive(dev, mm_sp_id, &msg, 1); + if (ret != 0) + return ret; + + sp_event_ret = msg.data0; /* x3 */ + + if (sp_event_ret == MM_SUCCESS) + return 0; + + /* Failure to notify the MM SP */ + + return -EACCES; +} + +/** + * 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_info *parts_info; + struct udevice *dev; + + uclass_first_device(UCLASS_FFA, &dev); + if (!dev) { + log_err("EFI: Cannot find FF-A bus device, MM SP discovery failure\n"); + return -ENODEV; + } + + /* Get from the driver the count of the SPs matching the UUID */ + ret = ffa_partition_info_get(dev, mm_sp_svc_uuid, &count, NULL); + if (ret != 0) { + log_err("EFI: Failure in querying SPs count (%d), MM SP discovery failure\n", ret); + return ret; + } + + if (!count) { + log_info("EFI: No MM partition found\n"); + return ret; + } + + /* Pre-allocate a buffer to be filled by the driver with ffa_partition_info structs */ + + log_info("EFI: Pre-allocating %d partition(s) info structures\n", count); + + parts_info = calloc(count, sizeof(*parts_info)); + if (!parts_info) + return -ENOMEM; + + /* Ask the driver to fill the buffer with the SPs info */ + ret = ffa_partition_info_get(dev, mm_sp_svc_uuid, &count, parts_info); + if (ret) { + log_err("EFI: Failure in querying SPs info (%d), MM SP discovery failure\n", ret); + free(parts_info); + return ret; + } + + /* MM SPs found , use the first one */ + + mm_sp_id = parts_info[0].id; + + log_info("EFI: MM partition ID 0x%x\n", mm_sp_id); + + free(parts_info); + + 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 + * + * Issues 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() != 0) { + 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 > FFA_SHARED_MM_BUFFER_SIZE) + return EFI_INVALID_PARAMETER; + + /* Copy the data to the shared buffer */ + + virt_shared_buf = map_sysmem((phys_addr_t)FFA_SHARED_MM_BUFFER_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; +} +#endif + +/** + * select_ffa_mm_comms() - checks FF-A support availability + * + * Making sure FF-A is compiled in. If that's the case try to make sure + * the FF-A bus is probed successfully which means FF-A communication + * with secure world works and ready to be used. + * + * Return: + * + * 0: FF-A ready for use. Otherwise, failure + */ +static efi_status_t select_ffa_mm_comms(void) +{ + efi_status_t ret = EFI_UNSUPPORTED; + + if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) { + struct udevice *dev; + + uclass_first_device(UCLASS_FFA, &dev); + if (!dev) { + log_err("EFI: Cannot find FF-A bus device, cannot select FF-A comms\n"); + ret = EFI_NOT_READY; + } else { + ret = EFI_SUCCESS; + } + } + + return ret; +}
/** - * mm_communicate() - Adjust the cmonnucation buffer to StandAlonneMM and send + * 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) @@ -162,7 +437,17 @@ static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) 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); + ret = select_ffa_mm_comms(); + if (ret != EFI_SUCCESS) { +#if (IS_ENABLED(CONFIG_OPTEE)) + ret = optee_mm_communicate(comm_buf, dsize); +#endif + } else { +#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) + ret = ffa_mm_communicate(comm_buf, dsize); +#endif + } + if (ret != EFI_SUCCESS) { log_err("%s failed!\n", __func__); return ret; @@ -258,6 +543,13 @@ efi_status_t EFIAPI get_max_payload(efi_uintn_t *size) goto out; } *size = var_payload->size; + + #if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) + if (*size > FFA_SHARED_MM_BUFFER_SIZE) + *size = FFA_SHARED_MM_BUFFER_SIZE - MM_COMMUNICATE_HEADER_SIZE - + MM_VARIABLE_COMMUNICATE_SIZE; + #endif + /* * There seems to be a bug in EDK2 miscalculating the boundaries and * size checks, so deduct 2 more bytes to fulfill this requirement. Fix @@ -697,7 +989,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);
/*

Hi Abdellatif,
On Tue, Mar 28, 2023 at 05:11:56PM +0100, Abdellatif El Khlifi wrote:
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.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Signed-off-by: Gowtham Suresh Kumar gowtham.sureshkumar@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v10:
- use the FF-A driver Uclass operations
- use uclass_first_device()
- addressing nits
v9: align how FF-A is used with FF-A discovery through DM
v8:
- isolate the compilation choices between FF-A and OP-TEE
- update partition_info_get() second argument to be an SP count
- pass NULL device pointer to the FF-A bus discovery and operations
v7:
- set the MM door bell event to use 64-bit direct messaging
- issue a compile time error when one of these macros are not found : FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_OFFSET, FFA_SHARED_MM_BUFFER_ADDR
- make mm_sp_svc_uuid static
- replace EINVAL with ENOMEM in ffa_discover_mm_sp_id() when calloc() fails
- improve use of unmap_sysmem() in ffa_mm_communicate()
v6:
- add FF-A runtime discovery at MM communication level
- drop EFI runtime support for FF-A MM communication
- revert the changes in include/mm_communication.h for efi_mm_communicate_header and smm_variable_access structures
v4:
- use the new FF-A driver interfaces
- discover MM partitions at runtime
- copy FF-A driver private data to EFI runtime section at ExitBootServices()
- drop use of FFA_ERR_STAT_SUCCESS error code
- replace EFI_BUFFER_TOO_SMALL with EFI_OUT_OF_RESOURCES in ffa_mm_communicate(). No need for efi_memcpy_runtime() anymore
- revert the error log in mm_communicate() in case of failure
- remove packed attribute from efi_mm_communicate_header and smm_variable_communicate_header
v2:
- set default values to 0 for FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_ADDR and MM_SP_UUID_DATA and add warnings
v1:
- introduce FF-A MM communication
include/mm_communication.h | 6 + lib/efi_loader/Kconfig | 14 +- lib/efi_loader/efi_variable_tee.c | 300 +++++++++++++++++++++++++++++- 3 files changed, 314 insertions(+), 6 deletions(-)
diff --git a/include/mm_communication.h b/include/mm_communication.h index e65fbde60d..87509fec3f 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. sughosh.ganu@linaro.org
- Copyright (C) 2020 Linaro Ltd. ilias.apalodimas@linaro.org
- Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
- Authors:
*/
Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
#ifndef _MM_COMMUNICATION_H_ @@ -13,6 +16,9 @@
#include <part_efi.h>
+/* MM service UUID string (big-endian format). This UUID is common across all MM SPs */ +#define MM_SP_UUID "33d532ed-e699-0942-c09c-a798d9cd722d"
/*
- Interface to the pseudo Trusted Application (TA), which provides a
- communication channel with the Standalone MM (Management Mode)
diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index c5835e6ef6..08a6b84101 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -55,13 +55,23 @@ 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"
- depends on OPTEE
bool "UEFI variables storage service via the trusted world"
depends on OPTEE || ARM_FFA_TRANSPORT 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 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 dfef18435d..a3060b4e7e 100644 --- a/lib/efi_loader/efi_variable_tee.c +++ b/lib/efi_loader/efi_variable_tee.c @@ -4,9 +4,14 @@
- Copyright (C) 2019 Linaro Ltd. sughosh.ganu@linaro.org
- Copyright (C) 2019 Linaro Ltd. ilias.apalodimas@linaro.org
- Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
- Authors:
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#include <common.h> +#include <dm.h> #include <efi.h> #include <efi_api.h> #include <efi_loader.h> @@ -15,6 +20,36 @@ #include <malloc.h> #include <mm_communication.h>
+#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
+#include <arm_ffa.h> +#include <cpu_func.h> +#include <mapmem.h>
+#ifndef FFA_SHARED_MM_BUFFER_SIZE +#error "FFA_SHARED_MM_BUFFER_SIZE must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_SIZE 0 +#endif
+#ifndef FFA_SHARED_MM_BUFFER_OFFSET +#error "FFA_SHARED_MM_BUFFER_OFFSET must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_OFFSET 0 +#endif
+#ifndef FFA_SHARED_MM_BUFFER_ADDR +#error "FFA_SHARED_MM_BUFFER_ADDR must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_ADDR 0 +#endif
+/* MM return codes */ +#define MM_SUCCESS (0)
+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 */ static efi_uintn_t max_payload_size; /* func + data */ @@ -24,6 +59,7 @@ struct mm_connection { u32 session; };
+#if (IS_ENABLED(CONFIG_OPTEE)) /**
- get_connection() - Retrieve OP-TEE session for a specific UUID.
@@ -143,13 +179,252 @@ static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize)
return ret; } +#endif
+#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
+/**
- ffa_notify_mm_sp() - Announce there is data in the shared buffer
- Notifies 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 = -1;
- struct udevice *dev;
- uclass_first_device(UCLASS_FFA, &dev);
- if (!dev) {
log_err("EFI: Cannot find FF-A bus device, notify MM SP failure\n");
return -ENODEV;
- }
- msg.data0 = FFA_SHARED_MM_BUFFER_OFFSET; /* x3 */
- ret = ffa_sync_send_receive(dev, mm_sp_id, &msg, 1);
- if (ret != 0)
We usually do if (!ret)
return ret;
- sp_event_ret = msg.data0; /* x3 */
- if (sp_event_ret == MM_SUCCESS)
return 0;
- /* Failure to notify the MM SP */
- return -EACCES;
+}
+/**
- 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_info *parts_info;
- struct udevice *dev;
- uclass_first_device(UCLASS_FFA, &dev);
- if (!dev) {
log_err("EFI: Cannot find FF-A bus device, MM SP discovery failure\n");
return -ENODEV;
- }
- /* Get from the driver the count of the SPs matching the UUID */
- ret = ffa_partition_info_get(dev, mm_sp_svc_uuid, &count, NULL);
- if (ret != 0) {
log_err("EFI: Failure in querying SPs count (%d), MM SP discovery failure\n", ret);
return ret;
- }
- if (!count) {
log_info("EFI: No MM partition found\n");
return ret;
- }
- /* Pre-allocate a buffer to be filled by the driver with ffa_partition_info structs */
- log_info("EFI: Pre-allocating %d partition(s) info structures\n", count);
- parts_info = calloc(count, sizeof(*parts_info));
- if (!parts_info)
return -ENOMEM;
- /* Ask the driver to fill the buffer with the SPs info */
- ret = ffa_partition_info_get(dev, mm_sp_svc_uuid, &count, parts_info);
- if (ret) {
log_err("EFI: Failure in querying SPs info (%d), MM SP discovery failure\n", ret);
free(parts_info);
return ret;
- }
- /* MM SPs found , use the first one */
- mm_sp_id = parts_info[0].id;
- log_info("EFI: MM partition ID 0x%x\n", mm_sp_id);
- free(parts_info);
- 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
- Issues 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() != 0) {
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 > FFA_SHARED_MM_BUFFER_SIZE)
return EFI_INVALID_PARAMETER;
- /* Copy the data to the shared buffer */
- virt_shared_buf = map_sysmem((phys_addr_t)FFA_SHARED_MM_BUFFER_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;
+} +#endif
+/**
- select_ffa_mm_comms() - checks FF-A support availability
- Making sure FF-A is compiled in. If that's the case try to make sure
- the FF-A bus is probed successfully which means FF-A communication
- with secure world works and ready to be used.
- Return:
- 0: FF-A ready for use. Otherwise, failure
- */
+static efi_status_t select_ffa_mm_comms(void) +{
- efi_status_t ret = EFI_UNSUPPORTED;
- if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) {
struct udevice *dev;
uclass_first_device(UCLASS_FFA, &dev);
if (!dev) {
log_err("EFI: Cannot find FF-A bus device, cannot select FF-A comms\n");
ret = EFI_NOT_READY;
} else {
ret = EFI_SUCCESS;
}
- }
- return ret;
+}
/**
- mm_communicate() - Adjust the cmonnucation buffer to StandAlonneMM and send
- 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) @@ -162,7 +437,17 @@ static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) 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);
- ret = select_ffa_mm_comms();
- if (ret != EFI_SUCCESS) {
+#if (IS_ENABLED(CONFIG_OPTEE))
ret = optee_mm_communicate(comm_buf, dsize);
+#endif
- } else {
+#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
ret = ffa_mm_communicate(comm_buf, dsize);
+#endif
This is a bit confusing to read. Why does select_ffa_mm_comms() have to return and EFI return code? It has nothing to do with EFI to begin with.
I think you can make select_ffa_mm_comms() return an enum with OPTEE/FFA. The you can rewrite this a bit cleaner Something along the lines of
efi_status_t ret = EFI_INVALID_PARAMETER; enum transport_select;
transport_select = select_ffa_mm_comms(); if (transport_select == OPTEE) ret = optee_mm_communicate(comm_buf, dsize); else ret = ffa_mm_communicate(comm_buf, dsize);
While at it stub the ffa_mm_communicate() and optee_mm_communicate() functions so they are always defined but return EFI_UNSUPPORTED if the Kconfig is not selected while printing an appropriate error at the same time to inform the user which Kconfig option is missing.
- }
- if (ret != EFI_SUCCESS) { log_err("%s failed!\n", __func__); return ret;
@@ -258,6 +543,13 @@ efi_status_t EFIAPI get_max_payload(efi_uintn_t *size) goto out; } *size = var_payload->size;
- #if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
if (*size > FFA_SHARED_MM_BUFFER_SIZE)
*size = FFA_SHARED_MM_BUFFER_SIZE - MM_COMMUNICATE_HEADER_SIZE -
MM_VARIABLE_COMMUNICATE_SIZE;
- #endif
- /*
- There seems to be a bug in EDK2 miscalculating the boundaries and
- size checks, so deduct 2 more bytes to fulfill this requirement. Fix
@@ -697,7 +989,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);
/*
-- 2.25.1
Thanks /Ilias

On Fri, Mar 31, 2023 at 11:29:26AM +0300, Ilias Apalodimas wrote:
Hi Abdellatif,
On Tue, Mar 28, 2023 at 05:11:56PM +0100, Abdellatif El Khlifi wrote:
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.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Signed-off-by: Gowtham Suresh Kumar gowtham.sureshkumar@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v10:
- use the FF-A driver Uclass operations
- use uclass_first_device()
- addressing nits
v9: align how FF-A is used with FF-A discovery through DM
v8:
- isolate the compilation choices between FF-A and OP-TEE
- update partition_info_get() second argument to be an SP count
- pass NULL device pointer to the FF-A bus discovery and operations
v7:
- set the MM door bell event to use 64-bit direct messaging
- issue a compile time error when one of these macros are not found : FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_OFFSET, FFA_SHARED_MM_BUFFER_ADDR
- make mm_sp_svc_uuid static
- replace EINVAL with ENOMEM in ffa_discover_mm_sp_id() when calloc() fails
- improve use of unmap_sysmem() in ffa_mm_communicate()
v6:
- add FF-A runtime discovery at MM communication level
- drop EFI runtime support for FF-A MM communication
- revert the changes in include/mm_communication.h for efi_mm_communicate_header and smm_variable_access structures
v4:
- use the new FF-A driver interfaces
- discover MM partitions at runtime
- copy FF-A driver private data to EFI runtime section at ExitBootServices()
- drop use of FFA_ERR_STAT_SUCCESS error code
- replace EFI_BUFFER_TOO_SMALL with EFI_OUT_OF_RESOURCES in ffa_mm_communicate(). No need for efi_memcpy_runtime() anymore
- revert the error log in mm_communicate() in case of failure
- remove packed attribute from efi_mm_communicate_header and smm_variable_communicate_header
v2:
- set default values to 0 for FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_ADDR and MM_SP_UUID_DATA and add warnings
v1:
- introduce FF-A MM communication
include/mm_communication.h | 6 + lib/efi_loader/Kconfig | 14 +- lib/efi_loader/efi_variable_tee.c | 300 +++++++++++++++++++++++++++++- 3 files changed, 314 insertions(+), 6 deletions(-)
diff --git a/include/mm_communication.h b/include/mm_communication.h index e65fbde60d..87509fec3f 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. sughosh.ganu@linaro.org
- Copyright (C) 2020 Linaro Ltd. ilias.apalodimas@linaro.org
- Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
- Authors:
*/
Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
#ifndef _MM_COMMUNICATION_H_ @@ -13,6 +16,9 @@
#include <part_efi.h>
+/* MM service UUID string (big-endian format). This UUID is common across all MM SPs */ +#define MM_SP_UUID "33d532ed-e699-0942-c09c-a798d9cd722d"
/*
- Interface to the pseudo Trusted Application (TA), which provides a
- communication channel with the Standalone MM (Management Mode)
diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index c5835e6ef6..08a6b84101 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -55,13 +55,23 @@ 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"
- depends on OPTEE
bool "UEFI variables storage service via the trusted world"
depends on OPTEE || ARM_FFA_TRANSPORT 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 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 dfef18435d..a3060b4e7e 100644 --- a/lib/efi_loader/efi_variable_tee.c +++ b/lib/efi_loader/efi_variable_tee.c @@ -4,9 +4,14 @@
- Copyright (C) 2019 Linaro Ltd. sughosh.ganu@linaro.org
- Copyright (C) 2019 Linaro Ltd. ilias.apalodimas@linaro.org
- Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
- Authors:
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#include <common.h> +#include <dm.h> #include <efi.h> #include <efi_api.h> #include <efi_loader.h> @@ -15,6 +20,36 @@ #include <malloc.h> #include <mm_communication.h>
+#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
+#include <arm_ffa.h> +#include <cpu_func.h> +#include <mapmem.h>
+#ifndef FFA_SHARED_MM_BUFFER_SIZE +#error "FFA_SHARED_MM_BUFFER_SIZE must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_SIZE 0 +#endif
+#ifndef FFA_SHARED_MM_BUFFER_OFFSET +#error "FFA_SHARED_MM_BUFFER_OFFSET must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_OFFSET 0 +#endif
+#ifndef FFA_SHARED_MM_BUFFER_ADDR +#error "FFA_SHARED_MM_BUFFER_ADDR must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_ADDR 0 +#endif
+/* MM return codes */ +#define MM_SUCCESS (0)
+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 */ static efi_uintn_t max_payload_size; /* func + data */ @@ -24,6 +59,7 @@ struct mm_connection { u32 session; };
+#if (IS_ENABLED(CONFIG_OPTEE)) /**
- get_connection() - Retrieve OP-TEE session for a specific UUID.
@@ -143,13 +179,252 @@ static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize)
return ret; } +#endif
+#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
+/**
- ffa_notify_mm_sp() - Announce there is data in the shared buffer
- Notifies 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 = -1;
- struct udevice *dev;
- uclass_first_device(UCLASS_FFA, &dev);
- if (!dev) {
log_err("EFI: Cannot find FF-A bus device, notify MM SP failure\n");
return -ENODEV;
- }
- msg.data0 = FFA_SHARED_MM_BUFFER_OFFSET; /* x3 */
- ret = ffa_sync_send_receive(dev, mm_sp_id, &msg, 1);
- if (ret != 0)
We usually do if (!ret)
return ret;
- sp_event_ret = msg.data0; /* x3 */
- if (sp_event_ret == MM_SUCCESS)
return 0;
- /* Failure to notify the MM SP */
- return -EACCES;
+}
+/**
- 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_info *parts_info;
- struct udevice *dev;
- uclass_first_device(UCLASS_FFA, &dev);
- if (!dev) {
log_err("EFI: Cannot find FF-A bus device, MM SP discovery failure\n");
return -ENODEV;
- }
- /* Get from the driver the count of the SPs matching the UUID */
- ret = ffa_partition_info_get(dev, mm_sp_svc_uuid, &count, NULL);
- if (ret != 0) {
log_err("EFI: Failure in querying SPs count (%d), MM SP discovery failure\n", ret);
return ret;
- }
- if (!count) {
log_info("EFI: No MM partition found\n");
return ret;
- }
- /* Pre-allocate a buffer to be filled by the driver with ffa_partition_info structs */
- log_info("EFI: Pre-allocating %d partition(s) info structures\n", count);
- parts_info = calloc(count, sizeof(*parts_info));
- if (!parts_info)
return -ENOMEM;
- /* Ask the driver to fill the buffer with the SPs info */
- ret = ffa_partition_info_get(dev, mm_sp_svc_uuid, &count, parts_info);
- if (ret) {
log_err("EFI: Failure in querying SPs info (%d), MM SP discovery failure\n", ret);
free(parts_info);
return ret;
- }
- /* MM SPs found , use the first one */
- mm_sp_id = parts_info[0].id;
- log_info("EFI: MM partition ID 0x%x\n", mm_sp_id);
- free(parts_info);
- 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
- Issues 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() != 0) {
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 > FFA_SHARED_MM_BUFFER_SIZE)
return EFI_INVALID_PARAMETER;
- /* Copy the data to the shared buffer */
- virt_shared_buf = map_sysmem((phys_addr_t)FFA_SHARED_MM_BUFFER_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;
+} +#endif
+/**
- select_ffa_mm_comms() - checks FF-A support availability
- Making sure FF-A is compiled in. If that's the case try to make sure
- the FF-A bus is probed successfully which means FF-A communication
- with secure world works and ready to be used.
- Return:
- 0: FF-A ready for use. Otherwise, failure
- */
+static efi_status_t select_ffa_mm_comms(void) +{
- efi_status_t ret = EFI_UNSUPPORTED;
- if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) {
struct udevice *dev;
uclass_first_device(UCLASS_FFA, &dev);
if (!dev) {
log_err("EFI: Cannot find FF-A bus device, cannot select FF-A comms\n");
ret = EFI_NOT_READY;
} else {
ret = EFI_SUCCESS;
}
- }
- return ret;
+}
/**
- mm_communicate() - Adjust the cmonnucation buffer to StandAlonneMM and send
- 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) @@ -162,7 +437,17 @@ static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) 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);
- ret = select_ffa_mm_comms();
- if (ret != EFI_SUCCESS) {
+#if (IS_ENABLED(CONFIG_OPTEE))
ret = optee_mm_communicate(comm_buf, dsize);
+#endif
- } else {
+#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
ret = ffa_mm_communicate(comm_buf, dsize);
+#endif
This is a bit confusing to read. Why does select_ffa_mm_comms() have to return and EFI return code? It has nothing to do with EFI to begin with.
I think you can make select_ffa_mm_comms() return an enum with OPTEE/FFA. The you can rewrite this a bit cleaner Something along the lines of
efi_status_t ret = EFI_INVALID_PARAMETER; enum transport_select;
transport_select = select_ffa_mm_comms(); if (transport_select == OPTEE) ret = optee_mm_communicate(comm_buf, dsize); else ret = ffa_mm_communicate(comm_buf, dsize);
While at it stub the ffa_mm_communicate() and optee_mm_communicate() functions so they are always defined but return EFI_UNSUPPORTED if the Kconfig is not selected while printing an appropriate error at the same time to inform the user which Kconfig option is missing.
Thanks. This is done in v11, please review.
Cheers, Abdellatif
- }
- if (ret != EFI_SUCCESS) { log_err("%s failed!\n", __func__); return ret;
@@ -258,6 +543,13 @@ efi_status_t EFIAPI get_max_payload(efi_uintn_t *size) goto out; } *size = var_payload->size;
- #if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
if (*size > FFA_SHARED_MM_BUFFER_SIZE)
*size = FFA_SHARED_MM_BUFFER_SIZE - MM_COMMUNICATE_HEADER_SIZE -
MM_VARIABLE_COMMUNICATE_SIZE;
- #endif
- /*
- There seems to be a bug in EDK2 miscalculating the boundaries and
- size checks, so deduct 2 more bytes to fulfill this requirement. Fix
@@ -697,7 +989,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);
/*
-- 2.25.1
Thanks /Ilias

turn on EFI MM communication
On corstone1000 platform MM communication between u-boot and the secure world (Optee) is done using the FF-A bus.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
--- Changelog: ===============
v9: update copyright string
v8:
* drop OP-TEE configs from Corstone-1000 defconfig
v7:
* improve the definition of FFA_SHARED_MM_BUFFER_ADDR and FFA_SHARED_MM_BUFFER_OFFSET * update FFA_SHARED_MM_BUFFER_ADDR value
v6:
* corstone-1000: enable optee driver * corstone-1000: remove CONFIG_ARM_FFA_EFI_RUNTIME_MODE from the defconfig
v4:
* corstone-1000: turn on EFI MM communication
configs/corstone1000_defconfig | 2 ++ include/configs/corstone1000.h | 15 +++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-)
diff --git a/configs/corstone1000_defconfig b/configs/corstone1000_defconfig index 74250c7524..a7bfcbb2b9 100644 --- a/configs/corstone1000_defconfig +++ b/configs/corstone1000_defconfig @@ -52,3 +52,5 @@ CONFIG_DM_SERIAL=y CONFIG_USB=y CONFIG_USB_ISP1760=y CONFIG_ERRNO_STR=y +CONFIG_EFI_MM_COMM_TEE=y +CONFIG_ARM_FFA_TRANSPORT=y diff --git a/include/configs/corstone1000.h b/include/configs/corstone1000.h index 3347c11792..4ef1f05e40 100644 --- a/include/configs/corstone1000.h +++ b/include/configs/corstone1000.h @@ -1,9 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0+ */ /* - * (C) Copyright 2022 ARM Limited * (C) Copyright 2022 Linaro * Rui Miguel Silva rui.silva@linaro.org - * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com * * Configuration for Corstone1000. Parts were derived from other ARM * configurations. @@ -14,6 +16,15 @@
#include <linux/sizes.h>
+#define FFA_SHARED_MM_BUFFER_SIZE SZ_4K /* 4 KB */ + +/* + * shared buffer physical address used for communication between + * u-boot and the MM SP + */ +#define FFA_SHARED_MM_BUFFER_ADDR 0x02000000UL +#define FFA_SHARED_MM_BUFFER_OFFSET 0 + #define V2M_BASE 0x80000000
#define CFG_PL011_CLOCK 50000000

Hi Abdellatif,
On Wed, 29 Mar 2023 at 05:12, Abdellatif El Khlifi < abdellatif.elkhlifi@arm.com> wrote:
turn on EFI MM communication
On corstone1000 platform MM communication between u-boot and the secure world (Optee) is done using the FF-A bus.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v9: update copyright string
v8:
- drop OP-TEE configs from Corstone-1000 defconfig
v7:
- improve the definition of FFA_SHARED_MM_BUFFER_ADDR and FFA_SHARED_MM_BUFFER_OFFSET
- update FFA_SHARED_MM_BUFFER_ADDR value
v6:
- corstone-1000: enable optee driver
- corstone-1000: remove CONFIG_ARM_FFA_EFI_RUNTIME_MODE from the defconfig
v4:
- corstone-1000: turn on EFI MM communication
configs/corstone1000_defconfig | 2 ++ include/configs/corstone1000.h | 15 +++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-)
diff --git a/configs/corstone1000_defconfig
b/configs/corstone1000_defconfig
index 74250c7524..a7bfcbb2b9 100644 --- a/configs/corstone1000_defconfig +++ b/configs/corstone1000_defconfig @@ -52,3 +52,5 @@ CONFIG_DM_SERIAL=y CONFIG_USB=y CONFIG_USB_ISP1760=y CONFIG_ERRNO_STR=y +CONFIG_EFI_MM_COMM_TEE=y +CONFIG_ARM_FFA_TRANSPORT=y diff --git a/include/configs/corstone1000.h
b/include/configs/corstone1000.h
index 3347c11792..4ef1f05e40 100644 --- a/include/configs/corstone1000.h +++ b/include/configs/corstone1000.h @@ -1,9 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0+ */ /*
- (C) Copyright 2022 ARM Limited
- (C) Copyright 2022 Linaro
- Rui Miguel Silva rui.silva@linaro.org
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- Copyright 2022-2023 Arm Limited and/or its affiliates <
open-source-office@arm.com>
- Authors:
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- Configuration for Corstone1000. Parts were derived from other ARM
- configurations.
@@ -14,6 +16,15 @@
#include <linux/sizes.h>
+#define FFA_SHARED_MM_BUFFER_SIZE SZ_4K /* 4 KB */
+/*
- shared buffer physical address used for communication between
- u-boot and the MM SP
- */
+#define FFA_SHARED_MM_BUFFER_ADDR 0x02000000UL +#define FFA_SHARED_MM_BUFFER_OFFSET 0
#define V2M_BASE 0x80000000
#define CFG_PL011_CLOCK 50000000
2.25.1
As a higher-level question, could this whole FFA implementation code be put into Linux or fwupd?
Regards, Simon

On Sun, Apr 02, 2023 at 02:41:17PM +1200, Simon Glass wrote:
Hi Abdellatif,
On Wed, 29 Mar 2023 at 05:12, Abdellatif El Khlifi < abdellatif.elkhlifi@arm.com> wrote:
turn on EFI MM communication
On corstone1000 platform MM communication between u-boot and the secure world (Optee) is done using the FF-A bus.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v9: update copyright string
v8:
- drop OP-TEE configs from Corstone-1000 defconfig
v7:
- improve the definition of FFA_SHARED_MM_BUFFER_ADDR and FFA_SHARED_MM_BUFFER_OFFSET
- update FFA_SHARED_MM_BUFFER_ADDR value
v6:
- corstone-1000: enable optee driver
- corstone-1000: remove CONFIG_ARM_FFA_EFI_RUNTIME_MODE from the defconfig
v4:
- corstone-1000: turn on EFI MM communication
configs/corstone1000_defconfig | 2 ++ include/configs/corstone1000.h | 15 +++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-)
diff --git a/configs/corstone1000_defconfig
b/configs/corstone1000_defconfig
index 74250c7524..a7bfcbb2b9 100644 --- a/configs/corstone1000_defconfig +++ b/configs/corstone1000_defconfig @@ -52,3 +52,5 @@ CONFIG_DM_SERIAL=y CONFIG_USB=y CONFIG_USB_ISP1760=y CONFIG_ERRNO_STR=y +CONFIG_EFI_MM_COMM_TEE=y +CONFIG_ARM_FFA_TRANSPORT=y diff --git a/include/configs/corstone1000.h
b/include/configs/corstone1000.h
index 3347c11792..4ef1f05e40 100644 --- a/include/configs/corstone1000.h +++ b/include/configs/corstone1000.h @@ -1,9 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0+ */ /*
- (C) Copyright 2022 ARM Limited
- (C) Copyright 2022 Linaro
- Rui Miguel Silva rui.silva@linaro.org
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- Copyright 2022-2023 Arm Limited and/or its affiliates <
open-source-office@arm.com>
- Authors:
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- Configuration for Corstone1000. Parts were derived from other ARM
- configurations.
@@ -14,6 +16,15 @@
#include <linux/sizes.h>
+#define FFA_SHARED_MM_BUFFER_SIZE SZ_4K /* 4 KB */
+/*
- shared buffer physical address used for communication between
- u-boot and the MM SP
- */
+#define FFA_SHARED_MM_BUFFER_ADDR 0x02000000UL +#define FFA_SHARED_MM_BUFFER_OFFSET 0
#define V2M_BASE 0x80000000
#define CFG_PL011_CLOCK 50000000
2.25.1
As a higher-level question, could this whole FFA implementation code be put into Linux or fwupd?
Hi Simon, FF-A is basically a communication mechanism between the secure and non-secure world for Arm. Linux has support for it. fwupd should be completely agnostic to the protocol
Regards /Ilias
Regards, Simon

On Mon, Apr 03, 2023 at 12:59:06PM +0300, Ilias Apalodimas wrote:
On Sun, Apr 02, 2023 at 02:41:17PM +1200, Simon Glass wrote:
Hi Abdellatif,
On Wed, 29 Mar 2023 at 05:12, Abdellatif El Khlifi < abdellatif.elkhlifi@arm.com> wrote:
turn on EFI MM communication
On corstone1000 platform MM communication between u-boot and the secure world (Optee) is done using the FF-A bus.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v9: update copyright string
v8:
- drop OP-TEE configs from Corstone-1000 defconfig
v7:
- improve the definition of FFA_SHARED_MM_BUFFER_ADDR and FFA_SHARED_MM_BUFFER_OFFSET
- update FFA_SHARED_MM_BUFFER_ADDR value
v6:
- corstone-1000: enable optee driver
- corstone-1000: remove CONFIG_ARM_FFA_EFI_RUNTIME_MODE from the defconfig
v4:
- corstone-1000: turn on EFI MM communication
configs/corstone1000_defconfig | 2 ++ include/configs/corstone1000.h | 15 +++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-)
diff --git a/configs/corstone1000_defconfig
b/configs/corstone1000_defconfig
index 74250c7524..a7bfcbb2b9 100644 --- a/configs/corstone1000_defconfig +++ b/configs/corstone1000_defconfig @@ -52,3 +52,5 @@ CONFIG_DM_SERIAL=y CONFIG_USB=y CONFIG_USB_ISP1760=y CONFIG_ERRNO_STR=y +CONFIG_EFI_MM_COMM_TEE=y +CONFIG_ARM_FFA_TRANSPORT=y diff --git a/include/configs/corstone1000.h
b/include/configs/corstone1000.h
index 3347c11792..4ef1f05e40 100644 --- a/include/configs/corstone1000.h +++ b/include/configs/corstone1000.h @@ -1,9 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0+ */ /*
- (C) Copyright 2022 ARM Limited
- (C) Copyright 2022 Linaro
- Rui Miguel Silva rui.silva@linaro.org
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- Copyright 2022-2023 Arm Limited and/or its affiliates <
open-source-office@arm.com>
- Authors:
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- Configuration for Corstone1000. Parts were derived from other ARM
- configurations.
@@ -14,6 +16,15 @@
#include <linux/sizes.h>
+#define FFA_SHARED_MM_BUFFER_SIZE SZ_4K /* 4 KB */
+/*
- shared buffer physical address used for communication between
- u-boot and the MM SP
- */
+#define FFA_SHARED_MM_BUFFER_ADDR 0x02000000UL +#define FFA_SHARED_MM_BUFFER_OFFSET 0
#define V2M_BASE 0x80000000
#define CFG_PL011_CLOCK 50000000
2.25.1
As a higher-level question, could this whole FFA implementation code be put into Linux or fwupd?
Hi Simon, FF-A is basically a communication mechanism between the secure and non-secure world for Arm. Linux has support for it. fwupd should be completely agnostic to the protocol
Hi Simon,
I'd like to add to Ilias's explanation the following helpful resources:
- The patchset cover letter for a high level overview of what FF-A is about [1]. - A closer look at the FF-A implementation in U-Boot the readme [2].
Cheers
[1]: https://lore.kernel.org/all/20230328161157.219375-1-abdellatif.elkhlifi@arm.... [2]: doc/arch/arm64.ffa.rst
Regards /Ilias
Regards, Simon

Adding support for Arm FF-A v1.0 (Arm Firmware Framework for Armv8-A) [A].
FF-A describes interfaces (ABIs) that standardize communication between the Secure World and Normal World. These interfaces enable a pair of software sandboxes to communicate with each other. A sandbox aka partition could be a VM in the Normal or Secure world, an application in S-EL0, or a Trusted OS in S-EL1.
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.
=> dm tree
Class Index Probed Driver Name ----------------------------------------------------------- ... firmware 0 [ + ] psci |-- psci ffa 0 [ ] arm_ffa | `-- arm_ffa ...
Clients are able to probe then use the FF-A bus by calling the DM class searching APIs (e.g: uclass_get_device_by_name).
This implementation of the specification provides support for Aarch64.
The FF-A driver uses the SMC ABIs defined by the FF-A specification to:
- Discover the presence of secure partitions (SPs) of interest - Access an SP's service through communication protocols e.g. EFI MM communication protocol
The FF-A support provides the following features:
- Being generic by design and can be used by any Arm 64-bit platform - A Uclass driver providing generic FF-A methods and driver operations - An Arm FF-A device driver - FF-A support can be compiled and used without EFI - Support for SMCCCv1.2 x0-x17 registers - Support for SMC32 calling convention - Support for 32-bit and 64-bit FF-A direct messaging - A new command called armffa is provided as an example of how to access the FF-A bus - Support for FF-A MM communication (compatible with EFI boot time) - Enabling FF-A and MM communication in Corstone1000 platform as a use case - A sandbox emulator for Arm FF-A - An FF-A sandbox device driver - Sandbox FF-A test cases
For more details about the FF-A support please refer to [B] and refer to [C] for how to use the armffa command.
Please find at [D] an example of the expected boot logs when enabling FF-A support for a platform. In this example the platform is Corstone1000. But it can be any Arm 64-bit platform.
Changelog of the major changes: ===========================
v11:
* move ffa_try_discovery() from the uclass to the Arm FF-A driver * rename ffa_try_discovery() to arm_ffa_discover() * add arm_ prefix to the Arm FF-A driver functions * use U_BOOT_CMD_WITH_SUBCMDS for armffa command * store the sandbox emulator pointer in the FF-A device uc_priv (struct ffa_priv) * set the emulator as parent of the sandbox FF-A device * rename select_ffa_mm_comms() to select_mm_comms() * improve the logic of MM transport selection in mm_communicate() * use ut_asserteq_mem() in uuid_str_to_le_bin test case * address nits
v10: [10]
* provide the FF-A driver operations through the Uclass (arm-ffa-uclass.c) * move the generic FF-A methods to the Uclass * keep Arm specific methods in the Arm driver (arm-ffa.c renamed from core.c) * split the FF-A sandbox support into an emulator (ffa-emul-uclass.c) and a driver (sandbox_ffa.c) * use the FF-A driver Uclass operations by clients (armffa command, tests, MM comms) * use uclass_first_device to search and probe the FF-A device (whether it is on Arm or on sandbox) * address nits
v9: [9]
* integrate the FF-A bus discovery in the DM and use ARM_SMCCC_FEATURES for binding * align FF-A sandbox driver with FF-A discovery through DM * use DM class APIs to probe and interact with the FF-A bus (in FF-A MM comms, armffa command, sandbox tests) * add documentation for the armffa command: doc/usage/cmd/armffa.rst * introduce testcase for uuid_str_to_le_bin
v8: [8]
* pass the FF-A bus device to the bus operations * isolate the compilation choices between FF-A and OP-TEE * drop OP-TEE configs from Corstone-1000 defconfig * make ffa_get_partitions_info() second argument to be an SP count in both modes
v7: [7]
* add support for 32-bit direct messaging (now we have 32-bit and 64-bit support) * set the MM door bell event to use 64-bit direct messaging * issue a compile time error when one of these macros are not found : FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_OFFSET, FFA_SHARED_MM_BUFFER_ADDR * make uuid_str_to_le_bin() implementation similar to uuid_str_to_bin() by using same APIs
v6: [6]
* remove clearing x0-x17 registers after SMC calls * drop use of EFI runtime support for FF-A (We decided with Linaro to add this later) * drop discovery from initcalls (discovery will be on demand by FF-A users) * add FF-A runtime discovery at MM communication level * update the documentation and move it to doc/arch/arm64.ffa.rst
v5: [5]
* move changelogs in each commit to the changes section
v4: [4]
* add FF-A support README (doc/README.ffa.drv) * improving error handling by mapping the FF-A errors to standard errors and logs * replacing panics with an error log * align sandbox driver and tests with the new FF-A driver interfaces and new way of error handling * use the new FF-A driver interfaces for MM communication * discover MM partitions at runtime * copy FF-A driver private data to EFI runtime section at ExitBootServices() * moving the FF-A driver work to drivers/firmware/arm-ffa * improving features discovery in FFA_FEATURES * add remove/unbind functions to the FF-A core device * improve how the driver behaves when bus discovery is done more than once * move clearing x0-x17 registers code into a new macro like done in the linux kernel * enable EFI MM communication for the Corstone1000 platform
v3: [3]
* port x0-x17 registers support from linux kernel as defined by SMCCCv1.2 * align the interfaces of the u-boot FF-A driver with those in the linux FF-A driver * remove the FF-A helper layer * make the u-boot FF-A driver independent from EFI * provide an optional config that enables copying the driver data to EFI runtime section at ExitBootServices service * use 64-bit version of FFA_RXTX_MAP, FFA_MSG_SEND_DIRECT_{REQ, RESP} * update armffa command with the new driver interfaces
v2 [2]:
* make FF-A bus discoverable using device_{bind, probe} APIs * remove device tree support
v1 [1]:
* introduce FF-A bus driver with device tree support * introduce armffa command * introduce FF-A Sandbox driver * add FF-A Sandbox test cases * introduce FF-A MM communication
Cheers, Abdellatif
List of previous patches:
[1]: https://lore.kernel.org/all/20220329151659.16894-1-abdellatif.elkhlifi@arm.c... [2]: https://lore.kernel.org/all/20220415122803.16666-1-abdellatif.elkhlifi@arm.c... [3]: https://lore.kernel.org/all/20220801172053.20163-1-abdellatif.elkhlifi@arm.c... [4]: https://lore.kernel.org/all/20220926101723.9965-1-abdellatif.elkhlifi@arm.co... [5]: https://lore.kernel.org/all/20220926140827.15125-1-abdellatif.elkhlifi@arm.c... [6]: https://lore.kernel.org/all/20221013103857.614-1-abdellatif.elkhlifi@arm.com... [7]: https://lore.kernel.org/all/20221107192055.21669-1-abdellatif.elkhlifi@arm.c... [8]: https://lore.kernel.org/all/20221122131751.22747-1-abdellatif.elkhlifi@arm.c... [9]: https://lore.kernel.org/all/20230310141016.137986-1-abdellatif.elkhlifi@arm.... [10]: https://lore.kernel.org/all/20230328161157.219375-1-abdellatif.elkhlifi@arm....
More details:
[A]: https://developer.arm.com/documentation/den0077/latest/ [B]: doc/arch/arm64.ffa.rst [C]: doc/usage/cmd/armffa.rst [D]: example of boot logs when enabling FF-A
``` U-Boot 2023.01 (Mar 27 2023 - 13:48:33 +0000) corstone1000 aarch64
DRAM: 2 GiB Arm FF-A framework discovery Using Arm SMC for FF-A conduit FF-A driver 1.0 FF-A framework 1.0 FF-A versions are compatible ... Hit any key to stop autoboot: 0 Loading kernel from 0x083EE000 to memory ... ... FF-A endpoint ID is 0 Using 1 4KB page(s) for FF-A RX/TX buffers size FF-A RX buffer at virtual address 00000000fdf4f000 FF-A TX buffer at virtual address 00000000fdf51000 FF-A RX/TX buffers mapped Reading FF-A partitions data from the RX buffer FF-A partition ID 8001 : info cached FF-A partition ID 8002 : info cached FF-A partition ID 8003 : info cached 3 FF-A partition(s) found and cached Preparing for checking FF-A partitions count Searching FF-A partitions using the provided UUID No FF-A partition found. Querying framework ... Reading FF-A partitions data from the RX buffer Number of FF-A partition(s) matching the UUID: 1 EFI: Pre-allocating 1 partition(s) info structures Preparing for filling FF-A partitions info Searching FF-A partitions using the provided UUID FF-A partition ID 8003 matches the provided UUID EFI: MM partition ID 0x8003 EFI: Corstone1000: Capsule shared buffer at 0x80000000 , size 8192 pages Booting /MemoryMapped(0x0,0x88200000,0xf00000) EFI stub: Booting Linux Kernel... EFI stub: Using DTB from configuration table EFI stub: Exiting boot services... unmapping FF-A RX/TX buffers Freeing FF-A RX/TX buffers Booting Linux on physical CPU 0x0000000000 [0x411fd040] 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 ```
Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Rob Herring robh@kernel.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Achin Gupta achin.gupta@arm.com Cc: Drew Reed Drew.Reed@arm.com Cc: Xueliang Zhong Xueliang.Zhong@arm.com
Abdellatif El Khlifi (10): arm64: smccc: add support for SMCCCv1.2 x0-x17 registers lib: uuid: introduce uuid_str_to_le_bin function lib: uuid: introduce testcase for uuid_str_to_le_bin arm_ffa: introduce Arm FF-A support arm_ffa: introduce armffa command arm_ffa: introduce sandbox FF-A support arm_ffa: introduce sandbox test cases for UCLASS_FFA arm_ffa: introduce armffa command Sandbox test arm_ffa: efi: introduce FF-A MM communication arm_ffa: efi: corstone1000: enable MM communication
MAINTAINERS | 18 + arch/arm/cpu/armv8/smccc-call.S | 57 +- arch/arm/lib/asm-offsets.c | 16 + arch/sandbox/dts/sandbox.dtsi | 8 + arch/sandbox/dts/test.dts | 8 + arch/sandbox/include/asm/sandbox_arm_ffa.h | 72 ++ .../include/asm/sandbox_arm_ffa_priv.h | 133 ++ cmd/Kconfig | 10 + cmd/Makefile | 2 + cmd/armffa.c | 212 +++ configs/corstone1000_defconfig | 2 + configs/sandbox64_defconfig | 1 + configs/sandbox_defconfig | 1 + doc/arch/arm64.ffa.rst | 294 +++++ doc/arch/index.rst | 1 + doc/arch/sandbox/sandbox.rst | 1 + doc/usage/cmd/armffa.rst | 105 ++ doc/usage/index.rst | 1 + drivers/Makefile | 1 + drivers/firmware/Kconfig | 1 + drivers/firmware/arm-ffa/Kconfig | 42 + drivers/firmware/arm-ffa/Makefile | 16 + drivers/firmware/arm-ffa/arm-ffa-uclass.c | 1143 +++++++++++++++++ drivers/firmware/arm-ffa/arm-ffa.c | 127 ++ drivers/firmware/arm-ffa/ffa-emul-uclass.c | 738 +++++++++++ drivers/firmware/arm-ffa/sandbox_ffa.c | 119 ++ include/arm_ffa.h | 217 ++++ include/arm_ffa_priv.h | 271 ++++ include/configs/corstone1000.h | 15 +- include/dm/uclass-id.h | 7 + include/linux/arm-smccc.h | 45 + include/mm_communication.h | 13 + include/uuid.h | 15 + lib/efi_loader/Kconfig | 14 +- lib/efi_loader/efi_variable_tee.c | 312 ++++- lib/uuid.c | 48 + test/cmd/Makefile | 2 + test/cmd/armffa.c | 39 + test/dm/Makefile | 2 + test/dm/ffa.c | 258 ++++ test/lib/Makefile | 1 + test/lib/uuid.c | 41 + 42 files changed, 4420 insertions(+), 9 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 cmd/armffa.c create mode 100644 doc/arch/arm64.ffa.rst create mode 100644 doc/usage/cmd/armffa.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/ffa-emul-uclass.c create mode 100644 drivers/firmware/arm-ffa/sandbox_ffa.c create mode 100644 include/arm_ffa.h create mode 100644 include/arm_ffa_priv.h create mode 100644 test/cmd/armffa.c create mode 100644 test/dm/ffa.c create mode 100644 test/lib/uuid.c

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 abdellatif.elkhlifi@arm.com Reviewed-by: Jens Wiklander jens.wiklander@linaro.org Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org
--- Changelog: ===============
v9:
* update the copyright string
v7:
* improve indentation of ARM_SMCCC_1_2_REGS_Xn_OFFS
v4:
* rename the commit title and improve description new commit title: the current
v3:
* port x0-x17 registers support from linux kernel as defined by SMCCCv1.2 commit title: arm64: smccc: add Xn registers support used by SMC calls
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 dc92b28777..93f66d3366 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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com +*/ #include <linux/linkage.h> #include <linux/arm-smccc.h> #include <generated/asm-offsets.h> @@ -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 6de0ce9152..181a8ac4c2 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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> @@ -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 e1d09884a1..f44e9e8f93 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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */ #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

convert UUID string to little endian binary data
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
--- Changelog: ===============
v9:
* add a full function prototype description in uuid.h
v8:
* use simple_strtoull() in uuid_str_to_le_bin() to support 32-bit platforms
v7:
* rename be_uuid_str_to_le_bin() to uuid_str_to_le_bin() * make uuid_str_to_le_bin() implementation similar to uuid_str_to_bin() by using same APIs
v4:
* rename ffa_uuid_str_to_bin to be_uuid_str_to_le_bin and put in a standalone commit (the current)
v3:
* introduce ffa_uuid_str_to_bin (provided by arm_ffa: introduce Arm FF-A low-level driver)
include/uuid.h | 15 +++++++++++++++ lib/uuid.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+)
diff --git a/include/uuid.h b/include/uuid.h index 4a4883d3b5..89b93e642b 100644 --- a/include/uuid.h +++ b/include/uuid.h @@ -2,6 +2,10 @@ /* * Copyright (C) 2014 Samsung Electronics * Przemyslaw Marczak p.marczak@samsung.com + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */ #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 96e1af3c8b..45f325d964 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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> @@ -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. *

provide a test case
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Simon Glass sjg@chromium.org
--- Changelog: ===============
v11:
* use ut_asserteq_mem()
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 d2e245e5e9..086e4cb3df 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1607,3 +1607,8 @@ S: Maintained F: arch/arm/dts/ls1021a-twr-u-boot.dtsi F: drivers/crypto/fsl/ F: include/fsl_sec.h + +UUID testing +M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com +S: Maintained +F: test/lib/uuid.c diff --git a/test/lib/Makefile b/test/lib/Makefile index e0bd9e04e8..e75a263e6a 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 0000000000..e24331a136 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <uuid.h> +#include <test/lib.h> +#include <test/test.h> +#include <test/ut.h> + +/* 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);

On Wed, 12 Apr 2023 at 03:43, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
provide a test case
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Simon Glass sjg@chromium.org
Changelog:
v11:
- use ut_asserteq_mem()
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 d2e245e5e9..086e4cb3df 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1607,3 +1607,8 @@ S: Maintained F: arch/arm/dts/ls1021a-twr-u-boot.dtsi F: drivers/crypto/fsl/ F: include/fsl_sec.h
+UUID testing +M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com +S: Maintained +F: test/lib/uuid.c diff --git a/test/lib/Makefile b/test/lib/Makefile index e0bd9e04e8..e75a263e6a 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 0000000000..e24331a136 --- /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 open-source-office@arm.com
- Authors:
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include <common.h> +#include <uuid.h> +#include <test/lib.h> +#include <test/test.h> +#include <test/ut.h>
+/* 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);
2.25.1
Reviewed-by: Simon Glass sjg@chromium.org

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 abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
--- Changelog: ===============
v11:
* move ffa_try_discovery() from the uclass to the Arm FF-A driver * rename ffa_try_discovery() to arm_ffa_discover() * pass dev as an argument of arm_ffa_discover() * add arm_ prefix to the Arm FF-A driver functions * add emul field in struct ffa_discovery_info * address nits
v10:
* provide the driver operations through the Uclass * move the generic FF-A methods to the Uclass * keep Arm specific methods in the Arm driver (arm-ffa.c) * rename core.c to arm-ffa.c * address nits
v9:
* integrate the FF-A bus discovery in the DM and use ARM_SMCCC_FEATURES for binding
v8:
* make ffa_get_partitions_info() second argument to be an SP count in both modes * update ffa_bus_prvdata_get() to return a pointer rather than a pointer address * remove packing from ffa_partition_info and ffa_send_direct_data structures * pass the FF-A bus device to the bus operations
v7:
* add support for 32-bit direct messaging * rename be_uuid_str_to_le_bin() to uuid_str_to_le_bin() * improve the declaration of error handling mapping * stating in doc/arch/arm64.ffa.rst that EFI runtime is not supported
v6:
* drop use of EFI runtime support (We decided with Linaro to add this later) * drop discovery from initcalls (discovery will be on demand by FF-A users) * set the alignment of the RX/TX buffers to the larger translation granule size * move FF-A RX/TX buffers unmapping at ExitBootServices() to a separate commit * update the documentation and move it to doc/arch/arm64.ffa.rst
v4:
* add doc/README.ffa.drv * moving the FF-A driver work to drivers/firmware/arm-ffa * use less #ifdefs in lib/efi_loader/efi_boottime.c and replace #if defined by #if CONFIG_IS_ENABLED * improving error handling by mapping the FF-A errors to standard errors and logs * replacing panics with an error log and returning an error code * improving features discovery in FFA_FEATURES by introducing rxtx_min_pages private data field * add ffa_remove and ffa_unbind functions * improve how the driver behaves when bus discovery is done more than once
v3:
* align the interfaces of the U-Boot FF-A driver with those in the linux FF-A driver * remove the FF-A helper layer * make the U-Boot FF-A driver independent from EFI * provide an optional config that enables copying the driver data to EFI runtime section at ExitBootServices service * use 64-bit version of FFA_RXTX_MAP, FFA_MSG_SEND_DIRECT_{REQ, RESP}
v2:
* make FF-A bus discoverable using device_{bind, probe} APIs * remove device tree support
v1:
* introduce FF-A bus driver with device tree support
MAINTAINERS | 8 + doc/arch/arm64.ffa.rst | 271 ++++ 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 | 1143 +++++++++++++++++ drivers/firmware/arm-ffa/arm-ffa.c | 127 ++ .../firmware/arm-ffa/sandbox_arm_ffa_priv.h | 14 + include/arm_ffa.h | 217 ++++ include/arm_ffa_priv.h | 271 ++++ include/dm/uclass-id.h | 6 + 13 files changed, 2104 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 086e4cb3df..76f0f276ce 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 abdellatif.elkhlifi@arm.com +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 sbabic@denx.de M: Fabio Estevam festevam@gmail.com diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst new file mode 100644 index 0000000000..466e77e3cd --- /dev/null +++ b/doc/arch/arm64.ffa.rst @@ -0,0 +1,271 @@ +.. 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 sandboxes to +communicate with each other. A sandbox aka 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 is 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 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 +specification v1.0 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. + +For more details please refer to the FF-A v1.0 spec: +https://documentation-service.arm.com/static/5fb7e8a6ca04df4095c1d65e?token= + +Hypervisors are supported if they are configured to trap SMC calls. + +The FF-A support uses 64-bit registers as per SMCCCv1.2 specification. + +For more details please refer to the SMC Calling Convention v1.2 spec: +https://documentation-service.arm.com/static/5f8edaeff86e16515cdbe4c6?token= + +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(). +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: + + - updating uc_priv with discovery information + - 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. + +FF-A device destruction +------------------------- + +When the FF-A device is removed by the DM, RX/TX buffers are automatically +unmapped and freed. + +For example, at EFI efi_exit_boot_services() active devices are automatically removed +by dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL). + +By consequence, the FF-A RX/TX are unmapped automatically. + +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() and this is done +automatically at efi_exit_boot_services(). + +If RX/TX buffers created by U-Boot are not unmapped and by consequence becoming +available at EFI runtime, secure world will get confused about RX/TX buffers +ownership (U-Boot vs kernel). + +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. + +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 (Mar 27 2023 - 13:48:33 +0000) corstone1000 aarch64 + + DRAM: 2 GiB + Arm FF-A framework discovery + Using Arm SMC for FF-A conduit + FF-A driver 1.0 + FF-A framework 1.0 + FF-A versions are compatible + ... + Hit any key to stop autoboot: 0 + Loading kernel from 0x083EE000 to memory ... + ... + FF-A endpoint ID is 0 + Using 1 4KB page(s) for FF-A RX/TX buffers size + FF-A RX buffer at virtual address 00000000fdf4f000 + FF-A TX buffer at virtual address 00000000fdf51000 + FF-A RX/TX buffers mapped + Reading FF-A partitions data from the RX buffer + FF-A partition ID 8001 : info cached + FF-A partition ID 8002 : info cached + FF-A partition ID 8003 : info cached + 3 FF-A partition(s) found and cached + Preparing for checking FF-A partitions count + Searching FF-A partitions using the provided UUID + No FF-A partition found. Querying framework ... + Reading FF-A partitions data from the RX buffer + Number of FF-A partition(s) matching the UUID: 1 + EFI: Pre-allocating 1 partition(s) info structures + Preparing for filling FF-A partitions info + Searching FF-A partitions using the provided UUID + FF-A partition ID 8003 matches the provided UUID + EFI: MM partition ID 0x8003 + EFI: Corstone1000: Capsule shared buffer at 0x80000000 , size 8192 pages + Booting /MemoryMapped(0x0,0x88200000,0xf00000) + EFI stub: Booting Linux Kernel... + EFI stub: Using DTB from configuration table + EFI stub: Exiting boot services... + unmapping FF-A RX/TX buffers + Freeing FF-A RX/TX buffers + Booting Linux on physical CPU 0x0000000000 [0x411fd040] + 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 abdellatif.elkhlifi@arm.com diff --git a/doc/arch/index.rst b/doc/arch/index.rst index b8da4b8c8e..2f916f4026 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 58be410135..885fdef4dc 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -112,6 +112,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 eae1c8ddc9..8789b1ea14 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 0000000000..9200c8028b --- /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 0000000000..11b1766285 --- /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 open-source-office@arm.com +# +# Authors: +# Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + +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 0000000000..c3ad19b8e7 --- /dev/null +++ b/drivers/firmware/arm-ffa/arm-ffa-uclass.c @@ -0,0 +1,1143 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ +#include <common.h> +#include <arm_ffa.h> +#include <arm_ffa_priv.h> +#include <dm.h> +#include <log.h> +#include <malloc.h> +#include <string.h> +#include <uuid.h> +#include <asm/global_data.h> +#include <dm/device-internal.h> +#include <dm/devres.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* FF-A discovery information */ +struct ffa_discovery_info dscvry_info; + +/* 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) */ + +/** + * 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; + + if (!dscvry_info.invoke_ffa_fn) + return -EINVAL; + + dscvry_info.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"); + + dscvry_info.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); + + uc_priv->dscvry_info.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_info("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: + * + * buf_4k_pages points to 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; + struct ffa_priv *uc_priv = dev_get_uclass_priv(dev); + + uc_priv->dscvry_info.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_info("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. + * + * 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_info("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_info("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_info("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 + */ + + uc_priv->dscvry_info.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_info("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 = NULL; + + log_info("unmapping FF-A RX/TX buffers\n"); + + uc_priv = dev_get_uclass_priv(dev); + + uc_priv->dscvry_info.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; + struct ffa_priv *uc_priv = dev_get_uclass_priv(dev); + + uc_priv->dscvry_info.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_info("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_info("FF-A partition ID %x : info cached\n", + uc_priv->partitions.descs[desc_idx].info.id); + } + + uc_priv->partitions.count = count; + + log_info("%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; + struct ffa_priv *uc_priv = dev_get_uclass_priv(dev); + + /* + * 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; + } + + uc_priv->dscvry_info.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 + * + * The passed arguments: + * Mode 1: When getting from the driver the number of + * secure partitions: + * @uuid_str: pointer to the UUID string + * @sp_count: pointer to the variable that contains the number of partitions + * The variable will be set by the driver + * @buffer: NULL + * + * Mode 2: When requesting the driver to return the + * partitions information: + * @dev: The FF-A bus device + * @uuid_str: pointer to the UUID string + * @sp_count: pointer to the variable that contains the number of empty partition descriptors + * The variable will be read by the driver + * @buffer: pointer to SPs information buffer + * (allocated by the client and contains empty @sp_count descriptors). + * The buffer will be filled by the driver + * + * 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(). + * + * When invoked through a client request, ffa_get_partitions_info_hdlr() should be + * called twice. First call is to get from the driver the number of secure + * partitions (SPs) associated to a particular UUID. + * Then, the caller (client) allocates the buffer to host the SPs data and + * issues a 2nd call. Then, the driver fills the SPs data in the pre-allocated + * buffer. + * + * To achieve the mechanism described above, ffa_get_partitions_info_hdlr() uses the + * following functions: + * ffa_read_partitions_info + * ffa_query_partitions_info + * + * Return: + * + * @sp_count: When pointing to the number of partitions variable, the number is + * set by the driver. + * When pointing to the partitions information buffer size, the buffer will be + * filled by the driver. + * + * 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_info *buffer) +{ + /* + * fill_data: + * 0: return the SP count + * 1: fill SP data and return it to the caller + */ + bool fill_data = 0; + u32 desc_idx, client_desc_idx; + struct ffa_partition_uuid part_uuid = {0}; + u32 sp_found = 0; + struct ffa_priv *uc_priv = NULL; + + 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 size/count provided\n"); + return -EINVAL; + } + + if (uuid_str_to_le_bin(uuid_str, (unsigned char *)&part_uuid)) { + log_err("invalid UUID\n"); + return -EINVAL; + } + + if (!buffer) { + /* Mode 1: getting the number of secure partitions */ + + fill_data = 0; + + log_info("Preparing for checking FF-A partitions count\n"); + + } else if (*sp_count) { + /* Mode 2: retrieving the partitions information */ + + fill_data = 1; + + client_desc_idx = 0; + + log_info("Preparing for filling FF-A partitions info\n"); + + } else { + log_err("invalid function arguments provided\n"); + return -EINVAL; + } + + log_info("Searching FF-A partitions using the provided UUID\n"); + + /* Search in the cached partitions */ + for (desc_idx = 0; + desc_idx < uc_priv->partitions.count; + desc_idx++) { + if (ffa_uuid_are_identical(&uc_priv->partitions.descs[desc_idx].sp_uuid, + &part_uuid)) { + log_info("FF-A partition ID %x matches the provided UUID\n", + uc_priv->partitions.descs[desc_idx].info.id); + + sp_found++; + + if (fill_data) { + /* Trying to fill the partition info in the input buffer */ + + if (client_desc_idx < *sp_count) { + buffer[client_desc_idx++] = + uc_priv->partitions.descs[desc_idx].info; + continue; + } + + log_err("failed to fill client descriptor, buffer full\n"); + return -ENOBUFS; + } + } + } + + if (!sp_found) { + int ret; + + log_info("No FF-A partition found. Querying framework ...\n"); + + ret = ffa_query_partitions_info(dev, &part_uuid, &sp_found); + + if (ret == 0) { + if (!fill_data) { + *sp_count = sp_found; + + log_info("Number of FF-A partition(s) matching the UUID: %d\n", + sp_found); + } else { + /* + * If SPs data detected, they are already in uc_priv. + * Retry searching SP data again to return them + * to the caller + */ + if (sp_found) + ret = ffa_get_partitions_info_hdlr(dev, uuid_str, sp_count, + buffer); + else + ret = -ENODATA; + } + } + + return ret; + } + + /* Partition(s) found */ + if (!fill_data) + *sp_count = sp_found; + + 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 = NULL; + + uc_priv = dev_get_uclass_priv(dev); + + if (!uc_priv->dscvry_info.invoke_ffa_fn) + return -EINVAL; + + /* 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); + } + + uc_priv->dscvry_info.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)) + uc_priv->dscvry_info.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 + * + * The passed arguments: + * Mode 1: When getting from the driver the number of + * secure partitions: + * @uuid_str: pointer to the UUID string + * @sp_count: pointer to the variable that contains the number of partitions + * The variable will be set by the driver + * @buffer: NULL + * + * Mode 2: When requesting the driver to return the + * partitions information: + * @dev: The FF-A bus device + * @uuid_str: pointer to the UUID string + * @sp_count: pointer to the variable that contains the number of empty partition descriptors + * The variable will be read by the driver + * @buffer: pointer to SPs information buffer + * (allocated by the client and contains empty @sp_count descriptors). + * The buffer will be filled by the driver + * + * Driver operation for FFA_PARTITION_INFO_GET. + * Please see ffa_get_partitions_info_hdlr() description for more details. + * + * Return: + * + * @sp_count: When pointing to the number of partitions variable, the number is + * set by the driver. + * When pointing to the partitions information buffer size, the buffer will be + * filled by the driver. + * + * 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_info *buffer) +{ + 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, buffer); +} + +/** + * 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: + * - updating uc_priv with discovery information + * - 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; + struct ffa_priv *uc_priv = dev_get_uclass_priv(dev); + + uc_priv->dscvry_info = dscvry_info; + + 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 0000000000..254f747010 --- /dev/null +++ b/drivers/firmware/arm-ffa/arm-ffa.c @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <arm_ffa.h> +#include <arm_ffa_priv.h> +#include <dm.h> +#include <log.h> +#include <asm/global_data.h> +#include <dm/device-internal.h> +#include <linux/errno.h> + +DECLARE_GLOBAL_DATA_PTR; + +/** + * __arm_ffa_fn_smc() - 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 + */ +static void __arm_ffa_fn_smc(ffa_value_t args, ffa_value_t *res) +{ + arm_smccc_1_2_smc(&args, res); +} + +/** + * arm_ffa_set_smc() - Set the SMC conduit + * @dev: The Arm FF-A bus device (arm_ffa) + * + * Select the SMC conduit by setting the FF-A ABI invoke function + * to the function executing the SMC call. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int arm_ffa_set_smc(struct udevice *dev) +{ + dscvry_info.invoke_ffa_fn = __arm_ffa_fn_smc; + log_info("Using Arm SMC for FF-A conduit\n"); + + return 0; +} + +/** + * 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 = arm_ffa_set_smc(dev); + if (ret) + return false; + + 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 0000000000..4338f9c9b1 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#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 0000000000..e14bc082f6 --- /dev/null +++ b/include/arm_ffa.h @@ -0,0 +1,217 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __ARM_FFA_H +#define __ARM_FFA_H + +#include <linux/printk.h> + +/* + * 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_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 */ +}; + +/* FF-A discovery information */ +extern struct ffa_discovery_info dscvry_info; + +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_info *buffer); + 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_info *buffer); + +/** + * ffa_get_partitions_info_hdlr() - FFA_PARTITION_INFO_GET handler function + * + * The passed arguments: + * Mode 1: When getting from the driver the number of + * secure partitions: + * @uuid_str: pointer to the UUID string + * @sp_count: pointer to the variable that contains the number of partitions + * The variable will be set by the driver + * @buffer: NULL + * + * Mode 2: When requesting the driver to return the + * partitions information: + * @dev: The arm_ffa bus device + * @uuid_str: pointer to the UUID string + * @sp_count: pointer to the variable that contains the number of empty partition descriptors + * The variable will be read by the driver + * @buffer: pointer to SPs information buffer + * (allocated by the client and contains empty @sp_count descriptors). + * The buffer will be filled by the driver + * + * This function queries the secure partition data from + * the private data structure. If not found, it invokes 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(). + * + * When invoked through a client request, ffa_get_partitions_info_hdlr() should be + * called twice. First call is to get from the driver the number of secure + * partitions (SPs) associated to a particular UUID. + * Then, the caller (client) allocates the buffer to host the SPs data and + * issues a 2nd call. Then, the driver fills the SPs data in the pre-allocated + * buffer. + * + * To achieve the mechanism described above, ffa_get_partitions_info_hdlr() uses the + * following functions: + * ffa_read_partitions_info + * ffa_query_partitions_info + * + * Return: + * + * @sp_count: When pointing to the number of partitions variable, the number is + * set by the driver. + * When pointing to the partitions information buffer size, the buffer will be + * filled by the driver. + * + * 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_info *buffer); + +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 0000000000..8851b7b410 --- /dev/null +++ b/include/arm_ffa_priv.h @@ -0,0 +1,271 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __ARM_FFA_PRV_H +#define __ARM_FFA_PRV_H + +#include <mapmem.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> + +/* 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 <linux/arm-smccc.h> +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) + +/* + * 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 */ +}; + +/** + * 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 - 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_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_discovery_info - discovery information + * @fwk_version: FF-A framework version + * @invoke_ffa_fn: The function executing the FF-A function (ABI) + * @emul: FF-A sandbox emulator + */ +struct ffa_discovery_info { + u32 fwk_version; + invoke_ffa_fn_t invoke_ffa_fn; + struct udevice *emul; +}; + +/** + * struct ffa_priv - the driver private data structure + * + * @dscvry_info: Initial information discovered + * @ops: The driver operations structure + * @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. + * The data is dynamically allocated, managed by devres + * and registered with the DM. + */ +struct ffa_priv { + struct ffa_discovery_info dscvry_info; + 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); + +#endif diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 33e43c20db..df77c7da58 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -4,6 +4,11 @@ * * (C) Copyright 2012 * Pavel Herrmann morpheus.ibis@gmail.com + * + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#ifndef _DM_UCLASS_ID_H @@ -55,6 +60,7 @@ enum uclass_id { UCLASS_EFI_MEDIA, /* Devices provided by UEFI firmware */ UCLASS_ETH, /* Ethernet device */ UCLASS_ETH_PHY, /* Ethernet PHY device */ + UCLASS_FFA, /* Arm Firmware Framework for Armv8-A */ UCLASS_FIRMWARE, /* Firmware */ UCLASS_FPGA, /* FPGA device */ UCLASS_FUZZING_ENGINE, /* Fuzzing engine */

Hi Abdellatif,
On Wed, 12 Apr 2023 at 03:43, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
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].
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
Changelog:
v11:
- move ffa_try_discovery() from the uclass to the Arm FF-A driver
- rename ffa_try_discovery() to arm_ffa_discover()
- pass dev as an argument of arm_ffa_discover()
- add arm_ prefix to the Arm FF-A driver functions
- add emul field in struct ffa_discovery_info
- address nits
v10:
- provide the driver operations through the Uclass
- move the generic FF-A methods to the Uclass
- keep Arm specific methods in the Arm driver (arm-ffa.c)
- rename core.c to arm-ffa.c
- address nits
v9:
- integrate the FF-A bus discovery in the DM and use ARM_SMCCC_FEATURES for binding
v8:
- make ffa_get_partitions_info() second argument to be an SP count in both modes
- update ffa_bus_prvdata_get() to return a pointer rather than a pointer address
- remove packing from ffa_partition_info and ffa_send_direct_data structures
- pass the FF-A bus device to the bus operations
v7:
- add support for 32-bit direct messaging
- rename be_uuid_str_to_le_bin() to uuid_str_to_le_bin()
- improve the declaration of error handling mapping
- stating in doc/arch/arm64.ffa.rst that EFI runtime is not supported
v6:
- drop use of EFI runtime support (We decided with Linaro to add this later)
- drop discovery from initcalls (discovery will be on demand by FF-A users)
- set the alignment of the RX/TX buffers to the larger translation granule size
- move FF-A RX/TX buffers unmapping at ExitBootServices() to a separate commit
- update the documentation and move it to doc/arch/arm64.ffa.rst
v4:
- add doc/README.ffa.drv
- moving the FF-A driver work to drivers/firmware/arm-ffa
- use less #ifdefs in lib/efi_loader/efi_boottime.c and replace #if defined by #if CONFIG_IS_ENABLED
- improving error handling by mapping the FF-A errors to standard errors and logs
- replacing panics with an error log and returning an error code
- improving features discovery in FFA_FEATURES by introducing rxtx_min_pages private data field
- add ffa_remove and ffa_unbind functions
- improve how the driver behaves when bus discovery is done more than once
v3:
- align the interfaces of the U-Boot FF-A driver with those in the linux FF-A driver
- remove the FF-A helper layer
- make the U-Boot FF-A driver independent from EFI
- provide an optional config that enables copying the driver data to EFI runtime section at ExitBootServices service
- use 64-bit version of FFA_RXTX_MAP, FFA_MSG_SEND_DIRECT_{REQ, RESP}
v2:
- make FF-A bus discoverable using device_{bind, probe} APIs
- remove device tree support
v1:
- introduce FF-A bus driver with device tree support
MAINTAINERS | 8 + doc/arch/arm64.ffa.rst | 271 ++++ 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 | 1143 +++++++++++++++++ drivers/firmware/arm-ffa/arm-ffa.c | 127 ++ .../firmware/arm-ffa/sandbox_arm_ffa_priv.h | 14 + include/arm_ffa.h | 217 ++++ include/arm_ffa_priv.h | 271 ++++ include/dm/uclass-id.h | 6 + 13 files changed, 2104 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
Reviewed-by: Simon Glass sjg@chromium.org nits below
diff --git a/MAINTAINERS b/MAINTAINERS index 086e4cb3df..76f0f276ce 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 abdellatif.elkhlifi@arm.com +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 sbabic@denx.de M: Fabio Estevam festevam@gmail.com diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst new file mode 100644 index 0000000000..466e77e3cd --- /dev/null +++ b/doc/arch/arm64.ffa.rst @@ -0,0 +1,271 @@ +.. 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 sandboxes to
This is confusing as we use 'sandbox' in U-Boot to mean something else.
To me, a sandbox is a play area where you can fiddle around, like kids in a sandbox.
It is widely used though, to mean a secure environment, which is unfortunate. I think a 'jail' or 'secure container' container would be a better name.
Anyway, for here, could you just put in brackets:
(not to be confused with the U-Boot sandbox build)
or something like that.
+communicate with each other. A sandbox aka 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 is supported.
How about: '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 driver providing Arm specific methods and reusing the Uclass methods.
Arm-specific
+FF-A and SMC specifications +-------------------------------------------
+The current implementation of the U-Boot FF-A support relies on FF-A +specification v1.0
Can you put a nice link here so people can click on it? You can do that in rST.
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.
+For more details please refer to the FF-A v1.0 spec: +https://documentation-service.arm.com/static/5fb7e8a6ca04df4095c1d65e?token=
same here (and drop the ?token=)
+Hypervisors are supported if they are configured to trap SMC calls.
+The FF-A support uses 64-bit registers as per SMCCCv1.2 specification.
+For more details please refer to the SMC Calling Convention v1.2 spec: +https://documentation-service.arm.com/static/5f8edaeff86e16515cdbe4c6?token=
same here
+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(). +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:
- updating uc_priv with discovery information
- 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.
+FF-A device destruction +-------------------------
+When the FF-A device is removed by the DM, RX/TX buffers are automatically +unmapped and freed.
+For example, at EFI efi_exit_boot_services() active devices are automatically removed +by dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL).
+By consequence, the FF-A RX/TX are unmapped automatically.
+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.
boot-time
+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() and this is done +automatically at efi_exit_boot_services().
+If RX/TX buffers created by U-Boot are not unmapped and by consequence becoming +available at EFI runtime, secure world will get confused about RX/TX buffers +ownership (U-Boot vs kernel).
Can you rewrite that a bit? It seems quite strange English.
+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.
+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 (Mar 27 2023 - 13:48:33 +0000) corstone1000 aarch64
- DRAM: 2 GiB
- Arm FF-A framework discovery
- Using Arm SMC for FF-A conduit
- FF-A driver 1.0
- FF-A framework 1.0
- FF-A versions are compatible
- ...
- Hit any key to stop autoboot: 0
- Loading kernel from 0x083EE000 to memory ...
- ...
- FF-A endpoint ID is 0
- Using 1 4KB page(s) for FF-A RX/TX buffers size
- FF-A RX buffer at virtual address 00000000fdf4f000
- FF-A TX buffer at virtual address 00000000fdf51000
- FF-A RX/TX buffers mapped
- Reading FF-A partitions data from the RX buffer
- FF-A partition ID 8001 : info cached
- FF-A partition ID 8002 : info cached
- FF-A partition ID 8003 : info cached
- 3 FF-A partition(s) found and cached
- Preparing for checking FF-A partitions count
- Searching FF-A partitions using the provided UUID
- No FF-A partition found. Querying framework ...
- Reading FF-A partitions data from the RX buffer
- Number of FF-A partition(s) matching the UUID: 1
- EFI: Pre-allocating 1 partition(s) info structures
- Preparing for filling FF-A partitions info
- Searching FF-A partitions using the provided UUID
- FF-A partition ID 8003 matches the provided UUID
- EFI: MM partition ID 0x8003
- EFI: Corstone1000: Capsule shared buffer at 0x80000000 , size 8192 pages
- Booting /MemoryMapped(0x0,0x88200000,0xf00000)
- EFI stub: Booting Linux Kernel...
- EFI stub: Using DTB from configuration table
- EFI stub: Exiting boot services...
- unmapping FF-A RX/TX buffers
- Freeing FF-A RX/TX buffers
- Booting Linux on physical CPU 0x0000000000 [0x411fd040]
- 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 abdellatif.elkhlifi@arm.com
diff --git a/doc/arch/index.rst b/doc/arch/index.rst index b8da4b8c8e..2f916f4026 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 58be410135..885fdef4dc 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -112,6 +112,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 eae1c8ddc9..8789b1ea14 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 0000000000..9200c8028b --- /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 0000000000..11b1766285 --- /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 open-source-office@arm.com +# +# Authors: +# Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
+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 0000000000..c3ad19b8e7 --- /dev/null +++ b/drivers/firmware/arm-ffa/arm-ffa-uclass.c @@ -0,0 +1,1143 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
- Authors:
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include <common.h> +#include <arm_ffa.h> +#include <arm_ffa_priv.h> +#include <dm.h> +#include <log.h> +#include <malloc.h> +#include <string.h> +#include <uuid.h> +#include <asm/global_data.h> +#include <dm/device-internal.h> +#include <dm/devres.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h>
+DECLARE_GLOBAL_DATA_PTR;
+/* FF-A discovery information */ +struct ffa_discovery_info dscvry_info;
This should be in the uclass priv data, right? We try to avoid global vars in driver model. [..]
Regards, Simon

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].
[1]: doc/usage/cmd/armffa.rst
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
--- Changelog: ===============
v11:
* use U_BOOT_CMD_WITH_SUBCMDS * address nits
v10:
* use the FF-A driver Uclass operations * use uclass_first_device() * address nits
v9:
* remove manual FF-A discovery and use DM * use DM class APIs to probe and interact with the FF-A bus * add doc/usage/cmd/armffa.rst
v8:
* update partition_info_get() second argument to be an SP count * pass NULL device pointer to the FF-A bus discovery and operations
v7:
* adapt do_ffa_dev_list() following the recent update on uclass_first_device/uclass_next_device functions (they return void now) * set armffa command to use 64-bit direct messaging
v4:
* remove pattern data in do_ffa_msg_send_direct_req
v3:
* use the new driver interfaces (partition_info_get, sync_send_receive) in armffa command
v2:
* replace use of ffa_helper_init_device function by ffa_helper_bus_discover
v1:
* introduce armffa command
MAINTAINERS | 2 + cmd/Kconfig | 10 ++ cmd/Makefile | 2 + cmd/armffa.c | 212 +++++++++++++++++++++++++++++++ doc/arch/arm64.ffa.rst | 7 + doc/usage/cmd/armffa.rst | 105 +++++++++++++++ doc/usage/index.rst | 1 + drivers/firmware/arm-ffa/Kconfig | 1 + 8 files changed, 340 insertions(+) create mode 100644 cmd/armffa.c create mode 100644 doc/usage/cmd/armffa.rst
diff --git a/MAINTAINERS b/MAINTAINERS index 76f0f276ce..c64804ca2d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -269,7 +269,9 @@ F: configs/cortina_presidio-asic-pnand_defconfig ARM FF-A M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com S: Maintained +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: include/sandbox_arm_ffa.h diff --git a/cmd/Kconfig b/cmd/Kconfig index 8c9b430f99..4cb0b2c167 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -934,6 +934,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 e032091621..9130b9078d 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -12,6 +12,8 @@ obj-y += panic.o obj-y += version.o
# command + +obj-$(CONFIG_CMD_ARMFFA) += armffa.o obj-$(CONFIG_CMD_ACPI) += acpi.o obj-$(CONFIG_CMD_ADDRMAP) += addrmap.o obj-$(CONFIG_CMD_AES) += aes.o diff --git a/cmd/armffa.c b/cmd/armffa.c new file mode 100644 index 0000000000..ab88412c7d --- /dev/null +++ b/cmd/armffa.c @@ -0,0 +1,212 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ +#include <common.h> +#include <arm_ffa.h> +#include <command.h> +#include <dm.h> +#include <mapmem.h> +#include <stdlib.h> +#include <asm/io.h> + +/** + * 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 + */ +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 -ENODEV; + } + + return 0; +} + +/** + * do_ffa_getpart() - implementation of the getpart subcommand + * @cmdtp: Command Table + * @flag: flags + * @argc: number of arguments + * @argv: arguments + * + * Query the secure partition information which the 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_info *parts_info; + u32 i; + struct udevice *dev; + + ret = ffa_get_dev(&dev); + if (ret) + return CMD_RET_FAILURE; + + /* Mode 1: getting the number of secure partitions */ + ret = ffa_partition_info_get(dev, argv[1], &count, NULL); + if (ret) { + log_err("Failure in querying partitions count (error code: %d)\n", ret); + return CMD_RET_FAILURE; + } + + if (!count) { + log_info("No secure partition found\n"); + return CMD_RET_FAILURE; + } + + /* + * Pre-allocate a buffer to be filled by the driver + * with ffa_partition_info structs + */ + + log_info("Pre-allocating %d partition(s) info structures\n", count); + + parts_info = calloc(count, sizeof(struct ffa_partition_info)); + if (!parts_info) + return CMD_RET_FAILURE; + + /* Ask the driver to fill the buffer with the SPs info */ + + ret = ffa_partition_info_get(dev, argv[1], &count, parts_info); + if (ret) { + log_err("Failure in querying partition(s) info (error code: %d)\n", ret); + free(parts_info); + 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", + parts_info[i].id, + parts_info[i].exec_ctxt, + parts_info[i].properties); + } + + free(parts_info); + + 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 the secure partition which the ID 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 + */ +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; + + errno = 0; + part_id = strtoul(argv[1], NULL, 16); + + if (errno) { + log_err("Invalid partition ID\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 + */ +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 name %s, dev %p, driver name %s, ops %p\n", + dev->name, + (void *)map_to_sysmem(dev), + dev->driver->name, + (void *)map_to_sysmem(dev->driver->ops)); + + return CMD_RET_SUCCESS; +} + +static char armffa_help_text[] = + "getpart <partition UUID>\n" + " - lists the partition(s) info\n" + "ping <partition ID>\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 466e77e3cd..ed7240aef7 100644 --- a/doc/arch/arm64.ffa.rst +++ b/doc/arch/arm64.ffa.rst @@ -216,6 +216,13 @@ The following features are provided:
- FF-A bus can be compiled and used without EFI
+The armffa command +----------------------------------- + +armffa is an implementation defined 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.rst + 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 0000000000..e73d03ae51 --- /dev/null +++ b/doc/usage/cmd/armffa.rst @@ -0,0 +1,105 @@ +.. SPDX-License-Identifier: GPL-2.0+: + +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 with debug logs enabled. + +* 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 + Preparing for checking FF-A partitions count + Searching FF-A partitions using the provided UUID + No FF-A partition found. Querying framework ... + Reading FF-A partitions data from the RX buffer + Number of FF-A partition(s) matching the UUID: 1 + Pre-allocating 1 partition(s) info structures + Preparing for filling FF-A partitions info + Searching FF-A partitions using the provided UUID + FF-A partition ID 8003 matches the provided UUID + Partition: id = 8003 , exec_ctxt 1 , properties 3 + +* getpart (failure case) + +:: + + corstone1000# armffa getpart 33d532ed-e699-0942-c09c-a798d9cd7221 + Preparing for checking FF-A partitions count + Searching FF-A partitions using the provided UUID + No FF-A partition found. Querying framework ... + 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 and a negative error code on failure. diff --git a/doc/usage/index.rst b/doc/usage/index.rst index bc85e1d49a..df107fb710 100644 --- a/doc/usage/index.rst +++ b/doc/usage/index.rst @@ -21,6 +21,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 9200c8028b..a7d5392859 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 select ARM_SMCCC select ARM_SMCCC_FEATURES + imply CMD_ARMFFA select LIB_UUID select DEVRES help

On 4/12/23 11:42, Abdellatif El Khlifi wrote:
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].
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
Changelog:
v11:
- use U_BOOT_CMD_WITH_SUBCMDS
- address nits
v10:
- use the FF-A driver Uclass operations
- use uclass_first_device()
- address nits
v9:
- remove manual FF-A discovery and use DM
- use DM class APIs to probe and interact with the FF-A bus
- add doc/usage/cmd/armffa.rst
v8:
- update partition_info_get() second argument to be an SP count
- pass NULL device pointer to the FF-A bus discovery and operations
v7:
- adapt do_ffa_dev_list() following the recent update on uclass_first_device/uclass_next_device functions (they return void now)
- set armffa command to use 64-bit direct messaging
v4:
- remove pattern data in do_ffa_msg_send_direct_req
v3:
- use the new driver interfaces (partition_info_get, sync_send_receive) in armffa command
v2:
- replace use of ffa_helper_init_device function by ffa_helper_bus_discover
v1:
introduce armffa command
MAINTAINERS | 2 + cmd/Kconfig | 10 ++ cmd/Makefile | 2 + cmd/armffa.c | 212 +++++++++++++++++++++++++++++++ doc/arch/arm64.ffa.rst | 7 + doc/usage/cmd/armffa.rst | 105 +++++++++++++++ doc/usage/index.rst | 1 + drivers/firmware/arm-ffa/Kconfig | 1 + 8 files changed, 340 insertions(+) create mode 100644 cmd/armffa.c create mode 100644 doc/usage/cmd/armffa.rst
diff --git a/MAINTAINERS b/MAINTAINERS index 76f0f276ce..c64804ca2d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -269,7 +269,9 @@ F: configs/cortina_presidio-asic-pnand_defconfig ARM FF-A M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com S: Maintained +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: include/sandbox_arm_ffa.h diff --git a/cmd/Kconfig b/cmd/Kconfig index 8c9b430f99..4cb0b2c167 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -934,6 +934,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 e032091621..9130b9078d 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -12,6 +12,8 @@ obj-y += panic.o obj-y += version.o
# command
+obj-$(CONFIG_CMD_ARMFFA) += armffa.o obj-$(CONFIG_CMD_ACPI) += acpi.o obj-$(CONFIG_CMD_ADDRMAP) += addrmap.o obj-$(CONFIG_CMD_AES) += aes.o diff --git a/cmd/armffa.c b/cmd/armffa.c new file mode 100644 index 0000000000..ab88412c7d --- /dev/null +++ b/cmd/armffa.c @@ -0,0 +1,212 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
- Authors:
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include <common.h> +#include <arm_ffa.h> +#include <command.h> +#include <dm.h> +#include <mapmem.h> +#include <stdlib.h> +#include <asm/io.h>
+/**
- 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
- */
+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 -ENODEV;
- }
- return 0;
+}
+/**
- do_ffa_getpart() - implementation of the getpart subcommand
- @cmdtp: Command Table
- @flag: flags
- @argc: number of arguments
- @argv: arguments
- Query the secure partition information which the 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_info *parts_info;
- u32 i;
- struct udevice *dev;
- ret = ffa_get_dev(&dev);
- if (ret)
return CMD_RET_FAILURE;
- /* Mode 1: getting the number of secure partitions */
- ret = ffa_partition_info_get(dev, argv[1], &count, NULL);
- if (ret) {
log_err("Failure in querying partitions count (error code: %d)\n", ret);
return CMD_RET_FAILURE;
- }
- if (!count) {
log_info("No secure partition found\n");
return CMD_RET_FAILURE;
- }
- /*
* Pre-allocate a buffer to be filled by the driver
* with ffa_partition_info structs
*/
- log_info("Pre-allocating %d partition(s) info structures\n", count);
- parts_info = calloc(count, sizeof(struct ffa_partition_info));
- if (!parts_info)
return CMD_RET_FAILURE;
- /* Ask the driver to fill the buffer with the SPs info */
- ret = ffa_partition_info_get(dev, argv[1], &count, parts_info);
- if (ret) {
log_err("Failure in querying partition(s) info (error code: %d)\n", ret);
free(parts_info);
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",
parts_info[i].id,
parts_info[i].exec_ctxt,
parts_info[i].properties);
- }
- free(parts_info);
- 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 the secure partition which the ID 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
- */
+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;
- errno = 0;
- part_id = strtoul(argv[1], NULL, 16);
- if (errno) {
log_err("Invalid partition ID\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
- */
+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 name %s, dev %p, driver name %s, ops %p\n",
dev->name,
(void *)map_to_sysmem(dev),
dev->driver->name,
(void *)map_to_sysmem(dev->driver->ops));
- return CMD_RET_SUCCESS;
+}
+static char armffa_help_text[] =
- "getpart <partition UUID>\n"
- " - lists the partition(s) info\n"
- "ping <partition ID>\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 466e77e3cd..ed7240aef7 100644 --- a/doc/arch/arm64.ffa.rst +++ b/doc/arch/arm64.ffa.rst @@ -216,6 +216,13 @@ The following features are provided:
- FF-A bus can be compiled and used without EFI
+The armffa command +-----------------------------------
+armffa is an implementation defined 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.rst
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 0000000000..e73d03ae51 --- /dev/null +++ b/doc/usage/cmd/armffa.rst @@ -0,0 +1,105 @@ +.. SPDX-License-Identifier: GPL-2.0+:
+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 with debug logs enabled.
+* 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
- Preparing for checking FF-A partitions count
- Searching FF-A partitions using the provided UUID
- No FF-A partition found. Querying framework ...
- Reading FF-A partitions data from the RX buffer
- Number of FF-A partition(s) matching the UUID: 1
- Pre-allocating 1 partition(s) info structures
- Preparing for filling FF-A partitions info
- Searching FF-A partitions using the provided UUID
- FF-A partition ID 8003 matches the provided UUID
- Partition: id = 8003 , exec_ctxt 1 , properties 3
+* getpart (failure case)
+::
- corstone1000# armffa getpart 33d532ed-e699-0942-c09c-a798d9cd7221
- Preparing for checking FF-A partitions count
- Searching FF-A partitions using the provided UUID
- No FF-A partition found. Querying framework ...
- 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 and a negative error code on failure.
CMD_RET_FAILURE results in $? being 1 (false). Did you actually see a negative value?
Best regards
Heinrich
diff --git a/doc/usage/index.rst b/doc/usage/index.rst index bc85e1d49a..df107fb710 100644 --- a/doc/usage/index.rst +++ b/doc/usage/index.rst @@ -21,6 +21,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 9200c8028b..a7d5392859 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 select ARM_SMCCC select ARM_SMCCC_FEATURES
- imply CMD_ARMFFA select LIB_UUID select DEVRES help

On Wed, Apr 12, 2023 at 04:02:43PM +0200, Heinrich Schuchardt wrote:
On 4/12/23 11:42, Abdellatif El Khlifi wrote:
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].
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
Changelog:
v11:
- use U_BOOT_CMD_WITH_SUBCMDS
- address nits
v10:
- use the FF-A driver Uclass operations
- use uclass_first_device()
- address nits
v9:
- remove manual FF-A discovery and use DM
- use DM class APIs to probe and interact with the FF-A bus
- add doc/usage/cmd/armffa.rst
v8:
- update partition_info_get() second argument to be an SP count
- pass NULL device pointer to the FF-A bus discovery and operations
v7:
- adapt do_ffa_dev_list() following the recent update on uclass_first_device/uclass_next_device functions (they return void now)
- set armffa command to use 64-bit direct messaging
v4:
- remove pattern data in do_ffa_msg_send_direct_req
v3:
- use the new driver interfaces (partition_info_get, sync_send_receive) in armffa command
v2:
- replace use of ffa_helper_init_device function by ffa_helper_bus_discover
v1:
introduce armffa command
MAINTAINERS | 2 + cmd/Kconfig | 10 ++ cmd/Makefile | 2 + cmd/armffa.c | 212 +++++++++++++++++++++++++++++++ doc/arch/arm64.ffa.rst | 7 + doc/usage/cmd/armffa.rst | 105 +++++++++++++++ doc/usage/index.rst | 1 + drivers/firmware/arm-ffa/Kconfig | 1 + 8 files changed, 340 insertions(+) create mode 100644 cmd/armffa.c create mode 100644 doc/usage/cmd/armffa.rst
diff --git a/MAINTAINERS b/MAINTAINERS index 76f0f276ce..c64804ca2d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -269,7 +269,9 @@ F: configs/cortina_presidio-asic-pnand_defconfig ARM FF-A M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com S: Maintained +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: include/sandbox_arm_ffa.h diff --git a/cmd/Kconfig b/cmd/Kconfig index 8c9b430f99..4cb0b2c167 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -934,6 +934,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 e032091621..9130b9078d 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -12,6 +12,8 @@ obj-y += panic.o obj-y += version.o
# command
+obj-$(CONFIG_CMD_ARMFFA) += armffa.o obj-$(CONFIG_CMD_ACPI) += acpi.o obj-$(CONFIG_CMD_ADDRMAP) += addrmap.o obj-$(CONFIG_CMD_AES) += aes.o diff --git a/cmd/armffa.c b/cmd/armffa.c new file mode 100644 index 0000000000..ab88412c7d --- /dev/null +++ b/cmd/armffa.c @@ -0,0 +1,212 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
- Authors:
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include <common.h> +#include <arm_ffa.h> +#include <command.h> +#include <dm.h> +#include <mapmem.h> +#include <stdlib.h> +#include <asm/io.h>
+/**
- 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
- */
+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 -ENODEV;
- }
- return 0;
+}
+/**
- do_ffa_getpart() - implementation of the getpart subcommand
- @cmdtp: Command Table
- @flag: flags
- @argc: number of arguments
- @argv: arguments
- Query the secure partition information which the 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_info *parts_info;
- u32 i;
- struct udevice *dev;
- ret = ffa_get_dev(&dev);
- if (ret)
return CMD_RET_FAILURE;
- /* Mode 1: getting the number of secure partitions */
- ret = ffa_partition_info_get(dev, argv[1], &count, NULL);
- if (ret) {
log_err("Failure in querying partitions count (error code: %d)\n", ret);
return CMD_RET_FAILURE;
- }
- if (!count) {
log_info("No secure partition found\n");
return CMD_RET_FAILURE;
- }
- /*
* Pre-allocate a buffer to be filled by the driver
* with ffa_partition_info structs
*/
- log_info("Pre-allocating %d partition(s) info structures\n", count);
- parts_info = calloc(count, sizeof(struct ffa_partition_info));
- if (!parts_info)
return CMD_RET_FAILURE;
- /* Ask the driver to fill the buffer with the SPs info */
- ret = ffa_partition_info_get(dev, argv[1], &count, parts_info);
- if (ret) {
log_err("Failure in querying partition(s) info (error code: %d)\n", ret);
free(parts_info);
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",
parts_info[i].id,
parts_info[i].exec_ctxt,
parts_info[i].properties);
- }
- free(parts_info);
- 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 the secure partition which the ID 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
- */
+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;
- errno = 0;
- part_id = strtoul(argv[1], NULL, 16);
- if (errno) {
log_err("Invalid partition ID\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
- */
+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 name %s, dev %p, driver name %s, ops %p\n",
dev->name,
(void *)map_to_sysmem(dev),
dev->driver->name,
(void *)map_to_sysmem(dev->driver->ops));
- return CMD_RET_SUCCESS;
+}
+static char armffa_help_text[] =
- "getpart <partition UUID>\n"
- " - lists the partition(s) info\n"
- "ping <partition ID>\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 466e77e3cd..ed7240aef7 100644 --- a/doc/arch/arm64.ffa.rst +++ b/doc/arch/arm64.ffa.rst @@ -216,6 +216,13 @@ The following features are provided:
- FF-A bus can be compiled and used without EFI
+The armffa command +-----------------------------------
+armffa is an implementation defined 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.rst
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 0000000000..e73d03ae51 --- /dev/null +++ b/doc/usage/cmd/armffa.rst @@ -0,0 +1,105 @@ +.. SPDX-License-Identifier: GPL-2.0+:
+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 with debug logs enabled.
+* 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
- Preparing for checking FF-A partitions count
- Searching FF-A partitions using the provided UUID
- No FF-A partition found. Querying framework ...
- Reading FF-A partitions data from the RX buffer
- Number of FF-A partition(s) matching the UUID: 1
- Pre-allocating 1 partition(s) info structures
- Preparing for filling FF-A partitions info
- Searching FF-A partitions using the provided UUID
- FF-A partition ID 8003 matches the provided UUID
- Partition: id = 8003 , exec_ctxt 1 , properties 3
+* getpart (failure case)
+::
- corstone1000# armffa getpart 33d532ed-e699-0942-c09c-a798d9cd7221
- Preparing for checking FF-A partitions count
- Searching FF-A partitions using the provided UUID
- No FF-A partition found. Querying framework ...
- 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 and a negative error code on failure.
CMD_RET_FAILURE results in $? being 1 (false). Did you actually see a negative value?
For armffa, the possible return codes are: CMD_RET_USAGE (-1), CMD_RET_FAILURE (1) and CMD_RET_SUCCESS (0)
I suggest replacing the sentence with:
The return value $? is 0 (true) on success, -1 on usage error and 1 (false) on FF-A related failures.
Cheers Abdellatif
Best regards
Heinrich
diff --git a/doc/usage/index.rst b/doc/usage/index.rst index bc85e1d49a..df107fb710 100644 --- a/doc/usage/index.rst +++ b/doc/usage/index.rst @@ -21,6 +21,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 9200c8028b..a7d5392859 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 select ARM_SMCCC select ARM_SMCCC_FEATURES
- imply CMD_ARMFFA select LIB_UUID select DEVRES help

Am 12. April 2023 17:53:23 MESZ schrieb Abdellatif El Khlifi abdellatif.elkhlifi@arm.com:
On Wed, Apr 12, 2023 at 04:02:43PM +0200, Heinrich Schuchardt wrote:
On 4/12/23 11:42, Abdellatif El Khlifi wrote:
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].
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
Changelog:
v11:
- use U_BOOT_CMD_WITH_SUBCMDS
- address nits
v10:
- use the FF-A driver Uclass operations
- use uclass_first_device()
- address nits
v9:
- remove manual FF-A discovery and use DM
- use DM class APIs to probe and interact with the FF-A bus
- add doc/usage/cmd/armffa.rst
v8:
- update partition_info_get() second argument to be an SP count
- pass NULL device pointer to the FF-A bus discovery and operations
v7:
- adapt do_ffa_dev_list() following the recent update on uclass_first_device/uclass_next_device functions (they return void now)
- set armffa command to use 64-bit direct messaging
v4:
- remove pattern data in do_ffa_msg_send_direct_req
v3:
- use the new driver interfaces (partition_info_get, sync_send_receive) in armffa command
v2:
- replace use of ffa_helper_init_device function by ffa_helper_bus_discover
v1:
introduce armffa command
MAINTAINERS | 2 + cmd/Kconfig | 10 ++ cmd/Makefile | 2 + cmd/armffa.c | 212 +++++++++++++++++++++++++++++++ doc/arch/arm64.ffa.rst | 7 + doc/usage/cmd/armffa.rst | 105 +++++++++++++++ doc/usage/index.rst | 1 + drivers/firmware/arm-ffa/Kconfig | 1 + 8 files changed, 340 insertions(+) create mode 100644 cmd/armffa.c create mode 100644 doc/usage/cmd/armffa.rst
diff --git a/MAINTAINERS b/MAINTAINERS index 76f0f276ce..c64804ca2d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -269,7 +269,9 @@ F: configs/cortina_presidio-asic-pnand_defconfig ARM FF-A M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com S: Maintained +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: include/sandbox_arm_ffa.h diff --git a/cmd/Kconfig b/cmd/Kconfig index 8c9b430f99..4cb0b2c167 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -934,6 +934,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 e032091621..9130b9078d 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -12,6 +12,8 @@ obj-y += panic.o obj-y += version.o
# command
+obj-$(CONFIG_CMD_ARMFFA) += armffa.o obj-$(CONFIG_CMD_ACPI) += acpi.o obj-$(CONFIG_CMD_ADDRMAP) += addrmap.o obj-$(CONFIG_CMD_AES) += aes.o diff --git a/cmd/armffa.c b/cmd/armffa.c new file mode 100644 index 0000000000..ab88412c7d --- /dev/null +++ b/cmd/armffa.c @@ -0,0 +1,212 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
- Authors:
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include <common.h> +#include <arm_ffa.h> +#include <command.h> +#include <dm.h> +#include <mapmem.h> +#include <stdlib.h> +#include <asm/io.h>
+/**
- 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
- */
+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 -ENODEV;
- }
- return 0;
+}
+/**
- do_ffa_getpart() - implementation of the getpart subcommand
- @cmdtp: Command Table
- @flag: flags
- @argc: number of arguments
- @argv: arguments
- Query the secure partition information which the 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_info *parts_info;
- u32 i;
- struct udevice *dev;
- ret = ffa_get_dev(&dev);
- if (ret)
return CMD_RET_FAILURE;
- /* Mode 1: getting the number of secure partitions */
- ret = ffa_partition_info_get(dev, argv[1], &count, NULL);
- if (ret) {
log_err("Failure in querying partitions count (error code: %d)\n", ret);
return CMD_RET_FAILURE;
- }
- if (!count) {
log_info("No secure partition found\n");
return CMD_RET_FAILURE;
- }
- /*
* Pre-allocate a buffer to be filled by the driver
* with ffa_partition_info structs
*/
- log_info("Pre-allocating %d partition(s) info structures\n", count);
- parts_info = calloc(count, sizeof(struct ffa_partition_info));
- if (!parts_info)
return CMD_RET_FAILURE;
- /* Ask the driver to fill the buffer with the SPs info */
- ret = ffa_partition_info_get(dev, argv[1], &count, parts_info);
- if (ret) {
log_err("Failure in querying partition(s) info (error code: %d)\n", ret);
free(parts_info);
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",
parts_info[i].id,
parts_info[i].exec_ctxt,
parts_info[i].properties);
- }
- free(parts_info);
- 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 the secure partition which the ID 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
- */
+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;
- errno = 0;
- part_id = strtoul(argv[1], NULL, 16);
- if (errno) {
log_err("Invalid partition ID\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
- */
+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 name %s, dev %p, driver name %s, ops %p\n",
dev->name,
(void *)map_to_sysmem(dev),
dev->driver->name,
(void *)map_to_sysmem(dev->driver->ops));
- return CMD_RET_SUCCESS;
+}
+static char armffa_help_text[] =
- "getpart <partition UUID>\n"
- " - lists the partition(s) info\n"
- "ping <partition ID>\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 466e77e3cd..ed7240aef7 100644 --- a/doc/arch/arm64.ffa.rst +++ b/doc/arch/arm64.ffa.rst @@ -216,6 +216,13 @@ The following features are provided:
- FF-A bus can be compiled and used without EFI
+The armffa command +-----------------------------------
+armffa is an implementation defined 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.rst
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 0000000000..e73d03ae51 --- /dev/null +++ b/doc/usage/cmd/armffa.rst @@ -0,0 +1,105 @@ +.. SPDX-License-Identifier: GPL-2.0+:
+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 with debug logs enabled.
+* 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
- Preparing for checking FF-A partitions count
- Searching FF-A partitions using the provided UUID
- No FF-A partition found. Querying framework ...
- Reading FF-A partitions data from the RX buffer
- Number of FF-A partition(s) matching the UUID: 1
- Pre-allocating 1 partition(s) info structures
- Preparing for filling FF-A partitions info
- Searching FF-A partitions using the provided UUID
- FF-A partition ID 8003 matches the provided UUID
- Partition: id = 8003 , exec_ctxt 1 , properties 3
+* getpart (failure case)
+::
- corstone1000# armffa getpart 33d532ed-e699-0942-c09c-a798d9cd7221
- Preparing for checking FF-A partitions count
- Searching FF-A partitions using the provided UUID
- No FF-A partition found. Querying framework ...
- 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 and a negative error code on failure.
CMD_RET_FAILURE results in $? being 1 (false). Did you actually see a negative value?
For armffa, the possible return codes are: CMD_RET_USAGE (-1), CMD_RET_FAILURE (1) and CMD_RET_SUCCESS (0)
I suggest replacing the sentence with:
The return value $? is 0 (true) on success, -1 on usage error and 1 (false) on FF-A related failures.
CMD_RET_USAGE results in $? =1.
Cheers Abdellatif
Best regards
Heinrich
diff --git a/doc/usage/index.rst b/doc/usage/index.rst index bc85e1d49a..df107fb710 100644 --- a/doc/usage/index.rst +++ b/doc/usage/index.rst @@ -21,6 +21,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 9200c8028b..a7d5392859 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 select ARM_SMCCC select ARM_SMCCC_FEATURES
- imply CMD_ARMFFA select LIB_UUID select DEVRES help

On Wed, Apr 12, 2023 at 10:00:38PM +0200, Heinrich Schuchardt wrote:
Am 12. April 2023 17:53:23 MESZ schrieb Abdellatif El Khlifi abdellatif.elkhlifi@arm.com:
On Wed, Apr 12, 2023 at 04:02:43PM +0200, Heinrich Schuchardt wrote:
On 4/12/23 11:42, Abdellatif El Khlifi wrote:
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].
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
Changelog:
v11:
- use U_BOOT_CMD_WITH_SUBCMDS
- address nits
v10:
- use the FF-A driver Uclass operations
- use uclass_first_device()
- address nits
v9:
- remove manual FF-A discovery and use DM
- use DM class APIs to probe and interact with the FF-A bus
- add doc/usage/cmd/armffa.rst
v8:
- update partition_info_get() second argument to be an SP count
- pass NULL device pointer to the FF-A bus discovery and operations
v7:
- adapt do_ffa_dev_list() following the recent update on uclass_first_device/uclass_next_device functions (they return void now)
- set armffa command to use 64-bit direct messaging
v4:
- remove pattern data in do_ffa_msg_send_direct_req
v3:
- use the new driver interfaces (partition_info_get, sync_send_receive) in armffa command
v2:
- replace use of ffa_helper_init_device function by ffa_helper_bus_discover
v1:
introduce armffa command
MAINTAINERS | 2 + cmd/Kconfig | 10 ++ cmd/Makefile | 2 + cmd/armffa.c | 212 +++++++++++++++++++++++++++++++ doc/arch/arm64.ffa.rst | 7 + doc/usage/cmd/armffa.rst | 105 +++++++++++++++ doc/usage/index.rst | 1 + drivers/firmware/arm-ffa/Kconfig | 1 + 8 files changed, 340 insertions(+) create mode 100644 cmd/armffa.c create mode 100644 doc/usage/cmd/armffa.rst
diff --git a/MAINTAINERS b/MAINTAINERS index 76f0f276ce..c64804ca2d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -269,7 +269,9 @@ F: configs/cortina_presidio-asic-pnand_defconfig ARM FF-A M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com S: Maintained +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: include/sandbox_arm_ffa.h diff --git a/cmd/Kconfig b/cmd/Kconfig index 8c9b430f99..4cb0b2c167 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -934,6 +934,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 e032091621..9130b9078d 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -12,6 +12,8 @@ obj-y += panic.o obj-y += version.o
# command
+obj-$(CONFIG_CMD_ARMFFA) += armffa.o obj-$(CONFIG_CMD_ACPI) += acpi.o obj-$(CONFIG_CMD_ADDRMAP) += addrmap.o obj-$(CONFIG_CMD_AES) += aes.o diff --git a/cmd/armffa.c b/cmd/armffa.c new file mode 100644 index 0000000000..ab88412c7d --- /dev/null +++ b/cmd/armffa.c @@ -0,0 +1,212 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
- Authors:
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include <common.h> +#include <arm_ffa.h> +#include <command.h> +#include <dm.h> +#include <mapmem.h> +#include <stdlib.h> +#include <asm/io.h>
+/**
- 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
- */
+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 -ENODEV;
- }
- return 0;
+}
+/**
- do_ffa_getpart() - implementation of the getpart subcommand
- @cmdtp: Command Table
- @flag: flags
- @argc: number of arguments
- @argv: arguments
- Query the secure partition information which the 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_info *parts_info;
- u32 i;
- struct udevice *dev;
- ret = ffa_get_dev(&dev);
- if (ret)
return CMD_RET_FAILURE;
- /* Mode 1: getting the number of secure partitions */
- ret = ffa_partition_info_get(dev, argv[1], &count, NULL);
- if (ret) {
log_err("Failure in querying partitions count (error code: %d)\n", ret);
return CMD_RET_FAILURE;
- }
- if (!count) {
log_info("No secure partition found\n");
return CMD_RET_FAILURE;
- }
- /*
* Pre-allocate a buffer to be filled by the driver
* with ffa_partition_info structs
*/
- log_info("Pre-allocating %d partition(s) info structures\n", count);
- parts_info = calloc(count, sizeof(struct ffa_partition_info));
- if (!parts_info)
return CMD_RET_FAILURE;
- /* Ask the driver to fill the buffer with the SPs info */
- ret = ffa_partition_info_get(dev, argv[1], &count, parts_info);
- if (ret) {
log_err("Failure in querying partition(s) info (error code: %d)\n", ret);
free(parts_info);
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",
parts_info[i].id,
parts_info[i].exec_ctxt,
parts_info[i].properties);
- }
- free(parts_info);
- 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 the secure partition which the ID 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
- */
+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;
- errno = 0;
- part_id = strtoul(argv[1], NULL, 16);
- if (errno) {
log_err("Invalid partition ID\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
- */
+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 name %s, dev %p, driver name %s, ops %p\n",
dev->name,
(void *)map_to_sysmem(dev),
dev->driver->name,
(void *)map_to_sysmem(dev->driver->ops));
- return CMD_RET_SUCCESS;
+}
+static char armffa_help_text[] =
- "getpart <partition UUID>\n"
- " - lists the partition(s) info\n"
- "ping <partition ID>\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 466e77e3cd..ed7240aef7 100644 --- a/doc/arch/arm64.ffa.rst +++ b/doc/arch/arm64.ffa.rst @@ -216,6 +216,13 @@ The following features are provided:
- FF-A bus can be compiled and used without EFI
+The armffa command +-----------------------------------
+armffa is an implementation defined 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.rst
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 0000000000..e73d03ae51 --- /dev/null +++ b/doc/usage/cmd/armffa.rst @@ -0,0 +1,105 @@ +.. SPDX-License-Identifier: GPL-2.0+:
+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 with debug logs enabled.
+* 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
- Preparing for checking FF-A partitions count
- Searching FF-A partitions using the provided UUID
- No FF-A partition found. Querying framework ...
- Reading FF-A partitions data from the RX buffer
- Number of FF-A partition(s) matching the UUID: 1
- Pre-allocating 1 partition(s) info structures
- Preparing for filling FF-A partitions info
- Searching FF-A partitions using the provided UUID
- FF-A partition ID 8003 matches the provided UUID
- Partition: id = 8003 , exec_ctxt 1 , properties 3
+* getpart (failure case)
+::
- corstone1000# armffa getpart 33d532ed-e699-0942-c09c-a798d9cd7221
- Preparing for checking FF-A partitions count
- Searching FF-A partitions using the provided UUID
- No FF-A partition found. Querying framework ...
- 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 and a negative error code on failure.
CMD_RET_FAILURE results in $? being 1 (false). Did you actually see a negative value?
For armffa, the possible return codes are: CMD_RET_USAGE (-1), CMD_RET_FAILURE (1) and CMD_RET_SUCCESS (0)
I suggest replacing the sentence with:
The return value $? is 0 (true) on success, -1 on usage error and 1 (false) on FF-A related failures.
CMD_RET_USAGE results in $? =1.
Ok, I'll take that into account. Thanks.
Cheers Abdellatif
Best regards
Heinrich
diff --git a/doc/usage/index.rst b/doc/usage/index.rst index bc85e1d49a..df107fb710 100644 --- a/doc/usage/index.rst +++ b/doc/usage/index.rst @@ -21,6 +21,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 9200c8028b..a7d5392859 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 select ARM_SMCCC select ARM_SMCCC_FEATURES
- imply CMD_ARMFFA select LIB_UUID select DEVRES help

Hi Abdellatif,
On Wed, 12 Apr 2023 at 03:43, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
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].
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
Changelog:
v11:
- use U_BOOT_CMD_WITH_SUBCMDS
- address nits
v10:
- use the FF-A driver Uclass operations
- use uclass_first_device()
- address nits
v9:
- remove manual FF-A discovery and use DM
- use DM class APIs to probe and interact with the FF-A bus
- add doc/usage/cmd/armffa.rst
v8:
- update partition_info_get() second argument to be an SP count
- pass NULL device pointer to the FF-A bus discovery and operations
v7:
- adapt do_ffa_dev_list() following the recent update on uclass_first_device/uclass_next_device functions (they return void now)
- set armffa command to use 64-bit direct messaging
v4:
- remove pattern data in do_ffa_msg_send_direct_req
v3:
- use the new driver interfaces (partition_info_get, sync_send_receive) in armffa command
v2:
- replace use of ffa_helper_init_device function by
ffa_helper_bus_discover
v1:
- introduce armffa command
MAINTAINERS | 2 + cmd/Kconfig | 10 ++ cmd/Makefile | 2 + cmd/armffa.c | 212 +++++++++++++++++++++++++++++++ doc/arch/arm64.ffa.rst | 7 + doc/usage/cmd/armffa.rst | 105 +++++++++++++++ doc/usage/index.rst | 1 + drivers/firmware/arm-ffa/Kconfig | 1 + 8 files changed, 340 insertions(+) create mode 100644 cmd/armffa.c create mode 100644 doc/usage/cmd/armffa.rst
Reviewed-by: Simon Glass sjg@chromium.org
with nits below
For your docs, please use a proper rST link for doc/usage/cmd/armffa.rst so people can click on it and view the command docs.
Also:
armffa is an implementation-defined command
(add hyphen)
although I'm not sure how an implementation can define the command?
The example output in your 'example' docs is very, very verbose. Shouldn't it just report problems?
diff --git a/MAINTAINERS b/MAINTAINERS index 76f0f276ce..c64804ca2d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -269,7 +269,9 @@ F: configs/cortina_presidio-asic-pnand_defconfig ARM FF-A M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com S: Maintained +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: include/sandbox_arm_ffa.h diff --git a/cmd/Kconfig b/cmd/Kconfig index 8c9b430f99..4cb0b2c167 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -934,6 +934,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 e032091621..9130b9078d 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -12,6 +12,8 @@ obj-y += panic.o obj-y += version.o
# command
Please drop blank line
+obj-$(CONFIG_CMD_ARMFFA) += armffa.o obj-$(CONFIG_CMD_ACPI) += acpi.o obj-$(CONFIG_CMD_ADDRMAP) += addrmap.o obj-$(CONFIG_CMD_AES) += aes.o diff --git a/cmd/armffa.c b/cmd/armffa.c new file mode 100644 index 0000000000..ab88412c7d --- /dev/null +++ b/cmd/armffa.c @@ -0,0 +1,212 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
- Authors:
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include <common.h> +#include <arm_ffa.h> +#include <command.h> +#include <dm.h> +#include <mapmem.h> +#include <stdlib.h> +#include <asm/io.h>
+/**
- 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
- */
+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 -ENODEV;
return ret
}
return 0;
+}
+/**
- do_ffa_getpart() - implementation of the getpart subcommand
- @cmdtp: Command Table
- @flag: flags
- @argc: number of arguments
- @argv: arguments
- Query the secure partition information which the 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_info *parts_info;
u32 i;
struct udevice *dev;
ret = ffa_get_dev(&dev);
if (ret)
return CMD_RET_FAILURE;
/* Mode 1: getting the number of secure partitions */
ret = ffa_partition_info_get(dev, argv[1], &count, NULL);
if (ret) {
log_err("Failure in querying partitions count (error code: %d)\n", ret);
return CMD_RET_FAILURE;
}
if (!count) {
log_info("No secure partition found\n");
return CMD_RET_FAILURE;
}
/*
* Pre-allocate a buffer to be filled by the driver
* with ffa_partition_info structs
*/
log_info("Pre-allocating %d partition(s) info structures\n", count);
parts_info = calloc(count, sizeof(struct ffa_partition_info));
This should be a local variable:
struct ffa_partition_info parts_info
I mentioned this before and you said that the number of things is fixed at runtime, but I don't see that here. Also I see is a simple struct, so there should be no need to allocate it.
if (!parts_info)
return CMD_RET_FAILURE;
/* Ask the driver to fill the buffer with the SPs info */
ret = ffa_partition_info_get(dev, argv[1], &count, parts_info);
if (ret) {
log_err("Failure in querying partition(s) info (error code: %d)\n", ret);
free(parts_info);
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",
parts_info[i].id,
parts_info[i].exec_ctxt,
parts_info[i].properties);
}
free(parts_info);
return CMD_RET_SUCCESS;
+}
[..]
Regards, Simon

Hi Simon,
On Wed, 12 Apr 2023 at 03:43, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
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].
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
Changelog:
v11:
- use U_BOOT_CMD_WITH_SUBCMDS
- address nits
v10:
- use the FF-A driver Uclass operations
- use uclass_first_device()
- address nits
v9:
- remove manual FF-A discovery and use DM
- use DM class APIs to probe and interact with the FF-A bus
- add doc/usage/cmd/armffa.rst
v8:
- update partition_info_get() second argument to be an SP count
- pass NULL device pointer to the FF-A bus discovery and operations
v7:
- adapt do_ffa_dev_list() following the recent update on uclass_first_device/uclass_next_device functions (they return void now)
- set armffa command to use 64-bit direct messaging
v4:
- remove pattern data in do_ffa_msg_send_direct_req
v3:
- use the new driver interfaces (partition_info_get, sync_send_receive) in armffa command
v2:
- replace use of ffa_helper_init_device function by
ffa_helper_bus_discover
v1:
- introduce armffa command
MAINTAINERS | 2 + cmd/Kconfig | 10 ++ cmd/Makefile | 2 + cmd/armffa.c | 212 +++++++++++++++++++++++++++++++ doc/arch/arm64.ffa.rst | 7 + doc/usage/cmd/armffa.rst | 105 +++++++++++++++ doc/usage/index.rst | 1 + drivers/firmware/arm-ffa/Kconfig | 1 + 8 files changed, 340 insertions(+) create mode 100644 cmd/armffa.c create mode 100644 doc/usage/cmd/armffa.rst
Reviewed-by: Simon Glass sjg@chromium.org
with nits below
For your docs, please use a proper rST link for doc/usage/cmd/armffa.rst so people can click on it and view the command docs.
Also:
armffa is an implementation-defined command
(add hyphen)
although I'm not sure how an implementation can define the command?
The example output in your 'example' docs is very, very verbose. Shouldn't it just report problems?
Thanks, addressed in v12.
Cheers, Abdellatif
diff --git a/MAINTAINERS b/MAINTAINERS index 76f0f276ce..c64804ca2d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -269,7 +269,9 @@ F: configs/cortina_presidio-asic-pnand_defconfig ARM FF-A M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com S: Maintained +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: include/sandbox_arm_ffa.h diff --git a/cmd/Kconfig b/cmd/Kconfig index 8c9b430f99..4cb0b2c167 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -934,6 +934,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 e032091621..9130b9078d 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -12,6 +12,8 @@ obj-y += panic.o obj-y += version.o
# command
Please drop blank line
+obj-$(CONFIG_CMD_ARMFFA) += armffa.o obj-$(CONFIG_CMD_ACPI) += acpi.o obj-$(CONFIG_CMD_ADDRMAP) += addrmap.o obj-$(CONFIG_CMD_AES) += aes.o diff --git a/cmd/armffa.c b/cmd/armffa.c new file mode 100644 index 0000000000..ab88412c7d --- /dev/null +++ b/cmd/armffa.c @@ -0,0 +1,212 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
- Authors:
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include <common.h> +#include <arm_ffa.h> +#include <command.h> +#include <dm.h> +#include <mapmem.h> +#include <stdlib.h> +#include <asm/io.h>
+/**
- 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
- */
+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 -ENODEV;
return ret
}
return 0;
+}
+/**
- do_ffa_getpart() - implementation of the getpart subcommand
- @cmdtp: Command Table
- @flag: flags
- @argc: number of arguments
- @argv: arguments
- Query the secure partition information which the 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_info *parts_info;
u32 i;
struct udevice *dev;
ret = ffa_get_dev(&dev);
if (ret)
return CMD_RET_FAILURE;
/* Mode 1: getting the number of secure partitions */
ret = ffa_partition_info_get(dev, argv[1], &count, NULL);
if (ret) {
log_err("Failure in querying partitions count (error code: %d)\n", ret);
return CMD_RET_FAILURE;
}
if (!count) {
log_info("No secure partition found\n");
return CMD_RET_FAILURE;
}
/*
* Pre-allocate a buffer to be filled by the driver
* with ffa_partition_info structs
*/
log_info("Pre-allocating %d partition(s) info structures\n", count);
parts_info = calloc(count, sizeof(struct ffa_partition_info));
This should be a local variable:
struct ffa_partition_info parts_info
I mentioned this before and you said that the number of things is fixed at runtime, but I don't see that here. Also I see is a simple struct, so there should be no need to allocate it.
if (!parts_info)
return CMD_RET_FAILURE;
/* Ask the driver to fill the buffer with the SPs info */
ret = ffa_partition_info_get(dev, argv[1], &count, parts_info);
if (ret) {
log_err("Failure in querying partition(s) info (error code: %d)\n", ret);
free(parts_info);
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",
parts_info[i].id,
parts_info[i].exec_ctxt,
parts_info[i].properties);
}
free(parts_info);
return CMD_RET_SUCCESS;
+}
[..]
Regards, Simon

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 abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
--- Changelog: ===============
v11:
* rename ffa_try_discovery() to sandbox_ffa_discover() * rename sandbox_ffa_query_core_state() to sandbox_query_ffa_emul_state() * store the sandbox emulator pointer in the FF-A device uc_priv (struct ffa_priv) * set the emulator as parent of the sandbox FF-A device
v10:
* split the FF-A sandbox support into an emulator and a driver * read FFA_VERSION and FFA_PARTITION_INFO_GET state using sandbox_ffa_query_core_state() * drop CONFIG_SANDBOX_FFA config * address nits
v9: align FF-A sandbox driver with FF-A discovery through DM
v8: update ffa_bus_prvdata_get() to return a pointer rather than a pointer address
v7: state that sandbox driver supports only 64-bit direct messaging
v4: align sandbox driver with the new FF-A driver interfaces and new way of error handling
v1: introduce the sandbox driver
MAINTAINERS | 3 +- arch/sandbox/dts/sandbox.dtsi | 8 + arch/sandbox/dts/test.dts | 8 + arch/sandbox/include/asm/sandbox_arm_ffa.h | 72 ++ .../include/asm/sandbox_arm_ffa_priv.h | 133 ++++ configs/sandbox64_defconfig | 1 + configs/sandbox_defconfig | 1 + doc/arch/arm64.ffa.rst | 21 +- 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 | 738 ++++++++++++++++++ .../firmware/arm-ffa/sandbox_arm_ffa_priv.h | 14 - drivers/firmware/arm-ffa/sandbox_ffa.c | 119 +++ include/dm/uclass-id.h | 1 + 15 files changed, 1120 insertions(+), 23 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 c64804ca2d..d7caad3bd4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -269,12 +269,13 @@ F: configs/cortina_presidio-asic-pnand_defconfig ARM FF-A M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com 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: include/sandbox_arm_ffa.h
ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de diff --git a/arch/sandbox/dts/sandbox.dtsi b/arch/sandbox/dts/sandbox.dtsi index 30a305c4d2..ac713800d1 100644 --- a/arch/sandbox/dts/sandbox.dtsi +++ b/arch/sandbox/dts/sandbox.dtsi @@ -445,6 +445,14 @@ 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 d72d7a567a..bd42906159 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -1802,6 +1802,14 @@ compatible = "u-boot,fwu-mdata-gpt"; fwu-mdata-store = <&mmc0>; }; + + 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 0000000000..be2790f496 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __SANDBOX_ARM_FFA_H +#define __SANDBOX_ARM_FFA_H + +#include <arm_ffa.h> + +/* + * 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 0000000000..8fbfa3760d --- /dev/null +++ b/arch/sandbox/include/asm/sandbox_arm_ffa_priv.h @@ -0,0 +1,133 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __SANDBOX_ARM_FFA_PRV_H +#define __SANDBOX_ARM_FFA_PRV_H + +#include <arm_ffa_priv.h> + +/* 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; +}; + +/** + * struct ffa_emul_ops - Operations for the FF-A emulator + * @invoke_ffa_fn: callback for the emulated SMC call + * + * Provides all the operations supported by the FF-A emulator. + */ +struct ffa_emul_ops { + void (*invoke_ffa_fn)(ffa_value_t args, ffa_value_t *res); +}; + +#define ffa_emul_get_ops(dev) ((struct ffa_emul_ops *)(dev)->driver->ops) + +/** + * 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 af2c56ad4c..3a0371557c 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 cbace259f8..dda668046a 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -339,3 +339,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 ed7240aef7..431c29f921 100644 --- a/doc/arch/arm64.ffa.rst +++ b/doc/arch/arm64.ffa.rst @@ -33,7 +33,11 @@ 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 driver providing Arm specific methods and reusing the Uclass 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 ------------------------------------------- @@ -69,6 +73,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 --------------------------------------- @@ -105,10 +110,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. @@ -216,6 +219,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 + The armffa command -----------------------------------
diff --git a/doc/arch/sandbox/sandbox.rst b/doc/arch/sandbox/sandbox.rst index cd7f8a2cb0..c5df372e00 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 a7d5392859..d75f8b53fd 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 imply CMD_ARMFFA select LIB_UUID select DEVRES @@ -33,5 +33,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 11b1766285..318123a7f4 100644 --- a/drivers/firmware/arm-ffa/Makefile +++ b/drivers/firmware/arm-ffa/Makefile @@ -5,4 +5,12 @@ # Authors: # Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
-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 0000000000..f12bae4820 --- /dev/null +++ b/drivers/firmware/arm-ffa/ffa-emul-uclass.c @@ -0,0 +1,738 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ +#include <common.h> +#include <dm.h> +#include <mapmem.h> +#include <string.h> +#include <asm/global_data.h> +#include <asm/sandbox_arm_ffa.h> +#include <asm/sandbox_arm_ffa_priv.h> +#include <dm/device-internal.h> +#include <dm/lists.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> + +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_SP2_ID, .exec_ctxt = 0x9587, + .properties = 0x45325621 }, + .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_SP3_ID, .exec_ctxt = 0x7687, + .properties = 0x23325621 }, + .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 -ENODEV; + } + + 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); +} + +/** + * 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 -ENODEV; + } + + /* the emulator device should be destroyed last */ + ret = device_reparent(dev, *emulp); + if (ret) { + log_err("Cannot set FF-A emulator as parent\n"); + return -ENODEV; + } + + log_info("FF-A emulator ready to use\n"); + + return 0; +} + +UCLASS_DRIVER(ffa_emul) = { + .name = "ffa_emul", + .id = UCLASS_FFA_EMUL, +}; + +/* Sandbox Arm FF-A emulator operations */ + +static const struct ffa_emul_ops sandbox_ffa_emul_ops = { + .invoke_ffa_fn = sandbox_arm_ffa_smccc_smc, +}; + +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, + .ops = &sandbox_ffa_emul_ops, + .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 4338f9c9b1..0000000000 --- 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 open-source-office@arm.com - * - * Authors: - * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com - */ - -#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 0000000000..2398305404 --- /dev/null +++ b/drivers/firmware/arm-ffa/sandbox_ffa.c @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ +#include <common.h> +#include <arm_ffa.h> +#include <dm.h> +#include <log.h> +#include <asm/global_data.h> +#include <asm/sandbox_arm_ffa_priv.h> +#include <dm/device-internal.h> +#include <linux/errno.h> + +DECLARE_GLOBAL_DATA_PTR; + +/** + * sandbox_ffa_set_smc() - Set the SMC conduit + * @dev: the sandbox-arm-ffa device + * + * Select the SMC conduit by setting the FF-A ABI invoke function. + * The function emulating the SMC call is provided by the FF-A emulator. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int sandbox_ffa_set_smc(struct udevice *dev) +{ + struct udevice *emul; + int ret; + + ret = ffa_emul_find(dev, &emul); + if (ret) { + log_err("Cannot find FF-A emulator, SMC emulation failure\n"); + return -ENODEV; + } + + if (!ffa_emul_get_ops(emul)->invoke_ffa_fn) { + log_err("Cannot get FF-A emulator ops, SMC emulation failure\n"); + return -ENOSYS; + } + + dscvry_info.emul = emul; + dscvry_info.invoke_ffa_fn = ffa_emul_get_ops(emul)->invoke_ffa_fn; + + log_info("Using emulated Arm SMC for FF-A conduit\n"); + + return 0; +} + +/** + * 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; + + log_info("Emulated FF-A framework discovery\n"); + + ret = sandbox_ffa_set_smc(dev); + if (ret) + return ret; + + ret = ffa_get_version_hdlr(dev); + if (ret) + 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, + .ops = &sandbox_ffa_ops, +}; diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index df77c7da58..4658411935 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -61,6 +61,7 @@ enum uclass_id { UCLASS_ETH, /* Ethernet device */ UCLASS_ETH_PHY, /* Ethernet PHY device */ 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 */

Hi Abdellatif,
On Wed, 12 Apr 2023 at 03:43, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
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 abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
Changelog:
v11:
- rename ffa_try_discovery() to sandbox_ffa_discover()
- rename sandbox_ffa_query_core_state() to sandbox_query_ffa_emul_state()
- store the sandbox emulator pointer in the FF-A device uc_priv (struct ffa_priv)
- set the emulator as parent of the sandbox FF-A device
This is close, but not quite what I expected.
I suspect the emulator should be the child of the FF-A device, not the parent? You should update the devicetree to show that. You should not need to reparent anything.
Then you put this in your FFA uclass so it binds the emulator:
.post_bind = dm_scan_fdt_dev,
Finding the emulator is probably then just a case of calling device_find_first_child().
I notice that you are sometimes breaking lines very short, e.g. the memset() lines. Please try to use all 80cols if you can.
Regards, Simon

Hi Simon,
Hi Abdellatif,
On Wed, 12 Apr 2023 at 03:43, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
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 abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
Changelog:
v11:
- rename ffa_try_discovery() to sandbox_ffa_discover()
- rename sandbox_ffa_query_core_state() to sandbox_query_ffa_emul_state()
- store the sandbox emulator pointer in the FF-A device uc_priv (struct ffa_priv)
- set the emulator as parent of the sandbox FF-A device
This is close, but not quite what I expected.
I suspect the emulator should be the child of the FF-A device, not the parent? You should update the devicetree to show that. You should not need to reparent anything.
Then you put this in your FFA uclass so it binds the emulator:
.post_bind = dm_scan_fdt_dev,
Thanks. Sorry for the late reply, I was in holidays.
I made few tweaks based on your suggestion and this will be in v12 patchset (work in progress). In v12, reparenting will be removed and DT will be used to reflect the relationship between the emulator and the FF-A device. Also, dm_scan_fdt_dev() is used to bind the child.
However, in case of FF-A the emulator should be the parent.
Please refer to the explanation below for more details.
DT nodes: (tested and works)
arm-ffa-emul { compatible = "sandbox,arm-ffa-emul";
sandbox-arm-ffa { compatible = "sandbox,arm-ffa"; }; };
In real HW, the secure side exists before the FF-A device is set up. The FF-A device needs the secure side up and running so it can query the FF-A framework version during FF-A discovery.
The same concept is followed in sandbox mode:
- The emulator device is the parent of the FF-A device. So, the emulator priv exists in memory before the FF-A device is bound. The emulator priv contains the secure side data (i.e: FF-A framework version)
- The FF-A device is the child, when bound it discovers FF-A framework by querying the version from the emulator
I hope this suggestion makes sense to you.
Cheers, Abdellatif

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 abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
--- Changelog: ===============
v11:
* drop unmapping test (taken care of by the DM when removing the device) * address nits
v10:
* use the FF-A driver Uclass operations * use uclass_first_device() * replace CONFIG_SANDBOX_FFA with CONFIG_ARM_FFA_TRANSPORT * address nits
v9: align FF-A sandbox tests with FF-A discovery through DM
v8:
* update partition_info_get() second argument to be an SP count * pass NULL device pointer to the FF-A bus discovery and operations
v7: set the tests to use 64-bit direct messaging
v4: align sandbox tests with the new FF-A driver interfaces and new way of error handling
v1: introduce sandbox tests
MAINTAINERS | 1 + doc/arch/arm64.ffa.rst | 1 + test/dm/Makefile | 2 + test/dm/ffa.c | 258 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 262 insertions(+) create mode 100644 test/dm/ffa.c
diff --git a/MAINTAINERS b/MAINTAINERS index d7caad3bd4..cd99e655db 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -276,6 +276,7 @@ F: doc/arch/arm64.ffa.rst F: doc/usage/cmd/armffa.rst F: drivers/firmware/arm-ffa/ F: include/arm_ffa.h +F: test/dm/ffa.c
ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst index 431c29f921..fdcb008d72 100644 --- a/doc/arch/arm64.ffa.rst +++ b/doc/arch/arm64.ffa.rst @@ -38,6 +38,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 7a79b6e1a2..45563bdfb4 100644 --- a/test/dm/Makefile +++ b/test/dm/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 open-source-office@arm.com
obj-$(CONFIG_UT_DM) += test-dm.o
@@ -85,6 +86,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 0000000000..ace2582e22 --- /dev/null +++ b/test/dm/ffa.c @@ -0,0 +1,258 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Functional tests for UCLASS_FFA class + * + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <console.h> +#include <dm.h> +#include <asm/sandbox_arm_ffa.h> +#include <asm/sandbox_arm_ffa_priv.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +/* 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->dscvry_info.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_info parts_info[SANDBOX_SP_COUNT_PER_VALID_SERVICE] = {0}; + u32 count, info_idx, exp_info_idx, 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 of the SPs matching the UUID */ + ut_assertok(ffa_partition_info_get(dev, service_uuid, &count, NULL)); + /* Make sure partitions are detected */ + ut_asserteq(SANDBOX_SP_COUNT_PER_VALID_SERVICE, count); + + /* Ask the driver to fill the buffer with the SPs info */ + ut_assertok(ffa_partition_info_get(dev, service_uuid, &count, parts_info)); + + /* 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 (info_idx = 0; info_idx < count ; info_idx++) { + for (exp_info_idx = 0; + exp_info_idx < partitions->count; + exp_info_idx++) { + if (parts_info[info_idx].id == + partitions->descs[exp_info_idx].info.id) { + valid_sps++; + ut_asserteq_mem(&parts_info[info_idx], + &partitions->descs[exp_info_idx] + .info, + sizeof(struct ffa_partition_info)); + /* Send and receive data from the current partition */ + test_ffa_msg_send_direct_req(parts_info[info_idx].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; + + /* 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, unvalid_svc_uuid, NULL, NULL); + ut_asserteq(-EINVAL, ret); + + /* Query partitions count using an invalid UUID string */ + ret = ffa_partition_info_get(dev, unvalid_svc_uuid_str, &count, NULL); + 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, NULL); + ut_assertok(count); + + /* Query partitions count using a valid UUID */ + count = 0; + ut_assertok(ffa_partition_info_get(dev, valid_svc_uuid, &count, NULL)); + /* Make sure partitions are detected */ + ut_asserteq(SANDBOX_SP_COUNT_PER_VALID_SERVICE, count); + + /* 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);

On Wed, 12 Apr 2023 at 03:43, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
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 abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
Changelog:
v11:
- drop unmapping test (taken care of by the DM when removing the device)
- address nits
v10:
- use the FF-A driver Uclass operations
- use uclass_first_device()
- replace CONFIG_SANDBOX_FFA with CONFIG_ARM_FFA_TRANSPORT
- address nits
v9: align FF-A sandbox tests with FF-A discovery through DM
v8:
- update partition_info_get() second argument to be an SP count
- pass NULL device pointer to the FF-A bus discovery and operations
v7: set the tests to use 64-bit direct messaging
v4: align sandbox tests with the new FF-A driver interfaces and new way of error handling
v1: introduce sandbox tests
MAINTAINERS | 1 + doc/arch/arm64.ffa.rst | 1 + test/dm/Makefile | 2 + test/dm/ffa.c | 258 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 262 insertions(+) create mode 100644 test/dm/ffa.c
Reviewed-by: Simon Glass sjg@chromium.org
nits below
diff --git a/MAINTAINERS b/MAINTAINERS index d7caad3bd4..cd99e655db 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -276,6 +276,7 @@ F: doc/arch/arm64.ffa.rst F: doc/usage/cmd/armffa.rst F: drivers/firmware/arm-ffa/ F: include/arm_ffa.h +F: test/dm/ffa.c
ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst index 431c29f921..fdcb008d72 100644 --- a/doc/arch/arm64.ffa.rst +++ b/doc/arch/arm64.ffa.rst @@ -38,6 +38,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 7a79b6e1a2..45563bdfb4 100644 --- a/test/dm/Makefile +++ b/test/dm/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 open-source-office@arm.com
obj-$(CONFIG_UT_DM) += test-dm.o
@@ -85,6 +86,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 0000000000..ace2582e22 --- /dev/null +++ b/test/dm/ffa.c @@ -0,0 +1,258 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Functional tests for UCLASS_FFA class
- Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
- Authors:
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include <common.h> +#include <console.h> +#include <dm.h> +#include <asm/sandbox_arm_ffa.h> +#include <asm/sandbox_arm_ffa_priv.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h>
+/* 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->dscvry_info.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_info parts_info[SANDBOX_SP_COUNT_PER_VALID_SERVICE] = {0};
u32 count, info_idx, exp_info_idx, 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 of the SPs matching the UUID */
ut_assertok(ffa_partition_info_get(dev, service_uuid, &count, NULL));
/* Make sure partitions are detected */
ut_asserteq(SANDBOX_SP_COUNT_PER_VALID_SERVICE, count);
/* Ask the driver to fill the buffer with the SPs info */
ut_assertok(ffa_partition_info_get(dev, service_uuid, &count, parts_info));
/* 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 (info_idx = 0; info_idx < count ; info_idx++) {
Use i as the index var ?
for (exp_info_idx = 0;
exp_info_idx < partitions->count;
exp_info_idx++) {
if (parts_info[info_idx].id ==
partitions->descs[exp_info_idx].info.id) {
valid_sps++;
ut_asserteq_mem(&parts_info[info_idx],
&partitions->descs[exp_info_idx]
.info,
sizeof(struct ffa_partition_info));
/* Send and receive data from the current partition */
test_ffa_msg_send_direct_req(parts_info[info_idx].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;
/* 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, unvalid_svc_uuid, NULL, NULL);
ut_asserteq(-EINVAL, ret);
/* Query partitions count using an invalid UUID string */
ret = ffa_partition_info_get(dev, unvalid_svc_uuid_str, &count, NULL);
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, NULL);
ut_assertok(count);
ut_assertok() is supposed to be used for error returns. So this should be:
ut_asserteq(0, count)
/* Query partitions count using a valid UUID */
count = 0;
ut_assertok(ffa_partition_info_get(dev, valid_svc_uuid, &count, NULL));
/* Make sure partitions are detected */
ut_asserteq(SANDBOX_SP_COUNT_PER_VALID_SERVICE, count);
/* 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);
2.25.1
Regards, Simon

Add Sandbox test for the armffa command
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
--- Changelog: ===============
v10:
* replace CMD_RET_SUCCESS with 0 * replace CONFIG_SANDBOX_FFA with CONFIG_ARM_FFA_TRANSPORT
v9: align the test with FF-A discovery through DM
v4: drop use of helper APIs
v1: introduce armffa command sandbox test
MAINTAINERS | 1 + test/cmd/Makefile | 2 ++ test/cmd/armffa.c | 39 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+) create mode 100644 test/cmd/armffa.c
diff --git a/MAINTAINERS b/MAINTAINERS index cd99e655db..67eed92a9b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -276,6 +276,7 @@ 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/test/cmd/Makefile b/test/cmd/Makefile index 055adc65a2..1d1dbb4fbc 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 open-source-office@arm.com
ifdef CONFIG_HUSH_PARSER obj-$(CONFIG_CONSOLE_RECORD) += test_echo.o @@ -23,6 +24,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 0000000000..a9768dbd2c --- /dev/null +++ b/test/cmd/armffa.c @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test for armffa command + * + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <string.h> +#include <asm/sandbox_arm_ffa.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +#define PING_CMD_SIZE 19 + +/* Basic test of 'armffa' command */ +static int dm_test_armffa_cmd(struct unit_test_state *uts) +{ + char ping_cmd[PING_CMD_SIZE] = {0}; + + /* armffa getpart <UUID> */ + ut_assertok(run_command("armffa getpart " SANDBOX_SERVICE1_UUID, 0)); + + snprintf(ping_cmd, PING_CMD_SIZE, "armffa ping 0x%x", SANDBOX_SP1_ID); + + /* armffa ping <ID> */ + ut_assertok(run_command(ping_cmd, 0)); + + /* 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);

Hi Abdellatif,
On Wed, 12 Apr 2023 at 03:43, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
Add Sandbox test for the armffa command
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v10:
- replace CMD_RET_SUCCESS with 0
- replace CONFIG_SANDBOX_FFA with CONFIG_ARM_FFA_TRANSPORT
v9: align the test with FF-A discovery through DM
v4: drop use of helper APIs
v1: introduce armffa command sandbox test
MAINTAINERS | 1 + test/cmd/Makefile | 2 ++ test/cmd/armffa.c | 39 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+) create mode 100644 test/cmd/armffa.c
Reviewed-by: Simon Glass sjg@chromium.org
diff --git a/MAINTAINERS b/MAINTAINERS index cd99e655db..67eed92a9b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -276,6 +276,7 @@ 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/test/cmd/Makefile b/test/cmd/Makefile index 055adc65a2..1d1dbb4fbc 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 open-source-office@arm.com
ifdef CONFIG_HUSH_PARSER obj-$(CONFIG_CONSOLE_RECORD) += test_echo.o @@ -23,6 +24,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 0000000000..a9768dbd2c --- /dev/null +++ b/test/cmd/armffa.c @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Test for armffa command
- Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
- Authors:
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include <common.h> +#include <string.h> +#include <asm/sandbox_arm_ffa.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h>
+#define PING_CMD_SIZE 19
+/* Basic test of 'armffa' command */ +static int dm_test_armffa_cmd(struct unit_test_state *uts) +{
char ping_cmd[PING_CMD_SIZE] = {0};
No need to assign a value for something that gets written in the function.
/* armffa getpart <UUID> */
ut_assertok(run_command("armffa getpart " SANDBOX_SERVICE1_UUID, 0));
snprintf(ping_cmd, PING_CMD_SIZE, "armffa ping 0x%x", SANDBOX_SP1_ID);
/* armffa ping <ID> */
ut_assertok(run_command(ping_cmd, 0));
See run_commandf() so you can =avoid the sprintf()
/* 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);
2.25.1
Regards, Simon

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.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Signed-off-by: Gowtham Suresh Kumar gowtham.sureshkumar@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
--- Changelog: ===============
v11:
* rename select_ffa_mm_comms() to select_mm_comms() * improve the logic of MM transport selection in mm_communicate() * addressing nits
v10:
* use the FF-A driver Uclass operations * use uclass_first_device() * addressing nits
v9: align how FF-A is used with FF-A discovery through DM
v8:
* isolate the compilation choices between FF-A and OP-TEE * update partition_info_get() second argument to be an SP count * pass NULL device pointer to the FF-A bus discovery and operations
v7:
* set the MM door bell event to use 64-bit direct messaging * issue a compile time error when one of these macros are not found : FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_OFFSET, FFA_SHARED_MM_BUFFER_ADDR * make mm_sp_svc_uuid static * replace EINVAL with ENOMEM in ffa_discover_mm_sp_id() when calloc() fails * improve use of unmap_sysmem() in ffa_mm_communicate()
v6:
* add FF-A runtime discovery at MM communication level * drop EFI runtime support for FF-A MM communication * revert the changes in include/mm_communication.h for efi_mm_communicate_header and smm_variable_access structures
v4:
* use the new FF-A driver interfaces * discover MM partitions at runtime * copy FF-A driver private data to EFI runtime section at ExitBootServices() * drop use of FFA_ERR_STAT_SUCCESS error code * replace EFI_BUFFER_TOO_SMALL with EFI_OUT_OF_RESOURCES in ffa_mm_communicate(). No need for efi_memcpy_runtime() anymore * revert the error log in mm_communicate() in case of failure * remove packed attribute from efi_mm_communicate_header and smm_variable_communicate_header
v2:
* set default values to 0 for FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_ADDR and MM_SP_UUID_DATA and add warnings
v1:
* introduce FF-A MM communication
include/mm_communication.h | 13 ++ lib/efi_loader/Kconfig | 14 +- lib/efi_loader/efi_variable_tee.c | 312 +++++++++++++++++++++++++++++- 3 files changed, 333 insertions(+), 6 deletions(-)
diff --git a/include/mm_communication.h b/include/mm_communication.h index e65fbde60d..f17847583b 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. sughosh.ganu@linaro.org * Copyright (C) 2020 Linaro Ltd. ilias.apalodimas@linaro.org + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#ifndef _MM_COMMUNICATION_H_ @@ -13,6 +16,9 @@
#include <part_efi.h>
+/* MM service UUID string (big-endian format). This UUID is common across all MM SPs */ +#define MM_SP_UUID "33d532ed-e699-0942-c09c-a798d9cd722d" + /* * Interface to the pseudo Trusted Application (TA), which provides a * communication channel with the Standalone MM (Management Mode) @@ -248,4 +254,11 @@ struct smm_variable_var_check_property { u16 name[]; };
+/* supported MM transports */ +enum mm_comms_select { + MM_COMMS_UNDEFINED, + MM_COMMS_FFA, + MM_COMMS_OPTEE +}; + #endif /* _MM_COMMUNICATION_H_ */ diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index c5835e6ef6..08a6b84101 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -55,13 +55,23 @@ 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" - depends on OPTEE + bool "UEFI variables storage service via the trusted world" + depends on OPTEE || ARM_FFA_TRANSPORT 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 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 dfef18435d..a6ec126db5 100644 --- a/lib/efi_loader/efi_variable_tee.c +++ b/lib/efi_loader/efi_variable_tee.c @@ -4,9 +4,14 @@ * * Copyright (C) 2019 Linaro Ltd. sughosh.ganu@linaro.org * Copyright (C) 2019 Linaro Ltd. ilias.apalodimas@linaro.org + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> +#include <dm.h> #include <efi.h> #include <efi_api.h> #include <efi_loader.h> @@ -15,6 +20,36 @@ #include <malloc.h> #include <mm_communication.h>
+#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) + +#include <arm_ffa.h> +#include <cpu_func.h> +#include <mapmem.h> + +#ifndef FFA_SHARED_MM_BUFFER_SIZE +#error "FFA_SHARED_MM_BUFFER_SIZE must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_SIZE 0 +#endif + +#ifndef FFA_SHARED_MM_BUFFER_OFFSET +#error "FFA_SHARED_MM_BUFFER_OFFSET must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_OFFSET 0 +#endif + +#ifndef FFA_SHARED_MM_BUFFER_ADDR +#error "FFA_SHARED_MM_BUFFER_ADDR must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_ADDR 0 +#endif + +/* MM return codes */ +#define MM_SUCCESS (0) + +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 */ static efi_uintn_t max_payload_size; /* func + data */ @@ -24,6 +59,7 @@ struct mm_connection { u32 session; };
+#if (IS_ENABLED(CONFIG_OPTEE)) /** * get_connection() - Retrieve OP-TEE session for a specific UUID. * @@ -60,6 +96,7 @@ static int get_connection(struct mm_connection *conn) out: return rc; } +#endif
/** * optee_mm_communicate() - Pass a buffer to StandaloneMM running in OP-TEE @@ -70,6 +107,7 @@ out: */ static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize) { +#if (IS_ENABLED(CONFIG_OPTEE)) ulong buf_size; efi_status_t ret; struct efi_mm_communicate_header *mm_hdr; @@ -142,19 +180,269 @@ static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize) }
return ret; +#else + log_err("EFI: Please enable CONFIG_OPTEE for MM comms\n"); + return EFI_UNSUPPORTED; +#endif +} + +#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) + +/** + * 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 = -1; + 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 -ENODEV; + } + + msg.data0 = FFA_SHARED_MM_BUFFER_OFFSET; /* x3 */ + + ret = ffa_sync_send_receive(dev, mm_sp_id, &msg, 1); + if (ret) + return ret; + + sp_event_ret = msg.data0; /* x3 */ + + if (sp_event_ret == MM_SUCCESS) + return 0; + + /* Failure to notify the MM SP */ + + return -EACCES; +} + +/** + * 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_info *parts_info; + 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 -ENODEV; + } + + /* Get from the driver the count of the SPs matching the UUID */ + ret = ffa_partition_info_get(dev, mm_sp_svc_uuid, &count, NULL); + if (ret) { + log_err("EFI: Failure in querying SPs count (%d), MM SP discovery failure\n", ret); + return ret; + } + + if (!count) { + log_info("EFI: No MM partition found\n"); + return ret; + } + + /* Pre-allocate a buffer to be filled by the driver with ffa_partition_info structs */ + + log_info("EFI: Pre-allocating %d partition(s) info structures\n", count); + + parts_info = calloc(count, sizeof(*parts_info)); + if (!parts_info) + return -ENOMEM; + + /* Ask the driver to fill the buffer with the SPs info */ + ret = ffa_partition_info_get(dev, mm_sp_svc_uuid, &count, parts_info); + if (ret) { + log_err("EFI: Failure in querying SPs info (%d), MM SP discovery failure\n", ret); + free(parts_info); + return ret; + } + + /* MM SPs found , use the first one */ + + mm_sp_id = parts_info[0].id; + + log_info("EFI: MM partition ID 0x%x\n", mm_sp_id); + + free(parts_info); + + return 0; +} +#endif + +/** + * 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) +{ +#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) + 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 > FFA_SHARED_MM_BUFFER_SIZE) + return EFI_INVALID_PARAMETER; + + /* Copy the data to the shared buffer */ + + virt_shared_buf = map_sysmem((phys_addr_t)FFA_SHARED_MM_BUFFER_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; +#else + log_err("EFI: Please enable CONFIG_ARM_FFA_TRANSPORT for MM comms\n"); + return EFI_UNSUPPORTED; +#endif +} + +/** + * select_mm_comms() - detect the available MM transport + * + * If FF-A is compiled in, make sure the FF-A bus is probed successfully + * which means FF-A communication with secure world works and ready + * to be used. + * + * If FF-A is not detected, check if OPTEE support is compiled in. + * + * Return: + * + * On success MM_COMMS_FFA or MM_COMMS_OPTEE. Otherwise, MM_COMMS_UNDEFINED + */ +static enum mm_comms_select select_mm_comms(void) +{ + struct udevice *dev; + int ret; + + if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) { + ret = uclass_first_device_err(UCLASS_FFA, &dev); + if (ret) + log_err("EFI: Cannot find FF-A bus device, cannot select FF-A comms\n"); + else + return MM_COMMS_FFA; + } + + if (IS_ENABLED(CONFIG_OPTEE)) + return MM_COMMS_OPTEE; + + return MM_COMMS_UNDEFINED; }
/** - * mm_communicate() - Adjust the cmonnucation buffer to StandAlonneMM and send + * 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) { efi_status_t ret; + enum mm_comms_select mm_comms; struct efi_mm_communicate_header *mm_hdr; struct smm_variable_communicate_header *var_hdr;
@@ -162,7 +450,16 @@ static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) 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); + mm_comms = select_mm_comms(); + if (mm_comms == MM_COMMS_UNDEFINED) { + ret = EFI_UNSUPPORTED; + } else { + if (mm_comms == MM_COMMS_OPTEE) + ret = optee_mm_communicate(comm_buf, dsize); + else + ret = ffa_mm_communicate(comm_buf, dsize); + } + if (ret != EFI_SUCCESS) { log_err("%s failed!\n", __func__); return ret; @@ -258,6 +555,13 @@ efi_status_t EFIAPI get_max_payload(efi_uintn_t *size) goto out; } *size = var_payload->size; + + #if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) + if (*size > FFA_SHARED_MM_BUFFER_SIZE) + *size = FFA_SHARED_MM_BUFFER_SIZE - MM_COMMUNICATE_HEADER_SIZE - + MM_VARIABLE_COMMUNICATE_SIZE; + #endif + /* * There seems to be a bug in EDK2 miscalculating the boundaries and * size checks, so deduct 2 more bytes to fulfill this requirement. Fix @@ -697,7 +1001,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);
/*

Hi Abdellatif,
On Wed, 12 Apr 2023 at 03:43, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
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.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Signed-off-by: Gowtham Suresh Kumar gowtham.sureshkumar@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v11:
- rename select_ffa_mm_comms() to select_mm_comms()
- improve the logic of MM transport selection in mm_communicate()
- addressing nits
v10:
- use the FF-A driver Uclass operations
- use uclass_first_device()
- addressing nits
v9: align how FF-A is used with FF-A discovery through DM
v8:
- isolate the compilation choices between FF-A and OP-TEE
- update partition_info_get() second argument to be an SP count
- pass NULL device pointer to the FF-A bus discovery and operations
v7:
- set the MM door bell event to use 64-bit direct messaging
- issue a compile time error when one of these macros are not found : FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_OFFSET, FFA_SHARED_MM_BUFFER_ADDR
- make mm_sp_svc_uuid static
- replace EINVAL with ENOMEM in ffa_discover_mm_sp_id() when calloc() fails
- improve use of unmap_sysmem() in ffa_mm_communicate()
v6:
- add FF-A runtime discovery at MM communication level
- drop EFI runtime support for FF-A MM communication
- revert the changes in include/mm_communication.h for efi_mm_communicate_header and smm_variable_access structures
v4:
- use the new FF-A driver interfaces
- discover MM partitions at runtime
- copy FF-A driver private data to EFI runtime section at ExitBootServices()
- drop use of FFA_ERR_STAT_SUCCESS error code
- replace EFI_BUFFER_TOO_SMALL with EFI_OUT_OF_RESOURCES in ffa_mm_communicate(). No need for efi_memcpy_runtime() anymore
- revert the error log in mm_communicate() in case of failure
- remove packed attribute from efi_mm_communicate_header and smm_variable_communicate_header
v2:
- set default values to 0 for FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_ADDR and MM_SP_UUID_DATA and add warnings
v1:
- introduce FF-A MM communication
include/mm_communication.h | 13 ++ lib/efi_loader/Kconfig | 14 +- lib/efi_loader/efi_variable_tee.c | 312 +++++++++++++++++++++++++++++- 3 files changed, 333 insertions(+), 6 deletions(-)
diff --git a/include/mm_communication.h b/include/mm_communication.h index e65fbde60d..f17847583b 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. sughosh.ganu@linaro.org
- Copyright (C) 2020 Linaro Ltd. ilias.apalodimas@linaro.org
- Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
- Authors:
*/
Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
#ifndef _MM_COMMUNICATION_H_ @@ -13,6 +16,9 @@
#include <part_efi.h>
+/* MM service UUID string (big-endian format). This UUID is common across all MM SPs */ +#define MM_SP_UUID "33d532ed-e699-0942-c09c-a798d9cd722d"
/*
- Interface to the pseudo Trusted Application (TA), which provides a
- communication channel with the Standalone MM (Management Mode)
@@ -248,4 +254,11 @@ struct smm_variable_var_check_property { u16 name[]; };
+/* supported MM transports */ +enum mm_comms_select {
MM_COMMS_UNDEFINED,
MM_COMMS_FFA,
MM_COMMS_OPTEE
+};
#endif /* _MM_COMMUNICATION_H_ */ diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index c5835e6ef6..08a6b84101 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -55,13 +55,23 @@ 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"
depends on OPTEE
bool "UEFI variables storage service via the trusted world"
depends on OPTEE || ARM_FFA_TRANSPORT 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 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 dfef18435d..a6ec126db5 100644 --- a/lib/efi_loader/efi_variable_tee.c +++ b/lib/efi_loader/efi_variable_tee.c @@ -4,9 +4,14 @@
- Copyright (C) 2019 Linaro Ltd. sughosh.ganu@linaro.org
- Copyright (C) 2019 Linaro Ltd. ilias.apalodimas@linaro.org
- Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
- Authors:
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#include <common.h> +#include <dm.h> #include <efi.h> #include <efi_api.h> #include <efi_loader.h> @@ -15,6 +20,36 @@ #include <malloc.h> #include <mm_communication.h>
+#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
+#include <arm_ffa.h> +#include <cpu_func.h> +#include <mapmem.h>
+#ifndef FFA_SHARED_MM_BUFFER_SIZE +#error "FFA_SHARED_MM_BUFFER_SIZE must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_SIZE 0 +#endif
+#ifndef FFA_SHARED_MM_BUFFER_OFFSET +#error "FFA_SHARED_MM_BUFFER_OFFSET must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_OFFSET 0 +#endif
+#ifndef FFA_SHARED_MM_BUFFER_ADDR +#error "FFA_SHARED_MM_BUFFER_ADDR must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_ADDR 0 +#endif
+/* MM return codes */ +#define MM_SUCCESS (0)
+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 */ static efi_uintn_t max_payload_size; /* func + data */ @@ -24,6 +59,7 @@ struct mm_connection { u32 session; };
+#if (IS_ENABLED(CONFIG_OPTEE)) /**
- get_connection() - Retrieve OP-TEE session for a specific UUID.
@@ -60,6 +96,7 @@ static int get_connection(struct mm_connection *conn) out: return rc; } +#endif
/**
- optee_mm_communicate() - Pass a buffer to StandaloneMM running in OP-TEE
@@ -70,6 +107,7 @@ out: */ static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize) { +#if (IS_ENABLED(CONFIG_OPTEE)) ulong buf_size; efi_status_t ret; struct efi_mm_communicate_header *mm_hdr; @@ -142,19 +180,269 @@ static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize) }
return ret;
+#else
log_err("EFI: Please enable CONFIG_OPTEE for MM comms\n");
return EFI_UNSUPPORTED;
+#endif +}
+#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
drop extra brackets
+/**
- 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 = -1;
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 -ENODEV;
return ret
}
msg.data0 = FFA_SHARED_MM_BUFFER_OFFSET; /* x3 */
ret = ffa_sync_send_receive(dev, mm_sp_id, &msg, 1);
if (ret)
return ret;
sp_event_ret = msg.data0; /* x3 */
if (sp_event_ret == MM_SUCCESS)
return 0;
/* Failure to notify the MM SP */
return -EACCES;
+}
+/**
- 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_info *parts_info;
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 -ENODEV;
return ret
Please check that you don't change error returns unless you really want to, as it makes it hard to debug failures.
}
/* Get from the driver the count of the SPs matching the UUID */
ret = ffa_partition_info_get(dev, mm_sp_svc_uuid, &count, NULL);
if (ret) {
log_err("EFI: Failure in querying SPs count (%d), MM SP discovery failure\n", ret);
return ret;
}
if (!count) {
log_info("EFI: No MM partition found\n");
return ret;
}
/* Pre-allocate a buffer to be filled by the driver with ffa_partition_info structs */
log_info("EFI: Pre-allocating %d partition(s) info structures\n", count);
parts_info = calloc(count, sizeof(*parts_info));
Use a local var and drop the calloc()
if (!parts_info)
return -ENOMEM;
/* Ask the driver to fill the buffer with the SPs info */
ret = ffa_partition_info_get(dev, mm_sp_svc_uuid, &count, parts_info);
if (ret) {
log_err("EFI: Failure in querying SPs info (%d), MM SP discovery failure\n", ret);
free(parts_info);
return ret;
}
/* MM SPs found , use the first one */
mm_sp_id = parts_info[0].id;
log_info("EFI: MM partition ID 0x%x\n", mm_sp_id);
free(parts_info);
return 0;
+} +#endif
+/**
- 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) +{ +#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
Please use if() if you can?
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 > FFA_SHARED_MM_BUFFER_SIZE)
return EFI_INVALID_PARAMETER;
/* Copy the data to the shared buffer */
virt_shared_buf = map_sysmem((phys_addr_t)FFA_SHARED_MM_BUFFER_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;
+#else
log_err("EFI: Please enable CONFIG_ARM_FFA_TRANSPORT for MM comms\n");
return EFI_UNSUPPORTED;
+#endif +}
+/**
- select_mm_comms() - detect the available MM transport
- If FF-A is compiled in, make sure the FF-A bus is probed successfully
- which means FF-A communication with secure world works and ready
- to be used.
- If FF-A is not detected, check if OPTEE support is compiled in.
- Return:
- On success MM_COMMS_FFA or MM_COMMS_OPTEE. Otherwise, MM_COMMS_UNDEFINED
- */
+static enum mm_comms_select select_mm_comms(void) +{
struct udevice *dev;
int ret;
if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) {
ret = uclass_first_device_err(UCLASS_FFA, &dev);
if (ret)
log_err("EFI: Cannot find FF-A bus device, cannot select FF-A comms\n");
else
return MM_COMMS_FFA;
}
if (IS_ENABLED(CONFIG_OPTEE))
return MM_COMMS_OPTEE;
return MM_COMMS_UNDEFINED;
}
/**
- mm_communicate() - Adjust the cmonnucation buffer to StandAlonneMM and send
- 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) { efi_status_t ret;
enum mm_comms_select mm_comms; struct efi_mm_communicate_header *mm_hdr; struct smm_variable_communicate_header *var_hdr;
@@ -162,7 +450,16 @@ static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) 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);
mm_comms = select_mm_comms();
if (mm_comms == MM_COMMS_UNDEFINED) {
ret = EFI_UNSUPPORTED;
} else {
if (mm_comms == MM_COMMS_OPTEE)
ret = optee_mm_communicate(comm_buf, dsize);
else
ret = ffa_mm_communicate(comm_buf, dsize);
}
if (ret != EFI_SUCCESS) { log_err("%s failed!\n", __func__); return ret;
@@ -258,6 +555,13 @@ efi_status_t EFIAPI get_max_payload(efi_uintn_t *size) goto out; } *size = var_payload->size;
#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
Can this use if () ?
if (*size > FFA_SHARED_MM_BUFFER_SIZE)
*size = FFA_SHARED_MM_BUFFER_SIZE - MM_COMMUNICATE_HEADER_SIZE -
MM_VARIABLE_COMMUNICATE_SIZE;
#endif
/* * There seems to be a bug in EDK2 miscalculating the boundaries and * size checks, so deduct 2 more bytes to fulfill this requirement. Fix
@@ -697,7 +1001,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); /*
-- 2.25.1
Regards, Simon

On Tue, Apr 18, 2023 at 07:49:07PM -0600, Simon Glass wrote:
Hi Simon,
On Wed, 12 Apr 2023 at 03:43, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
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.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Signed-off-by: Gowtham Suresh Kumar gowtham.sureshkumar@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v11:
- rename select_ffa_mm_comms() to select_mm_comms()
- improve the logic of MM transport selection in mm_communicate()
- addressing nits
v10:
- use the FF-A driver Uclass operations
- use uclass_first_device()
- addressing nits
v9: align how FF-A is used with FF-A discovery through DM
v8:
- isolate the compilation choices between FF-A and OP-TEE
- update partition_info_get() second argument to be an SP count
- pass NULL device pointer to the FF-A bus discovery and operations
v7:
- set the MM door bell event to use 64-bit direct messaging
- issue a compile time error when one of these macros are not found : FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_OFFSET, FFA_SHARED_MM_BUFFER_ADDR
- make mm_sp_svc_uuid static
- replace EINVAL with ENOMEM in ffa_discover_mm_sp_id() when calloc() fails
- improve use of unmap_sysmem() in ffa_mm_communicate()
v6:
- add FF-A runtime discovery at MM communication level
- drop EFI runtime support for FF-A MM communication
- revert the changes in include/mm_communication.h for efi_mm_communicate_header and smm_variable_access structures
v4:
- use the new FF-A driver interfaces
- discover MM partitions at runtime
- copy FF-A driver private data to EFI runtime section at ExitBootServices()
- drop use of FFA_ERR_STAT_SUCCESS error code
- replace EFI_BUFFER_TOO_SMALL with EFI_OUT_OF_RESOURCES in ffa_mm_communicate(). No need for efi_memcpy_runtime() anymore
- revert the error log in mm_communicate() in case of failure
- remove packed attribute from efi_mm_communicate_header and smm_variable_communicate_header
v2:
- set default values to 0 for FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_ADDR and MM_SP_UUID_DATA and add warnings
v1:
- introduce FF-A MM communication
include/mm_communication.h | 13 ++ lib/efi_loader/Kconfig | 14 +- lib/efi_loader/efi_variable_tee.c | 312 +++++++++++++++++++++++++++++- 3 files changed, 333 insertions(+), 6 deletions(-)
diff --git a/include/mm_communication.h b/include/mm_communication.h index e65fbde60d..f17847583b 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. sughosh.ganu@linaro.org
- Copyright (C) 2020 Linaro Ltd. ilias.apalodimas@linaro.org
- Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
- Authors:
*/
Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
#ifndef _MM_COMMUNICATION_H_ @@ -13,6 +16,9 @@
#include <part_efi.h>
+/* MM service UUID string (big-endian format). This UUID is common across all MM SPs */ +#define MM_SP_UUID "33d532ed-e699-0942-c09c-a798d9cd722d"
/*
- Interface to the pseudo Trusted Application (TA), which provides a
- communication channel with the Standalone MM (Management Mode)
@@ -248,4 +254,11 @@ struct smm_variable_var_check_property { u16 name[]; };
+/* supported MM transports */ +enum mm_comms_select {
MM_COMMS_UNDEFINED,
MM_COMMS_FFA,
MM_COMMS_OPTEE
+};
#endif /* _MM_COMMUNICATION_H_ */ diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index c5835e6ef6..08a6b84101 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -55,13 +55,23 @@ 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"
depends on OPTEE
bool "UEFI variables storage service via the trusted world"
depends on OPTEE || ARM_FFA_TRANSPORT 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 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 dfef18435d..a6ec126db5 100644 --- a/lib/efi_loader/efi_variable_tee.c +++ b/lib/efi_loader/efi_variable_tee.c @@ -4,9 +4,14 @@
- Copyright (C) 2019 Linaro Ltd. sughosh.ganu@linaro.org
- Copyright (C) 2019 Linaro Ltd. ilias.apalodimas@linaro.org
- Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
- Authors:
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#include <common.h> +#include <dm.h> #include <efi.h> #include <efi_api.h> #include <efi_loader.h> @@ -15,6 +20,36 @@ #include <malloc.h> #include <mm_communication.h>
+#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
+#include <arm_ffa.h> +#include <cpu_func.h> +#include <mapmem.h>
+#ifndef FFA_SHARED_MM_BUFFER_SIZE +#error "FFA_SHARED_MM_BUFFER_SIZE must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_SIZE 0 +#endif
+#ifndef FFA_SHARED_MM_BUFFER_OFFSET +#error "FFA_SHARED_MM_BUFFER_OFFSET must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_OFFSET 0 +#endif
+#ifndef FFA_SHARED_MM_BUFFER_ADDR +#error "FFA_SHARED_MM_BUFFER_ADDR must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_ADDR 0 +#endif
+/* MM return codes */ +#define MM_SUCCESS (0)
+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 */ static efi_uintn_t max_payload_size; /* func + data */ @@ -24,6 +59,7 @@ struct mm_connection { u32 session; };
+#if (IS_ENABLED(CONFIG_OPTEE)) /**
- get_connection() - Retrieve OP-TEE session for a specific UUID.
@@ -60,6 +96,7 @@ static int get_connection(struct mm_connection *conn) out: return rc; } +#endif
/**
- optee_mm_communicate() - Pass a buffer to StandaloneMM running in OP-TEE
@@ -70,6 +107,7 @@ out: */ static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize) { +#if (IS_ENABLED(CONFIG_OPTEE)) ulong buf_size; efi_status_t ret; struct efi_mm_communicate_header *mm_hdr; @@ -142,19 +180,269 @@ static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize) }
return ret;
+#else
log_err("EFI: Please enable CONFIG_OPTEE for MM comms\n");
return EFI_UNSUPPORTED;
+#endif +}
+#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
drop extra brackets
+/**
- 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 = -1;
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 -ENODEV;
return ret
}
msg.data0 = FFA_SHARED_MM_BUFFER_OFFSET; /* x3 */
ret = ffa_sync_send_receive(dev, mm_sp_id, &msg, 1);
if (ret)
return ret;
sp_event_ret = msg.data0; /* x3 */
if (sp_event_ret == MM_SUCCESS)
return 0;
/* Failure to notify the MM SP */
return -EACCES;
+}
+/**
- 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_info *parts_info;
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 -ENODEV;
return ret
Please check that you don't change error returns unless you really want to, as it makes it hard to debug failures.
}
/* Get from the driver the count of the SPs matching the UUID */
ret = ffa_partition_info_get(dev, mm_sp_svc_uuid, &count, NULL);
if (ret) {
log_err("EFI: Failure in querying SPs count (%d), MM SP discovery failure\n", ret);
return ret;
}
if (!count) {
log_info("EFI: No MM partition found\n");
return ret;
}
/* Pre-allocate a buffer to be filled by the driver with ffa_partition_info structs */
log_info("EFI: Pre-allocating %d partition(s) info structures\n", count);
parts_info = calloc(count, sizeof(*parts_info));
Use a local var and drop the calloc()
if (!parts_info)
return -ENOMEM;
/* Ask the driver to fill the buffer with the SPs info */
ret = ffa_partition_info_get(dev, mm_sp_svc_uuid, &count, parts_info);
if (ret) {
log_err("EFI: Failure in querying SPs info (%d), MM SP discovery failure\n", ret);
free(parts_info);
return ret;
}
/* MM SPs found , use the first one */
mm_sp_id = parts_info[0].id;
log_info("EFI: MM partition ID 0x%x\n", mm_sp_id);
free(parts_info);
return 0;
+} +#endif
+/**
- 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) +{ +#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
Please use if() if you can?
Thanks for pointing out this. I'm trying my best to reduce the #ifdefs as much as possible. For this one, what goes under the #ifdef is specific to FF-A. It doesn't make sense to compile the FF-A logic and functions when we don't turn on the FF-A config. I agreed with Ilias to keep ffa_mm_communicate() as a stub in case FF-A is off, and to show a message at the preprocessor level.
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 > FFA_SHARED_MM_BUFFER_SIZE)
return EFI_INVALID_PARAMETER;
/* Copy the data to the shared buffer */
virt_shared_buf = map_sysmem((phys_addr_t)FFA_SHARED_MM_BUFFER_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;
+#else
log_err("EFI: Please enable CONFIG_ARM_FFA_TRANSPORT for MM comms\n");
return EFI_UNSUPPORTED;
+#endif +}
+/**
- select_mm_comms() - detect the available MM transport
- If FF-A is compiled in, make sure the FF-A bus is probed successfully
- which means FF-A communication with secure world works and ready
- to be used.
- If FF-A is not detected, check if OPTEE support is compiled in.
- Return:
- On success MM_COMMS_FFA or MM_COMMS_OPTEE. Otherwise, MM_COMMS_UNDEFINED
- */
+static enum mm_comms_select select_mm_comms(void) +{
struct udevice *dev;
int ret;
if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) {
ret = uclass_first_device_err(UCLASS_FFA, &dev);
if (ret)
log_err("EFI: Cannot find FF-A bus device, cannot select FF-A comms\n");
else
return MM_COMMS_FFA;
}
if (IS_ENABLED(CONFIG_OPTEE))
return MM_COMMS_OPTEE;
return MM_COMMS_UNDEFINED;
}
/**
- mm_communicate() - Adjust the cmonnucation buffer to StandAlonneMM and send
- 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) { efi_status_t ret;
enum mm_comms_select mm_comms; struct efi_mm_communicate_header *mm_hdr; struct smm_variable_communicate_header *var_hdr;
@@ -162,7 +450,16 @@ static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) 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);
mm_comms = select_mm_comms();
if (mm_comms == MM_COMMS_UNDEFINED) {
ret = EFI_UNSUPPORTED;
} else {
if (mm_comms == MM_COMMS_OPTEE)
ret = optee_mm_communicate(comm_buf, dsize);
else
ret = ffa_mm_communicate(comm_buf, dsize);
}
if (ret != EFI_SUCCESS) { log_err("%s failed!\n", __func__); return ret;
@@ -258,6 +555,13 @@ efi_status_t EFIAPI get_max_payload(efi_uintn_t *size) goto out; } *size = var_payload->size;
#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
Can this use if () ?
FFA_SHARED_MM_BUFFER_SIZE is a platform-specific value related to the MM layer (firmware). Platforms that don't use FF-A won't define FFA_SHARED_MM_BUFFER_SIZE, leading to a compilation error. So, we need to use an #ifdef in this case.
Cheers, Abdellatif
if (*size > FFA_SHARED_MM_BUFFER_SIZE)
*size = FFA_SHARED_MM_BUFFER_SIZE - MM_COMMUNICATE_HEADER_SIZE -
MM_VARIABLE_COMMUNICATE_SIZE;
#endif
/* * There seems to be a bug in EDK2 miscalculating the boundaries and * size checks, so deduct 2 more bytes to fulfill this requirement. Fix
@@ -697,7 +1001,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); /*
-- 2.25.1
Regards, Simon

Hi Abdellatif,
[...]
@@ -162,7 +450,16 @@ static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) 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);
- mm_comms = select_mm_comms();
switch()/case probably looks more readable here
- if (mm_comms == MM_COMMS_UNDEFINED) {
ret = EFI_UNSUPPORTED;
- } else {
if (mm_comms == MM_COMMS_OPTEE)
ret = optee_mm_communicate(comm_buf, dsize);
else
ret = ffa_mm_communicate(comm_buf, dsize);
- }
- if (ret != EFI_SUCCESS) { log_err("%s failed!\n", __func__); return ret;
@@ -258,6 +555,13 @@ efi_status_t EFIAPI get_max_payload(efi_uintn_t *size) goto out; } *size = var_payload->size;
- #if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT))
if (*size > FFA_SHARED_MM_BUFFER_SIZE)
*size = FFA_SHARED_MM_BUFFER_SIZE - MM_COMMUNICATE_HEADER_SIZE -
MM_VARIABLE_COMMUNICATE_SIZE;
- #endif
- /*
- There seems to be a bug in EDK2 miscalculating the boundaries and
- size checks, so deduct 2 more bytes to fulfill this requirement. Fix
@@ -697,7 +1001,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);
/*
-- 2.25.1
Thanks /Ilias

turn on EFI MM communication
On corstone1000 platform MM communication between u-boot and the secure world (Optee) is done using the FF-A bus.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
--- Changelog: ===============
v9: update copyright string
v8:
* drop OP-TEE configs from Corstone-1000 defconfig
v7:
* improve the definition of FFA_SHARED_MM_BUFFER_ADDR and FFA_SHARED_MM_BUFFER_OFFSET * update FFA_SHARED_MM_BUFFER_ADDR value
v6:
* corstone-1000: enable optee driver * corstone-1000: remove CONFIG_ARM_FFA_EFI_RUNTIME_MODE from the defconfig
v4:
* corstone-1000: turn on EFI MM communication
configs/corstone1000_defconfig | 2 ++ include/configs/corstone1000.h | 15 +++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-)
diff --git a/configs/corstone1000_defconfig b/configs/corstone1000_defconfig index 383317fefe..403d7aee2d 100644 --- a/configs/corstone1000_defconfig +++ b/configs/corstone1000_defconfig @@ -52,3 +52,5 @@ CONFIG_DM_SERIAL=y CONFIG_USB=y CONFIG_USB_ISP1760=y CONFIG_ERRNO_STR=y +CONFIG_EFI_MM_COMM_TEE=y +CONFIG_ARM_FFA_TRANSPORT=y diff --git a/include/configs/corstone1000.h b/include/configs/corstone1000.h index 3347c11792..4ef1f05e40 100644 --- a/include/configs/corstone1000.h +++ b/include/configs/corstone1000.h @@ -1,9 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0+ */ /* - * (C) Copyright 2022 ARM Limited * (C) Copyright 2022 Linaro * Rui Miguel Silva rui.silva@linaro.org - * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com * * Configuration for Corstone1000. Parts were derived from other ARM * configurations. @@ -14,6 +16,15 @@
#include <linux/sizes.h>
+#define FFA_SHARED_MM_BUFFER_SIZE SZ_4K /* 4 KB */ + +/* + * shared buffer physical address used for communication between + * u-boot and the MM SP + */ +#define FFA_SHARED_MM_BUFFER_ADDR 0x02000000UL +#define FFA_SHARED_MM_BUFFER_OFFSET 0 + #define V2M_BASE 0x80000000
#define CFG_PL011_CLOCK 50000000

Hi Simon,
Adding support for Arm FF-A v1.0 (Arm Firmware Framework for Armv8-A) [A].
FF-A describes interfaces (ABIs) that standardize communication between the Secure World and Normal World. These interfaces enable a pair of software sandboxes to communicate with each other. A sandbox aka partition could be a VM in the Normal or Secure world, an application in S-EL0, or a Trusted OS in S-EL1.
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.
=> dm tree
Class Index Probed Driver Name
... firmware 0 [ + ] psci |-- psci ffa 0 [ ] arm_ffa | `-- arm_ffa ...
Clients are able to probe then use the FF-A bus by calling the DM class searching APIs (e.g: uclass_get_device_by_name).
This implementation of the specification provides support for Aarch64.
The FF-A driver uses the SMC ABIs defined by the FF-A specification to:
- Discover the presence of secure partitions (SPs) of interest - Access an SP's service through communication protocols e.g. EFI MM communication protocol
The FF-A support provides the following features:
- Being generic by design and can be used by any Arm 64-bit platform - A Uclass driver providing generic FF-A methods and driver operations - An Arm FF-A device driver - FF-A support can be compiled and used without EFI - Support for SMCCCv1.2 x0-x17 registers - Support for SMC32 calling convention - Support for 32-bit and 64-bit FF-A direct messaging - A new command called armffa is provided as an example of how to access the FF-A bus - Support for FF-A MM communication (compatible with EFI boot time) - Enabling FF-A and MM communication in Corstone1000 platform as a use case - A sandbox emulator for Arm FF-A - An FF-A sandbox device driver - Sandbox FF-A test cases
For more details about the FF-A support please refer to [B] and refer to [C] for how to use the armffa command.
Please find at [D] an example of the expected boot logs when enabling FF-A support for a platform. In this example the platform is Corstone1000. But it can be any Arm 64-bit platform.
Changelog of the major changes:
v11:
- move ffa_try_discovery() from the uclass to the Arm FF-A driver
- rename ffa_try_discovery() to arm_ffa_discover()
- add arm_ prefix to the Arm FF-A driver functions
- use U_BOOT_CMD_WITH_SUBCMDS for armffa command
- store the sandbox emulator pointer in the FF-A device uc_priv (struct ffa_priv)
- set the emulator as parent of the sandbox FF-A device
- rename select_ffa_mm_comms() to select_mm_comms()
- improve the logic of MM transport selection in mm_communicate()
- use ut_asserteq_mem() in uuid_str_to_le_bin test case
- address nits
A gentle reminder for reviewing the v11 changes please. I already put you in CC :)
Thank you in advance.
Cheers, Abdellatif
v10: [10]
- provide the FF-A driver operations through the Uclass (arm-ffa-uclass.c)
- move the generic FF-A methods to the Uclass
- keep Arm specific methods in the Arm driver (arm-ffa.c renamed from core.c)
- split the FF-A sandbox support into an emulator (ffa-emul-uclass.c) and a driver (sandbox_ffa.c)
- use the FF-A driver Uclass operations by clients (armffa command, tests, MM comms)
- use uclass_first_device to search and probe the FF-A device (whether it is on Arm or on sandbox)
- address nits
v9: [9]
- integrate the FF-A bus discovery in the DM and use ARM_SMCCC_FEATURES for binding
- align FF-A sandbox driver with FF-A discovery through DM
- use DM class APIs to probe and interact with the FF-A bus (in FF-A MM comms, armffa command, sandbox tests)
- add documentation for the armffa command: doc/usage/cmd/armffa.rst
- introduce testcase for uuid_str_to_le_bin
v8: [8]
- pass the FF-A bus device to the bus operations
- isolate the compilation choices between FF-A and OP-TEE
- drop OP-TEE configs from Corstone-1000 defconfig
- make ffa_get_partitions_info() second argument to be an SP count in both modes
v7: [7]
- add support for 32-bit direct messaging (now we have 32-bit and 64-bit support)
- set the MM door bell event to use 64-bit direct messaging
- issue a compile time error when one of these macros are not found : FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_OFFSET, FFA_SHARED_MM_BUFFER_ADDR
- make uuid_str_to_le_bin() implementation similar to uuid_str_to_bin() by using same APIs
v6: [6]
- remove clearing x0-x17 registers after SMC calls
- drop use of EFI runtime support for FF-A (We decided with Linaro to add this later)
- drop discovery from initcalls (discovery will be on demand by FF-A users)
- add FF-A runtime discovery at MM communication level
- update the documentation and move it to doc/arch/arm64.ffa.rst
v5: [5]
- move changelogs in each commit to the changes section
v4: [4]
- add FF-A support README (doc/README.ffa.drv)
- improving error handling by mapping the FF-A errors to standard errors and logs
- replacing panics with an error log
- align sandbox driver and tests with the new FF-A driver interfaces
and new way of error handling
- use the new FF-A driver interfaces for MM communication
- discover MM partitions at runtime
- copy FF-A driver private data to EFI runtime section at ExitBootServices()
- moving the FF-A driver work to drivers/firmware/arm-ffa
- improving features discovery in FFA_FEATURES
- add remove/unbind functions to the FF-A core device
- improve how the driver behaves when bus discovery is done more than once
- move clearing x0-x17 registers code into a new macro like done in the linux kernel
- enable EFI MM communication for the Corstone1000 platform
v3: [3]
- port x0-x17 registers support from linux kernel as defined by SMCCCv1.2
- align the interfaces of the u-boot FF-A driver with those in the linux FF-A driver
- remove the FF-A helper layer
- make the u-boot FF-A driver independent from EFI
- provide an optional config that enables copying the driver data to EFI runtime section at ExitBootServices service
- use 64-bit version of FFA_RXTX_MAP, FFA_MSG_SEND_DIRECT_{REQ, RESP}
- update armffa command with the new driver interfaces
v2 [2]:
- make FF-A bus discoverable using device_{bind, probe} APIs
- remove device tree support
v1 [1]:
- introduce FF-A bus driver with device tree support
- introduce armffa command
- introduce FF-A Sandbox driver
- add FF-A Sandbox test cases
- introduce FF-A MM communication
Cheers, Abdellatif
List of previous patches:
More details:
[D]: example of boot logs when enabling FF-A
U-Boot 2023.01 (Mar 27 2023 - 13:48:33 +0000) corstone1000 aarch64 DRAM: 2 GiB Arm FF-A framework discovery Using Arm SMC for FF-A conduit FF-A driver 1.0 FF-A framework 1.0 FF-A versions are compatible ... Hit any key to stop autoboot: 0 Loading kernel from 0x083EE000 to memory ... ... FF-A endpoint ID is 0 Using 1 4KB page(s) for FF-A RX/TX buffers size FF-A RX buffer at virtual address 00000000fdf4f000 FF-A TX buffer at virtual address 00000000fdf51000 FF-A RX/TX buffers mapped Reading FF-A partitions data from the RX buffer FF-A partition ID 8001 : info cached FF-A partition ID 8002 : info cached FF-A partition ID 8003 : info cached 3 FF-A partition(s) found and cached Preparing for checking FF-A partitions count Searching FF-A partitions using the provided UUID No FF-A partition found. Querying framework ... Reading FF-A partitions data from the RX buffer Number of FF-A partition(s) matching the UUID: 1 EFI: Pre-allocating 1 partition(s) info structures Preparing for filling FF-A partitions info Searching FF-A partitions using the provided UUID FF-A partition ID 8003 matches the provided UUID EFI: MM partition ID 0x8003 EFI: Corstone1000: Capsule shared buffer at 0x80000000 , size 8192 pages Booting /MemoryMapped(0x0,0x88200000,0xf00000) EFI stub: Booting Linux Kernel... EFI stub: Using DTB from configuration table EFI stub: Exiting boot services... unmapping FF-A RX/TX buffers Freeing FF-A RX/TX buffers Booting Linux on physical CPU 0x0000000000 [0x411fd040] 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
Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Rob Herring robh@kernel.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Achin Gupta achin.gupta@arm.com Cc: Drew Reed Drew.Reed@arm.com Cc: Xueliang Zhong Xueliang.Zhong@arm.com
Abdellatif El Khlifi (10): arm64: smccc: add support for SMCCCv1.2 x0-x17 registers lib: uuid: introduce uuid_str_to_le_bin function lib: uuid: introduce testcase for uuid_str_to_le_bin arm_ffa: introduce Arm FF-A support arm_ffa: introduce armffa command arm_ffa: introduce sandbox FF-A support arm_ffa: introduce sandbox test cases for UCLASS_FFA arm_ffa: introduce armffa command Sandbox test arm_ffa: efi: introduce FF-A MM communication arm_ffa: efi: corstone1000: enable MM communication
MAINTAINERS | 18 + arch/arm/cpu/armv8/smccc-call.S | 57 +- arch/arm/lib/asm-offsets.c | 16 + arch/sandbox/dts/sandbox.dtsi | 8 + arch/sandbox/dts/test.dts | 8 + arch/sandbox/include/asm/sandbox_arm_ffa.h | 72 ++ .../include/asm/sandbox_arm_ffa_priv.h | 133 ++ cmd/Kconfig | 10 + cmd/Makefile | 2 + cmd/armffa.c | 212 +++ configs/corstone1000_defconfig | 2 + configs/sandbox64_defconfig | 1 + configs/sandbox_defconfig | 1 + doc/arch/arm64.ffa.rst | 294 +++++ doc/arch/index.rst | 1 + doc/arch/sandbox/sandbox.rst | 1 + doc/usage/cmd/armffa.rst | 105 ++ doc/usage/index.rst | 1 + drivers/Makefile | 1 + drivers/firmware/Kconfig | 1 + drivers/firmware/arm-ffa/Kconfig | 42 + drivers/firmware/arm-ffa/Makefile | 16 + drivers/firmware/arm-ffa/arm-ffa-uclass.c | 1143 +++++++++++++++++ drivers/firmware/arm-ffa/arm-ffa.c | 127 ++ drivers/firmware/arm-ffa/ffa-emul-uclass.c | 738 +++++++++++ drivers/firmware/arm-ffa/sandbox_ffa.c | 119 ++ include/arm_ffa.h | 217 ++++ include/arm_ffa_priv.h | 271 ++++ include/configs/corstone1000.h | 15 +- include/dm/uclass-id.h | 7 + include/linux/arm-smccc.h | 45 + include/mm_communication.h | 13 + include/uuid.h | 15 + lib/efi_loader/Kconfig | 14 +- lib/efi_loader/efi_variable_tee.c | 312 ++++- lib/uuid.c | 48 + test/cmd/Makefile | 2 + test/cmd/armffa.c | 39 + test/dm/Makefile | 2 + test/dm/ffa.c | 258 ++++ test/lib/Makefile | 1 + test/lib/uuid.c | 41 + 42 files changed, 4420 insertions(+), 9 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 cmd/armffa.c create mode 100644 doc/arch/arm64.ffa.rst create mode 100644 doc/usage/cmd/armffa.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/ffa-emul-uclass.c create mode 100644 drivers/firmware/arm-ffa/sandbox_ffa.c create mode 100644 include/arm_ffa.h create mode 100644 include/arm_ffa_priv.h create mode 100644 test/cmd/armffa.c create mode 100644 test/dm/ffa.c create mode 100644 test/lib/uuid.c
-- 2.25.1

Hi Abdellatif,
On Mon, 17 Apr 2023 at 10:03, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
Hi Simon,
Adding support for Arm FF-A v1.0 (Arm Firmware Framework for Armv8-A) [A].
FF-A describes interfaces (ABIs) that standardize communication between the Secure World and Normal World. These interfaces enable a pair of software sandboxes to communicate with each other. A sandbox aka partition could be a VM in the Normal or Secure world, an application in S-EL0, or a Trusted OS in S-EL1.
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.
=> dm tree
Class Index Probed Driver Name
... firmware 0 [ + ] psci |-- psci ffa 0 [ ] arm_ffa | `-- arm_ffa ...
Clients are able to probe then use the FF-A bus by calling the DM class searching APIs (e.g: uclass_get_device_by_name).
This implementation of the specification provides support for Aarch64.
The FF-A driver uses the SMC ABIs defined by the FF-A specification to:
- Discover the presence of secure partitions (SPs) of interest - Access an SP's service through communication protocols e.g. EFI MM communication protocol
The FF-A support provides the following features:
- Being generic by design and can be used by any Arm 64-bit platform - A Uclass driver providing generic FF-A methods and driver operations - An Arm FF-A device driver - FF-A support can be compiled and used without EFI - Support for SMCCCv1.2 x0-x17 registers - Support for SMC32 calling convention - Support for 32-bit and 64-bit FF-A direct messaging - A new command called armffa is provided as an example of how to access the FF-A bus - Support for FF-A MM communication (compatible with EFI boot time) - Enabling FF-A and MM communication in Corstone1000 platform as a use case - A sandbox emulator for Arm FF-A - An FF-A sandbox device driver - Sandbox FF-A test cases
For more details about the FF-A support please refer to [B] and refer to [C] for how to use the armffa command.
Please find at [D] an example of the expected boot logs when enabling FF-A support for a platform. In this example the platform is Corstone1000. But it can be any Arm 64-bit platform.
Changelog of the major changes:
v11:
- move ffa_try_discovery() from the uclass to the Arm FF-A driver
- rename ffa_try_discovery() to arm_ffa_discover()
- add arm_ prefix to the Arm FF-A driver functions
- use U_BOOT_CMD_WITH_SUBCMDS for armffa command
- store the sandbox emulator pointer in the FF-A device uc_priv (struct ffa_priv)
- set the emulator as parent of the sandbox FF-A device
- rename select_ffa_mm_comms() to select_mm_comms()
- improve the logic of MM transport selection in mm_communicate()
- use ut_asserteq_mem() in uuid_str_to_le_bin test case
- address nits
A gentle reminder for reviewing the v11 changes please. I already put you in CC :)
Thank you in advance.
Yes I have started looking at it. Should be by Monday.
Regards, Simon

Adding support for Arm FF-A v1.0 (Arm Firmware Framework for Armv8-A) [A].
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.
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.
=> dm tree
Class Index Probed Driver Name ----------------------------------------------------------- ... firmware 0 [ + ] psci |-- psci ffa 0 [ ] arm_ffa | `-- arm_ffa ...
Clients are able to probe then use the FF-A bus by calling the DM class searching APIs (e.g: uclass_first_device).
This implementation of the specification provides support for Aarch64.
The FF-A driver uses the SMC ABIs defined by the FF-A specification to:
- Discover the presence of secure partitions (SPs) of interest - Access an SP's service through communication protocols (e.g: EFI MM communication protocol)
The FF-A support provides the following features:
- Being generic by design and can be used by any Arm 64-bit platform - FF-A support can be compiled and used without EFI - Support for SMCCCv1.2 x0-x17 registers - Support for SMC32 calling convention - Support for 32-bit and 64-bit FF-A direct messaging - Support for FF-A MM communication (compatible with EFI boot time) - Enabling FF-A and MM communication in Corstone1000 platform as a use case - 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. - Sandbox FF-A test cases. - A new command called armffa is provided as an example of how to access the FF-A bus
For more details about the FF-A support please refer to [B] and refer to [C] for how to use the armffa command.
Please find at [D] an example of the expected boot logs when enabling FF-A support for a platform. In this example the platform is Corstone1000. But it can be any Arm 64-bit platform.
Changelog of the major changes: ===========================
v12:
* remove the global variable (dscvry_info), use uc_priv instead * replace dscvry_info.invoke_ffa_fn() with a weak invoke_ffa_fn (user drivers can override it) * improve FFA_PARTITION_INFO_GET implementation (clients no longer need to calloc a buffer) * remove reparenting by making the sandbox emulator parent of the FF-A device in the DT * improve argument checks for the armffa command * address nits
v11: [11]
* move ffa_try_discovery() from the uclass to the Arm FF-A driver * rename ffa_try_discovery() to arm_ffa_discover() * add arm_ prefix to the Arm FF-A driver functions * use U_BOOT_CMD_WITH_SUBCMDS for armffa command * store the sandbox emulator pointer in the FF-A device uc_priv (struct ffa_priv) * set the emulator as parent of the sandbox FF-A device * rename select_ffa_mm_comms() to select_mm_comms() * improve the logic of MM transport selection in mm_communicate() * use ut_asserteq_mem() in uuid_str_to_le_bin test case * address nits
v10: [10]
* provide the FF-A driver operations through the Uclass (arm-ffa-uclass.c) * move the generic FF-A methods to the Uclass * keep Arm specific methods in the Arm driver (arm-ffa.c renamed from core.c) * split the FF-A sandbox support into an emulator (ffa-emul-uclass.c) and a driver (sandbox_ffa.c) * use the FF-A driver Uclass operations by clients (armffa command, tests, MM comms) * use uclass_first_device to search and probe the FF-A device (whether it is on Arm or on sandbox) * address nits
v9: [9]
* integrate the FF-A bus discovery in the DM and use ARM_SMCCC_FEATURES for binding * align FF-A sandbox driver with FF-A discovery through DM * use DM class APIs to probe and interact with the FF-A bus (in FF-A MM comms, armffa command, sandbox tests) * add documentation for the armffa command: doc/usage/cmd/armffa.rst * introduce testcase for uuid_str_to_le_bin
v8: [8]
* pass the FF-A bus device to the bus operations * isolate the compilation choices between FF-A and OP-TEE * drop OP-TEE configs from Corstone-1000 defconfig * make ffa_get_partitions_info() second argument to be an SP count in both modes
v7: [7]
* add support for 32-bit direct messaging (now we have 32-bit and 64-bit support) * set the MM door bell event to use 64-bit direct messaging * issue a compile time error when one of these macros are not found : FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_OFFSET, FFA_SHARED_MM_BUFFER_ADDR * make uuid_str_to_le_bin() implementation similar to uuid_str_to_bin() by using same APIs
v6: [6]
* remove clearing x0-x17 registers after SMC calls * drop use of EFI runtime support for FF-A (We decided with Linaro to add this later) * drop discovery from initcalls (discovery will be on demand by FF-A users) * add FF-A runtime discovery at MM communication level * update the documentation and move it to doc/arch/arm64.ffa.rst
v5: [5]
* move changelogs in each commit to the changes section
v4: [4]
* add FF-A support README (doc/README.ffa.drv) * improving error handling by mapping the FF-A errors to standard errors and logs * replacing panics with an error log * align sandbox driver and tests with the new FF-A driver interfaces and new way of error handling * use the new FF-A driver interfaces for MM communication * discover MM partitions at runtime * copy FF-A driver private data to EFI runtime section at ExitBootServices() * moving the FF-A driver work to drivers/firmware/arm-ffa * improving features discovery in FFA_FEATURES * add remove/unbind functions to the FF-A core device * improve how the driver behaves when bus discovery is done more than once * move clearing x0-x17 registers code into a new macro like done in the linux kernel * enable EFI MM communication for the Corstone1000 platform
v3: [3]
* port x0-x17 registers support from linux kernel as defined by SMCCCv1.2 * align the interfaces of the u-boot FF-A driver with those in the linux FF-A driver * remove the FF-A helper layer * make the u-boot FF-A driver independent from EFI * provide an optional config that enables copying the driver data to EFI runtime section at ExitBootServices service * use 64-bit version of FFA_RXTX_MAP, FFA_MSG_SEND_DIRECT_{REQ, RESP} * update armffa command with the new driver interfaces
v2 [2]:
* make FF-A bus discoverable using device_{bind, probe} APIs * remove device tree support
v1 [1]:
* introduce FF-A bus driver with device tree support * introduce armffa command * introduce FF-A Sandbox driver * add FF-A Sandbox test cases * introduce FF-A MM communication
Cheers, Abdellatif
List of previous patches:
[1]: https://lore.kernel.org/all/20220329151659.16894-1-abdellatif.elkhlifi@arm.c... [2]: https://lore.kernel.org/all/20220415122803.16666-1-abdellatif.elkhlifi@arm.c... [3]: https://lore.kernel.org/all/20220801172053.20163-1-abdellatif.elkhlifi@arm.c... [4]: https://lore.kernel.org/all/20220926101723.9965-1-abdellatif.elkhlifi@arm.co... [5]: https://lore.kernel.org/all/20220926140827.15125-1-abdellatif.elkhlifi@arm.c... [6]: https://lore.kernel.org/all/20221013103857.614-1-abdellatif.elkhlifi@arm.com... [7]: https://lore.kernel.org/all/20221107192055.21669-1-abdellatif.elkhlifi@arm.c... [8]: https://lore.kernel.org/all/20221122131751.22747-1-abdellatif.elkhlifi@arm.c... [9]: https://lore.kernel.org/all/20230310141016.137986-1-abdellatif.elkhlifi@arm.... [10]: https://lore.kernel.org/all/20230328161157.219375-1-abdellatif.elkhlifi@arm.... [11]: https://lore.kernel.org/all/20230412094245.44674-1-abdellatif.elkhlifi@arm.c...
More details:
[A]: https://developer.arm.com/documentation/den0077/latest/ [B]: doc/arch/arm64.ffa.rst [C]: doc/usage/cmd/armffa.rst [D]: example of boot logs when enabling FF-A
``` 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 ```
Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Rob Herring robh@kernel.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Achin Gupta achin.gupta@arm.com Cc: Drew Reed Drew.Reed@arm.com Cc: Xueliang Zhong Xueliang.Zhong@arm.com
Abdellatif El Khlifi (10): arm64: smccc: add support for SMCCCv1.2 x0-x17 registers lib: uuid: introduce uuid_str_to_le_bin function lib: uuid: introduce testcase for uuid_str_to_le_bin arm_ffa: introduce Arm FF-A support arm_ffa: introduce armffa command arm_ffa: introduce sandbox FF-A support arm_ffa: introduce sandbox test cases for UCLASS_FFA arm_ffa: introduce armffa command Sandbox test arm_ffa: efi: introduce FF-A MM communication arm_ffa: efi: corstone1000: enable MM communication
MAINTAINERS | 18 + arch/arm/cpu/armv8/smccc-call.S | 57 +- arch/arm/lib/asm-offsets.c | 16 + 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 ++ cmd/Kconfig | 10 + cmd/Makefile | 1 + cmd/armffa.c | 196 +++ configs/corstone1000_defconfig | 2 + configs/sandbox64_defconfig | 1 + configs/sandbox_defconfig | 1 + doc/arch/arm64.ffa.rst | 270 +++++ doc/arch/index.rst | 1 + doc/arch/sandbox/sandbox.rst | 1 + doc/usage/cmd/armffa.rst | 93 ++ doc/usage/index.rst | 1 + drivers/Makefile | 1 + drivers/firmware/Kconfig | 1 + drivers/firmware/arm-ffa/Kconfig | 42 + drivers/firmware/arm-ffa/Makefile | 16 + drivers/firmware/arm-ffa/arm-ffa-uclass.c | 1065 +++++++++++++++++ drivers/firmware/arm-ffa/arm-ffa.c | 104 ++ drivers/firmware/arm-ffa/ffa-emul-uclass.c | 720 +++++++++++ drivers/firmware/arm-ffa/sandbox_ffa.c | 110 ++ include/arm_ffa.h | 213 ++++ include/arm_ffa_priv.h | 246 ++++ include/configs/corstone1000.h | 15 +- include/dm/uclass-id.h | 7 + include/linux/arm-smccc.h | 45 + include/mm_communication.h | 13 + include/uuid.h | 15 + lib/efi_loader/Kconfig | 14 +- lib/efi_loader/efi_variable_tee.c | 291 ++++- lib/uuid.c | 48 + test/cmd/Makefile | 2 + test/cmd/armffa.c | 33 + test/dm/Makefile | 3 +- test/dm/ffa.c | 261 ++++ test/lib/Makefile | 1 + test/lib/uuid.c | 41 + 42 files changed, 4175 insertions(+), 10 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 cmd/armffa.c create mode 100644 doc/arch/arm64.ffa.rst create mode 100644 doc/usage/cmd/armffa.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/ffa-emul-uclass.c create mode 100644 drivers/firmware/arm-ffa/sandbox_ffa.c create mode 100644 include/arm_ffa.h create mode 100644 include/arm_ffa_priv.h create mode 100644 test/cmd/armffa.c create mode 100644 test/dm/ffa.c create mode 100644 test/lib/uuid.c

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 abdellatif.elkhlifi@arm.com Reviewed-by: Jens Wiklander jens.wiklander@linaro.org Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org
---
Changelog: ===============
v9:
* update the copyright string
v7:
* improve indentation of ARM_SMCCC_1_2_REGS_Xn_OFFS
v4:
* rename the commit title and improve description new commit title: the current
v3:
* port x0-x17 registers support from linux kernel as defined by SMCCCv1.2 commit title: arm64: smccc: add Xn registers support used by SMC calls
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 dc92b28777..93f66d3366 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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com +*/ #include <linux/linkage.h> #include <linux/arm-smccc.h> #include <generated/asm-offsets.h> @@ -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 6de0ce9152..181a8ac4c2 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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> @@ -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 e1d09884a1..f44e9e8f93 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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */ #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

On Fri, May 12, 2023 at 01:10:35PM +0100, Abdellatif El Khlifi wrote:
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
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Reviewed-by: Jens Wiklander jens.wiklander@linaro.org Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org
Changelog:
v9:
- update the copyright string
v7:
- improve indentation of ARM_SMCCC_1_2_REGS_Xn_OFFS
v4:
- rename the commit title and improve description new commit title: the current
v3:
- port x0-x17 registers support from linux kernel as defined by SMCCCv1.2 commit title: arm64: smccc: add Xn registers support used by SMC calls
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 dc92b28777..93f66d3366 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 open-source-office@arm.com
- Authors:
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
+*/ #include <linux/linkage.h> #include <linux/arm-smccc.h> #include <generated/asm-offsets.h> @@ -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 6de0ce9152..181a8ac4c2 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 open-source-office@arm.com
- Authors:
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#include <common.h> @@ -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 e1d09884a1..f44e9e8f93 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 open-source-office@arm.com
- Authors:
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#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
-- 2.25.1
Reviewed-by: Ilias Apalodimas ilias.apalodimas@linaro.org

convert UUID string to little endian binary data
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
--- Changelog: ===============
v9:
* add a full function prototype description in uuid.h
v8:
* use simple_strtoull() in uuid_str_to_le_bin() to support 32-bit platforms
v7:
* rename be_uuid_str_to_le_bin() to uuid_str_to_le_bin() * make uuid_str_to_le_bin() implementation similar to uuid_str_to_bin() by using same APIs
v4:
* rename ffa_uuid_str_to_bin to be_uuid_str_to_le_bin and put in a standalone commit (the current)
v3:
* introduce ffa_uuid_str_to_bin (provided by arm_ffa: introduce Arm FF-A low-level driver)
include/uuid.h | 15 +++++++++++++++ lib/uuid.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+)
diff --git a/include/uuid.h b/include/uuid.h index 4a4883d3b5..89b93e642b 100644 --- a/include/uuid.h +++ b/include/uuid.h @@ -2,6 +2,10 @@ /* * Copyright (C) 2014 Samsung Electronics * Przemyslaw Marczak p.marczak@samsung.com + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */ #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 96e1af3c8b..45f325d964 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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> @@ -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. *

provide a test case
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Simon Glass sjg@chromium.org
--- Changelog: ===============
v11:
* use ut_asserteq_mem()
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 c8f72e9ec6..926c7201a5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1634,3 +1634,8 @@ S: Maintained F: arch/arm/dts/ls1021a-twr-u-boot.dtsi F: drivers/crypto/fsl/ F: include/fsl_sec.h + +UUID testing +M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com +S: Maintained +F: test/lib/uuid.c diff --git a/test/lib/Makefile b/test/lib/Makefile index e0bd9e04e8..e75a263e6a 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 0000000000..e24331a136 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <uuid.h> +#include <test/lib.h> +#include <test/test.h> +#include <test/ut.h> + +/* 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);

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 abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
--- Changelog: ===============
v12:
* remove dscvry_info * replace dscvry_info.invoke_ffa_fn() with a weak invoke_ffa_fn (user drivers can override it) * improve FFA_PARTITION_INFO_GET implementation (clients no longer need to calloc a buffer) * address nits
v11:
* move ffa_try_discovery() from the uclass to the Arm FF-A driver * rename ffa_try_discovery() to arm_ffa_discover() * pass dev as an argument of arm_ffa_discover() * add arm_ prefix to the Arm FF-A driver functions * add emul field in struct ffa_discovery_info * address nits
v10:
* provide the driver operations through the Uclass * move the generic FF-A methods to the Uclass * keep Arm specific methods in the Arm driver (arm-ffa.c) * rename core.c to arm-ffa.c * address nits
v9:
* integrate the FF-A bus discovery in the DM and use ARM_SMCCC_FEATURES for binding
v8:
* make ffa_get_partitions_info() second argument to be an SP count in both modes * update ffa_bus_prvdata_get() to return a pointer rather than a pointer address * remove packing from ffa_partition_info and ffa_send_direct_data structures * pass the FF-A bus device to the bus operations
v7:
* add support for 32-bit direct messaging * rename be_uuid_str_to_le_bin() to uuid_str_to_le_bin() * improve the declaration of error handling mapping * stating in doc/arch/arm64.ffa.rst that EFI runtime is not supported
v6:
* drop use of EFI runtime support (We decided with Linaro to add this later) * drop discovery from initcalls (discovery will be on demand by FF-A users) * set the alignment of the RX/TX buffers to the larger translation granule size * move FF-A RX/TX buffers unmapping at ExitBootServices() to a separate commit * update the documentation and move it to doc/arch/arm64.ffa.rst
v4:
* add doc/README.ffa.drv * moving the FF-A driver work to drivers/firmware/arm-ffa * use less #ifdefs in lib/efi_loader/efi_boottime.c and replace #if defined by #if CONFIG_IS_ENABLED * improving error handling by mapping the FF-A errors to standard errors and logs * replacing panics with an error log and returning an error code * improving features discovery in FFA_FEATURES by introducing rxtx_min_pages private data field * add ffa_remove and ffa_unbind functions * improve how the driver behaves when bus discovery is done more than once
v3:
* align the interfaces of the U-Boot FF-A driver with those in the linux FF-A driver * remove the FF-A helper layer * make the U-Boot FF-A driver independent from EFI * provide an optional config that enables copying the driver data to EFI runtime section at ExitBootServices service * use 64-bit version of FFA_RXTX_MAP, FFA_MSG_SEND_DIRECT_{REQ, RESP}
v2:
* make FF-A bus discoverable using device_{bind, probe} APIs * remove device tree support
v1:
* introduce FF-A bus driver with device tree support
MAINTAINERS | 8 + doc/arch/arm64.ffa.rst | 247 ++++ 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, 1950 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 926c7201a5..4e285f88cb 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 abdellatif.elkhlifi@arm.com +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 sbabic@denx.de M: Fabio Estevam festevam@gmail.com diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst new file mode 100644 index 0000000000..7719215cdf --- /dev/null +++ b/doc/arch/arm64.ffa.rst @@ -0,0 +1,247 @@ +.. 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(). +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: + + - 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. + +FF-A device destruction +------------------------- + +When the FF-A device is removed by the DM, RX/TX buffers are automatically +unmapped and freed. + +For example, at EFI efi_exit_boot_services() active devices are automatically removed +by dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL). + +By consequence, the FF-A RX/TX are unmapped automatically. + +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() and this is done +automatically 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 abdellatif.elkhlifi@arm.com + +.. _`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 b8da4b8c8e..2f916f4026 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 29be78a3f2..6094fac50d 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -114,6 +114,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 eae1c8ddc9..8789b1ea14 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 0000000000..9200c8028b --- /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 0000000000..11b1766285 --- /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 open-source-office@arm.com +# +# Authors: +# Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + +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 0000000000..ffa9d81fa7 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ +#include <common.h> +#include <arm_ffa.h> +#include <arm_ffa_priv.h> +#include <dm.h> +#include <log.h> +#include <malloc.h> +#include <string.h> +#include <uuid.h> +#include <asm/global_data.h> +#include <dm/device-internal.h> +#include <dm/devres.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> + +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 0000000000..68df75bd9e --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <arm_ffa.h> +#include <arm_ffa_priv.h> +#include <dm.h> +#include <log.h> +#include <asm/global_data.h> +#include <dm/device-internal.h> +#include <linux/errno.h> + +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 0000000000..4338f9c9b1 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#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 0000000000..db9b1be995 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __ARM_FFA_H +#define __ARM_FFA_H + +#include <linux/printk.h> + +/* + * 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 0000000000..d564c33c64 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __ARM_FFA_PRV_H +#define __ARM_FFA_PRV_H + +#include <mapmem.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> + +/* 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 <linux/arm-smccc.h> +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 307ad6931c..3c6af2e3d2 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -4,6 +4,11 @@ * * (C) Copyright 2012 * Pavel Herrmann morpheus.ibis@gmail.com + * + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#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 */

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].
[1]: doc/usage/cmd/armffa.rst
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
--- Changelog: ===============
v12:
* add subcommands argument checks * usage documentation: update command return codes * remove calloc when querying SPs * address nits
v11:
* use U_BOOT_CMD_WITH_SUBCMDS * address nits
v10:
* use the FF-A driver Uclass operations * use uclass_first_device() * address nits
v9:
* remove manual FF-A discovery and use DM * use DM class APIs to probe and interact with the FF-A bus * add doc/usage/cmd/armffa.rst
v8:
* update partition_info_get() second argument to be an SP count * pass NULL device pointer to the FF-A bus discovery and operations
v7:
* adapt do_ffa_dev_list() following the recent update on uclass_first_device/uclass_next_device functions (they return void now) * set armffa command to use 64-bit direct messaging
v4:
* remove pattern data in do_ffa_msg_send_direct_req
v3:
* use the new driver interfaces (partition_info_get, sync_send_receive) in armffa command
v2:
* replace use of ffa_helper_init_device function by ffa_helper_bus_discover
v1:
* introduce armffa command
MAINTAINERS | 2 + cmd/Kconfig | 10 ++ cmd/Makefile | 1 + cmd/armffa.c | 196 +++++++++++++++++++++++++++++++ doc/arch/arm64.ffa.rst | 7 ++ doc/usage/cmd/armffa.rst | 93 +++++++++++++++ doc/usage/index.rst | 1 + drivers/firmware/arm-ffa/Kconfig | 1 + 8 files changed, 311 insertions(+) create mode 100644 cmd/armffa.c create mode 100644 doc/usage/cmd/armffa.rst
diff --git a/MAINTAINERS b/MAINTAINERS index 4e285f88cb..40c356e539 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -269,7 +269,9 @@ F: configs/cortina_presidio-asic-pnand_defconfig ARM FF-A M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com S: Maintained +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: include/sandbox_arm_ffa.h diff --git a/cmd/Kconfig b/cmd/Kconfig index 65957da7f5..82f03d1f00 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -934,6 +934,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 6c37521b4e..7d20a85a46 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 0000000000..fa268e9cb9 --- /dev/null +++ b/cmd/armffa.c @@ -0,0 +1,196 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ +#include <common.h> +#include <arm_ffa.h> +#include <command.h> +#include <dm.h> +#include <mapmem.h> +#include <stdlib.h> +#include <asm/io.h> + +/** + * 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 + */ +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 the secure partition information which the 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 the secure partition which the ID 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 + */ +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; + } + + errno = 0; + part_id = strtoul(argv[1], NULL, 16); + + if (errno) { + log_err("Invalid partition ID\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 + */ +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 name %s, dev %p, driver name %s, ops %p\n", + dev->name, + (void *)map_to_sysmem(dev), + dev->driver->name, + (void *)map_to_sysmem(dev->driver->ops)); + + return CMD_RET_SUCCESS; +} + +static char armffa_help_text[] = + "getpart <partition UUID>\n" + " - lists the partition(s) info\n" + "ping <partition ID>\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 7719215cdf..c7fca0f103 100644 --- a/doc/arch/arm64.ffa.rst +++ b/doc/arch/arm64.ffa.rst @@ -214,6 +214,13 @@ The following features are provided:
- FF-A bus can be compiled and used without EFI
+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 0000000000..3d422686c1 --- /dev/null +++ b/doc/usage/cmd/armffa.rst @@ -0,0 +1,93 @@ +.. SPDX-License-Identifier: GPL-2.0+: + +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 0fde130a54..3115ca4aff 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 9200c8028b..a7d5392859 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 select ARM_SMCCC select ARM_SMCCC_FEATURES + imply CMD_ARMFFA select LIB_UUID select DEVRES help

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 abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
--- Changelog: ===============
v12:
* remove reparenting by making the emulator parent of the FF-A device in the DT * add invoke_ffa_fn() * address nits
v11:
* rename ffa_try_discovery() to sandbox_ffa_discover() * rename sandbox_ffa_query_core_state() to sandbox_query_ffa_emul_state() * store the sandbox emulator pointer in the FF-A device uc_priv (struct ffa_priv) * set the emulator as parent of the sandbox FF-A device
v10:
* split the FF-A sandbox support into an emulator and a driver * read FFA_VERSION and FFA_PARTITION_INFO_GET state using sandbox_ffa_query_core_state() * drop CONFIG_SANDBOX_FFA config * address nits
v9: align FF-A sandbox driver with FF-A discovery through DM
v8: update ffa_bus_prvdata_get() to return a pointer rather than a pointer address
v7: state that sandbox driver supports only 64-bit direct messaging
v4: align sandbox driver with the new FF-A driver interfaces and new way of error handling
v1: introduce the sandbox driver
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 40c356e539..598644bb00 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -269,12 +269,13 @@ F: configs/cortina_presidio-asic-pnand_defconfig ARM FF-A M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com 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: include/sandbox_arm_ffa.h
ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de diff --git a/arch/sandbox/dts/sandbox.dtsi b/arch/sandbox/dts/sandbox.dtsi index 30a305c4d2..94a08814b8 100644 --- a/arch/sandbox/dts/sandbox.dtsi +++ b/arch/sandbox/dts/sandbox.dtsi @@ -445,6 +445,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 453e53db71..7188ac1f46 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -1820,6 +1820,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 0000000000..be2790f496 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __SANDBOX_ARM_FFA_H +#define __SANDBOX_ARM_FFA_H + +#include <arm_ffa.h> + +/* + * 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 0000000000..b0881822d7 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __SANDBOX_ARM_FFA_PRV_H +#define __SANDBOX_ARM_FFA_PRV_H + +#include <arm_ffa_priv.h> + +/* 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 f3002de857..5ed2540a0e 100644 --- a/configs/sandbox64_defconfig +++ b/configs/sandbox64_defconfig @@ -261,3 +261,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 1ec44d5b33..8269bec879 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -344,3 +344,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 c7fca0f103..54c5b11f3b 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. @@ -214,6 +217,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 + The armffa command -----------------------------------
diff --git a/doc/arch/sandbox/sandbox.rst b/doc/arch/sandbox/sandbox.rst index 77ca6bc4cc..a3631de749 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 a7d5392859..d75f8b53fd 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 imply CMD_ARMFFA select LIB_UUID select DEVRES @@ -33,5 +33,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 11b1766285..318123a7f4 100644 --- a/drivers/firmware/arm-ffa/Makefile +++ b/drivers/firmware/arm-ffa/Makefile @@ -5,4 +5,12 @@ # Authors: # Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
-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 0000000000..5562bbaac3 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ +#include <common.h> +#include <dm.h> +#include <mapmem.h> +#include <string.h> +#include <asm/global_data.h> +#include <asm/sandbox_arm_ffa.h> +#include <asm/sandbox_arm_ffa_priv.h> +#include <dm/device-internal.h> +#include <dm/lists.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> + +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 4338f9c9b1..0000000000 --- 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 open-source-office@arm.com - * - * Authors: - * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com - */ - -#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 0000000000..ef9491ccea --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ +#include <common.h> +#include <arm_ffa.h> +#include <dm.h> +#include <log.h> +#include <asm/global_data.h> +#include <asm/sandbox_arm_ffa_priv.h> +#include <dm/device-internal.h> +#include <linux/errno.h> + +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 3c6af2e3d2..0432c95c9e 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 */

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 abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
--- Changelog: ===============
v12:
* remove use of dscvry_info * drop use of calloc when querying SPs * address nits
v11:
* drop unmapping test (taken care of by the DM when removing the device) * address nits
v10:
* use the FF-A driver Uclass operations * use uclass_first_device() * replace CONFIG_SANDBOX_FFA with CONFIG_ARM_FFA_TRANSPORT * address nits
v9: align FF-A sandbox tests with FF-A discovery through DM
v8:
* update partition_info_get() second argument to be an SP count * pass NULL device pointer to the FF-A bus discovery and operations
v7: set the tests to use 64-bit direct messaging
v4: align sandbox tests with the new FF-A driver interfaces and new way of error handling
v1: introduce sandbox tests
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 598644bb00..1c81728b15 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -276,6 +276,7 @@ F: doc/arch/arm64.ffa.rst F: doc/usage/cmd/armffa.rst F: drivers/firmware/arm-ffa/ F: include/arm_ffa.h +F: test/dm/ffa.c
ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst index 54c5b11f3b..577603870c 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 3799b1ae8f..7ed00733c1 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 open-source-office@arm.com +# Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
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 0000000000..6912666bb4 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <console.h> +#include <dm.h> +#include <asm/sandbox_arm_ffa.h> +#include <asm/sandbox_arm_ffa_priv.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +/* 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);

Add Sandbox test for the armffa command
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
--- Changelog: ===============
v12:
* address nits
v10:
* replace CMD_RET_SUCCESS with 0 * replace CONFIG_SANDBOX_FFA with CONFIG_ARM_FFA_TRANSPORT
v9: align the test with FF-A discovery through DM
v4: drop use of helper APIs
v1: introduce armffa command sandbox test
MAINTAINERS | 1 + test/cmd/Makefile | 2 ++ test/cmd/armffa.c | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+) create mode 100644 test/cmd/armffa.c
diff --git a/MAINTAINERS b/MAINTAINERS index 1c81728b15..96141c9f59 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -276,6 +276,7 @@ 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/test/cmd/Makefile b/test/cmd/Makefile index 055adc65a2..1d1dbb4fbc 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 open-source-office@arm.com
ifdef CONFIG_HUSH_PARSER obj-$(CONFIG_CONSOLE_RECORD) += test_echo.o @@ -23,6 +24,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 0000000000..9a44a397e8 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <string.h> +#include <asm/sandbox_arm_ffa.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +/* Basic test of 'armffa' command */ +static int dm_test_armffa_cmd(struct unit_test_state *uts) +{ + /* armffa getpart <UUID> */ + ut_assertok(run_command("armffa getpart " SANDBOX_SERVICE1_UUID, 0)); + + /* armffa ping <ID> */ + 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);

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.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Signed-off-by: Gowtham Suresh Kumar gowtham.sureshkumar@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
--- Changelog: ===============
v12:
* drop use of calloc when querying SPs * address nits
v11:
* rename select_ffa_mm_comms() to select_mm_comms() * improve the logic of MM transport selection in mm_communicate() * addressing nits
v10:
* use the FF-A driver Uclass operations * use uclass_first_device() * addressing nits
v9: align how FF-A is used with FF-A discovery through DM
v8:
* isolate the compilation choices between FF-A and OP-TEE * update partition_info_get() second argument to be an SP count * pass NULL device pointer to the FF-A bus discovery and operations
v7:
* set the MM door bell event to use 64-bit direct messaging * issue a compile time error when one of these macros are not found : FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_OFFSET, FFA_SHARED_MM_BUFFER_ADDR * make mm_sp_svc_uuid static * replace EINVAL with ENOMEM in ffa_discover_mm_sp_id() when calloc() fails * improve use of unmap_sysmem() in ffa_mm_communicate()
v6:
* add FF-A runtime discovery at MM communication level * drop EFI runtime support for FF-A MM communication * revert the changes in include/mm_communication.h for efi_mm_communicate_header and smm_variable_access structures
v4:
* use the new FF-A driver interfaces * discover MM partitions at runtime * copy FF-A driver private data to EFI runtime section at ExitBootServices() * drop use of FFA_ERR_STAT_SUCCESS error code * replace EFI_BUFFER_TOO_SMALL with EFI_OUT_OF_RESOURCES in ffa_mm_communicate(). No need for efi_memcpy_runtime() anymore * revert the error log in mm_communicate() in case of failure * remove packed attribute from efi_mm_communicate_header and smm_variable_communicate_header
v2:
* set default values to 0 for FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_ADDR and MM_SP_UUID_DATA and add warnings
v1:
* introduce FF-A MM communication
include/mm_communication.h | 13 ++ lib/efi_loader/Kconfig | 14 +- lib/efi_loader/efi_variable_tee.c | 291 +++++++++++++++++++++++++++++- 3 files changed, 312 insertions(+), 6 deletions(-)
diff --git a/include/mm_communication.h b/include/mm_communication.h index e65fbde60d..f17847583b 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. sughosh.ganu@linaro.org * Copyright (C) 2020 Linaro Ltd. ilias.apalodimas@linaro.org + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#ifndef _MM_COMMUNICATION_H_ @@ -13,6 +16,9 @@
#include <part_efi.h>
+/* MM service UUID string (big-endian format). This UUID is common across all MM SPs */ +#define MM_SP_UUID "33d532ed-e699-0942-c09c-a798d9cd722d" + /* * Interface to the pseudo Trusted Application (TA), which provides a * communication channel with the Standalone MM (Management Mode) @@ -248,4 +254,11 @@ struct smm_variable_var_check_property { u16 name[]; };
+/* supported MM transports */ +enum mm_comms_select { + MM_COMMS_UNDEFINED, + MM_COMMS_FFA, + MM_COMMS_OPTEE +}; + #endif /* _MM_COMMUNICATION_H_ */ diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index c5835e6ef6..08a6b84101 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -55,13 +55,23 @@ 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" - depends on OPTEE + bool "UEFI variables storage service via the trusted world" + depends on OPTEE || ARM_FFA_TRANSPORT 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 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 dfef18435d..2effb705d7 100644 --- a/lib/efi_loader/efi_variable_tee.c +++ b/lib/efi_loader/efi_variable_tee.c @@ -4,9 +4,14 @@ * * Copyright (C) 2019 Linaro Ltd. sughosh.ganu@linaro.org * Copyright (C) 2019 Linaro Ltd. ilias.apalodimas@linaro.org + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> +#include <dm.h> #include <efi.h> #include <efi_api.h> #include <efi_loader.h> @@ -15,6 +20,36 @@ #include <malloc.h> #include <mm_communication.h>
+#if IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT) + +#include <arm_ffa.h> +#include <cpu_func.h> +#include <mapmem.h> + +#ifndef FFA_SHARED_MM_BUFFER_SIZE +#error "FFA_SHARED_MM_BUFFER_SIZE must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_SIZE 0 +#endif + +#ifndef FFA_SHARED_MM_BUFFER_OFFSET +#error "FFA_SHARED_MM_BUFFER_OFFSET must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_OFFSET 0 +#endif + +#ifndef FFA_SHARED_MM_BUFFER_ADDR +#error "FFA_SHARED_MM_BUFFER_ADDR must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_ADDR 0 +#endif + +/* MM return codes */ +#define MM_SUCCESS (0) + +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 */ static efi_uintn_t max_payload_size; /* func + data */ @@ -24,6 +59,7 @@ struct mm_connection { u32 session; };
+#if (IS_ENABLED(CONFIG_OPTEE)) /** * get_connection() - Retrieve OP-TEE session for a specific UUID. * @@ -60,6 +96,7 @@ static int get_connection(struct mm_connection *conn) out: return rc; } +#endif
/** * optee_mm_communicate() - Pass a buffer to StandaloneMM running in OP-TEE @@ -70,6 +107,7 @@ out: */ static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize) { +#if (IS_ENABLED(CONFIG_OPTEE)) ulong buf_size; efi_status_t ret; struct efi_mm_communicate_header *mm_hdr; @@ -142,19 +180,246 @@ static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize) }
return ret; +#else + log_err("EFI: Please enable CONFIG_OPTEE for MM comms\n"); + return EFI_UNSUPPORTED; +#endif }
+#if IS_ENABLED(CONFIG_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 = -1; + 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 = FFA_SHARED_MM_BUFFER_OFFSET; /* x3 */ + + ret = ffa_sync_send_receive(dev, mm_sp_id, &msg, 1); + if (ret) + return ret; + + sp_event_ret = msg.data0; /* x3 */ + + if (sp_event_ret == MM_SUCCESS) + return 0; + + /* Failure to notify the MM SP */ + + return -EACCES; +} + +/** + * 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; +} +#endif + +/** + * 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) +{ +#if IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT) + 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 > FFA_SHARED_MM_BUFFER_SIZE) + return EFI_INVALID_PARAMETER; + + /* Copy the data to the shared buffer */ + + virt_shared_buf = map_sysmem((phys_addr_t)FFA_SHARED_MM_BUFFER_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; +#else + log_err("EFI: Please enable CONFIG_ARM_FFA_TRANSPORT for MM comms\n"); + return EFI_UNSUPPORTED; +#endif +} + +/** + * select_mm_comms() - detect the available MM transport + * + * If FF-A is compiled in, make sure the FF-A bus is probed successfully + * which means FF-A communication with secure world works and ready + * to be used. + * + * If FF-A is not detected, check if OPTEE support is compiled in. + * + * Return: + * + * On success MM_COMMS_FFA or MM_COMMS_OPTEE. Otherwise, MM_COMMS_UNDEFINED + */ +static enum mm_comms_select select_mm_comms(void) +{ + struct udevice *dev; + int ret; + + if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) { + ret = uclass_first_device_err(UCLASS_FFA, &dev); + if (ret) + log_err("EFI: Cannot find FF-A bus device, cannot select FF-A comms\n"); + else + return MM_COMMS_FFA; + } + + if (IS_ENABLED(CONFIG_OPTEE)) + return MM_COMMS_OPTEE; + + return MM_COMMS_UNDEFINED; +} + +/** + * 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) { efi_status_t ret; + enum mm_comms_select mm_comms; struct efi_mm_communicate_header *mm_hdr; struct smm_variable_communicate_header *var_hdr;
@@ -162,7 +427,18 @@ static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) 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); + mm_comms = select_mm_comms(); + switch (mm_comms) { + case MM_COMMS_UNDEFINED: + ret = EFI_UNSUPPORTED; + break; + case MM_COMMS_OPTEE: + ret = optee_mm_communicate(comm_buf, dsize); + break; + default: + ret = ffa_mm_communicate(comm_buf, dsize); + } + if (ret != EFI_SUCCESS) { log_err("%s failed!\n", __func__); return ret; @@ -258,6 +534,13 @@ efi_status_t EFIAPI get_max_payload(efi_uintn_t *size) goto out; } *size = var_payload->size; + + #if IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT) + if (*size > FFA_SHARED_MM_BUFFER_SIZE) + *size = FFA_SHARED_MM_BUFFER_SIZE - MM_COMMUNICATE_HEADER_SIZE - + MM_VARIABLE_COMMUNICATE_SIZE; + #endif + /* * There seems to be a bug in EDK2 miscalculating the boundaries and * size checks, so deduct 2 more bytes to fulfill this requirement. Fix @@ -697,7 +980,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);
/*

Hi Abdellatif
I still have some concerns on this
In the past [0] I asking why this needs to be a Kconfig option. Since FF-A is a mechanism we can use to discover SPs, in theory we dont need the ifdefery. We might need something if the code difference grows too much but I think we are fine for now.
On top of that I am nissing how was this tested? did you test the current SMC to optee and the stmm for the RPMB backend?
+++ b/include/mm_communication.h @@ -6,6 +6,9 @@
- Copyright (c) 2017, Intel Corporation. All rights reserved.
- Copyright (C) 2020 Linaro Ltd. sughosh.ganu@linaro.org
- Copyright (C) 2020 Linaro Ltd. ilias.apalodimas@linaro.org
- Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
- Authors:
*/
Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
#ifndef _MM_COMMUNICATION_H_ @@ -13,6 +16,9 @@
#include <part_efi.h>
+/* MM service UUID string (big-endian format). This UUID is common across all MM SPs */ +#define MM_SP_UUID "33d532ed-e699-0942-c09c-a798d9cd722d"
/*
- Interface to the pseudo Trusted Application (TA), which provides a
- communication channel with the Standalone MM (Management Mode)
@@ -248,4 +254,11 @@ struct smm_variable_var_check_property { u16 name[]; };
+/* supported MM transports */ +enum mm_comms_select {
- MM_COMMS_UNDEFINED,
- MM_COMMS_FFA,
- MM_COMMS_OPTEE
+};
#endif /* _MM_COMMUNICATION_H_ */ diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index c5835e6ef6..08a6b84101 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -55,13 +55,23 @@ 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"
- depends on OPTEE
bool "UEFI variables storage service via the trusted world"
depends on OPTEE || ARM_FFA_TRANSPORT 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 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 dfef18435d..2effb705d7 100644 --- a/lib/efi_loader/efi_variable_tee.c +++ b/lib/efi_loader/efi_variable_tee.c @@ -4,9 +4,14 @@
- Copyright (C) 2019 Linaro Ltd. sughosh.ganu@linaro.org
- Copyright (C) 2019 Linaro Ltd. ilias.apalodimas@linaro.org
- Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
- Authors:
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#include <common.h> +#include <dm.h> #include <efi.h> #include <efi_api.h> #include <efi_loader.h> @@ -15,6 +20,36 @@ #include <malloc.h> #include <mm_communication.h>
+#if IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)
+#include <arm_ffa.h> +#include <cpu_func.h> +#include <mapmem.h>
+#ifndef FFA_SHARED_MM_BUFFER_SIZE +#error "FFA_SHARED_MM_BUFFER_SIZE must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_SIZE 0 +#endif
+#ifndef FFA_SHARED_MM_BUFFER_OFFSET +#error "FFA_SHARED_MM_BUFFER_OFFSET must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_OFFSET 0 +#endif
+#ifndef FFA_SHARED_MM_BUFFER_ADDR +#error "FFA_SHARED_MM_BUFFER_ADDR must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_ADDR 0 +#endif
+/* MM return codes */ +#define MM_SUCCESS (0)
+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 */ static efi_uintn_t max_payload_size; /* func + data */ @@ -24,6 +59,7 @@ struct mm_connection { u32 session; };
+#if (IS_ENABLED(CONFIG_OPTEE))
/**
- get_connection() - Retrieve OP-TEE session for a specific UUID.
[...]
Isn't this problematic? At the moment there's not > 8.4 hardware out there. As a result the SPMC *is* implemented in OP-TEE. So how do we expect FF-A to work?
[...]
[0] https://lore.kernel.org/u-boot/Y3NV991bKRgas1zZ@hera/
Thanks /Ilias

Hi Ilias,
Hi Abdellatif
I still have some concerns on this
In the past [0] I asking why this needs to be a Kconfig option. Since FF-A is a mechanism we can use to discover SPs, in theory we dont need the ifdefery. We might need something if the code difference grows too much but I think we are fine for now.
Is my understanding correct you prefer that CONFIG_EFI_MM_COMM_TEE implies CONFIG_ARM_FFA_TRANSPORT ? So, no ifdefs anymore in efi_variable_tee.c.
On top of that I am nissing how was this tested? did you test the current SMC to optee and the stmm for the RPMB backend?
We use Corstone-1000 platform for testing FF-A. U-Boot communicates with smm-gateway (leight version of stmm) using FF-A SMC ABIs. In the secure world we use TF-A, Optee OS and Trusted Services providing smm-gateway SP. In U-Boot we don't use the Optee driver because we use FF-A communication only. I hope this clears all doubts.
Cheers Abdellatif
+++ b/include/mm_communication.h @@ -6,6 +6,9 @@
- Copyright (c) 2017, Intel Corporation. All rights reserved.
- Copyright (C) 2020 Linaro Ltd. sughosh.ganu@linaro.org
- Copyright (C) 2020 Linaro Ltd. ilias.apalodimas@linaro.org
- Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
- Authors:
*/
Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
#ifndef _MM_COMMUNICATION_H_ @@ -13,6 +16,9 @@
#include <part_efi.h>
+/* MM service UUID string (big-endian format). This UUID is common across all MM SPs */ +#define MM_SP_UUID "33d532ed-e699-0942-c09c-a798d9cd722d"
/*
- Interface to the pseudo Trusted Application (TA), which provides a
- communication channel with the Standalone MM (Management Mode)
@@ -248,4 +254,11 @@ struct smm_variable_var_check_property { u16 name[]; };
+/* supported MM transports */ +enum mm_comms_select {
- MM_COMMS_UNDEFINED,
- MM_COMMS_FFA,
- MM_COMMS_OPTEE
+};
#endif /* _MM_COMMUNICATION_H_ */ diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index c5835e6ef6..08a6b84101 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -55,13 +55,23 @@ 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"
- depends on OPTEE
bool "UEFI variables storage service via the trusted world"
depends on OPTEE || ARM_FFA_TRANSPORT 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 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 dfef18435d..2effb705d7 100644 --- a/lib/efi_loader/efi_variable_tee.c +++ b/lib/efi_loader/efi_variable_tee.c @@ -4,9 +4,14 @@
- Copyright (C) 2019 Linaro Ltd. sughosh.ganu@linaro.org
- Copyright (C) 2019 Linaro Ltd. ilias.apalodimas@linaro.org
- Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
- Authors:
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#include <common.h> +#include <dm.h> #include <efi.h> #include <efi_api.h> #include <efi_loader.h> @@ -15,6 +20,36 @@ #include <malloc.h> #include <mm_communication.h>
+#if IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)
+#include <arm_ffa.h> +#include <cpu_func.h> +#include <mapmem.h>
+#ifndef FFA_SHARED_MM_BUFFER_SIZE +#error "FFA_SHARED_MM_BUFFER_SIZE must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_SIZE 0 +#endif
+#ifndef FFA_SHARED_MM_BUFFER_OFFSET +#error "FFA_SHARED_MM_BUFFER_OFFSET must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_OFFSET 0 +#endif
+#ifndef FFA_SHARED_MM_BUFFER_ADDR +#error "FFA_SHARED_MM_BUFFER_ADDR must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_ADDR 0 +#endif
+/* MM return codes */ +#define MM_SUCCESS (0)
+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 */ static efi_uintn_t max_payload_size; /* func + data */ @@ -24,6 +59,7 @@ struct mm_connection { u32 session; };
+#if (IS_ENABLED(CONFIG_OPTEE))
/**
- get_connection() - Retrieve OP-TEE session for a specific UUID.
[...]
Isn't this problematic? At the moment there's not > 8.4 hardware out there. As a result the SPMC *is* implemented in OP-TEE. So how do we expect FF-A to work?
[...]
[0] https://lore.kernel.org/u-boot/Y3NV991bKRgas1zZ@hera/
Thanks /Ilias

On Fri, May 19, 2023 at 02:36:55PM +0100, Abdellatif El Khlifi wrote:
Hi Ilias,
Hi Abdellatif
I still have some concerns on this
In the past [0] I asking why this needs to be a Kconfig option. Since FF-A is a mechanism we can use to discover SPs, in theory we dont need the ifdefery. We might need something if the code difference grows too much but I think we are fine for now.
Is my understanding correct you prefer that CONFIG_EFI_MM_COMM_TEE implies CONFIG_ARM_FFA_TRANSPORT ? So, no ifdefs anymore in efi_variable_tee.c.
I wonder what it takes to autodiscover that and get rid of all ifdefs (OPTEE and FF-A). In theory you could probe FF-A and if it's not there switch to the smc calling no?
On top of that I am nissing how was this tested? did you test the current SMC to optee and the stmm for the RPMB backend?
We use Corstone-1000 platform for testing FF-A. U-Boot communicates with smm-gateway (leight version of stmm) using FF-A SMC ABIs. In the secure world we use TF-A, Optee OS and Trusted Services providing smm-gateway SP. In U-Boot we don't use the Optee driver because we use FF-A communication only. I hope this clears all doubts.
Ok, but you will still need to load op-tee from BL2. My problem here is that this is a bit confusing from an architectural point of view and people need to be aware of what config options to choose, but if my understanding is correct we can automate that.
Thanks /Ilias
Cheers Abdellatif
+++ b/include/mm_communication.h @@ -6,6 +6,9 @@
- Copyright (c) 2017, Intel Corporation. All rights reserved.
- Copyright (C) 2020 Linaro Ltd. sughosh.ganu@linaro.org
- Copyright (C) 2020 Linaro Ltd. ilias.apalodimas@linaro.org
- Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
- Authors:
*/
Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
#ifndef _MM_COMMUNICATION_H_ @@ -13,6 +16,9 @@
#include <part_efi.h>
+/* MM service UUID string (big-endian format). This UUID is common across all MM SPs */ +#define MM_SP_UUID "33d532ed-e699-0942-c09c-a798d9cd722d"
/*
- Interface to the pseudo Trusted Application (TA), which provides a
- communication channel with the Standalone MM (Management Mode)
@@ -248,4 +254,11 @@ struct smm_variable_var_check_property { u16 name[]; };
+/* supported MM transports */ +enum mm_comms_select {
- MM_COMMS_UNDEFINED,
- MM_COMMS_FFA,
- MM_COMMS_OPTEE
+};
#endif /* _MM_COMMUNICATION_H_ */ diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index c5835e6ef6..08a6b84101 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -55,13 +55,23 @@ 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"
- depends on OPTEE
bool "UEFI variables storage service via the trusted world"
depends on OPTEE || ARM_FFA_TRANSPORT 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 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 dfef18435d..2effb705d7 100644 --- a/lib/efi_loader/efi_variable_tee.c +++ b/lib/efi_loader/efi_variable_tee.c @@ -4,9 +4,14 @@
- Copyright (C) 2019 Linaro Ltd. sughosh.ganu@linaro.org
- Copyright (C) 2019 Linaro Ltd. ilias.apalodimas@linaro.org
- Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
- Authors:
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#include <common.h> +#include <dm.h> #include <efi.h> #include <efi_api.h> #include <efi_loader.h> @@ -15,6 +20,36 @@ #include <malloc.h> #include <mm_communication.h>
+#if IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)
+#include <arm_ffa.h> +#include <cpu_func.h> +#include <mapmem.h>
+#ifndef FFA_SHARED_MM_BUFFER_SIZE +#error "FFA_SHARED_MM_BUFFER_SIZE must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_SIZE 0 +#endif
+#ifndef FFA_SHARED_MM_BUFFER_OFFSET +#error "FFA_SHARED_MM_BUFFER_OFFSET must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_OFFSET 0 +#endif
+#ifndef FFA_SHARED_MM_BUFFER_ADDR +#error "FFA_SHARED_MM_BUFFER_ADDR must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_ADDR 0 +#endif
+/* MM return codes */ +#define MM_SUCCESS (0)
+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 */ static efi_uintn_t max_payload_size; /* func + data */ @@ -24,6 +59,7 @@ struct mm_connection { u32 session; };
+#if (IS_ENABLED(CONFIG_OPTEE))
/**
- get_connection() - Retrieve OP-TEE session for a specific UUID.
[...]
Isn't this problematic? At the moment there's not > 8.4 hardware out there. As a result the SPMC *is* implemented in OP-TEE. So how do we expect FF-A to work?
[...]
[0] https://lore.kernel.org/u-boot/Y3NV991bKRgas1zZ@hera/
Thanks /Ilias

turn on EFI MM communication
On corstone1000 platform MM communication between u-boot and the secure world (Optee) is done using the FF-A bus.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
--- Changelog: ===============
v9: update copyright string
v8:
* drop OP-TEE configs from Corstone-1000 defconfig
v7:
* improve the definition of FFA_SHARED_MM_BUFFER_ADDR and FFA_SHARED_MM_BUFFER_OFFSET * update FFA_SHARED_MM_BUFFER_ADDR value
v6:
* corstone-1000: enable optee driver * corstone-1000: remove CONFIG_ARM_FFA_EFI_RUNTIME_MODE from the defconfig
v4:
* corstone-1000: turn on EFI MM communication
configs/corstone1000_defconfig | 2 ++ include/configs/corstone1000.h | 15 +++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-)
diff --git a/configs/corstone1000_defconfig b/configs/corstone1000_defconfig index 2d391048cd..0a48df9fbc 100644 --- a/configs/corstone1000_defconfig +++ b/configs/corstone1000_defconfig @@ -53,3 +53,5 @@ CONFIG_DM_SERIAL=y CONFIG_USB=y CONFIG_USB_ISP1760=y CONFIG_ERRNO_STR=y +CONFIG_EFI_MM_COMM_TEE=y +CONFIG_ARM_FFA_TRANSPORT=y diff --git a/include/configs/corstone1000.h b/include/configs/corstone1000.h index 3347c11792..4ef1f05e40 100644 --- a/include/configs/corstone1000.h +++ b/include/configs/corstone1000.h @@ -1,9 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0+ */ /* - * (C) Copyright 2022 ARM Limited * (C) Copyright 2022 Linaro * Rui Miguel Silva rui.silva@linaro.org - * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com * * Configuration for Corstone1000. Parts were derived from other ARM * configurations. @@ -14,6 +16,15 @@
#include <linux/sizes.h>
+#define FFA_SHARED_MM_BUFFER_SIZE SZ_4K /* 4 KB */ + +/* + * shared buffer physical address used for communication between + * u-boot and the MM SP + */ +#define FFA_SHARED_MM_BUFFER_ADDR 0x02000000UL +#define FFA_SHARED_MM_BUFFER_OFFSET 0 + #define V2M_BASE 0x80000000
#define CFG_PL011_CLOCK 50000000

Hi Simon,
Adding support for Arm FF-A v1.0 (Arm Firmware Framework for Armv8-A) [A].
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.
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.
=> dm tree
Class Index Probed Driver Name
... firmware 0 [ + ] psci |-- psci ffa 0 [ ] arm_ffa | `-- arm_ffa ...
Clients are able to probe then use the FF-A bus by calling the DM class searching APIs (e.g: uclass_first_device).
This implementation of the specification provides support for Aarch64.
The FF-A driver uses the SMC ABIs defined by the FF-A specification to:
- Discover the presence of secure partitions (SPs) of interest - Access an SP's service through communication protocols (e.g: EFI MM communication protocol)
The FF-A support provides the following features:
- Being generic by design and can be used by any Arm 64-bit platform - FF-A support can be compiled and used without EFI - Support for SMCCCv1.2 x0-x17 registers - Support for SMC32 calling convention - Support for 32-bit and 64-bit FF-A direct messaging - Support for FF-A MM communication (compatible with EFI boot time) - Enabling FF-A and MM communication in Corstone1000 platform as a use case - 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. - Sandbox FF-A test cases. - A new command called armffa is provided as an example of how to access the FF-A bus
For more details about the FF-A support please refer to [B] and refer to [C] for how to use the armffa command.
Please find at [D] an example of the expected boot logs when enabling FF-A support for a platform. In this example the platform is Corstone1000. But it can be any Arm 64-bit platform.
Changelog of the major changes:
v12:
- remove the global variable (dscvry_info), use uc_priv instead
- replace dscvry_info.invoke_ffa_fn() with a weak invoke_ffa_fn (user drivers can override it)
- improve FFA_PARTITION_INFO_GET implementation (clients no longer need to calloc a buffer)
- remove reparenting by making the sandbox emulator parent of the FF-A device in the DT
- improve argument checks for the armffa command
- address nits
v11: [11]
- move ffa_try_discovery() from the uclass to the Arm FF-A driver
- rename ffa_try_discovery() to arm_ffa_discover()
- add arm_ prefix to the Arm FF-A driver functions
- use U_BOOT_CMD_WITH_SUBCMDS for armffa command
- store the sandbox emulator pointer in the FF-A device uc_priv (struct ffa_priv)
- set the emulator as parent of the sandbox FF-A device
- rename select_ffa_mm_comms() to select_mm_comms()
- improve the logic of MM transport selection in mm_communicate()
- use ut_asserteq_mem() in uuid_str_to_le_bin test case
- address nits
v10: [10]
- provide the FF-A driver operations through the Uclass (arm-ffa-uclass.c)
- move the generic FF-A methods to the Uclass
- keep Arm specific methods in the Arm driver (arm-ffa.c renamed from core.c)
- split the FF-A sandbox support into an emulator (ffa-emul-uclass.c) and a driver (sandbox_ffa.c)
- use the FF-A driver Uclass operations by clients (armffa command, tests, MM comms)
- use uclass_first_device to search and probe the FF-A device (whether it is on Arm or on sandbox)
- address nits
v9: [9]
- integrate the FF-A bus discovery in the DM and use ARM_SMCCC_FEATURES for binding
- align FF-A sandbox driver with FF-A discovery through DM
- use DM class APIs to probe and interact with the FF-A bus (in FF-A MM comms, armffa command, sandbox tests)
- add documentation for the armffa command: doc/usage/cmd/armffa.rst
- introduce testcase for uuid_str_to_le_bin
v8: [8]
- pass the FF-A bus device to the bus operations
- isolate the compilation choices between FF-A and OP-TEE
- drop OP-TEE configs from Corstone-1000 defconfig
- make ffa_get_partitions_info() second argument to be an SP count in both modes
v7: [7]
- add support for 32-bit direct messaging (now we have 32-bit and 64-bit support)
- set the MM door bell event to use 64-bit direct messaging
- issue a compile time error when one of these macros are not found : FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_OFFSET, FFA_SHARED_MM_BUFFER_ADDR
- make uuid_str_to_le_bin() implementation similar to uuid_str_to_bin() by using same APIs
v6: [6]
- remove clearing x0-x17 registers after SMC calls
- drop use of EFI runtime support for FF-A (We decided with Linaro to add this later)
- drop discovery from initcalls (discovery will be on demand by FF-A users)
- add FF-A runtime discovery at MM communication level
- update the documentation and move it to doc/arch/arm64.ffa.rst
v5: [5]
- move changelogs in each commit to the changes section
v4: [4]
- add FF-A support README (doc/README.ffa.drv)
- improving error handling by mapping the FF-A errors to standard errors and logs
- replacing panics with an error log
- align sandbox driver and tests with the new FF-A driver interfaces
and new way of error handling
- use the new FF-A driver interfaces for MM communication
- discover MM partitions at runtime
- copy FF-A driver private data to EFI runtime section at ExitBootServices()
- moving the FF-A driver work to drivers/firmware/arm-ffa
- improving features discovery in FFA_FEATURES
- add remove/unbind functions to the FF-A core device
- improve how the driver behaves when bus discovery is done more than once
- move clearing x0-x17 registers code into a new macro like done in the linux kernel
- enable EFI MM communication for the Corstone1000 platform
v3: [3]
- port x0-x17 registers support from linux kernel as defined by SMCCCv1.2
- align the interfaces of the u-boot FF-A driver with those in the linux FF-A driver
- remove the FF-A helper layer
- make the u-boot FF-A driver independent from EFI
- provide an optional config that enables copying the driver data to EFI runtime section at ExitBootServices service
- use 64-bit version of FFA_RXTX_MAP, FFA_MSG_SEND_DIRECT_{REQ, RESP}
- update armffa command with the new driver interfaces
v2 [2]:
- make FF-A bus discoverable using device_{bind, probe} APIs
- remove device tree support
v1 [1]:
- introduce FF-A bus driver with device tree support
- introduce armffa command
- introduce FF-A Sandbox driver
- add FF-A Sandbox test cases
- introduce FF-A MM communication
Cheers, Abdellatif
List of previous patches:
More details:
[D]: example of boot logs when enabling FF-A
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
Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org
A gentle reminder about the v12 patchset.
Your feedback is more than welcome :)
Cheers
Cc: Rob Herring robh@kernel.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Achin Gupta achin.gupta@arm.com Cc: Drew Reed Drew.Reed@arm.com Cc: Xueliang Zhong Xueliang.Zhong@arm.com
Abdellatif El Khlifi (10): arm64: smccc: add support for SMCCCv1.2 x0-x17 registers lib: uuid: introduce uuid_str_to_le_bin function lib: uuid: introduce testcase for uuid_str_to_le_bin arm_ffa: introduce Arm FF-A support arm_ffa: introduce armffa command arm_ffa: introduce sandbox FF-A support arm_ffa: introduce sandbox test cases for UCLASS_FFA arm_ffa: introduce armffa command Sandbox test arm_ffa: efi: introduce FF-A MM communication arm_ffa: efi: corstone1000: enable MM communication
MAINTAINERS | 18 + arch/arm/cpu/armv8/smccc-call.S | 57 +- arch/arm/lib/asm-offsets.c | 16 + 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 ++ cmd/Kconfig | 10 + cmd/Makefile | 1 + cmd/armffa.c | 196 +++ configs/corstone1000_defconfig | 2 + configs/sandbox64_defconfig | 1 + configs/sandbox_defconfig | 1 + doc/arch/arm64.ffa.rst | 270 +++++ doc/arch/index.rst | 1 + doc/arch/sandbox/sandbox.rst | 1 + doc/usage/cmd/armffa.rst | 93 ++ doc/usage/index.rst | 1 + drivers/Makefile | 1 + drivers/firmware/Kconfig | 1 + drivers/firmware/arm-ffa/Kconfig | 42 + drivers/firmware/arm-ffa/Makefile | 16 + drivers/firmware/arm-ffa/arm-ffa-uclass.c | 1065 +++++++++++++++++ drivers/firmware/arm-ffa/arm-ffa.c | 104 ++ drivers/firmware/arm-ffa/ffa-emul-uclass.c | 720 +++++++++++ drivers/firmware/arm-ffa/sandbox_ffa.c | 110 ++ include/arm_ffa.h | 213 ++++ include/arm_ffa_priv.h | 246 ++++ include/configs/corstone1000.h | 15 +- include/dm/uclass-id.h | 7 + include/linux/arm-smccc.h | 45 + include/mm_communication.h | 13 + include/uuid.h | 15 + lib/efi_loader/Kconfig | 14 +- lib/efi_loader/efi_variable_tee.c | 291 ++++- lib/uuid.c | 48 + test/cmd/Makefile | 2 + test/cmd/armffa.c | 33 + test/dm/Makefile | 3 +- test/dm/ffa.c | 261 ++++ test/lib/Makefile | 1 + test/lib/uuid.c | 41 + 42 files changed, 4175 insertions(+), 10 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 cmd/armffa.c create mode 100644 doc/arch/arm64.ffa.rst create mode 100644 doc/usage/cmd/armffa.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/ffa-emul-uclass.c create mode 100644 drivers/firmware/arm-ffa/sandbox_ffa.c create mode 100644 include/arm_ffa.h create mode 100644 include/arm_ffa_priv.h create mode 100644 test/cmd/armffa.c create mode 100644 test/dm/ffa.c create mode 100644 test/lib/uuid.c
-- 2.25.1

On Mon, May 22, 2023 at 10:13:20AM +0100, Abdellatif El Khlifi wrote:
Hi Simon, Tom,
Hi Simon,
Adding support for Arm FF-A v1.0 (Arm Firmware Framework for Armv8-A) [A].
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.
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.
=> dm tree
Class Index Probed Driver Name
... firmware 0 [ + ] psci |-- psci ffa 0 [ ] arm_ffa | `-- arm_ffa ...
Clients are able to probe then use the FF-A bus by calling the DM class searching APIs (e.g: uclass_first_device).
This implementation of the specification provides support for Aarch64.
The FF-A driver uses the SMC ABIs defined by the FF-A specification to:
- Discover the presence of secure partitions (SPs) of interest - Access an SP's service through communication protocols (e.g: EFI MM communication protocol)
The FF-A support provides the following features:
- Being generic by design and can be used by any Arm 64-bit platform - FF-A support can be compiled and used without EFI - Support for SMCCCv1.2 x0-x17 registers - Support for SMC32 calling convention - Support for 32-bit and 64-bit FF-A direct messaging - Support for FF-A MM communication (compatible with EFI boot time) - Enabling FF-A and MM communication in Corstone1000 platform as a use case - 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. - Sandbox FF-A test cases. - A new command called armffa is provided as an example of how to access the FF-A bus
For more details about the FF-A support please refer to [B] and refer to [C] for how to use the armffa command.
Please find at [D] an example of the expected boot logs when enabling FF-A support for a platform. In this example the platform is Corstone1000. But it can be any Arm 64-bit platform.
Changelog of the major changes:
v12:
- remove the global variable (dscvry_info), use uc_priv instead
- replace dscvry_info.invoke_ffa_fn() with a weak invoke_ffa_fn (user drivers can override it)
- improve FFA_PARTITION_INFO_GET implementation (clients no longer need to calloc a buffer)
- remove reparenting by making the sandbox emulator parent of the FF-A device in the DT
- improve argument checks for the armffa command
- address nits
...
Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org
A gentle reminder about the v12 patchset.
Your feedback is more than welcome :)
Could you please provide a feedback about the v12 patchset ?
Most of the patches are generic.
I'm gonna address Ilias comment for patch #9 with your future comments.
Your review is very much appreciated.
Kind regards Abdellatif

On Tue, Jun 06, 2023 at 12:56:28PM +0100, Abdellatif El Khlifi wrote:
On Mon, May 22, 2023 at 10:13:20AM +0100, Abdellatif El Khlifi wrote:
Hi Simon, Tom,
Hi Simon,
Adding support for Arm FF-A v1.0 (Arm Firmware Framework for Armv8-A) [A].
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.
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.
=> dm tree
Class Index Probed Driver Name
... firmware 0 [ + ] psci |-- psci ffa 0 [ ] arm_ffa | `-- arm_ffa ...
Clients are able to probe then use the FF-A bus by calling the DM class searching APIs (e.g: uclass_first_device).
This implementation of the specification provides support for Aarch64.
The FF-A driver uses the SMC ABIs defined by the FF-A specification to:
- Discover the presence of secure partitions (SPs) of interest - Access an SP's service through communication protocols (e.g: EFI MM communication protocol)
The FF-A support provides the following features:
- Being generic by design and can be used by any Arm 64-bit platform - FF-A support can be compiled and used without EFI - Support for SMCCCv1.2 x0-x17 registers - Support for SMC32 calling convention - Support for 32-bit and 64-bit FF-A direct messaging - Support for FF-A MM communication (compatible with EFI boot time) - Enabling FF-A and MM communication in Corstone1000 platform as a use case - 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. - Sandbox FF-A test cases. - A new command called armffa is provided as an example of how to access the FF-A bus
For more details about the FF-A support please refer to [B] and refer to [C] for how to use the armffa command.
Please find at [D] an example of the expected boot logs when enabling FF-A support for a platform. In this example the platform is Corstone1000. But it can be any Arm 64-bit platform.
Changelog of the major changes:
v12:
- remove the global variable (dscvry_info), use uc_priv instead
- replace dscvry_info.invoke_ffa_fn() with a weak invoke_ffa_fn (user drivers can override it)
- improve FFA_PARTITION_INFO_GET implementation (clients no longer need to calloc a buffer)
- remove reparenting by making the sandbox emulator parent of the FF-A device in the DT
- improve argument checks for the armffa command
- address nits
...
Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org
A gentle reminder about the v12 patchset.
Your feedback is more than welcome :)
Could you please provide a feedback about the v12 patchset ?
Most of the patches are generic.
I'm gonna address Ilias comment for patch #9 with your future comments.
Your review is very much appreciated.
I've been waiting for Ilias to be happy with the series and then I'll pick it up for -next.

Adding support for Arm FF-A v1.0 (Arm Firmware Framework for Armv8-A) [A].
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.
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.
=> dm tree
Class Index Probed Driver Name ----------------------------------------------------------- ... firmware 0 [ + ] psci |-- psci ffa 0 [ ] arm_ffa | `-- arm_ffa ...
Clients are able to probe then use the FF-A bus by calling the DM class searching APIs (e.g: uclass_first_device).
This implementation of the specification provides support for Aarch64.
The FF-A driver uses the SMC ABIs defined by the FF-A specification to:
- Discover the presence of secure partitions (SPs) of interest - Access an SP's service through communication protocols (e.g: EFI MM communication protocol)
The FF-A support provides the following features:
- Being generic by design and can be used by any Arm 64-bit platform - FF-A support can be compiled and used without EFI - Support for SMCCCv1.2 x0-x17 registers - Support for SMC32 calling convention - Support for 32-bit and 64-bit FF-A direct messaging - Support for FF-A MM communication (compatible with EFI boot time) - Enabling FF-A and MM communication in Corstone1000 platform as a use case - 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. - Sandbox FF-A test cases. - A new command called armffa is provided as an example of how to access the FF-A bus
For more details about the FF-A support please refer to [B] and refer to [C] for how to use the armffa command.
Please find at [D] an example of the expected boot logs when enabling FF-A support for a platform. In this example the platform is Corstone1000. But it can be any Arm 64-bit platform.
Changelog of changes: ===========================
v13:
Ilias: * remove FF-A and Optee ifdefs in efi_variable_tee.c * doc minor change: specify in the readme that the user should call ffa_rxtx_unmap() driver operation to unmap the RX/TX buffers on demand.
v12: [12]
* remove the global variable (dscvry_info), use uc_priv instead * replace dscvry_info.invoke_ffa_fn() with a weak invoke_ffa_fn (user drivers can override it) * improve FFA_PARTITION_INFO_GET implementation (clients no longer need to calloc a buffer) * remove reparenting by making the sandbox emulator parent of the FF-A device in the DT * improve argument checks for the armffa command * address nits
v11: [11]
* move ffa_try_discovery() from the uclass to the Arm FF-A driver * rename ffa_try_discovery() to arm_ffa_discover() * add arm_ prefix to the Arm FF-A driver functions * use U_BOOT_CMD_WITH_SUBCMDS for armffa command * store the sandbox emulator pointer in the FF-A device uc_priv (struct ffa_priv) * set the emulator as parent of the sandbox FF-A device * rename select_ffa_mm_comms() to select_mm_comms() * improve the logic of MM transport selection in mm_communicate() * use ut_asserteq_mem() in uuid_str_to_le_bin test case * address nits
v10: [10]
* provide the FF-A driver operations through the Uclass (arm-ffa-uclass.c) * move the generic FF-A methods to the Uclass * keep Arm specific methods in the Arm driver (arm-ffa.c renamed from core.c) * split the FF-A sandbox support into an emulator (ffa-emul-uclass.c) and a driver (sandbox_ffa.c) * use the FF-A driver Uclass operations by clients (armffa command, tests, MM comms) * use uclass_first_device to search and probe the FF-A device (whether it is on Arm or on sandbox) * address nits
v9: [9]
* integrate the FF-A bus discovery in the DM and use ARM_SMCCC_FEATURES for binding * align FF-A sandbox driver with FF-A discovery through DM * use DM class APIs to probe and interact with the FF-A bus (in FF-A MM comms, armffa command, sandbox tests) * add documentation for the armffa command: doc/usage/cmd/armffa.rst * introduce testcase for uuid_str_to_le_bin
v8: [8]
* pass the FF-A bus device to the bus operations * isolate the compilation choices between FF-A and OP-TEE * drop OP-TEE configs from Corstone-1000 defconfig * make ffa_get_partitions_info() second argument to be an SP count in both modes
v7: [7]
* add support for 32-bit direct messaging (now we have 32-bit and 64-bit support) * set the MM door bell event to use 64-bit direct messaging * issue a compile time error when one of these macros are not found : FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_OFFSET, FFA_SHARED_MM_BUFFER_ADDR * make uuid_str_to_le_bin() implementation similar to uuid_str_to_bin() by using same APIs
v6: [6]
* remove clearing x0-x17 registers after SMC calls * drop use of EFI runtime support for FF-A (We decided with Linaro to add this later) * drop discovery from initcalls (discovery will be on demand by FF-A users) * add FF-A runtime discovery at MM communication level * update the documentation and move it to doc/arch/arm64.ffa.rst
v5: [5]
* move changelogs in each commit to the changes section
v4: [4]
* add FF-A support README (doc/README.ffa.drv) * improving error handling by mapping the FF-A errors to standard errors and logs * replacing panics with an error log * align sandbox driver and tests with the new FF-A driver interfaces and new way of error handling * use the new FF-A driver interfaces for MM communication * discover MM partitions at runtime * copy FF-A driver private data to EFI runtime section at ExitBootServices() * moving the FF-A driver work to drivers/firmware/arm-ffa * improving features discovery in FFA_FEATURES * add remove/unbind functions to the FF-A core device * improve how the driver behaves when bus discovery is done more than once * move clearing x0-x17 registers code into a new macro like done in the linux kernel * enable EFI MM communication for the Corstone1000 platform
v3: [3]
* port x0-x17 registers support from linux kernel as defined by SMCCCv1.2 * align the interfaces of the u-boot FF-A driver with those in the linux FF-A driver * remove the FF-A helper layer * make the u-boot FF-A driver independent from EFI * provide an optional config that enables copying the driver data to EFI runtime section at ExitBootServices service * use 64-bit version of FFA_RXTX_MAP, FFA_MSG_SEND_DIRECT_{REQ, RESP} * update armffa command with the new driver interfaces
v2 [2]:
* make FF-A bus discoverable using device_{bind, probe} APIs * remove device tree support
v1 [1]:
* introduce FF-A bus driver with device tree support * introduce armffa command * introduce FF-A Sandbox driver * add FF-A Sandbox test cases * introduce FF-A MM communication
Cheers, Abdellatif
List of previous patches:
[1]: https://lore.kernel.org/all/20220329151659.16894-1-abdellatif.elkhlifi@arm.c... [2]: https://lore.kernel.org/all/20220415122803.16666-1-abdellatif.elkhlifi@arm.c... [3]: https://lore.kernel.org/all/20220801172053.20163-1-abdellatif.elkhlifi@arm.c... [4]: https://lore.kernel.org/all/20220926101723.9965-1-abdellatif.elkhlifi@arm.co... [5]: https://lore.kernel.org/all/20220926140827.15125-1-abdellatif.elkhlifi@arm.c... [6]: https://lore.kernel.org/all/20221013103857.614-1-abdellatif.elkhlifi@arm.com... [7]: https://lore.kernel.org/all/20221107192055.21669-1-abdellatif.elkhlifi@arm.c... [8]: https://lore.kernel.org/all/20221122131751.22747-1-abdellatif.elkhlifi@arm.c... [9]: https://lore.kernel.org/all/20230310141016.137986-1-abdellatif.elkhlifi@arm.... [10]: https://lore.kernel.org/all/20230328161157.219375-1-abdellatif.elkhlifi@arm.... [11]: https://lore.kernel.org/all/20230412094245.44674-1-abdellatif.elkhlifi@arm.c... [12]: https://lore.kernel.org/all/20230512121044.111574-1-abdellatif.elkhlifi@arm....
More details:
[A]: https://developer.arm.com/documentation/den0077/latest/ [B]: doc/arch/arm64.ffa.rst [C]: doc/usage/cmd/armffa.rst [D]: example of boot logs when enabling FF-A
``` 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 ```
Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Rob Herring robh@kernel.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Achin Gupta achin.gupta@arm.com Cc: Drew Reed Drew.Reed@arm.com Cc: Xueliang Zhong Xueliang.Zhong@arm.com
Abdellatif El Khlifi (10): arm64: smccc: add support for SMCCCv1.2 x0-x17 registers lib: uuid: introduce uuid_str_to_le_bin function lib: uuid: introduce testcase for uuid_str_to_le_bin arm_ffa: introduce Arm FF-A support arm_ffa: introduce armffa command arm_ffa: introduce sandbox FF-A support arm_ffa: introduce sandbox test cases for UCLASS_FFA arm_ffa: introduce armffa command Sandbox test arm_ffa: efi: introduce FF-A MM communication arm_ffa: efi: corstone1000: enable MM communication
MAINTAINERS | 18 + arch/arm/cpu/armv8/smccc-call.S | 57 +- arch/arm/lib/asm-offsets.c | 16 + 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 ++ cmd/Kconfig | 10 + cmd/Makefile | 1 + cmd/armffa.c | 196 +++ configs/corstone1000_defconfig | 1 + configs/sandbox64_defconfig | 1 + configs/sandbox_defconfig | 1 + doc/arch/arm64.ffa.rst | 261 ++++ doc/arch/index.rst | 1 + doc/arch/sandbox/sandbox.rst | 1 + doc/usage/cmd/armffa.rst | 93 ++ doc/usage/index.rst | 1 + drivers/Makefile | 1 + drivers/firmware/Kconfig | 1 + drivers/firmware/arm-ffa/Kconfig | 42 + drivers/firmware/arm-ffa/Makefile | 16 + drivers/firmware/arm-ffa/arm-ffa-uclass.c | 1065 +++++++++++++++++ drivers/firmware/arm-ffa/arm-ffa.c | 104 ++ drivers/firmware/arm-ffa/ffa-emul-uclass.c | 720 +++++++++++ drivers/firmware/arm-ffa/sandbox_ffa.c | 110 ++ include/arm_ffa.h | 213 ++++ include/arm_ffa_priv.h | 246 ++++ include/configs/corstone1000.h | 15 +- include/dm/uclass-id.h | 7 + include/linux/arm-smccc.h | 45 + include/mm_communication.h | 13 + include/uuid.h | 15 + lib/efi_loader/Kconfig | 16 +- lib/efi_loader/efi_variable_tee.c | 260 +++- lib/uuid.c | 48 + test/cmd/Makefile | 2 + test/cmd/armffa.c | 33 + test/dm/Makefile | 3 +- test/dm/ffa.c | 261 ++++ test/lib/Makefile | 1 + test/lib/uuid.c | 41 + 42 files changed, 4135 insertions(+), 11 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 cmd/armffa.c create mode 100644 doc/arch/arm64.ffa.rst create mode 100644 doc/usage/cmd/armffa.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/ffa-emul-uclass.c create mode 100644 drivers/firmware/arm-ffa/sandbox_ffa.c create mode 100644 include/arm_ffa.h create mode 100644 include/arm_ffa_priv.h create mode 100644 test/cmd/armffa.c create mode 100644 test/dm/ffa.c create mode 100644 test/lib/uuid.c

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 abdellatif.elkhlifi@arm.com Reviewed-by: Jens Wiklander jens.wiklander@linaro.org Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org
---
Changelog: ===============
v9:
* update the copyright string
v7:
* improve indentation of ARM_SMCCC_1_2_REGS_Xn_OFFS
v4:
* rename the commit title and improve description new commit title: the current
v3:
* port x0-x17 registers support from linux kernel as defined by SMCCCv1.2 commit title: arm64: smccc: add Xn registers support used by SMC calls
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 dc92b28777..93f66d3366 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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com +*/ #include <linux/linkage.h> #include <linux/arm-smccc.h> #include <generated/asm-offsets.h> @@ -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 6de0ce9152..181a8ac4c2 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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> @@ -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 e1d09884a1..f44e9e8f93 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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */ #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

Hi Abdellatif,
On Fri, 16 Jun 2023 at 18:28, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
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
I did review this one in the past, any reason why that is missing? Did the file change?
Thanks /Ilias
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Reviewed-by: Jens Wiklander jens.wiklander@linaro.org Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org
Changelog:
v9:
- update the copyright string
v7:
- improve indentation of ARM_SMCCC_1_2_REGS_Xn_OFFS
v4:
- rename the commit title and improve description new commit title: the current
v3:
- port x0-x17 registers support from linux kernel as defined by SMCCCv1.2 commit title: arm64: smccc: add Xn registers support used by SMC calls
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 dc92b28777..93f66d3366 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 open-source-office@arm.com
- Authors:
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
+*/ #include <linux/linkage.h> #include <linux/arm-smccc.h> #include <generated/asm-offsets.h> @@ -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 6de0ce9152..181a8ac4c2 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 open-source-office@arm.com
- Authors:
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#include <common.h> @@ -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 e1d09884a1..f44e9e8f93 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 open-source-office@arm.com
- Authors:
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#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
-- 2.25.1

Hi Ilias,
On Tue, Jun 20, 2023 at 05:05:52PM +0300, Ilias Apalodimas wrote:
Hi Abdellatif,
On Fri, 16 Jun 2023 at 18:28, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
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
I did review this one in the past, any reason why that is missing? Did the file change?
Thanks, I was waiting for your confirmation. I'll add your Reviewed-by in v14.
Cheers

convert UUID string to little endian binary data
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v9:
* add a full function prototype description in uuid.h
v8:
* use simple_strtoull() in uuid_str_to_le_bin() to support 32-bit platforms
v7:
* rename be_uuid_str_to_le_bin() to uuid_str_to_le_bin() * make uuid_str_to_le_bin() implementation similar to uuid_str_to_bin() by using same APIs
v4:
* rename ffa_uuid_str_to_bin to be_uuid_str_to_le_bin and put in a standalone commit (the current)
v3:
* introduce ffa_uuid_str_to_bin (provided by arm_ffa: introduce Arm FF-A low-level driver)
include/uuid.h | 15 +++++++++++++++ lib/uuid.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+)
diff --git a/include/uuid.h b/include/uuid.h index 4a4883d3b5..89b93e642b 100644 --- a/include/uuid.h +++ b/include/uuid.h @@ -2,6 +2,10 @@ /* * Copyright (C) 2014 Samsung Electronics * Przemyslaw Marczak p.marczak@samsung.com + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */ #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 96e1af3c8b..45f325d964 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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> @@ -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. *

provide a test case
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Simon Glass sjg@chromium.org
---
Changelog: ===============
v11:
* use ut_asserteq_mem()
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 228d8af433..b9e26cac82 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1632,3 +1632,8 @@ S: Maintained F: arch/arm/dts/ls1021a-twr-u-boot.dtsi F: drivers/crypto/fsl/ F: include/fsl_sec.h + +UUID testing +M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com +S: Maintained +F: test/lib/uuid.c diff --git a/test/lib/Makefile b/test/lib/Makefile index e0bd9e04e8..e75a263e6a 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 0000000000..e24331a136 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <uuid.h> +#include <test/lib.h> +#include <test/test.h> +#include <test/ut.h> + +/* 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);

On Fri, 16 Jun 2023 at 16:28, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
provide a test case
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Simon Glass sjg@chromium.org
Changelog:
v11:
- use ut_asserteq_mem()
MAINTAINERS | 5 +++++ test/lib/Makefile | 1 + test/lib/uuid.c | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100644 test/lib/uuid.c
Reviewed-by: Simon Glass sjg@chromium.org

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 abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
---
Changelog: ===============
v13:
* doc minor change: specify in the readme that the user should call ffa_rxtx_unmap() driver operation to unmap the RX/TX buffers on demand.
v12:
* remove dscvry_info * replace dscvry_info.invoke_ffa_fn() with a weak invoke_ffa_fn (user drivers can override it) * improve FFA_PARTITION_INFO_GET implementation (clients no longer need to calloc a buffer) * address nits
v11:
* move ffa_try_discovery() from the uclass to the Arm FF-A driver * rename ffa_try_discovery() to arm_ffa_discover() * pass dev as an argument of arm_ffa_discover() * add arm_ prefix to the Arm FF-A driver functions * add emul field in struct ffa_discovery_info * address nits
v10:
* provide the driver operations through the Uclass * move the generic FF-A methods to the Uclass * keep Arm specific methods in the Arm driver (arm-ffa.c) * rename core.c to arm-ffa.c * address nits
v9:
* integrate the FF-A bus discovery in the DM and use ARM_SMCCC_FEATURES for binding
v8:
* make ffa_get_partitions_info() second argument to be an SP count in both modes * update ffa_bus_prvdata_get() to return a pointer rather than a pointer address * remove packing from ffa_partition_info and ffa_send_direct_data structures * pass the FF-A bus device to the bus operations
v7:
* add support for 32-bit direct messaging * rename be_uuid_str_to_le_bin() to uuid_str_to_le_bin() * improve the declaration of error handling mapping * stating in doc/arch/arm64.ffa.rst that EFI runtime is not supported
v6:
* drop use of EFI runtime support (We decided with Linaro to add this later) * drop discovery from initcalls (discovery will be on demand by FF-A users) * set the alignment of the RX/TX buffers to the larger translation granule size * move FF-A RX/TX buffers unmapping at ExitBootServices() to a separate commit * update the documentation and move it to doc/arch/arm64.ffa.rst
v4:
* add doc/README.ffa.drv * moving the FF-A driver work to drivers/firmware/arm-ffa * use less #ifdefs in lib/efi_loader/efi_boottime.c and replace #if defined by #if CONFIG_IS_ENABLED * improving error handling by mapping the FF-A errors to standard errors and logs * replacing panics with an error log and returning an error code * improving features discovery in FFA_FEATURES by introducing rxtx_min_pages private data field * add ffa_remove and ffa_unbind functions * improve how the driver behaves when bus discovery is done more than once
v3:
* align the interfaces of the U-Boot FF-A driver with those in the linux FF-A driver * remove the FF-A helper layer * make the U-Boot FF-A driver independent from EFI * provide an optional config that enables copying the driver data to EFI runtime section at ExitBootServices service * use 64-bit version of FFA_RXTX_MAP, FFA_MSG_SEND_DIRECT_{REQ, RESP}
v2:
* make FF-A bus discoverable using device_{bind, probe} APIs * remove device tree support
v1:
* introduce FF-A bus driver with device tree support
MAINTAINERS | 8 + doc/arch/arm64.ffa.rst | 238 ++++ 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, 1941 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 b9e26cac82..43603522fd 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 abdellatif.elkhlifi@arm.com +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 sbabic@denx.de M: Fabio Estevam festevam@gmail.com diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst new file mode 100644 index 0000000000..4f817f053c --- /dev/null +++ b/doc/arch/arm64.ffa.rst @@ -0,0 +1,238 @@ +.. 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(). +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: + + - 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 abdellatif.elkhlifi@arm.com + +.. _`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 b8da4b8c8e..2f916f4026 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 29be78a3f2..6094fac50d 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -114,6 +114,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 eae1c8ddc9..8789b1ea14 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 0000000000..9200c8028b --- /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 0000000000..11b1766285 --- /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 open-source-office@arm.com +# +# Authors: +# Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + +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 0000000000..ffa9d81fa7 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ +#include <common.h> +#include <arm_ffa.h> +#include <arm_ffa_priv.h> +#include <dm.h> +#include <log.h> +#include <malloc.h> +#include <string.h> +#include <uuid.h> +#include <asm/global_data.h> +#include <dm/device-internal.h> +#include <dm/devres.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> + +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 0000000000..68df75bd9e --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <arm_ffa.h> +#include <arm_ffa_priv.h> +#include <dm.h> +#include <log.h> +#include <asm/global_data.h> +#include <dm/device-internal.h> +#include <linux/errno.h> + +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 0000000000..4338f9c9b1 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#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 0000000000..db9b1be995 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __ARM_FFA_H +#define __ARM_FFA_H + +#include <linux/printk.h> + +/* + * 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 0000000000..d564c33c64 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __ARM_FFA_PRV_H +#define __ARM_FFA_PRV_H + +#include <mapmem.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> + +/* 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 <linux/arm-smccc.h> +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 307ad6931c..3c6af2e3d2 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -4,6 +4,11 @@ * * (C) Copyright 2012 * Pavel Herrmann morpheus.ibis@gmail.com + * + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#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 */

On Fri, 16 Jun 2023 at 16:28, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
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].
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
Changelog:
v13:
- doc minor change: specify in the readme that the user should call ffa_rxtx_unmap() driver operation to unmap the RX/TX buffers on demand.
v12:
- remove dscvry_info
- replace dscvry_info.invoke_ffa_fn() with a weak invoke_ffa_fn (user drivers can override it)
- improve FFA_PARTITION_INFO_GET implementation (clients no longer need to calloc a buffer)
- address nits
v11:
- move ffa_try_discovery() from the uclass to the Arm FF-A driver
- rename ffa_try_discovery() to arm_ffa_discover()
- pass dev as an argument of arm_ffa_discover()
- add arm_ prefix to the Arm FF-A driver functions
- add emul field in struct ffa_discovery_info
- address nits
v10:
- provide the driver operations through the Uclass
- move the generic FF-A methods to the Uclass
- keep Arm specific methods in the Arm driver (arm-ffa.c)
- rename core.c to arm-ffa.c
- address nits
v9:
- integrate the FF-A bus discovery in the DM and use ARM_SMCCC_FEATURES for binding
v8:
- make ffa_get_partitions_info() second argument to be an SP count in both modes
- update ffa_bus_prvdata_get() to return a pointer rather than a pointer address
- remove packing from ffa_partition_info and ffa_send_direct_data structures
- pass the FF-A bus device to the bus operations
v7:
- add support for 32-bit direct messaging
- rename be_uuid_str_to_le_bin() to uuid_str_to_le_bin()
- improve the declaration of error handling mapping
- stating in doc/arch/arm64.ffa.rst that EFI runtime is not supported
v6:
- drop use of EFI runtime support (We decided with Linaro to add this later)
- drop discovery from initcalls (discovery will be on demand by FF-A users)
- set the alignment of the RX/TX buffers to the larger translation granule size
- move FF-A RX/TX buffers unmapping at ExitBootServices() to a separate commit
- update the documentation and move it to doc/arch/arm64.ffa.rst
v4:
- add doc/README.ffa.drv
- moving the FF-A driver work to drivers/firmware/arm-ffa
- use less #ifdefs in lib/efi_loader/efi_boottime.c and replace #if defined by #if CONFIG_IS_ENABLED
- improving error handling by mapping the FF-A errors to standard errors and logs
- replacing panics with an error log and returning an error code
- improving features discovery in FFA_FEATURES by introducing rxtx_min_pages private data field
- add ffa_remove and ffa_unbind functions
- improve how the driver behaves when bus discovery is done more than once
v3:
- align the interfaces of the U-Boot FF-A driver with those in the linux FF-A driver
- remove the FF-A helper layer
- make the U-Boot FF-A driver independent from EFI
- provide an optional config that enables copying the driver data to EFI runtime section at ExitBootServices service
- use 64-bit version of FFA_RXTX_MAP, FFA_MSG_SEND_DIRECT_{REQ, RESP}
v2:
- make FF-A bus discoverable using device_{bind, probe} APIs
- remove device tree support
v1:
- introduce FF-A bus driver with device tree support
MAINTAINERS | 8 + doc/arch/arm64.ffa.rst | 238 ++++ 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, 1941 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

Hi Simon,
On Tue, Jun 20, 2023 at 11:27:20AM +0100, Simon Glass wrote:
abdellatif.elkhlifi@arm.com wrote:
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].
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
Changelog:
v13:
- doc minor change: specify in the readme that the user should call ffa_rxtx_unmap() driver operation to unmap the RX/TX buffers on demand.
Are you happy with this commit please ? May I add a Reviewed-by ?
Cheers Abdellatif

Hi Abdellatif,
On Fri, 30 Jun 2023 at 13:49, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
Hi Simon,
On Tue, Jun 20, 2023 at 11:27:20AM +0100, Simon Glass wrote:
abdellatif.elkhlifi@arm.com wrote:
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].
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
Changelog:
v13:
- doc minor change: specify in the readme that the user should call ffa_rxtx_unmap() driver operation to unmap the RX/TX buffers on demand.
Are you happy with this commit please ? May I add a Reviewed-by ?
Sorry I think I did something wrong on the previous reply.
Reviewed-by: Simon Glass sjg@chromium.org
Regards, Simon

Hi Simon,
On Sun, Jul 02, 2023 at 04:44:41PM +0100, Simon Glass wrote:
Hi Abdellatif,
...
Changelog:
v13:
- doc minor change: specify in the readme that the user should call ffa_rxtx_unmap() driver operation to unmap the RX/TX buffers on demand.
Are you happy with this commit please ? May I add a Reviewed-by ?
Sorry I think I did something wrong on the previous reply.
Reviewed-by: Simon Glass sjg@chromium.org
Thanks.
Are you happy with the Sandbox support commit too [1] ?
[1]: arm_ffa: introduce sandbox FF-A support
Cheers Abdellatif

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].
[1]: doc/usage/cmd/armffa.rst
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
---
Changelog: ===============
v12:
* add subcommands argument checks * usage documentation: update command return codes * remove calloc when querying SPs * address nits
v11:
* use U_BOOT_CMD_WITH_SUBCMDS * address nits
v10:
* use the FF-A driver Uclass operations * use uclass_first_device() * address nits
v9:
* remove manual FF-A discovery and use DM * use DM class APIs to probe and interact with the FF-A bus * add doc/usage/cmd/armffa.rst
v8:
* update partition_info_get() second argument to be an SP count * pass NULL device pointer to the FF-A bus discovery and operations
v7:
* adapt do_ffa_dev_list() following the recent update on uclass_first_device/uclass_next_device functions (they return void now) * set armffa command to use 64-bit direct messaging
v4:
* remove pattern data in do_ffa_msg_send_direct_req
v3:
* use the new driver interfaces (partition_info_get, sync_send_receive) in armffa command
v2:
* replace use of ffa_helper_init_device function by ffa_helper_bus_discover
v1:
* introduce armffa command
MAINTAINERS | 2 + cmd/Kconfig | 10 ++ cmd/Makefile | 1 + cmd/armffa.c | 196 +++++++++++++++++++++++++++++++ doc/arch/arm64.ffa.rst | 7 ++ doc/usage/cmd/armffa.rst | 93 +++++++++++++++ doc/usage/index.rst | 1 + drivers/firmware/arm-ffa/Kconfig | 1 + 8 files changed, 311 insertions(+) create mode 100644 cmd/armffa.c create mode 100644 doc/usage/cmd/armffa.rst
diff --git a/MAINTAINERS b/MAINTAINERS index 43603522fd..0d960731cf 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -269,7 +269,9 @@ F: configs/cortina_presidio-asic-pnand_defconfig ARM FF-A M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com S: Maintained +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: include/sandbox_arm_ffa.h diff --git a/cmd/Kconfig b/cmd/Kconfig index 365371fb51..86af7bcc9e 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -934,6 +934,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 6c37521b4e..7d20a85a46 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 0000000000..fa268e9cb9 --- /dev/null +++ b/cmd/armffa.c @@ -0,0 +1,196 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ +#include <common.h> +#include <arm_ffa.h> +#include <command.h> +#include <dm.h> +#include <mapmem.h> +#include <stdlib.h> +#include <asm/io.h> + +/** + * 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 + */ +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 the secure partition information which the 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 the secure partition which the ID 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 + */ +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; + } + + errno = 0; + part_id = strtoul(argv[1], NULL, 16); + + if (errno) { + log_err("Invalid partition ID\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 + */ +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 name %s, dev %p, driver name %s, ops %p\n", + dev->name, + (void *)map_to_sysmem(dev), + dev->driver->name, + (void *)map_to_sysmem(dev->driver->ops)); + + return CMD_RET_SUCCESS; +} + +static char armffa_help_text[] = + "getpart <partition UUID>\n" + " - lists the partition(s) info\n" + "ping <partition ID>\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 4f817f053c..aefd527447 100644 --- a/doc/arch/arm64.ffa.rst +++ b/doc/arch/arm64.ffa.rst @@ -205,6 +205,13 @@ The following features are provided:
- FF-A bus can be compiled and used without EFI
+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 0000000000..3d422686c1 --- /dev/null +++ b/doc/usage/cmd/armffa.rst @@ -0,0 +1,93 @@ +.. SPDX-License-Identifier: GPL-2.0+: + +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 84ef8a9a42..cfd438d88c 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 9200c8028b..a7d5392859 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 select ARM_SMCCC select ARM_SMCCC_FEATURES + imply CMD_ARMFFA select LIB_UUID select DEVRES help

On Fri, 16 Jun 2023 at 16:28, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
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].
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
Changelog:
v12:
- add subcommands argument checks
- usage documentation: update command return codes
- remove calloc when querying SPs
- address nits
v11:
- use U_BOOT_CMD_WITH_SUBCMDS
- address nits
v10:
- use the FF-A driver Uclass operations
- use uclass_first_device()
- address nits
v9:
- remove manual FF-A discovery and use DM
- use DM class APIs to probe and interact with the FF-A bus
- add doc/usage/cmd/armffa.rst
v8:
- update partition_info_get() second argument to be an SP count
- pass NULL device pointer to the FF-A bus discovery and operations
v7:
- adapt do_ffa_dev_list() following the recent update on uclass_first_device/uclass_next_device functions (they return void now)
- set armffa command to use 64-bit direct messaging
v4:
- remove pattern data in do_ffa_msg_send_direct_req
v3:
- use the new driver interfaces (partition_info_get, sync_send_receive) in armffa command
v2:
- replace use of ffa_helper_init_device function by
ffa_helper_bus_discover
v1:
- introduce armffa command
MAINTAINERS | 2 + cmd/Kconfig | 10 ++ cmd/Makefile | 1 + cmd/armffa.c | 196 +++++++++++++++++++++++++++++++ doc/arch/arm64.ffa.rst | 7 ++ doc/usage/cmd/armffa.rst | 93 +++++++++++++++ doc/usage/index.rst | 1 + drivers/firmware/arm-ffa/Kconfig | 1 + 8 files changed, 311 insertions(+) create mode 100644 cmd/armffa.c create mode 100644 doc/usage/cmd/armffa.rst
Reviewed-by: Simon Glass sjg@chromium.org

[...]
diff --git a/MAINTAINERS b/MAINTAINERS index 43603522fd..0d960731cf 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -269,7 +269,9 @@ F: configs/cortina_presidio-asic-pnand_defconfig ARM FF-A M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com S: Maintained +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: include/sandbox_arm_ffa.h diff --git a/cmd/Kconfig b/cmd/Kconfig index 365371fb51..86af7bcc9e 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -934,6 +934,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 6c37521b4e..7d20a85a46 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 0000000000..fa268e9cb9 --- /dev/null +++ b/cmd/armffa.c @@ -0,0 +1,196 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
- Authors:
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include <common.h> +#include <arm_ffa.h> +#include <command.h> +#include <dm.h> +#include <mapmem.h> +#include <stdlib.h> +#include <asm/io.h>
+/**
- 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
- */
+int ffa_get_dev(struct udevice **devp)
Why isn't this static? If it's used in another file we need to move it to a library suitable file.
+{
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 the secure partition information which the 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 the secure partition which the ID is provided
s/which/ in which/
- 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
- */
+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;
}
errno = 0;
part_id = strtoul(argv[1], NULL, 16);
if (errno) {
Is errno used in strtoul? Does FF-A have any limits regarding the partition id? If yes, it would be saner to check against that.
log_err("Invalid partition ID\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]);
I am not sure I understand why we print it like this.
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
- */
+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 name %s, dev %p, driver name %s, ops %p\n",
dev->name,
(void *)map_to_sysmem(dev),
dev->driver->name,
(void *)map_to_sysmem(dev->driver->ops));
Isn't it more useful to print the physical address map_to_sysmem() retuns?
return CMD_RET_SUCCESS;
+}
Thanks /Ilias

Hi Ilias,
On Tue, Jun 20, 2023 at 05:25:51PM +0300, Ilias Apalodimas wrote:
[...]
+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;
}
errno = 0;
part_id = strtoul(argv[1], NULL, 16);
if (errno) {
Is errno used in strtoul?
Yes, please refer to [1].
[1]: https://man7.org/linux/man-pages/man3/strtoul.3.html
Does FF-A have any limits regarding the partition id? If yes, it would be saner to check against that.
The only value that would be invalid is 0. I'll add a check for that in v14.
log_err("Invalid partition ID\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]);
I am not sure I understand why we print it like this.
We would like to show the data received from secure world and in which order.
example:
corstone1000# armffa ping 0x8003 SP response: [LSB] fffffffe 0 0 0 0
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
- */
+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 name %s, dev %p, driver name %s, ops %p\n",
dev->name,
(void *)map_to_sysmem(dev),
dev->driver->name,
(void *)map_to_sysmem(dev->driver->ops));
Isn't it more useful to print the physical address map_to_sysmem() retuns?
That's what map_to_sysmem() does, it returns a physical address and it's shown in the log.
Cheers, Abdellatif

On Mon, 3 Jul 2023 at 12:55, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
Hi Ilias,
On Tue, Jun 20, 2023 at 05:25:51PM +0300, Ilias Apalodimas wrote:
[...]
+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;
}
errno = 0;
part_id = strtoul(argv[1], NULL, 16);
if (errno) {
Is errno used in strtoul?
Yes, please refer to [1].
that's what the libc version does. Can you check the u-boot version?
Does FF-A have any limits regarding the partition id? If yes, it would be saner to check against that.
The only value that would be invalid is 0. I'll add a check for that in v14.
log_err("Invalid partition ID\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]);
I am not sure I understand why we print it like this.
We would like to show the data received from secure world and in which order.
example: corstone1000# armffa ping 0x8003 SP response: [LSB] fffffffe 0 0 0 0
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
- */
+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 name %s, dev %p, driver name %s, ops %p\n",
dev->name,
(void *)map_to_sysmem(dev),
dev->driver->name,
(void *)map_to_sysmem(dev->driver->ops));
Isn't it more useful to print the physical address map_to_sysmem() retuns?
That's what map_to_sysmem() does, it returns a physical address and it's shown in the log.
I dont have access to u-boot source right, but why do you need all these void * casts then?
Thanks /Ilias
Cheers, Abdellatif

Hi Ilias,
On Mon, Jul 03, 2023 at 12:59:58PM +0300, Ilias Apalodimas wrote:
[...]
+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;
}
errno = 0;
part_id = strtoul(argv[1], NULL, 16);
if (errno) {
Is errno used in strtoul?
Yes, please refer to [1].
that's what the libc version does. Can you check the u-boot version?
Short answer: errno not used in strtoul() an I'm gonna remove the errno check.
More details:
strtoul() is defined as simple_strtoul() in strto.c errno variable is not set by simple_strtoul() or its callees. errno.h is included by strto.c to access the error codes.
...
+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 name %s, dev %p, driver name %s, ops %p\n",
dev->name,
(void *)map_to_sysmem(dev),
dev->driver->name,
(void *)map_to_sysmem(dev->driver->ops));
Isn't it more useful to print the physical address map_to_sysmem() retuns?
That's what map_to_sysmem() does, it returns a physical address and it's shown in the log.
I dont have access to u-boot source right, but why do you need all these void * casts then?
Because map_to_sysmem() returns an 'phys_addr_t' (aka 'long long unsigned int') . However, %p expects 'void *'.
Compilation warning:
cmd/armffa.c:181:18: warning: format ‘%p’ expects argument of type ‘void *’, but argument 3 has type ‘phys_addr_t’ {aka ‘long long unsigned int’} [8; ;https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html#index-Wformat=-Wform...;;] 181 | log_info("device name %s, dev %p, driver name %s, ops %p\n", | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 182 | dev->name, 183 | map_to_sysmem(dev), | ~~~~~~~~~~~~~~~~~~ | | | phys_addr_t {aka long long unsigned int}
Cheers, Abdellatif

Hi Abdellatif,
On Mon, 3 Jul 2023 at 13:09, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
Hi Ilias,
On Mon, Jul 03, 2023 at 12:59:58PM +0300, Ilias Apalodimas wrote:
[...]
+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;
}
errno = 0;
part_id = strtoul(argv[1], NULL, 16);
if (errno) {
Is errno used in strtoul?
Yes, please refer to [1].
that's what the libc version does. Can you check the u-boot version?
Short answer: errno not used in strtoul() an I'm gonna remove the errno check.
More details:
strtoul() is defined as simple_strtoul() in strto.c errno variable is not set by simple_strtoul() or its callees. errno.h is included by strto.c to access the error codes.
...
+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 name %s, dev %p, driver name %s, ops %p\n",
dev->name,
(void *)map_to_sysmem(dev),
dev->driver->name,
(void *)map_to_sysmem(dev->driver->ops));
Isn't it more useful to print the physical address map_to_sysmem() retuns?
That's what map_to_sysmem() does, it returns a physical address and it's shown in the log.
I dont have access to u-boot source right, but why do you need all these void * casts then?
Because map_to_sysmem() returns an 'phys_addr_t' (aka 'long long unsigned int') . However, %p expects 'void *'.
Compilation warning:
cmd/armffa.c:181:18: warning: format ‘%p’ expects argument of type ‘void *’, but argument 3 has type ‘phys_addr_t’ {aka ‘long long unsigned int’} [8; ;https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html#index-Wformat=-Wform...;;] 181 | log_info("device name %s, dev %p, driver name %s, ops %p\n", | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 182 | dev->name, 183 | map_to_sysmem(dev), | ~~~~~~~~~~~~~~~~~~ | | | phys_addr_t {aka long long unsigned int}
You should be using %lx with map_to_sysmen() since it returns a . Use %p for pointers.
In general in U-Boot we use addresses (ulong) rather than pointers. Unfortunately the phys_addr_t type can be larger than the word size. I suggest creating a printf prefix for that type. See the top of blk.h for an example of how to do it. Perhaps in a follow-up patch?
Regards, Simon

Hi Simon, Ilias,
On Mon, Jul 03, 2023 at 02:30:51PM +0100, Simon Glass wrote: ...
log_info("device name %s, dev %p, driver name %s, ops %p\n",
dev->name,
(void *)map_to_sysmem(dev),
dev->driver->name,
(void *)map_to_sysmem(dev->driver->ops));
Isn't it more useful to print the physical address map_to_sysmem() retuns?
That's what map_to_sysmem() does, it returns a physical address and it's shown in the log.
I dont have access to u-boot source right, but why do you need all these void * casts then?
Because map_to_sysmem() returns an 'phys_addr_t' (aka 'long long unsigned int') . However, %p expects 'void *'.
Compilation warning:
cmd/armffa.c:181:18: warning: format ‘%p’ expects argument of type ‘void *’, but argument 3 has type ‘phys_addr_t’ {aka ‘long long unsigned int’} [8; ;https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html#index-Wformat=-Wform...;;] 181 | log_info("device name %s, dev %p, driver name %s, ops %p\n", | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 182 | dev->name, 183 | map_to_sysmem(dev), | ~~~~~~~~~~~~~~~~~~ | | | phys_addr_t {aka long long unsigned int}
You should be using %lx with map_to_sysmen() since it returns a . Use %p for pointers.
In general in U-Boot we use addresses (ulong) rather than pointers. Unfortunately the phys_addr_t type can be larger than the word size. I suggest creating a printf prefix for that type. See the top of blk.h for an example of how to do it. Perhaps in a follow-up patch?
Sounds good. I'm gonna add it in a generic way under include/log.h if that makes sense:
#ifdef CONFIG_PHYS_64BIT #define PhysAddrLength "ll" #else #define PhysAddrLength "" #endif #define PHYS_ADDR_LN "%" PhysAddrLength "x" #define PHYS_ADDR_LNU "%" PhysAddrLength "u" #endif
Note: In a 32-bit platform phys_addr_t is "unsigned int", in a 64-bit platform it is "long long unsigned int"
Then using it this way:
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));
Cheers Abdellatif

On Mon, 3 Jul 2023 at 16:53, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
Hi Simon, Ilias,
On Mon, Jul 03, 2023 at 02:30:51PM +0100, Simon Glass wrote: ...
> + log_info("device name %s, dev %p, driver name %s, ops %p\n", > + dev->name, > + (void *)map_to_sysmem(dev), > + dev->driver->name, > + (void *)map_to_sysmem(dev->driver->ops));
Isn't it more useful to print the physical address map_to_sysmem() retuns?
That's what map_to_sysmem() does, it returns a physical address and it's shown in the log.
I dont have access to u-boot source right, but why do you need all these void * casts then?
Because map_to_sysmem() returns an 'phys_addr_t' (aka 'long long unsigned int') . However, %p expects 'void *'.
Compilation warning:
cmd/armffa.c:181:18: warning: format ‘%p’ expects argument of type ‘void *’, but argument 3 has type ‘phys_addr_t’ {aka ‘long long unsigned int’} [8; ;https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html#index-Wformat=-Wform...;;] 181 | log_info("device name %s, dev %p, driver name %s, ops %p\n", | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 182 | dev->name, 183 | map_to_sysmem(dev), | ~~~~~~~~~~~~~~~~~~ | | | phys_addr_t {aka long long unsigned int}
You should be using %lx with map_to_sysmen() since it returns a . Use %p for pointers.
In general in U-Boot we use addresses (ulong) rather than pointers. Unfortunately the phys_addr_t type can be larger than the word size. I suggest creating a printf prefix for that type. See the top of blk.h for an example of how to do it. Perhaps in a follow-up patch?
Sounds good. I'm gonna add it in a generic way under include/log.h if that makes sense:
#ifdef CONFIG_PHYS_64BIT #define PhysAddrLength "ll" #else #define PhysAddrLength "" #endif #define PHYS_ADDR_LN "%" PhysAddrLength "x" #define PHYS_ADDR_LNU "%" PhysAddrLength "u" #endif
Note: In a 32-bit platform phys_addr_t is "unsigned int", in a 64-bit platform it is "long long unsigned int"
Then using it this way:
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));
LGTM thanks
Cheers Abdellatif

Adding support for Arm FF-A v1.0 (Arm Firmware Framework for Armv8-A) [A].
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.
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.
=> dm tree
Class Index Probed Driver Name ----------------------------------------------------------- ... firmware 0 [ + ] psci |-- psci ffa 0 [ ] arm_ffa | `-- arm_ffa ...
Clients are able to probe then use the FF-A bus by calling the DM class searching APIs (e.g: uclass_first_device).
This implementation of the specification provides support for Aarch64.
The FF-A driver uses the SMC ABIs defined by the FF-A specification to:
- Discover the presence of secure partitions (SPs) of interest - Access an SP's service through communication protocols (e.g: EFI MM communication protocol)
The FF-A support provides the following features:
- Being generic by design and can be used by any Arm 64-bit platform - FF-A support can be compiled and used without EFI - Support for SMCCCv1.2 x0-x17 registers - Support for SMC32 calling convention - Support for 32-bit and 64-bit FF-A direct messaging - Support for FF-A MM communication (compatible with EFI boot time) - Enabling FF-A and MM communication in Corstone1000 platform as a use case - 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. - Sandbox FF-A test cases. - A new command called armffa is provided as an example of how to access the FF-A bus
For more details about the FF-A support please refer to [B] and refer to [C] for how to use the armffa command.
Please find at [D] an example of the expected boot logs when enabling FF-A support for a platform. In this example the platform is Corstone1000. But it can be any Arm 64-bit platform.
Changelog of changes: ===========================
v14:
Simon:
* add to log.h a generic physical address formatting
Ilias:
* armffa command: in do_ffa_ping() reject the SP ID if it's 0 * MM comms: drop truncating var_payload->size when using FF-A * MM comms: map the MM SP return codes to errnos * address nits
v13: [13]
Ilias: * remove FF-A and Optee ifdefs in efi_variable_tee.c * doc minor change: specify in the readme that the user should call ffa_rxtx_unmap() driver operation to unmap the RX/TX buffers on demand.
v12: [12]
* remove the global variable (dscvry_info), use uc_priv instead * replace dscvry_info.invoke_ffa_fn() with a weak invoke_ffa_fn (user drivers can override it) * improve FFA_PARTITION_INFO_GET implementation (clients no longer need to calloc a buffer) * remove reparenting by making the sandbox emulator parent of the FF-A device in the DT * improve argument checks for the armffa command * address nits
v11: [11]
* move ffa_try_discovery() from the uclass to the Arm FF-A driver * rename ffa_try_discovery() to arm_ffa_discover() * add arm_ prefix to the Arm FF-A driver functions * use U_BOOT_CMD_WITH_SUBCMDS for armffa command * store the sandbox emulator pointer in the FF-A device uc_priv (struct ffa_priv) * set the emulator as parent of the sandbox FF-A device * rename select_ffa_mm_comms() to select_mm_comms() * improve the logic of MM transport selection in mm_communicate() * use ut_asserteq_mem() in uuid_str_to_le_bin test case * address nits
v10: [10]
* provide the FF-A driver operations through the Uclass (arm-ffa-uclass.c) * move the generic FF-A methods to the Uclass * keep Arm specific methods in the Arm driver (arm-ffa.c renamed from core.c) * split the FF-A sandbox support into an emulator (ffa-emul-uclass.c) and a driver (sandbox_ffa.c) * use the FF-A driver Uclass operations by clients (armffa command, tests, MM comms) * use uclass_first_device to search and probe the FF-A device (whether it is on Arm or on sandbox) * address nits
v9: [9]
* integrate the FF-A bus discovery in the DM and use ARM_SMCCC_FEATURES for binding * align FF-A sandbox driver with FF-A discovery through DM * use DM class APIs to probe and interact with the FF-A bus (in FF-A MM comms, armffa command, sandbox tests) * add documentation for the armffa command: doc/usage/cmd/armffa.rst * introduce testcase for uuid_str_to_le_bin
v8: [8]
* pass the FF-A bus device to the bus operations * isolate the compilation choices between FF-A and OP-TEE * drop OP-TEE configs from Corstone-1000 defconfig * make ffa_get_partitions_info() second argument to be an SP count in both modes
v7: [7]
* add support for 32-bit direct messaging (now we have 32-bit and 64-bit support) * set the MM door bell event to use 64-bit direct messaging * issue a compile time error when one of these macros are not found : FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_OFFSET, FFA_SHARED_MM_BUFFER_ADDR * make uuid_str_to_le_bin() implementation similar to uuid_str_to_bin() by using same APIs
v6: [6]
* remove clearing x0-x17 registers after SMC calls * drop use of EFI runtime support for FF-A (We decided with Linaro to add this later) * drop discovery from initcalls (discovery will be on demand by FF-A users) * add FF-A runtime discovery at MM communication level * update the documentation and move it to doc/arch/arm64.ffa.rst
v5: [5]
* move changelogs in each commit to the changes section
v4: [4]
* add FF-A support README (doc/README.ffa.drv) * improving error handling by mapping the FF-A errors to standard errors and logs * replacing panics with an error log * align sandbox driver and tests with the new FF-A driver interfaces and new way of error handling * use the new FF-A driver interfaces for MM communication * discover MM partitions at runtime * copy FF-A driver private data to EFI runtime section at ExitBootServices() * moving the FF-A driver work to drivers/firmware/arm-ffa * improving features discovery in FFA_FEATURES * add remove/unbind functions to the FF-A core device * improve how the driver behaves when bus discovery is done more than once * move clearing x0-x17 registers code into a new macro like done in the linux kernel * enable EFI MM communication for the Corstone1000 platform
v3: [3]
* port x0-x17 registers support from linux kernel as defined by SMCCCv1.2 * align the interfaces of the u-boot FF-A driver with those in the linux FF-A driver * remove the FF-A helper layer * make the u-boot FF-A driver independent from EFI * provide an optional config that enables copying the driver data to EFI runtime section at ExitBootServices service * use 64-bit version of FFA_RXTX_MAP, FFA_MSG_SEND_DIRECT_{REQ, RESP} * update armffa command with the new driver interfaces
v2 [2]:
* make FF-A bus discoverable using device_{bind, probe} APIs * remove device tree support
v1 [1]:
* introduce FF-A bus driver with device tree support * introduce armffa command * introduce FF-A Sandbox driver * add FF-A Sandbox test cases * introduce FF-A MM communication
Cheers, Abdellatif
List of previous patches:
[1]: https://lore.kernel.org/all/20220329151659.16894-1-abdellatif.elkhlifi@arm.c... [2]: https://lore.kernel.org/all/20220415122803.16666-1-abdellatif.elkhlifi@arm.c... [3]: https://lore.kernel.org/all/20220801172053.20163-1-abdellatif.elkhlifi@arm.c... [4]: https://lore.kernel.org/all/20220926101723.9965-1-abdellatif.elkhlifi@arm.co... [5]: https://lore.kernel.org/all/20220926140827.15125-1-abdellatif.elkhlifi@arm.c... [6]: https://lore.kernel.org/all/20221013103857.614-1-abdellatif.elkhlifi@arm.com... [7]: https://lore.kernel.org/all/20221107192055.21669-1-abdellatif.elkhlifi@arm.c... [8]: https://lore.kernel.org/all/20221122131751.22747-1-abdellatif.elkhlifi@arm.c... [9]: https://lore.kernel.org/all/20230310141016.137986-1-abdellatif.elkhlifi@arm.... [10]: https://lore.kernel.org/all/20230328161157.219375-1-abdellatif.elkhlifi@arm.... [11]: https://lore.kernel.org/all/20230412094245.44674-1-abdellatif.elkhlifi@arm.c... [12]: https://lore.kernel.org/all/20230512121044.111574-1-abdellatif.elkhlifi@arm.... [13]: https://lore.kernel.org/all/20230616152817.319869-1-abdellatif.elkhlifi@arm....
More details:
[A]: https://developer.arm.com/documentation/den0077/latest/ [B]: doc/arch/arm64.ffa.rst [C]: doc/usage/cmd/armffa.rst [D]: example of boot logs when enabling FF-A
``` 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 ```
Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Rob Herring robh@kernel.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Achin Gupta achin.gupta@arm.com Cc: Drew Reed Drew.Reed@arm.com Cc: Xueliang Zhong Xueliang.Zhong@arm.com
Abdellatif El Khlifi (11): arm64: smccc: add support for SMCCCv1.2 x0-x17 registers lib: uuid: introduce uuid_str_to_le_bin function lib: uuid: introduce testcase for uuid_str_to_le_bin arm_ffa: introduce Arm FF-A support log: select physical address formatting in a generic way arm_ffa: introduce armffa command arm_ffa: introduce sandbox FF-A support arm_ffa: introduce sandbox test cases for UCLASS_FFA arm_ffa: introduce armffa command Sandbox test arm_ffa: efi: introduce FF-A MM communication arm_ffa: efi: corstone1000: enable MM communication
MAINTAINERS | 18 + arch/arm/cpu/armv8/smccc-call.S | 57 +- arch/arm/lib/asm-offsets.c | 16 + 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 ++ cmd/Kconfig | 10 + cmd/Makefile | 1 + cmd/armffa.c | 194 +++ configs/corstone1000_defconfig | 1 + configs/sandbox64_defconfig | 1 + configs/sandbox_defconfig | 1 + doc/arch/arm64.ffa.rst | 261 ++++ doc/arch/index.rst | 1 + doc/arch/sandbox/sandbox.rst | 1 + doc/usage/cmd/armffa.rst | 93 ++ doc/usage/index.rst | 1 + drivers/Makefile | 1 + drivers/firmware/Kconfig | 1 + drivers/firmware/arm-ffa/Kconfig | 42 + drivers/firmware/arm-ffa/Makefile | 16 + drivers/firmware/arm-ffa/arm-ffa-uclass.c | 1065 +++++++++++++++++ drivers/firmware/arm-ffa/arm-ffa.c | 104 ++ drivers/firmware/arm-ffa/ffa-emul-uclass.c | 720 +++++++++++ drivers/firmware/arm-ffa/sandbox_ffa.c | 110 ++ include/arm_ffa.h | 213 ++++ include/arm_ffa_priv.h | 246 ++++ include/configs/corstone1000.h | 15 +- include/dm/uclass-id.h | 7 + include/linux/arm-smccc.h | 45 + include/log.h | 8 + include/mm_communication.h | 13 + include/uuid.h | 15 + lib/efi_loader/Kconfig | 16 +- lib/efi_loader/efi_variable_tee.c | 272 ++++- lib/uuid.c | 48 + test/cmd/Makefile | 2 + test/cmd/armffa.c | 33 + test/dm/Makefile | 3 +- test/dm/ffa.c | 261 ++++ test/lib/Makefile | 1 + test/lib/uuid.c | 41 + 43 files changed, 4153 insertions(+), 11 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 cmd/armffa.c create mode 100644 doc/arch/arm64.ffa.rst create mode 100644 doc/usage/cmd/armffa.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/ffa-emul-uclass.c create mode 100644 drivers/firmware/arm-ffa/sandbox_ffa.c create mode 100644 include/arm_ffa.h create mode 100644 include/arm_ffa_priv.h create mode 100644 test/cmd/armffa.c create mode 100644 test/dm/ffa.c create mode 100644 test/lib/uuid.c

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 abdellatif.elkhlifi@arm.com Reviewed-by: Ilias Apalodimas ilias.apalodimas@linaro.org Reviewed-by: Jens Wiklander jens.wiklander@linaro.org Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com
---
Changelog: ===============
v9:
* update the copyright string
v7:
* improve indentation of ARM_SMCCC_1_2_REGS_Xn_OFFS
v4:
* rename the commit title and improve description new commit title: the current
v3:
* port x0-x17 registers support from linux kernel as defined by SMCCCv1.2 commit title: arm64: smccc: add Xn registers support used by SMC calls
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 dc92b28777..93f66d3366 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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com +*/ #include <linux/linkage.h> #include <linux/arm-smccc.h> #include <generated/asm-offsets.h> @@ -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 6de0ce9152..181a8ac4c2 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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> @@ -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 e1d09884a1..f44e9e8f93 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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */ #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

convert UUID string to little endian binary data
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v9:
* add a full function prototype description in uuid.h
v8:
* use simple_strtoull() in uuid_str_to_le_bin() to support 32-bit platforms
v7:
* rename be_uuid_str_to_le_bin() to uuid_str_to_le_bin() * make uuid_str_to_le_bin() implementation similar to uuid_str_to_bin() by using same APIs
v4:
* rename ffa_uuid_str_to_bin to be_uuid_str_to_le_bin and put in a standalone commit (the current)
v3:
* introduce ffa_uuid_str_to_bin (provided by arm_ffa: introduce Arm FF-A low-level driver)
include/uuid.h | 15 +++++++++++++++ lib/uuid.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+)
diff --git a/include/uuid.h b/include/uuid.h index 4a4883d3b5..89b93e642b 100644 --- a/include/uuid.h +++ b/include/uuid.h @@ -2,6 +2,10 @@ /* * Copyright (C) 2014 Samsung Electronics * Przemyslaw Marczak p.marczak@samsung.com + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */ #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 96e1af3c8b..45f325d964 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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> @@ -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. *

provide a test case
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com
---
Changelog: ===============
v11:
* use ut_asserteq_mem()
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 d724b64673..a1e34ab63a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1634,3 +1634,8 @@ S: Maintained F: arch/arm/dts/ls1021a-twr-u-boot.dtsi F: drivers/crypto/fsl/ F: include/fsl_sec.h + +UUID testing +M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com +S: Maintained +F: test/lib/uuid.c diff --git a/test/lib/Makefile b/test/lib/Makefile index e0bd9e04e8..e75a263e6a 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 0000000000..e24331a136 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <uuid.h> +#include <test/lib.h> +#include <test/test.h> +#include <test/ut.h> + +/* 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);

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 abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
---
Changelog: ===============
v13:
* doc minor change: specify in the readme that the user should call ffa_rxtx_unmap() driver operation to unmap the RX/TX buffers on demand.
v12:
* remove dscvry_info * replace dscvry_info.invoke_ffa_fn() with a weak invoke_ffa_fn (user drivers can override it) * improve FFA_PARTITION_INFO_GET implementation (clients no longer need to calloc a buffer) * address nits
v11:
* move ffa_try_discovery() from the uclass to the Arm FF-A driver * rename ffa_try_discovery() to arm_ffa_discover() * pass dev as an argument of arm_ffa_discover() * add arm_ prefix to the Arm FF-A driver functions * add emul field in struct ffa_discovery_info * address nits
v10:
* provide the driver operations through the Uclass * move the generic FF-A methods to the Uclass * keep Arm specific methods in the Arm driver (arm-ffa.c) * rename core.c to arm-ffa.c * address nits
v9:
* integrate the FF-A bus discovery in the DM and use ARM_SMCCC_FEATURES for binding
v8:
* make ffa_get_partitions_info() second argument to be an SP count in both modes * update ffa_bus_prvdata_get() to return a pointer rather than a pointer address * remove packing from ffa_partition_info and ffa_send_direct_data structures * pass the FF-A bus device to the bus operations
v7:
* add support for 32-bit direct messaging * rename be_uuid_str_to_le_bin() to uuid_str_to_le_bin() * improve the declaration of error handling mapping * stating in doc/arch/arm64.ffa.rst that EFI runtime is not supported
v6:
* drop use of EFI runtime support (We decided with Linaro to add this later) * drop discovery from initcalls (discovery will be on demand by FF-A users) * set the alignment of the RX/TX buffers to the larger translation granule size * move FF-A RX/TX buffers unmapping at ExitBootServices() to a separate commit * update the documentation and move it to doc/arch/arm64.ffa.rst
v4:
* add doc/README.ffa.drv * moving the FF-A driver work to drivers/firmware/arm-ffa * use less #ifdefs in lib/efi_loader/efi_boottime.c and replace #if defined by #if CONFIG_IS_ENABLED * improving error handling by mapping the FF-A errors to standard errors and logs * replacing panics with an error log and returning an error code * improving features discovery in FFA_FEATURES by introducing rxtx_min_pages private data field * add ffa_remove and ffa_unbind functions * improve how the driver behaves when bus discovery is done more than once
v3:
* align the interfaces of the U-Boot FF-A driver with those in the linux FF-A driver * remove the FF-A helper layer * make the U-Boot FF-A driver independent from EFI * provide an optional config that enables copying the driver data to EFI runtime section at ExitBootServices service * use 64-bit version of FFA_RXTX_MAP, FFA_MSG_SEND_DIRECT_{REQ, RESP}
v2:
* make FF-A bus discoverable using device_{bind, probe} APIs * remove device tree support
v1:
* introduce FF-A bus driver with device tree support
MAINTAINERS | 8 + doc/arch/arm64.ffa.rst | 238 ++++ 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, 1941 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 a1e34ab63a..b3a16ed802 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 abdellatif.elkhlifi@arm.com +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 sbabic@denx.de M: Fabio Estevam festevam@gmail.com diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst new file mode 100644 index 0000000000..4f817f053c --- /dev/null +++ b/doc/arch/arm64.ffa.rst @@ -0,0 +1,238 @@ +.. 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(). +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: + + - 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 abdellatif.elkhlifi@arm.com + +.. _`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 b8da4b8c8e..2f916f4026 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 78dcf62f76..5044f45253 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 eae1c8ddc9..8789b1ea14 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 0000000000..9200c8028b --- /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 0000000000..11b1766285 --- /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 open-source-office@arm.com +# +# Authors: +# Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + +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 0000000000..ffa9d81fa7 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ +#include <common.h> +#include <arm_ffa.h> +#include <arm_ffa_priv.h> +#include <dm.h> +#include <log.h> +#include <malloc.h> +#include <string.h> +#include <uuid.h> +#include <asm/global_data.h> +#include <dm/device-internal.h> +#include <dm/devres.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> + +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 0000000000..68df75bd9e --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <arm_ffa.h> +#include <arm_ffa_priv.h> +#include <dm.h> +#include <log.h> +#include <asm/global_data.h> +#include <dm/device-internal.h> +#include <linux/errno.h> + +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 0000000000..4338f9c9b1 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#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 0000000000..db9b1be995 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __ARM_FFA_H +#define __ARM_FFA_H + +#include <linux/printk.h> + +/* + * 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 0000000000..d564c33c64 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __ARM_FFA_PRV_H +#define __ARM_FFA_PRV_H + +#include <mapmem.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> + +/* 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 <linux/arm-smccc.h> +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 307ad6931c..3c6af2e3d2 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -4,6 +4,11 @@ * * (C) Copyright 2012 * Pavel Herrmann morpheus.ibis@gmail.com + * + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#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 */

sets the log formatting according to the platform (64-bit vs 32-bit)
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Simon Glass sjg@chromium.org --- include/log.h | 8 ++++++++ 1 file changed, 8 insertions(+)
diff --git a/include/log.h b/include/log.h index 3bab40b617..689cef905b 100644 --- a/include/log.h +++ b/include/log.h @@ -686,4 +686,12 @@ static inline int log_get_default_format(void) (IS_ENABLED(CONFIG_LOGF_FUNC) ? BIT(LOGF_FUNC) : 0); }
+/* 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" +#define PHYS_ADDR_LNU "%" PhysAddrLength "u" #endif

Hi Abdellatif,
On Fri, 7 Jul 2023 at 15:44, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
sets the log formatting according to the platform (64-bit vs 32-bit)
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Simon Glass sjg@chromium.org
include/log.h | 8 ++++++++ 1 file changed, 8 insertions(+)
diff --git a/include/log.h b/include/log.h index 3bab40b617..689cef905b 100644 --- a/include/log.h +++ b/include/log.h @@ -686,4 +686,12 @@ static inline int log_get_default_format(void) (IS_ENABLED(CONFIG_LOGF_FUNC) ? BIT(LOGF_FUNC) : 0); }
+/* Select the right physical address formatting according to the platform */ +#ifdef CONFIG_PHYS_64BIT +#define PhysAddrLength "ll" +#else +#define PhysAddrLength ""
Shouldn't this be "l" ? We normally use ulong for addresses.
+#endif +#define PHYS_ADDR_LN "%" PhysAddrLength "x" +#define PHYS_ADDR_LNU "%" PhysAddrLength "u"
#endif
2.25.1
Regards, Simon

Hi Simon,
On Fri, Jul 07, 2023 at 06:34:14PM +0100, Simon Glass wrote:
Hi Abdellatif,
On Fri, 7 Jul 2023 at 15:44, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
sets the log formatting according to the platform (64-bit vs 32-bit)
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Simon Glass sjg@chromium.org
include/log.h | 8 ++++++++ 1 file changed, 8 insertions(+)
diff --git a/include/log.h b/include/log.h index 3bab40b617..689cef905b 100644 --- a/include/log.h +++ b/include/log.h @@ -686,4 +686,12 @@ static inline int log_get_default_format(void) (IS_ENABLED(CONFIG_LOGF_FUNC) ? BIT(LOGF_FUNC) : 0); }
+/* Select the right physical address formatting according to the platform */ +#ifdef CONFIG_PHYS_64BIT +#define PhysAddrLength "ll" +#else +#define PhysAddrLength ""
Shouldn't this be "l" ? We normally use ulong for addresses.
The "ll" is needed by many architectures who declare "phys_addr_t" as "unsigned long long"
Example of architetures doing that: arm, powerpc, ...
Also mips and sandbox use "u64" for "phys_addr_t" , ( "u64" is an "unsigned long long").
Note: When using an "l" for arm, the compiler shows this warning:
cmd/armffa.c:174:18: warning: format ‘%lx’ expects argument of type ‘long unsigned int’, but argument 3 has type ‘phys_addr_t’ {aka ‘long long unsign ed int’} [8;;https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html#index-Wformat=-Wform...;;] 174 | log_info("device %s, addr " PHYS_ADDR_LN ", driver %s, ops " PHYS_ADDR_LN "\n", | ^~~~~~~~~~~~~~~~~~ 175 | dev->name, 176 | map_to_sysmem(dev), | ~~~~~~~~~~~~~~~~~~ | | | phys_addr_t {aka long long unsigned int}
Cheers, Abdellatif

Hi Abdellatif,
On Mon, 10 Jul 2023 at 06:15, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
Hi Simon,
On Fri, Jul 07, 2023 at 06:34:14PM +0100, Simon Glass wrote:
Hi Abdellatif,
On Fri, 7 Jul 2023 at 15:44, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
sets the log formatting according to the platform (64-bit vs 32-bit)
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Simon Glass sjg@chromium.org
include/log.h | 8 ++++++++ 1 file changed, 8 insertions(+)
diff --git a/include/log.h b/include/log.h index 3bab40b617..689cef905b 100644 --- a/include/log.h +++ b/include/log.h @@ -686,4 +686,12 @@ static inline int log_get_default_format(void) (IS_ENABLED(CONFIG_LOGF_FUNC) ? BIT(LOGF_FUNC) : 0); }
+/* Select the right physical address formatting according to the platform */ +#ifdef CONFIG_PHYS_64BIT +#define PhysAddrLength "ll" +#else +#define PhysAddrLength ""
Shouldn't this be "l" ? We normally use ulong for addresses.
The "ll" is needed by many architectures who declare "phys_addr_t" as "unsigned long long"
Example of architetures doing that: arm, powerpc, ...
Also mips and sandbox use "u64" for "phys_addr_t" , ( "u64" is an "unsigned long long").
Yes, the ll looks right. I was referring to the "" line.
Note: When using an "l" for arm, the compiler shows this warning:
cmd/armffa.c:174:18: warning: format ‘%lx’ expects argument of type ‘long unsigned int’, but argument 3 has type ‘phys_addr_t’ {aka ‘long long unsign ed int’} [8;;https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html#index-Wformat=-Wform...;;] 174 | log_info("device %s, addr " PHYS_ADDR_LN ", driver %s, ops " PHYS_ADDR_LN "\n", | ^~~~~~~~~~~~~~~~~~ 175 | dev->name, 176 | map_to_sysmem(dev), | ~~~~~~~~~~~~~~~~~~ | | | phys_addr_t {aka long long unsigned int}
Hmm I was hoping that we could use ll for 64-bit and l for 32-bit and that this could be a generic header for all archs.
Can we get your series in as is and deal with this separately. I was intending for this to be a follow-up patch.
Regards, Simon

Hi Simon,
On Mon, Jul 10, 2023 at 08:17:41AM -0600, Simon Glass wrote:
Hi Abdellatif,
On Mon, 10 Jul 2023 at 06:15, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
Hi Simon,
On Fri, Jul 07, 2023 at 06:34:14PM +0100, Simon Glass wrote:
Hi Abdellatif,
On Fri, 7 Jul 2023 at 15:44, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
sets the log formatting according to the platform (64-bit vs 32-bit)
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Simon Glass sjg@chromium.org
include/log.h | 8 ++++++++ 1 file changed, 8 insertions(+)
diff --git a/include/log.h b/include/log.h index 3bab40b617..689cef905b 100644 --- a/include/log.h +++ b/include/log.h @@ -686,4 +686,12 @@ static inline int log_get_default_format(void) (IS_ENABLED(CONFIG_LOGF_FUNC) ? BIT(LOGF_FUNC) : 0); }
+/* Select the right physical address formatting according to the platform */ +#ifdef CONFIG_PHYS_64BIT +#define PhysAddrLength "ll" +#else +#define PhysAddrLength ""
Shouldn't this be "l" ? We normally use ulong for addresses.
The "ll" is needed by many architectures who declare "phys_addr_t" as "unsigned long long"
Example of architetures doing that: arm, powerpc, ...
Also mips and sandbox use "u64" for "phys_addr_t" , ( "u64" is an "unsigned long long").
Yes, the ll looks right. I was referring to the "" line.
Note: When using an "l" for arm, the compiler shows this warning:
cmd/armffa.c:174:18: warning: format ‘%lx’ expects argument of type ‘long unsigned int’, but argument 3 has type ‘phys_addr_t’ {aka ‘long long unsign ed int’} [8;;https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html#index-Wformat=-Wform...;;] 174 | log_info("device %s, addr " PHYS_ADDR_LN ", driver %s, ops " PHYS_ADDR_LN "\n", | ^~~~~~~~~~~~~~~~~~ 175 | dev->name, 176 | map_to_sysmem(dev), | ~~~~~~~~~~~~~~~~~~ | | | phys_addr_t {aka long long unsigned int}
Hmm I was hoping that we could use ll for 64-bit and l for 32-bit and that this could be a generic header for all archs.
The "" was there for 32-bit architecures (e.g: sandbox, mips) who declare "phys_addr_t" as u32 or "unsigned int"
For example, when using sandbox with "l" in place of "", there is a compiler warning:
cmd/armffa.c:174:11: warning: format ‘%lx’ expects argument of type ‘long unsigned int’, but argument 8 has type ‘phys_addr_t’ {aka ‘unsigned int’} - Wformat=] 174 | log_info("device %s, addr " PHYS_ADDR_LN ", driver %s, ops " PHYS_ADDR_LN "\n",
Can we get your series in as is and deal with this separately. I was intending for this to be a follow-up patch.
Sounds good to me. I'll move this commit out of the FF-A patchset and define PHYS_ADDR_LN in armffa.c to be used only there.
Is that ok ?
Cheers, Abdellatif

Hi Abdellatif,
On Mon, 10 Jul 2023 at 08:49, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
Hi Simon,
On Mon, Jul 10, 2023 at 08:17:41AM -0600, Simon Glass wrote:
Hi Abdellatif,
On Mon, 10 Jul 2023 at 06:15, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
Hi Simon,
On Fri, Jul 07, 2023 at 06:34:14PM +0100, Simon Glass wrote:
Hi Abdellatif,
On Fri, 7 Jul 2023 at 15:44, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
sets the log formatting according to the platform (64-bit vs 32-bit)
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Simon Glass sjg@chromium.org
include/log.h | 8 ++++++++ 1 file changed, 8 insertions(+)
diff --git a/include/log.h b/include/log.h index 3bab40b617..689cef905b 100644 --- a/include/log.h +++ b/include/log.h @@ -686,4 +686,12 @@ static inline int log_get_default_format(void) (IS_ENABLED(CONFIG_LOGF_FUNC) ? BIT(LOGF_FUNC) : 0); }
+/* Select the right physical address formatting according to the platform */ +#ifdef CONFIG_PHYS_64BIT +#define PhysAddrLength "ll" +#else +#define PhysAddrLength ""
Shouldn't this be "l" ? We normally use ulong for addresses.
The "ll" is needed by many architectures who declare "phys_addr_t" as "unsigned long long"
Example of architetures doing that: arm, powerpc, ...
Also mips and sandbox use "u64" for "phys_addr_t" , ( "u64" is an "unsigned long long").
Yes, the ll looks right. I was referring to the "" line.
Note: When using an "l" for arm, the compiler shows this warning:
cmd/armffa.c:174:18: warning: format ‘%lx’ expects argument of type ‘long unsigned int’, but argument 3 has type ‘phys_addr_t’ {aka ‘long long unsign ed int’} [8;;https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html#index-Wformat=-Wform...;;] 174 | log_info("device %s, addr " PHYS_ADDR_LN ", driver %s, ops " PHYS_ADDR_LN "\n", | ^~~~~~~~~~~~~~~~~~ 175 | dev->name, 176 | map_to_sysmem(dev), | ~~~~~~~~~~~~~~~~~~ | | | phys_addr_t {aka long long unsigned int}
Hmm I was hoping that we could use ll for 64-bit and l for 32-bit and that this could be a generic header for all archs.
The "" was there for 32-bit architecures (e.g: sandbox, mips) who declare "phys_addr_t" as u32 or "unsigned int"
For example, when using sandbox with "l" in place of "", there is a compiler warning:
cmd/armffa.c:174:11: warning: format ‘%lx’ expects argument of type ‘long unsigned int’, but argument 8 has type ‘phys_addr_t’ {aka ‘unsigned int’} - Wformat=] 174 | log_info("device %s, addr " PHYS_ADDR_LN ", driver %s, ops " PHYS_ADDR_LN "\n",
Can we get your series in as is and deal with this separately. I was intending for this to be a follow-up patch.
Sounds good to me. I'll move this commit out of the FF-A patchset and define PHYS_ADDR_LN in armffa.c to be used only there.
Is that ok ?
It's fine with me.
Regards, Simon

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].
[1]: doc/usage/cmd/armffa.rst
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
---
Changelog: ===============
v14:
Ilias:
* address nits * in do_ffa_ping() reject the SP ID if it's 0 * use PHYS_ADDR_LN in formatting the physical addresses
v12:
* add subcommands argument checks * usage documentation: update command return codes * remove calloc when querying SPs * address nits
v11:
* use U_BOOT_CMD_WITH_SUBCMDS * address nits
v10:
* use the FF-A driver Uclass operations * use uclass_first_device() * address nits
v9:
* remove manual FF-A discovery and use DM * use DM class APIs to probe and interact with the FF-A bus * add doc/usage/cmd/armffa.rst
v8:
* update partition_info_get() second argument to be an SP count * pass NULL device pointer to the FF-A bus discovery and operations
v7:
* adapt do_ffa_dev_list() following the recent update on uclass_first_device/uclass_next_device functions (they return void now) * set armffa command to use 64-bit direct messaging
v4:
* remove pattern data in do_ffa_msg_send_direct_req
v3:
* use the new driver interfaces (partition_info_get, sync_send_receive) in armffa command
v2:
* replace use of ffa_helper_init_device function by ffa_helper_bus_discover
v1:
* introduce armffa command
MAINTAINERS | 2 + cmd/Kconfig | 10 ++ cmd/Makefile | 1 + cmd/armffa.c | 194 +++++++++++++++++++++++++++++++ doc/arch/arm64.ffa.rst | 7 ++ doc/usage/cmd/armffa.rst | 93 +++++++++++++++ doc/usage/index.rst | 1 + drivers/firmware/arm-ffa/Kconfig | 1 + 8 files changed, 309 insertions(+) create mode 100644 cmd/armffa.c create mode 100644 doc/usage/cmd/armffa.rst
diff --git a/MAINTAINERS b/MAINTAINERS index b3a16ed802..ff6a222960 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -269,7 +269,9 @@ F: configs/cortina_presidio-asic-pnand_defconfig ARM FF-A M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com S: Maintained +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: include/sandbox_arm_ffa.h diff --git a/cmd/Kconfig b/cmd/Kconfig index 02e54f1e50..79b4f8367a 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -935,6 +935,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 6c37521b4e..7d20a85a46 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 0000000000..ab0fd54d97 --- /dev/null +++ b/cmd/armffa.c @@ -0,0 +1,194 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ +#include <common.h> +#include <arm_ffa.h> +#include <command.h> +#include <dm.h> +#include <mapmem.h> +#include <stdlib.h> +#include <asm/io.h> + +/** + * 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 <partition UUID>\n" + " - lists the partition(s) info\n" + "ping <partition ID>\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 4f817f053c..aefd527447 100644 --- a/doc/arch/arm64.ffa.rst +++ b/doc/arch/arm64.ffa.rst @@ -205,6 +205,13 @@ The following features are provided:
- FF-A bus can be compiled and used without EFI
+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 0000000000..3d422686c1 --- /dev/null +++ b/doc/usage/cmd/armffa.rst @@ -0,0 +1,93 @@ +.. SPDX-License-Identifier: GPL-2.0+: + +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 388e59f173..e462de2806 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 9200c8028b..a7d5392859 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 select ARM_SMCCC select ARM_SMCCC_FEATURES + imply CMD_ARMFFA select LIB_UUID select DEVRES help

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 abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
---
Changelog: ===============
v12:
* remove reparenting by making the emulator parent of the FF-A device in the DT * add invoke_ffa_fn() * address nits
v11:
* rename ffa_try_discovery() to sandbox_ffa_discover() * rename sandbox_ffa_query_core_state() to sandbox_query_ffa_emul_state() * store the sandbox emulator pointer in the FF-A device uc_priv (struct ffa_priv) * set the emulator as parent of the sandbox FF-A device
v10:
* split the FF-A sandbox support into an emulator and a driver * read FFA_VERSION and FFA_PARTITION_INFO_GET state using sandbox_ffa_query_core_state() * drop CONFIG_SANDBOX_FFA config * address nits
v9: align FF-A sandbox driver with FF-A discovery through DM
v8: update ffa_bus_prvdata_get() to return a pointer rather than a pointer address
v7: state that sandbox driver supports only 64-bit direct messaging
v4: align sandbox driver with the new FF-A driver interfaces and new way of error handling
v1: introduce the sandbox driver
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 ff6a222960..f8948b7635 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -269,12 +269,13 @@ F: configs/cortina_presidio-asic-pnand_defconfig ARM FF-A M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com 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: include/sandbox_arm_ffa.h
ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de diff --git a/arch/sandbox/dts/sandbox.dtsi b/arch/sandbox/dts/sandbox.dtsi index 30a305c4d2..94a08814b8 100644 --- a/arch/sandbox/dts/sandbox.dtsi +++ b/arch/sandbox/dts/sandbox.dtsi @@ -445,6 +445,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 ff9f9222e6..96b5404991 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -1820,6 +1820,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 0000000000..be2790f496 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __SANDBOX_ARM_FFA_H +#define __SANDBOX_ARM_FFA_H + +#include <arm_ffa.h> + +/* + * 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 0000000000..b0881822d7 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __SANDBOX_ARM_FFA_PRV_H +#define __SANDBOX_ARM_FFA_PRV_H + +#include <arm_ffa_priv.h> + +/* 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 98b3e0cda4..72ea3d21ab 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 1ec44d5b33..8269bec879 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -344,3 +344,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 aefd527447..b7c754fa3d 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. @@ -205,6 +208,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 + The armffa command -----------------------------------
diff --git a/doc/arch/sandbox/sandbox.rst b/doc/arch/sandbox/sandbox.rst index 77ca6bc4cc..a3631de749 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 a7d5392859..d75f8b53fd 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 imply CMD_ARMFFA select LIB_UUID select DEVRES @@ -33,5 +33,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 11b1766285..318123a7f4 100644 --- a/drivers/firmware/arm-ffa/Makefile +++ b/drivers/firmware/arm-ffa/Makefile @@ -5,4 +5,12 @@ # Authors: # Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
-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 0000000000..5562bbaac3 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ +#include <common.h> +#include <dm.h> +#include <mapmem.h> +#include <string.h> +#include <asm/global_data.h> +#include <asm/sandbox_arm_ffa.h> +#include <asm/sandbox_arm_ffa_priv.h> +#include <dm/device-internal.h> +#include <dm/lists.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> + +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 4338f9c9b1..0000000000 --- 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 open-source-office@arm.com - * - * Authors: - * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com - */ - -#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 0000000000..ef9491ccea --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ +#include <common.h> +#include <arm_ffa.h> +#include <dm.h> +#include <log.h> +#include <asm/global_data.h> +#include <asm/sandbox_arm_ffa_priv.h> +#include <dm/device-internal.h> +#include <linux/errno.h> + +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 3c6af2e3d2..0432c95c9e 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 */

Hi Abdellatif,
On Fri, 7 Jul 2023 at 15:44, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
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 abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
Changelog:
v12:
- remove reparenting by making the emulator parent of the FF-A device in the DT
- add invoke_ffa_fn()
- address nits
v11:
- rename ffa_try_discovery() to sandbox_ffa_discover()
- rename sandbox_ffa_query_core_state() to sandbox_query_ffa_emul_state()
- store the sandbox emulator pointer in the FF-A device uc_priv (struct ffa_priv)
- set the emulator as parent of the sandbox FF-A device
v10:
- split the FF-A sandbox support into an emulator and a driver
- read FFA_VERSION and FFA_PARTITION_INFO_GET state using sandbox_ffa_query_core_state()
- drop CONFIG_SANDBOX_FFA config
- address nits
v9: align FF-A sandbox driver with FF-A discovery through DM
v8: update ffa_bus_prvdata_get() to return a pointer rather than a pointer address
v7: state that sandbox driver supports only 64-bit direct messaging
v4: align sandbox driver with the new FF-A driver interfaces and new way of error handling
v1: introduce the sandbox driver
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
Reviewed-by: Simon Glass sjg@chromium.org

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 abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
---
Changelog: ===============
v12:
* remove use of dscvry_info * drop use of calloc when querying SPs * address nits
v11:
* drop unmapping test (taken care of by the DM when removing the device) * address nits
v10:
* use the FF-A driver Uclass operations * use uclass_first_device() * replace CONFIG_SANDBOX_FFA with CONFIG_ARM_FFA_TRANSPORT * address nits
v9: align FF-A sandbox tests with FF-A discovery through DM
v8:
* update partition_info_get() second argument to be an SP count * pass NULL device pointer to the FF-A bus discovery and operations
v7: set the tests to use 64-bit direct messaging
v4: align sandbox tests with the new FF-A driver interfaces and new way of error handling
v1: introduce sandbox tests
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 f8948b7635..4a8b3a5419 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -276,6 +276,7 @@ F: doc/arch/arm64.ffa.rst F: doc/usage/cmd/armffa.rst F: drivers/firmware/arm-ffa/ F: include/arm_ffa.h +F: test/dm/ffa.c
ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst index b7c754fa3d..325fb80346 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 3799b1ae8f..7ed00733c1 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 open-source-office@arm.com +# Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
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 0000000000..6912666bb4 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <console.h> +#include <dm.h> +#include <asm/sandbox_arm_ffa.h> +#include <asm/sandbox_arm_ffa_priv.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +/* 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);

Add Sandbox test for the armffa command
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v12:
* address nits
v10:
* replace CMD_RET_SUCCESS with 0 * replace CONFIG_SANDBOX_FFA with CONFIG_ARM_FFA_TRANSPORT
v9: align the test with FF-A discovery through DM
v4: drop use of helper APIs
v1: introduce armffa command sandbox test
MAINTAINERS | 1 + test/cmd/Makefile | 2 ++ test/cmd/armffa.c | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+) create mode 100644 test/cmd/armffa.c
diff --git a/MAINTAINERS b/MAINTAINERS index 4a8b3a5419..30d1b87149 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -276,6 +276,7 @@ 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/test/cmd/Makefile b/test/cmd/Makefile index a3cf983739..6e3d7e919e 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 open-source-office@arm.com
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 0000000000..9a44a397e8 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <string.h> +#include <asm/sandbox_arm_ffa.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +/* Basic test of 'armffa' command */ +static int dm_test_armffa_cmd(struct unit_test_state *uts) +{ + /* armffa getpart <UUID> */ + ut_assertok(run_command("armffa getpart " SANDBOX_SERVICE1_UUID, 0)); + + /* armffa ping <ID> */ + 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);

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.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Tested-by: Gowtham Suresh Kumar gowtham.sureshkumar@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v14:
Ilias:
* drop truncating var_payload->size when using FF-A * map the MM SP return codes to errnos
v13:
* remove FF-A and Optee ifdefs
v12:
* drop use of calloc when querying SPs * address nits
v11:
* rename select_ffa_mm_comms() to select_mm_comms() * improve the logic of MM transport selection in mm_communicate() * addressing nits
v10:
* use the FF-A driver Uclass operations * use uclass_first_device() * addressing nits
v9: align how FF-A is used with FF-A discovery through DM
v8:
* isolate the compilation choices between FF-A and OP-TEE * update partition_info_get() second argument to be an SP count * pass NULL device pointer to the FF-A bus discovery and operations
v7:
* set the MM door bell event to use 64-bit direct messaging * issue a compile time error when one of these macros are not found : FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_OFFSET, FFA_SHARED_MM_BUFFER_ADDR * make mm_sp_svc_uuid static * replace EINVAL with ENOMEM in ffa_discover_mm_sp_id() when calloc() fails * improve use of unmap_sysmem() in ffa_mm_communicate()
v6:
* add FF-A runtime discovery at MM communication level * drop EFI runtime support for FF-A MM communication * revert the changes in include/mm_communication.h for efi_mm_communicate_header and smm_variable_access structures
v4:
* use the new FF-A driver interfaces * discover MM partitions at runtime * copy FF-A driver private data to EFI runtime section at ExitBootServices() * drop use of FFA_ERR_STAT_SUCCESS error code * replace EFI_BUFFER_TOO_SMALL with EFI_OUT_OF_RESOURCES in ffa_mm_communicate(). No need for efi_memcpy_runtime() anymore * revert the error log in mm_communicate() in case of failure * remove packed attribute from efi_mm_communicate_header and smm_variable_communicate_header
v2:
* set default values to 0 for FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_ADDR and MM_SP_UUID_DATA and add warnings
v1:
* introduce FF-A MM communication
include/mm_communication.h | 13 ++ lib/efi_loader/Kconfig | 16 +- lib/efi_loader/efi_variable_tee.c | 272 +++++++++++++++++++++++++++++- 3 files changed, 294 insertions(+), 7 deletions(-)
diff --git a/include/mm_communication.h b/include/mm_communication.h index e65fbde60d..f17847583b 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. sughosh.ganu@linaro.org * Copyright (C) 2020 Linaro Ltd. ilias.apalodimas@linaro.org + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#ifndef _MM_COMMUNICATION_H_ @@ -13,6 +16,9 @@
#include <part_efi.h>
+/* MM service UUID string (big-endian format). This UUID is common across all MM SPs */ +#define MM_SP_UUID "33d532ed-e699-0942-c09c-a798d9cd722d" + /* * Interface to the pseudo Trusted Application (TA), which provides a * communication channel with the Standalone MM (Management Mode) @@ -248,4 +254,11 @@ struct smm_variable_var_check_property { u16 name[]; };
+/* supported MM transports */ +enum mm_comms_select { + MM_COMMS_UNDEFINED, + MM_COMMS_FFA, + MM_COMMS_OPTEE +}; + #endif /* _MM_COMMUNICATION_H_ */ diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index c5835e6ef6..cb26e110fd 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -55,13 +55,25 @@ 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" - depends on OPTEE + bool "UEFI variables storage service via the trusted world" + select ARM_FFA_TRANSPORT + select TEE + select 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 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 dfef18435d..aef75cb42b 100644 --- a/lib/efi_loader/efi_variable_tee.c +++ b/lib/efi_loader/efi_variable_tee.c @@ -4,17 +4,49 @@ * * Copyright (C) 2019 Linaro Ltd. sughosh.ganu@linaro.org * Copyright (C) 2019 Linaro Ltd. ilias.apalodimas@linaro.org + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> +#include <arm_ffa.h> +#include <cpu_func.h> +#include <dm.h> #include <efi.h> #include <efi_api.h> #include <efi_loader.h> #include <efi_variable.h> -#include <tee.h> #include <malloc.h> +#include <mapmem.h> #include <mm_communication.h> +#include <tee.h>
+#ifndef FFA_SHARED_MM_BUFFER_SIZE +#error "FFA_SHARED_MM_BUFFER_SIZE must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_SIZE 0 +#endif + +#ifndef FFA_SHARED_MM_BUFFER_OFFSET +#error "FFA_SHARED_MM_BUFFER_OFFSET must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_OFFSET 0 +#endif + +#ifndef FFA_SHARED_MM_BUFFER_ADDR +#error "FFA_SHARED_MM_BUFFER_ADDR must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_ADDR 0 +#endif + +/* 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; extern struct efi_var_file __efi_runtime_data *efi_var_buf; static efi_uintn_t max_buffer_size; /* comm + var + func + data */ static efi_uintn_t max_payload_size; /* func + data */ @@ -145,16 +177,241 @@ static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize) }
/** - * 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 = FFA_SHARED_MM_BUFFER_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 > FFA_SHARED_MM_BUFFER_SIZE) + return EFI_INVALID_PARAMETER; + + /* Copy the data to the shared buffer */ + + virt_shared_buf = map_sysmem((phys_addr_t)FFA_SHARED_MM_BUFFER_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_err("EFI: Cannot find FF-A bus device, trying Optee comms\n"); + return MM_COMMS_OPTEE; + } + + return MM_COMMS_FFA; +} + +/** + * 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) { efi_status_t ret; + enum mm_comms_select mm_comms; struct efi_mm_communicate_header *mm_hdr; struct smm_variable_communicate_header *var_hdr;
@@ -162,7 +419,12 @@ static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) 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); + 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); + if (ret != EFI_SUCCESS) { log_err("%s failed!\n", __func__); return ret; @@ -697,7 +959,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);
/*

turn on EFI MM communication
On corstone1000 platform MM communication between u-boot and the secure world (Optee) is done using the FF-A bus.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v13:
* remove FF-A config in the defconfig (because it's enabled automatically by CONFIG_EFI_MM_COMM_TEE)
v9:
* update copyright string
v8:
* drop OP-TEE configs from Corstone-1000 defconfig
v7:
* improve the definition of FFA_SHARED_MM_BUFFER_ADDR and FFA_SHARED_MM_BUFFER_OFFSET * update FFA_SHARED_MM_BUFFER_ADDR value
v6:
* corstone-1000: enable optee driver * corstone-1000: remove CONFIG_ARM_FFA_EFI_RUNTIME_MODE from the defconfig
v4:
* corstone-1000: turn on EFI MM communication
configs/corstone1000_defconfig | 1 + include/configs/corstone1000.h | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/configs/corstone1000_defconfig b/configs/corstone1000_defconfig index a8a79fd105..b57e2322c4 100644 --- a/configs/corstone1000_defconfig +++ b/configs/corstone1000_defconfig @@ -65,3 +65,4 @@ CONFIG_EFI_CAPSULE_ON_DISK=y CONFIG_EFI_IGNORE_OSINDICATIONS=y CONFIG_FWU_MULTI_BANK_UPDATE=y CONFIG_ERRNO_STR=y +CONFIG_EFI_MM_COMM_TEE=y diff --git a/include/configs/corstone1000.h b/include/configs/corstone1000.h index 3347c11792..4ef1f05e40 100644 --- a/include/configs/corstone1000.h +++ b/include/configs/corstone1000.h @@ -1,9 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0+ */ /* - * (C) Copyright 2022 ARM Limited * (C) Copyright 2022 Linaro * Rui Miguel Silva rui.silva@linaro.org - * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com * * Configuration for Corstone1000. Parts were derived from other ARM * configurations. @@ -14,6 +16,15 @@
#include <linux/sizes.h>
+#define FFA_SHARED_MM_BUFFER_SIZE SZ_4K /* 4 KB */ + +/* + * shared buffer physical address used for communication between + * u-boot and the MM SP + */ +#define FFA_SHARED_MM_BUFFER_ADDR 0x02000000UL +#define FFA_SHARED_MM_BUFFER_OFFSET 0 + #define V2M_BASE 0x80000000
#define CFG_PL011_CLOCK 50000000

Hi Abdellatif,
On Fri, 7 Jul 2023 at 15:44, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
turn on EFI MM communication
On corstone1000 platform MM communication between u-boot and the secure world (Optee) is done using the FF-A bus.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v13:
- remove FF-A config in the defconfig (because it's enabled automatically by CONFIG_EFI_MM_COMM_TEE)
v9:
- update copyright string
v8:
- drop OP-TEE configs from Corstone-1000 defconfig
v7:
- improve the definition of FFA_SHARED_MM_BUFFER_ADDR and FFA_SHARED_MM_BUFFER_OFFSET
- update FFA_SHARED_MM_BUFFER_ADDR value
v6:
- corstone-1000: enable optee driver
- corstone-1000: remove CONFIG_ARM_FFA_EFI_RUNTIME_MODE from the defconfig
v4:
- corstone-1000: turn on EFI MM communication
configs/corstone1000_defconfig | 1 + include/configs/corstone1000.h | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/configs/corstone1000_defconfig b/configs/corstone1000_defconfig index a8a79fd105..b57e2322c4 100644 --- a/configs/corstone1000_defconfig +++ b/configs/corstone1000_defconfig @@ -65,3 +65,4 @@ CONFIG_EFI_CAPSULE_ON_DISK=y CONFIG_EFI_IGNORE_OSINDICATIONS=y CONFIG_FWU_MULTI_BANK_UPDATE=y CONFIG_ERRNO_STR=y +CONFIG_EFI_MM_COMM_TEE=y diff --git a/include/configs/corstone1000.h b/include/configs/corstone1000.h index 3347c11792..4ef1f05e40 100644 --- a/include/configs/corstone1000.h +++ b/include/configs/corstone1000.h @@ -1,9 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0+ */ /*
- (C) Copyright 2022 ARM Limited
- (C) Copyright 2022 Linaro
- Rui Miguel Silva rui.silva@linaro.org
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
- Authors:
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- Configuration for Corstone1000. Parts were derived from other ARM
- configurations.
@@ -14,6 +16,15 @@
#include <linux/sizes.h>
+#define FFA_SHARED_MM_BUFFER_SIZE SZ_4K /* 4 KB */
+/*
- shared buffer physical address used for communication between
- u-boot and the MM SP
- */
+#define FFA_SHARED_MM_BUFFER_ADDR 0x02000000UL +#define FFA_SHARED_MM_BUFFER_OFFSET 0
These should be in devicetree properties, shouldn't they? We don't want things in board config.h files anymore.
#define V2M_BASE 0x80000000
#define CFG_PL011_CLOCK 50000000
2.25.1
Regards, Simon

On Fri, Jul 07, 2023 at 11:35:05AM -0600, Simon Glass wrote:
Hi Abdellatif,
On Fri, 7 Jul 2023 at 15:44, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
turn on EFI MM communication
On corstone1000 platform MM communication between u-boot and the secure world (Optee) is done using the FF-A bus.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v13:
- remove FF-A config in the defconfig (because it's enabled automatically by CONFIG_EFI_MM_COMM_TEE)
v9:
- update copyright string
v8:
- drop OP-TEE configs from Corstone-1000 defconfig
v7:
- improve the definition of FFA_SHARED_MM_BUFFER_ADDR and FFA_SHARED_MM_BUFFER_OFFSET
- update FFA_SHARED_MM_BUFFER_ADDR value
v6:
- corstone-1000: enable optee driver
- corstone-1000: remove CONFIG_ARM_FFA_EFI_RUNTIME_MODE from the defconfig
v4:
- corstone-1000: turn on EFI MM communication
configs/corstone1000_defconfig | 1 + include/configs/corstone1000.h | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/configs/corstone1000_defconfig b/configs/corstone1000_defconfig index a8a79fd105..b57e2322c4 100644 --- a/configs/corstone1000_defconfig +++ b/configs/corstone1000_defconfig @@ -65,3 +65,4 @@ CONFIG_EFI_CAPSULE_ON_DISK=y CONFIG_EFI_IGNORE_OSINDICATIONS=y CONFIG_FWU_MULTI_BANK_UPDATE=y CONFIG_ERRNO_STR=y +CONFIG_EFI_MM_COMM_TEE=y diff --git a/include/configs/corstone1000.h b/include/configs/corstone1000.h index 3347c11792..4ef1f05e40 100644 --- a/include/configs/corstone1000.h +++ b/include/configs/corstone1000.h @@ -1,9 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0+ */ /*
- (C) Copyright 2022 ARM Limited
- (C) Copyright 2022 Linaro
- Rui Miguel Silva rui.silva@linaro.org
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
- Authors:
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- Configuration for Corstone1000. Parts were derived from other ARM
- configurations.
@@ -14,6 +16,15 @@
#include <linux/sizes.h>
+#define FFA_SHARED_MM_BUFFER_SIZE SZ_4K /* 4 KB */
+/*
- shared buffer physical address used for communication between
- u-boot and the MM SP
- */
+#define FFA_SHARED_MM_BUFFER_ADDR 0x02000000UL +#define FFA_SHARED_MM_BUFFER_OFFSET 0
These should be in devicetree properties, shouldn't they? We don't want things in board config.h files anymore.
Wasn't one of the earlier debates on if the whole thing needs to be in device tree, or not, because it's a "discoverable bus" and so Linux wasn't needing one? I don't want to de-rail this series, which I think we're just about otherwise ready to merge, over that debate again.
As these values have to match up (I assume) with the other side, are these truly per-board, or SoC dependent?

Hi Tom,
On Fri, 7 Jul 2023 at 11:44, Tom Rini trini@konsulko.com wrote:
On Fri, Jul 07, 2023 at 11:35:05AM -0600, Simon Glass wrote:
Hi Abdellatif,
On Fri, 7 Jul 2023 at 15:44, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
turn on EFI MM communication
On corstone1000 platform MM communication between u-boot and the secure world (Optee) is done using the FF-A bus.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v13:
- remove FF-A config in the defconfig (because it's enabled automatically by CONFIG_EFI_MM_COMM_TEE)
v9:
- update copyright string
v8:
- drop OP-TEE configs from Corstone-1000 defconfig
v7:
- improve the definition of FFA_SHARED_MM_BUFFER_ADDR and FFA_SHARED_MM_BUFFER_OFFSET
- update FFA_SHARED_MM_BUFFER_ADDR value
v6:
- corstone-1000: enable optee driver
- corstone-1000: remove CONFIG_ARM_FFA_EFI_RUNTIME_MODE from the defconfig
v4:
- corstone-1000: turn on EFI MM communication
configs/corstone1000_defconfig | 1 + include/configs/corstone1000.h | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/configs/corstone1000_defconfig b/configs/corstone1000_defconfig index a8a79fd105..b57e2322c4 100644 --- a/configs/corstone1000_defconfig +++ b/configs/corstone1000_defconfig @@ -65,3 +65,4 @@ CONFIG_EFI_CAPSULE_ON_DISK=y CONFIG_EFI_IGNORE_OSINDICATIONS=y CONFIG_FWU_MULTI_BANK_UPDATE=y CONFIG_ERRNO_STR=y +CONFIG_EFI_MM_COMM_TEE=y diff --git a/include/configs/corstone1000.h b/include/configs/corstone1000.h index 3347c11792..4ef1f05e40 100644 --- a/include/configs/corstone1000.h +++ b/include/configs/corstone1000.h @@ -1,9 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0+ */ /*
- (C) Copyright 2022 ARM Limited
- (C) Copyright 2022 Linaro
- Rui Miguel Silva rui.silva@linaro.org
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
- Authors:
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- Configuration for Corstone1000. Parts were derived from other ARM
- configurations.
@@ -14,6 +16,15 @@
#include <linux/sizes.h>
+#define FFA_SHARED_MM_BUFFER_SIZE SZ_4K /* 4 KB */
+/*
- shared buffer physical address used for communication between
- u-boot and the MM SP
- */
+#define FFA_SHARED_MM_BUFFER_ADDR 0x02000000UL +#define FFA_SHARED_MM_BUFFER_OFFSET 0
These should be in devicetree properties, shouldn't they? We don't want things in board config.h files anymore.
Wasn't one of the earlier debates on if the whole thing needs to be in device tree, or not, because it's a "discoverable bus" and so Linux wasn't needing one? I don't want to de-rail this series, which I think we're just about otherwise ready to merge, over that debate again.
As these values have to match up (I assume) with the other side, are these truly per-board, or SoC dependent?
Yes let's not derail the series...it has gone on too long already.
But we do need to think about this. Addresses in #defines are not a great look. It isn't even a CFG.
Regards, Simon

Hi Simon, Tom,
#include <linux/sizes.h>
+#define FFA_SHARED_MM_BUFFER_SIZE SZ_4K /* 4 KB */
+/*
- shared buffer physical address used for communication between
- u-boot and the MM SP
- */
+#define FFA_SHARED_MM_BUFFER_ADDR 0x02000000UL +#define FFA_SHARED_MM_BUFFER_OFFSET 0
These should be in devicetree properties, shouldn't they? We don't want things in board config.h files anymore.
Wasn't one of the earlier debates on if the whole thing needs to be in device tree, or not, because it's a "discoverable bus" and so Linux wasn't needing one? I don't want to de-rail this series, which I think we're just about otherwise ready to merge, over that debate again.
As these values have to match up (I assume) with the other side, are these truly per-board, or SoC dependent?
Yes let's not derail the series...it has gone on too long already.
But we do need to think about this. Addresses in #defines are not a great look. It isn't even a CFG.
I think replacing the FFA_SHARED_MM_BUFFER_* defines with a config makes sense.
In v15 I'll add these as configs if you think guys it's appropriate:
FFA_SHARED_MM_BUFFER_SIZE FFA_SHARED_MM_BUFFER_ADDR FFA_SHARED_MM_BUFFER_OFFSET
Cheers Abdellatif

On Mon, Jul 10, 2023 at 04:03:22PM +0100, Abdellatif El Khlifi wrote:
Hi Simon, Tom,
#include <linux/sizes.h>
+#define FFA_SHARED_MM_BUFFER_SIZE SZ_4K /* 4 KB */
+/*
- shared buffer physical address used for communication between
- u-boot and the MM SP
- */
+#define FFA_SHARED_MM_BUFFER_ADDR 0x02000000UL +#define FFA_SHARED_MM_BUFFER_OFFSET 0
These should be in devicetree properties, shouldn't they? We don't want things in board config.h files anymore.
Wasn't one of the earlier debates on if the whole thing needs to be in device tree, or not, because it's a "discoverable bus" and so Linux wasn't needing one? I don't want to de-rail this series, which I think we're just about otherwise ready to merge, over that debate again.
As these values have to match up (I assume) with the other side, are these truly per-board, or SoC dependent?
Yes let's not derail the series...it has gone on too long already.
But we do need to think about this. Addresses in #defines are not a great look. It isn't even a CFG.
I think replacing the FFA_SHARED_MM_BUFFER_* defines with a config makes sense.
In v15 I'll add these as configs if you think guys it's appropriate:
FFA_SHARED_MM_BUFFER_SIZE FFA_SHARED_MM_BUFFER_ADDR FFA_SHARED_MM_BUFFER_OFFSET
Sounds good, thanks!

Adding support for Arm FF-A v1.0 (Arm Firmware Framework for Armv8-A) [A].
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.
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.
=> dm tree
Class Index Probed Driver Name ----------------------------------------------------------- ... firmware 0 [ + ] psci |-- psci ffa 0 [ ] arm_ffa | `-- arm_ffa ...
Clients are able to probe then use the FF-A bus by calling the DM class searching APIs (e.g: uclass_first_device).
This implementation of the specification provides support for Aarch64.
The FF-A driver uses the SMC ABIs defined by the FF-A specification to:
- Discover the presence of secure partitions (SPs) of interest - Access an SP's service through communication protocols (e.g: EFI MM communication protocol)
The FF-A support provides the following features:
- Being generic by design and can be used by any Arm 64-bit platform - FF-A support can be compiled and used without EFI - Support for SMCCCv1.2 x0-x17 registers - Support for SMC32 calling convention - Support for 32-bit and 64-bit FF-A direct messaging - Support for FF-A MM communication (compatible with EFI boot time) - Enabling FF-A and MM communication in Corstone1000 platform as a use case - 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. - Sandbox FF-A test cases. - A new command called armffa is provided as an example of how to access the FF-A bus
For more details about the FF-A support please refer to [B] and refer to [C] for how to use the armffa command.
Please find at [D] an example of the expected boot logs when enabling FF-A support for a platform. In this example the platform is Corstone1000. But it can be any Arm 64-bit platform.
Changelog of changes: ===========================
v15:
Simon:
* drop commit "log: select physical address formatting in a generic way", this will be sent as a follow-up commit independently from this patchset * armffa.c : integrate PHYS_ADDR_LN * replace FFA_SHARED_MM_BUFFER_* defines with configs
v14: [14]
Simon:
* add to log.h a generic physical address formatting
Ilias:
* armffa command: in do_ffa_ping() reject the SP ID if it's 0 * MM comms: drop truncating var_payload->size when using FF-A * MM comms: map the MM SP return codes to errnos * address nits
v13: [13]
Ilias: * remove FF-A and Optee ifdefs in efi_variable_tee.c * doc minor change: specify in the readme that the user should call ffa_rxtx_unmap() driver operation to unmap the RX/TX buffers on demand.
v12: [12]
* remove the global variable (dscvry_info), use uc_priv instead * replace dscvry_info.invoke_ffa_fn() with a weak invoke_ffa_fn (user drivers can override it) * improve FFA_PARTITION_INFO_GET implementation (clients no longer need to calloc a buffer) * remove reparenting by making the sandbox emulator parent of the FF-A device in the DT * improve argument checks for the armffa command * address nits
v11: [11]
* move ffa_try_discovery() from the uclass to the Arm FF-A driver * rename ffa_try_discovery() to arm_ffa_discover() * add arm_ prefix to the Arm FF-A driver functions * use U_BOOT_CMD_WITH_SUBCMDS for armffa command * store the sandbox emulator pointer in the FF-A device uc_priv (struct ffa_priv) * set the emulator as parent of the sandbox FF-A device * rename select_ffa_mm_comms() to select_mm_comms() * improve the logic of MM transport selection in mm_communicate() * use ut_asserteq_mem() in uuid_str_to_le_bin test case * address nits
v10: [10]
* provide the FF-A driver operations through the Uclass (arm-ffa-uclass.c) * move the generic FF-A methods to the Uclass * keep Arm specific methods in the Arm driver (arm-ffa.c renamed from core.c) * split the FF-A sandbox support into an emulator (ffa-emul-uclass.c) and a driver (sandbox_ffa.c) * use the FF-A driver Uclass operations by clients (armffa command, tests, MM comms) * use uclass_first_device to search and probe the FF-A device (whether it is on Arm or on sandbox) * address nits
v9: [9]
* integrate the FF-A bus discovery in the DM and use ARM_SMCCC_FEATURES for binding * align FF-A sandbox driver with FF-A discovery through DM * use DM class APIs to probe and interact with the FF-A bus (in FF-A MM comms, armffa command, sandbox tests) * add documentation for the armffa command: doc/usage/cmd/armffa.rst * introduce testcase for uuid_str_to_le_bin
v8: [8]
* pass the FF-A bus device to the bus operations * isolate the compilation choices between FF-A and OP-TEE * drop OP-TEE configs from Corstone-1000 defconfig * make ffa_get_partitions_info() second argument to be an SP count in both modes
v7: [7]
* add support for 32-bit direct messaging (now we have 32-bit and 64-bit support) * set the MM door bell event to use 64-bit direct messaging * issue a compile time error when one of these macros are not found : FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_OFFSET, FFA_SHARED_MM_BUFFER_ADDR * make uuid_str_to_le_bin() implementation similar to uuid_str_to_bin() by using same APIs
v6: [6]
* remove clearing x0-x17 registers after SMC calls * drop use of EFI runtime support for FF-A (We decided with Linaro to add this later) * drop discovery from initcalls (discovery will be on demand by FF-A users) * add FF-A runtime discovery at MM communication level * update the documentation and move it to doc/arch/arm64.ffa.rst
v5: [5]
* move changelogs in each commit to the changes section
v4: [4]
* add FF-A support README (doc/README.ffa.drv) * improving error handling by mapping the FF-A errors to standard errors and logs * replacing panics with an error log * align sandbox driver and tests with the new FF-A driver interfaces and new way of error handling * use the new FF-A driver interfaces for MM communication * discover MM partitions at runtime * copy FF-A driver private data to EFI runtime section at ExitBootServices() * moving the FF-A driver work to drivers/firmware/arm-ffa * improving features discovery in FFA_FEATURES * add remove/unbind functions to the FF-A core device * improve how the driver behaves when bus discovery is done more than once * move clearing x0-x17 registers code into a new macro like done in the linux kernel * enable EFI MM communication for the Corstone1000 platform
v3: [3]
* port x0-x17 registers support from linux kernel as defined by SMCCCv1.2 * align the interfaces of the u-boot FF-A driver with those in the linux FF-A driver * remove the FF-A helper layer * make the u-boot FF-A driver independent from EFI * provide an optional config that enables copying the driver data to EFI runtime section at ExitBootServices service * use 64-bit version of FFA_RXTX_MAP, FFA_MSG_SEND_DIRECT_{REQ, RESP} * update armffa command with the new driver interfaces
v2 [2]:
* make FF-A bus discoverable using device_{bind, probe} APIs * remove device tree support
v1 [1]:
* introduce FF-A bus driver with device tree support * introduce armffa command * introduce FF-A Sandbox driver * add FF-A Sandbox test cases * introduce FF-A MM communication
Cheers, Abdellatif
List of previous patches:
[1]: https://lore.kernel.org/all/20220329151659.16894-1-abdellatif.elkhlifi@arm.c... [2]: https://lore.kernel.org/all/20220415122803.16666-1-abdellatif.elkhlifi@arm.c... [3]: https://lore.kernel.org/all/20220801172053.20163-1-abdellatif.elkhlifi@arm.c... [4]: https://lore.kernel.org/all/20220926101723.9965-1-abdellatif.elkhlifi@arm.co... [5]: https://lore.kernel.org/all/20220926140827.15125-1-abdellatif.elkhlifi@arm.c... [6]: https://lore.kernel.org/all/20221013103857.614-1-abdellatif.elkhlifi@arm.com... [7]: https://lore.kernel.org/all/20221107192055.21669-1-abdellatif.elkhlifi@arm.c... [8]: https://lore.kernel.org/all/20221122131751.22747-1-abdellatif.elkhlifi@arm.c... [9]: https://lore.kernel.org/all/20230310141016.137986-1-abdellatif.elkhlifi@arm.... [10]: https://lore.kernel.org/all/20230328161157.219375-1-abdellatif.elkhlifi@arm.... [11]: https://lore.kernel.org/all/20230412094245.44674-1-abdellatif.elkhlifi@arm.c... [12]: https://lore.kernel.org/all/20230512121044.111574-1-abdellatif.elkhlifi@arm.... [13]: https://lore.kernel.org/all/20230616152817.319869-1-abdellatif.elkhlifi@arm.... [14]: https://lore.kernel.org/all/20230707144410.228472-1-abdellatif.elkhlifi@arm....
More details:
[A]: https://developer.arm.com/documentation/den0077/latest/ [B]: doc/arch/arm64.ffa.rst [C]: doc/usage/cmd/armffa.rst [D]: example of boot logs when enabling FF-A
``` 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 ```
Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Rob Herring robh@kernel.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Achin Gupta achin.gupta@arm.com Cc: Drew Reed Drew.Reed@arm.com Cc: Xueliang Zhong Xueliang.Zhong@arm.com
Abdellatif El Khlifi (10): arm64: smccc: add support for SMCCCv1.2 x0-x17 registers lib: uuid: introduce uuid_str_to_le_bin function lib: uuid: introduce testcase for uuid_str_to_le_bin arm_ffa: introduce Arm FF-A support arm_ffa: introduce armffa command arm_ffa: introduce sandbox FF-A support arm_ffa: introduce sandbox test cases for UCLASS_FFA arm_ffa: introduce armffa command Sandbox test arm_ffa: efi: introduce FF-A MM communication arm_ffa: efi: corstone1000: enable MM communication
MAINTAINERS | 18 + arch/arm/cpu/armv8/smccc-call.S | 57 +- arch/arm/lib/asm-offsets.c | 16 + 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 ++ cmd/Kconfig | 10 + cmd/Makefile | 1 + cmd/armffa.c | 202 ++++ configs/corstone1000_defconfig | 4 + configs/sandbox64_defconfig | 1 + configs/sandbox_defconfig | 1 + doc/arch/arm64.ffa.rst | 261 ++++ doc/arch/index.rst | 1 + doc/arch/sandbox/sandbox.rst | 1 + doc/usage/cmd/armffa.rst | 93 ++ doc/usage/index.rst | 1 + drivers/Makefile | 1 + drivers/firmware/Kconfig | 1 + drivers/firmware/arm-ffa/Kconfig | 42 + drivers/firmware/arm-ffa/Makefile | 16 + drivers/firmware/arm-ffa/arm-ffa-uclass.c | 1065 +++++++++++++++++ drivers/firmware/arm-ffa/arm-ffa.c | 104 ++ drivers/firmware/arm-ffa/ffa-emul-uclass.c | 720 +++++++++++ drivers/firmware/arm-ffa/sandbox_ffa.c | 110 ++ include/arm_ffa.h | 213 ++++ include/arm_ffa_priv.h | 246 ++++ include/dm/uclass-id.h | 7 + include/linux/arm-smccc.h | 45 + include/mm_communication.h | 13 + include/uuid.h | 15 + lib/efi_loader/Kconfig | 46 +- lib/efi_loader/efi_variable_tee.c | 257 +++- lib/uuid.c | 48 + test/cmd/Makefile | 2 + test/cmd/armffa.c | 33 + test/dm/Makefile | 3 +- test/dm/ffa.c | 261 ++++ test/lib/Makefile | 1 + test/lib/uuid.c | 41 + 41 files changed, 4158 insertions(+), 9 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 cmd/armffa.c create mode 100644 doc/arch/arm64.ffa.rst create mode 100644 doc/usage/cmd/armffa.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/ffa-emul-uclass.c create mode 100644 drivers/firmware/arm-ffa/sandbox_ffa.c create mode 100644 include/arm_ffa.h create mode 100644 include/arm_ffa_priv.h create mode 100644 test/cmd/armffa.c create mode 100644 test/dm/ffa.c create mode 100644 test/lib/uuid.c

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 abdellatif.elkhlifi@arm.com Reviewed-by: Ilias Apalodimas ilias.apalodimas@linaro.org Reviewed-by: Jens Wiklander jens.wiklander@linaro.org Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com
---
Changelog: ===============
v9:
* update the copyright string
v7:
* improve indentation of ARM_SMCCC_1_2_REGS_Xn_OFFS
v4:
* rename the commit title and improve description new commit title: the current
v3:
* port x0-x17 registers support from linux kernel as defined by SMCCCv1.2 commit title: arm64: smccc: add Xn registers support used by SMC calls
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 dc92b28777..93f66d3366 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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com +*/ #include <linux/linkage.h> #include <linux/arm-smccc.h> #include <generated/asm-offsets.h> @@ -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 6de0ce9152..181a8ac4c2 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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> @@ -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 e1d09884a1..f44e9e8f93 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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */ #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

convert UUID string to little endian binary data
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v9:
* add a full function prototype description in uuid.h
v8:
* use simple_strtoull() in uuid_str_to_le_bin() to support 32-bit platforms
v7:
* rename be_uuid_str_to_le_bin() to uuid_str_to_le_bin() * make uuid_str_to_le_bin() implementation similar to uuid_str_to_bin() by using same APIs
v4:
* rename ffa_uuid_str_to_bin to be_uuid_str_to_le_bin and put in a standalone commit (the current)
v3:
* introduce ffa_uuid_str_to_bin (provided by arm_ffa: introduce Arm FF-A low-level driver)
include/uuid.h | 15 +++++++++++++++ lib/uuid.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+)
diff --git a/include/uuid.h b/include/uuid.h index 4a4883d3b5..89b93e642b 100644 --- a/include/uuid.h +++ b/include/uuid.h @@ -2,6 +2,10 @@ /* * Copyright (C) 2014 Samsung Electronics * Przemyslaw Marczak p.marczak@samsung.com + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */ #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 96e1af3c8b..45f325d964 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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> @@ -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. *

provide a test case
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com
---
Changelog: ===============
v11:
* use ut_asserteq_mem()
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 d724b64673..a1e34ab63a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1634,3 +1634,8 @@ S: Maintained F: arch/arm/dts/ls1021a-twr-u-boot.dtsi F: drivers/crypto/fsl/ F: include/fsl_sec.h + +UUID testing +M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com +S: Maintained +F: test/lib/uuid.c diff --git a/test/lib/Makefile b/test/lib/Makefile index e0bd9e04e8..e75a263e6a 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 0000000000..e24331a136 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <uuid.h> +#include <test/lib.h> +#include <test/test.h> +#include <test/ut.h> + +/* 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);

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 abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
---
Changelog: ===============
v13:
* doc minor change: specify in the readme that the user should call ffa_rxtx_unmap() driver operation to unmap the RX/TX buffers on demand.
v12:
* remove dscvry_info * replace dscvry_info.invoke_ffa_fn() with a weak invoke_ffa_fn (user drivers can override it) * improve FFA_PARTITION_INFO_GET implementation (clients no longer need to calloc a buffer) * address nits
v11:
* move ffa_try_discovery() from the uclass to the Arm FF-A driver * rename ffa_try_discovery() to arm_ffa_discover() * pass dev as an argument of arm_ffa_discover() * add arm_ prefix to the Arm FF-A driver functions * add emul field in struct ffa_discovery_info * address nits
v10:
* provide the driver operations through the Uclass * move the generic FF-A methods to the Uclass * keep Arm specific methods in the Arm driver (arm-ffa.c) * rename core.c to arm-ffa.c * address nits
v9:
* integrate the FF-A bus discovery in the DM and use ARM_SMCCC_FEATURES for binding
v8:
* make ffa_get_partitions_info() second argument to be an SP count in both modes * update ffa_bus_prvdata_get() to return a pointer rather than a pointer address * remove packing from ffa_partition_info and ffa_send_direct_data structures * pass the FF-A bus device to the bus operations
v7:
* add support for 32-bit direct messaging * rename be_uuid_str_to_le_bin() to uuid_str_to_le_bin() * improve the declaration of error handling mapping * stating in doc/arch/arm64.ffa.rst that EFI runtime is not supported
v6:
* drop use of EFI runtime support (We decided with Linaro to add this later) * drop discovery from initcalls (discovery will be on demand by FF-A users) * set the alignment of the RX/TX buffers to the larger translation granule size * move FF-A RX/TX buffers unmapping at ExitBootServices() to a separate commit * update the documentation and move it to doc/arch/arm64.ffa.rst
v4:
* add doc/README.ffa.drv * moving the FF-A driver work to drivers/firmware/arm-ffa * use less #ifdefs in lib/efi_loader/efi_boottime.c and replace #if defined by #if CONFIG_IS_ENABLED * improving error handling by mapping the FF-A errors to standard errors and logs * replacing panics with an error log and returning an error code * improving features discovery in FFA_FEATURES by introducing rxtx_min_pages private data field * add ffa_remove and ffa_unbind functions * improve how the driver behaves when bus discovery is done more than once
v3:
* align the interfaces of the U-Boot FF-A driver with those in the linux FF-A driver * remove the FF-A helper layer * make the U-Boot FF-A driver independent from EFI * provide an optional config that enables copying the driver data to EFI runtime section at ExitBootServices service * use 64-bit version of FFA_RXTX_MAP, FFA_MSG_SEND_DIRECT_{REQ, RESP}
v2:
* make FF-A bus discoverable using device_{bind, probe} APIs * remove device tree support
v1:
* introduce FF-A bus driver with device tree support
MAINTAINERS | 8 + doc/arch/arm64.ffa.rst | 238 ++++ 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, 1941 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 a1e34ab63a..b3a16ed802 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 abdellatif.elkhlifi@arm.com +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 sbabic@denx.de M: Fabio Estevam festevam@gmail.com diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst new file mode 100644 index 0000000000..4f817f053c --- /dev/null +++ b/doc/arch/arm64.ffa.rst @@ -0,0 +1,238 @@ +.. 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(). +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: + + - 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 abdellatif.elkhlifi@arm.com + +.. _`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 b8da4b8c8e..2f916f4026 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 78dcf62f76..5044f45253 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 eae1c8ddc9..8789b1ea14 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 0000000000..9200c8028b --- /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 0000000000..11b1766285 --- /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 open-source-office@arm.com +# +# Authors: +# Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + +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 0000000000..ffa9d81fa7 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ +#include <common.h> +#include <arm_ffa.h> +#include <arm_ffa_priv.h> +#include <dm.h> +#include <log.h> +#include <malloc.h> +#include <string.h> +#include <uuid.h> +#include <asm/global_data.h> +#include <dm/device-internal.h> +#include <dm/devres.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> + +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 0000000000..68df75bd9e --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <arm_ffa.h> +#include <arm_ffa_priv.h> +#include <dm.h> +#include <log.h> +#include <asm/global_data.h> +#include <dm/device-internal.h> +#include <linux/errno.h> + +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 0000000000..4338f9c9b1 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#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 0000000000..db9b1be995 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __ARM_FFA_H +#define __ARM_FFA_H + +#include <linux/printk.h> + +/* + * 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 0000000000..d564c33c64 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __ARM_FFA_PRV_H +#define __ARM_FFA_PRV_H + +#include <mapmem.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> + +/* 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 <linux/arm-smccc.h> +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 307ad6931c..3c6af2e3d2 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -4,6 +4,11 @@ * * (C) Copyright 2012 * Pavel Herrmann morpheus.ibis@gmail.com + * + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#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 */

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].
[1]: doc/usage/cmd/armffa.rst
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
---
Changelog: ===============
v15:
Simon:
* armffa.c : integrate PHYS_ADDR_LN
v14:
Ilias:
* address nits * in do_ffa_ping() reject the SP ID if it's 0 * use PHYS_ADDR_LN in formatting the physical addresses
v12:
* add subcommands argument checks * usage documentation: update command return codes * remove calloc when querying SPs * address nits
v11:
* use U_BOOT_CMD_WITH_SUBCMDS * address nits
v10:
* use the FF-A driver Uclass operations * use uclass_first_device() * address nits
v9:
* remove manual FF-A discovery and use DM * use DM class APIs to probe and interact with the FF-A bus * add doc/usage/cmd/armffa.rst
v8:
* update partition_info_get() second argument to be an SP count * pass NULL device pointer to the FF-A bus discovery and operations
v7:
* adapt do_ffa_dev_list() following the recent update on uclass_first_device/uclass_next_device functions (they return void now) * set armffa command to use 64-bit direct messaging
v4:
* remove pattern data in do_ffa_msg_send_direct_req
v3:
* use the new driver interfaces (partition_info_get, sync_send_receive) in armffa command
v2:
* replace use of ffa_helper_init_device function by ffa_helper_bus_discover
v1:
* introduce armffa command
MAINTAINERS | 2 + cmd/Kconfig | 10 ++ cmd/Makefile | 1 + cmd/armffa.c | 202 +++++++++++++++++++++++++++++++ doc/arch/arm64.ffa.rst | 7 ++ doc/usage/cmd/armffa.rst | 93 ++++++++++++++ doc/usage/index.rst | 1 + drivers/firmware/arm-ffa/Kconfig | 1 + 8 files changed, 317 insertions(+) create mode 100644 cmd/armffa.c create mode 100644 doc/usage/cmd/armffa.rst
diff --git a/MAINTAINERS b/MAINTAINERS index b3a16ed802..ff6a222960 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -269,7 +269,9 @@ F: configs/cortina_presidio-asic-pnand_defconfig ARM FF-A M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com S: Maintained +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: include/sandbox_arm_ffa.h diff --git a/cmd/Kconfig b/cmd/Kconfig index 02e54f1e50..79b4f8367a 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -935,6 +935,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 6c37521b4e..7d20a85a46 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 0000000000..7e6eafc03a --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ +#include <common.h> +#include <arm_ffa.h> +#include <command.h> +#include <dm.h> +#include <mapmem.h> +#include <stdlib.h> +#include <asm/io.h> + +/* 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 <partition UUID>\n" + " - lists the partition(s) info\n" + "ping <partition ID>\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 4f817f053c..aefd527447 100644 --- a/doc/arch/arm64.ffa.rst +++ b/doc/arch/arm64.ffa.rst @@ -205,6 +205,13 @@ The following features are provided:
- FF-A bus can be compiled and used without EFI
+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 0000000000..3d422686c1 --- /dev/null +++ b/doc/usage/cmd/armffa.rst @@ -0,0 +1,93 @@ +.. SPDX-License-Identifier: GPL-2.0+: + +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 388e59f173..e462de2806 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 9200c8028b..a7d5392859 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 select ARM_SMCCC select ARM_SMCCC_FEATURES + imply CMD_ARMFFA select LIB_UUID select DEVRES help

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 abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
---
Changelog: ===============
v12:
* remove reparenting by making the emulator parent of the FF-A device in the DT * add invoke_ffa_fn() * address nits
v11:
* rename ffa_try_discovery() to sandbox_ffa_discover() * rename sandbox_ffa_query_core_state() to sandbox_query_ffa_emul_state() * store the sandbox emulator pointer in the FF-A device uc_priv (struct ffa_priv) * set the emulator as parent of the sandbox FF-A device
v10:
* split the FF-A sandbox support into an emulator and a driver * read FFA_VERSION and FFA_PARTITION_INFO_GET state using sandbox_ffa_query_core_state() * drop CONFIG_SANDBOX_FFA config * address nits
v9: align FF-A sandbox driver with FF-A discovery through DM
v8: update ffa_bus_prvdata_get() to return a pointer rather than a pointer address
v7: state that sandbox driver supports only 64-bit direct messaging
v4: align sandbox driver with the new FF-A driver interfaces and new way of error handling
v1: introduce the sandbox driver
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 ff6a222960..f8948b7635 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -269,12 +269,13 @@ F: configs/cortina_presidio-asic-pnand_defconfig ARM FF-A M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com 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: include/sandbox_arm_ffa.h
ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de diff --git a/arch/sandbox/dts/sandbox.dtsi b/arch/sandbox/dts/sandbox.dtsi index 30a305c4d2..94a08814b8 100644 --- a/arch/sandbox/dts/sandbox.dtsi +++ b/arch/sandbox/dts/sandbox.dtsi @@ -445,6 +445,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 ff9f9222e6..96b5404991 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -1820,6 +1820,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 0000000000..be2790f496 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __SANDBOX_ARM_FFA_H +#define __SANDBOX_ARM_FFA_H + +#include <arm_ffa.h> + +/* + * 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 0000000000..b0881822d7 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __SANDBOX_ARM_FFA_PRV_H +#define __SANDBOX_ARM_FFA_PRV_H + +#include <arm_ffa_priv.h> + +/* 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 98b3e0cda4..72ea3d21ab 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 1ec44d5b33..8269bec879 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -344,3 +344,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 aefd527447..b7c754fa3d 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. @@ -205,6 +208,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 + The armffa command -----------------------------------
diff --git a/doc/arch/sandbox/sandbox.rst b/doc/arch/sandbox/sandbox.rst index 77ca6bc4cc..a3631de749 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 a7d5392859..d75f8b53fd 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 imply CMD_ARMFFA select LIB_UUID select DEVRES @@ -33,5 +33,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 11b1766285..318123a7f4 100644 --- a/drivers/firmware/arm-ffa/Makefile +++ b/drivers/firmware/arm-ffa/Makefile @@ -5,4 +5,12 @@ # Authors: # Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
-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 0000000000..5562bbaac3 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ +#include <common.h> +#include <dm.h> +#include <mapmem.h> +#include <string.h> +#include <asm/global_data.h> +#include <asm/sandbox_arm_ffa.h> +#include <asm/sandbox_arm_ffa_priv.h> +#include <dm/device-internal.h> +#include <dm/lists.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> + +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 4338f9c9b1..0000000000 --- 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 open-source-office@arm.com - * - * Authors: - * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com - */ - -#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 0000000000..ef9491ccea --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ +#include <common.h> +#include <arm_ffa.h> +#include <dm.h> +#include <log.h> +#include <asm/global_data.h> +#include <asm/sandbox_arm_ffa_priv.h> +#include <dm/device-internal.h> +#include <linux/errno.h> + +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 3c6af2e3d2..0432c95c9e 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 */

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 abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
---
Changelog: ===============
v12:
* remove use of dscvry_info * drop use of calloc when querying SPs * address nits
v11:
* drop unmapping test (taken care of by the DM when removing the device) * address nits
v10:
* use the FF-A driver Uclass operations * use uclass_first_device() * replace CONFIG_SANDBOX_FFA with CONFIG_ARM_FFA_TRANSPORT * address nits
v9: align FF-A sandbox tests with FF-A discovery through DM
v8:
* update partition_info_get() second argument to be an SP count * pass NULL device pointer to the FF-A bus discovery and operations
v7: set the tests to use 64-bit direct messaging
v4: align sandbox tests with the new FF-A driver interfaces and new way of error handling
v1: introduce sandbox tests
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 f8948b7635..4a8b3a5419 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -276,6 +276,7 @@ F: doc/arch/arm64.ffa.rst F: doc/usage/cmd/armffa.rst F: drivers/firmware/arm-ffa/ F: include/arm_ffa.h +F: test/dm/ffa.c
ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst index b7c754fa3d..325fb80346 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 3799b1ae8f..7ed00733c1 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 open-source-office@arm.com +# Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
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 0000000000..6912666bb4 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <console.h> +#include <dm.h> +#include <asm/sandbox_arm_ffa.h> +#include <asm/sandbox_arm_ffa_priv.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +/* 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);

Add Sandbox test for the armffa command
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v12:
* address nits
v10:
* replace CMD_RET_SUCCESS with 0 * replace CONFIG_SANDBOX_FFA with CONFIG_ARM_FFA_TRANSPORT
v9: align the test with FF-A discovery through DM
v4: drop use of helper APIs
v1: introduce armffa command sandbox test
MAINTAINERS | 1 + test/cmd/Makefile | 2 ++ test/cmd/armffa.c | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+) create mode 100644 test/cmd/armffa.c
diff --git a/MAINTAINERS b/MAINTAINERS index 4a8b3a5419..30d1b87149 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -276,6 +276,7 @@ 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/test/cmd/Makefile b/test/cmd/Makefile index a3cf983739..6e3d7e919e 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 open-source-office@arm.com
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 0000000000..9a44a397e8 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <string.h> +#include <asm/sandbox_arm_ffa.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +/* Basic test of 'armffa' command */ +static int dm_test_armffa_cmd(struct unit_test_state *uts) +{ + /* armffa getpart <UUID> */ + ut_assertok(run_command("armffa getpart " SANDBOX_SERVICE1_UUID, 0)); + + /* armffa ping <ID> */ + 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);

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.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Tested-by: Gowtham Suresh Kumar gowtham.sureshkumar@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v15:
Simon:
* replace FFA_SHARED_MM_BUFFER_* defines with configs
v14:
Ilias:
* drop truncating var_payload->size when using FF-A * map the MM SP return codes to errnos
v13:
* remove FF-A and Optee ifdefs
v12:
* drop use of calloc when querying SPs * address nits
v11:
* rename select_ffa_mm_comms() to select_mm_comms() * improve the logic of MM transport selection in mm_communicate() * addressing nits
v10:
* use the FF-A driver Uclass operations * use uclass_first_device() * addressing nits
v9: align how FF-A is used with FF-A discovery through DM
v8:
* isolate the compilation choices between FF-A and OP-TEE * update partition_info_get() second argument to be an SP count * pass NULL device pointer to the FF-A bus discovery and operations
v7:
* set the MM door bell event to use 64-bit direct messaging * issue a compile time error when one of these macros are not found : FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_OFFSET, FFA_SHARED_MM_BUFFER_ADDR * make mm_sp_svc_uuid static * replace EINVAL with ENOMEM in ffa_discover_mm_sp_id() when calloc() fails * improve use of unmap_sysmem() in ffa_mm_communicate()
v6:
* add FF-A runtime discovery at MM communication level * drop EFI runtime support for FF-A MM communication * revert the changes in include/mm_communication.h for efi_mm_communicate_header and smm_variable_access structures
v4:
* use the new FF-A driver interfaces * discover MM partitions at runtime * copy FF-A driver private data to EFI runtime section at ExitBootServices() * drop use of FFA_ERR_STAT_SUCCESS error code * replace EFI_BUFFER_TOO_SMALL with EFI_OUT_OF_RESOURCES in ffa_mm_communicate(). No need for efi_memcpy_runtime() anymore * revert the error log in mm_communicate() in case of failure * remove packed attribute from efi_mm_communicate_header and smm_variable_communicate_header
v2:
* set default values to 0 for FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_ADDR and MM_SP_UUID_DATA and add warnings
v1:
* introduce FF-A MM communication
include/mm_communication.h | 13 ++ lib/efi_loader/Kconfig | 46 +++++- lib/efi_loader/efi_variable_tee.c | 257 +++++++++++++++++++++++++++++- 3 files changed, 309 insertions(+), 7 deletions(-)
diff --git a/include/mm_communication.h b/include/mm_communication.h index e65fbde60d..f17847583b 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. sughosh.ganu@linaro.org * Copyright (C) 2020 Linaro Ltd. ilias.apalodimas@linaro.org + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#ifndef _MM_COMMUNICATION_H_ @@ -13,6 +16,9 @@
#include <part_efi.h>
+/* MM service UUID string (big-endian format). This UUID is common across all MM SPs */ +#define MM_SP_UUID "33d532ed-e699-0942-c09c-a798d9cd722d" + /* * Interface to the pseudo Trusted Application (TA), which provides a * communication channel with the Standalone MM (Management Mode) @@ -248,4 +254,11 @@ struct smm_variable_var_check_property { u16 name[]; };
+/* supported MM transports */ +enum mm_comms_select { + MM_COMMS_UNDEFINED, + MM_COMMS_FFA, + MM_COMMS_OPTEE +}; + #endif /* _MM_COMMUNICATION_H_ */ diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index c5835e6ef6..553e6a30a2 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -55,13 +55,55 @@ 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" - depends on OPTEE + bool "UEFI variables storage service via the trusted world" + select ARM_FFA_TRANSPORT + select TEE + select 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" + default 0 + depends on EFI_MM_COMM_TEE + 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" + default 0 + depends on EFI_MM_COMM_TEE + 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" + default 0x0 + depends on EFI_MM_COMM_TEE + 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 dfef18435d..5137b871ea 100644 --- a/lib/efi_loader/efi_variable_tee.c +++ b/lib/efi_loader/efi_variable_tee.c @@ -4,17 +4,34 @@ * * Copyright (C) 2019 Linaro Ltd. sughosh.ganu@linaro.org * Copyright (C) 2019 Linaro Ltd. ilias.apalodimas@linaro.org + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> +#include <arm_ffa.h> +#include <cpu_func.h> +#include <dm.h> #include <efi.h> #include <efi_api.h> #include <efi_loader.h> #include <efi_variable.h> -#include <tee.h> #include <malloc.h> +#include <mapmem.h> #include <mm_communication.h> +#include <tee.h> + +/* 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; extern struct efi_var_file __efi_runtime_data *efi_var_buf; static efi_uintn_t max_buffer_size; /* comm + var + func + data */ static efi_uintn_t max_payload_size; /* func + data */ @@ -145,16 +162,241 @@ static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize) }
/** - * 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_err("EFI: Cannot find FF-A bus device, trying Optee comms\n"); + return MM_COMMS_OPTEE; + } + + return MM_COMMS_FFA; +} + +/** + * 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) { efi_status_t ret; + enum mm_comms_select mm_comms; struct efi_mm_communicate_header *mm_hdr; struct smm_variable_communicate_header *var_hdr;
@@ -162,7 +404,12 @@ static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) 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); + 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); + if (ret != EFI_SUCCESS) { log_err("%s failed!\n", __func__); return ret; @@ -697,7 +944,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);
/*

turn on EFI MM communication
On corstone1000 platform MM communication between u-boot and the secure world (Optee) is done using the FF-A bus.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v15:
Simon:
* use CONFIG_FFA_SHARED_MM_BUF_* configs in place of FFA_SHARED_MM_BUFFER_*
v13:
* remove FF-A config in the defconfig (because it's enabled automatically by CONFIG_EFI_MM_COMM_TEE)
v9:
* update copyright string
v8:
* drop OP-TEE configs from Corstone-1000 defconfig
v7:
* improve the definition of FFA_SHARED_MM_BUFFER_ADDR and FFA_SHARED_MM_BUFFER_OFFSET * update FFA_SHARED_MM_BUFFER_ADDR value
v6:
* corstone-1000: enable optee driver * corstone-1000: remove CONFIG_ARM_FFA_EFI_RUNTIME_MODE from the defconfig
v4:
* corstone-1000: turn on EFI MM communication
configs/corstone1000_defconfig | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/configs/corstone1000_defconfig b/configs/corstone1000_defconfig index a8a79fd105..5689712545 100644 --- a/configs/corstone1000_defconfig +++ b/configs/corstone1000_defconfig @@ -65,3 +65,7 @@ CONFIG_EFI_CAPSULE_ON_DISK=y CONFIG_EFI_IGNORE_OSINDICATIONS=y CONFIG_FWU_MULTI_BANK_UPDATE=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

Hi Tom, Simon,
On Thu, Jul 13, 2023 at 02:28:37PM +0100, Abdellatif El Khlifi wrote:
Adding support for Arm FF-A v1.0 (Arm Firmware Framework for Armv8-A) [A].
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.
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.
=> dm tree
Class Index Probed Driver Name
... firmware 0 [ + ] psci |-- psci ffa 0 [ ] arm_ffa | `-- arm_ffa ...
Clients are able to probe then use the FF-A bus by calling the DM class searching APIs (e.g: uclass_first_device).
This implementation of the specification provides support for Aarch64.
The FF-A driver uses the SMC ABIs defined by the FF-A specification to:
- Discover the presence of secure partitions (SPs) of interest - Access an SP's service through communication protocols (e.g: EFI MM communication protocol)
The FF-A support provides the following features:
- Being generic by design and can be used by any Arm 64-bit platform - FF-A support can be compiled and used without EFI - Support for SMCCCv1.2 x0-x17 registers - Support for SMC32 calling convention - Support for 32-bit and 64-bit FF-A direct messaging - Support for FF-A MM communication (compatible with EFI boot time) - Enabling FF-A and MM communication in Corstone1000 platform as a use case - 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. - Sandbox FF-A test cases. - A new command called armffa is provided as an example of how to access the FF-A bus
For more details about the FF-A support please refer to [B] and refer to [C] for how to use the armffa command.
Please find at [D] an example of the expected boot logs when enabling FF-A support for a platform. In this example the platform is Corstone1000. But it can be any Arm 64-bit platform.
Changelog of changes:
v15:
Simon:
- drop commit "log: select physical address formatting in a generic way", this will be sent as a follow-up commit independently from this patchset
- armffa.c : integrate PHYS_ADDR_LN
- replace FFA_SHARED_MM_BUFFER_* defines with configs
A gentle reminder about this patchset. All remaining comments have been addressed in v15.
Cheers Abdellatif

On Thu, 13 Jul 2023 14:28:37 +0100, Abdellatif El Khlifi wrote:
Adding support for Arm FF-A v1.0 (Arm Firmware Framework for Armv8-A) [A].
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.
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.
[...]
Applied to u-boot/master, thanks!

On Thu, Jul 13, 2023 at 02:28:37PM +0100, Abdellatif El Khlifi wrote:
Adding support for Arm FF-A v1.0 (Arm Firmware Framework for Armv8-A) [A].
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.
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.
=> dm tree
Class Index Probed Driver Name
... firmware 0 [ + ] psci |-- psci ffa 0 [ ] arm_ffa | `-- arm_ffa ...
Clients are able to probe then use the FF-A bus by calling the DM class searching APIs (e.g: uclass_first_device).
This implementation of the specification provides support for Aarch64.
The FF-A driver uses the SMC ABIs defined by the FF-A specification to:
- Discover the presence of secure partitions (SPs) of interest - Access an SP's service through communication protocols (e.g: EFI MM communication protocol)
The FF-A support provides the following features:
- Being generic by design and can be used by any Arm 64-bit platform - FF-A support can be compiled and used without EFI - Support for SMCCCv1.2 x0-x17 registers - Support for SMC32 calling convention - Support for 32-bit and 64-bit FF-A direct messaging - Support for FF-A MM communication (compatible with EFI boot time) - Enabling FF-A and MM communication in Corstone1000 platform as a use case - 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. - Sandbox FF-A test cases. - A new command called armffa is provided as an example of how to access the FF-A bus
For more details about the FF-A support please refer to [B] and refer to [C] for how to use the armffa command.
Please find at [D] an example of the expected boot logs when enabling FF-A support for a platform. In this example the platform is Corstone1000. But it can be any Arm 64-bit platform.
Changelog of changes:
v15:
So, this does not pass CI: https://source.denx.de/u-boot/u-boot/-/jobs/662303 https://source.denx.de/u-boot/u-boot/-/jobs/662304#L2234
And it's on me for merging this before letting the CI run I kicked complete, sorry. But I'm reverting this now.

Hi Tom,
On Mon, Jul 24, 2023 at 07:50:07PM -0400, Tom Rini wrote:
On Thu, Jul 13, 2023 at 02:28:37PM +0100, Abdellatif El Khlifi wrote:
Adding support for Arm FF-A v1.0 (Arm Firmware Framework for Armv8-A) [A].
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.
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.
...
Changelog of changes:
v15:
So, this does not pass CI: https://source.denx.de/u-boot/u-boot/-/jobs/662303 https://source.denx.de/u-boot/u-boot/-/jobs/662304#L2234
And it's on me for merging this before letting the CI run I kicked complete, sorry. But I'm reverting this now.
The v15 patchset has been built successfully on next using this SHA: [1].
The tested platforms are the following: [2].
Is there any action from my side please ?
[1]: 56c7fac8ad89955d3e5d08864bbd1343a058bf4b [2]: tested platforms
sandbox64 sandbox corstone1000-fvp corstone1000-mps3 qemu_arm64
Cheers, Abdellatif

On Tue, Jul 25, 2023 at 10:26:16AM +0100, Abdellatif El Khlifi wrote:
Hi Tom,
On Mon, Jul 24, 2023 at 07:50:07PM -0400, Tom Rini wrote:
On Thu, Jul 13, 2023 at 02:28:37PM +0100, Abdellatif El Khlifi wrote:
Adding support for Arm FF-A v1.0 (Arm Firmware Framework for Armv8-A) [A].
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.
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.
...
Changelog of changes:
v15:
So, this does not pass CI: https://source.denx.de/u-boot/u-boot/-/jobs/662303 https://source.denx.de/u-boot/u-boot/-/jobs/662304#L2234
And it's on me for merging this before letting the CI run I kicked complete, sorry. But I'm reverting this now.
The v15 patchset has been built successfully on next using this SHA: [1].
The tested platforms are the following: [2].
Is there any action from my side please ?
[2]: tested platforms
sandbox64 sandbox corstone1000-fvp corstone1000-mps3 qemu_arm64
Yes, you need to go and fix all of the problems that CI is showing: - On 64bit, espresso7420 is building cmd/armffa.c and pointing out an unused variable. - Maybe related to why that platform is building the code, a ton of 32bit platforms are building the code and showing warnings. And also that your Kconfig logic is wrong. - Finally, the MAINTAINERS file needs to be alphabetical.

Hi Tom,
On Tue, Jul 25, 2023 at 09:47:24AM -0400, Tom Rini wrote:
On Tue, Jul 25, 2023 at 10:26:16AM +0100, Abdellatif El Khlifi wrote:
Hi Tom,
On Mon, Jul 24, 2023 at 07:50:07PM -0400, Tom Rini wrote:
On Thu, Jul 13, 2023 at 02:28:37PM +0100, Abdellatif El Khlifi wrote:
Adding support for Arm FF-A v1.0 (Arm Firmware Framework for Armv8-A) [A].
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.
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.
...
Changelog of changes:
v15:
So, this does not pass CI: https://source.denx.de/u-boot/u-boot/-/jobs/662303 https://source.denx.de/u-boot/u-boot/-/jobs/662304#L2234
And it's on me for merging this before letting the CI run I kicked complete, sorry. But I'm reverting this now.
The v15 patchset has been built successfully on next using this SHA: [1].
The tested platforms are the following: [2].
Is there any action from my side please ?
[2]: tested platforms
sandbox64 sandbox corstone1000-fvp corstone1000-mps3 qemu_arm64
Yes, you need to go and fix all of the problems that CI is showing:
- On 64bit, espresso7420 is building cmd/armffa.c and pointing out an unused variable.
- Maybe related to why that platform is building the code, a ton of 32bit platforms are building the code and showing warnings. And also that your Kconfig logic is wrong.
- Finally, the MAINTAINERS file needs to be alphabetical.
Thanks for the details. I fixed all the issues and will send them shortly in v16 (a minor change).
espresso7420 builds fine and Arm 32-bit boards no longer automatically pick up FF-A.
For the alphabetical order in MAINTAINERS, please help me undertand where the issue is. The FF-A part is already in alphabetical order:
ARM CORTINA ACCESS CAxxxx ...
ARM FF-A M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com 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 ...
Cheers, Abdellatif

On Tue, Jul 25, 2023 at 07:34:39PM +0100, Abdellatif El Khlifi wrote:
Hi Tom,
On Tue, Jul 25, 2023 at 09:47:24AM -0400, Tom Rini wrote:
On Tue, Jul 25, 2023 at 10:26:16AM +0100, Abdellatif El Khlifi wrote:
Hi Tom,
On Mon, Jul 24, 2023 at 07:50:07PM -0400, Tom Rini wrote:
On Thu, Jul 13, 2023 at 02:28:37PM +0100, Abdellatif El Khlifi wrote:
Adding support for Arm FF-A v1.0 (Arm Firmware Framework for Armv8-A) [A].
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.
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.
...
Changelog of changes:
v15:
So, this does not pass CI: https://source.denx.de/u-boot/u-boot/-/jobs/662303 https://source.denx.de/u-boot/u-boot/-/jobs/662304#L2234
And it's on me for merging this before letting the CI run I kicked complete, sorry. But I'm reverting this now.
The v15 patchset has been built successfully on next using this SHA: [1].
The tested platforms are the following: [2].
Is there any action from my side please ?
[2]: tested platforms
sandbox64 sandbox corstone1000-fvp corstone1000-mps3 qemu_arm64
Yes, you need to go and fix all of the problems that CI is showing:
- On 64bit, espresso7420 is building cmd/armffa.c and pointing out an unused variable.
- Maybe related to why that platform is building the code, a ton of 32bit platforms are building the code and showing warnings. And also that your Kconfig logic is wrong.
- Finally, the MAINTAINERS file needs to be alphabetical.
Thanks for the details. I fixed all the issues and will send them shortly in v16 (a minor change).
espresso7420 builds fine and Arm 32-bit boards no longer automatically pick up FF-A.
Thanks.
For the alphabetical order in MAINTAINERS, please help me undertand where the issue is. The FF-A part is already in alphabetical order:
ARM CORTINA ACCESS CAxxxx
The UUID test portion was not, however.

Adding support for Arm FF-A v1.0 (Arm Firmware Framework for Armv8-A) [A].
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.
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.
=> dm tree
Class Index Probed Driver Name ----------------------------------------------------------- ... firmware 0 [ + ] psci |-- psci ffa 0 [ ] arm_ffa | `-- arm_ffa ...
Clients are able to probe then use the FF-A bus by calling the DM class searching APIs (e.g: uclass_first_device).
This implementation of the specification provides support for Aarch64.
The FF-A driver uses the SMC ABIs defined by the FF-A specification to:
- Discover the presence of secure partitions (SPs) of interest - Access an SP's service through communication protocols (e.g: EFI MM communication protocol)
The FF-A support provides the following features:
- Being generic by design and can be used by any Arm 64-bit platform - FF-A support can be compiled and used without EFI - Support for SMCCCv1.2 x0-x17 registers - Support for SMC32 calling convention - Support for 32-bit and 64-bit FF-A direct messaging - Support for FF-A MM communication (compatible with EFI boot time) - Enabling FF-A and MM communication in Corstone1000 platform as a use case - 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. - Sandbox FF-A test cases. - A new command called armffa is provided as an example of how to access the FF-A bus
For more details about the FF-A support please refer to [B] and refer to [C] for how to use the armffa command.
Please find at [D] an example of the expected boot logs when enabling FF-A support for a platform. In this example the platform is Corstone1000. But it can be any Arm 64-bit platform.
Changelog of changes: ===========================
v16:
Tom:
* lib/efi_loader/Kconfig: rather than automatically selecting OPTEE and ARM_FFA_TRANSPORT configs by EFI_MM_COMM_TEE, set them as dependencies (Otherwise FF-A will be automatically enabled for boards that don't need it).
* configs/corstone1000_defconfig: enable MM communication by setting the configs: ARM_FFA_TRANSPORT, OPTEE, TEE
v15: [15]
Simon:
* drop commit "log: select physical address formatting in a generic way", this will be sent as a follow-up commit independently from this patchset * armffa.c : integrate PHYS_ADDR_LN * replace FFA_SHARED_MM_BUFFER_* defines with configs
v14: [14]
Simon:
* add to log.h a generic physical address formatting
Ilias:
* armffa command: in do_ffa_ping() reject the SP ID if it's 0 * MM comms: drop truncating var_payload->size when using FF-A * MM comms: map the MM SP return codes to errnos * address nits
v13: [13]
Ilias: * remove FF-A and Optee ifdefs in efi_variable_tee.c * doc minor change: specify in the readme that the user should call ffa_rxtx_unmap() driver operation to unmap the RX/TX buffers on demand.
v12: [12]
* remove the global variable (dscvry_info), use uc_priv instead * replace dscvry_info.invoke_ffa_fn() with a weak invoke_ffa_fn (user drivers can override it) * improve FFA_PARTITION_INFO_GET implementation (clients no longer need to calloc a buffer) * remove reparenting by making the sandbox emulator parent of the FF-A device in the DT * improve argument checks for the armffa command * address nits
v11: [11]
* move ffa_try_discovery() from the uclass to the Arm FF-A driver * rename ffa_try_discovery() to arm_ffa_discover() * add arm_ prefix to the Arm FF-A driver functions * use U_BOOT_CMD_WITH_SUBCMDS for armffa command * store the sandbox emulator pointer in the FF-A device uc_priv (struct ffa_priv) * set the emulator as parent of the sandbox FF-A device * rename select_ffa_mm_comms() to select_mm_comms() * improve the logic of MM transport selection in mm_communicate() * use ut_asserteq_mem() in uuid_str_to_le_bin test case * address nits
v10: [10]
* provide the FF-A driver operations through the Uclass (arm-ffa-uclass.c) * move the generic FF-A methods to the Uclass * keep Arm specific methods in the Arm driver (arm-ffa.c renamed from core.c) * split the FF-A sandbox support into an emulator (ffa-emul-uclass.c) and a driver (sandbox_ffa.c) * use the FF-A driver Uclass operations by clients (armffa command, tests, MM comms) * use uclass_first_device to search and probe the FF-A device (whether it is on Arm or on sandbox) * address nits
v9: [9]
* integrate the FF-A bus discovery in the DM and use ARM_SMCCC_FEATURES for binding * align FF-A sandbox driver with FF-A discovery through DM * use DM class APIs to probe and interact with the FF-A bus (in FF-A MM comms, armffa command, sandbox tests) * add documentation for the armffa command: doc/usage/cmd/armffa.rst * introduce testcase for uuid_str_to_le_bin
v8: [8]
* pass the FF-A bus device to the bus operations * isolate the compilation choices between FF-A and OP-TEE * drop OP-TEE configs from Corstone-1000 defconfig * make ffa_get_partitions_info() second argument to be an SP count in both modes
v7: [7]
* add support for 32-bit direct messaging (now we have 32-bit and 64-bit support) * set the MM door bell event to use 64-bit direct messaging * issue a compile time error when one of these macros are not found : FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_OFFSET, FFA_SHARED_MM_BUFFER_ADDR * make uuid_str_to_le_bin() implementation similar to uuid_str_to_bin() by using same APIs
v6: [6]
* remove clearing x0-x17 registers after SMC calls * drop use of EFI runtime support for FF-A (We decided with Linaro to add this later) * drop discovery from initcalls (discovery will be on demand by FF-A users) * add FF-A runtime discovery at MM communication level * update the documentation and move it to doc/arch/arm64.ffa.rst
v5: [5]
* move changelogs in each commit to the changes section
v4: [4]
* add FF-A support README (doc/README.ffa.drv) * improving error handling by mapping the FF-A errors to standard errors and logs * replacing panics with an error log * align sandbox driver and tests with the new FF-A driver interfaces and new way of error handling * use the new FF-A driver interfaces for MM communication * discover MM partitions at runtime * copy FF-A driver private data to EFI runtime section at ExitBootServices() * moving the FF-A driver work to drivers/firmware/arm-ffa * improving features discovery in FFA_FEATURES * add remove/unbind functions to the FF-A core device * improve how the driver behaves when bus discovery is done more than once * move clearing x0-x17 registers code into a new macro like done in the linux kernel * enable EFI MM communication for the Corstone1000 platform
v3: [3]
* port x0-x17 registers support from linux kernel as defined by SMCCCv1.2 * align the interfaces of the u-boot FF-A driver with those in the linux FF-A driver * remove the FF-A helper layer * make the u-boot FF-A driver independent from EFI * provide an optional config that enables copying the driver data to EFI runtime section at ExitBootServices service * use 64-bit version of FFA_RXTX_MAP, FFA_MSG_SEND_DIRECT_{REQ, RESP} * update armffa command with the new driver interfaces
v2 [2]:
* make FF-A bus discoverable using device_{bind, probe} APIs * remove device tree support
v1 [1]:
* introduce FF-A bus driver with device tree support * introduce armffa command * introduce FF-A Sandbox driver * add FF-A Sandbox test cases * introduce FF-A MM communication
Cheers, Abdellatif
List of previous patches:
[1]: https://lore.kernel.org/all/20220329151659.16894-1-abdellatif.elkhlifi@arm.c... [2]: https://lore.kernel.org/all/20220415122803.16666-1-abdellatif.elkhlifi@arm.c... [3]: https://lore.kernel.org/all/20220801172053.20163-1-abdellatif.elkhlifi@arm.c... [4]: https://lore.kernel.org/all/20220926101723.9965-1-abdellatif.elkhlifi@arm.co... [5]: https://lore.kernel.org/all/20220926140827.15125-1-abdellatif.elkhlifi@arm.c... [6]: https://lore.kernel.org/all/20221013103857.614-1-abdellatif.elkhlifi@arm.com... [7]: https://lore.kernel.org/all/20221107192055.21669-1-abdellatif.elkhlifi@arm.c... [8]: https://lore.kernel.org/all/20221122131751.22747-1-abdellatif.elkhlifi@arm.c... [9]: https://lore.kernel.org/all/20230310141016.137986-1-abdellatif.elkhlifi@arm.... [10]: https://lore.kernel.org/all/20230328161157.219375-1-abdellatif.elkhlifi@arm.... [11]: https://lore.kernel.org/all/20230412094245.44674-1-abdellatif.elkhlifi@arm.c... [12]: https://lore.kernel.org/all/20230512121044.111574-1-abdellatif.elkhlifi@arm.... [13]: https://lore.kernel.org/all/20230616152817.319869-1-abdellatif.elkhlifi@arm.... [14]: https://lore.kernel.org/all/20230707144410.228472-1-abdellatif.elkhlifi@arm.... [15]: https://lore.kernel.org/all/20230713132847.176000-1-abdellatif.elkhlifi@arm....
More details:
[A]: https://developer.arm.com/documentation/den0077/latest/ [B]: doc/arch/arm64.ffa.rst [C]: doc/usage/cmd/armffa.rst [D]: example of boot logs when enabling FF-A
``` 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 ```
Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Rob Herring robh@kernel.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Achin Gupta achin.gupta@arm.com Cc: Drew Reed Drew.Reed@arm.com Cc: Xueliang Zhong Xueliang.Zhong@arm.com
Abdellatif El Khlifi (10): arm64: smccc: add support for SMCCCv1.2 x0-x17 registers lib: uuid: introduce uuid_str_to_le_bin function lib: uuid: introduce testcase for uuid_str_to_le_bin arm_ffa: introduce Arm FF-A support arm_ffa: introduce armffa command arm_ffa: introduce sandbox FF-A support arm_ffa: introduce sandbox test cases for UCLASS_FFA arm_ffa: introduce armffa command Sandbox test arm_ffa: efi: introduce FF-A MM communication arm_ffa: efi: corstone1000: enable MM communication
MAINTAINERS | 18 + arch/arm/cpu/armv8/smccc-call.S | 57 +- arch/arm/lib/asm-offsets.c | 16 + 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 ++ cmd/Kconfig | 10 + cmd/Makefile | 1 + cmd/armffa.c | 202 ++++ configs/corstone1000_defconfig | 7 + configs/sandbox64_defconfig | 1 + configs/sandbox_defconfig | 1 + doc/arch/arm64.ffa.rst | 261 ++++ doc/arch/index.rst | 1 + doc/arch/sandbox/sandbox.rst | 1 + doc/usage/cmd/armffa.rst | 94 ++ doc/usage/index.rst | 1 + drivers/Makefile | 1 + drivers/firmware/Kconfig | 1 + drivers/firmware/arm-ffa/Kconfig | 42 + drivers/firmware/arm-ffa/Makefile | 16 + drivers/firmware/arm-ffa/arm-ffa-uclass.c | 1065 +++++++++++++++++ drivers/firmware/arm-ffa/arm-ffa.c | 104 ++ drivers/firmware/arm-ffa/ffa-emul-uclass.c | 720 +++++++++++ drivers/firmware/arm-ffa/sandbox_ffa.c | 110 ++ include/arm_ffa.h | 213 ++++ include/arm_ffa_priv.h | 246 ++++ include/dm/uclass-id.h | 7 + include/linux/arm-smccc.h | 45 + include/mm_communication.h | 13 + include/uuid.h | 15 + lib/efi_loader/Kconfig | 44 +- lib/efi_loader/efi_variable_tee.c | 257 +++- lib/uuid.c | 48 + test/cmd/Makefile | 2 + test/cmd/armffa.c | 33 + test/dm/Makefile | 3 +- test/dm/ffa.c | 261 ++++ test/lib/Makefile | 1 + test/lib/uuid.c | 41 + 41 files changed, 4160 insertions(+), 9 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 cmd/armffa.c create mode 100644 doc/arch/arm64.ffa.rst create mode 100644 doc/usage/cmd/armffa.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/ffa-emul-uclass.c create mode 100644 drivers/firmware/arm-ffa/sandbox_ffa.c create mode 100644 include/arm_ffa.h create mode 100644 include/arm_ffa_priv.h create mode 100644 test/cmd/armffa.c create mode 100644 test/dm/ffa.c create mode 100644 test/lib/uuid.c

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 abdellatif.elkhlifi@arm.com Reviewed-by: Ilias Apalodimas ilias.apalodimas@linaro.org Reviewed-by: Jens Wiklander jens.wiklander@linaro.org Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com
---
Changelog: ===============
v9:
* update the copyright string
v7:
* improve indentation of ARM_SMCCC_1_2_REGS_Xn_OFFS
v4:
* rename the commit title and improve description new commit title: the current
v3:
* port x0-x17 registers support from linux kernel as defined by SMCCCv1.2 commit title: arm64: smccc: add Xn registers support used by SMC calls
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 dc92b28777..93f66d3366 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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com +*/ #include <linux/linkage.h> #include <linux/arm-smccc.h> #include <generated/asm-offsets.h> @@ -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 6de0ce9152..181a8ac4c2 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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> @@ -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 e1d09884a1..f44e9e8f93 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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */ #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

convert UUID string to little endian binary data
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v9:
* add a full function prototype description in uuid.h
v8:
* use simple_strtoull() in uuid_str_to_le_bin() to support 32-bit platforms
v7:
* rename be_uuid_str_to_le_bin() to uuid_str_to_le_bin() * make uuid_str_to_le_bin() implementation similar to uuid_str_to_bin() by using same APIs
v4:
* rename ffa_uuid_str_to_bin to be_uuid_str_to_le_bin and put in a standalone commit (the current)
v3:
* introduce ffa_uuid_str_to_bin (provided by arm_ffa: introduce Arm FF-A low-level driver)
include/uuid.h | 15 +++++++++++++++ lib/uuid.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+)
diff --git a/include/uuid.h b/include/uuid.h index 4a4883d3b5..89b93e642b 100644 --- a/include/uuid.h +++ b/include/uuid.h @@ -2,6 +2,10 @@ /* * Copyright (C) 2014 Samsung Electronics * Przemyslaw Marczak p.marczak@samsung.com + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */ #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 96e1af3c8b..45f325d964 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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> @@ -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. *

provide a test case
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com
---
Changelog: ===============
v16:
* MAINTAINERS: place the UUID part in an alphabetical order
v11:
* use ut_asserteq_mem()
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 d724b64673..4324965d26 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1555,6 +1555,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 abdellatif.elkhlifi@arm.com +S: Maintained +F: test/lib/uuid.c + VIDEO M: Anatolij Gustschin agust@denx.de S: Maintained diff --git a/test/lib/Makefile b/test/lib/Makefile index e0bd9e04e8..e75a263e6a 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 0000000000..e24331a136 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <uuid.h> +#include <test/lib.h> +#include <test/test.h> +#include <test/ut.h> + +/* 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);

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 abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
---
Changelog: ===============
v13:
* doc minor change: specify in the readme that the user should call ffa_rxtx_unmap() driver operation to unmap the RX/TX buffers on demand.
v12:
* remove dscvry_info * replace dscvry_info.invoke_ffa_fn() with a weak invoke_ffa_fn (user drivers can override it) * improve FFA_PARTITION_INFO_GET implementation (clients no longer need to calloc a buffer) * address nits
v11:
* move ffa_try_discovery() from the uclass to the Arm FF-A driver * rename ffa_try_discovery() to arm_ffa_discover() * pass dev as an argument of arm_ffa_discover() * add arm_ prefix to the Arm FF-A driver functions * add emul field in struct ffa_discovery_info * address nits
v10:
* provide the driver operations through the Uclass * move the generic FF-A methods to the Uclass * keep Arm specific methods in the Arm driver (arm-ffa.c) * rename core.c to arm-ffa.c * address nits
v9:
* integrate the FF-A bus discovery in the DM and use ARM_SMCCC_FEATURES for binding
v8:
* make ffa_get_partitions_info() second argument to be an SP count in both modes * update ffa_bus_prvdata_get() to return a pointer rather than a pointer address * remove packing from ffa_partition_info and ffa_send_direct_data structures * pass the FF-A bus device to the bus operations
v7:
* add support for 32-bit direct messaging * rename be_uuid_str_to_le_bin() to uuid_str_to_le_bin() * improve the declaration of error handling mapping * stating in doc/arch/arm64.ffa.rst that EFI runtime is not supported
v6:
* drop use of EFI runtime support (We decided with Linaro to add this later) * drop discovery from initcalls (discovery will be on demand by FF-A users) * set the alignment of the RX/TX buffers to the larger translation granule size * move FF-A RX/TX buffers unmapping at ExitBootServices() to a separate commit * update the documentation and move it to doc/arch/arm64.ffa.rst
v4:
* add doc/README.ffa.drv * moving the FF-A driver work to drivers/firmware/arm-ffa * use less #ifdefs in lib/efi_loader/efi_boottime.c and replace #if defined by #if CONFIG_IS_ENABLED * improving error handling by mapping the FF-A errors to standard errors and logs * replacing panics with an error log and returning an error code * improving features discovery in FFA_FEATURES by introducing rxtx_min_pages private data field * add ffa_remove and ffa_unbind functions * improve how the driver behaves when bus discovery is done more than once
v3:
* align the interfaces of the U-Boot FF-A driver with those in the linux FF-A driver * remove the FF-A helper layer * make the U-Boot FF-A driver independent from EFI * provide an optional config that enables copying the driver data to EFI runtime section at ExitBootServices service * use 64-bit version of FFA_RXTX_MAP, FFA_MSG_SEND_DIRECT_{REQ, RESP}
v2:
* make FF-A bus discoverable using device_{bind, probe} APIs * remove device tree support
v1:
* introduce FF-A bus driver with device tree support
MAINTAINERS | 8 + doc/arch/arm64.ffa.rst | 238 ++++ 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, 1941 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 4324965d26..4fd5768de0 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 abdellatif.elkhlifi@arm.com +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 sbabic@denx.de M: Fabio Estevam festevam@gmail.com diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst new file mode 100644 index 0000000000..4f817f053c --- /dev/null +++ b/doc/arch/arm64.ffa.rst @@ -0,0 +1,238 @@ +.. 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(). +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: + + - 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 abdellatif.elkhlifi@arm.com + +.. _`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 b8da4b8c8e..2f916f4026 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 78dcf62f76..5044f45253 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 eae1c8ddc9..8789b1ea14 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 0000000000..9200c8028b --- /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 0000000000..11b1766285 --- /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 open-source-office@arm.com +# +# Authors: +# Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + +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 0000000000..ffa9d81fa7 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ +#include <common.h> +#include <arm_ffa.h> +#include <arm_ffa_priv.h> +#include <dm.h> +#include <log.h> +#include <malloc.h> +#include <string.h> +#include <uuid.h> +#include <asm/global_data.h> +#include <dm/device-internal.h> +#include <dm/devres.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> + +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 0000000000..68df75bd9e --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <arm_ffa.h> +#include <arm_ffa_priv.h> +#include <dm.h> +#include <log.h> +#include <asm/global_data.h> +#include <dm/device-internal.h> +#include <linux/errno.h> + +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 0000000000..4338f9c9b1 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#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 0000000000..db9b1be995 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __ARM_FFA_H +#define __ARM_FFA_H + +#include <linux/printk.h> + +/* + * 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 0000000000..d564c33c64 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __ARM_FFA_PRV_H +#define __ARM_FFA_PRV_H + +#include <mapmem.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> + +/* 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 <linux/arm-smccc.h> +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 307ad6931c..3c6af2e3d2 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -4,6 +4,11 @@ * * (C) Copyright 2012 * Pavel Herrmann morpheus.ibis@gmail.com + * + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#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 */

On Wed, Jul 26, 2023 at 10:44:57AM +0100, Abdellatif El Khlifi wrote:
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].
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
Changelog:
v13:
- doc minor change: specify in the readme that the user should call ffa_rxtx_unmap() driver operation to unmap the RX/TX buffers on demand.
v12:
- remove dscvry_info
- replace dscvry_info.invoke_ffa_fn() with a weak invoke_ffa_fn (user drivers can override it)
- improve FFA_PARTITION_INFO_GET implementation (clients no longer need to calloc a buffer)
- address nits
v11:
- move ffa_try_discovery() from the uclass to the Arm FF-A driver
- rename ffa_try_discovery() to arm_ffa_discover()
- pass dev as an argument of arm_ffa_discover()
- add arm_ prefix to the Arm FF-A driver functions
- add emul field in struct ffa_discovery_info
- address nits
v10:
- provide the driver operations through the Uclass
- move the generic FF-A methods to the Uclass
- keep Arm specific methods in the Arm driver (arm-ffa.c)
- rename core.c to arm-ffa.c
- address nits
v9:
- integrate the FF-A bus discovery in the DM and use ARM_SMCCC_FEATURES for binding
v8:
- make ffa_get_partitions_info() second argument to be an SP count in both modes
- update ffa_bus_prvdata_get() to return a pointer rather than a pointer address
- remove packing from ffa_partition_info and ffa_send_direct_data structures
- pass the FF-A bus device to the bus operations
v7:
- add support for 32-bit direct messaging
- rename be_uuid_str_to_le_bin() to uuid_str_to_le_bin()
- improve the declaration of error handling mapping
- stating in doc/arch/arm64.ffa.rst that EFI runtime is not supported
v6:
- drop use of EFI runtime support (We decided with Linaro to add this later)
- drop discovery from initcalls (discovery will be on demand by FF-A users)
- set the alignment of the RX/TX buffers to the larger translation granule size
- move FF-A RX/TX buffers unmapping at ExitBootServices() to a separate commit
- update the documentation and move it to doc/arch/arm64.ffa.rst
v4:
- add doc/README.ffa.drv
- moving the FF-A driver work to drivers/firmware/arm-ffa
- use less #ifdefs in lib/efi_loader/efi_boottime.c and replace #if defined by #if CONFIG_IS_ENABLED
- improving error handling by mapping the FF-A errors to standard errors and logs
- replacing panics with an error log and returning an error code
- improving features discovery in FFA_FEATURES by introducing rxtx_min_pages private data field
- add ffa_remove and ffa_unbind functions
- improve how the driver behaves when bus discovery is done more than once
v3:
- align the interfaces of the U-Boot FF-A driver with those in the linux FF-A driver
- remove the FF-A helper layer
- make the U-Boot FF-A driver independent from EFI
- provide an optional config that enables copying the driver data to EFI runtime section at ExitBootServices service
- use 64-bit version of FFA_RXTX_MAP, FFA_MSG_SEND_DIRECT_{REQ, RESP}
v2:
- make FF-A bus discoverable using device_{bind, probe} APIs
- remove device tree support
v1:
- introduce FF-A bus driver with device tree support
MAINTAINERS | 8 + doc/arch/arm64.ffa.rst | 238 ++++ 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, 1941 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 4324965d26..4fd5768de0 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 abdellatif.elkhlifi@arm.com +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 sbabic@denx.de M: Fabio Estevam festevam@gmail.com diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst new file mode 100644 index 0000000000..4f817f053c --- /dev/null +++ b/doc/arch/arm64.ffa.rst @@ -0,0 +1,238 @@ +.. 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(). +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:
- 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 abdellatif.elkhlifi@arm.com
+.. _`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 b8da4b8c8e..2f916f4026 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 78dcf62f76..5044f45253 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 eae1c8ddc9..8789b1ea14 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 0000000000..9200c8028b --- /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 0000000000..11b1766285 --- /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 open-source-office@arm.com +# +# Authors: +# Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
+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 0000000000..ffa9d81fa7 --- /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 open-source-office@arm.com
- Authors:
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include <common.h> +#include <arm_ffa.h> +#include <arm_ffa_priv.h> +#include <dm.h> +#include <log.h> +#include <malloc.h> +#include <string.h> +#include <uuid.h> +#include <asm/global_data.h> +#include <dm/device-internal.h> +#include <dm/devres.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h>
+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 0000000000..68df75bd9e --- /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 open-source-office@arm.com
- Authors:
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include <common.h> +#include <arm_ffa.h> +#include <arm_ffa_priv.h> +#include <dm.h> +#include <log.h> +#include <asm/global_data.h> +#include <dm/device-internal.h> +#include <linux/errno.h>
+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 0000000000..4338f9c9b1 --- /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 open-source-office@arm.com
- Authors:
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#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 0000000000..db9b1be995 --- /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 open-source-office@arm.com
- Authors:
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#ifndef __ARM_FFA_H +#define __ARM_FFA_H
+#include <linux/printk.h>
+/*
- 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 0000000000..d564c33c64 --- /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 open-source-office@arm.com
- Authors:
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#ifndef __ARM_FFA_PRV_H +#define __ARM_FFA_PRV_H
+#include <mapmem.h> +#include <linux/bitfield.h> +#include <linux/bitops.h>
+/* 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 <linux/arm-smccc.h> +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 307ad6931c..3c6af2e3d2 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -4,6 +4,11 @@
- (C) Copyright 2012
- Pavel Herrmann morpheus.ibis@gmail.com
- Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
- Authors:
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#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 */
-- 2.25.1
Acked-by: Ilias Apalodimas ilias.apalodimas@linaro.org

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].
[1]: doc/usage/cmd/armffa.rst
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
---
Changelog: ===============
v15:
Simon:
* armffa.c : integrate PHYS_ADDR_LN
v14:
Ilias:
* address nits * in do_ffa_ping() reject the SP ID if it's 0 * use PHYS_ADDR_LN in formatting the physical addresses
v12:
* add subcommands argument checks * usage documentation: update command return codes * remove calloc when querying SPs * address nits
v11:
* use U_BOOT_CMD_WITH_SUBCMDS * address nits
v10:
* use the FF-A driver Uclass operations * use uclass_first_device() * address nits
v9:
* remove manual FF-A discovery and use DM * use DM class APIs to probe and interact with the FF-A bus * add doc/usage/cmd/armffa.rst
v8:
* update partition_info_get() second argument to be an SP count * pass NULL device pointer to the FF-A bus discovery and operations
v7:
* adapt do_ffa_dev_list() following the recent update on uclass_first_device/uclass_next_device functions (they return void now) * set armffa command to use 64-bit direct messaging
v4:
* remove pattern data in do_ffa_msg_send_direct_req
v3:
* use the new driver interfaces (partition_info_get, sync_send_receive) in armffa command
v2:
* replace use of ffa_helper_init_device function by ffa_helper_bus_discover
v1:
* introduce armffa command
MAINTAINERS | 2 + cmd/Kconfig | 10 ++ cmd/Makefile | 1 + cmd/armffa.c | 202 +++++++++++++++++++++++++++++++ doc/arch/arm64.ffa.rst | 7 ++ doc/usage/cmd/armffa.rst | 94 ++++++++++++++ doc/usage/index.rst | 1 + drivers/firmware/arm-ffa/Kconfig | 1 + 8 files changed, 318 insertions(+) create mode 100644 cmd/armffa.c create mode 100644 doc/usage/cmd/armffa.rst
diff --git a/MAINTAINERS b/MAINTAINERS index 4fd5768de0..7bfac78e59 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -269,7 +269,9 @@ F: configs/cortina_presidio-asic-pnand_defconfig ARM FF-A M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com S: Maintained +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: include/sandbox_arm_ffa.h diff --git a/cmd/Kconfig b/cmd/Kconfig index 02e54f1e50..79b4f8367a 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -935,6 +935,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 6c37521b4e..7d20a85a46 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 0000000000..7e6eafc03a --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ +#include <common.h> +#include <arm_ffa.h> +#include <command.h> +#include <dm.h> +#include <mapmem.h> +#include <stdlib.h> +#include <asm/io.h> + +/* 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 <partition UUID>\n" + " - lists the partition(s) info\n" + "ping <partition ID>\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 4f817f053c..aefd527447 100644 --- a/doc/arch/arm64.ffa.rst +++ b/doc/arch/arm64.ffa.rst @@ -205,6 +205,13 @@ The following features are provided:
- FF-A bus can be compiled and used without EFI
+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 0000000000..13fa90c129 --- /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 open-source-office@arm.com + +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 388e59f173..e462de2806 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 9200c8028b..a7d5392859 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 select ARM_SMCCC select ARM_SMCCC_FEATURES + imply CMD_ARMFFA select LIB_UUID select DEVRES help

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 abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
---
Changelog: ===============
v12:
* remove reparenting by making the emulator parent of the FF-A device in the DT * add invoke_ffa_fn() * address nits
v11:
* rename ffa_try_discovery() to sandbox_ffa_discover() * rename sandbox_ffa_query_core_state() to sandbox_query_ffa_emul_state() * store the sandbox emulator pointer in the FF-A device uc_priv (struct ffa_priv) * set the emulator as parent of the sandbox FF-A device
v10:
* split the FF-A sandbox support into an emulator and a driver * read FFA_VERSION and FFA_PARTITION_INFO_GET state using sandbox_ffa_query_core_state() * drop CONFIG_SANDBOX_FFA config * address nits
v9: align FF-A sandbox driver with FF-A discovery through DM
v8: update ffa_bus_prvdata_get() to return a pointer rather than a pointer address
v7: state that sandbox driver supports only 64-bit direct messaging
v4: align sandbox driver with the new FF-A driver interfaces and new way of error handling
v1: introduce the sandbox driver
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 7bfac78e59..7f4efb6358 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -269,12 +269,13 @@ F: configs/cortina_presidio-asic-pnand_defconfig ARM FF-A M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com 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: include/sandbox_arm_ffa.h
ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de diff --git a/arch/sandbox/dts/sandbox.dtsi b/arch/sandbox/dts/sandbox.dtsi index 30a305c4d2..94a08814b8 100644 --- a/arch/sandbox/dts/sandbox.dtsi +++ b/arch/sandbox/dts/sandbox.dtsi @@ -445,6 +445,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 ff9f9222e6..96b5404991 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -1820,6 +1820,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 0000000000..be2790f496 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __SANDBOX_ARM_FFA_H +#define __SANDBOX_ARM_FFA_H + +#include <arm_ffa.h> + +/* + * 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 0000000000..b0881822d7 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __SANDBOX_ARM_FFA_PRV_H +#define __SANDBOX_ARM_FFA_PRV_H + +#include <arm_ffa_priv.h> + +/* 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 98b3e0cda4..72ea3d21ab 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 1ec44d5b33..8269bec879 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -344,3 +344,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 aefd527447..b7c754fa3d 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. @@ -205,6 +208,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 + The armffa command -----------------------------------
diff --git a/doc/arch/sandbox/sandbox.rst b/doc/arch/sandbox/sandbox.rst index 77ca6bc4cc..a3631de749 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 a7d5392859..d75f8b53fd 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 imply CMD_ARMFFA select LIB_UUID select DEVRES @@ -33,5 +33,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 11b1766285..318123a7f4 100644 --- a/drivers/firmware/arm-ffa/Makefile +++ b/drivers/firmware/arm-ffa/Makefile @@ -5,4 +5,12 @@ # Authors: # Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
-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 0000000000..5562bbaac3 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ +#include <common.h> +#include <dm.h> +#include <mapmem.h> +#include <string.h> +#include <asm/global_data.h> +#include <asm/sandbox_arm_ffa.h> +#include <asm/sandbox_arm_ffa_priv.h> +#include <dm/device-internal.h> +#include <dm/lists.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> + +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 4338f9c9b1..0000000000 --- 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 open-source-office@arm.com - * - * Authors: - * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com - */ - -#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 0000000000..ef9491ccea --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ +#include <common.h> +#include <arm_ffa.h> +#include <dm.h> +#include <log.h> +#include <asm/global_data.h> +#include <asm/sandbox_arm_ffa_priv.h> +#include <dm/device-internal.h> +#include <linux/errno.h> + +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 3c6af2e3d2..0432c95c9e 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 */

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 abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
---
Changelog: ===============
v12:
* remove use of dscvry_info * drop use of calloc when querying SPs * address nits
v11:
* drop unmapping test (taken care of by the DM when removing the device) * address nits
v10:
* use the FF-A driver Uclass operations * use uclass_first_device() * replace CONFIG_SANDBOX_FFA with CONFIG_ARM_FFA_TRANSPORT * address nits
v9: align FF-A sandbox tests with FF-A discovery through DM
v8:
* update partition_info_get() second argument to be an SP count * pass NULL device pointer to the FF-A bus discovery and operations
v7: set the tests to use 64-bit direct messaging
v4: align sandbox tests with the new FF-A driver interfaces and new way of error handling
v1: introduce sandbox tests
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 7f4efb6358..a79e3a8429 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -276,6 +276,7 @@ F: doc/arch/arm64.ffa.rst F: doc/usage/cmd/armffa.rst F: drivers/firmware/arm-ffa/ F: include/arm_ffa.h +F: test/dm/ffa.c
ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst index b7c754fa3d..325fb80346 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 3799b1ae8f..7ed00733c1 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 open-source-office@arm.com +# Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
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 0000000000..6912666bb4 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <console.h> +#include <dm.h> +#include <asm/sandbox_arm_ffa.h> +#include <asm/sandbox_arm_ffa_priv.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +/* 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);

Add Sandbox test for the armffa command
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v12:
* address nits
v10:
* replace CMD_RET_SUCCESS with 0 * replace CONFIG_SANDBOX_FFA with CONFIG_ARM_FFA_TRANSPORT
v9: align the test with FF-A discovery through DM
v4: drop use of helper APIs
v1: introduce armffa command sandbox test
MAINTAINERS | 1 + test/cmd/Makefile | 2 ++ test/cmd/armffa.c | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+) create mode 100644 test/cmd/armffa.c
diff --git a/MAINTAINERS b/MAINTAINERS index a79e3a8429..bd3dba3d95 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -276,6 +276,7 @@ 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/test/cmd/Makefile b/test/cmd/Makefile index a3cf983739..6e3d7e919e 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 open-source-office@arm.com
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 0000000000..9a44a397e8 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <string.h> +#include <asm/sandbox_arm_ffa.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +/* Basic test of 'armffa' command */ +static int dm_test_armffa_cmd(struct unit_test_state *uts) +{ + /* armffa getpart <UUID> */ + ut_assertok(run_command("armffa getpart " SANDBOX_SERVICE1_UUID, 0)); + + /* armffa ping <ID> */ + 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);

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.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Tested-by: Gowtham Suresh Kumar gowtham.sureshkumar@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v16:
* lib/efi_loader/Kconfig: rather than automatically selecting OPTEE and ARM_FFA_TRANSPORT configs by EFI_MM_COMM_TEE, set them as dependencies (Otherwise FF-A will be automatically enabled for boards that don't need it).
v15:
Simon:
* replace FFA_SHARED_MM_BUFFER_* defines with configs
v14:
Ilias:
* drop truncating var_payload->size when using FF-A * map the MM SP return codes to errnos
v13:
* remove FF-A and Optee ifdefs
v12:
* drop use of calloc when querying SPs * address nits
v11:
* rename select_ffa_mm_comms() to select_mm_comms() * improve the logic of MM transport selection in mm_communicate() * addressing nits
v10:
* use the FF-A driver Uclass operations * use uclass_first_device() * addressing nits
v9: align how FF-A is used with FF-A discovery through DM
v8:
* isolate the compilation choices between FF-A and OP-TEE * update partition_info_get() second argument to be an SP count * pass NULL device pointer to the FF-A bus discovery and operations
v7:
* set the MM door bell event to use 64-bit direct messaging * issue a compile time error when one of these macros are not found : FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_OFFSET, FFA_SHARED_MM_BUFFER_ADDR * make mm_sp_svc_uuid static * replace EINVAL with ENOMEM in ffa_discover_mm_sp_id() when calloc() fails * improve use of unmap_sysmem() in ffa_mm_communicate()
v6:
* add FF-A runtime discovery at MM communication level * drop EFI runtime support for FF-A MM communication * revert the changes in include/mm_communication.h for efi_mm_communicate_header and smm_variable_access structures
v4:
* use the new FF-A driver interfaces * discover MM partitions at runtime * copy FF-A driver private data to EFI runtime section at ExitBootServices() * drop use of FFA_ERR_STAT_SUCCESS error code * replace EFI_BUFFER_TOO_SMALL with EFI_OUT_OF_RESOURCES in ffa_mm_communicate(). No need for efi_memcpy_runtime() anymore * revert the error log in mm_communicate() in case of failure * remove packed attribute from efi_mm_communicate_header and smm_variable_communicate_header
v2:
* set default values to 0 for FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_ADDR and MM_SP_UUID_DATA and add warnings
v1:
* introduce FF-A MM communication
include/mm_communication.h | 13 ++ lib/efi_loader/Kconfig | 44 ++++- lib/efi_loader/efi_variable_tee.c | 257 +++++++++++++++++++++++++++++- 3 files changed, 307 insertions(+), 7 deletions(-)
diff --git a/include/mm_communication.h b/include/mm_communication.h index e65fbde60d..f17847583b 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. sughosh.ganu@linaro.org * Copyright (C) 2020 Linaro Ltd. ilias.apalodimas@linaro.org + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#ifndef _MM_COMMUNICATION_H_ @@ -13,6 +16,9 @@
#include <part_efi.h>
+/* MM service UUID string (big-endian format). This UUID is common across all MM SPs */ +#define MM_SP_UUID "33d532ed-e699-0942-c09c-a798d9cd722d" + /* * Interface to the pseudo Trusted Application (TA), which provides a * communication channel with the Standalone MM (Management Mode) @@ -248,4 +254,11 @@ struct smm_variable_var_check_property { u16 name[]; };
+/* supported MM transports */ +enum mm_comms_select { + MM_COMMS_UNDEFINED, + MM_COMMS_FFA, + MM_COMMS_OPTEE +}; + #endif /* _MM_COMMUNICATION_H_ */ diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index c5835e6ef6..8fbadb9201 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -55,13 +55,53 @@ 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" - depends on OPTEE + bool "UEFI variables storage service via the trusted world" + depends on OPTEE && ARM_FFA_TRANSPORT 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" + default 0 + depends on EFI_MM_COMM_TEE + 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" + default 0 + depends on EFI_MM_COMM_TEE + 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" + default 0x0 + depends on EFI_MM_COMM_TEE + 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 dfef18435d..5137b871ea 100644 --- a/lib/efi_loader/efi_variable_tee.c +++ b/lib/efi_loader/efi_variable_tee.c @@ -4,17 +4,34 @@ * * Copyright (C) 2019 Linaro Ltd. sughosh.ganu@linaro.org * Copyright (C) 2019 Linaro Ltd. ilias.apalodimas@linaro.org + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> +#include <arm_ffa.h> +#include <cpu_func.h> +#include <dm.h> #include <efi.h> #include <efi_api.h> #include <efi_loader.h> #include <efi_variable.h> -#include <tee.h> #include <malloc.h> +#include <mapmem.h> #include <mm_communication.h> +#include <tee.h> + +/* 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; extern struct efi_var_file __efi_runtime_data *efi_var_buf; static efi_uintn_t max_buffer_size; /* comm + var + func + data */ static efi_uintn_t max_payload_size; /* func + data */ @@ -145,16 +162,241 @@ static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize) }
/** - * 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_err("EFI: Cannot find FF-A bus device, trying Optee comms\n"); + return MM_COMMS_OPTEE; + } + + return MM_COMMS_FFA; +} + +/** + * 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) { efi_status_t ret; + enum mm_comms_select mm_comms; struct efi_mm_communicate_header *mm_hdr; struct smm_variable_communicate_header *var_hdr;
@@ -162,7 +404,12 @@ static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) 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); + 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); + if (ret != EFI_SUCCESS) { log_err("%s failed!\n", __func__); return ret; @@ -697,7 +944,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);
/*

On Wed, Jul 26, 2023 at 10:45:02AM +0100, Abdellatif El Khlifi wrote:
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.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Tested-by: Gowtham Suresh Kumar gowtham.sureshkumar@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
So, at this point in the series we impact lx2160ardb_tfa_stmm which is the only config in the tree prior to this series that sets CONFIG_EFI_MM_COMM_TEE. I'm not going to block this series[1] on updating lx2160ardb_tfa_stmm as well, but I do want to make sure the maintainers there are aware and can update the config to support the current state of this technology.
[1]: https://patchwork.ozlabs.org/project/uboot/list/?series=365876&state=*

Hi Tom,
On Wed, Jul 26, 2023 at 03:39:12PM -0400, Tom Rini wrote:
On Wed, Jul 26, 2023 at 10:45:02AM +0100, Abdellatif El Khlifi wrote:
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.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Tested-by: Gowtham Suresh Kumar gowtham.sureshkumar@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
So, at this point in the series we impact lx2160ardb_tfa_stmm which is the only config in the tree prior to this series that sets CONFIG_EFI_MM_COMM_TEE. I'm not going to block this series[1] on updating lx2160ardb_tfa_stmm as well, but I do want to make sure the maintainers there are aware and can update the config to support the current state of this technology.
[1]: https://patchwork.ozlabs.org/project/uboot/list/?series=365876&state=*
Following a decision made with Ilias, the new MM comms design works as follows:
- Try to communicate using FF-A bus first - If that fails, try to communicate using Optee. So, platforms that don't support FF-A in the Secure side can still use Optee communication
This is done through the code below [1].
This logic needs CONFIG_ARM_FFA_TRANSPORT=y in the defconfig.
I added CONFIG_ARM_FFA_TRANSPORT=y to lx2160ardb_tfa_stmm_defconfig, CONFIG_EFI_MM_COMM_TEE is enabled and it builds fine.
Is it expected that lx2160ardb_tfa_stmm maintainers add CONFIG_ARM_FFA_TRANSPORT=y to lx2160ardb_tfa_stmm_defconfig ?
Cheers Abdellatif
[1]: Selecting the MM comms method:
static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) ... 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);

On Thu, Jul 27, 2023 at 10:34:50AM +0100, Abdellatif El Khlifi wrote:
Hi Tom,
On Wed, Jul 26, 2023 at 03:39:12PM -0400, Tom Rini wrote:
On Wed, Jul 26, 2023 at 10:45:02AM +0100, Abdellatif El Khlifi wrote:
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.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Tested-by: Gowtham Suresh Kumar gowtham.sureshkumar@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
So, at this point in the series we impact lx2160ardb_tfa_stmm which is the only config in the tree prior to this series that sets CONFIG_EFI_MM_COMM_TEE. I'm not going to block this series[1] on updating lx2160ardb_tfa_stmm as well, but I do want to make sure the maintainers there are aware and can update the config to support the current state of this technology.
[1]: https://patchwork.ozlabs.org/project/uboot/list/?series=365876&state=*
Following a decision made with Ilias, the new MM comms design works as follows:
- Try to communicate using FF-A bus first
- If that fails, try to communicate using Optee. So, platforms that don't support FF-A in the Secure side can still use Optee communication
This is done through the code below [1].
This logic needs CONFIG_ARM_FFA_TRANSPORT=y in the defconfig.
I added CONFIG_ARM_FFA_TRANSPORT=y to lx2160ardb_tfa_stmm_defconfig, CONFIG_EFI_MM_COMM_TEE is enabled and it builds fine.
Is it expected that lx2160ardb_tfa_stmm maintainers add CONFIG_ARM_FFA_TRANSPORT=y to lx2160ardb_tfa_stmm_defconfig ?
Ah, it sounds like the Kconfig logic this patch adds is wrong then. Is there a use case for ARM_FFA_TRANSPORT without CONFIG_EFI_MM_COMM_TEE=y ? If yes, then it's just that the FF-A related symbols for EFI_MM_COMM_TEE need to depend on ARM_FFA_TRANSPORT. If no, ARM_FFA_TRANSPORT needs to depend on EFI_MM_COMM_TEE (and these new symbols depend on ARM_FFA_TRANSPORT).

On Wed, Jul 26, 2023 at 10:45:02AM +0100, Abdellatif El Khlifi wrote:
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.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Tested-by: Gowtham Suresh Kumar gowtham.sureshkumar@arm.com
Is there a public record of thise tested-by? If not please don't inject them like this or have Gowtham respond on the mailing list
Thanks /Ilias
Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v16:
- lib/efi_loader/Kconfig: rather than automatically selecting OPTEE and ARM_FFA_TRANSPORT configs by EFI_MM_COMM_TEE, set them as dependencies (Otherwise FF-A will be automatically enabled for boards that don't need it).
v15:
Simon:
- replace FFA_SHARED_MM_BUFFER_* defines with configs
v14:
Ilias:
- drop truncating var_payload->size when using FF-A
- map the MM SP return codes to errnos
v13:
- remove FF-A and Optee ifdefs
v12:
- drop use of calloc when querying SPs
- address nits
v11:
- rename select_ffa_mm_comms() to select_mm_comms()
- improve the logic of MM transport selection in mm_communicate()
- addressing nits
v10:
- use the FF-A driver Uclass operations
- use uclass_first_device()
- addressing nits
v9: align how FF-A is used with FF-A discovery through DM
v8:
- isolate the compilation choices between FF-A and OP-TEE
- update partition_info_get() second argument to be an SP count
- pass NULL device pointer to the FF-A bus discovery and operations
v7:
- set the MM door bell event to use 64-bit direct messaging
- issue a compile time error when one of these macros are not found : FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_OFFSET, FFA_SHARED_MM_BUFFER_ADDR
- make mm_sp_svc_uuid static
- replace EINVAL with ENOMEM in ffa_discover_mm_sp_id() when calloc() fails
- improve use of unmap_sysmem() in ffa_mm_communicate()
v6:
- add FF-A runtime discovery at MM communication level
- drop EFI runtime support for FF-A MM communication
- revert the changes in include/mm_communication.h for efi_mm_communicate_header and smm_variable_access structures
v4:
- use the new FF-A driver interfaces
- discover MM partitions at runtime
- copy FF-A driver private data to EFI runtime section at ExitBootServices()
- drop use of FFA_ERR_STAT_SUCCESS error code
- replace EFI_BUFFER_TOO_SMALL with EFI_OUT_OF_RESOURCES in ffa_mm_communicate(). No need for efi_memcpy_runtime() anymore
- revert the error log in mm_communicate() in case of failure
- remove packed attribute from efi_mm_communicate_header and smm_variable_communicate_header
v2:
- set default values to 0 for FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_ADDR and MM_SP_UUID_DATA and add warnings
v1:
- introduce FF-A MM communication
include/mm_communication.h | 13 ++ lib/efi_loader/Kconfig | 44 ++++- lib/efi_loader/efi_variable_tee.c | 257 +++++++++++++++++++++++++++++- 3 files changed, 307 insertions(+), 7 deletions(-)
diff --git a/include/mm_communication.h b/include/mm_communication.h index e65fbde60d..f17847583b 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. sughosh.ganu@linaro.org
- Copyright (C) 2020 Linaro Ltd. ilias.apalodimas@linaro.org
- Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
- Authors:
*/
Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
#ifndef _MM_COMMUNICATION_H_ @@ -13,6 +16,9 @@
#include <part_efi.h>
+/* MM service UUID string (big-endian format). This UUID is common across all MM SPs */ +#define MM_SP_UUID "33d532ed-e699-0942-c09c-a798d9cd722d"
/*
- Interface to the pseudo Trusted Application (TA), which provides a
- communication channel with the Standalone MM (Management Mode)
@@ -248,4 +254,11 @@ struct smm_variable_var_check_property { u16 name[]; };
+/* supported MM transports */ +enum mm_comms_select {
- MM_COMMS_UNDEFINED,
- MM_COMMS_FFA,
- MM_COMMS_OPTEE
+};
#endif /* _MM_COMMUNICATION_H_ */ diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index c5835e6ef6..8fbadb9201 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -55,13 +55,53 @@ 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"
- depends on OPTEE
bool "UEFI variables storage service via the trusted world"
depends on OPTEE && ARM_FFA_TRANSPORT 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"
- default 0
- depends on EFI_MM_COMM_TEE
- 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"
- default 0
- depends on EFI_MM_COMM_TEE
- 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"
- default 0x0
- depends on EFI_MM_COMM_TEE
- 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 dfef18435d..5137b871ea 100644 --- a/lib/efi_loader/efi_variable_tee.c +++ b/lib/efi_loader/efi_variable_tee.c @@ -4,17 +4,34 @@
- Copyright (C) 2019 Linaro Ltd. sughosh.ganu@linaro.org
- Copyright (C) 2019 Linaro Ltd. ilias.apalodimas@linaro.org
- Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
- Authors:
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#include <common.h> +#include <arm_ffa.h> +#include <cpu_func.h> +#include <dm.h> #include <efi.h> #include <efi_api.h> #include <efi_loader.h> #include <efi_variable.h> -#include <tee.h> #include <malloc.h> +#include <mapmem.h> #include <mm_communication.h> +#include <tee.h>
+/* 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; extern struct efi_var_file __efi_runtime_data *efi_var_buf; static efi_uintn_t max_buffer_size; /* comm + var + func + data */ static efi_uintn_t max_payload_size; /* func + data */ @@ -145,16 +162,241 @@ static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize) }
/**
- 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_err("EFI: Cannot find FF-A bus device, trying Optee comms\n");
return MM_COMMS_OPTEE;
- }
- return MM_COMMS_FFA;
+}
+/**
- 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) { efi_status_t ret;
- enum mm_comms_select mm_comms; struct efi_mm_communicate_header *mm_hdr; struct smm_variable_communicate_header *var_hdr;
@@ -162,7 +404,12 @@ static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) 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);
- 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);
- if (ret != EFI_SUCCESS) { log_err("%s failed!\n", __func__); return ret;
@@ -697,7 +944,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);
/*
-- 2.25.1

Hi Ilias,
On Thu, Jul 27, 2023 at 12:58:57PM +0300, Ilias Apalodimas wrote:
On Wed, Jul 26, 2023 at 10:45:02AM +0100, Abdellatif El Khlifi wrote:
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.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Tested-by: Gowtham Suresh Kumar gowtham.sureshkumar@arm.com
Is there a public record of thise tested-by? If not please don't inject them like this or have Gowtham respond on the mailing list
Yes, 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 release.
Please find the test reports at [1].
Kind regards, Gowtham Suresh Kumar
[1]: https://gitlab.arm.com/arm-reference-solutions/arm-reference-solutions-test-...

Hi Abdellatif,
[...]
+}
+/**
- 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_err("EFI: Cannot find FF-A bus device, trying Optee comms\n");
This isn't an error. If you mark it as one it will pollute the console every time you try to decide if it's an FFA or an SMC call to op-tee. So either remote this or switch it to log_debug or something. While at it, any reason why we need to keep probing the ffa bus? Can't we just run this once during init?
return MM_COMMS_OPTEE;
- }
- return MM_COMMS_FFA;
+}
+/**
- 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) { efi_status_t ret;
- enum mm_comms_select mm_comms; struct efi_mm_communicate_header *mm_hdr; struct smm_variable_communicate_header *var_hdr;
@@ -162,7 +404,12 @@ static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) 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);
- 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);
- if (ret != EFI_SUCCESS) { log_err("%s failed!\n", __func__); return ret;
@@ -697,7 +944,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);
/*
-- 2.25.1
Thanks /Ilias

Hi Ilias,
On Thu, Jul 27, 2023 at 02:27:21PM +0300, Ilias Apalodimas wrote:
Hi Abdellatif,
[...]
+}
+/**
- 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_err("EFI: Cannot find FF-A bus device, trying Optee comms\n");
This isn't an error. If you mark it as one it will pollute the console every time you try to decide if it's an FFA or an SMC call to op-tee. So either remote this or switch it to log_debug or something. While at it,
Good point, thanks. I'll replace it with log_debug.
any reason why we need to keep probing the ffa bus? Can't we just run this once during init?
Probing the FF-A bus is only done once. Subsequent calls to uclass_first_device_err() will not probe again, they only return the dev. An existing dev means FF-A bus is up and running. Failure to find a dev means either FF-A discovery failed (e.g: FF-A no implemented in secure world), or probing failed (e.g: a setup FF-A ABI failed).
Cheers, Abdellatif

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.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v16:
* configs/corstone1000_defconfig: enable MM communication by setting the configs: ARM_FFA_TRANSPORT, OPTEE, TEE
v15:
Simon:
* use CONFIG_FFA_SHARED_MM_BUF_* configs in place of FFA_SHARED_MM_BUFFER_*
v13:
* remove FF-A config in the defconfig (because it's enabled automatically by CONFIG_EFI_MM_COMM_TEE)
v9:
* update copyright string
v8:
* drop OP-TEE configs from Corstone-1000 defconfig
v7:
* improve the definition of FFA_SHARED_MM_BUFFER_ADDR and FFA_SHARED_MM_BUFFER_OFFSET * update FFA_SHARED_MM_BUFFER_ADDR value
v6:
* corstone-1000: enable optee driver * corstone-1000: remove CONFIG_ARM_FFA_EFI_RUNTIME_MODE from the defconfig
v4:
* corstone-1000: turn on EFI MM communication
configs/corstone1000_defconfig | 7 +++++++ 1 file changed, 7 insertions(+)
diff --git a/configs/corstone1000_defconfig b/configs/corstone1000_defconfig index a8a79fd105..fa9f9e4f07 100644 --- a/configs/corstone1000_defconfig +++ b/configs/corstone1000_defconfig @@ -65,3 +65,10 @@ CONFIG_EFI_CAPSULE_ON_DISK=y CONFIG_EFI_IGNORE_OSINDICATIONS=y CONFIG_FWU_MULTI_BANK_UPDATE=y CONFIG_ERRNO_STR=y +CONFIG_ARM_FFA_TRANSPORT=y +CONFIG_OPTEE=y +CONFIG_TEE=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

On Wed, Jul 26, 2023 at 10:45:03AM +0100, Abdellatif El Khlifi wrote:
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.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
[snip]
diff --git a/configs/corstone1000_defconfig b/configs/corstone1000_defconfig index a8a79fd105..fa9f9e4f07 100644 --- a/configs/corstone1000_defconfig +++ b/configs/corstone1000_defconfig @@ -65,3 +65,10 @@ CONFIG_EFI_CAPSULE_ON_DISK=y CONFIG_EFI_IGNORE_OSINDICATIONS=y CONFIG_FWU_MULTI_BANK_UPDATE=y CONFIG_ERRNO_STR=y +CONFIG_ARM_FFA_TRANSPORT=y +CONFIG_OPTEE=y +CONFIG_TEE=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
Not a nak and unless there's other comments I'll simply fix this up, but you cannot just add options to the bottom of the configs. These are "defconfig" files like the kernel so for something like this you would do (and I did locally before throwing this at CI now): $ make corstone1000_defconfig savedefconfig $ mv defconfig configs/corstone1000_defconfig

Adding support for Arm FF-A v1.0 (Arm Firmware Framework for Armv8-A) [A].
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.
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.
=> dm tree
Class Index Probed Driver Name ----------------------------------------------------------- ... firmware 0 [ + ] psci |-- psci ffa 0 [ ] arm_ffa | `-- arm_ffa ...
Clients are able to probe then use the FF-A bus by calling the DM class searching APIs (e.g: uclass_first_device).
This implementation of the specification provides support for Aarch64.
The FF-A driver uses the SMC ABIs defined by the FF-A specification to:
- Discover the presence of secure partitions (SPs) of interest - Access an SP's service through communication protocols (e.g: EFI MM communication protocol)
The FF-A support provides the following features:
- Being generic by design and can be used by any Arm 64-bit platform - FF-A support can be compiled and used without EFI - Support for SMCCCv1.2 x0-x17 registers - Support for SMC32 calling convention - Support for 32-bit and 64-bit FF-A direct messaging - Support for FF-A MM communication (compatible with EFI boot time) - Enabling FF-A and MM communication in Corstone1000 platform as a use case - 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. - Sandbox FF-A test cases. - A new command called armffa is provided as an example of how to access the FF-A bus
For more details about the FF-A support please refer to [B] and refer to [C] for how to use the armffa command.
Please find at [D] an example of the expected boot logs when enabling FF-A support for a platform. In this example the platform is Corstone1000. But it can be any Arm 64-bit platform.
Changelog of changes: ===========================
v17:
Ilias:
* show a debug message rather than an error when FF-A is not detected
Tom:
* use savedefconfig to generate corstone1000_defconfig with FF-A MM comms enabled
v16: [16]
Tom:
* lib/efi_loader/Kconfig: rather than automatically selecting OPTEE and ARM_FFA_TRANSPORT configs by EFI_MM_COMM_TEE, set them as dependencies (Otherwise FF-A will be automatically enabled for boards that don't need it).
* configs/corstone1000_defconfig: enable MM communication by setting the configs: ARM_FFA_TRANSPORT, OPTEE, TEE
v15: [15]
Simon:
* drop commit "log: select physical address formatting in a generic way", this will be sent as a follow-up commit independently from this patchset * armffa.c : integrate PHYS_ADDR_LN * replace FFA_SHARED_MM_BUFFER_* defines with configs
v14: [14]
Simon:
* add to log.h a generic physical address formatting
Ilias:
* armffa command: in do_ffa_ping() reject the SP ID if it's 0 * MM comms: drop truncating var_payload->size when using FF-A * MM comms: map the MM SP return codes to errnos * address nits
v13: [13]
Ilias: * remove FF-A and Optee ifdefs in efi_variable_tee.c * doc minor change: specify in the readme that the user should call ffa_rxtx_unmap() driver operation to unmap the RX/TX buffers on demand.
v12: [12]
* remove the global variable (dscvry_info), use uc_priv instead * replace dscvry_info.invoke_ffa_fn() with a weak invoke_ffa_fn (user drivers can override it) * improve FFA_PARTITION_INFO_GET implementation (clients no longer need to calloc a buffer) * remove reparenting by making the sandbox emulator parent of the FF-A device in the DT * improve argument checks for the armffa command * address nits
v11: [11]
* move ffa_try_discovery() from the uclass to the Arm FF-A driver * rename ffa_try_discovery() to arm_ffa_discover() * add arm_ prefix to the Arm FF-A driver functions * use U_BOOT_CMD_WITH_SUBCMDS for armffa command * store the sandbox emulator pointer in the FF-A device uc_priv (struct ffa_priv) * set the emulator as parent of the sandbox FF-A device * rename select_ffa_mm_comms() to select_mm_comms() * improve the logic of MM transport selection in mm_communicate() * use ut_asserteq_mem() in uuid_str_to_le_bin test case * address nits
v10: [10]
* provide the FF-A driver operations through the Uclass (arm-ffa-uclass.c) * move the generic FF-A methods to the Uclass * keep Arm specific methods in the Arm driver (arm-ffa.c renamed from core.c) * split the FF-A sandbox support into an emulator (ffa-emul-uclass.c) and a driver (sandbox_ffa.c) * use the FF-A driver Uclass operations by clients (armffa command, tests, MM comms) * use uclass_first_device to search and probe the FF-A device (whether it is on Arm or on sandbox) * address nits
v9: [9]
* integrate the FF-A bus discovery in the DM and use ARM_SMCCC_FEATURES for binding * align FF-A sandbox driver with FF-A discovery through DM * use DM class APIs to probe and interact with the FF-A bus (in FF-A MM comms, armffa command, sandbox tests) * add documentation for the armffa command: doc/usage/cmd/armffa.rst * introduce testcase for uuid_str_to_le_bin
v8: [8]
* pass the FF-A bus device to the bus operations * isolate the compilation choices between FF-A and OP-TEE * drop OP-TEE configs from Corstone-1000 defconfig * make ffa_get_partitions_info() second argument to be an SP count in both modes
v7: [7]
* add support for 32-bit direct messaging (now we have 32-bit and 64-bit support) * set the MM door bell event to use 64-bit direct messaging * issue a compile time error when one of these macros are not found : FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_OFFSET, FFA_SHARED_MM_BUFFER_ADDR * make uuid_str_to_le_bin() implementation similar to uuid_str_to_bin() by using same APIs
v6: [6]
* remove clearing x0-x17 registers after SMC calls * drop use of EFI runtime support for FF-A (We decided with Linaro to add this later) * drop discovery from initcalls (discovery will be on demand by FF-A users) * add FF-A runtime discovery at MM communication level * update the documentation and move it to doc/arch/arm64.ffa.rst
v5: [5]
* move changelogs in each commit to the changes section
v4: [4]
* add FF-A support README (doc/README.ffa.drv) * improving error handling by mapping the FF-A errors to standard errors and logs * replacing panics with an error log * align sandbox driver and tests with the new FF-A driver interfaces and new way of error handling * use the new FF-A driver interfaces for MM communication * discover MM partitions at runtime * copy FF-A driver private data to EFI runtime section at ExitBootServices() * moving the FF-A driver work to drivers/firmware/arm-ffa * improving features discovery in FFA_FEATURES * add remove/unbind functions to the FF-A core device * improve how the driver behaves when bus discovery is done more than once * move clearing x0-x17 registers code into a new macro like done in the linux kernel * enable EFI MM communication for the Corstone1000 platform
v3: [3]
* port x0-x17 registers support from linux kernel as defined by SMCCCv1.2 * align the interfaces of the u-boot FF-A driver with those in the linux FF-A driver * remove the FF-A helper layer * make the u-boot FF-A driver independent from EFI * provide an optional config that enables copying the driver data to EFI runtime section at ExitBootServices service * use 64-bit version of FFA_RXTX_MAP, FFA_MSG_SEND_DIRECT_{REQ, RESP} * update armffa command with the new driver interfaces
v2 [2]:
* make FF-A bus discoverable using device_{bind, probe} APIs * remove device tree support
v1 [1]:
* introduce FF-A bus driver with device tree support * introduce armffa command * introduce FF-A Sandbox driver * add FF-A Sandbox test cases * introduce FF-A MM communication
Cheers, Abdellatif
List of previous patches:
[1]: https://lore.kernel.org/all/20220329151659.16894-1-abdellatif.elkhlifi@arm.c... [2]: https://lore.kernel.org/all/20220415122803.16666-1-abdellatif.elkhlifi@arm.c... [3]: https://lore.kernel.org/all/20220801172053.20163-1-abdellatif.elkhlifi@arm.c... [4]: https://lore.kernel.org/all/20220926101723.9965-1-abdellatif.elkhlifi@arm.co... [5]: https://lore.kernel.org/all/20220926140827.15125-1-abdellatif.elkhlifi@arm.c... [6]: https://lore.kernel.org/all/20221013103857.614-1-abdellatif.elkhlifi@arm.com... [7]: https://lore.kernel.org/all/20221107192055.21669-1-abdellatif.elkhlifi@arm.c... [8]: https://lore.kernel.org/all/20221122131751.22747-1-abdellatif.elkhlifi@arm.c... [9]: https://lore.kernel.org/all/20230310141016.137986-1-abdellatif.elkhlifi@arm.... [10]: https://lore.kernel.org/all/20230328161157.219375-1-abdellatif.elkhlifi@arm.... [11]: https://lore.kernel.org/all/20230412094245.44674-1-abdellatif.elkhlifi@arm.c... [12]: https://lore.kernel.org/all/20230512121044.111574-1-abdellatif.elkhlifi@arm.... [13]: https://lore.kernel.org/all/20230616152817.319869-1-abdellatif.elkhlifi@arm.... [14]: https://lore.kernel.org/all/20230707144410.228472-1-abdellatif.elkhlifi@arm.... [15]: https://lore.kernel.org/all/20230713132847.176000-1-abdellatif.elkhlifi@arm.... [16]: https://lore.kernel.org/all/20230726094503.100497-1-abdellatif.elkhlifi@arm....
More details:
[A]: https://developer.arm.com/documentation/den0077/latest/ [B]: doc/arch/arm64.ffa.rst [C]: doc/usage/cmd/armffa.rst [D]: example of boot logs when enabling FF-A
``` 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 ```
Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Rob Herring robh@kernel.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Achin Gupta achin.gupta@arm.com Cc: Drew Reed Drew.Reed@arm.com Cc: Xueliang Zhong Xueliang.Zhong@arm.com
Abdellatif El Khlifi (10): arm64: smccc: add support for SMCCCv1.2 x0-x17 registers lib: uuid: introduce uuid_str_to_le_bin function lib: uuid: introduce testcase for uuid_str_to_le_bin arm_ffa: introduce Arm FF-A support arm_ffa: introduce armffa command arm_ffa: introduce sandbox FF-A support arm_ffa: introduce sandbox test cases for UCLASS_FFA arm_ffa: introduce armffa command Sandbox test arm_ffa: efi: introduce FF-A MM communication arm_ffa: efi: corstone1000: enable MM communication
MAINTAINERS | 18 + arch/arm/cpu/armv8/smccc-call.S | 57 +- arch/arm/lib/asm-offsets.c | 16 + 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 ++ cmd/Kconfig | 10 + cmd/Makefile | 1 + cmd/armffa.c | 202 ++++ configs/corstone1000_defconfig | 12 +- configs/sandbox64_defconfig | 1 + configs/sandbox_defconfig | 1 + doc/arch/arm64.ffa.rst | 261 ++++ doc/arch/index.rst | 1 + doc/arch/sandbox/sandbox.rst | 1 + doc/usage/cmd/armffa.rst | 94 ++ doc/usage/index.rst | 1 + drivers/Makefile | 1 + drivers/firmware/Kconfig | 1 + drivers/firmware/arm-ffa/Kconfig | 42 + drivers/firmware/arm-ffa/Makefile | 16 + drivers/firmware/arm-ffa/arm-ffa-uclass.c | 1065 +++++++++++++++++ drivers/firmware/arm-ffa/arm-ffa.c | 104 ++ drivers/firmware/arm-ffa/ffa-emul-uclass.c | 720 +++++++++++ drivers/firmware/arm-ffa/sandbox_ffa.c | 110 ++ include/arm_ffa.h | 213 ++++ include/arm_ffa_priv.h | 246 ++++ include/dm/uclass-id.h | 7 + include/linux/arm-smccc.h | 45 + include/mm_communication.h | 13 + include/uuid.h | 15 + lib/efi_loader/Kconfig | 44 +- lib/efi_loader/efi_variable_tee.c | 257 +++- lib/uuid.c | 48 + test/cmd/Makefile | 2 + test/cmd/armffa.c | 33 + test/dm/Makefile | 3 +- test/dm/ffa.c | 261 ++++ test/lib/Makefile | 1 + test/lib/uuid.c | 41 + 41 files changed, 4160 insertions(+), 14 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 cmd/armffa.c create mode 100644 doc/arch/arm64.ffa.rst create mode 100644 doc/usage/cmd/armffa.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/ffa-emul-uclass.c create mode 100644 drivers/firmware/arm-ffa/sandbox_ffa.c create mode 100644 include/arm_ffa.h create mode 100644 include/arm_ffa_priv.h create mode 100644 test/cmd/armffa.c create mode 100644 test/dm/ffa.c create mode 100644 test/lib/uuid.c

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 abdellatif.elkhlifi@arm.com Reviewed-by: Ilias Apalodimas ilias.apalodimas@linaro.org Reviewed-by: Jens Wiklander jens.wiklander@linaro.org Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com
---
Changelog: ===============
v9:
* update the copyright string
v7:
* improve indentation of ARM_SMCCC_1_2_REGS_Xn_OFFS
v4:
* rename the commit title and improve description new commit title: the current
v3:
* port x0-x17 registers support from linux kernel as defined by SMCCCv1.2 commit title: arm64: smccc: add Xn registers support used by SMC calls
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 dc92b28777..93f66d3366 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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com +*/ #include <linux/linkage.h> #include <linux/arm-smccc.h> #include <generated/asm-offsets.h> @@ -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 6de0ce9152..181a8ac4c2 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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> @@ -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 e1d09884a1..f44e9e8f93 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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */ #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

convert UUID string to little endian binary data
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v9:
* add a full function prototype description in uuid.h
v8:
* use simple_strtoull() in uuid_str_to_le_bin() to support 32-bit platforms
v7:
* rename be_uuid_str_to_le_bin() to uuid_str_to_le_bin() * make uuid_str_to_le_bin() implementation similar to uuid_str_to_bin() by using same APIs
v4:
* rename ffa_uuid_str_to_bin to be_uuid_str_to_le_bin and put in a standalone commit (the current)
v3:
* introduce ffa_uuid_str_to_bin (provided by arm_ffa: introduce Arm FF-A low-level driver)
include/uuid.h | 15 +++++++++++++++ lib/uuid.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+)
diff --git a/include/uuid.h b/include/uuid.h index 4a4883d3b5..89b93e642b 100644 --- a/include/uuid.h +++ b/include/uuid.h @@ -2,6 +2,10 @@ /* * Copyright (C) 2014 Samsung Electronics * Przemyslaw Marczak p.marczak@samsung.com + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */ #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 96e1af3c8b..45f325d964 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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> @@ -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. *

provide a test case
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com
---
Changelog: ===============
v16:
* MAINTAINERS: place the UUID part in an alphabetical order
v11:
* use ut_asserteq_mem()
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 d724b64673..4324965d26 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1555,6 +1555,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 abdellatif.elkhlifi@arm.com +S: Maintained +F: test/lib/uuid.c + VIDEO M: Anatolij Gustschin agust@denx.de S: Maintained diff --git a/test/lib/Makefile b/test/lib/Makefile index e0bd9e04e8..e75a263e6a 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 0000000000..e24331a136 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <uuid.h> +#include <test/lib.h> +#include <test/test.h> +#include <test/ut.h> + +/* 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);

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 abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Reviewed-by: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Tom Rini trini@konsulko.com Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
---
Changelog: ===============
v13:
* doc minor change: specify in the readme that the user should call ffa_rxtx_unmap() driver operation to unmap the RX/TX buffers on demand.
v12:
* remove dscvry_info * replace dscvry_info.invoke_ffa_fn() with a weak invoke_ffa_fn (user drivers can override it) * improve FFA_PARTITION_INFO_GET implementation (clients no longer need to calloc a buffer) * address nits
v11:
* move ffa_try_discovery() from the uclass to the Arm FF-A driver * rename ffa_try_discovery() to arm_ffa_discover() * pass dev as an argument of arm_ffa_discover() * add arm_ prefix to the Arm FF-A driver functions * add emul field in struct ffa_discovery_info * address nits
v10:
* provide the driver operations through the Uclass * move the generic FF-A methods to the Uclass * keep Arm specific methods in the Arm driver (arm-ffa.c) * rename core.c to arm-ffa.c * address nits
v9:
* integrate the FF-A bus discovery in the DM and use ARM_SMCCC_FEATURES for binding
v8:
* make ffa_get_partitions_info() second argument to be an SP count in both modes * update ffa_bus_prvdata_get() to return a pointer rather than a pointer address * remove packing from ffa_partition_info and ffa_send_direct_data structures * pass the FF-A bus device to the bus operations
v7:
* add support for 32-bit direct messaging * rename be_uuid_str_to_le_bin() to uuid_str_to_le_bin() * improve the declaration of error handling mapping * stating in doc/arch/arm64.ffa.rst that EFI runtime is not supported
v6:
* drop use of EFI runtime support (We decided with Linaro to add this later) * drop discovery from initcalls (discovery will be on demand by FF-A users) * set the alignment of the RX/TX buffers to the larger translation granule size * move FF-A RX/TX buffers unmapping at ExitBootServices() to a separate commit * update the documentation and move it to doc/arch/arm64.ffa.rst
v4:
* add doc/README.ffa.drv * moving the FF-A driver work to drivers/firmware/arm-ffa * use less #ifdefs in lib/efi_loader/efi_boottime.c and replace #if defined by #if CONFIG_IS_ENABLED * improving error handling by mapping the FF-A errors to standard errors and logs * replacing panics with an error log and returning an error code * improving features discovery in FFA_FEATURES by introducing rxtx_min_pages private data field * add ffa_remove and ffa_unbind functions * improve how the driver behaves when bus discovery is done more than once
v3:
* align the interfaces of the U-Boot FF-A driver with those in the linux FF-A driver * remove the FF-A helper layer * make the U-Boot FF-A driver independent from EFI * provide an optional config that enables copying the driver data to EFI runtime section at ExitBootServices service * use 64-bit version of FFA_RXTX_MAP, FFA_MSG_SEND_DIRECT_{REQ, RESP}
v2:
* make FF-A bus discoverable using device_{bind, probe} APIs * remove device tree support
v1:
* introduce FF-A bus driver with device tree support
MAINTAINERS | 8 + doc/arch/arm64.ffa.rst | 238 ++++ 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, 1941 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 4324965d26..4fd5768de0 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 abdellatif.elkhlifi@arm.com +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 sbabic@denx.de M: Fabio Estevam festevam@gmail.com diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst new file mode 100644 index 0000000000..4f817f053c --- /dev/null +++ b/doc/arch/arm64.ffa.rst @@ -0,0 +1,238 @@ +.. 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(). +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: + + - 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 abdellatif.elkhlifi@arm.com + +.. _`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 b8da4b8c8e..2f916f4026 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 78dcf62f76..5044f45253 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 eae1c8ddc9..8789b1ea14 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 0000000000..9200c8028b --- /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 0000000000..11b1766285 --- /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 open-source-office@arm.com +# +# Authors: +# Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + +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 0000000000..ffa9d81fa7 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ +#include <common.h> +#include <arm_ffa.h> +#include <arm_ffa_priv.h> +#include <dm.h> +#include <log.h> +#include <malloc.h> +#include <string.h> +#include <uuid.h> +#include <asm/global_data.h> +#include <dm/device-internal.h> +#include <dm/devres.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> + +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 0000000000..68df75bd9e --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <arm_ffa.h> +#include <arm_ffa_priv.h> +#include <dm.h> +#include <log.h> +#include <asm/global_data.h> +#include <dm/device-internal.h> +#include <linux/errno.h> + +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 0000000000..4338f9c9b1 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#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 0000000000..db9b1be995 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __ARM_FFA_H +#define __ARM_FFA_H + +#include <linux/printk.h> + +/* + * 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 0000000000..d564c33c64 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __ARM_FFA_PRV_H +#define __ARM_FFA_PRV_H + +#include <mapmem.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> + +/* 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 <linux/arm-smccc.h> +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 307ad6931c..3c6af2e3d2 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -4,6 +4,11 @@ * * (C) Copyright 2012 * Pavel Herrmann morpheus.ibis@gmail.com + * + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#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 */

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].
[1]: doc/usage/cmd/armffa.rst
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
---
Changelog: ===============
v15:
Simon:
* armffa.c : integrate PHYS_ADDR_LN
v14:
Ilias:
* address nits * in do_ffa_ping() reject the SP ID if it's 0 * use PHYS_ADDR_LN in formatting the physical addresses
v12:
* add subcommands argument checks * usage documentation: update command return codes * remove calloc when querying SPs * address nits
v11:
* use U_BOOT_CMD_WITH_SUBCMDS * address nits
v10:
* use the FF-A driver Uclass operations * use uclass_first_device() * address nits
v9:
* remove manual FF-A discovery and use DM * use DM class APIs to probe and interact with the FF-A bus * add doc/usage/cmd/armffa.rst
v8:
* update partition_info_get() second argument to be an SP count * pass NULL device pointer to the FF-A bus discovery and operations
v7:
* adapt do_ffa_dev_list() following the recent update on uclass_first_device/uclass_next_device functions (they return void now) * set armffa command to use 64-bit direct messaging
v4:
* remove pattern data in do_ffa_msg_send_direct_req
v3:
* use the new driver interfaces (partition_info_get, sync_send_receive) in armffa command
v2:
* replace use of ffa_helper_init_device function by ffa_helper_bus_discover
v1:
* introduce armffa command
MAINTAINERS | 2 + cmd/Kconfig | 10 ++ cmd/Makefile | 1 + cmd/armffa.c | 202 +++++++++++++++++++++++++++++++ doc/arch/arm64.ffa.rst | 7 ++ doc/usage/cmd/armffa.rst | 94 ++++++++++++++ doc/usage/index.rst | 1 + drivers/firmware/arm-ffa/Kconfig | 1 + 8 files changed, 318 insertions(+) create mode 100644 cmd/armffa.c create mode 100644 doc/usage/cmd/armffa.rst
diff --git a/MAINTAINERS b/MAINTAINERS index 4fd5768de0..7bfac78e59 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -269,7 +269,9 @@ F: configs/cortina_presidio-asic-pnand_defconfig ARM FF-A M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com S: Maintained +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: include/sandbox_arm_ffa.h diff --git a/cmd/Kconfig b/cmd/Kconfig index 02e54f1e50..79b4f8367a 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -935,6 +935,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 6c37521b4e..7d20a85a46 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 0000000000..7e6eafc03a --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ +#include <common.h> +#include <arm_ffa.h> +#include <command.h> +#include <dm.h> +#include <mapmem.h> +#include <stdlib.h> +#include <asm/io.h> + +/* 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 <partition UUID>\n" + " - lists the partition(s) info\n" + "ping <partition ID>\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 4f817f053c..aefd527447 100644 --- a/doc/arch/arm64.ffa.rst +++ b/doc/arch/arm64.ffa.rst @@ -205,6 +205,13 @@ The following features are provided:
- FF-A bus can be compiled and used without EFI
+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 0000000000..13fa90c129 --- /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 open-source-office@arm.com + +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 388e59f173..e462de2806 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 9200c8028b..a7d5392859 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 select ARM_SMCCC select ARM_SMCCC_FEATURES + imply CMD_ARMFFA select LIB_UUID select DEVRES help

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 abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
---
Changelog: ===============
v12:
* remove reparenting by making the emulator parent of the FF-A device in the DT * add invoke_ffa_fn() * address nits
v11:
* rename ffa_try_discovery() to sandbox_ffa_discover() * rename sandbox_ffa_query_core_state() to sandbox_query_ffa_emul_state() * store the sandbox emulator pointer in the FF-A device uc_priv (struct ffa_priv) * set the emulator as parent of the sandbox FF-A device
v10:
* split the FF-A sandbox support into an emulator and a driver * read FFA_VERSION and FFA_PARTITION_INFO_GET state using sandbox_ffa_query_core_state() * drop CONFIG_SANDBOX_FFA config * address nits
v9: align FF-A sandbox driver with FF-A discovery through DM
v8: update ffa_bus_prvdata_get() to return a pointer rather than a pointer address
v7: state that sandbox driver supports only 64-bit direct messaging
v4: align sandbox driver with the new FF-A driver interfaces and new way of error handling
v1: introduce the sandbox driver
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 7bfac78e59..7f4efb6358 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -269,12 +269,13 @@ F: configs/cortina_presidio-asic-pnand_defconfig ARM FF-A M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com 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: include/sandbox_arm_ffa.h
ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de diff --git a/arch/sandbox/dts/sandbox.dtsi b/arch/sandbox/dts/sandbox.dtsi index 30a305c4d2..94a08814b8 100644 --- a/arch/sandbox/dts/sandbox.dtsi +++ b/arch/sandbox/dts/sandbox.dtsi @@ -445,6 +445,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 ff9f9222e6..96b5404991 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -1820,6 +1820,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 0000000000..be2790f496 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __SANDBOX_ARM_FFA_H +#define __SANDBOX_ARM_FFA_H + +#include <arm_ffa.h> + +/* + * 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 0000000000..b0881822d7 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __SANDBOX_ARM_FFA_PRV_H +#define __SANDBOX_ARM_FFA_PRV_H + +#include <arm_ffa_priv.h> + +/* 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 98b3e0cda4..72ea3d21ab 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 1ec44d5b33..8269bec879 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -344,3 +344,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 aefd527447..b7c754fa3d 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. @@ -205,6 +208,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 + The armffa command -----------------------------------
diff --git a/doc/arch/sandbox/sandbox.rst b/doc/arch/sandbox/sandbox.rst index 77ca6bc4cc..a3631de749 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 a7d5392859..d75f8b53fd 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 imply CMD_ARMFFA select LIB_UUID select DEVRES @@ -33,5 +33,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 11b1766285..318123a7f4 100644 --- a/drivers/firmware/arm-ffa/Makefile +++ b/drivers/firmware/arm-ffa/Makefile @@ -5,4 +5,12 @@ # Authors: # Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
-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 0000000000..5562bbaac3 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ +#include <common.h> +#include <dm.h> +#include <mapmem.h> +#include <string.h> +#include <asm/global_data.h> +#include <asm/sandbox_arm_ffa.h> +#include <asm/sandbox_arm_ffa_priv.h> +#include <dm/device-internal.h> +#include <dm/lists.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> + +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 4338f9c9b1..0000000000 --- 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 open-source-office@arm.com - * - * Authors: - * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com - */ - -#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 0000000000..ef9491ccea --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ +#include <common.h> +#include <arm_ffa.h> +#include <dm.h> +#include <log.h> +#include <asm/global_data.h> +#include <asm/sandbox_arm_ffa_priv.h> +#include <dm/device-internal.h> +#include <linux/errno.h> + +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 3c6af2e3d2..0432c95c9e 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 */

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 abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
---
Changelog: ===============
v12:
* remove use of dscvry_info * drop use of calloc when querying SPs * address nits
v11:
* drop unmapping test (taken care of by the DM when removing the device) * address nits
v10:
* use the FF-A driver Uclass operations * use uclass_first_device() * replace CONFIG_SANDBOX_FFA with CONFIG_ARM_FFA_TRANSPORT * address nits
v9: align FF-A sandbox tests with FF-A discovery through DM
v8:
* update partition_info_get() second argument to be an SP count * pass NULL device pointer to the FF-A bus discovery and operations
v7: set the tests to use 64-bit direct messaging
v4: align sandbox tests with the new FF-A driver interfaces and new way of error handling
v1: introduce sandbox tests
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 7f4efb6358..a79e3a8429 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -276,6 +276,7 @@ F: doc/arch/arm64.ffa.rst F: doc/usage/cmd/armffa.rst F: drivers/firmware/arm-ffa/ F: include/arm_ffa.h +F: test/dm/ffa.c
ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst index b7c754fa3d..325fb80346 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 3799b1ae8f..7ed00733c1 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 open-source-office@arm.com +# Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
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 0000000000..6912666bb4 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <console.h> +#include <dm.h> +#include <asm/sandbox_arm_ffa.h> +#include <asm/sandbox_arm_ffa_priv.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +/* 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);

Add Sandbox test for the armffa command
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v12:
* address nits
v10:
* replace CMD_RET_SUCCESS with 0 * replace CONFIG_SANDBOX_FFA with CONFIG_ARM_FFA_TRANSPORT
v9: align the test with FF-A discovery through DM
v4: drop use of helper APIs
v1: introduce armffa command sandbox test
MAINTAINERS | 1 + test/cmd/Makefile | 2 ++ test/cmd/armffa.c | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+) create mode 100644 test/cmd/armffa.c
diff --git a/MAINTAINERS b/MAINTAINERS index a79e3a8429..bd3dba3d95 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -276,6 +276,7 @@ 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/test/cmd/Makefile b/test/cmd/Makefile index a3cf983739..6e3d7e919e 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 open-source-office@arm.com
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 0000000000..9a44a397e8 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <string.h> +#include <asm/sandbox_arm_ffa.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +/* Basic test of 'armffa' command */ +static int dm_test_armffa_cmd(struct unit_test_state *uts) +{ + /* armffa getpart <UUID> */ + ut_assertok(run_command("armffa getpart " SANDBOX_SERVICE1_UUID, 0)); + + /* armffa ping <ID> */ + 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);

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.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Tested-by: Gowtham Suresh Kumar gowtham.sureshkumar@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v17:
* show a debug message rather than an error when FF-A is not detected
v16:
* lib/efi_loader/Kconfig: rather than automatically selecting OPTEE and ARM_FFA_TRANSPORT configs by EFI_MM_COMM_TEE, set them as dependencies (Otherwise FF-A will be automatically enabled for boards that don't need it).
v15:
Simon:
* replace FFA_SHARED_MM_BUFFER_* defines with configs
v14:
Ilias:
* drop truncating var_payload->size when using FF-A * map the MM SP return codes to errnos
v13:
* remove FF-A and Optee ifdefs
v12:
* drop use of calloc when querying SPs * address nits
v11:
* rename select_ffa_mm_comms() to select_mm_comms() * improve the logic of MM transport selection in mm_communicate() * addressing nits
v10:
* use the FF-A driver Uclass operations * use uclass_first_device() * addressing nits
v9: align how FF-A is used with FF-A discovery through DM
v8:
* isolate the compilation choices between FF-A and OP-TEE * update partition_info_get() second argument to be an SP count * pass NULL device pointer to the FF-A bus discovery and operations
v7:
* set the MM door bell event to use 64-bit direct messaging * issue a compile time error when one of these macros are not found : FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_OFFSET, FFA_SHARED_MM_BUFFER_ADDR * make mm_sp_svc_uuid static * replace EINVAL with ENOMEM in ffa_discover_mm_sp_id() when calloc() fails * improve use of unmap_sysmem() in ffa_mm_communicate()
v6:
* add FF-A runtime discovery at MM communication level * drop EFI runtime support for FF-A MM communication * revert the changes in include/mm_communication.h for efi_mm_communicate_header and smm_variable_access structures
v4:
* use the new FF-A driver interfaces * discover MM partitions at runtime * copy FF-A driver private data to EFI runtime section at ExitBootServices() * drop use of FFA_ERR_STAT_SUCCESS error code * replace EFI_BUFFER_TOO_SMALL with EFI_OUT_OF_RESOURCES in ffa_mm_communicate(). No need for efi_memcpy_runtime() anymore * revert the error log in mm_communicate() in case of failure * remove packed attribute from efi_mm_communicate_header and smm_variable_communicate_header
v2:
* set default values to 0 for FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_ADDR and MM_SP_UUID_DATA and add warnings
v1:
* introduce FF-A MM communication
include/mm_communication.h | 13 ++ lib/efi_loader/Kconfig | 44 ++++- lib/efi_loader/efi_variable_tee.c | 257 +++++++++++++++++++++++++++++- 3 files changed, 307 insertions(+), 7 deletions(-)
diff --git a/include/mm_communication.h b/include/mm_communication.h index e65fbde60d..f17847583b 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. sughosh.ganu@linaro.org * Copyright (C) 2020 Linaro Ltd. ilias.apalodimas@linaro.org + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#ifndef _MM_COMMUNICATION_H_ @@ -13,6 +16,9 @@
#include <part_efi.h>
+/* MM service UUID string (big-endian format). This UUID is common across all MM SPs */ +#define MM_SP_UUID "33d532ed-e699-0942-c09c-a798d9cd722d" + /* * Interface to the pseudo Trusted Application (TA), which provides a * communication channel with the Standalone MM (Management Mode) @@ -248,4 +254,11 @@ struct smm_variable_var_check_property { u16 name[]; };
+/* supported MM transports */ +enum mm_comms_select { + MM_COMMS_UNDEFINED, + MM_COMMS_FFA, + MM_COMMS_OPTEE +}; + #endif /* _MM_COMMUNICATION_H_ */ diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index c5835e6ef6..8fbadb9201 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -55,13 +55,53 @@ 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" - depends on OPTEE + bool "UEFI variables storage service via the trusted world" + depends on OPTEE && ARM_FFA_TRANSPORT 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" + default 0 + depends on EFI_MM_COMM_TEE + 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" + default 0 + depends on EFI_MM_COMM_TEE + 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" + default 0x0 + depends on EFI_MM_COMM_TEE + 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 dfef18435d..3064a88ffe 100644 --- a/lib/efi_loader/efi_variable_tee.c +++ b/lib/efi_loader/efi_variable_tee.c @@ -4,17 +4,34 @@ * * Copyright (C) 2019 Linaro Ltd. sughosh.ganu@linaro.org * Copyright (C) 2019 Linaro Ltd. ilias.apalodimas@linaro.org + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> +#include <arm_ffa.h> +#include <cpu_func.h> +#include <dm.h> #include <efi.h> #include <efi_api.h> #include <efi_loader.h> #include <efi_variable.h> -#include <tee.h> #include <malloc.h> +#include <mapmem.h> #include <mm_communication.h> +#include <tee.h> + +/* 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; extern struct efi_var_file __efi_runtime_data *efi_var_buf; static efi_uintn_t max_buffer_size; /* comm + var + func + data */ static efi_uintn_t max_payload_size; /* func + data */ @@ -145,16 +162,241 @@ static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize) }
/** - * 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; +} + +/** + * 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) { efi_status_t ret; + enum mm_comms_select mm_comms; struct efi_mm_communicate_header *mm_hdr; struct smm_variable_communicate_header *var_hdr;
@@ -162,7 +404,12 @@ static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) 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); + 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); + if (ret != EFI_SUCCESS) { log_err("%s failed!\n", __func__); return ret; @@ -697,7 +944,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);
/*

On Thu, Jul 27, 2023 at 05:07:11PM +0100, Abdellatif El Khlifi wrote:
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.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Tested-by: Gowtham Suresh Kumar gowtham.sureshkumar@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v17:
- show a debug message rather than an error when FF-A is not detected
[snip]
diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index c5835e6ef6..8fbadb9201 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -55,13 +55,53 @@ 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"
- depends on OPTEE
- bool "UEFI variables storage service via the trusted world"
- depends on OPTEE && ARM_FFA_TRANSPORT
You didn't get my changes in here however. If you can do EFI_MM_COMM_TEE without ARM_FFA_TRANSPORT (as lx2160ardb_tfa_stmm_defconfig does) then you don't make this option depend on EFI_MM_COMM_TEE. If FF-A is only for use here, you make FF-A depend on this, and the FF-A specific variable depend on ARM_FFA_TRANSPORT.

Hi Tom,
On Thu, Jul 27, 2023 at 12:43:45PM -0400, Tom Rini wrote:
On Thu, Jul 27, 2023 at 05:07:11PM +0100, Abdellatif El Khlifi wrote:
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.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Tested-by: Gowtham Suresh Kumar gowtham.sureshkumar@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v17:
- show a debug message rather than an error when FF-A is not detected
[snip]
diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index c5835e6ef6..8fbadb9201 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -55,13 +55,53 @@ 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"
- depends on OPTEE
- bool "UEFI variables storage service via the trusted world"
- depends on OPTEE && ARM_FFA_TRANSPORT
You didn't get my changes in here however. If you can do EFI_MM_COMM_TEE without ARM_FFA_TRANSPORT (as lx2160ardb_tfa_stmm_defconfig does) then you don't make this option depend on EFI_MM_COMM_TEE. If FF-A is only for use here, you make FF-A depend on this, and the FF-A specific variable depend on ARM_FFA_TRANSPORT.
Thanks for the suggestion.
EFI_MM_COMM_TEE needs ARM_FFA_TRANSPORT.
EFI_MM_COMM_TEE enables efi_variable_tee.c
efi_variable_tee.c has new code for FF-A and Optee. Detecting which method to use is done at runtime. Ilias and I agreed on that.
ARM_FFA_TRANSPORT is meant to be generic and not tied to EFI. Making it depends on EFI_MM_COMM_TEE will tie it to EFI.
Cheers Abdellatif
-- Tom

Hi Tom
On Thu, 27 Jul 2023 at 19:43, Tom Rini trini@konsulko.com wrote:
On Thu, Jul 27, 2023 at 05:07:11PM +0100, Abdellatif El Khlifi wrote:
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.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Tested-by: Gowtham Suresh Kumar gowtham.sureshkumar@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v17:
- show a debug message rather than an error when FF-A is not detected
[snip]
diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index c5835e6ef6..8fbadb9201 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -55,13 +55,53 @@ 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"
depends on OPTEE
bool "UEFI variables storage service via the trusted world"
depends on OPTEE && ARM_FFA_TRANSPORT
You didn't get my changes in here however. If you can do EFI_MM_COMM_TEE without ARM_FFA_TRANSPORT (as lx2160ardb_tfa_stmm_defconfig does) then you don't make this option depend on . If FF-A is only for use here, you make FF-A depend on this, and the FF-A specific variable depend on ARM_FFA_TRANSPORT.
Abdellatif hinted at what's going on here. When I added this Kconfig option to lx2160 FF-A wasn't implemented yet. Since FF-A isn't a new communication mechanism but builds upon the existing SMCs to build an easier API, I asked Abdellatif to hide this complexity. We had two options, either make Kconfig options for either FF-A or the traditional SMCs and remove the dependencies, or piggyback on FF-As discovery mechanism and make the choice at runtime. The latter has a small impact on code size, but imho makes developers' life a lot easier.
Thanks /Ilias
-- Tom

On Fri, Jul 28, 2023 at 02:00:25PM +0300, Ilias Apalodimas wrote:
Hi Tom
On Thu, 27 Jul 2023 at 19:43, Tom Rini trini@konsulko.com wrote:
On Thu, Jul 27, 2023 at 05:07:11PM +0100, Abdellatif El Khlifi wrote:
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.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Tested-by: Gowtham Suresh Kumar gowtham.sureshkumar@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v17:
- show a debug message rather than an error when FF-A is not detected
[snip]
diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index c5835e6ef6..8fbadb9201 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -55,13 +55,53 @@ 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"
depends on OPTEE
bool "UEFI variables storage service via the trusted world"
depends on OPTEE && ARM_FFA_TRANSPORT
You didn't get my changes in here however. If you can do EFI_MM_COMM_TEE without ARM_FFA_TRANSPORT (as lx2160ardb_tfa_stmm_defconfig does) then you don't make this option depend on . If FF-A is only for use here, you make FF-A depend on this, and the FF-A specific variable depend on ARM_FFA_TRANSPORT.
Abdellatif hinted at what's going on here. When I added this Kconfig option to lx2160 FF-A wasn't implemented yet.
The defconfig has existed since May 2020, which is when you added EFI_MM_COMM_TEE itself too. So I think it's that no one did the check I did until now and saw this series was disabling what was on the other platform.
Since FF-A isn't a new communication mechanism but builds upon the existing SMCs to build an easier API, I asked Abdellatif to hide this complexity. We had two options, either make Kconfig options for either FF-A or the traditional SMCs and remove the dependencies, or piggyback on FF-As discovery mechanism and make the choice at runtime. The latter has a small impact on code size, but imho makes developers' life a lot easier.
I'm not sure how much you can do a run-time option here since you're setting a bunch of default values for FF-A to 0 in Kconfig. If we're supposed to be able to get them at run time, we shouldn't need a Kconfig option at all. I'm also not sure how valid a use case it is where we won't know at build time what the rest of the firmware stack supports here.

HI Tom,
On Fri, Jul 28, 2023 at 09:54:15AM -0400, Tom Rini wrote:
On Fri, Jul 28, 2023 at 02:00:25PM +0300, Ilias Apalodimas wrote:
Hi Tom
On Thu, 27 Jul 2023 at 19:43, Tom Rini trini@konsulko.com wrote:
On Thu, Jul 27, 2023 at 05:07:11PM +0100, Abdellatif El Khlifi wrote:
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.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Tested-by: Gowtham Suresh Kumar gowtham.sureshkumar@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v17:
- show a debug message rather than an error when FF-A is not detected
[snip]
diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index c5835e6ef6..8fbadb9201 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -55,13 +55,53 @@ 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"
depends on OPTEE
bool "UEFI variables storage service via the trusted world"
depends on OPTEE && ARM_FFA_TRANSPORT
You didn't get my changes in here however. If you can do EFI_MM_COMM_TEE without ARM_FFA_TRANSPORT (as lx2160ardb_tfa_stmm_defconfig does) then you don't make this option depend on . If FF-A is only for use here, you make FF-A depend on this, and the FF-A specific variable depend on ARM_FFA_TRANSPORT.
Abdellatif hinted at what's going on here. When I added this Kconfig option to lx2160 FF-A wasn't implemented yet.
The defconfig has existed since May 2020, which is when you added EFI_MM_COMM_TEE itself too. So I think it's that no one did the check I did until now and saw this series was disabling what was on the other platform.
Since FF-A isn't a new communication mechanism but builds upon the existing SMCs to build an easier API, I asked Abdellatif to hide this complexity. We had two options, either make Kconfig options for either FF-A or the traditional SMCs and remove the dependencies, or piggyback on FF-As discovery mechanism and make the choice at runtime. The latter has a small impact on code size, but imho makes developers' life a lot easier.
I'm not sure how much you can do a run-time option here since you're setting a bunch of default values for FF-A to 0 in Kconfig. If we're supposed to be able to get them at run time, we shouldn't need a Kconfig option at all. I'm also not sure how valid a use case it is where we won't know at build time what the rest of the firmware stack supports here.
That's a fair point. FF-A in theory has APIs to discover memory. Abdellatif, why do we need the Kconfigs for shared memory on FF-A?
Regards /Ilias
-- Tom

Hi Ilias,
On Mon, Jul 31, 2023 at 12:38:16PM +0300, Ilias Apalodimas wrote:
... Changelog: ===============
v17:
- show a debug message rather than an error when FF-A is not detected
[snip]
diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index c5835e6ef6..8fbadb9201 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -55,13 +55,53 @@ 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"
depends on OPTEE
bool "UEFI variables storage service via the trusted world"
depends on OPTEE && ARM_FFA_TRANSPORT
You didn't get my changes in here however. If you can do EFI_MM_COMM_TEE without ARM_FFA_TRANSPORT (as lx2160ardb_tfa_stmm_defconfig does) then you don't make this option depend on . If FF-A is only for use here, you make FF-A depend on this, and the FF-A specific variable depend on ARM_FFA_TRANSPORT.
Abdellatif hinted at what's going on here. When I added this Kconfig option to lx2160 FF-A wasn't implemented yet.
The defconfig has existed since May 2020, which is when you added EFI_MM_COMM_TEE itself too. So I think it's that no one did the check I did until now and saw this series was disabling what was on the other platform.
Since FF-A isn't a new communication mechanism but builds upon the existing SMCs to build an easier API, I asked Abdellatif to hide this complexity. We had two options, either make Kconfig options for either FF-A or the traditional SMCs and remove the dependencies, or piggyback on FF-As discovery mechanism and make the choice at runtime. The latter has a small impact on code size, but imho makes developers' life a lot easier.
I'm not sure how much you can do a run-time option here since you're setting a bunch of default values for FF-A to 0 in Kconfig. If we're supposed to be able to get them at run time, we shouldn't need a Kconfig option at all. I'm also not sure how valid a use case it is where we won't know at build time what the rest of the firmware stack supports here.
That's a fair point. FF-A in theory has APIs to discover memory. Abdellatif, why do we need the Kconfigs for shared memory on FF-A?
The statically carved out MM shared buffer address, size and offset cannot be discovered by FF-A ABIs. The MM communication driver in U-Boot could allocate the buffer and share it with the MM SP but we do not implement that support currently in either U-Boot or UEFI.
Simon suggested we use build configs to set the buffer address, size and offset since we don't want a DT node for the MM firmware.
Kind regards Abdellatif

Hi Abdellatif,
On Mon, 31 Jul 2023 at 05:46, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
Hi Ilias,
On Mon, Jul 31, 2023 at 12:38:16PM +0300, Ilias Apalodimas wrote:
... Changelog: ===============
v17:
- show a debug message rather than an error when FF-A is not detected
[snip]
diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index c5835e6ef6..8fbadb9201 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -55,13 +55,53 @@ 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"
depends on OPTEE
bool "UEFI variables storage service via the trusted world"
depends on OPTEE && ARM_FFA_TRANSPORT
You didn't get my changes in here however. If you can do EFI_MM_COMM_TEE without ARM_FFA_TRANSPORT (as lx2160ardb_tfa_stmm_defconfig does) then you don't make this option depend on . If FF-A is only for use here, you make FF-A depend on this, and the FF-A specific variable depend on ARM_FFA_TRANSPORT.
Abdellatif hinted at what's going on here. When I added this Kconfig option to lx2160 FF-A wasn't implemented yet.
The defconfig has existed since May 2020, which is when you added EFI_MM_COMM_TEE itself too. So I think it's that no one did the check I did until now and saw this series was disabling what was on the other platform.
Since FF-A isn't a new communication mechanism but builds upon the existing SMCs to build an easier API, I asked Abdellatif to hide this complexity. We had two options, either make Kconfig options for either FF-A or the traditional SMCs and remove the dependencies, or piggyback on FF-As discovery mechanism and make the choice at runtime. The latter has a small impact on code size, but imho makes developers' life a lot easier.
I'm not sure how much you can do a run-time option here since you're setting a bunch of default values for FF-A to 0 in Kconfig. If we're supposed to be able to get them at run time, we shouldn't need a Kconfig option at all. I'm also not sure how valid a use case it is where we won't know at build time what the rest of the firmware stack supports here.
That's a fair point. FF-A in theory has APIs to discover memory. Abdellatif, why do we need the Kconfigs for shared memory on FF-A?
The statically carved out MM shared buffer address, size and offset cannot be discovered by FF-A ABIs. The MM communication driver in U-Boot could allocate the buffer and share it with the MM SP but we do not implement that support currently in either U-Boot or UEFI.
Simon suggested we use build configs to set the buffer address, size and offset since we don't want a DT node for the MM firmware.
Simon would really prefer that we (once and for all) get over our DT aversion and just put it in the DT where it belongs.
But if you still don't want to do that, then falling back to Kconfig is OK.
Regards.
Simon

Hi Abdellatif
On Mon, 31 Jul 2023 at 14:46, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
Hi Ilias,
On Mon, Jul 31, 2023 at 12:38:16PM +0300, Ilias Apalodimas wrote:
... Changelog: ===============
v17:
- show a debug message rather than an error when FF-A is not detected
[snip]
diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index c5835e6ef6..8fbadb9201 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -55,13 +55,53 @@ 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"
depends on OPTEE
bool "UEFI variables storage service via the trusted world"
depends on OPTEE && ARM_FFA_TRANSPORT
You didn't get my changes in here however. If you can do EFI_MM_COMM_TEE without ARM_FFA_TRANSPORT (as lx2160ardb_tfa_stmm_defconfig does) then you don't make this option depend on . If FF-A is only for use here, you make FF-A depend on this, and the FF-A specific variable depend on ARM_FFA_TRANSPORT.
Abdellatif hinted at what's going on here. When I added this Kconfig option to lx2160 FF-A wasn't implemented yet.
The defconfig has existed since May 2020, which is when you added EFI_MM_COMM_TEE itself too. So I think it's that no one did the check I did until now and saw this series was disabling what was on the other platform.
Since FF-A isn't a new communication mechanism but builds upon the existing SMCs to build an easier API, I asked Abdellatif to hide this complexity. We had two options, either make Kconfig options for either FF-A or the traditional SMCs and remove the dependencies, or piggyback on FF-As discovery mechanism and make the choice at runtime. The latter has a small impact on code size, but imho makes developers' life a lot easier.
I'm not sure how much you can do a run-time option here since you're setting a bunch of default values for FF-A to 0 in Kconfig. If we're supposed to be able to get them at run time, we shouldn't need a Kconfig option at all. I'm also not sure how valid a use case it is where we won't know at build time what the rest of the firmware stack supports here.
That's a fair point. FF-A in theory has APIs to discover memory. Abdellatif, why do we need the Kconfigs for shared memory on FF-A?
The statically carved out MM shared buffer address, size and offset cannot be discovered by FF-A ABIs. The MM communication driver in U-Boot could allocate the buffer and share it with the MM SP but we do not implement that support currently in either U-Boot or UEFI.
Ok, that's a bit unfortunate, but Tom is right. Having the FF-A addresses show up is as confusing as having Kconfig options for discrete options. The whole point of my suggestion was to make users' lives easier. Apologies for the confusion but can you bring back the ifdefs? Looking at the patch this should be minimal just use ifdef ARM_FFA_TRANSPORT and ifndef ARM_FFA_TRANSPORT.
Tom you prefer that as well?
Thanks /Ilias
Simon suggested we use build configs to set the buffer address, size and offset since we don't want a DT node for the MM firmware.
Kind regards Abdellatif

On Tue, Aug 01, 2023 at 11:24:46AM +0300, Ilias Apalodimas wrote:
Hi Abdellatif
On Mon, 31 Jul 2023 at 14:46, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
Hi Ilias,
On Mon, Jul 31, 2023 at 12:38:16PM +0300, Ilias Apalodimas wrote:
> ... > Changelog: > =============== > > v17: > > * show a debug message rather than an error when FF-A is not detected [snip] > diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig > index c5835e6ef6..8fbadb9201 100644 > --- a/lib/efi_loader/Kconfig > +++ b/lib/efi_loader/Kconfig > @@ -55,13 +55,53 @@ 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" > - depends on OPTEE > + bool "UEFI variables storage service via the trusted world" > + depends on OPTEE && ARM_FFA_TRANSPORT
You didn't get my changes in here however. If you can do EFI_MM_COMM_TEE without ARM_FFA_TRANSPORT (as lx2160ardb_tfa_stmm_defconfig does) then you don't make this option depend on . If FF-A is only for use here, you make FF-A depend on this, and the FF-A specific variable depend on ARM_FFA_TRANSPORT.
Abdellatif hinted at what's going on here. When I added this Kconfig option to lx2160 FF-A wasn't implemented yet.
The defconfig has existed since May 2020, which is when you added EFI_MM_COMM_TEE itself too. So I think it's that no one did the check I did until now and saw this series was disabling what was on the other platform.
Since FF-A isn't a new communication mechanism but builds upon the existing SMCs to build an easier API, I asked Abdellatif to hide this complexity. We had two options, either make Kconfig options for either FF-A or the traditional SMCs and remove the dependencies, or piggyback on FF-As discovery mechanism and make the choice at runtime. The latter has a small impact on code size, but imho makes developers' life a lot easier.
I'm not sure how much you can do a run-time option here since you're setting a bunch of default values for FF-A to 0 in Kconfig. If we're supposed to be able to get them at run time, we shouldn't need a Kconfig option at all. I'm also not sure how valid a use case it is where we won't know at build time what the rest of the firmware stack supports here.
That's a fair point. FF-A in theory has APIs to discover memory. Abdellatif, why do we need the Kconfigs for shared memory on FF-A?
The statically carved out MM shared buffer address, size and offset cannot be discovered by FF-A ABIs. The MM communication driver in U-Boot could allocate the buffer and share it with the MM SP but we do not implement that support currently in either U-Boot or UEFI.
Ok, that's a bit unfortunate, but Tom is right. Having the FF-A addresses show up is as confusing as having Kconfig options for discrete options. The whole point of my suggestion was to make users' lives easier. Apologies for the confusion but can you bring back the ifdefs? Looking at the patch this should be minimal just use ifdef ARM_FFA_TRANSPORT and ifndef ARM_FFA_TRANSPORT.
Tom you prefer that as well?
Pending an answer to Jens' feedback, yes, going back to #ifdef's is fine, especially since default values of 0 are nonsense in this case (and as Heinrich's patch re SYS_MALLOC_LEN shows, dangerous since 0 != 0x0 once we do string comparisons).

Hi guys,
On Tue, Aug 01, 2023 at 11:00:57AM -0400, Tom Rini wrote:
> > ... > > Changelog: > > =============== > > > > v17: > > > > * show a debug message rather than an error when FF-A is not detected > [snip] > > diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig > > index c5835e6ef6..8fbadb9201 100644 > > --- a/lib/efi_loader/Kconfig > > +++ b/lib/efi_loader/Kconfig > > @@ -55,13 +55,53 @@ 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" > > - depends on OPTEE > > + bool "UEFI variables storage service via the trusted world" > > + depends on OPTEE && ARM_FFA_TRANSPORT > > You didn't get my changes in here however. If you can do EFI_MM_COMM_TEE > without ARM_FFA_TRANSPORT (as lx2160ardb_tfa_stmm_defconfig does) then > you don't make this option depend on . If FF-A is only > for use here, you make FF-A depend on this, and the FF-A specific > variable depend on ARM_FFA_TRANSPORT.
Abdellatif hinted at what's going on here. When I added this Kconfig option to lx2160 FF-A wasn't implemented yet.
The defconfig has existed since May 2020, which is when you added EFI_MM_COMM_TEE itself too. So I think it's that no one did the check I did until now and saw this series was disabling what was on the other platform.
Since FF-A isn't a new communication mechanism but builds upon the existing SMCs to build an easier API, I asked Abdellatif to hide this complexity. We had two options, either make Kconfig options for either FF-A or the traditional SMCs and remove the dependencies, or piggyback on FF-As discovery mechanism and make the choice at runtime. The latter has a small impact on code size, but imho makes developers' life a lot easier.
I'm not sure how much you can do a run-time option here since you're setting a bunch of default values for FF-A to 0 in Kconfig. If we're supposed to be able to get them at run time, we shouldn't need a Kconfig option at all. I'm also not sure how valid a use case it is where we won't know at build time what the rest of the firmware stack supports here.
That's a fair point. FF-A in theory has APIs to discover memory. Abdellatif, why do we need the Kconfigs for shared memory on FF-A?
The statically carved out MM shared buffer address, size and offset cannot be discovered by FF-A ABIs. The MM communication driver in U-Boot could allocate the buffer and share it with the MM SP but we do not implement that support currently in either U-Boot or UEFI.
Ok, that's a bit unfortunate, but Tom is right. Having the FF-A addresses show up is as confusing as having Kconfig options for discrete options. The whole point of my suggestion was to make users' lives easier. Apologies for the confusion but can you bring back the ifdefs? Looking at the patch this should be minimal just use ifdef ARM_FFA_TRANSPORT and ifndef ARM_FFA_TRANSPORT.
Tom you prefer that as well?
Pending an answer to Jens' feedback, yes, going back to #ifdef's is fine, especially since default values of 0 are nonsense in this case (and as Heinrich's patch re SYS_MALLOC_LEN shows, dangerous since 0 != 0x0 once we do string comparisons).
I'd like to give some context why it's important for Corstone-1000 platform that the DT passed to the kernel matches the official kernel DT.
There is a SystemReady IR 2.0 test checking the DT. It compares the DT passed by U-Boot with a reference DT (the kernel DT) . The test fails if there is a mismatch. So, if we add a DT node in U-Boot and the node is not upstreamed to the kernel DT, the DT test will fail.
To be approved by the kernel DT maintainers, the node should have a use case in the kernel which is not the case.
There is a solution for this which is deleting the node we don't want to pass to the kernel using delete-node in the U-Boot DT.
This method is used by many boards in U-Boot.
Example: /delete-node/ mm-comms-buf@02000000;
Corstone-1000 can have a DT overlay that deletes the node just before running Linux (in the boot command).
If you are happy with this solution, I can do the following:
1/ create a reserved-memory memory node for Corstone-1000, adding mm-comms-buf sub-node which defines the buffer address, size and offset.
With this we can get rid of the configs and the #defines: FFA_SHARED_MM_BUF_ADDR, FFA_SHARED_MM_BUF_OFFSET and FFA_SHARED_MM_BUF_SIZE.
Also, we will avoid setting 0 as default values for the address, size and offset.
2/ the FF-A specific code in efi_variable_tee.c will try to find the mm-comms-buf reserved memory node. When found, it reads the buffer address, size and offset.
3/ adding #ifdef CONFIG_ARM_FFA_TRANSPORT in lib/efi_loader/efi_variable_tee.c for the FF-A specific code.
4/ make EFI_MM_COMM_TEE depends on OPTEE only
What do you think guys ?
Kind regards Abdellatif

On Tue, Aug 01, 2023 at 05:10:08PM +0100, Abdellatif El Khlifi wrote:
Hi guys,
On Tue, Aug 01, 2023 at 11:00:57AM -0400, Tom Rini wrote:
> > > ... > > > Changelog: > > > =============== > > > > > > v17: > > > > > > * show a debug message rather than an error when FF-A is not detected > > [snip] > > > diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig > > > index c5835e6ef6..8fbadb9201 100644 > > > --- a/lib/efi_loader/Kconfig > > > +++ b/lib/efi_loader/Kconfig > > > @@ -55,13 +55,53 @@ 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" > > > - depends on OPTEE > > > + bool "UEFI variables storage service via the trusted world" > > > + depends on OPTEE && ARM_FFA_TRANSPORT > > > > You didn't get my changes in here however. If you can do EFI_MM_COMM_TEE > > without ARM_FFA_TRANSPORT (as lx2160ardb_tfa_stmm_defconfig does) then > > you don't make this option depend on . If FF-A is only > > for use here, you make FF-A depend on this, and the FF-A specific > > variable depend on ARM_FFA_TRANSPORT. > > Abdellatif hinted at what's going on here. When I added this Kconfig > option to lx2160 FF-A wasn't implemented yet.
The defconfig has existed since May 2020, which is when you added EFI_MM_COMM_TEE itself too. So I think it's that no one did the check I did until now and saw this series was disabling what was on the other platform.
> Since FF-A isn't a new > communication mechanism but builds upon the existing SMCs to build an > easier API, I asked Abdellatif to hide this complexity. > We had two options, either make Kconfig options for either FF-A or the > traditional SMCs and remove the dependencies, or piggyback on FF-As > discovery mechanism and make the choice at runtime. The latter has a > small impact on code size, but imho makes developers' life a lot > easier.
I'm not sure how much you can do a run-time option here since you're setting a bunch of default values for FF-A to 0 in Kconfig. If we're supposed to be able to get them at run time, we shouldn't need a Kconfig option at all. I'm also not sure how valid a use case it is where we won't know at build time what the rest of the firmware stack supports here.
That's a fair point. FF-A in theory has APIs to discover memory. Abdellatif, why do we need the Kconfigs for shared memory on FF-A?
The statically carved out MM shared buffer address, size and offset cannot be discovered by FF-A ABIs. The MM communication driver in U-Boot could allocate the buffer and share it with the MM SP but we do not implement that support currently in either U-Boot or UEFI.
Ok, that's a bit unfortunate, but Tom is right. Having the FF-A addresses show up is as confusing as having Kconfig options for discrete options. The whole point of my suggestion was to make users' lives easier. Apologies for the confusion but can you bring back the ifdefs? Looking at the patch this should be minimal just use ifdef ARM_FFA_TRANSPORT and ifndef ARM_FFA_TRANSPORT.
Tom you prefer that as well?
Pending an answer to Jens' feedback, yes, going back to #ifdef's is fine, especially since default values of 0 are nonsense in this case (and as Heinrich's patch re SYS_MALLOC_LEN shows, dangerous since 0 != 0x0 once we do string comparisons).
I'd like to give some context why it's important for Corstone-1000 platform that the DT passed to the kernel matches the official kernel DT.
Note that we've set aside the "should this be in DT or not" question.
There is a SystemReady IR 2.0 test checking the DT. It compares the DT passed by U-Boot with a reference DT (the kernel DT) . The test fails if there is a mismatch. So, if we add a DT node in U-Boot and the node is not upstreamed to the kernel DT, the DT test will fail.
This is overall good and progress.
To be approved by the kernel DT maintainers, the node should have a use case in the kernel which is not the case.
This is, I believe / hope wrong. It needs to be in the dt-schema repository, not strictly "the kernel". For example, bootph-all (etc) are in dt-schema and so can be in the upstream kernel but are not used in the kernel itself.
There is a solution for this which is deleting the node we don't want to pass to the kernel using delete-node in the U-Boot DT.
Something like this will likely be needed, in the end, at least for some cases. But the goal is that everything gets in to dt-schema.
[snip]
With this we can get rid of the configs and the #defines: FFA_SHARED_MM_BUF_ADDR, FFA_SHARED_MM_BUF_OFFSET and FFA_SHARED_MM_BUF_SIZE.
Also, we will avoid setting 0 as default values for the address, size and offset.
We just need to not have default values offered. The symbols just need to depend on FFA so that they aren't asked when not used.
2/ the FF-A specific code in efi_variable_tee.c will try to find the mm-comms-buf reserved memory node. When found, it reads the buffer address, size and offset.
3/ adding #ifdef CONFIG_ARM_FFA_TRANSPORT in lib/efi_loader/efi_variable_tee.c for the FF-A specific code.
4/ make EFI_MM_COMM_TEE depends on OPTEE only
What do you think guys ?
Yes, we need to do 3 and 4.

On Tue, 1 Aug 2023 at 19:19, Tom Rini trini@konsulko.com wrote:
On Tue, Aug 01, 2023 at 05:10:08PM +0100, Abdellatif El Khlifi wrote:
Hi guys,
On Tue, Aug 01, 2023 at 11:00:57AM -0400, Tom Rini wrote:
> > > > ... > > > > Changelog: > > > > =============== > > > > > > > > v17: > > > > > > > > * show a debug message rather than an error when FF-A is not detected > > > [snip] > > > > diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig > > > > index c5835e6ef6..8fbadb9201 100644 > > > > --- a/lib/efi_loader/Kconfig > > > > +++ b/lib/efi_loader/Kconfig > > > > @@ -55,13 +55,53 @@ 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" > > > > - depends on OPTEE > > > > + bool "UEFI variables storage service via the trusted world" > > > > + depends on OPTEE && ARM_FFA_TRANSPORT > > > > > > You didn't get my changes in here however. If you can do EFI_MM_COMM_TEE > > > without ARM_FFA_TRANSPORT (as lx2160ardb_tfa_stmm_defconfig does) then > > > you don't make this option depend on . If FF-A is only > > > for use here, you make FF-A depend on this, and the FF-A specific > > > variable depend on ARM_FFA_TRANSPORT. > > > > Abdellatif hinted at what's going on here. When I added this Kconfig > > option to lx2160 FF-A wasn't implemented yet. > > The defconfig has existed since May 2020, which is when you added > EFI_MM_COMM_TEE itself too. So I think it's that no one did the check I > did until now and saw this series was disabling what was on the other > platform. > > > Since FF-A isn't a new > > communication mechanism but builds upon the existing SMCs to build an > > easier API, I asked Abdellatif to hide this complexity. > > We had two options, either make Kconfig options for either FF-A or the > > traditional SMCs and remove the dependencies, or piggyback on FF-As > > discovery mechanism and make the choice at runtime. The latter has a > > small impact on code size, but imho makes developers' life a lot > > easier. > > I'm not sure how much you can do a run-time option here since you're > setting a bunch of default values for FF-A to 0 in Kconfig. If we're > supposed to be able to get them at run time, we shouldn't need a Kconfig > option at all. I'm also not sure how valid a use case it is where we > won't know at build time what the rest of the firmware stack supports > here. >
That's a fair point. FF-A in theory has APIs to discover memory. Abdellatif, why do we need the Kconfigs for shared memory on FF-A?
The statically carved out MM shared buffer address, size and offset cannot be discovered by FF-A ABIs. The MM communication driver in U-Boot could allocate the buffer and share it with the MM SP but we do not implement that support currently in either U-Boot or UEFI.
Ok, that's a bit unfortunate, but Tom is right. Having the FF-A addresses show up is as confusing as having Kconfig options for discrete options. The whole point of my suggestion was to make users' lives easier. Apologies for the confusion but can you bring back the ifdefs? Looking at the patch this should be minimal just use ifdef ARM_FFA_TRANSPORT and ifndef ARM_FFA_TRANSPORT.
Tom you prefer that as well?
Pending an answer to Jens' feedback, yes, going back to #ifdef's is fine, especially since default values of 0 are nonsense in this case (and as Heinrich's patch re SYS_MALLOC_LEN shows, dangerous since 0 != 0x0 once we do string comparisons).
I'd like to give some context why it's important for Corstone-1000 platform that the DT passed to the kernel matches the official kernel DT.
Note that we've set aside the "should this be in DT or not" question.
There is a SystemReady IR 2.0 test checking the DT. It compares the DT passed by U-Boot with a reference DT (the kernel DT) . The test fails if there is a mismatch. So, if we add a DT node in U-Boot and the node is not upstreamed to the kernel DT, the DT test will fail.
This is overall good and progress.
To be approved by the kernel DT maintainers, the node should have a use case in the kernel which is not the case.
This is, I believe / hope wrong. It needs to be in the dt-schema repository, not strictly "the kernel". For example, bootph-all (etc) are in dt-schema and so can be in the upstream kernel but are not used in the kernel itself.
There is a solution for this which is deleting the node we don't want to pass to the kernel using delete-node in the U-Boot DT.
Something like this will likely be needed, in the end, at least for some cases. But the goal is that everything gets in to dt-schema.
We are already working on U-Boot on that. The idea is rather simple. We will have an array with nodes and node entries. Before we boot up we'll scan that array, if a node entry exists we will delete that, otherwise we will just get rid of the entire node. That should be pretty easy to maintain and extend. U-Boot will then be able to use it;s internal bidings without polluting the DT we handover to the kernel.
I think you should just wait for that patch instead of hacking the boot cmd.
[snip]
With this we can get rid of the configs and the #defines: FFA_SHARED_MM_BUF_ADDR, FFA_SHARED_MM_BUF_OFFSET and FFA_SHARED_MM_BUF_SIZE.
Also, we will avoid setting 0 as default values for the address, size and offset.
We just need to not have default values offered. The symbols just need to depend on FFA so that they aren't asked when not used.
2/ the FF-A specific code in efi_variable_tee.c will try to find the mm-comms-buf reserved memory node. When found, it reads the buffer address, size and offset.
3/ adding #ifdef CONFIG_ARM_FFA_TRANSPORT in lib/efi_loader/efi_variable_tee.c for the FF-A specific code.
4/ make EFI_MM_COMM_TEE depends on OPTEE only
What do you think guys ?
Yes, we need to do 3 and 4.
+1 here
Thanks /Ilias
-- Tom

Hi Ilias,
On Wed, 2 Aug 2023 at 00:52, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
On Tue, 1 Aug 2023 at 19:19, Tom Rini trini@konsulko.com wrote:
On Tue, Aug 01, 2023 at 05:10:08PM +0100, Abdellatif El Khlifi wrote:
Hi guys,
On Tue, Aug 01, 2023 at 11:00:57AM -0400, Tom Rini wrote:
> > > > > ... > > > > > Changelog: > > > > > =============== > > > > > > > > > > v17: > > > > > > > > > > * show a debug message rather than an error when FF-A is not detected > > > > [snip] > > > > > diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig > > > > > index c5835e6ef6..8fbadb9201 100644 > > > > > --- a/lib/efi_loader/Kconfig > > > > > +++ b/lib/efi_loader/Kconfig > > > > > @@ -55,13 +55,53 @@ 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" > > > > > - depends on OPTEE > > > > > + bool "UEFI variables storage service via the trusted world" > > > > > + depends on OPTEE && ARM_FFA_TRANSPORT > > > > > > > > You didn't get my changes in here however. If you can do EFI_MM_COMM_TEE > > > > without ARM_FFA_TRANSPORT (as lx2160ardb_tfa_stmm_defconfig does) then > > > > you don't make this option depend on . If FF-A is only > > > > for use here, you make FF-A depend on this, and the FF-A specific > > > > variable depend on ARM_FFA_TRANSPORT. > > > > > > Abdellatif hinted at what's going on here. When I added this Kconfig > > > option to lx2160 FF-A wasn't implemented yet. > > > > The defconfig has existed since May 2020, which is when you added > > EFI_MM_COMM_TEE itself too. So I think it's that no one did the check I > > did until now and saw this series was disabling what was on the other > > platform. > > > > > Since FF-A isn't a new > > > communication mechanism but builds upon the existing SMCs to build an > > > easier API, I asked Abdellatif to hide this complexity. > > > We had two options, either make Kconfig options for either FF-A or the > > > traditional SMCs and remove the dependencies, or piggyback on FF-As > > > discovery mechanism and make the choice at runtime. The latter has a > > > small impact on code size, but imho makes developers' life a lot > > > easier. > > > > I'm not sure how much you can do a run-time option here since you're > > setting a bunch of default values for FF-A to 0 in Kconfig. If we're > > supposed to be able to get them at run time, we shouldn't need a Kconfig > > option at all. I'm also not sure how valid a use case it is where we > > won't know at build time what the rest of the firmware stack supports > > here. > > > > That's a fair point. FF-A in theory has APIs to discover memory. > Abdellatif, why do we need the Kconfigs for shared memory on FF-A?
The statically carved out MM shared buffer address, size and offset cannot be discovered by FF-A ABIs. The MM communication driver in U-Boot could allocate the buffer and share it with the MM SP but we do not implement that support currently in either U-Boot or UEFI.
Ok, that's a bit unfortunate, but Tom is right. Having the FF-A addresses show up is as confusing as having Kconfig options for discrete options. The whole point of my suggestion was to make users' lives easier. Apologies for the confusion but can you bring back the ifdefs? Looking at the patch this should be minimal just use ifdef ARM_FFA_TRANSPORT and ifndef ARM_FFA_TRANSPORT.
Tom you prefer that as well?
Pending an answer to Jens' feedback, yes, going back to #ifdef's is fine, especially since default values of 0 are nonsense in this case (and as Heinrich's patch re SYS_MALLOC_LEN shows, dangerous since 0 != 0x0 once we do string comparisons).
I'd like to give some context why it's important for Corstone-1000 platform that the DT passed to the kernel matches the official kernel DT.
Note that we've set aside the "should this be in DT or not" question.
There is a SystemReady IR 2.0 test checking the DT. It compares the DT passed by U-Boot with a reference DT (the kernel DT) . The test fails if there is a mismatch. So, if we add a DT node in U-Boot and the node is not upstreamed to the kernel DT, the DT test will fail.
This is overall good and progress.
To be approved by the kernel DT maintainers, the node should have a use case in the kernel which is not the case.
This is, I believe / hope wrong. It needs to be in the dt-schema repository, not strictly "the kernel". For example, bootph-all (etc) are in dt-schema and so can be in the upstream kernel but are not used in the kernel itself.
There is a solution for this which is deleting the node we don't want to pass to the kernel using delete-node in the U-Boot DT.
Something like this will likely be needed, in the end, at least for some cases. But the goal is that everything gets in to dt-schema.
We are already working on U-Boot on that. The idea is rather simple. We will have an array with nodes and node entries. Before we boot up we'll scan that array, if a node entry exists we will delete that, otherwise we will just get rid of the entire node. That should be pretty easy to maintain and extend. U-Boot will then be able to use it;s internal bidings without polluting the DT we handover to the kernel.
This is not pollution - we have moved past that now and Linux has accepted some U-Boot bindings. This is the DT and if there are things in it that are not related to Linux, it can ignore them.
We should add whatever bindings we need to make U-Boot work efficiently and correctly.
I think you should just wait for that patch instead of hacking the boot cmd.
[snip]
With this we can get rid of the configs and the #defines: FFA_SHARED_MM_BUF_ADDR, FFA_SHARED_MM_BUF_OFFSET and FFA_SHARED_MM_BUF_SIZE.
Also, we will avoid setting 0 as default values for the address, size and offset.
We just need to not have default values offered. The symbols just need to depend on FFA so that they aren't asked when not used.
2/ the FF-A specific code in efi_variable_tee.c will try to find the mm-comms-buf reserved memory node. When found, it reads the buffer address, size and offset.
3/ adding #ifdef CONFIG_ARM_FFA_TRANSPORT in lib/efi_loader/efi_variable_tee.c for the FF-A specific code.
4/ make EFI_MM_COMM_TEE depends on OPTEE only
What do you think guys ?
Yes, we need to do 3 and 4.
+1 here
Regards, Simon

Hi Simon,
On Wed, 2 Aug 2023 at 15:52, Simon Glass sjg@chromium.org wrote:
Hi Ilias,
On Wed, 2 Aug 2023 at 00:52, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
On Tue, 1 Aug 2023 at 19:19, Tom Rini trini@konsulko.com wrote:
On Tue, Aug 01, 2023 at 05:10:08PM +0100, Abdellatif El Khlifi wrote:
Hi guys,
On Tue, Aug 01, 2023 at 11:00:57AM -0400, Tom Rini wrote:
> > > > > > ... > > > > > > Changelog: > > > > > > =============== > > > > > > > > > > > > v17: > > > > > > > > > > > > * show a debug message rather than an error when FF-A is not detected > > > > > [snip] > > > > > > diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig > > > > > > index c5835e6ef6..8fbadb9201 100644 > > > > > > --- a/lib/efi_loader/Kconfig > > > > > > +++ b/lib/efi_loader/Kconfig > > > > > > @@ -55,13 +55,53 @@ 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" > > > > > > - depends on OPTEE > > > > > > + bool "UEFI variables storage service via the trusted world" > > > > > > + depends on OPTEE && ARM_FFA_TRANSPORT > > > > > > > > > > You didn't get my changes in here however. If you can do EFI_MM_COMM_TEE > > > > > without ARM_FFA_TRANSPORT (as lx2160ardb_tfa_stmm_defconfig does) then > > > > > you don't make this option depend on . If FF-A is only > > > > > for use here, you make FF-A depend on this, and the FF-A specific > > > > > variable depend on ARM_FFA_TRANSPORT. > > > > > > > > Abdellatif hinted at what's going on here. When I added this Kconfig > > > > option to lx2160 FF-A wasn't implemented yet. > > > > > > The defconfig has existed since May 2020, which is when you added > > > EFI_MM_COMM_TEE itself too. So I think it's that no one did the check I > > > did until now and saw this series was disabling what was on the other > > > platform. > > > > > > > Since FF-A isn't a new > > > > communication mechanism but builds upon the existing SMCs to build an > > > > easier API, I asked Abdellatif to hide this complexity. > > > > We had two options, either make Kconfig options for either FF-A or the > > > > traditional SMCs and remove the dependencies, or piggyback on FF-As > > > > discovery mechanism and make the choice at runtime. The latter has a > > > > small impact on code size, but imho makes developers' life a lot > > > > easier. > > > > > > I'm not sure how much you can do a run-time option here since you're > > > setting a bunch of default values for FF-A to 0 in Kconfig. If we're > > > supposed to be able to get them at run time, we shouldn't need a Kconfig > > > option at all. I'm also not sure how valid a use case it is where we > > > won't know at build time what the rest of the firmware stack supports > > > here. > > > > > > > That's a fair point. FF-A in theory has APIs to discover memory. > > Abdellatif, why do we need the Kconfigs for shared memory on FF-A? > > The statically carved out MM shared buffer address, size and offset cannot be discovered by FF-A ABIs. > The MM communication driver in U-Boot could allocate the buffer and share it with the MM SP but > we do not implement that support currently in either U-Boot or UEFI.
Ok, that's a bit unfortunate, but Tom is right. Having the FF-A addresses show up is as confusing as having Kconfig options for discrete options. The whole point of my suggestion was to make users' lives easier. Apologies for the confusion but can you bring back the ifdefs? Looking at the patch this should be minimal just use ifdef ARM_FFA_TRANSPORT and ifndef ARM_FFA_TRANSPORT.
Tom you prefer that as well?
Pending an answer to Jens' feedback, yes, going back to #ifdef's is fine, especially since default values of 0 are nonsense in this case (and as Heinrich's patch re SYS_MALLOC_LEN shows, dangerous since 0 != 0x0 once we do string comparisons).
I'd like to give some context why it's important for Corstone-1000 platform that the DT passed to the kernel matches the official kernel DT.
Note that we've set aside the "should this be in DT or not" question.
There is a SystemReady IR 2.0 test checking the DT. It compares the DT passed by U-Boot with a reference DT (the kernel DT) . The test fails if there is a mismatch. So, if we add a DT node in U-Boot and the node is not upstreamed to the kernel DT, the DT test will fail.
This is overall good and progress.
To be approved by the kernel DT maintainers, the node should have a use case in the kernel which is not the case.
This is, I believe / hope wrong. It needs to be in the dt-schema repository, not strictly "the kernel". For example, bootph-all (etc) are in dt-schema and so can be in the upstream kernel but are not used in the kernel itself.
There is a solution for this which is deleting the node we don't want to pass to the kernel using delete-node in the U-Boot DT.
Something like this will likely be needed, in the end, at least for some cases. But the goal is that everything gets in to dt-schema.
We are already working on U-Boot on that. The idea is rather simple. We will have an array with nodes and node entries. Before we boot up we'll scan that array, if a node entry exists we will delete that, otherwise we will just get rid of the entire node. That should be pretty easy to maintain and extend. U-Boot will then be able to use it;s internal bidings without polluting the DT we handover to the kernel.
This is not pollution - we have moved past that now and Linux has accepted some U-Boot bindings. This is the DT and if there are things in it that are not related to Linux, it can ignore them.
We should add whatever bindings we need to make U-Boot work efficiently and correctly.
The cases that are already accepted make sense. Things like the public part of the certificates used to authenticate capsule updates or the encoding of the recent a/b update nodes are not needed in any way in an OS. Those don't make sense to upstream and those are polluting the DT and need stripping
Regards /Ilias
I think you should just wait for that patch instead of hacking the boot cmd.
[snip]
With this we can get rid of the configs and the #defines: FFA_SHARED_MM_BUF_ADDR, FFA_SHARED_MM_BUF_OFFSET and FFA_SHARED_MM_BUF_SIZE.
Also, we will avoid setting 0 as default values for the address, size and offset.
We just need to not have default values offered. The symbols just need to depend on FFA so that they aren't asked when not used.
2/ the FF-A specific code in efi_variable_tee.c will try to find the mm-comms-buf reserved memory node. When found, it reads the buffer address, size and offset.
3/ adding #ifdef CONFIG_ARM_FFA_TRANSPORT in lib/efi_loader/efi_variable_tee.c for the FF-A specific code.
4/ make EFI_MM_COMM_TEE depends on OPTEE only
What do you think guys ?
Yes, we need to do 3 and 4.
+1 here
Regards, Simon

Hi Ilias,
On Wed, 2 Aug 2023 at 07:02, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Hi Simon,
On Wed, 2 Aug 2023 at 15:52, Simon Glass sjg@chromium.org wrote:
Hi Ilias,
On Wed, 2 Aug 2023 at 00:52, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
On Tue, 1 Aug 2023 at 19:19, Tom Rini trini@konsulko.com wrote:
On Tue, Aug 01, 2023 at 05:10:08PM +0100, Abdellatif El Khlifi wrote:
Hi guys,
On Tue, Aug 01, 2023 at 11:00:57AM -0400, Tom Rini wrote:
> > > > > > > ... > > > > > > > Changelog: > > > > > > > =============== > > > > > > > > > > > > > > v17: > > > > > > > > > > > > > > * show a debug message rather than an error when FF-A is not detected > > > > > > [snip] > > > > > > > diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig > > > > > > > index c5835e6ef6..8fbadb9201 100644 > > > > > > > --- a/lib/efi_loader/Kconfig > > > > > > > +++ b/lib/efi_loader/Kconfig > > > > > > > @@ -55,13 +55,53 @@ 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" > > > > > > > - depends on OPTEE > > > > > > > + bool "UEFI variables storage service via the trusted world" > > > > > > > + depends on OPTEE && ARM_FFA_TRANSPORT > > > > > > > > > > > > You didn't get my changes in here however. If you can do EFI_MM_COMM_TEE > > > > > > without ARM_FFA_TRANSPORT (as lx2160ardb_tfa_stmm_defconfig does) then > > > > > > you don't make this option depend on . If FF-A is only > > > > > > for use here, you make FF-A depend on this, and the FF-A specific > > > > > > variable depend on ARM_FFA_TRANSPORT. > > > > > > > > > > Abdellatif hinted at what's going on here. When I added this Kconfig > > > > > option to lx2160 FF-A wasn't implemented yet. > > > > > > > > The defconfig has existed since May 2020, which is when you added > > > > EFI_MM_COMM_TEE itself too. So I think it's that no one did the check I > > > > did until now and saw this series was disabling what was on the other > > > > platform. > > > > > > > > > Since FF-A isn't a new > > > > > communication mechanism but builds upon the existing SMCs to build an > > > > > easier API, I asked Abdellatif to hide this complexity. > > > > > We had two options, either make Kconfig options for either FF-A or the > > > > > traditional SMCs and remove the dependencies, or piggyback on FF-As > > > > > discovery mechanism and make the choice at runtime. The latter has a > > > > > small impact on code size, but imho makes developers' life a lot > > > > > easier. > > > > > > > > I'm not sure how much you can do a run-time option here since you're > > > > setting a bunch of default values for FF-A to 0 in Kconfig. If we're > > > > supposed to be able to get them at run time, we shouldn't need a Kconfig > > > > option at all. I'm also not sure how valid a use case it is where we > > > > won't know at build time what the rest of the firmware stack supports > > > > here. > > > > > > > > > > That's a fair point. FF-A in theory has APIs to discover memory. > > > Abdellatif, why do we need the Kconfigs for shared memory on FF-A? > > > > The statically carved out MM shared buffer address, size and offset cannot be discovered by FF-A ABIs. > > The MM communication driver in U-Boot could allocate the buffer and share it with the MM SP but > > we do not implement that support currently in either U-Boot or UEFI. > > Ok, that's a bit unfortunate, but Tom is right. Having the FF-A > addresses show up is as confusing as having Kconfig options for > discrete options. The whole point of my suggestion was to make users' > lives easier. Apologies for the confusion but can you bring back the > ifdefs? Looking at the patch this should be minimal just use > ifdef ARM_FFA_TRANSPORT and ifndef ARM_FFA_TRANSPORT. > > Tom you prefer that as well?
Pending an answer to Jens' feedback, yes, going back to #ifdef's is fine, especially since default values of 0 are nonsense in this case (and as Heinrich's patch re SYS_MALLOC_LEN shows, dangerous since 0 != 0x0 once we do string comparisons).
I'd like to give some context why it's important for Corstone-1000 platform that the DT passed to the kernel matches the official kernel DT.
Note that we've set aside the "should this be in DT or not" question.
There is a SystemReady IR 2.0 test checking the DT. It compares the DT passed by U-Boot with a reference DT (the kernel DT) . The test fails if there is a mismatch. So, if we add a DT node in U-Boot and the node is not upstreamed to the kernel DT, the DT test will fail.
This is overall good and progress.
To be approved by the kernel DT maintainers, the node should have a use case in the kernel which is not the case.
This is, I believe / hope wrong. It needs to be in the dt-schema repository, not strictly "the kernel". For example, bootph-all (etc) are in dt-schema and so can be in the upstream kernel but are not used in the kernel itself.
There is a solution for this which is deleting the node we don't want to pass to the kernel using delete-node in the U-Boot DT.
Something like this will likely be needed, in the end, at least for some cases. But the goal is that everything gets in to dt-schema.
We are already working on U-Boot on that. The idea is rather simple. We will have an array with nodes and node entries. Before we boot up we'll scan that array, if a node entry exists we will delete that, otherwise we will just get rid of the entire node. That should be pretty easy to maintain and extend. U-Boot will then be able to use it;s internal bidings without polluting the DT we handover to the kernel.
This is not pollution - we have moved past that now and Linux has accepted some U-Boot bindings. This is the DT and if there are things in it that are not related to Linux, it can ignore them.
We should add whatever bindings we need to make U-Boot work efficiently and correctly.
The cases that are already accepted make sense. Things like the public part of the certificates used to authenticate capsule updates or the encoding of the recent a/b update nodes are not needed in any way in an OS. Those don't make sense to upstream and those are polluting the DT and need stripping
It doesn't matter that Linux doesn't *need* it. If it is there it will have to accommodate it. We have loads of Linux stuff in the DT that means nothing to U-Boot. Many of the bindings chosen by Linux are wildly inefficient for U-Boot to implement.
We don't need to strip anything. This is not pollution. It is a binding agreement between projects.
Regards, Simon

Hi Simon,
On Wed, 2 Aug 2023 at 16:09, Simon Glass sjg@chromium.org wrote:
Hi Ilias,
On Wed, 2 Aug 2023 at 07:02, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Hi Simon,
On Wed, 2 Aug 2023 at 15:52, Simon Glass sjg@chromium.org wrote:
Hi Ilias,
On Wed, 2 Aug 2023 at 00:52, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
On Tue, 1 Aug 2023 at 19:19, Tom Rini trini@konsulko.com wrote:
On Tue, Aug 01, 2023 at 05:10:08PM +0100, Abdellatif El Khlifi wrote:
Hi guys,
On Tue, Aug 01, 2023 at 11:00:57AM -0400, Tom Rini wrote: > > > > > > > > ... > > > > > > > > Changelog: > > > > > > > > =============== > > > > > > > > > > > > > > > > v17: > > > > > > > > > > > > > > > > * show a debug message rather than an error when FF-A is not detected > > > > > > > [snip] > > > > > > > > diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig > > > > > > > > index c5835e6ef6..8fbadb9201 100644 > > > > > > > > --- a/lib/efi_loader/Kconfig > > > > > > > > +++ b/lib/efi_loader/Kconfig > > > > > > > > @@ -55,13 +55,53 @@ 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" > > > > > > > > - depends on OPTEE > > > > > > > > + bool "UEFI variables storage service via the trusted world" > > > > > > > > + depends on OPTEE && ARM_FFA_TRANSPORT > > > > > > > > > > > > > > You didn't get my changes in here however. If you can do EFI_MM_COMM_TEE > > > > > > > without ARM_FFA_TRANSPORT (as lx2160ardb_tfa_stmm_defconfig does) then > > > > > > > you don't make this option depend on . If FF-A is only > > > > > > > for use here, you make FF-A depend on this, and the FF-A specific > > > > > > > variable depend on ARM_FFA_TRANSPORT. > > > > > > > > > > > > Abdellatif hinted at what's going on here. When I added this Kconfig > > > > > > option to lx2160 FF-A wasn't implemented yet. > > > > > > > > > > The defconfig has existed since May 2020, which is when you added > > > > > EFI_MM_COMM_TEE itself too. So I think it's that no one did the check I > > > > > did until now and saw this series was disabling what was on the other > > > > > platform. > > > > > > > > > > > Since FF-A isn't a new > > > > > > communication mechanism but builds upon the existing SMCs to build an > > > > > > easier API, I asked Abdellatif to hide this complexity. > > > > > > We had two options, either make Kconfig options for either FF-A or the > > > > > > traditional SMCs and remove the dependencies, or piggyback on FF-As > > > > > > discovery mechanism and make the choice at runtime. The latter has a > > > > > > small impact on code size, but imho makes developers' life a lot > > > > > > easier. > > > > > > > > > > I'm not sure how much you can do a run-time option here since you're > > > > > setting a bunch of default values for FF-A to 0 in Kconfig. If we're > > > > > supposed to be able to get them at run time, we shouldn't need a Kconfig > > > > > option at all. I'm also not sure how valid a use case it is where we > > > > > won't know at build time what the rest of the firmware stack supports > > > > > here. > > > > > > > > > > > > > That's a fair point. FF-A in theory has APIs to discover memory. > > > > Abdellatif, why do we need the Kconfigs for shared memory on FF-A? > > > > > > The statically carved out MM shared buffer address, size and offset cannot be discovered by FF-A ABIs. > > > The MM communication driver in U-Boot could allocate the buffer and share it with the MM SP but > > > we do not implement that support currently in either U-Boot or UEFI. > > > > Ok, that's a bit unfortunate, but Tom is right. Having the FF-A > > addresses show up is as confusing as having Kconfig options for > > discrete options. The whole point of my suggestion was to make users' > > lives easier. Apologies for the confusion but can you bring back the > > ifdefs? Looking at the patch this should be minimal just use > > ifdef ARM_FFA_TRANSPORT and ifndef ARM_FFA_TRANSPORT. > > > > Tom you prefer that as well? > > Pending an answer to Jens' feedback, yes, going back to #ifdef's is > fine, especially since default values of 0 are nonsense in this case > (and as Heinrich's patch re SYS_MALLOC_LEN shows, dangerous since 0 != > 0x0 once we do string comparisons). >
I'd like to give some context why it's important for Corstone-1000 platform that the DT passed to the kernel matches the official kernel DT.
Note that we've set aside the "should this be in DT or not" question.
There is a SystemReady IR 2.0 test checking the DT. It compares the DT passed by U-Boot with a reference DT (the kernel DT) . The test fails if there is a mismatch. So, if we add a DT node in U-Boot and the node is not upstreamed to the kernel DT, the DT test will fail.
This is overall good and progress.
To be approved by the kernel DT maintainers, the node should have a use case in the kernel which is not the case.
This is, I believe / hope wrong. It needs to be in the dt-schema repository, not strictly "the kernel". For example, bootph-all (etc) are in dt-schema and so can be in the upstream kernel but are not used in the kernel itself.
There is a solution for this which is deleting the node we don't want to pass to the kernel using delete-node in the U-Boot DT.
Something like this will likely be needed, in the end, at least for some cases. But the goal is that everything gets in to dt-schema.
We are already working on U-Boot on that. The idea is rather simple. We will have an array with nodes and node entries. Before we boot up we'll scan that array, if a node entry exists we will delete that, otherwise we will just get rid of the entire node. That should be pretty easy to maintain and extend. U-Boot will then be able to use it;s internal bidings without polluting the DT we handover to the kernel.
This is not pollution - we have moved past that now and Linux has accepted some U-Boot bindings. This is the DT and if there are things in it that are not related to Linux, it can ignore them.
We should add whatever bindings we need to make U-Boot work efficiently and correctly.
The cases that are already accepted make sense. Things like the public part of the certificates used to authenticate capsule updates or the encoding of the recent a/b update nodes are not needed in any way in an OS. Those don't make sense to upstream and those are polluting the DT and need stripping
It doesn't matter that Linux doesn't *need* it. If it is there it will have to accommodate it. We have loads of Linux stuff in the DT that means nothing to U-Boot. Many of the bindings chosen by Linux are wildly inefficient for U-Boot to implement.
We don't need to strip anything. This is not pollution. It is a binding agreement between projects.
I am not a maintainer, but I doubt they view it that way. In any case, the DT produced by u-boot fails to pass the certification on every single platform that uses nonupstream nodes, so cleaning that up is needed. If people care enough and upstream those bindings we can preserve them
Thanks /Ilias
Regards, Simon

Hi Ilias,
On Wed, 2 Aug 2023 at 07:27, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Hi Simon,
On Wed, 2 Aug 2023 at 16:09, Simon Glass sjg@chromium.org wrote:
Hi Ilias,
On Wed, 2 Aug 2023 at 07:02, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Hi Simon,
On Wed, 2 Aug 2023 at 15:52, Simon Glass sjg@chromium.org wrote:
Hi Ilias,
On Wed, 2 Aug 2023 at 00:52, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
On Tue, 1 Aug 2023 at 19:19, Tom Rini trini@konsulko.com wrote:
On Tue, Aug 01, 2023 at 05:10:08PM +0100, Abdellatif El Khlifi wrote: > Hi guys, > > On Tue, Aug 01, 2023 at 11:00:57AM -0400, Tom Rini wrote: > > > > > > > > > ... > > > > > > > > > Changelog: > > > > > > > > > =============== > > > > > > > > > > > > > > > > > > v17: > > > > > > > > > > > > > > > > > > * show a debug message rather than an error when FF-A is not detected > > > > > > > > [snip] > > > > > > > > > diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig > > > > > > > > > index c5835e6ef6..8fbadb9201 100644 > > > > > > > > > --- a/lib/efi_loader/Kconfig > > > > > > > > > +++ b/lib/efi_loader/Kconfig > > > > > > > > > @@ -55,13 +55,53 @@ 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" > > > > > > > > > - depends on OPTEE > > > > > > > > > + bool "UEFI variables storage service via the trusted world" > > > > > > > > > + depends on OPTEE && ARM_FFA_TRANSPORT > > > > > > > > > > > > > > > > You didn't get my changes in here however. If you can do EFI_MM_COMM_TEE > > > > > > > > without ARM_FFA_TRANSPORT (as lx2160ardb_tfa_stmm_defconfig does) then > > > > > > > > you don't make this option depend on . If FF-A is only > > > > > > > > for use here, you make FF-A depend on this, and the FF-A specific > > > > > > > > variable depend on ARM_FFA_TRANSPORT. > > > > > > > > > > > > > > Abdellatif hinted at what's going on here. When I added this Kconfig > > > > > > > option to lx2160 FF-A wasn't implemented yet. > > > > > > > > > > > > The defconfig has existed since May 2020, which is when you added > > > > > > EFI_MM_COMM_TEE itself too. So I think it's that no one did the check I > > > > > > did until now and saw this series was disabling what was on the other > > > > > > platform. > > > > > > > > > > > > > Since FF-A isn't a new > > > > > > > communication mechanism but builds upon the existing SMCs to build an > > > > > > > easier API, I asked Abdellatif to hide this complexity. > > > > > > > We had two options, either make Kconfig options for either FF-A or the > > > > > > > traditional SMCs and remove the dependencies, or piggyback on FF-As > > > > > > > discovery mechanism and make the choice at runtime. The latter has a > > > > > > > small impact on code size, but imho makes developers' life a lot > > > > > > > easier. > > > > > > > > > > > > I'm not sure how much you can do a run-time option here since you're > > > > > > setting a bunch of default values for FF-A to 0 in Kconfig. If we're > > > > > > supposed to be able to get them at run time, we shouldn't need a Kconfig > > > > > > option at all. I'm also not sure how valid a use case it is where we > > > > > > won't know at build time what the rest of the firmware stack supports > > > > > > here. > > > > > > > > > > > > > > > > That's a fair point. FF-A in theory has APIs to discover memory. > > > > > Abdellatif, why do we need the Kconfigs for shared memory on FF-A? > > > > > > > > The statically carved out MM shared buffer address, size and offset cannot be discovered by FF-A ABIs. > > > > The MM communication driver in U-Boot could allocate the buffer and share it with the MM SP but > > > > we do not implement that support currently in either U-Boot or UEFI. > > > > > > Ok, that's a bit unfortunate, but Tom is right. Having the FF-A > > > addresses show up is as confusing as having Kconfig options for > > > discrete options. The whole point of my suggestion was to make users' > > > lives easier. Apologies for the confusion but can you bring back the > > > ifdefs? Looking at the patch this should be minimal just use > > > ifdef ARM_FFA_TRANSPORT and ifndef ARM_FFA_TRANSPORT. > > > > > > Tom you prefer that as well? > > > > Pending an answer to Jens' feedback, yes, going back to #ifdef's is > > fine, especially since default values of 0 are nonsense in this case > > (and as Heinrich's patch re SYS_MALLOC_LEN shows, dangerous since 0 != > > 0x0 once we do string comparisons). > > > > I'd like to give some context why it's important for Corstone-1000 platform > that the DT passed to the kernel matches the official kernel DT.
Note that we've set aside the "should this be in DT or not" question.
> There is a SystemReady IR 2.0 test checking the DT. It compares the DT > passed by U-Boot with a reference DT (the kernel DT) . The test fails if there > is a mismatch. So, if we add a DT node in U-Boot and the node is not upstreamed > to the kernel DT, the DT test will fail.
This is overall good and progress.
> To be approved by the kernel DT maintainers, the node should have a use case > in the kernel which is not the case.
This is, I believe / hope wrong. It needs to be in the dt-schema repository, not strictly "the kernel". For example, bootph-all (etc) are in dt-schema and so can be in the upstream kernel but are not used in the kernel itself.
> There is a solution for this which is deleting the node we don't want to pass to > the kernel using delete-node in the U-Boot DT.
Something like this will likely be needed, in the end, at least for some cases. But the goal is that everything gets in to dt-schema.
We are already working on U-Boot on that. The idea is rather simple. We will have an array with nodes and node entries. Before we boot up we'll scan that array, if a node entry exists we will delete that, otherwise we will just get rid of the entire node. That should be pretty easy to maintain and extend. U-Boot will then be able to use it;s internal bidings without polluting the DT we handover to the kernel.
This is not pollution - we have moved past that now and Linux has accepted some U-Boot bindings. This is the DT and if there are things in it that are not related to Linux, it can ignore them.
We should add whatever bindings we need to make U-Boot work efficiently and correctly.
The cases that are already accepted make sense. Things like the public part of the certificates used to authenticate capsule updates or the encoding of the recent a/b update nodes are not needed in any way in an OS. Those don't make sense to upstream and those are polluting the DT and need stripping
It doesn't matter that Linux doesn't *need* it. If it is there it will have to accommodate it. We have loads of Linux stuff in the DT that means nothing to U-Boot. Many of the bindings chosen by Linux are wildly inefficient for U-Boot to implement.
We don't need to strip anything. This is not pollution. It is a binding agreement between projects.
I am not a maintainer, but I doubt they view it that way. In any
Who?
case, the DT produced by u-boot fails to pass the certification on
U-Boot
every single platform that uses nonupstream nodes, so cleaning that up is needed. If people care enough and upstream those bindings we can preserve them
Yes I agree, and the bindings that are added need to be upstream in dt-schema. This applies also to the work that Linaro does.
I will mention this to Sugosh as well as he is adding a public key.
Regards, Simon

Hi Simon,
On Wed, 2 Aug 2023 at 16:34, Simon Glass sjg@chromium.org wrote:
Hi Ilias,
On Wed, 2 Aug 2023 at 07:27, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Hi Simon,
On Wed, 2 Aug 2023 at 16:09, Simon Glass sjg@chromium.org wrote:
Hi Ilias,
On Wed, 2 Aug 2023 at 07:02, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Hi Simon,
On Wed, 2 Aug 2023 at 15:52, Simon Glass sjg@chromium.org wrote:
Hi Ilias,
On Wed, 2 Aug 2023 at 00:52, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
On Tue, 1 Aug 2023 at 19:19, Tom Rini trini@konsulko.com wrote: > > On Tue, Aug 01, 2023 at 05:10:08PM +0100, Abdellatif El Khlifi wrote: > > Hi guys, > > > > On Tue, Aug 01, 2023 at 11:00:57AM -0400, Tom Rini wrote: > > > > > > > > > > ... > > > > > > > > > > Changelog: > > > > > > > > > > =============== > > > > > > > > > > > > > > > > > > > > v17: > > > > > > > > > > > > > > > > > > > > * show a debug message rather than an error when FF-A is not detected > > > > > > > > > [snip] > > > > > > > > > > diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig > > > > > > > > > > index c5835e6ef6..8fbadb9201 100644 > > > > > > > > > > --- a/lib/efi_loader/Kconfig > > > > > > > > > > +++ b/lib/efi_loader/Kconfig > > > > > > > > > > @@ -55,13 +55,53 @@ 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" > > > > > > > > > > - depends on OPTEE > > > > > > > > > > + bool "UEFI variables storage service via the trusted world" > > > > > > > > > > + depends on OPTEE && ARM_FFA_TRANSPORT > > > > > > > > > > > > > > > > > > You didn't get my changes in here however. If you can do EFI_MM_COMM_TEE > > > > > > > > > without ARM_FFA_TRANSPORT (as lx2160ardb_tfa_stmm_defconfig does) then > > > > > > > > > you don't make this option depend on . If FF-A is only > > > > > > > > > for use here, you make FF-A depend on this, and the FF-A specific > > > > > > > > > variable depend on ARM_FFA_TRANSPORT. > > > > > > > > > > > > > > > > Abdellatif hinted at what's going on here. When I added this Kconfig > > > > > > > > option to lx2160 FF-A wasn't implemented yet. > > > > > > > > > > > > > > The defconfig has existed since May 2020, which is when you added > > > > > > > EFI_MM_COMM_TEE itself too. So I think it's that no one did the check I > > > > > > > did until now and saw this series was disabling what was on the other > > > > > > > platform. > > > > > > > > > > > > > > > Since FF-A isn't a new > > > > > > > > communication mechanism but builds upon the existing SMCs to build an > > > > > > > > easier API, I asked Abdellatif to hide this complexity. > > > > > > > > We had two options, either make Kconfig options for either FF-A or the > > > > > > > > traditional SMCs and remove the dependencies, or piggyback on FF-As > > > > > > > > discovery mechanism and make the choice at runtime. The latter has a > > > > > > > > small impact on code size, but imho makes developers' life a lot > > > > > > > > easier. > > > > > > > > > > > > > > I'm not sure how much you can do a run-time option here since you're > > > > > > > setting a bunch of default values for FF-A to 0 in Kconfig. If we're > > > > > > > supposed to be able to get them at run time, we shouldn't need a Kconfig > > > > > > > option at all. I'm also not sure how valid a use case it is where we > > > > > > > won't know at build time what the rest of the firmware stack supports > > > > > > > here. > > > > > > > > > > > > > > > > > > > That's a fair point. FF-A in theory has APIs to discover memory. > > > > > > Abdellatif, why do we need the Kconfigs for shared memory on FF-A? > > > > > > > > > > The statically carved out MM shared buffer address, size and offset cannot be discovered by FF-A ABIs. > > > > > The MM communication driver in U-Boot could allocate the buffer and share it with the MM SP but > > > > > we do not implement that support currently in either U-Boot or UEFI. > > > > > > > > Ok, that's a bit unfortunate, but Tom is right. Having the FF-A > > > > addresses show up is as confusing as having Kconfig options for > > > > discrete options. The whole point of my suggestion was to make users' > > > > lives easier. Apologies for the confusion but can you bring back the > > > > ifdefs? Looking at the patch this should be minimal just use > > > > ifdef ARM_FFA_TRANSPORT and ifndef ARM_FFA_TRANSPORT. > > > > > > > > Tom you prefer that as well? > > > > > > Pending an answer to Jens' feedback, yes, going back to #ifdef's is > > > fine, especially since default values of 0 are nonsense in this case > > > (and as Heinrich's patch re SYS_MALLOC_LEN shows, dangerous since 0 != > > > 0x0 once we do string comparisons). > > > > > > > I'd like to give some context why it's important for Corstone-1000 platform > > that the DT passed to the kernel matches the official kernel DT. > > Note that we've set aside the "should this be in DT or not" question. > > > There is a SystemReady IR 2.0 test checking the DT. It compares the DT > > passed by U-Boot with a reference DT (the kernel DT) . The test fails if there > > is a mismatch. So, if we add a DT node in U-Boot and the node is not upstreamed > > to the kernel DT, the DT test will fail. > > This is overall good and progress. > > > To be approved by the kernel DT maintainers, the node should have a use case > > in the kernel which is not the case. > > This is, I believe / hope wrong. It needs to be in the dt-schema > repository, not strictly "the kernel". For example, bootph-all (etc) > are in dt-schema and so can be in the upstream kernel but are not used > in the kernel itself. > > > There is a solution for this which is deleting the node we don't want to pass to > > the kernel using delete-node in the U-Boot DT. > > Something like this will likely be needed, in the end, at least for some > cases. But the goal is that everything gets in to dt-schema.
We are already working on U-Boot on that. The idea is rather simple. We will have an array with nodes and node entries. Before we boot up we'll scan that array, if a node entry exists we will delete that, otherwise we will just get rid of the entire node. That should be pretty easy to maintain and extend. U-Boot will then be able to use it;s internal bidings without polluting the DT we handover to the kernel.
This is not pollution - we have moved past that now and Linux has accepted some U-Boot bindings. This is the DT and if there are things in it that are not related to Linux, it can ignore them.
We should add whatever bindings we need to make U-Boot work efficiently and correctly.
The cases that are already accepted make sense. Things like the public part of the certificates used to authenticate capsule updates or the encoding of the recent a/b update nodes are not needed in any way in an OS. Those don't make sense to upstream and those are polluting the DT and need stripping
It doesn't matter that Linux doesn't *need* it. If it is there it will have to accommodate it. We have loads of Linux stuff in the DT that means nothing to U-Boot. Many of the bindings chosen by Linux are wildly inefficient for U-Boot to implement.
We don't need to strip anything. This is not pollution. It is a binding agreement between projects.
I am not a maintainer, but I doubt they view it that way. In any
Who?
case, the DT produced by u-boot fails to pass the certification on
U-Boot
every single platform that uses nonupstream nodes, so cleaning that up is needed. If people care enough and upstream those bindings we can preserve them
Yes I agree, and the bindings that are added need to be upstream in dt-schema. This applies also to the work that Linaro does.
I will mention this to Sugosh as well as he is adding a public key.
He is already aware, he is working on a PoC that does exactly what I described. Once we verify devices are starting to pass the SystemReady2.0 certification he will send an RFC
Regards /Ilias
Regards, Simon

Hi Ilias,
On Wed, 2 Aug 2023 at 07:38, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Hi Simon,
On Wed, 2 Aug 2023 at 16:34, Simon Glass sjg@chromium.org wrote:
Hi Ilias,
On Wed, 2 Aug 2023 at 07:27, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Hi Simon,
On Wed, 2 Aug 2023 at 16:09, Simon Glass sjg@chromium.org wrote:
Hi Ilias,
On Wed, 2 Aug 2023 at 07:02, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Hi Simon,
On Wed, 2 Aug 2023 at 15:52, Simon Glass sjg@chromium.org wrote:
Hi Ilias,
On Wed, 2 Aug 2023 at 00:52, Ilias Apalodimas ilias.apalodimas@linaro.org wrote: > > On Tue, 1 Aug 2023 at 19:19, Tom Rini trini@konsulko.com wrote: > > > > On Tue, Aug 01, 2023 at 05:10:08PM +0100, Abdellatif El Khlifi wrote: > > > Hi guys, > > > > > > On Tue, Aug 01, 2023 at 11:00:57AM -0400, Tom Rini wrote: > > > > > > > > > > > ... > > > > > > > > > > > Changelog: > > > > > > > > > > > =============== > > > > > > > > > > > > > > > > > > > > > > v17: > > > > > > > > > > > > > > > > > > > > > > * show a debug message rather than an error when FF-A is not detected > > > > > > > > > > [snip] > > > > > > > > > > > diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig > > > > > > > > > > > index c5835e6ef6..8fbadb9201 100644 > > > > > > > > > > > --- a/lib/efi_loader/Kconfig > > > > > > > > > > > +++ b/lib/efi_loader/Kconfig > > > > > > > > > > > @@ -55,13 +55,53 @@ 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" > > > > > > > > > > > - depends on OPTEE > > > > > > > > > > > + bool "UEFI variables storage service via the trusted world" > > > > > > > > > > > + depends on OPTEE && ARM_FFA_TRANSPORT > > > > > > > > > > > > > > > > > > > > You didn't get my changes in here however. If you can do EFI_MM_COMM_TEE > > > > > > > > > > without ARM_FFA_TRANSPORT (as lx2160ardb_tfa_stmm_defconfig does) then > > > > > > > > > > you don't make this option depend on . If FF-A is only > > > > > > > > > > for use here, you make FF-A depend on this, and the FF-A specific > > > > > > > > > > variable depend on ARM_FFA_TRANSPORT. > > > > > > > > > > > > > > > > > > Abdellatif hinted at what's going on here. When I added this Kconfig > > > > > > > > > option to lx2160 FF-A wasn't implemented yet. > > > > > > > > > > > > > > > > The defconfig has existed since May 2020, which is when you added > > > > > > > > EFI_MM_COMM_TEE itself too. So I think it's that no one did the check I > > > > > > > > did until now and saw this series was disabling what was on the other > > > > > > > > platform. > > > > > > > > > > > > > > > > > Since FF-A isn't a new > > > > > > > > > communication mechanism but builds upon the existing SMCs to build an > > > > > > > > > easier API, I asked Abdellatif to hide this complexity. > > > > > > > > > We had two options, either make Kconfig options for either FF-A or the > > > > > > > > > traditional SMCs and remove the dependencies, or piggyback on FF-As > > > > > > > > > discovery mechanism and make the choice at runtime. The latter has a > > > > > > > > > small impact on code size, but imho makes developers' life a lot > > > > > > > > > easier. > > > > > > > > > > > > > > > > I'm not sure how much you can do a run-time option here since you're > > > > > > > > setting a bunch of default values for FF-A to 0 in Kconfig. If we're > > > > > > > > supposed to be able to get them at run time, we shouldn't need a Kconfig > > > > > > > > option at all. I'm also not sure how valid a use case it is where we > > > > > > > > won't know at build time what the rest of the firmware stack supports > > > > > > > > here. > > > > > > > > > > > > > > > > > > > > > > That's a fair point. FF-A in theory has APIs to discover memory. > > > > > > > Abdellatif, why do we need the Kconfigs for shared memory on FF-A? > > > > > > > > > > > > The statically carved out MM shared buffer address, size and offset cannot be discovered by FF-A ABIs. > > > > > > The MM communication driver in U-Boot could allocate the buffer and share it with the MM SP but > > > > > > we do not implement that support currently in either U-Boot or UEFI. > > > > > > > > > > Ok, that's a bit unfortunate, but Tom is right. Having the FF-A > > > > > addresses show up is as confusing as having Kconfig options for > > > > > discrete options. The whole point of my suggestion was to make users' > > > > > lives easier. Apologies for the confusion but can you bring back the > > > > > ifdefs? Looking at the patch this should be minimal just use > > > > > ifdef ARM_FFA_TRANSPORT and ifndef ARM_FFA_TRANSPORT. > > > > > > > > > > Tom you prefer that as well? > > > > > > > > Pending an answer to Jens' feedback, yes, going back to #ifdef's is > > > > fine, especially since default values of 0 are nonsense in this case > > > > (and as Heinrich's patch re SYS_MALLOC_LEN shows, dangerous since 0 != > > > > 0x0 once we do string comparisons). > > > > > > > > > > I'd like to give some context why it's important for Corstone-1000 platform > > > that the DT passed to the kernel matches the official kernel DT. > > > > Note that we've set aside the "should this be in DT or not" question. > > > > > There is a SystemReady IR 2.0 test checking the DT. It compares the DT > > > passed by U-Boot with a reference DT (the kernel DT) . The test fails if there > > > is a mismatch. So, if we add a DT node in U-Boot and the node is not upstreamed > > > to the kernel DT, the DT test will fail. > > > > This is overall good and progress. > > > > > To be approved by the kernel DT maintainers, the node should have a use case > > > in the kernel which is not the case. > > > > This is, I believe / hope wrong. It needs to be in the dt-schema > > repository, not strictly "the kernel". For example, bootph-all (etc) > > are in dt-schema and so can be in the upstream kernel but are not used > > in the kernel itself. > > > > > There is a solution for this which is deleting the node we don't want to pass to > > > the kernel using delete-node in the U-Boot DT. > > > > Something like this will likely be needed, in the end, at least for some > > cases. But the goal is that everything gets in to dt-schema. > > We are already working on U-Boot on that. The idea is rather simple. > We will have an array with nodes and node entries. Before we boot up > we'll scan that array, if a node entry exists we will delete that, > otherwise we will just get rid of the entire node. That should be > pretty easy to maintain and extend. U-Boot will then be able to use > it;s internal bidings without polluting the DT we handover to the > kernel.
This is not pollution - we have moved past that now and Linux has accepted some U-Boot bindings. This is the DT and if there are things in it that are not related to Linux, it can ignore them.
We should add whatever bindings we need to make U-Boot work efficiently and correctly.
The cases that are already accepted make sense. Things like the public part of the certificates used to authenticate capsule updates or the encoding of the recent a/b update nodes are not needed in any way in an OS. Those don't make sense to upstream and those are polluting the DT and need stripping
It doesn't matter that Linux doesn't *need* it. If it is there it will have to accommodate it. We have loads of Linux stuff in the DT that means nothing to U-Boot. Many of the bindings chosen by Linux are wildly inefficient for U-Boot to implement.
We don't need to strip anything. This is not pollution. It is a binding agreement between projects.
I am not a maintainer, but I doubt they view it that way. In any
Who?
case, the DT produced by u-boot fails to pass the certification on
U-Boot
every single platform that uses nonupstream nodes, so cleaning that up is needed. If people care enough and upstream those bindings we can preserve them
Yes I agree, and the bindings that are added need to be upstream in dt-schema. This applies also to the work that Linaro does.
I will mention this to Sugosh as well as he is adding a public key.
He is already aware, he is working on a PoC that does exactly what I described. Once we verify devices are starting to pass the SystemReady2.0 certification he will send an RFC
What PoC? You mean bindings?
Regards, Simon

On Wed, 2 Aug 2023 at 16:42, Simon Glass sjg@chromium.org wrote:
Hi Ilias,
On Wed, 2 Aug 2023 at 07:38, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Hi Simon,
On Wed, 2 Aug 2023 at 16:34, Simon Glass sjg@chromium.org wrote:
Hi Ilias,
On Wed, 2 Aug 2023 at 07:27, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Hi Simon,
On Wed, 2 Aug 2023 at 16:09, Simon Glass sjg@chromium.org wrote:
Hi Ilias,
On Wed, 2 Aug 2023 at 07:02, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Hi Simon,
On Wed, 2 Aug 2023 at 15:52, Simon Glass sjg@chromium.org wrote: > > Hi Ilias, > > On Wed, 2 Aug 2023 at 00:52, Ilias Apalodimas > ilias.apalodimas@linaro.org wrote: > > > > On Tue, 1 Aug 2023 at 19:19, Tom Rini trini@konsulko.com wrote: > > > > > > On Tue, Aug 01, 2023 at 05:10:08PM +0100, Abdellatif El Khlifi wrote: > > > > Hi guys, > > > > > > > > On Tue, Aug 01, 2023 at 11:00:57AM -0400, Tom Rini wrote: > > > > > > > > > > > > ... > > > > > > > > > > > > Changelog: > > > > > > > > > > > > =============== > > > > > > > > > > > > > > > > > > > > > > > > v17: > > > > > > > > > > > > > > > > > > > > > > > > * show a debug message rather than an error when FF-A is not detected > > > > > > > > > > > [snip] > > > > > > > > > > > > diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig > > > > > > > > > > > > index c5835e6ef6..8fbadb9201 100644 > > > > > > > > > > > > --- a/lib/efi_loader/Kconfig > > > > > > > > > > > > +++ b/lib/efi_loader/Kconfig > > > > > > > > > > > > @@ -55,13 +55,53 @@ 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" > > > > > > > > > > > > - depends on OPTEE > > > > > > > > > > > > + bool "UEFI variables storage service via the trusted world" > > > > > > > > > > > > + depends on OPTEE && ARM_FFA_TRANSPORT > > > > > > > > > > > > > > > > > > > > > > You didn't get my changes in here however. If you can do EFI_MM_COMM_TEE > > > > > > > > > > > without ARM_FFA_TRANSPORT (as lx2160ardb_tfa_stmm_defconfig does) then > > > > > > > > > > > you don't make this option depend on . If FF-A is only > > > > > > > > > > > for use here, you make FF-A depend on this, and the FF-A specific > > > > > > > > > > > variable depend on ARM_FFA_TRANSPORT. > > > > > > > > > > > > > > > > > > > > Abdellatif hinted at what's going on here. When I added this Kconfig > > > > > > > > > > option to lx2160 FF-A wasn't implemented yet. > > > > > > > > > > > > > > > > > > The defconfig has existed since May 2020, which is when you added > > > > > > > > > EFI_MM_COMM_TEE itself too. So I think it's that no one did the check I > > > > > > > > > did until now and saw this series was disabling what was on the other > > > > > > > > > platform. > > > > > > > > > > > > > > > > > > > Since FF-A isn't a new > > > > > > > > > > communication mechanism but builds upon the existing SMCs to build an > > > > > > > > > > easier API, I asked Abdellatif to hide this complexity. > > > > > > > > > > We had two options, either make Kconfig options for either FF-A or the > > > > > > > > > > traditional SMCs and remove the dependencies, or piggyback on FF-As > > > > > > > > > > discovery mechanism and make the choice at runtime. The latter has a > > > > > > > > > > small impact on code size, but imho makes developers' life a lot > > > > > > > > > > easier. > > > > > > > > > > > > > > > > > > I'm not sure how much you can do a run-time option here since you're > > > > > > > > > setting a bunch of default values for FF-A to 0 in Kconfig. If we're > > > > > > > > > supposed to be able to get them at run time, we shouldn't need a Kconfig > > > > > > > > > option at all. I'm also not sure how valid a use case it is where we > > > > > > > > > won't know at build time what the rest of the firmware stack supports > > > > > > > > > here. > > > > > > > > > > > > > > > > > > > > > > > > > That's a fair point. FF-A in theory has APIs to discover memory. > > > > > > > > Abdellatif, why do we need the Kconfigs for shared memory on FF-A? > > > > > > > > > > > > > > The statically carved out MM shared buffer address, size and offset cannot be discovered by FF-A ABIs. > > > > > > > The MM communication driver in U-Boot could allocate the buffer and share it with the MM SP but > > > > > > > we do not implement that support currently in either U-Boot or UEFI. > > > > > > > > > > > > Ok, that's a bit unfortunate, but Tom is right. Having the FF-A > > > > > > addresses show up is as confusing as having Kconfig options for > > > > > > discrete options. The whole point of my suggestion was to make users' > > > > > > lives easier. Apologies for the confusion but can you bring back the > > > > > > ifdefs? Looking at the patch this should be minimal just use > > > > > > ifdef ARM_FFA_TRANSPORT and ifndef ARM_FFA_TRANSPORT. > > > > > > > > > > > > Tom you prefer that as well? > > > > > > > > > > Pending an answer to Jens' feedback, yes, going back to #ifdef's is > > > > > fine, especially since default values of 0 are nonsense in this case > > > > > (and as Heinrich's patch re SYS_MALLOC_LEN shows, dangerous since 0 != > > > > > 0x0 once we do string comparisons). > > > > > > > > > > > > > I'd like to give some context why it's important for Corstone-1000 platform > > > > that the DT passed to the kernel matches the official kernel DT. > > > > > > Note that we've set aside the "should this be in DT or not" question. > > > > > > > There is a SystemReady IR 2.0 test checking the DT. It compares the DT > > > > passed by U-Boot with a reference DT (the kernel DT) . The test fails if there > > > > is a mismatch. So, if we add a DT node in U-Boot and the node is not upstreamed > > > > to the kernel DT, the DT test will fail. > > > > > > This is overall good and progress. > > > > > > > To be approved by the kernel DT maintainers, the node should have a use case > > > > in the kernel which is not the case. > > > > > > This is, I believe / hope wrong. It needs to be in the dt-schema > > > repository, not strictly "the kernel". For example, bootph-all (etc) > > > are in dt-schema and so can be in the upstream kernel but are not used > > > in the kernel itself. > > > > > > > There is a solution for this which is deleting the node we don't want to pass to > > > > the kernel using delete-node in the U-Boot DT. > > > > > > Something like this will likely be needed, in the end, at least for some > > > cases. But the goal is that everything gets in to dt-schema. > > > > We are already working on U-Boot on that. The idea is rather simple. > > We will have an array with nodes and node entries. Before we boot up > > we'll scan that array, if a node entry exists we will delete that, > > otherwise we will just get rid of the entire node. That should be > > pretty easy to maintain and extend. U-Boot will then be able to use > > it;s internal bidings without polluting the DT we handover to the > > kernel. > > This is not pollution - we have moved past that now and Linux has > accepted some U-Boot bindings. This is the DT and if there are things > in it that are not related to Linux, it can ignore them. > > We should add whatever bindings we need to make U-Boot work > efficiently and correctly. >
The cases that are already accepted make sense. Things like the public part of the certificates used to authenticate capsule updates or the encoding of the recent a/b update nodes are not needed in any way in an OS. Those don't make sense to upstream and those are polluting the DT and need stripping
It doesn't matter that Linux doesn't *need* it. If it is there it will have to accommodate it. We have loads of Linux stuff in the DT that means nothing to U-Boot. Many of the bindings chosen by Linux are wildly inefficient for U-Boot to implement.
We don't need to strip anything. This is not pollution. It is a binding agreement between projects.
I am not a maintainer, but I doubt they view it that way. In any
Who?
case, the DT produced by u-boot fails to pass the certification on
U-Boot
every single platform that uses nonupstream nodes, so cleaning that up is needed. If people care enough and upstream those bindings we can preserve them
Yes I agree, and the bindings that are added need to be upstream in dt-schema. This applies also to the work that Linaro does.
I will mention this to Sugosh as well as he is adding a public key.
He is already aware, he is working on a PoC that does exactly what I described. Once we verify devices are starting to pass the SystemReady2.0 certification he will send an RFC
What PoC? You mean bindings?
stripping of bindings that are not upstreamed in the dt-schema
Regards, Simon

Hi Ilias,
On Wed, 2 Aug 2023 at 07:43, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
On Wed, 2 Aug 2023 at 16:42, Simon Glass sjg@chromium.org wrote:
Hi Ilias,
On Wed, 2 Aug 2023 at 07:38, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Hi Simon,
On Wed, 2 Aug 2023 at 16:34, Simon Glass sjg@chromium.org wrote:
Hi Ilias,
On Wed, 2 Aug 2023 at 07:27, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Hi Simon,
On Wed, 2 Aug 2023 at 16:09, Simon Glass sjg@chromium.org wrote:
Hi Ilias,
On Wed, 2 Aug 2023 at 07:02, Ilias Apalodimas ilias.apalodimas@linaro.org wrote: > > Hi Simon, > > On Wed, 2 Aug 2023 at 15:52, Simon Glass sjg@chromium.org wrote: > > > > Hi Ilias, > > > > On Wed, 2 Aug 2023 at 00:52, Ilias Apalodimas > > ilias.apalodimas@linaro.org wrote: > > > > > > On Tue, 1 Aug 2023 at 19:19, Tom Rini trini@konsulko.com wrote: > > > > > > > > On Tue, Aug 01, 2023 at 05:10:08PM +0100, Abdellatif El Khlifi wrote: > > > > > Hi guys, > > > > > > > > > > On Tue, Aug 01, 2023 at 11:00:57AM -0400, Tom Rini wrote: > > > > > > > > > > > > > ... > > > > > > > > > > > > > Changelog: > > > > > > > > > > > > > =============== > > > > > > > > > > > > > > > > > > > > > > > > > > v17: > > > > > > > > > > > > > > > > > > > > > > > > > > * show a debug message rather than an error when FF-A is not detected > > > > > > > > > > > > [snip] > > > > > > > > > > > > > diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig > > > > > > > > > > > > > index c5835e6ef6..8fbadb9201 100644 > > > > > > > > > > > > > --- a/lib/efi_loader/Kconfig > > > > > > > > > > > > > +++ b/lib/efi_loader/Kconfig > > > > > > > > > > > > > @@ -55,13 +55,53 @@ 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" > > > > > > > > > > > > > - depends on OPTEE > > > > > > > > > > > > > + bool "UEFI variables storage service via the trusted world" > > > > > > > > > > > > > + depends on OPTEE && ARM_FFA_TRANSPORT > > > > > > > > > > > > > > > > > > > > > > > > You didn't get my changes in here however. If you can do EFI_MM_COMM_TEE > > > > > > > > > > > > without ARM_FFA_TRANSPORT (as lx2160ardb_tfa_stmm_defconfig does) then > > > > > > > > > > > > you don't make this option depend on . If FF-A is only > > > > > > > > > > > > for use here, you make FF-A depend on this, and the FF-A specific > > > > > > > > > > > > variable depend on ARM_FFA_TRANSPORT. > > > > > > > > > > > > > > > > > > > > > > Abdellatif hinted at what's going on here. When I added this Kconfig > > > > > > > > > > > option to lx2160 FF-A wasn't implemented yet. > > > > > > > > > > > > > > > > > > > > The defconfig has existed since May 2020, which is when you added > > > > > > > > > > EFI_MM_COMM_TEE itself too. So I think it's that no one did the check I > > > > > > > > > > did until now and saw this series was disabling what was on the other > > > > > > > > > > platform. > > > > > > > > > > > > > > > > > > > > > Since FF-A isn't a new > > > > > > > > > > > communication mechanism but builds upon the existing SMCs to build an > > > > > > > > > > > easier API, I asked Abdellatif to hide this complexity. > > > > > > > > > > > We had two options, either make Kconfig options for either FF-A or the > > > > > > > > > > > traditional SMCs and remove the dependencies, or piggyback on FF-As > > > > > > > > > > > discovery mechanism and make the choice at runtime. The latter has a > > > > > > > > > > > small impact on code size, but imho makes developers' life a lot > > > > > > > > > > > easier. > > > > > > > > > > > > > > > > > > > > I'm not sure how much you can do a run-time option here since you're > > > > > > > > > > setting a bunch of default values for FF-A to 0 in Kconfig. If we're > > > > > > > > > > supposed to be able to get them at run time, we shouldn't need a Kconfig > > > > > > > > > > option at all. I'm also not sure how valid a use case it is where we > > > > > > > > > > won't know at build time what the rest of the firmware stack supports > > > > > > > > > > here. > > > > > > > > > > > > > > > > > > > > > > > > > > > > That's a fair point. FF-A in theory has APIs to discover memory. > > > > > > > > > Abdellatif, why do we need the Kconfigs for shared memory on FF-A? > > > > > > > > > > > > > > > > The statically carved out MM shared buffer address, size and offset cannot be discovered by FF-A ABIs. > > > > > > > > The MM communication driver in U-Boot could allocate the buffer and share it with the MM SP but > > > > > > > > we do not implement that support currently in either U-Boot or UEFI. > > > > > > > > > > > > > > Ok, that's a bit unfortunate, but Tom is right. Having the FF-A > > > > > > > addresses show up is as confusing as having Kconfig options for > > > > > > > discrete options. The whole point of my suggestion was to make users' > > > > > > > lives easier. Apologies for the confusion but can you bring back the > > > > > > > ifdefs? Looking at the patch this should be minimal just use > > > > > > > ifdef ARM_FFA_TRANSPORT and ifndef ARM_FFA_TRANSPORT. > > > > > > > > > > > > > > Tom you prefer that as well? > > > > > > > > > > > > Pending an answer to Jens' feedback, yes, going back to #ifdef's is > > > > > > fine, especially since default values of 0 are nonsense in this case > > > > > > (and as Heinrich's patch re SYS_MALLOC_LEN shows, dangerous since 0 != > > > > > > 0x0 once we do string comparisons). > > > > > > > > > > > > > > > > I'd like to give some context why it's important for Corstone-1000 platform > > > > > that the DT passed to the kernel matches the official kernel DT. > > > > > > > > Note that we've set aside the "should this be in DT or not" question. > > > > > > > > > There is a SystemReady IR 2.0 test checking the DT. It compares the DT > > > > > passed by U-Boot with a reference DT (the kernel DT) . The test fails if there > > > > > is a mismatch. So, if we add a DT node in U-Boot and the node is not upstreamed > > > > > to the kernel DT, the DT test will fail. > > > > > > > > This is overall good and progress. > > > > > > > > > To be approved by the kernel DT maintainers, the node should have a use case > > > > > in the kernel which is not the case. > > > > > > > > This is, I believe / hope wrong. It needs to be in the dt-schema > > > > repository, not strictly "the kernel". For example, bootph-all (etc) > > > > are in dt-schema and so can be in the upstream kernel but are not used > > > > in the kernel itself. > > > > > > > > > There is a solution for this which is deleting the node we don't want to pass to > > > > > the kernel using delete-node in the U-Boot DT. > > > > > > > > Something like this will likely be needed, in the end, at least for some > > > > cases. But the goal is that everything gets in to dt-schema. > > > > > > We are already working on U-Boot on that. The idea is rather simple. > > > We will have an array with nodes and node entries. Before we boot up > > > we'll scan that array, if a node entry exists we will delete that, > > > otherwise we will just get rid of the entire node. That should be > > > pretty easy to maintain and extend. U-Boot will then be able to use > > > it;s internal bidings without polluting the DT we handover to the > > > kernel. > > > > This is not pollution - we have moved past that now and Linux has > > accepted some U-Boot bindings. This is the DT and if there are things > > in it that are not related to Linux, it can ignore them. > > > > We should add whatever bindings we need to make U-Boot work > > efficiently and correctly. > > > > The cases that are already accepted make sense. Things like the > public part of the certificates used to authenticate capsule updates > or the encoding of the recent a/b update nodes are not needed in any > way in an OS. Those don't make sense to upstream and those are > polluting the DT and need stripping
It doesn't matter that Linux doesn't *need* it. If it is there it will have to accommodate it. We have loads of Linux stuff in the DT that means nothing to U-Boot. Many of the bindings chosen by Linux are wildly inefficient for U-Boot to implement.
We don't need to strip anything. This is not pollution. It is a binding agreement between projects.
I am not a maintainer, but I doubt they view it that way. In any
Who?
case, the DT produced by u-boot fails to pass the certification on
U-Boot
every single platform that uses nonupstream nodes, so cleaning that up is needed. If people care enough and upstream those bindings we can preserve them
Yes I agree, and the bindings that are added need to be upstream in dt-schema. This applies also to the work that Linaro does.
I will mention this to Sugosh as well as he is adding a public key.
He is already aware, he is working on a PoC that does exactly what I described. Once we verify devices are starting to pass the SystemReady2.0 certification he will send an RFC
What PoC? You mean bindings?
stripping of bindings that are not upstreamed in the dt-schema
If that is what you want to do, then the binding needs to go upstream before we accept his patches.
Regards, Simon

Hi Simon,
On Wed, 2 Aug 2023 at 16:44, Simon Glass sjg@chromium.org wrote:
Hi Ilias,
On Wed, 2 Aug 2023 at 07:43, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
On Wed, 2 Aug 2023 at 16:42, Simon Glass sjg@chromium.org wrote:
Hi Ilias,
On Wed, 2 Aug 2023 at 07:38, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Hi Simon,
On Wed, 2 Aug 2023 at 16:34, Simon Glass sjg@chromium.org wrote:
Hi Ilias,
On Wed, 2 Aug 2023 at 07:27, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Hi Simon,
On Wed, 2 Aug 2023 at 16:09, Simon Glass sjg@chromium.org wrote: > > Hi Ilias, > > On Wed, 2 Aug 2023 at 07:02, Ilias Apalodimas > ilias.apalodimas@linaro.org wrote: > > > > Hi Simon, > > > > On Wed, 2 Aug 2023 at 15:52, Simon Glass sjg@chromium.org wrote: > > > > > > Hi Ilias, > > > > > > On Wed, 2 Aug 2023 at 00:52, Ilias Apalodimas > > > ilias.apalodimas@linaro.org wrote: > > > > > > > > On Tue, 1 Aug 2023 at 19:19, Tom Rini trini@konsulko.com wrote: > > > > > > > > > > On Tue, Aug 01, 2023 at 05:10:08PM +0100, Abdellatif El Khlifi wrote: > > > > > > Hi guys, > > > > > > > > > > > > On Tue, Aug 01, 2023 at 11:00:57AM -0400, Tom Rini wrote: > > > > > > > > > > > > > > ... > > > > > > > > > > > > > > Changelog: > > > > > > > > > > > > > > =============== > > > > > > > > > > > > > > > > > > > > > > > > > > > > v17: > > > > > > > > > > > > > > > > > > > > > > > > > > > > * show a debug message rather than an error when FF-A is not detected > > > > > > > > > > > > > [snip] > > > > > > > > > > > > > > diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig > > > > > > > > > > > > > > index c5835e6ef6..8fbadb9201 100644 > > > > > > > > > > > > > > --- a/lib/efi_loader/Kconfig > > > > > > > > > > > > > > +++ b/lib/efi_loader/Kconfig > > > > > > > > > > > > > > @@ -55,13 +55,53 @@ 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" > > > > > > > > > > > > > > - depends on OPTEE > > > > > > > > > > > > > > + bool "UEFI variables storage service via the trusted world" > > > > > > > > > > > > > > + depends on OPTEE && ARM_FFA_TRANSPORT > > > > > > > > > > > > > > > > > > > > > > > > > > You didn't get my changes in here however. If you can do EFI_MM_COMM_TEE > > > > > > > > > > > > > without ARM_FFA_TRANSPORT (as lx2160ardb_tfa_stmm_defconfig does) then > > > > > > > > > > > > > you don't make this option depend on . If FF-A is only > > > > > > > > > > > > > for use here, you make FF-A depend on this, and the FF-A specific > > > > > > > > > > > > > variable depend on ARM_FFA_TRANSPORT. > > > > > > > > > > > > > > > > > > > > > > > > Abdellatif hinted at what's going on here. When I added this Kconfig > > > > > > > > > > > > option to lx2160 FF-A wasn't implemented yet. > > > > > > > > > > > > > > > > > > > > > > The defconfig has existed since May 2020, which is when you added > > > > > > > > > > > EFI_MM_COMM_TEE itself too. So I think it's that no one did the check I > > > > > > > > > > > did until now and saw this series was disabling what was on the other > > > > > > > > > > > platform. > > > > > > > > > > > > > > > > > > > > > > > Since FF-A isn't a new > > > > > > > > > > > > communication mechanism but builds upon the existing SMCs to build an > > > > > > > > > > > > easier API, I asked Abdellatif to hide this complexity. > > > > > > > > > > > > We had two options, either make Kconfig options for either FF-A or the > > > > > > > > > > > > traditional SMCs and remove the dependencies, or piggyback on FF-As > > > > > > > > > > > > discovery mechanism and make the choice at runtime. The latter has a > > > > > > > > > > > > small impact on code size, but imho makes developers' life a lot > > > > > > > > > > > > easier. > > > > > > > > > > > > > > > > > > > > > > I'm not sure how much you can do a run-time option here since you're > > > > > > > > > > > setting a bunch of default values for FF-A to 0 in Kconfig. If we're > > > > > > > > > > > supposed to be able to get them at run time, we shouldn't need a Kconfig > > > > > > > > > > > option at all. I'm also not sure how valid a use case it is where we > > > > > > > > > > > won't know at build time what the rest of the firmware stack supports > > > > > > > > > > > here. > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > That's a fair point. FF-A in theory has APIs to discover memory. > > > > > > > > > > Abdellatif, why do we need the Kconfigs for shared memory on FF-A? > > > > > > > > > > > > > > > > > > The statically carved out MM shared buffer address, size and offset cannot be discovered by FF-A ABIs. > > > > > > > > > The MM communication driver in U-Boot could allocate the buffer and share it with the MM SP but > > > > > > > > > we do not implement that support currently in either U-Boot or UEFI. > > > > > > > > > > > > > > > > Ok, that's a bit unfortunate, but Tom is right. Having the FF-A > > > > > > > > addresses show up is as confusing as having Kconfig options for > > > > > > > > discrete options. The whole point of my suggestion was to make users' > > > > > > > > lives easier. Apologies for the confusion but can you bring back the > > > > > > > > ifdefs? Looking at the patch this should be minimal just use > > > > > > > > ifdef ARM_FFA_TRANSPORT and ifndef ARM_FFA_TRANSPORT. > > > > > > > > > > > > > > > > Tom you prefer that as well? > > > > > > > > > > > > > > Pending an answer to Jens' feedback, yes, going back to #ifdef's is > > > > > > > fine, especially since default values of 0 are nonsense in this case > > > > > > > (and as Heinrich's patch re SYS_MALLOC_LEN shows, dangerous since 0 != > > > > > > > 0x0 once we do string comparisons). > > > > > > > > > > > > > > > > > > > I'd like to give some context why it's important for Corstone-1000 platform > > > > > > that the DT passed to the kernel matches the official kernel DT. > > > > > > > > > > Note that we've set aside the "should this be in DT or not" question. > > > > > > > > > > > There is a SystemReady IR 2.0 test checking the DT. It compares the DT > > > > > > passed by U-Boot with a reference DT (the kernel DT) . The test fails if there > > > > > > is a mismatch. So, if we add a DT node in U-Boot and the node is not upstreamed > > > > > > to the kernel DT, the DT test will fail. > > > > > > > > > > This is overall good and progress. > > > > > > > > > > > To be approved by the kernel DT maintainers, the node should have a use case > > > > > > in the kernel which is not the case. > > > > > > > > > > This is, I believe / hope wrong. It needs to be in the dt-schema > > > > > repository, not strictly "the kernel". For example, bootph-all (etc) > > > > > are in dt-schema and so can be in the upstream kernel but are not used > > > > > in the kernel itself. > > > > > > > > > > > There is a solution for this which is deleting the node we don't want to pass to > > > > > > the kernel using delete-node in the U-Boot DT. > > > > > > > > > > Something like this will likely be needed, in the end, at least for some > > > > > cases. But the goal is that everything gets in to dt-schema. > > > > > > > > We are already working on U-Boot on that. The idea is rather simple. > > > > We will have an array with nodes and node entries. Before we boot up > > > > we'll scan that array, if a node entry exists we will delete that, > > > > otherwise we will just get rid of the entire node. That should be > > > > pretty easy to maintain and extend. U-Boot will then be able to use > > > > it;s internal bidings without polluting the DT we handover to the > > > > kernel. > > > > > > This is not pollution - we have moved past that now and Linux has > > > accepted some U-Boot bindings. This is the DT and if there are things > > > in it that are not related to Linux, it can ignore them. > > > > > > We should add whatever bindings we need to make U-Boot work > > > efficiently and correctly. > > > > > > > The cases that are already accepted make sense. Things like the > > public part of the certificates used to authenticate capsule updates > > or the encoding of the recent a/b update nodes are not needed in any > > way in an OS. Those don't make sense to upstream and those are > > polluting the DT and need stripping > > It doesn't matter that Linux doesn't *need* it. If it is there it will > have to accommodate it. We have loads of Linux stuff in the DT that > means nothing to U-Boot. Many of the bindings chosen by Linux are > wildly inefficient for U-Boot to implement. > > We don't need to strip anything. This is not pollution. It is a > binding agreement between projects. >
I am not a maintainer, but I doubt they view it that way. In any
Who?
case, the DT produced by u-boot fails to pass the certification on
U-Boot
every single platform that uses nonupstream nodes, so cleaning that up is needed. If people care enough and upstream those bindings we can preserve them
Yes I agree, and the bindings that are added need to be upstream in dt-schema. This applies also to the work that Linaro does.
I will mention this to Sugosh as well as he is adding a public key.
He is already aware, he is working on a PoC that does exactly what I described. Once we verify devices are starting to pass the SystemReady2.0 certification he will send an RFC
What PoC? You mean bindings?
stripping of bindings that are not upstreamed in the dt-schema
If that is what you want to do, then the binding needs to go upstream before we accept his patches.
This makes no sense whatsoever. Sughosh isn't adding anything new. Having the public portion on the DT file is something you insisted upon years ago and even reverted patches from me that had the key as a Kconfig option (which notably suffered from non of these problems, or the increased complexity we are adding now). What Sughosh is adding is autogenerating that, instead of having to manually stitch the DT.
Thanks /Ilias
Regards, Simon

Hi Ilias,
On Wed, 2 Aug 2023 at 07:48, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Hi Simon,
On Wed, 2 Aug 2023 at 16:44, Simon Glass sjg@chromium.org wrote:
Hi Ilias,
On Wed, 2 Aug 2023 at 07:43, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
On Wed, 2 Aug 2023 at 16:42, Simon Glass sjg@chromium.org wrote:
Hi Ilias,
On Wed, 2 Aug 2023 at 07:38, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Hi Simon,
On Wed, 2 Aug 2023 at 16:34, Simon Glass sjg@chromium.org wrote:
Hi Ilias,
On Wed, 2 Aug 2023 at 07:27, Ilias Apalodimas ilias.apalodimas@linaro.org wrote: > > Hi Simon, > > > On Wed, 2 Aug 2023 at 16:09, Simon Glass sjg@chromium.org wrote: > > > > Hi Ilias, > > > > On Wed, 2 Aug 2023 at 07:02, Ilias Apalodimas > > ilias.apalodimas@linaro.org wrote: > > > > > > Hi Simon, > > > > > > On Wed, 2 Aug 2023 at 15:52, Simon Glass sjg@chromium.org wrote: > > > > > > > > Hi Ilias, > > > > > > > > On Wed, 2 Aug 2023 at 00:52, Ilias Apalodimas > > > > ilias.apalodimas@linaro.org wrote: > > > > > > > > > > On Tue, 1 Aug 2023 at 19:19, Tom Rini trini@konsulko.com wrote: > > > > > > > > > > > > On Tue, Aug 01, 2023 at 05:10:08PM +0100, Abdellatif El Khlifi wrote: > > > > > > > Hi guys, > > > > > > > > > > > > > > On Tue, Aug 01, 2023 at 11:00:57AM -0400, Tom Rini wrote: > > > > > > > > > > > > > > > ... > > > > > > > > > > > > > > > Changelog: > > > > > > > > > > > > > > > =============== > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > v17: > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > * show a debug message rather than an error when FF-A is not detected > > > > > > > > > > > > > > [snip] > > > > > > > > > > > > > > > diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig > > > > > > > > > > > > > > > index c5835e6ef6..8fbadb9201 100644 > > > > > > > > > > > > > > > --- a/lib/efi_loader/Kconfig > > > > > > > > > > > > > > > +++ b/lib/efi_loader/Kconfig > > > > > > > > > > > > > > > @@ -55,13 +55,53 @@ 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" > > > > > > > > > > > > > > > - depends on OPTEE > > > > > > > > > > > > > > > + bool "UEFI variables storage service via the trusted world" > > > > > > > > > > > > > > > + depends on OPTEE && ARM_FFA_TRANSPORT > > > > > > > > > > > > > > > > > > > > > > > > > > > > You didn't get my changes in here however. If you can do EFI_MM_COMM_TEE > > > > > > > > > > > > > > without ARM_FFA_TRANSPORT (as lx2160ardb_tfa_stmm_defconfig does) then > > > > > > > > > > > > > > you don't make this option depend on . If FF-A is only > > > > > > > > > > > > > > for use here, you make FF-A depend on this, and the FF-A specific > > > > > > > > > > > > > > variable depend on ARM_FFA_TRANSPORT. > > > > > > > > > > > > > > > > > > > > > > > > > > Abdellatif hinted at what's going on here. When I added this Kconfig > > > > > > > > > > > > > option to lx2160 FF-A wasn't implemented yet. > > > > > > > > > > > > > > > > > > > > > > > > The defconfig has existed since May 2020, which is when you added > > > > > > > > > > > > EFI_MM_COMM_TEE itself too. So I think it's that no one did the check I > > > > > > > > > > > > did until now and saw this series was disabling what was on the other > > > > > > > > > > > > platform. > > > > > > > > > > > > > > > > > > > > > > > > > Since FF-A isn't a new > > > > > > > > > > > > > communication mechanism but builds upon the existing SMCs to build an > > > > > > > > > > > > > easier API, I asked Abdellatif to hide this complexity. > > > > > > > > > > > > > We had two options, either make Kconfig options for either FF-A or the > > > > > > > > > > > > > traditional SMCs and remove the dependencies, or piggyback on FF-As > > > > > > > > > > > > > discovery mechanism and make the choice at runtime. The latter has a > > > > > > > > > > > > > small impact on code size, but imho makes developers' life a lot > > > > > > > > > > > > > easier. > > > > > > > > > > > > > > > > > > > > > > > > I'm not sure how much you can do a run-time option here since you're > > > > > > > > > > > > setting a bunch of default values for FF-A to 0 in Kconfig. If we're > > > > > > > > > > > > supposed to be able to get them at run time, we shouldn't need a Kconfig > > > > > > > > > > > > option at all. I'm also not sure how valid a use case it is where we > > > > > > > > > > > > won't know at build time what the rest of the firmware stack supports > > > > > > > > > > > > here. > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > That's a fair point. FF-A in theory has APIs to discover memory. > > > > > > > > > > > Abdellatif, why do we need the Kconfigs for shared memory on FF-A? > > > > > > > > > > > > > > > > > > > > The statically carved out MM shared buffer address, size and offset cannot be discovered by FF-A ABIs. > > > > > > > > > > The MM communication driver in U-Boot could allocate the buffer and share it with the MM SP but > > > > > > > > > > we do not implement that support currently in either U-Boot or UEFI. > > > > > > > > > > > > > > > > > > Ok, that's a bit unfortunate, but Tom is right. Having the FF-A > > > > > > > > > addresses show up is as confusing as having Kconfig options for > > > > > > > > > discrete options. The whole point of my suggestion was to make users' > > > > > > > > > lives easier. Apologies for the confusion but can you bring back the > > > > > > > > > ifdefs? Looking at the patch this should be minimal just use > > > > > > > > > ifdef ARM_FFA_TRANSPORT and ifndef ARM_FFA_TRANSPORT. > > > > > > > > > > > > > > > > > > Tom you prefer that as well? > > > > > > > > > > > > > > > > Pending an answer to Jens' feedback, yes, going back to #ifdef's is > > > > > > > > fine, especially since default values of 0 are nonsense in this case > > > > > > > > (and as Heinrich's patch re SYS_MALLOC_LEN shows, dangerous since 0 != > > > > > > > > 0x0 once we do string comparisons). > > > > > > > > > > > > > > > > > > > > > > I'd like to give some context why it's important for Corstone-1000 platform > > > > > > > that the DT passed to the kernel matches the official kernel DT. > > > > > > > > > > > > Note that we've set aside the "should this be in DT or not" question. > > > > > > > > > > > > > There is a SystemReady IR 2.0 test checking the DT. It compares the DT > > > > > > > passed by U-Boot with a reference DT (the kernel DT) . The test fails if there > > > > > > > is a mismatch. So, if we add a DT node in U-Boot and the node is not upstreamed > > > > > > > to the kernel DT, the DT test will fail. > > > > > > > > > > > > This is overall good and progress. > > > > > > > > > > > > > To be approved by the kernel DT maintainers, the node should have a use case > > > > > > > in the kernel which is not the case. > > > > > > > > > > > > This is, I believe / hope wrong. It needs to be in the dt-schema > > > > > > repository, not strictly "the kernel". For example, bootph-all (etc) > > > > > > are in dt-schema and so can be in the upstream kernel but are not used > > > > > > in the kernel itself. > > > > > > > > > > > > > There is a solution for this which is deleting the node we don't want to pass to > > > > > > > the kernel using delete-node in the U-Boot DT. > > > > > > > > > > > > Something like this will likely be needed, in the end, at least for some > > > > > > cases. But the goal is that everything gets in to dt-schema. > > > > > > > > > > We are already working on U-Boot on that. The idea is rather simple. > > > > > We will have an array with nodes and node entries. Before we boot up > > > > > we'll scan that array, if a node entry exists we will delete that, > > > > > otherwise we will just get rid of the entire node. That should be > > > > > pretty easy to maintain and extend. U-Boot will then be able to use > > > > > it;s internal bidings without polluting the DT we handover to the > > > > > kernel. > > > > > > > > This is not pollution - we have moved past that now and Linux has > > > > accepted some U-Boot bindings. This is the DT and if there are things > > > > in it that are not related to Linux, it can ignore them. > > > > > > > > We should add whatever bindings we need to make U-Boot work > > > > efficiently and correctly. > > > > > > > > > > The cases that are already accepted make sense. Things like the > > > public part of the certificates used to authenticate capsule updates > > > or the encoding of the recent a/b update nodes are not needed in any > > > way in an OS. Those don't make sense to upstream and those are > > > polluting the DT and need stripping > > > > It doesn't matter that Linux doesn't *need* it. If it is there it will > > have to accommodate it. We have loads of Linux stuff in the DT that > > means nothing to U-Boot. Many of the bindings chosen by Linux are > > wildly inefficient for U-Boot to implement. > > > > We don't need to strip anything. This is not pollution. It is a > > binding agreement between projects. > > > > I am not a maintainer, but I doubt they view it that way. In any
Who?
> case, the DT produced by u-boot fails to pass the certification on
U-Boot
> every single platform that uses nonupstream nodes, so cleaning that up > is needed. If people care enough and upstream those bindings we can > preserve them
Yes I agree, and the bindings that are added need to be upstream in dt-schema. This applies also to the work that Linaro does.
I will mention this to Sugosh as well as he is adding a public key.
He is already aware, he is working on a PoC that does exactly what I described. Once we verify devices are starting to pass the SystemReady2.0 certification he will send an RFC
What PoC? You mean bindings?
stripping of bindings that are not upstreamed in the dt-schema
If that is what you want to do, then the binding needs to go upstream before we accept his patches.
This makes no sense whatsoever. Sughosh isn't adding anything new. Having the public portion on the DT file is something you insisted upon years ago and even reverted patches from me that had the key as a Kconfig option (which notably suffered from non of these problems, or the increased complexity we are adding now). What Sughosh is adding is autogenerating that, instead of having to manually stitch the DT.
Yes and it forms part of the machine's DT and needs to have a binding, just like the Binman nodes need a binding. Isn't this the whole point?
Either: - we upstream the U-Boot bindings, or - we allow U-Boot to put whatever it wants in there and accept it
I much prefer the first option since we can run validation on it, as I'm sure you would prefer. But removing nodes before booting just because we haven't upstreamed things is just going to lead to no one bothering to do it.
Regards, Simon

Hi Simon,
On Wed, 2 Aug 2023 at 16:55, Simon Glass sjg@chromium.org wrote:
Hi Ilias,
On Wed, 2 Aug 2023 at 07:48, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Hi Simon,
On Wed, 2 Aug 2023 at 16:44, Simon Glass sjg@chromium.org wrote:
Hi Ilias,
On Wed, 2 Aug 2023 at 07:43, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
On Wed, 2 Aug 2023 at 16:42, Simon Glass sjg@chromium.org wrote:
Hi Ilias,
On Wed, 2 Aug 2023 at 07:38, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Hi Simon,
On Wed, 2 Aug 2023 at 16:34, Simon Glass sjg@chromium.org wrote: > > Hi Ilias, > > On Wed, 2 Aug 2023 at 07:27, Ilias Apalodimas > ilias.apalodimas@linaro.org wrote: > > > > Hi Simon, > > > > > > On Wed, 2 Aug 2023 at 16:09, Simon Glass sjg@chromium.org wrote: > > > > > > Hi Ilias, > > > > > > On Wed, 2 Aug 2023 at 07:02, Ilias Apalodimas > > > ilias.apalodimas@linaro.org wrote: > > > > > > > > Hi Simon, > > > > > > > > On Wed, 2 Aug 2023 at 15:52, Simon Glass sjg@chromium.org wrote: > > > > > > > > > > Hi Ilias, > > > > > > > > > > On Wed, 2 Aug 2023 at 00:52, Ilias Apalodimas > > > > > ilias.apalodimas@linaro.org wrote: > > > > > > > > > > > > On Tue, 1 Aug 2023 at 19:19, Tom Rini trini@konsulko.com wrote: > > > > > > > > > > > > > > On Tue, Aug 01, 2023 at 05:10:08PM +0100, Abdellatif El Khlifi wrote: > > > > > > > > Hi guys, > > > > > > > > > > > > > > > > On Tue, Aug 01, 2023 at 11:00:57AM -0400, Tom Rini wrote: > > > > > > > > > > > > > > > > ... > > > > > > > > > > > > > > > > Changelog: > > > > > > > > > > > > > > > > =============== > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > v17: > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > * show a debug message rather than an error when FF-A is not detected > > > > > > > > > > > > > > > [snip] > > > > > > > > > > > > > > > > diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig > > > > > > > > > > > > > > > > index c5835e6ef6..8fbadb9201 100644 > > > > > > > > > > > > > > > > --- a/lib/efi_loader/Kconfig > > > > > > > > > > > > > > > > +++ b/lib/efi_loader/Kconfig > > > > > > > > > > > > > > > > @@ -55,13 +55,53 @@ 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" > > > > > > > > > > > > > > > > - depends on OPTEE > > > > > > > > > > > > > > > > + bool "UEFI variables storage service via the trusted world" > > > > > > > > > > > > > > > > + depends on OPTEE && ARM_FFA_TRANSPORT > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > You didn't get my changes in here however. If you can do EFI_MM_COMM_TEE > > > > > > > > > > > > > > > without ARM_FFA_TRANSPORT (as lx2160ardb_tfa_stmm_defconfig does) then > > > > > > > > > > > > > > > you don't make this option depend on . If FF-A is only > > > > > > > > > > > > > > > for use here, you make FF-A depend on this, and the FF-A specific > > > > > > > > > > > > > > > variable depend on ARM_FFA_TRANSPORT. > > > > > > > > > > > > > > > > > > > > > > > > > > > > Abdellatif hinted at what's going on here. When I added this Kconfig > > > > > > > > > > > > > > option to lx2160 FF-A wasn't implemented yet. > > > > > > > > > > > > > > > > > > > > > > > > > > The defconfig has existed since May 2020, which is when you added > > > > > > > > > > > > > EFI_MM_COMM_TEE itself too. So I think it's that no one did the check I > > > > > > > > > > > > > did until now and saw this series was disabling what was on the other > > > > > > > > > > > > > platform. > > > > > > > > > > > > > > > > > > > > > > > > > > > Since FF-A isn't a new > > > > > > > > > > > > > > communication mechanism but builds upon the existing SMCs to build an > > > > > > > > > > > > > > easier API, I asked Abdellatif to hide this complexity. > > > > > > > > > > > > > > We had two options, either make Kconfig options for either FF-A or the > > > > > > > > > > > > > > traditional SMCs and remove the dependencies, or piggyback on FF-As > > > > > > > > > > > > > > discovery mechanism and make the choice at runtime. The latter has a > > > > > > > > > > > > > > small impact on code size, but imho makes developers' life a lot > > > > > > > > > > > > > > easier. > > > > > > > > > > > > > > > > > > > > > > > > > > I'm not sure how much you can do a run-time option here since you're > > > > > > > > > > > > > setting a bunch of default values for FF-A to 0 in Kconfig. If we're > > > > > > > > > > > > > supposed to be able to get them at run time, we shouldn't need a Kconfig > > > > > > > > > > > > > option at all. I'm also not sure how valid a use case it is where we > > > > > > > > > > > > > won't know at build time what the rest of the firmware stack supports > > > > > > > > > > > > > here. > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > That's a fair point. FF-A in theory has APIs to discover memory. > > > > > > > > > > > > Abdellatif, why do we need the Kconfigs for shared memory on FF-A? > > > > > > > > > > > > > > > > > > > > > > The statically carved out MM shared buffer address, size and offset cannot be discovered by FF-A ABIs. > > > > > > > > > > > The MM communication driver in U-Boot could allocate the buffer and share it with the MM SP but > > > > > > > > > > > we do not implement that support currently in either U-Boot or UEFI. > > > > > > > > > > > > > > > > > > > > Ok, that's a bit unfortunate, but Tom is right. Having the FF-A > > > > > > > > > > addresses show up is as confusing as having Kconfig options for > > > > > > > > > > discrete options. The whole point of my suggestion was to make users' > > > > > > > > > > lives easier. Apologies for the confusion but can you bring back the > > > > > > > > > > ifdefs? Looking at the patch this should be minimal just use > > > > > > > > > > ifdef ARM_FFA_TRANSPORT and ifndef ARM_FFA_TRANSPORT. > > > > > > > > > > > > > > > > > > > > Tom you prefer that as well? > > > > > > > > > > > > > > > > > > Pending an answer to Jens' feedback, yes, going back to #ifdef's is > > > > > > > > > fine, especially since default values of 0 are nonsense in this case > > > > > > > > > (and as Heinrich's patch re SYS_MALLOC_LEN shows, dangerous since 0 != > > > > > > > > > 0x0 once we do string comparisons). > > > > > > > > > > > > > > > > > > > > > > > > > I'd like to give some context why it's important for Corstone-1000 platform > > > > > > > > that the DT passed to the kernel matches the official kernel DT. > > > > > > > > > > > > > > Note that we've set aside the "should this be in DT or not" question. > > > > > > > > > > > > > > > There is a SystemReady IR 2.0 test checking the DT. It compares the DT > > > > > > > > passed by U-Boot with a reference DT (the kernel DT) . The test fails if there > > > > > > > > is a mismatch. So, if we add a DT node in U-Boot and the node is not upstreamed > > > > > > > > to the kernel DT, the DT test will fail. > > > > > > > > > > > > > > This is overall good and progress. > > > > > > > > > > > > > > > To be approved by the kernel DT maintainers, the node should have a use case > > > > > > > > in the kernel which is not the case. > > > > > > > > > > > > > > This is, I believe / hope wrong. It needs to be in the dt-schema > > > > > > > repository, not strictly "the kernel". For example, bootph-all (etc) > > > > > > > are in dt-schema and so can be in the upstream kernel but are not used > > > > > > > in the kernel itself. > > > > > > > > > > > > > > > There is a solution for this which is deleting the node we don't want to pass to > > > > > > > > the kernel using delete-node in the U-Boot DT. > > > > > > > > > > > > > > Something like this will likely be needed, in the end, at least for some > > > > > > > cases. But the goal is that everything gets in to dt-schema. > > > > > > > > > > > > We are already working on U-Boot on that. The idea is rather simple. > > > > > > We will have an array with nodes and node entries. Before we boot up > > > > > > we'll scan that array, if a node entry exists we will delete that, > > > > > > otherwise we will just get rid of the entire node. That should be > > > > > > pretty easy to maintain and extend. U-Boot will then be able to use > > > > > > it;s internal bidings without polluting the DT we handover to the > > > > > > kernel. > > > > > > > > > > This is not pollution - we have moved past that now and Linux has > > > > > accepted some U-Boot bindings. This is the DT and if there are things > > > > > in it that are not related to Linux, it can ignore them. > > > > > > > > > > We should add whatever bindings we need to make U-Boot work > > > > > efficiently and correctly. > > > > > > > > > > > > > The cases that are already accepted make sense. Things like the > > > > public part of the certificates used to authenticate capsule updates > > > > or the encoding of the recent a/b update nodes are not needed in any > > > > way in an OS. Those don't make sense to upstream and those are > > > > polluting the DT and need stripping > > > > > > It doesn't matter that Linux doesn't *need* it. If it is there it will > > > have to accommodate it. We have loads of Linux stuff in the DT that > > > means nothing to U-Boot. Many of the bindings chosen by Linux are > > > wildly inefficient for U-Boot to implement. > > > > > > We don't need to strip anything. This is not pollution. It is a > > > binding agreement between projects. > > > > > > > I am not a maintainer, but I doubt they view it that way. In any > > Who? > > > case, the DT produced by u-boot fails to pass the certification on > > U-Boot > > > every single platform that uses nonupstream nodes, so cleaning that up > > is needed. If people care enough and upstream those bindings we can > > preserve them > > Yes I agree, and the bindings that are added need to be upstream in > dt-schema. This applies also to the work that Linaro does. > > I will mention this to Sugosh as well as he is adding a public key.
He is already aware, he is working on a PoC that does exactly what I described. Once we verify devices are starting to pass the SystemReady2.0 certification he will send an RFC
What PoC? You mean bindings?
stripping of bindings that are not upstreamed in the dt-schema
If that is what you want to do, then the binding needs to go upstream before we accept his patches.
This makes no sense whatsoever. Sughosh isn't adding anything new. Having the public portion on the DT file is something you insisted upon years ago and even reverted patches from me that had the key as a Kconfig option (which notably suffered from non of these problems, or the increased complexity we are adding now). What Sughosh is adding is autogenerating that, instead of having to manually stitch the DT.
Yes and it forms part of the machine's DT and needs to have a binding, just like the Binman nodes need a binding. Isn't this the whole point?
Either:
- we upstream the U-Boot bindings, or
- we allow U-Boot to put whatever it wants in there and accept it
The latter is not an option. U-Boot is the only certified systemready bootloader and you are trying to break that.
I much prefer the first option since we can run validation on it, as I'm sure you would prefer. But removing nodes before booting just because we haven't upstreamed things is just going to lead to no one bothering to do it.
If you read up a few mail before you'll figure out no one has to do that manually. We can have an array that applies to all internal bindings and clean them up for all boards.
You've tried to upstream bindings for years and only a handful has been accepted. So delaying the certification process because of internal U-Boot ABI issues isn't really sane for me.
Thanks /Ilias
Regards, Simon

Hi Ilias,
On Wed, 2 Aug 2023 at 08:00, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Hi Simon,
On Wed, 2 Aug 2023 at 16:55, Simon Glass sjg@chromium.org wrote:
Hi Ilias,
On Wed, 2 Aug 2023 at 07:48, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Hi Simon,
On Wed, 2 Aug 2023 at 16:44, Simon Glass sjg@chromium.org wrote:
Hi Ilias,
On Wed, 2 Aug 2023 at 07:43, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
On Wed, 2 Aug 2023 at 16:42, Simon Glass sjg@chromium.org wrote:
Hi Ilias,
On Wed, 2 Aug 2023 at 07:38, Ilias Apalodimas ilias.apalodimas@linaro.org wrote: > > Hi Simon, > > On Wed, 2 Aug 2023 at 16:34, Simon Glass sjg@chromium.org wrote: > > > > Hi Ilias, > > > > On Wed, 2 Aug 2023 at 07:27, Ilias Apalodimas > > ilias.apalodimas@linaro.org wrote: > > > > > > Hi Simon, > > > > > > > > > On Wed, 2 Aug 2023 at 16:09, Simon Glass sjg@chromium.org wrote: > > > > > > > > Hi Ilias, > > > > > > > > On Wed, 2 Aug 2023 at 07:02, Ilias Apalodimas > > > > ilias.apalodimas@linaro.org wrote: > > > > > > > > > > Hi Simon, > > > > > > > > > > On Wed, 2 Aug 2023 at 15:52, Simon Glass sjg@chromium.org wrote: > > > > > > > > > > > > Hi Ilias, > > > > > > > > > > > > On Wed, 2 Aug 2023 at 00:52, Ilias Apalodimas > > > > > > ilias.apalodimas@linaro.org wrote: > > > > > > > > > > > > > > On Tue, 1 Aug 2023 at 19:19, Tom Rini trini@konsulko.com wrote: > > > > > > > > > > > > > > > > On Tue, Aug 01, 2023 at 05:10:08PM +0100, Abdellatif El Khlifi wrote: > > > > > > > > > Hi guys, > > > > > > > > > > > > > > > > > > On Tue, Aug 01, 2023 at 11:00:57AM -0400, Tom Rini wrote: > > > > > > > > > > > > > > > > > ... > > > > > > > > > > > > > > > > > Changelog: > > > > > > > > > > > > > > > > > =============== > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > v17: > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > * show a debug message rather than an error when FF-A is not detected > > > > > > > > > > > > > > > > [snip] > > > > > > > > > > > > > > > > > diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig > > > > > > > > > > > > > > > > > index c5835e6ef6..8fbadb9201 100644 > > > > > > > > > > > > > > > > > --- a/lib/efi_loader/Kconfig > > > > > > > > > > > > > > > > > +++ b/lib/efi_loader/Kconfig > > > > > > > > > > > > > > > > > @@ -55,13 +55,53 @@ 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" > > > > > > > > > > > > > > > > > - depends on OPTEE > > > > > > > > > > > > > > > > > + bool "UEFI variables storage service via the trusted world" > > > > > > > > > > > > > > > > > + depends on OPTEE && ARM_FFA_TRANSPORT > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > You didn't get my changes in here however. If you can do EFI_MM_COMM_TEE > > > > > > > > > > > > > > > > without ARM_FFA_TRANSPORT (as lx2160ardb_tfa_stmm_defconfig does) then > > > > > > > > > > > > > > > > you don't make this option depend on . If FF-A is only > > > > > > > > > > > > > > > > for use here, you make FF-A depend on this, and the FF-A specific > > > > > > > > > > > > > > > > variable depend on ARM_FFA_TRANSPORT. > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > Abdellatif hinted at what's going on here. When I added this Kconfig > > > > > > > > > > > > > > > option to lx2160 FF-A wasn't implemented yet. > > > > > > > > > > > > > > > > > > > > > > > > > > > > The defconfig has existed since May 2020, which is when you added > > > > > > > > > > > > > > EFI_MM_COMM_TEE itself too. So I think it's that no one did the check I > > > > > > > > > > > > > > did until now and saw this series was disabling what was on the other > > > > > > > > > > > > > > platform. > > > > > > > > > > > > > > > > > > > > > > > > > > > > > Since FF-A isn't a new > > > > > > > > > > > > > > > communication mechanism but builds upon the existing SMCs to build an > > > > > > > > > > > > > > > easier API, I asked Abdellatif to hide this complexity. > > > > > > > > > > > > > > > We had two options, either make Kconfig options for either FF-A or the > > > > > > > > > > > > > > > traditional SMCs and remove the dependencies, or piggyback on FF-As > > > > > > > > > > > > > > > discovery mechanism and make the choice at runtime. The latter has a > > > > > > > > > > > > > > > small impact on code size, but imho makes developers' life a lot > > > > > > > > > > > > > > > easier. > > > > > > > > > > > > > > > > > > > > > > > > > > > > I'm not sure how much you can do a run-time option here since you're > > > > > > > > > > > > > > setting a bunch of default values for FF-A to 0 in Kconfig. If we're > > > > > > > > > > > > > > supposed to be able to get them at run time, we shouldn't need a Kconfig > > > > > > > > > > > > > > option at all. I'm also not sure how valid a use case it is where we > > > > > > > > > > > > > > won't know at build time what the rest of the firmware stack supports > > > > > > > > > > > > > > here. > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > That's a fair point. FF-A in theory has APIs to discover memory. > > > > > > > > > > > > > Abdellatif, why do we need the Kconfigs for shared memory on FF-A? > > > > > > > > > > > > > > > > > > > > > > > > The statically carved out MM shared buffer address, size and offset cannot be discovered by FF-A ABIs. > > > > > > > > > > > > The MM communication driver in U-Boot could allocate the buffer and share it with the MM SP but > > > > > > > > > > > > we do not implement that support currently in either U-Boot or UEFI. > > > > > > > > > > > > > > > > > > > > > > Ok, that's a bit unfortunate, but Tom is right. Having the FF-A > > > > > > > > > > > addresses show up is as confusing as having Kconfig options for > > > > > > > > > > > discrete options. The whole point of my suggestion was to make users' > > > > > > > > > > > lives easier. Apologies for the confusion but can you bring back the > > > > > > > > > > > ifdefs? Looking at the patch this should be minimal just use > > > > > > > > > > > ifdef ARM_FFA_TRANSPORT and ifndef ARM_FFA_TRANSPORT. > > > > > > > > > > > > > > > > > > > > > > Tom you prefer that as well? > > > > > > > > > > > > > > > > > > > > Pending an answer to Jens' feedback, yes, going back to #ifdef's is > > > > > > > > > > fine, especially since default values of 0 are nonsense in this case > > > > > > > > > > (and as Heinrich's patch re SYS_MALLOC_LEN shows, dangerous since 0 != > > > > > > > > > > 0x0 once we do string comparisons). > > > > > > > > > > > > > > > > > > > > > > > > > > > > I'd like to give some context why it's important for Corstone-1000 platform > > > > > > > > > that the DT passed to the kernel matches the official kernel DT. > > > > > > > > > > > > > > > > Note that we've set aside the "should this be in DT or not" question. > > > > > > > > > > > > > > > > > There is a SystemReady IR 2.0 test checking the DT. It compares the DT > > > > > > > > > passed by U-Boot with a reference DT (the kernel DT) . The test fails if there > > > > > > > > > is a mismatch. So, if we add a DT node in U-Boot and the node is not upstreamed > > > > > > > > > to the kernel DT, the DT test will fail. > > > > > > > > > > > > > > > > This is overall good and progress. > > > > > > > > > > > > > > > > > To be approved by the kernel DT maintainers, the node should have a use case > > > > > > > > > in the kernel which is not the case. > > > > > > > > > > > > > > > > This is, I believe / hope wrong. It needs to be in the dt-schema > > > > > > > > repository, not strictly "the kernel". For example, bootph-all (etc) > > > > > > > > are in dt-schema and so can be in the upstream kernel but are not used > > > > > > > > in the kernel itself. > > > > > > > > > > > > > > > > > There is a solution for this which is deleting the node we don't want to pass to > > > > > > > > > the kernel using delete-node in the U-Boot DT. > > > > > > > > > > > > > > > > Something like this will likely be needed, in the end, at least for some > > > > > > > > cases. But the goal is that everything gets in to dt-schema. > > > > > > > > > > > > > > We are already working on U-Boot on that. The idea is rather simple. > > > > > > > We will have an array with nodes and node entries. Before we boot up > > > > > > > we'll scan that array, if a node entry exists we will delete that, > > > > > > > otherwise we will just get rid of the entire node. That should be > > > > > > > pretty easy to maintain and extend. U-Boot will then be able to use > > > > > > > it;s internal bidings without polluting the DT we handover to the > > > > > > > kernel. > > > > > > > > > > > > This is not pollution - we have moved past that now and Linux has > > > > > > accepted some U-Boot bindings. This is the DT and if there are things > > > > > > in it that are not related to Linux, it can ignore them. > > > > > > > > > > > > We should add whatever bindings we need to make U-Boot work > > > > > > efficiently and correctly. > > > > > > > > > > > > > > > > The cases that are already accepted make sense. Things like the > > > > > public part of the certificates used to authenticate capsule updates > > > > > or the encoding of the recent a/b update nodes are not needed in any > > > > > way in an OS. Those don't make sense to upstream and those are > > > > > polluting the DT and need stripping > > > > > > > > It doesn't matter that Linux doesn't *need* it. If it is there it will > > > > have to accommodate it. We have loads of Linux stuff in the DT that > > > > means nothing to U-Boot. Many of the bindings chosen by Linux are > > > > wildly inefficient for U-Boot to implement. > > > > > > > > We don't need to strip anything. This is not pollution. It is a > > > > binding agreement between projects. > > > > > > > > > > I am not a maintainer, but I doubt they view it that way. In any > > > > Who? > > > > > case, the DT produced by u-boot fails to pass the certification on > > > > U-Boot > > > > > every single platform that uses nonupstream nodes, so cleaning that up > > > is needed. If people care enough and upstream those bindings we can > > > preserve them > > > > Yes I agree, and the bindings that are added need to be upstream in > > dt-schema. This applies also to the work that Linaro does. > > > > I will mention this to Sugosh as well as he is adding a public key. > > He is already aware, he is working on a PoC that does exactly what I > described. Once we verify devices are starting to pass the > SystemReady2.0 certification he will send an RFC
What PoC? You mean bindings?
stripping of bindings that are not upstreamed in the dt-schema
If that is what you want to do, then the binding needs to go upstream before we accept his patches.
This makes no sense whatsoever. Sughosh isn't adding anything new. Having the public portion on the DT file is something you insisted upon years ago and even reverted patches from me that had the key as a Kconfig option (which notably suffered from non of these problems, or the increased complexity we are adding now). What Sughosh is adding is autogenerating that, instead of having to manually stitch the DT.
Yes and it forms part of the machine's DT and needs to have a binding, just like the Binman nodes need a binding. Isn't this the whole point?
Either:
- we upstream the U-Boot bindings, or
- we allow U-Boot to put whatever it wants in there and accept it
The latter is not an option. U-Boot is the only certified systemready bootloader and you are trying to break that.
I am missing something here, to say the least.
I much prefer the first option since we can run validation on it, as I'm sure you would prefer. But removing nodes before booting just because we haven't upstreamed things is just going to lead to no one bothering to do it.
If you read up a few mail before you'll figure out no one has to do that manually. We can have an array that applies to all internal bindings and clean them up for all boards.
You've tried to upstream bindings for years and only a handful has been accepted.
Can you find something here? I remember perhaps sending one (uart clock-frequency?), but it was mostly the discussions with a kernel maintainer that made me see it was a waste of time. Of course my memory is not that great. It is certainly true that I gave up a long time ago and only restarted due to a change of guard.
So delaying the certification process because of internal U-Boot ABI issues isn't really sane for me.
I am not suggesting that...we just need to upstream our bindings. That is all. If there is no path to upstream, then we either have a bad binding or we have a process / people problem. Either way, we should fix it and carry on.
Regards, Simon

On Wed, Aug 02, 2023 at 07:55:18AM -0600, Simon Glass wrote:
Hi Ilias,
On Wed, 2 Aug 2023 at 07:48, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Hi Simon,
On Wed, 2 Aug 2023 at 16:44, Simon Glass sjg@chromium.org wrote:
Hi Ilias,
On Wed, 2 Aug 2023 at 07:43, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
On Wed, 2 Aug 2023 at 16:42, Simon Glass sjg@chromium.org wrote:
Hi Ilias,
On Wed, 2 Aug 2023 at 07:38, Ilias Apalodimas ilias.apalodimas@linaro.org wrote:
Hi Simon,
On Wed, 2 Aug 2023 at 16:34, Simon Glass sjg@chromium.org wrote: > > Hi Ilias, > > On Wed, 2 Aug 2023 at 07:27, Ilias Apalodimas > ilias.apalodimas@linaro.org wrote: > > > > Hi Simon, > > > > > > On Wed, 2 Aug 2023 at 16:09, Simon Glass sjg@chromium.org wrote: > > > > > > Hi Ilias, > > > > > > On Wed, 2 Aug 2023 at 07:02, Ilias Apalodimas > > > ilias.apalodimas@linaro.org wrote: > > > > > > > > Hi Simon, > > > > > > > > On Wed, 2 Aug 2023 at 15:52, Simon Glass sjg@chromium.org wrote: > > > > > > > > > > Hi Ilias, > > > > > > > > > > On Wed, 2 Aug 2023 at 00:52, Ilias Apalodimas > > > > > ilias.apalodimas@linaro.org wrote: > > > > > > > > > > > > On Tue, 1 Aug 2023 at 19:19, Tom Rini trini@konsulko.com wrote: > > > > > > > > > > > > > > On Tue, Aug 01, 2023 at 05:10:08PM +0100, Abdellatif El Khlifi wrote: > > > > > > > > Hi guys, > > > > > > > > > > > > > > > > On Tue, Aug 01, 2023 at 11:00:57AM -0400, Tom Rini wrote: > > > > > > > > > > > > > > > > ... > > > > > > > > > > > > > > > > Changelog: > > > > > > > > > > > > > > > > =============== > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > v17: > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > * show a debug message rather than an error when FF-A is not detected > > > > > > > > > > > > > > > [snip] > > > > > > > > > > > > > > > > diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig > > > > > > > > > > > > > > > > index c5835e6ef6..8fbadb9201 100644 > > > > > > > > > > > > > > > > --- a/lib/efi_loader/Kconfig > > > > > > > > > > > > > > > > +++ b/lib/efi_loader/Kconfig > > > > > > > > > > > > > > > > @@ -55,13 +55,53 @@ 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" > > > > > > > > > > > > > > > > - depends on OPTEE > > > > > > > > > > > > > > > > + bool "UEFI variables storage service via the trusted world" > > > > > > > > > > > > > > > > + depends on OPTEE && ARM_FFA_TRANSPORT > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > You didn't get my changes in here however. If you can do EFI_MM_COMM_TEE > > > > > > > > > > > > > > > without ARM_FFA_TRANSPORT (as lx2160ardb_tfa_stmm_defconfig does) then > > > > > > > > > > > > > > > you don't make this option depend on . If FF-A is only > > > > > > > > > > > > > > > for use here, you make FF-A depend on this, and the FF-A specific > > > > > > > > > > > > > > > variable depend on ARM_FFA_TRANSPORT. > > > > > > > > > > > > > > > > > > > > > > > > > > > > Abdellatif hinted at what's going on here. When I added this Kconfig > > > > > > > > > > > > > > option to lx2160 FF-A wasn't implemented yet. > > > > > > > > > > > > > > > > > > > > > > > > > > The defconfig has existed since May 2020, which is when you added > > > > > > > > > > > > > EFI_MM_COMM_TEE itself too. So I think it's that no one did the check I > > > > > > > > > > > > > did until now and saw this series was disabling what was on the other > > > > > > > > > > > > > platform. > > > > > > > > > > > > > > > > > > > > > > > > > > > Since FF-A isn't a new > > > > > > > > > > > > > > communication mechanism but builds upon the existing SMCs to build an > > > > > > > > > > > > > > easier API, I asked Abdellatif to hide this complexity. > > > > > > > > > > > > > > We had two options, either make Kconfig options for either FF-A or the > > > > > > > > > > > > > > traditional SMCs and remove the dependencies, or piggyback on FF-As > > > > > > > > > > > > > > discovery mechanism and make the choice at runtime. The latter has a > > > > > > > > > > > > > > small impact on code size, but imho makes developers' life a lot > > > > > > > > > > > > > > easier. > > > > > > > > > > > > > > > > > > > > > > > > > > I'm not sure how much you can do a run-time option here since you're > > > > > > > > > > > > > setting a bunch of default values for FF-A to 0 in Kconfig. If we're > > > > > > > > > > > > > supposed to be able to get them at run time, we shouldn't need a Kconfig > > > > > > > > > > > > > option at all. I'm also not sure how valid a use case it is where we > > > > > > > > > > > > > won't know at build time what the rest of the firmware stack supports > > > > > > > > > > > > > here. > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > That's a fair point. FF-A in theory has APIs to discover memory. > > > > > > > > > > > > Abdellatif, why do we need the Kconfigs for shared memory on FF-A? > > > > > > > > > > > > > > > > > > > > > > The statically carved out MM shared buffer address, size and offset cannot be discovered by FF-A ABIs. > > > > > > > > > > > The MM communication driver in U-Boot could allocate the buffer and share it with the MM SP but > > > > > > > > > > > we do not implement that support currently in either U-Boot or UEFI. > > > > > > > > > > > > > > > > > > > > Ok, that's a bit unfortunate, but Tom is right. Having the FF-A > > > > > > > > > > addresses show up is as confusing as having Kconfig options for > > > > > > > > > > discrete options. The whole point of my suggestion was to make users' > > > > > > > > > > lives easier. Apologies for the confusion but can you bring back the > > > > > > > > > > ifdefs? Looking at the patch this should be minimal just use > > > > > > > > > > ifdef ARM_FFA_TRANSPORT and ifndef ARM_FFA_TRANSPORT. > > > > > > > > > > > > > > > > > > > > Tom you prefer that as well? > > > > > > > > > > > > > > > > > > Pending an answer to Jens' feedback, yes, going back to #ifdef's is > > > > > > > > > fine, especially since default values of 0 are nonsense in this case > > > > > > > > > (and as Heinrich's patch re SYS_MALLOC_LEN shows, dangerous since 0 != > > > > > > > > > 0x0 once we do string comparisons). > > > > > > > > > > > > > > > > > > > > > > > > > I'd like to give some context why it's important for Corstone-1000 platform > > > > > > > > that the DT passed to the kernel matches the official kernel DT. > > > > > > > > > > > > > > Note that we've set aside the "should this be in DT or not" question. > > > > > > > > > > > > > > > There is a SystemReady IR 2.0 test checking the DT. It compares the DT > > > > > > > > passed by U-Boot with a reference DT (the kernel DT) . The test fails if there > > > > > > > > is a mismatch. So, if we add a DT node in U-Boot and the node is not upstreamed > > > > > > > > to the kernel DT, the DT test will fail. > > > > > > > > > > > > > > This is overall good and progress. > > > > > > > > > > > > > > > To be approved by the kernel DT maintainers, the node should have a use case > > > > > > > > in the kernel which is not the case. > > > > > > > > > > > > > > This is, I believe / hope wrong. It needs to be in the dt-schema > > > > > > > repository, not strictly "the kernel". For example, bootph-all (etc) > > > > > > > are in dt-schema and so can be in the upstream kernel but are not used > > > > > > > in the kernel itself. > > > > > > > > > > > > > > > There is a solution for this which is deleting the node we don't want to pass to > > > > > > > > the kernel using delete-node in the U-Boot DT. > > > > > > > > > > > > > > Something like this will likely be needed, in the end, at least for some > > > > > > > cases. But the goal is that everything gets in to dt-schema. > > > > > > > > > > > > We are already working on U-Boot on that. The idea is rather simple. > > > > > > We will have an array with nodes and node entries. Before we boot up > > > > > > we'll scan that array, if a node entry exists we will delete that, > > > > > > otherwise we will just get rid of the entire node. That should be > > > > > > pretty easy to maintain and extend. U-Boot will then be able to use > > > > > > it;s internal bidings without polluting the DT we handover to the > > > > > > kernel. > > > > > > > > > > This is not pollution - we have moved past that now and Linux has > > > > > accepted some U-Boot bindings. This is the DT and if there are things > > > > > in it that are not related to Linux, it can ignore them. > > > > > > > > > > We should add whatever bindings we need to make U-Boot work > > > > > efficiently and correctly. > > > > > > > > > > > > > The cases that are already accepted make sense. Things like the > > > > public part of the certificates used to authenticate capsule updates > > > > or the encoding of the recent a/b update nodes are not needed in any > > > > way in an OS. Those don't make sense to upstream and those are > > > > polluting the DT and need stripping > > > > > > It doesn't matter that Linux doesn't *need* it. If it is there it will > > > have to accommodate it. We have loads of Linux stuff in the DT that > > > means nothing to U-Boot. Many of the bindings chosen by Linux are > > > wildly inefficient for U-Boot to implement. > > > > > > We don't need to strip anything. This is not pollution. It is a > > > binding agreement between projects. > > > > > > > I am not a maintainer, but I doubt they view it that way. In any > > Who? > > > case, the DT produced by u-boot fails to pass the certification on > > U-Boot > > > every single platform that uses nonupstream nodes, so cleaning that up > > is needed. If people care enough and upstream those bindings we can > > preserve them > > Yes I agree, and the bindings that are added need to be upstream in > dt-schema. This applies also to the work that Linaro does. > > I will mention this to Sugosh as well as he is adding a public key.
He is already aware, he is working on a PoC that does exactly what I described. Once we verify devices are starting to pass the SystemReady2.0 certification he will send an RFC
What PoC? You mean bindings?
stripping of bindings that are not upstreamed in the dt-schema
If that is what you want to do, then the binding needs to go upstream before we accept his patches.
This makes no sense whatsoever. Sughosh isn't adding anything new. Having the public portion on the DT file is something you insisted upon years ago and even reverted patches from me that had the key as a Kconfig option (which notably suffered from non of these problems, or the increased complexity we are adding now). What Sughosh is adding is autogenerating that, instead of having to manually stitch the DT.
Yes and it forms part of the machine's DT and needs to have a binding, just like the Binman nodes need a binding. Isn't this the whole point?
Either:
- we upstream the U-Boot bindings, or
- we allow U-Boot to put whatever it wants in there and accept it
I much prefer the first option since we can run validation on it, as I'm sure you would prefer. But removing nodes before booting just because we haven't upstreamed things is just going to lead to no one bothering to do it.
I think we're talking past each other now. All of the following are true: - It has historically been hard to get bindings accepted that are not used in the linux kernel. - It is not only possible but reasonable today to get bindings accepted that are not used in the linux kernel. - There are many bindings outstanding today that need to be adjusted, upstreamed and accepted to dt-schema. - For SystemReady certification, U-Boot _is_ an important part of the ecosystem. - U-Boot as a project sees this as important (and I personally see this as important). - SystemReady is going to be more strict about what can / can't be in the device tree. This is good because it will prioritize accepted bindings rather than "garbage" bindings. - Today, one can just chain up "fdt" commands to make this happen. - In the future, we'll have a cleaner way to do this. This is good because it means we won't hide these behind environment strings and so forth to "sneak" in, but instead be pragmatic about it. Heck, we should make one of the conditions for being listed there be a link to discussion on upstreaming OR why it's inappropriate. This will make sure it doesn't stifle the upstreaming of bindings. - Using non-upstreamed bindings in the device tree passed to the kernel is at least as big a deal, if not bigger a deal, for the vendor kernel tree than it is for U-Boot.

Adding support for Arm FF-A v1.0 (Arm Firmware Framework for Armv8-A) [A].
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.
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.
=> dm tree
Class Index Probed Driver Name ----------------------------------------------------------- ... firmware 0 [ + ] psci |-- psci ffa 0 [ ] arm_ffa | `-- arm_ffa ...
Clients are able to probe then use the FF-A bus by calling the DM class searching APIs (e.g: uclass_first_device).
This implementation of the specification provides support for Aarch64.
The FF-A driver uses the SMC ABIs defined by the FF-A specification to:
- Discover the presence of secure partitions (SPs) of interest - Access an SP's service through communication protocols (e.g: EFI MM communication protocol)
The FF-A support provides the following features:
- Being generic by design and can be used by any Arm 64-bit platform - FF-A support can be compiled and used without EFI - Support for SMCCCv1.2 x0-x17 registers - Support for SMC32 calling convention - Support for 32-bit and 64-bit FF-A direct messaging - Support for FF-A MM communication (compatible with EFI boot time) - Enabling FF-A and MM communication in Corstone1000 platform as a use case - 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. - Sandbox FF-A test cases. - A new command called armffa is provided as an example of how to access the FF-A bus
For more details about the FF-A support please refer to [B] and refer to [C] for how to use the armffa command.
Please find at [D] an example of the expected boot logs when enabling FF-A support for a platform. In this example the platform is Corstone1000. But it can be any Arm 64-bit platform.
Changelog of changes: ===========================
v18:
Ilias, Tom:
* drop the use of configs for the shared MM buffer, put back #ifdefs instead * add test information to the MM comms commit message
v17: [17]
Ilias:
* show a debug message rather than an error when FF-A is not detected
Tom:
* use savedefconfig to generate corstone1000_defconfig with FF-A MM comms enabled
v16: [16]
Tom:
* lib/efi_loader/Kconfig: rather than automatically selecting OPTEE and ARM_FFA_TRANSPORT configs by EFI_MM_COMM_TEE, set them as dependencies (Otherwise FF-A will be automatically enabled for boards that don't need it).
* configs/corstone1000_defconfig: enable MM communication by setting the configs: ARM_FFA_TRANSPORT, OPTEE, TEE
v15: [15]
Simon:
* drop commit "log: select physical address formatting in a generic way", this will be sent as a follow-up commit independently from this patchset * armffa.c : integrate PHYS_ADDR_LN * replace FFA_SHARED_MM_BUFFER_* defines with configs
v14: [14]
Simon:
* add to log.h a generic physical address formatting
Ilias:
* armffa command: in do_ffa_ping() reject the SP ID if it's 0 * MM comms: drop truncating var_payload->size when using FF-A * MM comms: map the MM SP return codes to errnos * address nits
v13: [13]
Ilias: * remove FF-A and Optee ifdefs in efi_variable_tee.c * doc minor change: specify in the readme that the user should call ffa_rxtx_unmap() driver operation to unmap the RX/TX buffers on demand.
v12: [12]
* remove the global variable (dscvry_info), use uc_priv instead * replace dscvry_info.invoke_ffa_fn() with a weak invoke_ffa_fn (user drivers can override it) * improve FFA_PARTITION_INFO_GET implementation (clients no longer need to calloc a buffer) * remove reparenting by making the sandbox emulator parent of the FF-A device in the DT * improve argument checks for the armffa command * address nits
v11: [11]
* move ffa_try_discovery() from the uclass to the Arm FF-A driver * rename ffa_try_discovery() to arm_ffa_discover() * add arm_ prefix to the Arm FF-A driver functions * use U_BOOT_CMD_WITH_SUBCMDS for armffa command * store the sandbox emulator pointer in the FF-A device uc_priv (struct ffa_priv) * set the emulator as parent of the sandbox FF-A device * rename select_ffa_mm_comms() to select_mm_comms() * improve the logic of MM transport selection in mm_communicate() * use ut_asserteq_mem() in uuid_str_to_le_bin test case * address nits
v10: [10]
* provide the FF-A driver operations through the Uclass (arm-ffa-uclass.c) * move the generic FF-A methods to the Uclass * keep Arm specific methods in the Arm driver (arm-ffa.c renamed from core.c) * split the FF-A sandbox support into an emulator (ffa-emul-uclass.c) and a driver (sandbox_ffa.c) * use the FF-A driver Uclass operations by clients (armffa command, tests, MM comms) * use uclass_first_device to search and probe the FF-A device (whether it is on Arm or on sandbox) * address nits
v9: [9]
* integrate the FF-A bus discovery in the DM and use ARM_SMCCC_FEATURES for binding * align FF-A sandbox driver with FF-A discovery through DM * use DM class APIs to probe and interact with the FF-A bus (in FF-A MM comms, armffa command, sandbox tests) * add documentation for the armffa command: doc/usage/cmd/armffa.rst * introduce testcase for uuid_str_to_le_bin
v8: [8]
* pass the FF-A bus device to the bus operations * isolate the compilation choices between FF-A and OP-TEE * drop OP-TEE configs from Corstone-1000 defconfig * make ffa_get_partitions_info() second argument to be an SP count in both modes
v7: [7]
* add support for 32-bit direct messaging (now we have 32-bit and 64-bit support) * set the MM door bell event to use 64-bit direct messaging * issue a compile time error when one of these macros are not found : FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_OFFSET, FFA_SHARED_MM_BUFFER_ADDR * make uuid_str_to_le_bin() implementation similar to uuid_str_to_bin() by using same APIs
v6: [6]
* remove clearing x0-x17 registers after SMC calls * drop use of EFI runtime support for FF-A (We decided with Linaro to add this later) * drop discovery from initcalls (discovery will be on demand by FF-A users) * add FF-A runtime discovery at MM communication level * update the documentation and move it to doc/arch/arm64.ffa.rst
v5: [5]
* move changelogs in each commit to the changes section
v4: [4]
* add FF-A support README (doc/README.ffa.drv) * improving error handling by mapping the FF-A errors to standard errors and logs * replacing panics with an error log * align sandbox driver and tests with the new FF-A driver interfaces and new way of error handling * use the new FF-A driver interfaces for MM communication * discover MM partitions at runtime * copy FF-A driver private data to EFI runtime section at ExitBootServices() * moving the FF-A driver work to drivers/firmware/arm-ffa * improving features discovery in FFA_FEATURES * add remove/unbind functions to the FF-A core device * improve how the driver behaves when bus discovery is done more than once * move clearing x0-x17 registers code into a new macro like done in the linux kernel * enable EFI MM communication for the Corstone1000 platform
v3: [3]
* port x0-x17 registers support from linux kernel as defined by SMCCCv1.2 * align the interfaces of the u-boot FF-A driver with those in the linux FF-A driver * remove the FF-A helper layer * make the u-boot FF-A driver independent from EFI * provide an optional config that enables copying the driver data to EFI runtime section at ExitBootServices service * use 64-bit version of FFA_RXTX_MAP, FFA_MSG_SEND_DIRECT_{REQ, RESP} * update armffa command with the new driver interfaces
v2 [2]:
* make FF-A bus discoverable using device_{bind, probe} APIs * remove device tree support
v1 [1]:
* introduce FF-A bus driver with device tree support * introduce armffa command * introduce FF-A Sandbox driver * add FF-A Sandbox test cases * introduce FF-A MM communication
Cheers, Abdellatif
List of previous patches:
[1]: https://lore.kernel.org/all/20220329151659.16894-1-abdellatif.elkhlifi@arm.c... [2]: https://lore.kernel.org/all/20220415122803.16666-1-abdellatif.elkhlifi@arm.c... [3]: https://lore.kernel.org/all/20220801172053.20163-1-abdellatif.elkhlifi@arm.c... [4]: https://lore.kernel.org/all/20220926101723.9965-1-abdellatif.elkhlifi@arm.co... [5]: https://lore.kernel.org/all/20220926140827.15125-1-abdellatif.elkhlifi@arm.c... [6]: https://lore.kernel.org/all/20221013103857.614-1-abdellatif.elkhlifi@arm.com... [7]: https://lore.kernel.org/all/20221107192055.21669-1-abdellatif.elkhlifi@arm.c... [8]: https://lore.kernel.org/all/20221122131751.22747-1-abdellatif.elkhlifi@arm.c... [9]: https://lore.kernel.org/all/20230310141016.137986-1-abdellatif.elkhlifi@arm.... [10]: https://lore.kernel.org/all/20230328161157.219375-1-abdellatif.elkhlifi@arm.... [11]: https://lore.kernel.org/all/20230412094245.44674-1-abdellatif.elkhlifi@arm.c... [12]: https://lore.kernel.org/all/20230512121044.111574-1-abdellatif.elkhlifi@arm.... [13]: https://lore.kernel.org/all/20230616152817.319869-1-abdellatif.elkhlifi@arm.... [14]: https://lore.kernel.org/all/20230707144410.228472-1-abdellatif.elkhlifi@arm.... [15]: https://lore.kernel.org/all/20230713132847.176000-1-abdellatif.elkhlifi@arm.... [16]: https://lore.kernel.org/all/20230726094503.100497-1-abdellatif.elkhlifi@arm.... [17]: https://lore.kernel.org/all/20230727160712.81477-1-abdellatif.elkhlifi@arm.c...
More details:
[A]: https://developer.arm.com/documentation/den0077/latest/ [B]: doc/arch/arm64.ffa.rst [C]: doc/usage/cmd/armffa.rst [D]: example of boot logs when enabling FF-A
``` 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 ```
Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Rob Herring robh@kernel.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Achin Gupta achin.gupta@arm.com Cc: Drew Reed Drew.Reed@arm.com Cc: Xueliang Zhong Xueliang.Zhong@arm.com
Abdellatif El Khlifi (9): arm64: smccc: add support for SMCCCv1.2 x0-x17 registers lib: uuid: introduce uuid_str_to_le_bin function lib: uuid: introduce testcase for uuid_str_to_le_bin arm_ffa: introduce Arm FF-A support arm_ffa: introduce sandbox FF-A support arm_ffa: introduce sandbox test cases for UCLASS_FFA arm_ffa: introduce armffa command arm_ffa: efi: introduce FF-A MM communication arm_ffa: efi: corstone1000: enable MM communication
MAINTAINERS | 18 + arch/arm/cpu/armv8/smccc-call.S | 57 +- arch/arm/lib/asm-offsets.c | 16 + 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 ++ cmd/Kconfig | 10 + cmd/Makefile | 1 + cmd/armffa.c | 202 ++++ configs/corstone1000_defconfig | 10 +- configs/sandbox64_defconfig | 1 + configs/sandbox_defconfig | 1 + doc/arch/arm64.ffa.rst | 261 ++++ doc/arch/index.rst | 1 + doc/arch/sandbox/sandbox.rst | 1 + doc/usage/cmd/armffa.rst | 94 ++ doc/usage/index.rst | 1 + drivers/Makefile | 1 + drivers/firmware/Kconfig | 1 + drivers/firmware/arm-ffa/Kconfig | 42 + drivers/firmware/arm-ffa/Makefile | 16 + drivers/firmware/arm-ffa/arm-ffa-uclass.c | 1065 +++++++++++++++++ drivers/firmware/arm-ffa/arm-ffa.c | 104 ++ drivers/firmware/arm-ffa/ffa-emul-uclass.c | 720 +++++++++++ drivers/firmware/arm-ffa/sandbox_ffa.c | 110 ++ include/arm_ffa.h | 213 ++++ include/arm_ffa_priv.h | 246 ++++ include/dm/uclass-id.h | 7 + include/linux/arm-smccc.h | 45 + include/mm_communication.h | 17 + include/uuid.h | 15 + lib/efi_loader/Kconfig | 42 +- lib/efi_loader/efi_variable_tee.c | 282 ++++- lib/uuid.c | 48 + test/cmd/Makefile | 2 + test/cmd/armffa.c | 33 + test/dm/Makefile | 3 +- test/dm/ffa.c | 261 ++++ test/lib/Makefile | 1 + test/lib/uuid.c | 41 + 41 files changed, 4186 insertions(+), 13 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 cmd/armffa.c create mode 100644 doc/arch/arm64.ffa.rst create mode 100644 doc/usage/cmd/armffa.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/ffa-emul-uclass.c create mode 100644 drivers/firmware/arm-ffa/sandbox_ffa.c create mode 100644 include/arm_ffa.h create mode 100644 include/arm_ffa_priv.h create mode 100644 test/cmd/armffa.c create mode 100644 test/dm/ffa.c create mode 100644 test/lib/uuid.c

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 abdellatif.elkhlifi@arm.com Reviewed-by: Ilias Apalodimas ilias.apalodimas@linaro.org Reviewed-by: Jens Wiklander jens.wiklander@linaro.org Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com
---
Changelog: ===============
v9:
* update the copyright string
v7:
* improve indentation of ARM_SMCCC_1_2_REGS_Xn_OFFS
v4:
* rename the commit title and improve description new commit title: the current
v3:
* port x0-x17 registers support from linux kernel as defined by SMCCCv1.2 commit title: arm64: smccc: add Xn registers support used by SMC calls
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 dc92b28777..93f66d3366 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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com +*/ #include <linux/linkage.h> #include <linux/arm-smccc.h> #include <generated/asm-offsets.h> @@ -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 6de0ce9152..181a8ac4c2 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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> @@ -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 e1d09884a1..f44e9e8f93 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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */ #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

convert UUID string to little endian binary data
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v9:
* add a full function prototype description in uuid.h
v8:
* use simple_strtoull() in uuid_str_to_le_bin() to support 32-bit platforms
v7:
* rename be_uuid_str_to_le_bin() to uuid_str_to_le_bin() * make uuid_str_to_le_bin() implementation similar to uuid_str_to_bin() by using same APIs
v4:
* rename ffa_uuid_str_to_bin to be_uuid_str_to_le_bin and put in a standalone commit (the current)
v3:
* introduce ffa_uuid_str_to_bin (provided by arm_ffa: introduce Arm FF-A low-level driver)
include/uuid.h | 15 +++++++++++++++ lib/uuid.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+)
diff --git a/include/uuid.h b/include/uuid.h index 4a4883d3b5..89b93e642b 100644 --- a/include/uuid.h +++ b/include/uuid.h @@ -2,6 +2,10 @@ /* * Copyright (C) 2014 Samsung Electronics * Przemyslaw Marczak p.marczak@samsung.com + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */ #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 96e1af3c8b..45f325d964 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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> @@ -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. *

provide a test case
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com
---
Changelog: ===============
v16:
* MAINTAINERS: place the UUID part in an alphabetical order
v11:
* use ut_asserteq_mem()
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 d724b64673..4324965d26 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1555,6 +1555,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 abdellatif.elkhlifi@arm.com +S: Maintained +F: test/lib/uuid.c + VIDEO M: Anatolij Gustschin agust@denx.de S: Maintained diff --git a/test/lib/Makefile b/test/lib/Makefile index e0bd9e04e8..e75a263e6a 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 0000000000..e24331a136 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <uuid.h> +#include <test/lib.h> +#include <test/test.h> +#include <test/ut.h> + +/* 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);

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 abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Reviewed-by: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Tom Rini trini@konsulko.com Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
---
Changelog: ===============
v13:
* doc minor change: specify in the readme that the user should call ffa_rxtx_unmap() driver operation to unmap the RX/TX buffers on demand.
v12:
* remove dscvry_info * replace dscvry_info.invoke_ffa_fn() with a weak invoke_ffa_fn (user drivers can override it) * improve FFA_PARTITION_INFO_GET implementation (clients no longer need to calloc a buffer) * address nits
v11:
* move ffa_try_discovery() from the uclass to the Arm FF-A driver * rename ffa_try_discovery() to arm_ffa_discover() * pass dev as an argument of arm_ffa_discover() * add arm_ prefix to the Arm FF-A driver functions * add emul field in struct ffa_discovery_info * address nits
v10:
* provide the driver operations through the Uclass * move the generic FF-A methods to the Uclass * keep Arm specific methods in the Arm driver (arm-ffa.c) * rename core.c to arm-ffa.c * address nits
v9:
* integrate the FF-A bus discovery in the DM and use ARM_SMCCC_FEATURES for binding
v8:
* make ffa_get_partitions_info() second argument to be an SP count in both modes * update ffa_bus_prvdata_get() to return a pointer rather than a pointer address * remove packing from ffa_partition_info and ffa_send_direct_data structures * pass the FF-A bus device to the bus operations
v7:
* add support for 32-bit direct messaging * rename be_uuid_str_to_le_bin() to uuid_str_to_le_bin() * improve the declaration of error handling mapping * stating in doc/arch/arm64.ffa.rst that EFI runtime is not supported
v6:
* drop use of EFI runtime support (We decided with Linaro to add this later) * drop discovery from initcalls (discovery will be on demand by FF-A users) * set the alignment of the RX/TX buffers to the larger translation granule size * move FF-A RX/TX buffers unmapping at ExitBootServices() to a separate commit * update the documentation and move it to doc/arch/arm64.ffa.rst
v4:
* add doc/README.ffa.drv * moving the FF-A driver work to drivers/firmware/arm-ffa * use less #ifdefs in lib/efi_loader/efi_boottime.c and replace #if defined by #if CONFIG_IS_ENABLED * improving error handling by mapping the FF-A errors to standard errors and logs * replacing panics with an error log and returning an error code * improving features discovery in FFA_FEATURES by introducing rxtx_min_pages private data field * add ffa_remove and ffa_unbind functions * improve how the driver behaves when bus discovery is done more than once
v3:
* align the interfaces of the U-Boot FF-A driver with those in the linux FF-A driver * remove the FF-A helper layer * make the U-Boot FF-A driver independent from EFI * provide an optional config that enables copying the driver data to EFI runtime section at ExitBootServices service * use 64-bit version of FFA_RXTX_MAP, FFA_MSG_SEND_DIRECT_{REQ, RESP}
v2:
* make FF-A bus discoverable using device_{bind, probe} APIs * remove device tree support
v1:
* introduce FF-A bus driver with device tree support
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 4324965d26..4fd5768de0 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 abdellatif.elkhlifi@arm.com +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 sbabic@denx.de M: Fabio Estevam festevam@gmail.com diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst new file mode 100644 index 0000000000..df18948774 --- /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 abdellatif.elkhlifi@arm.com + +.. _`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 b8da4b8c8e..2f916f4026 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 78dcf62f76..5044f45253 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 eae1c8ddc9..8789b1ea14 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 0000000000..9200c8028b --- /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 0000000000..11b1766285 --- /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 open-source-office@arm.com +# +# Authors: +# Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + +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 0000000000..ffa9d81fa7 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ +#include <common.h> +#include <arm_ffa.h> +#include <arm_ffa_priv.h> +#include <dm.h> +#include <log.h> +#include <malloc.h> +#include <string.h> +#include <uuid.h> +#include <asm/global_data.h> +#include <dm/device-internal.h> +#include <dm/devres.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> + +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 0000000000..68df75bd9e --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <arm_ffa.h> +#include <arm_ffa_priv.h> +#include <dm.h> +#include <log.h> +#include <asm/global_data.h> +#include <dm/device-internal.h> +#include <linux/errno.h> + +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 0000000000..4338f9c9b1 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#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 0000000000..db9b1be995 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __ARM_FFA_H +#define __ARM_FFA_H + +#include <linux/printk.h> + +/* + * 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 0000000000..d564c33c64 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __ARM_FFA_PRV_H +#define __ARM_FFA_PRV_H + +#include <mapmem.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> + +/* 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 <linux/arm-smccc.h> +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 307ad6931c..3c6af2e3d2 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -4,6 +4,11 @@ * * (C) Copyright 2012 * Pavel Herrmann morpheus.ibis@gmail.com + * + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#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 */

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 abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
---
Changelog: ===============
v12:
* remove reparenting by making the emulator parent of the FF-A device in the DT * add invoke_ffa_fn() * address nits
v11:
* rename ffa_try_discovery() to sandbox_ffa_discover() * rename sandbox_ffa_query_core_state() to sandbox_query_ffa_emul_state() * store the sandbox emulator pointer in the FF-A device uc_priv (struct ffa_priv) * set the emulator as parent of the sandbox FF-A device
v10:
* split the FF-A sandbox support into an emulator and a driver * read FFA_VERSION and FFA_PARTITION_INFO_GET state using sandbox_ffa_query_core_state() * drop CONFIG_SANDBOX_FFA config * address nits
v9: align FF-A sandbox driver with FF-A discovery through DM
v8: update ffa_bus_prvdata_get() to return a pointer rather than a pointer address
v7: state that sandbox driver supports only 64-bit direct messaging
v4: align sandbox driver with the new FF-A driver interfaces and new way of error handling
v1: introduce the sandbox driver
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 4fd5768de0..63cf37290c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -269,10 +269,11 @@ F: configs/cortina_presidio-asic-pnand_defconfig ARM FF-A M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com 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 sbabic@denx.de diff --git a/arch/sandbox/dts/sandbox.dtsi b/arch/sandbox/dts/sandbox.dtsi index 30a305c4d2..94a08814b8 100644 --- a/arch/sandbox/dts/sandbox.dtsi +++ b/arch/sandbox/dts/sandbox.dtsi @@ -445,6 +445,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 ff9f9222e6..96b5404991 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -1820,6 +1820,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 0000000000..be2790f496 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __SANDBOX_ARM_FFA_H +#define __SANDBOX_ARM_FFA_H + +#include <arm_ffa.h> + +/* + * 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 0000000000..b0881822d7 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __SANDBOX_ARM_FFA_PRV_H +#define __SANDBOX_ARM_FFA_PRV_H + +#include <arm_ffa_priv.h> + +/* 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 98b3e0cda4..72ea3d21ab 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 1ec44d5b33..8269bec879 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -344,3 +344,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 df18948774..792898321a 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 77ca6bc4cc..a3631de749 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 9200c8028b..fc668c109d 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 11b1766285..318123a7f4 100644 --- a/drivers/firmware/arm-ffa/Makefile +++ b/drivers/firmware/arm-ffa/Makefile @@ -5,4 +5,12 @@ # Authors: # Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
-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 0000000000..5562bbaac3 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ +#include <common.h> +#include <dm.h> +#include <mapmem.h> +#include <string.h> +#include <asm/global_data.h> +#include <asm/sandbox_arm_ffa.h> +#include <asm/sandbox_arm_ffa_priv.h> +#include <dm/device-internal.h> +#include <dm/lists.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> + +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 4338f9c9b1..0000000000 --- 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 open-source-office@arm.com - * - * Authors: - * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com - */ - -#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 0000000000..ef9491ccea --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ +#include <common.h> +#include <arm_ffa.h> +#include <dm.h> +#include <log.h> +#include <asm/global_data.h> +#include <asm/sandbox_arm_ffa_priv.h> +#include <dm/device-internal.h> +#include <linux/errno.h> + +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 3c6af2e3d2..0432c95c9e 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 */

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 abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
---
Changelog: ===============
v12:
* remove use of dscvry_info * drop use of calloc when querying SPs * address nits
v11:
* drop unmapping test (taken care of by the DM when removing the device) * address nits
v10:
* use the FF-A driver Uclass operations * use uclass_first_device() * replace CONFIG_SANDBOX_FFA with CONFIG_ARM_FFA_TRANSPORT * address nits
v9: align FF-A sandbox tests with FF-A discovery through DM
v8:
* update partition_info_get() second argument to be an SP count * pass NULL device pointer to the FF-A bus discovery and operations
v7: set the tests to use 64-bit direct messaging
v4: align sandbox tests with the new FF-A driver interfaces and new way of error handling
v1: introduce sandbox tests
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 63cf37290c..b6d7263010 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 sbabic@denx.de diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst index 792898321a..71606373f9 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 3799b1ae8f..7ed00733c1 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 open-source-office@arm.com +# Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
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 0000000000..6912666bb4 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <console.h> +#include <dm.h> +#include <asm/sandbox_arm_ffa.h> +#include <asm/sandbox_arm_ffa_priv.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +/* 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);

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 abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
---
Changelog: ===============
v18:
Simon:
* Combining the commits of the command and the test case
v15:
Simon:
* armffa.c : integrate PHYS_ADDR_LN
v14:
Ilias:
* address nits * in do_ffa_ping() reject the SP ID if it's 0 * use PHYS_ADDR_LN in formatting the physical addresses
v12:
* add subcommands argument checks * usage documentation: update command return codes * remove calloc when querying SPs * address nits
v11:
* use U_BOOT_CMD_WITH_SUBCMDS * address nits
v10:
* use the FF-A driver Uclass operations * use uclass_first_device() * address nits
v9:
* remove manual FF-A discovery and use DM * use DM class APIs to probe and interact with the FF-A bus * add doc/usage/cmd/armffa.rst
v8:
* update partition_info_get() second argument to be an SP count * pass NULL device pointer to the FF-A bus discovery and operations
v7:
* adapt do_ffa_dev_list() following the recent update on uclass_first_device/uclass_next_device functions (they return void now) * set armffa command to use 64-bit direct messaging
v4:
* remove pattern data in do_ffa_msg_send_direct_req
v3:
* use the new driver interfaces (partition_info_get, sync_send_receive) in armffa command
v2:
* replace use of ffa_helper_init_device function by ffa_helper_bus_discover
v1:
* introduce armffa command
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 b6d7263010..bd3dba3d95 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -271,9 +271,12 @@ M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com 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 c1941849f9..da8569b417 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -935,6 +935,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 6c37521b4e..7d20a85a46 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 0000000000..7e6eafc03a --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ +#include <common.h> +#include <arm_ffa.h> +#include <command.h> +#include <dm.h> +#include <mapmem.h> +#include <stdlib.h> +#include <asm/io.h> + +/* 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 <partition UUID>\n" + " - lists the partition(s) info\n" + "ping <partition ID>\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 71606373f9..325fb80346 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 0000000000..13fa90c129 --- /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 open-source-office@arm.com + +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 388e59f173..e462de2806 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 fc668c109d..d75f8b53fd 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 a3cf983739..6e3d7e919e 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 open-source-office@arm.com
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 0000000000..9a44a397e8 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <string.h> +#include <asm/sandbox_arm_ffa.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +/* Basic test of 'armffa' command */ +static int dm_test_armffa_cmd(struct unit_test_state *uts) +{ + /* armffa getpart <UUID> */ + ut_assertok(run_command("armffa getpart " SANDBOX_SERVICE1_UUID, 0)); + + /* armffa ping <ID> */ + 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);

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-...
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Tested-by: Gowtham Suresh Kumar gowtham.sureshkumar@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v18:
Ilias, Tom:
* drop the use of configs for the shared MM buffer, put back #ifdefs instead * add test information to the commit log
v17:
* show a debug message rather than an error when FF-A is not detected
v16:
* lib/efi_loader/Kconfig: rather than automatically selecting OPTEE and ARM_FFA_TRANSPORT configs by EFI_MM_COMM_TEE, set them as dependencies (Otherwise FF-A will be automatically enabled for boards that don't need it).
v15:
Simon:
* replace FFA_SHARED_MM_BUFFER_* defines with configs
v14:
Ilias:
* drop truncating var_payload->size when using FF-A * map the MM SP return codes to errnos
v13:
* remove FF-A and Optee ifdefs
v12:
* drop use of calloc when querying SPs * address nits
v11:
* rename select_ffa_mm_comms() to select_mm_comms() * improve the logic of MM transport selection in mm_communicate() * addressing nits
v10:
* use the FF-A driver Uclass operations * use uclass_first_device() * addressing nits
v9: align how FF-A is used with FF-A discovery through DM
v8:
* isolate the compilation choices between FF-A and OP-TEE * update partition_info_get() second argument to be an SP count * pass NULL device pointer to the FF-A bus discovery and operations
v7:
* set the MM door bell event to use 64-bit direct messaging * issue a compile time error when one of these macros are not found : FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_OFFSET, FFA_SHARED_MM_BUFFER_ADDR * make mm_sp_svc_uuid static * replace EINVAL with ENOMEM in ffa_discover_mm_sp_id() when calloc() fails * improve use of unmap_sysmem() in ffa_mm_communicate()
v6:
* add FF-A runtime discovery at MM communication level * drop EFI runtime support for FF-A MM communication * revert the changes in include/mm_communication.h for efi_mm_communicate_header and smm_variable_access structures
v4:
* use the new FF-A driver interfaces * discover MM partitions at runtime * copy FF-A driver private data to EFI runtime section at ExitBootServices() * drop use of FFA_ERR_STAT_SUCCESS error code * replace EFI_BUFFER_TOO_SMALL with EFI_OUT_OF_RESOURCES in ffa_mm_communicate(). No need for efi_memcpy_runtime() anymore * revert the error log in mm_communicate() in case of failure * remove packed attribute from efi_mm_communicate_header and smm_variable_communicate_header
v2:
* set default values to 0 for FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_ADDR and MM_SP_UUID_DATA and add warnings
v1:
* introduce FF-A MM communication
include/mm_communication.h | 17 ++ lib/efi_loader/Kconfig | 42 ++++- lib/efi_loader/efi_variable_tee.c | 282 +++++++++++++++++++++++++++++- 3 files changed, 335 insertions(+), 6 deletions(-)
diff --git a/include/mm_communication.h b/include/mm_communication.h index e65fbde60d..f38f1a5344 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. sughosh.ganu@linaro.org * Copyright (C) 2020 Linaro Ltd. ilias.apalodimas@linaro.org + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#ifndef _MM_COMMUNICATION_H_ @@ -13,6 +16,11 @@
#include <part_efi.h>
+#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 c5835e6ef6..19e51bf503 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -55,13 +55,53 @@ 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" + default 0 + depends on EFI_MM_COMM_TEE + 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" + default 0 + depends on EFI_MM_COMM_TEE + 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" + default 0x0 + depends on EFI_MM_COMM_TEE + 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 dfef18435d..07bf64f270 100644 --- a/lib/efi_loader/efi_variable_tee.c +++ b/lib/efi_loader/efi_variable_tee.c @@ -4,16 +4,50 @@ * * Copyright (C) 2019 Linaro Ltd. sughosh.ganu@linaro.org * Copyright (C) 2019 Linaro Ltd. ilias.apalodimas@linaro.org + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> +#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT) +#include <arm_ffa.h> +#endif +#include <cpu_func.h> +#include <dm.h> #include <efi.h> #include <efi_api.h> #include <efi_loader.h> #include <efi_variable.h> -#include <tee.h> #include <malloc.h> +#include <mapmem.h> #include <mm_communication.h> +#include <tee.h> + +#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT) +#ifndef FFA_SHARED_MM_BUFFER_SIZE +#define FFA_SHARED_MM_BUFFER_SIZE SZ_4K +#endif + +#ifndef FFA_SHARED_MM_BUFFER_OFFSET +#define FFA_SHARED_MM_BUFFER_OFFSET 0 +#endif + +#ifndef FFA_SHARED_MM_BUFFER_ADDR +#define FFA_SHARED_MM_BUFFER_ADDR 0x02000000UL +#endif + +/* 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 +178,238 @@ static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize) return ret; }
+#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT) +/** + * 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 = FFA_SHARED_MM_BUFFER_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 > FFA_SHARED_MM_BUFFER_SIZE) + return EFI_INVALID_PARAMETER; + + /* Copy the data to the shared buffer */ + + virt_shared_buf = map_sysmem((phys_addr_t)FFA_SHARED_MM_BUFFER_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 cmonnucation buffer to StandAlonneMM and send + * 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 +417,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 +969,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);
/*

On Thu, Aug 03, 2023 at 05:03:49PM +0100, Abdellatif El Khlifi wrote:
[snip]
diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index c5835e6ef6..19e51bf503 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig
[snip]
+config FFA_SHARED_MM_BUF_SIZE
- int "Memory size of the shared MM communication buffer"
- default 0
Remove the default.
- depends on EFI_MM_COMM_TEE
This should be EFI_MM_COMM_TEE && ARM_FFA_TRANSPORT
[snip]
+config FFA_SHARED_MM_BUF_OFFSET
- int "Data offset in the shared MM communication buffer"
- default 0
- depends on EFI_MM_COMM_TEE
Same.
[snip]
+config FFA_SHARED_MM_BUF_ADDR
- hex "Define the address of the shared MM communication buffer"
- default 0x0
- depends on EFI_MM_COMM_TEE
Same.

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 abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v18:
Ilias, Tom:
* drop use of CONFIG_FFA_SHARED_MM_BUF_*
v17:
* use savedefconfig to generate corstone1000_defconfig with FF-A MM comms enabled
v16:
* configs/corstone1000_defconfig: enable MM communication by setting the configs: ARM_FFA_TRANSPORT, OPTEE, TEE
v15:
Simon:
* use CONFIG_FFA_SHARED_MM_BUF_* configs in place of FFA_SHARED_MM_BUFFER_*
v13:
* remove FF-A config in the defconfig (because it's enabled automatically by CONFIG_EFI_MM_COMM_TEE)
v9:
* update copyright string
v8:
* drop OP-TEE configs from Corstone-1000 defconfig
v7:
* improve the definition of FFA_SHARED_MM_BUFFER_ADDR and FFA_SHARED_MM_BUFFER_OFFSET * update FFA_SHARED_MM_BUFFER_ADDR value
v6:
* corstone-1000: enable optee driver * corstone-1000: remove CONFIG_ARM_FFA_EFI_RUNTIME_MODE from the defconfig
v4:
* corstone-1000: turn on EFI MM communication
configs/corstone1000_defconfig | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/configs/corstone1000_defconfig b/configs/corstone1000_defconfig index a8a79fd105..59ce1ec15c 100644 --- a/configs/corstone1000_defconfig +++ b/configs/corstone1000_defconfig @@ -28,12 +28,10 @@ CONFIG_CMD_FWU_METADATA=y CONFIG_CMD_BOOTZ=y CONFIG_SYS_BOOTM_LEN=0x800000 # CONFIG_CMD_XIMG is not set -CONFIG_CMD_NVMXIP=y CONFIG_CMD_GPT=y # CONFIG_RANDOM_UUID is not set CONFIG_CMD_LOADM=y # CONFIG_CMD_LOADS is not set -CONFIG_CMD_MMC=y CONFIG_CMD_USB=y # CONFIG_CMD_SETEXPR is not set # CONFIG_CMD_NFS is not set @@ -45,8 +43,7 @@ CONFIG_OF_CONTROL=y CONFIG_VERSION_VARIABLE=y CONFIG_NET_RANDOM_ETHADDR=y CONFIG_REGMAP=y -CONFIG_FWU_MDATA=y -CONFIG_FWU_MDATA_GPT_BLK=y +CONFIG_ARM_FFA_TRANSPORT=y CONFIG_MISC=y # CONFIG_MMC is not set CONFIG_NVMXIP_QSPI=y @@ -59,9 +56,12 @@ 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_EFI_CAPSULE_ON_DISK=y CONFIG_EFI_IGNORE_OSINDICATIONS=y CONFIG_FWU_MULTI_BANK_UPDATE=y -CONFIG_ERRNO_STR=y

On Thu, Aug 03, 2023 at 05:03:50PM +0100, Abdellatif El Khlifi wrote:
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 abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v18:
Ilias, Tom:
- drop use of CONFIG_FFA_SHARED_MM_BUF_*
Why? What was wrong before was what I commented on still being wrong in 8/9, this part was fine.

Hi Tom,
Changelog:
v18:
Ilias, Tom:
- drop use of CONFIG_FFA_SHARED_MM_BUF_*
Why? What was wrong before was what I commented on still being wrong in 8/9, this part was fine.
I thought when we decided to bring back the #ifdefs the buffer macros are also to be brought back.
Well, I'll keep CONFIG_FFA_SHARED_MM_BUF_*.
Cheers

Adding support for Arm FF-A v1.0 (Arm Firmware Framework for Armv8-A) [A].
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.
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.
=> dm tree
Class Index Probed Driver Name ----------------------------------------------------------- ... firmware 0 [ + ] psci |-- psci ffa 0 [ ] arm_ffa | `-- arm_ffa ...
Clients are able to probe then use the FF-A bus by calling the DM class searching APIs (e.g: uclass_first_device).
This implementation of the specification provides support for Aarch64.
The FF-A driver uses the SMC ABIs defined by the FF-A specification to:
- Discover the presence of secure partitions (SPs) of interest - Access an SP's service through communication protocols (e.g: EFI MM communication protocol)
The FF-A support provides the following features:
- Being generic by design and can be used by any Arm 64-bit platform - FF-A support can be compiled and used without EFI - Support for SMCCCv1.2 x0-x17 registers - Support for SMC32 calling convention - Support for 32-bit and 64-bit FF-A direct messaging - Support for FF-A MM communication (compatible with EFI boot time) - Enabling FF-A and MM communication in Corstone1000 platform as a use case - 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. - Sandbox FF-A test cases. - A new command called armffa is provided as an example of how to access the FF-A bus
For more details about the FF-A support please refer to [B] and refer to [C] for how to use the armffa command.
Please find at [D] an example of the expected boot logs when enabling FF-A support for a platform. In this example the platform is Corstone1000. But it can be any Arm 64-bit platform.
Changelog of changes: ===========================
v19:
Tom:
* use CONFIG_FFA_SHARED_MM_BUF_* in place of macros
v18: [18]
Ilias, Tom:
* drop the use of configs for the shared MM buffer, put back #ifdefs instead * add test information to the MM comms commit message
v17: [17]
Ilias:
* show a debug message rather than an error when FF-A is not detected
Tom:
* use savedefconfig to generate corstone1000_defconfig with FF-A MM comms enabled
v16: [16]
Tom:
* lib/efi_loader/Kconfig: rather than automatically selecting OPTEE and ARM_FFA_TRANSPORT configs by EFI_MM_COMM_TEE, set them as dependencies (Otherwise FF-A will be automatically enabled for boards that don't need it).
* configs/corstone1000_defconfig: enable MM communication by setting the configs: ARM_FFA_TRANSPORT, OPTEE, TEE
v15: [15]
Simon:
* drop commit "log: select physical address formatting in a generic way", this will be sent as a follow-up commit independently from this patchset * armffa.c : integrate PHYS_ADDR_LN * replace FFA_SHARED_MM_BUFFER_* defines with configs
v14: [14]
Simon:
* add to log.h a generic physical address formatting
Ilias:
* armffa command: in do_ffa_ping() reject the SP ID if it's 0 * MM comms: drop truncating var_payload->size when using FF-A * MM comms: map the MM SP return codes to errnos * address nits
v13: [13]
Ilias: * remove FF-A and Optee ifdefs in efi_variable_tee.c * doc minor change: specify in the readme that the user should call ffa_rxtx_unmap() driver operation to unmap the RX/TX buffers on demand.
v12: [12]
* remove the global variable (dscvry_info), use uc_priv instead * replace dscvry_info.invoke_ffa_fn() with a weak invoke_ffa_fn (user drivers can override it) * improve FFA_PARTITION_INFO_GET implementation (clients no longer need to calloc a buffer) * remove reparenting by making the sandbox emulator parent of the FF-A device in the DT * improve argument checks for the armffa command * address nits
v11: [11]
* move ffa_try_discovery() from the uclass to the Arm FF-A driver * rename ffa_try_discovery() to arm_ffa_discover() * add arm_ prefix to the Arm FF-A driver functions * use U_BOOT_CMD_WITH_SUBCMDS for armffa command * store the sandbox emulator pointer in the FF-A device uc_priv (struct ffa_priv) * set the emulator as parent of the sandbox FF-A device * rename select_ffa_mm_comms() to select_mm_comms() * improve the logic of MM transport selection in mm_communicate() * use ut_asserteq_mem() in uuid_str_to_le_bin test case * address nits
v10: [10]
* provide the FF-A driver operations through the Uclass (arm-ffa-uclass.c) * move the generic FF-A methods to the Uclass * keep Arm specific methods in the Arm driver (arm-ffa.c renamed from core.c) * split the FF-A sandbox support into an emulator (ffa-emul-uclass.c) and a driver (sandbox_ffa.c) * use the FF-A driver Uclass operations by clients (armffa command, tests, MM comms) * use uclass_first_device to search and probe the FF-A device (whether it is on Arm or on sandbox) * address nits
v9: [9]
* integrate the FF-A bus discovery in the DM and use ARM_SMCCC_FEATURES for binding * align FF-A sandbox driver with FF-A discovery through DM * use DM class APIs to probe and interact with the FF-A bus (in FF-A MM comms, armffa command, sandbox tests) * add documentation for the armffa command: doc/usage/cmd/armffa.rst * introduce testcase for uuid_str_to_le_bin
v8: [8]
* pass the FF-A bus device to the bus operations * isolate the compilation choices between FF-A and OP-TEE * drop OP-TEE configs from Corstone-1000 defconfig * make ffa_get_partitions_info() second argument to be an SP count in both modes
v7: [7]
* add support for 32-bit direct messaging (now we have 32-bit and 64-bit support) * set the MM door bell event to use 64-bit direct messaging * issue a compile time error when one of these macros are not found : FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_OFFSET, FFA_SHARED_MM_BUFFER_ADDR * make uuid_str_to_le_bin() implementation similar to uuid_str_to_bin() by using same APIs
v6: [6]
* remove clearing x0-x17 registers after SMC calls * drop use of EFI runtime support for FF-A (We decided with Linaro to add this later) * drop discovery from initcalls (discovery will be on demand by FF-A users) * add FF-A runtime discovery at MM communication level * update the documentation and move it to doc/arch/arm64.ffa.rst
v5: [5]
* move changelogs in each commit to the changes section
v4: [4]
* add FF-A support README (doc/README.ffa.drv) * improving error handling by mapping the FF-A errors to standard errors and logs * replacing panics with an error log * align sandbox driver and tests with the new FF-A driver interfaces and new way of error handling * use the new FF-A driver interfaces for MM communication * discover MM partitions at runtime * copy FF-A driver private data to EFI runtime section at ExitBootServices() * moving the FF-A driver work to drivers/firmware/arm-ffa * improving features discovery in FFA_FEATURES * add remove/unbind functions to the FF-A core device * improve how the driver behaves when bus discovery is done more than once * move clearing x0-x17 registers code into a new macro like done in the linux kernel * enable EFI MM communication for the Corstone1000 platform
v3: [3]
* port x0-x17 registers support from linux kernel as defined by SMCCCv1.2 * align the interfaces of the u-boot FF-A driver with those in the linux FF-A driver * remove the FF-A helper layer * make the u-boot FF-A driver independent from EFI * provide an optional config that enables copying the driver data to EFI runtime section at ExitBootServices service * use 64-bit version of FFA_RXTX_MAP, FFA_MSG_SEND_DIRECT_{REQ, RESP} * update armffa command with the new driver interfaces
v2 [2]:
* make FF-A bus discoverable using device_{bind, probe} APIs * remove device tree support
v1 [1]:
* introduce FF-A bus driver with device tree support * introduce armffa command * introduce FF-A Sandbox driver * add FF-A Sandbox test cases * introduce FF-A MM communication
Cheers, Abdellatif
List of previous patches:
[1]: https://lore.kernel.org/all/20220329151659.16894-1-abdellatif.elkhlifi@arm.c... [2]: https://lore.kernel.org/all/20220415122803.16666-1-abdellatif.elkhlifi@arm.c... [3]: https://lore.kernel.org/all/20220801172053.20163-1-abdellatif.elkhlifi@arm.c... [4]: https://lore.kernel.org/all/20220926101723.9965-1-abdellatif.elkhlifi@arm.co... [5]: https://lore.kernel.org/all/20220926140827.15125-1-abdellatif.elkhlifi@arm.c... [6]: https://lore.kernel.org/all/20221013103857.614-1-abdellatif.elkhlifi@arm.com... [7]: https://lore.kernel.org/all/20221107192055.21669-1-abdellatif.elkhlifi@arm.c... [8]: https://lore.kernel.org/all/20221122131751.22747-1-abdellatif.elkhlifi@arm.c... [9]: https://lore.kernel.org/all/20230310141016.137986-1-abdellatif.elkhlifi@arm.... [10]: https://lore.kernel.org/all/20230328161157.219375-1-abdellatif.elkhlifi@arm.... [11]: https://lore.kernel.org/all/20230412094245.44674-1-abdellatif.elkhlifi@arm.c... [12]: https://lore.kernel.org/all/20230512121044.111574-1-abdellatif.elkhlifi@arm.... [13]: https://lore.kernel.org/all/20230616152817.319869-1-abdellatif.elkhlifi@arm.... [14]: https://lore.kernel.org/all/20230707144410.228472-1-abdellatif.elkhlifi@arm.... [15]: https://lore.kernel.org/all/20230713132847.176000-1-abdellatif.elkhlifi@arm.... [16]: https://lore.kernel.org/all/20230726094503.100497-1-abdellatif.elkhlifi@arm.... [17]: https://lore.kernel.org/all/20230727160712.81477-1-abdellatif.elkhlifi@arm.c... [18]: https://lore.kernel.org/all/20230803160350.477781-1-abdellatif.elkhlifi@arm....
More details:
[A]: https://developer.arm.com/documentation/den0077/latest/ [B]: doc/arch/arm64.ffa.rst [C]: doc/usage/cmd/armffa.rst [D]: example of boot logs when enabling FF-A
``` 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 ```
Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Rob Herring robh@kernel.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Achin Gupta achin.gupta@arm.com Cc: Drew Reed Drew.Reed@arm.com Cc: Xueliang Zhong Xueliang.Zhong@arm.com
Abdellatif El Khlifi (9): arm64: smccc: add support for SMCCCv1.2 x0-x17 registers lib: uuid: introduce uuid_str_to_le_bin function lib: uuid: introduce testcase for uuid_str_to_le_bin arm_ffa: introduce Arm FF-A support arm_ffa: introduce sandbox FF-A support arm_ffa: introduce sandbox test cases for UCLASS_FFA arm_ffa: introduce armffa command arm_ffa: efi: introduce FF-A MM communication arm_ffa: efi: corstone1000: enable MM communication
MAINTAINERS | 18 + arch/arm/cpu/armv8/smccc-call.S | 57 +- arch/arm/lib/asm-offsets.c | 16 + 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 ++ cmd/Kconfig | 10 + cmd/Makefile | 1 + cmd/armffa.c | 202 ++++ configs/corstone1000_defconfig | 13 +- configs/sandbox64_defconfig | 1 + configs/sandbox_defconfig | 1 + doc/arch/arm64.ffa.rst | 261 ++++ doc/arch/index.rst | 1 + doc/arch/sandbox/sandbox.rst | 1 + doc/usage/cmd/armffa.rst | 94 ++ doc/usage/index.rst | 1 + drivers/Makefile | 1 + drivers/firmware/Kconfig | 1 + drivers/firmware/arm-ffa/Kconfig | 42 + drivers/firmware/arm-ffa/Makefile | 16 + drivers/firmware/arm-ffa/arm-ffa-uclass.c | 1065 +++++++++++++++++ drivers/firmware/arm-ffa/arm-ffa.c | 104 ++ drivers/firmware/arm-ffa/ffa-emul-uclass.c | 720 +++++++++++ drivers/firmware/arm-ffa/sandbox_ffa.c | 110 ++ include/arm_ffa.h | 213 ++++ include/arm_ffa_priv.h | 246 ++++ include/dm/uclass-id.h | 7 + include/linux/arm-smccc.h | 45 + include/mm_communication.h | 17 + include/uuid.h | 15 + lib/efi_loader/Kconfig | 39 +- lib/efi_loader/efi_variable_tee.c | 270 ++++- lib/uuid.c | 48 + test/cmd/Makefile | 2 + test/cmd/armffa.c | 33 + test/dm/Makefile | 3 +- test/dm/ffa.c | 261 ++++ test/lib/Makefile | 1 + test/lib/uuid.c | 41 + 41 files changed, 4174 insertions(+), 13 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 cmd/armffa.c create mode 100644 doc/arch/arm64.ffa.rst create mode 100644 doc/usage/cmd/armffa.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/ffa-emul-uclass.c create mode 100644 drivers/firmware/arm-ffa/sandbox_ffa.c create mode 100644 include/arm_ffa.h create mode 100644 include/arm_ffa_priv.h create mode 100644 test/cmd/armffa.c create mode 100644 test/dm/ffa.c create mode 100644 test/lib/uuid.c

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 abdellatif.elkhlifi@arm.com Reviewed-by: Ilias Apalodimas ilias.apalodimas@linaro.org Reviewed-by: Jens Wiklander jens.wiklander@linaro.org Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com
---
Changelog: ===============
v9:
* update the copyright string
v7:
* improve indentation of ARM_SMCCC_1_2_REGS_Xn_OFFS
v4:
* rename the commit title and improve description new commit title: the current
v3:
* port x0-x17 registers support from linux kernel as defined by SMCCCv1.2 commit title: arm64: smccc: add Xn registers support used by SMC calls
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 dc92b28777..93f66d3366 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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com +*/ #include <linux/linkage.h> #include <linux/arm-smccc.h> #include <generated/asm-offsets.h> @@ -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 6de0ce9152..181a8ac4c2 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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> @@ -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 e1d09884a1..f44e9e8f93 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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */ #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

convert UUID string to little endian binary data
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v9:
* add a full function prototype description in uuid.h
v8:
* use simple_strtoull() in uuid_str_to_le_bin() to support 32-bit platforms
v7:
* rename be_uuid_str_to_le_bin() to uuid_str_to_le_bin() * make uuid_str_to_le_bin() implementation similar to uuid_str_to_bin() by using same APIs
v4:
* rename ffa_uuid_str_to_bin to be_uuid_str_to_le_bin and put in a standalone commit (the current)
v3:
* introduce ffa_uuid_str_to_bin (provided by arm_ffa: introduce Arm FF-A low-level driver)
include/uuid.h | 15 +++++++++++++++ lib/uuid.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+)
diff --git a/include/uuid.h b/include/uuid.h index 4a4883d3b5..89b93e642b 100644 --- a/include/uuid.h +++ b/include/uuid.h @@ -2,6 +2,10 @@ /* * Copyright (C) 2014 Samsung Electronics * Przemyslaw Marczak p.marczak@samsung.com + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */ #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 96e1af3c8b..45f325d964 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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> @@ -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. *

provide a test case
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com
---
Changelog: ===============
v16:
* MAINTAINERS: place the UUID part in an alphabetical order
v11:
* use ut_asserteq_mem()
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 d724b64673..4324965d26 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1555,6 +1555,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 abdellatif.elkhlifi@arm.com +S: Maintained +F: test/lib/uuid.c + VIDEO M: Anatolij Gustschin agust@denx.de S: Maintained diff --git a/test/lib/Makefile b/test/lib/Makefile index e0bd9e04e8..e75a263e6a 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 0000000000..e24331a136 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <uuid.h> +#include <test/lib.h> +#include <test/test.h> +#include <test/ut.h> + +/* 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);

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 abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Reviewed-by: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Tom Rini trini@konsulko.com Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
---
Changelog: ===============
v13:
* doc minor change: specify in the readme that the user should call ffa_rxtx_unmap() driver operation to unmap the RX/TX buffers on demand.
v12:
* remove dscvry_info * replace dscvry_info.invoke_ffa_fn() with a weak invoke_ffa_fn (user drivers can override it) * improve FFA_PARTITION_INFO_GET implementation (clients no longer need to calloc a buffer) * address nits
v11:
* move ffa_try_discovery() from the uclass to the Arm FF-A driver * rename ffa_try_discovery() to arm_ffa_discover() * pass dev as an argument of arm_ffa_discover() * add arm_ prefix to the Arm FF-A driver functions * add emul field in struct ffa_discovery_info * address nits
v10:
* provide the driver operations through the Uclass * move the generic FF-A methods to the Uclass * keep Arm specific methods in the Arm driver (arm-ffa.c) * rename core.c to arm-ffa.c * address nits
v9:
* integrate the FF-A bus discovery in the DM and use ARM_SMCCC_FEATURES for binding
v8:
* make ffa_get_partitions_info() second argument to be an SP count in both modes * update ffa_bus_prvdata_get() to return a pointer rather than a pointer address * remove packing from ffa_partition_info and ffa_send_direct_data structures * pass the FF-A bus device to the bus operations
v7:
* add support for 32-bit direct messaging * rename be_uuid_str_to_le_bin() to uuid_str_to_le_bin() * improve the declaration of error handling mapping * stating in doc/arch/arm64.ffa.rst that EFI runtime is not supported
v6:
* drop use of EFI runtime support (We decided with Linaro to add this later) * drop discovery from initcalls (discovery will be on demand by FF-A users) * set the alignment of the RX/TX buffers to the larger translation granule size * move FF-A RX/TX buffers unmapping at ExitBootServices() to a separate commit * update the documentation and move it to doc/arch/arm64.ffa.rst
v4:
* add doc/README.ffa.drv * moving the FF-A driver work to drivers/firmware/arm-ffa * use less #ifdefs in lib/efi_loader/efi_boottime.c and replace #if defined by #if CONFIG_IS_ENABLED * improving error handling by mapping the FF-A errors to standard errors and logs * replacing panics with an error log and returning an error code * improving features discovery in FFA_FEATURES by introducing rxtx_min_pages private data field * add ffa_remove and ffa_unbind functions * improve how the driver behaves when bus discovery is done more than once
v3:
* align the interfaces of the U-Boot FF-A driver with those in the linux FF-A driver * remove the FF-A helper layer * make the U-Boot FF-A driver independent from EFI * provide an optional config that enables copying the driver data to EFI runtime section at ExitBootServices service * use 64-bit version of FFA_RXTX_MAP, FFA_MSG_SEND_DIRECT_{REQ, RESP}
v2:
* make FF-A bus discoverable using device_{bind, probe} APIs * remove device tree support
v1:
* introduce FF-A bus driver with device tree support
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 4324965d26..4fd5768de0 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 abdellatif.elkhlifi@arm.com +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 sbabic@denx.de M: Fabio Estevam festevam@gmail.com diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst new file mode 100644 index 0000000000..df18948774 --- /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 abdellatif.elkhlifi@arm.com + +.. _`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 b8da4b8c8e..2f916f4026 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 78dcf62f76..5044f45253 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 eae1c8ddc9..8789b1ea14 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 0000000000..9200c8028b --- /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 0000000000..11b1766285 --- /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 open-source-office@arm.com +# +# Authors: +# Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + +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 0000000000..ffa9d81fa7 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ +#include <common.h> +#include <arm_ffa.h> +#include <arm_ffa_priv.h> +#include <dm.h> +#include <log.h> +#include <malloc.h> +#include <string.h> +#include <uuid.h> +#include <asm/global_data.h> +#include <dm/device-internal.h> +#include <dm/devres.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> + +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 0000000000..68df75bd9e --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <arm_ffa.h> +#include <arm_ffa_priv.h> +#include <dm.h> +#include <log.h> +#include <asm/global_data.h> +#include <dm/device-internal.h> +#include <linux/errno.h> + +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 0000000000..4338f9c9b1 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#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 0000000000..db9b1be995 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __ARM_FFA_H +#define __ARM_FFA_H + +#include <linux/printk.h> + +/* + * 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 0000000000..d564c33c64 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __ARM_FFA_PRV_H +#define __ARM_FFA_PRV_H + +#include <mapmem.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> + +/* 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 <linux/arm-smccc.h> +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 307ad6931c..3c6af2e3d2 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -4,6 +4,11 @@ * * (C) Copyright 2012 * Pavel Herrmann morpheus.ibis@gmail.com + * + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#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 */

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 abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
---
Changelog: ===============
v12:
* remove reparenting by making the emulator parent of the FF-A device in the DT * add invoke_ffa_fn() * address nits
v11:
* rename ffa_try_discovery() to sandbox_ffa_discover() * rename sandbox_ffa_query_core_state() to sandbox_query_ffa_emul_state() * store the sandbox emulator pointer in the FF-A device uc_priv (struct ffa_priv) * set the emulator as parent of the sandbox FF-A device
v10:
* split the FF-A sandbox support into an emulator and a driver * read FFA_VERSION and FFA_PARTITION_INFO_GET state using sandbox_ffa_query_core_state() * drop CONFIG_SANDBOX_FFA config * address nits
v9: align FF-A sandbox driver with FF-A discovery through DM
v8: update ffa_bus_prvdata_get() to return a pointer rather than a pointer address
v7: state that sandbox driver supports only 64-bit direct messaging
v4: align sandbox driver with the new FF-A driver interfaces and new way of error handling
v1: introduce the sandbox driver
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 4fd5768de0..63cf37290c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -269,10 +269,11 @@ F: configs/cortina_presidio-asic-pnand_defconfig ARM FF-A M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com 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 sbabic@denx.de diff --git a/arch/sandbox/dts/sandbox.dtsi b/arch/sandbox/dts/sandbox.dtsi index 30a305c4d2..94a08814b8 100644 --- a/arch/sandbox/dts/sandbox.dtsi +++ b/arch/sandbox/dts/sandbox.dtsi @@ -445,6 +445,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 ff9f9222e6..96b5404991 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -1820,6 +1820,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 0000000000..be2790f496 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __SANDBOX_ARM_FFA_H +#define __SANDBOX_ARM_FFA_H + +#include <arm_ffa.h> + +/* + * 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 0000000000..b0881822d7 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __SANDBOX_ARM_FFA_PRV_H +#define __SANDBOX_ARM_FFA_PRV_H + +#include <arm_ffa_priv.h> + +/* 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 98b3e0cda4..72ea3d21ab 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 1ec44d5b33..8269bec879 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -344,3 +344,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 df18948774..792898321a 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 77ca6bc4cc..a3631de749 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 9200c8028b..fc668c109d 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 11b1766285..318123a7f4 100644 --- a/drivers/firmware/arm-ffa/Makefile +++ b/drivers/firmware/arm-ffa/Makefile @@ -5,4 +5,12 @@ # Authors: # Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
-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 0000000000..5562bbaac3 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ +#include <common.h> +#include <dm.h> +#include <mapmem.h> +#include <string.h> +#include <asm/global_data.h> +#include <asm/sandbox_arm_ffa.h> +#include <asm/sandbox_arm_ffa_priv.h> +#include <dm/device-internal.h> +#include <dm/lists.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> + +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 4338f9c9b1..0000000000 --- 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 open-source-office@arm.com - * - * Authors: - * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com - */ - -#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 0000000000..ef9491ccea --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ +#include <common.h> +#include <arm_ffa.h> +#include <dm.h> +#include <log.h> +#include <asm/global_data.h> +#include <asm/sandbox_arm_ffa_priv.h> +#include <dm/device-internal.h> +#include <linux/errno.h> + +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 3c6af2e3d2..0432c95c9e 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 */

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 abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
---
Changelog: ===============
v12:
* remove use of dscvry_info * drop use of calloc when querying SPs * address nits
v11:
* drop unmapping test (taken care of by the DM when removing the device) * address nits
v10:
* use the FF-A driver Uclass operations * use uclass_first_device() * replace CONFIG_SANDBOX_FFA with CONFIG_ARM_FFA_TRANSPORT * address nits
v9: align FF-A sandbox tests with FF-A discovery through DM
v8:
* update partition_info_get() second argument to be an SP count * pass NULL device pointer to the FF-A bus discovery and operations
v7: set the tests to use 64-bit direct messaging
v4: align sandbox tests with the new FF-A driver interfaces and new way of error handling
v1: introduce sandbox tests
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 63cf37290c..b6d7263010 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 sbabic@denx.de diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst index 792898321a..71606373f9 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 3799b1ae8f..7ed00733c1 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 open-source-office@arm.com +# Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
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 0000000000..6912666bb4 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <console.h> +#include <dm.h> +#include <asm/sandbox_arm_ffa.h> +#include <asm/sandbox_arm_ffa_priv.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +/* 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);

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 abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
---
Changelog: ===============
v18:
Simon:
* Combining the commits of the command and the test case
v15:
Simon:
* armffa.c : integrate PHYS_ADDR_LN
v14:
Ilias:
* address nits * in do_ffa_ping() reject the SP ID if it's 0 * use PHYS_ADDR_LN in formatting the physical addresses
v12:
* add subcommands argument checks * usage documentation: update command return codes * remove calloc when querying SPs * address nits
v11:
* use U_BOOT_CMD_WITH_SUBCMDS * address nits
v10:
* use the FF-A driver Uclass operations * use uclass_first_device() * address nits
v9:
* remove manual FF-A discovery and use DM * use DM class APIs to probe and interact with the FF-A bus * add doc/usage/cmd/armffa.rst
v8:
* update partition_info_get() second argument to be an SP count * pass NULL device pointer to the FF-A bus discovery and operations
v7:
* adapt do_ffa_dev_list() following the recent update on uclass_first_device/uclass_next_device functions (they return void now) * set armffa command to use 64-bit direct messaging
v4:
* remove pattern data in do_ffa_msg_send_direct_req
v3:
* use the new driver interfaces (partition_info_get, sync_send_receive) in armffa command
v2:
* replace use of ffa_helper_init_device function by ffa_helper_bus_discover
v1:
* introduce armffa command
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 b6d7263010..bd3dba3d95 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -271,9 +271,12 @@ M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com 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 c1941849f9..da8569b417 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -935,6 +935,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 6c37521b4e..7d20a85a46 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 0000000000..7e6eafc03a --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ +#include <common.h> +#include <arm_ffa.h> +#include <command.h> +#include <dm.h> +#include <mapmem.h> +#include <stdlib.h> +#include <asm/io.h> + +/* 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 <partition UUID>\n" + " - lists the partition(s) info\n" + "ping <partition ID>\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 71606373f9..325fb80346 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 0000000000..13fa90c129 --- /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 open-source-office@arm.com + +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 388e59f173..e462de2806 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 fc668c109d..d75f8b53fd 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 a3cf983739..6e3d7e919e 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 open-source-office@arm.com
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 0000000000..9a44a397e8 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <string.h> +#include <asm/sandbox_arm_ffa.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +/* Basic test of 'armffa' command */ +static int dm_test_armffa_cmd(struct unit_test_state *uts) +{ + /* armffa getpart <UUID> */ + ut_assertok(run_command("armffa getpart " SANDBOX_SERVICE1_UUID, 0)); + + /* armffa ping <ID> */ + 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);

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-...
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Tested-by: Gowtham Suresh Kumar gowtham.sureshkumar@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v19:
Tom:
* use CONFIG_FFA_SHARED_MM_BUF_* in place of macros
v18:
Ilias, Tom:
* drop the use of configs for the shared MM buffer, put back #ifdefs instead * add test information to the commit log
v17:
* show a debug message rather than an error when FF-A is not detected
v16:
* lib/efi_loader/Kconfig: rather than automatically selecting OPTEE and ARM_FFA_TRANSPORT configs by EFI_MM_COMM_TEE, set them as dependencies (Otherwise FF-A will be automatically enabled for boards that don't need it).
v15:
Simon:
* replace FFA_SHARED_MM_BUFFER_* defines with configs
v14:
Ilias:
* drop truncating var_payload->size when using FF-A * map the MM SP return codes to errnos
v13:
* remove FF-A and Optee ifdefs
v12:
* drop use of calloc when querying SPs * address nits
v11:
* rename select_ffa_mm_comms() to select_mm_comms() * improve the logic of MM transport selection in mm_communicate() * addressing nits
v10:
* use the FF-A driver Uclass operations * use uclass_first_device() * addressing nits
v9: align how FF-A is used with FF-A discovery through DM
v8:
* isolate the compilation choices between FF-A and OP-TEE * update partition_info_get() second argument to be an SP count * pass NULL device pointer to the FF-A bus discovery and operations
v7:
* set the MM door bell event to use 64-bit direct messaging * issue a compile time error when one of these macros are not found : FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_OFFSET, FFA_SHARED_MM_BUFFER_ADDR * make mm_sp_svc_uuid static * replace EINVAL with ENOMEM in ffa_discover_mm_sp_id() when calloc() fails * improve use of unmap_sysmem() in ffa_mm_communicate()
v6:
* add FF-A runtime discovery at MM communication level * drop EFI runtime support for FF-A MM communication * revert the changes in include/mm_communication.h for efi_mm_communicate_header and smm_variable_access structures
v4:
* use the new FF-A driver interfaces * discover MM partitions at runtime * copy FF-A driver private data to EFI runtime section at ExitBootServices() * drop use of FFA_ERR_STAT_SUCCESS error code * replace EFI_BUFFER_TOO_SMALL with EFI_OUT_OF_RESOURCES in ffa_mm_communicate(). No need for efi_memcpy_runtime() anymore * revert the error log in mm_communicate() in case of failure * remove packed attribute from efi_mm_communicate_header and smm_variable_communicate_header
v2:
* set default values to 0 for FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_ADDR and MM_SP_UUID_DATA and add warnings
v1:
* introduce FF-A MM communication
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 e65fbde60d..f38f1a5344 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. sughosh.ganu@linaro.org * Copyright (C) 2020 Linaro Ltd. ilias.apalodimas@linaro.org + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#ifndef _MM_COMMUNICATION_H_ @@ -13,6 +16,11 @@
#include <part_efi.h>
+#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 c5835e6ef6..852f9671d8 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 dfef18435d..09d03c0eee 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. sughosh.ganu@linaro.org * Copyright (C) 2019 Linaro Ltd. ilias.apalodimas@linaro.org + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> +#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT) +#include <arm_ffa.h> +#endif +#include <cpu_func.h> +#include <dm.h> #include <efi.h> #include <efi_api.h> #include <efi_loader.h> #include <efi_variable.h> -#include <tee.h> #include <malloc.h> +#include <mapmem.h> #include <mm_communication.h> +#include <tee.h> + +#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) +/** + * 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; +} + /** - * mm_communicate() - Adjust the cmonnucation buffer to StandAlonneMM and send + * 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);
/*

On Fri, Aug 04, 2023 at 02:33:44PM +0100, Abdellatif El Khlifi wrote:
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].
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Tested-by: Gowtham Suresh Kumar gowtham.sureshkumar@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Thanks for re-working things again.
Reviewed-by: Tom Rini trini@konsulko.com

On Fri, Aug 04, 2023 at 02:33:44PM +0100, Abdellatif El Khlifi wrote:
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].
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Tested-by: Gowtham Suresh Kumar gowtham.sureshkumar@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v19:
Tom:
- use CONFIG_FFA_SHARED_MM_BUF_* in place of macros
v18:
Ilias, Tom:
- drop the use of configs for the shared MM buffer, put back #ifdefs instead
- add test information to the commit log
Thanks for the quick rework. I like the fact you have the SMC as a fallback to FF-A in case it's not discovered. Once and if we get dynamic buffer allocation for the NS-world we can easily switch this to an automatically scanable feature again.
Tom, I am fine with the series now. I've reviewed the relevant EFI and asm bits, feel free to pull it in if the rest of the people are happy.
Reviewed-by: Ilias Apalodimas ilias.apalodimas@linaro.org
v17:
- show a debug message rather than an error when FF-A is not detected
v16:
- lib/efi_loader/Kconfig: rather than automatically selecting OPTEE and ARM_FFA_TRANSPORT configs by EFI_MM_COMM_TEE, set them as dependencies (Otherwise FF-A will be automatically enabled for boards that don't need it).
v15:
Simon:
- replace FFA_SHARED_MM_BUFFER_* defines with configs
v14:
Ilias:
- drop truncating var_payload->size when using FF-A
- map the MM SP return codes to errnos
v13:
- remove FF-A and Optee ifdefs
v12:
- drop use of calloc when querying SPs
- address nits
v11:
- rename select_ffa_mm_comms() to select_mm_comms()
- improve the logic of MM transport selection in mm_communicate()
- addressing nits
v10:
- use the FF-A driver Uclass operations
- use uclass_first_device()
- addressing nits
v9: align how FF-A is used with FF-A discovery through DM
v8:
- isolate the compilation choices between FF-A and OP-TEE
- update partition_info_get() second argument to be an SP count
- pass NULL device pointer to the FF-A bus discovery and operations
v7:
- set the MM door bell event to use 64-bit direct messaging
- issue a compile time error when one of these macros are not found : FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_OFFSET, FFA_SHARED_MM_BUFFER_ADDR
- make mm_sp_svc_uuid static
- replace EINVAL with ENOMEM in ffa_discover_mm_sp_id() when calloc() fails
- improve use of unmap_sysmem() in ffa_mm_communicate()
v6:
- add FF-A runtime discovery at MM communication level
- drop EFI runtime support for FF-A MM communication
- revert the changes in include/mm_communication.h for efi_mm_communicate_header and smm_variable_access structures
v4:
- use the new FF-A driver interfaces
- discover MM partitions at runtime
- copy FF-A driver private data to EFI runtime section at ExitBootServices()
- drop use of FFA_ERR_STAT_SUCCESS error code
- replace EFI_BUFFER_TOO_SMALL with EFI_OUT_OF_RESOURCES in ffa_mm_communicate(). No need for efi_memcpy_runtime() anymore
- revert the error log in mm_communicate() in case of failure
- remove packed attribute from efi_mm_communicate_header and smm_variable_communicate_header
v2:
- set default values to 0 for FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_ADDR and MM_SP_UUID_DATA and add warnings
v1:
- introduce FF-A MM communication
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 e65fbde60d..f38f1a5344 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. sughosh.ganu@linaro.org
- Copyright (C) 2020 Linaro Ltd. ilias.apalodimas@linaro.org
- Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
- Authors:
*/
Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
#ifndef _MM_COMMUNICATION_H_ @@ -13,6 +16,11 @@
#include <part_efi.h>
+#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 c5835e6ef6..852f9671d8 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 dfef18435d..09d03c0eee 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. sughosh.ganu@linaro.org
- Copyright (C) 2019 Linaro Ltd. ilias.apalodimas@linaro.org
- Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
- Authors:
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#include <common.h> +#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT) +#include <arm_ffa.h> +#endif +#include <cpu_func.h> +#include <dm.h> #include <efi.h> #include <efi_api.h> #include <efi_loader.h> #include <efi_variable.h> -#include <tee.h> #include <malloc.h> +#include <mapmem.h> #include <mm_communication.h> +#include <tee.h>
+#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) +/**
- 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;
+}
/**
- mm_communicate() - Adjust the cmonnucation buffer to StandAlonneMM and send
- 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);
/*
-- 2.25.1

Hi Ilias,
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. ...
Changelog:
v19:
Tom:
- use CONFIG_FFA_SHARED_MM_BUF_* in place of macros
v18:
Ilias, Tom:
- drop the use of configs for the shared MM buffer, put back #ifdefs instead
- add test information to the commit log
Thanks for the quick rework. I like the fact you have the SMC as a fallback to FF-A in case it's not discovered. Once and if we get dynamic buffer allocation for the NS-world we can easily switch this to an automatically scanable feature again.
Tom, I am fine with the series now. I've reviewed the relevant EFI and asm bits, feel free to pull it in if the rest of the people are happy.
Reviewed-by: Ilias Apalodimas ilias.apalodimas@linaro.org
You're welcome :)
Cheers, Abdellatif

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 abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v19:
Tom:
* set CONFIG_FFA_SHARED_MM_BUF_*
v18:
Ilias, Tom:
* drop use of CONFIG_FFA_SHARED_MM_BUF_*
v17:
* use savedefconfig to generate corstone1000_defconfig with FF-A MM comms enabled
v16:
* configs/corstone1000_defconfig: enable MM communication by setting the configs: ARM_FFA_TRANSPORT, OPTEE, TEE
v15:
Simon:
* use CONFIG_FFA_SHARED_MM_BUF_* configs in place of FFA_SHARED_MM_BUFFER_*
v13:
* remove FF-A config in the defconfig (because it's enabled automatically by CONFIG_EFI_MM_COMM_TEE)
v9:
* update copyright string
v8:
* drop OP-TEE configs from Corstone-1000 defconfig
v7:
* improve the definition of FFA_SHARED_MM_BUFFER_ADDR and FFA_SHARED_MM_BUFFER_OFFSET * update FFA_SHARED_MM_BUFFER_ADDR value
v6:
* corstone-1000: enable optee driver * corstone-1000: remove CONFIG_ARM_FFA_EFI_RUNTIME_MODE from the defconfig
v4:
* corstone-1000: turn on EFI MM communication
configs/corstone1000_defconfig | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-)
diff --git a/configs/corstone1000_defconfig b/configs/corstone1000_defconfig index a8a79fd105..83109b6a7e 100644 --- a/configs/corstone1000_defconfig +++ b/configs/corstone1000_defconfig @@ -28,12 +28,10 @@ CONFIG_CMD_FWU_METADATA=y CONFIG_CMD_BOOTZ=y CONFIG_SYS_BOOTM_LEN=0x800000 # CONFIG_CMD_XIMG is not set -CONFIG_CMD_NVMXIP=y CONFIG_CMD_GPT=y # CONFIG_RANDOM_UUID is not set CONFIG_CMD_LOADM=y # CONFIG_CMD_LOADS is not set -CONFIG_CMD_MMC=y CONFIG_CMD_USB=y # CONFIG_CMD_SETEXPR is not set # CONFIG_CMD_NFS is not set @@ -45,8 +43,7 @@ CONFIG_OF_CONTROL=y CONFIG_VERSION_VARIABLE=y CONFIG_NET_RANDOM_ETHADDR=y CONFIG_REGMAP=y -CONFIG_FWU_MDATA=y -CONFIG_FWU_MDATA_GPT_BLK=y +CONFIG_ARM_FFA_TRANSPORT=y CONFIG_MISC=y # CONFIG_MMC is not set CONFIG_NVMXIP_QSPI=y @@ -59,9 +56,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 -CONFIG_ERRNO_STR=y

On Fri, Aug 04, 2023 at 02:33:36PM +0100, Abdellatif El Khlifi wrote:
Adding support for Arm FF-A v1.0 (Arm Firmware Framework for Armv8-A) [A].
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.
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.
=> dm tree
Class Index Probed Driver Name
... firmware 0 [ + ] psci |-- psci ffa 0 [ ] arm_ffa | `-- arm_ffa ...
Clients are able to probe then use the FF-A bus by calling the DM class searching APIs (e.g: uclass_first_device).
This implementation of the specification provides support for Aarch64.
The FF-A driver uses the SMC ABIs defined by the FF-A specification to:
- Discover the presence of secure partitions (SPs) of interest - Access an SP's service through communication protocols (e.g: EFI MM communication protocol)
The FF-A support provides the following features:
- Being generic by design and can be used by any Arm 64-bit platform - FF-A support can be compiled and used without EFI - Support for SMCCCv1.2 x0-x17 registers - Support for SMC32 calling convention - Support for 32-bit and 64-bit FF-A direct messaging - Support for FF-A MM communication (compatible with EFI boot time) - Enabling FF-A and MM communication in Corstone1000 platform as a use case - 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. - Sandbox FF-A test cases. - A new command called armffa is provided as an example of how to access the FF-A bus
For more details about the FF-A support please refer to [B] and refer to [C] for how to use the armffa command.
Please find at [D] an example of the expected boot logs when enabling FF-A support for a platform. In this example the platform is Corstone1000. But it can be any Arm 64-bit platform.
Depending on further feedback, I will pick this up but likely just after -rc2. Given how long this has been going I will take it for v2023.10 all the same, thanks.

Hi Tom,
Adding support for Arm FF-A v1.0 (Arm Firmware Framework for Armv8-A) [A].
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.
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. ...
Depending on further feedback, I will pick this up but likely just after -rc2. Given how long this has been going I will take it for v2023.10 all the same, thanks.
Very much appreciated. Thank you :)
Cheers, Abdellatif

On Fri, Aug 04, 2023 at 02:33:36PM +0100, Abdellatif El Khlifi wrote:
Adding support for Arm FF-A v1.0 (Arm Firmware Framework for Armv8-A) [A].
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.
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.
=> dm tree
Class Index Probed Driver Name
... firmware 0 [ + ] psci |-- psci ffa 0 [ ] arm_ffa | `-- arm_ffa ...
Clients are able to probe then use the FF-A bus by calling the DM class searching APIs (e.g: uclass_first_device).
This implementation of the specification provides support for Aarch64.
The FF-A driver uses the SMC ABIs defined by the FF-A specification to:
- Discover the presence of secure partitions (SPs) of interest - Access an SP's service through communication protocols (e.g: EFI MM communication protocol)
The FF-A support provides the following features:
- Being generic by design and can be used by any Arm 64-bit platform - FF-A support can be compiled and used without EFI - Support for SMCCCv1.2 x0-x17 registers - Support for SMC32 calling convention - Support for 32-bit and 64-bit FF-A direct messaging - Support for FF-A MM communication (compatible with EFI boot time) - Enabling FF-A and MM communication in Corstone1000 platform as a use case - 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. - Sandbox FF-A test cases. - A new command called armffa is provided as an example of how to access the FF-A bus
For more details about the FF-A support please refer to [B] and refer to [C] for how to use the armffa command.
Please find at [D] an example of the expected boot logs when enabling FF-A support for a platform. In this example the platform is Corstone1000. But it can be any Arm 64-bit platform.
For the series, applied to u-boot/master, thanks!

Hi,
On Tue, 8 Aug 2023 at 13:25, Tom Rini trini@konsulko.com wrote:
On Fri, Aug 04, 2023 at 02:33:36PM +0100, Abdellatif El Khlifi wrote:
Adding support for Arm FF-A v1.0 (Arm Firmware Framework for Armv8-A) [A].
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.
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.
=> dm tree
Class Index Probed Driver Name
... firmware 0 [ + ] psci |-- psci ffa 0 [ ] arm_ffa | `-- arm_ffa ...
Clients are able to probe then use the FF-A bus by calling the DM class searching APIs (e.g: uclass_first_device).
This implementation of the specification provides support for Aarch64.
The FF-A driver uses the SMC ABIs defined by the FF-A specification to:
- Discover the presence of secure partitions (SPs) of interest - Access an SP's service through communication protocols (e.g: EFI MM communication protocol)
The FF-A support provides the following features:
- Being generic by design and can be used by any Arm 64-bit platform - FF-A support can be compiled and used without EFI - Support for SMCCCv1.2 x0-x17 registers - Support for SMC32 calling convention - Support for 32-bit and 64-bit FF-A direct messaging - Support for FF-A MM communication (compatible with EFI boot time) - Enabling FF-A and MM communication in Corstone1000 platform as a use case - 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. - Sandbox FF-A test cases. - A new command called armffa is provided as an example of how to access the FF-A bus
For more details about the FF-A support please refer to [B] and refer to [C] for how to use the armffa command.
Please find at [D] an example of the expected boot logs when enabling FF-A support for a platform. In this example the platform is Corstone1000. But it can be any Arm 64-bit platform.
For the series, applied to u-boot/master, thanks!
Oh dear. Running this I see this debug output. Please can you suppress it?
Emulated FF-A framework discovery FF-A emulator ready to use FF-A driver 1.0 FF-A framework 1.0 FF-A versions are compatible Test: dm_test_power_regulator_autoset_list: regulator.c (flat tree) Emulated FF-A framework discovery FF-A emulator ready to use FF-A driver 1.0 FF-A framework 1.0 FF-A versions are compatible Emulated FF-A framework discovery FF-A emulator ready to use FF-A driver 1.0 FF-A framework 1.0 FF-A versions are compatible
Regards, Simon

replace info logs with debug logs
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org --- doc/arch/arm64.ffa.rst | 2 +- drivers/firmware/arm-ffa/arm-ffa-uclass.c | 4 ++-- drivers/firmware/arm-ffa/arm-ffa.c | 2 +- drivers/firmware/arm-ffa/ffa-emul-uclass.c | 2 +- drivers/firmware/arm-ffa/sandbox_ffa.c | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst index 325fb80346..4ecdc31716 100644 --- a/doc/arch/arm64.ffa.rst +++ b/doc/arch/arm64.ffa.rst @@ -231,7 +231,7 @@ Please refer the command documentation at :doc:`../usage/cmd/armffa` Example of boot logs with FF-A enabled --------------------------------------
-For example, when using FF-A with Corstone-1000 the logs are as follows: +For example, when using FF-A with Corstone-1000, debug logs enabled, the output is as follows:
::
diff --git a/drivers/firmware/arm-ffa/arm-ffa-uclass.c b/drivers/firmware/arm-ffa/arm-ffa-uclass.c index ffa9d81fa7..8c17b19eaf 100644 --- a/drivers/firmware/arm-ffa/arm-ffa-uclass.c +++ b/drivers/firmware/arm-ffa/arm-ffa-uclass.c @@ -201,11 +201,11 @@ int ffa_get_version_hdlr(struct udevice *dev) 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", + log_debug("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"); + log_debug("FF-A versions are compatible\n");
if (dev) { uc_priv = dev_get_uclass_priv(dev); diff --git a/drivers/firmware/arm-ffa/arm-ffa.c b/drivers/firmware/arm-ffa/arm-ffa.c index 68df75bd9e..ee0bf9a55b 100644 --- a/drivers/firmware/arm-ffa/arm-ffa.c +++ b/drivers/firmware/arm-ffa/arm-ffa.c @@ -42,7 +42,7 @@ static bool arm_ffa_discover(struct udevice *dev) { int ret;
- log_info("Arm FF-A framework discovery\n"); + log_debug("Arm FF-A framework discovery\n");
ret = ffa_get_version_hdlr(dev); if (ret) diff --git a/drivers/firmware/arm-ffa/ffa-emul-uclass.c b/drivers/firmware/arm-ffa/ffa-emul-uclass.c index 5562bbaac3..4bf9f6041f 100644 --- a/drivers/firmware/arm-ffa/ffa-emul-uclass.c +++ b/drivers/firmware/arm-ffa/ffa-emul-uclass.c @@ -695,7 +695,7 @@ int ffa_emul_find(struct udevice *dev, struct udevice **emulp) return ret; }
- log_info("FF-A emulator ready to use\n"); + log_debug("FF-A emulator ready to use\n");
return 0; } diff --git a/drivers/firmware/arm-ffa/sandbox_ffa.c b/drivers/firmware/arm-ffa/sandbox_ffa.c index ef9491ccea..11142429c0 100644 --- a/drivers/firmware/arm-ffa/sandbox_ffa.c +++ b/drivers/firmware/arm-ffa/sandbox_ffa.c @@ -30,7 +30,7 @@ static int sandbox_ffa_discover(struct udevice *dev) int ret; struct udevice *emul;
- log_info("Emulated FF-A framework discovery\n"); + log_debug("Emulated FF-A framework discovery\n");
ret = ffa_emul_find(dev, &emul); if (ret) {

On Wed, 9 Aug 2023 at 05:47, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
replace info logs with debug logs
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org
doc/arch/arm64.ffa.rst | 2 +- drivers/firmware/arm-ffa/arm-ffa-uclass.c | 4 ++-- drivers/firmware/arm-ffa/arm-ffa.c | 2 +- drivers/firmware/arm-ffa/ffa-emul-uclass.c | 2 +- drivers/firmware/arm-ffa/sandbox_ffa.c | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-)
Reviewed-by: Simon Glass sjg@chromium.org
Thanks, Simon

On Wed, Aug 09, 2023 at 12:47:30PM +0100, Abdellatif El Khlifi wrote:
replace info logs with debug logs
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Reviewed-by: Simon Glass sjg@chromium.org
Applied to u-boot/master, thanks!

Hi,
On Tue, 1 Aug 2023 at 10:19, Tom Rini trini@konsulko.com wrote:
On Tue, Aug 01, 2023 at 05:10:08PM +0100, Abdellatif El Khlifi wrote:
Hi guys,
On Tue, Aug 01, 2023 at 11:00:57AM -0400, Tom Rini wrote:
> > > > ... > > > > Changelog: > > > > =============== > > > > > > > > v17: > > > > > > > > * show a debug message rather than an error when FF-A is not detected > > > [snip] > > > > diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig > > > > index c5835e6ef6..8fbadb9201 100644 > > > > --- a/lib/efi_loader/Kconfig > > > > +++ b/lib/efi_loader/Kconfig > > > > @@ -55,13 +55,53 @@ 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" > > > > - depends on OPTEE > > > > + bool "UEFI variables storage service via the trusted world" > > > > + depends on OPTEE && ARM_FFA_TRANSPORT > > > > > > You didn't get my changes in here however. If you can do EFI_MM_COMM_TEE > > > without ARM_FFA_TRANSPORT (as lx2160ardb_tfa_stmm_defconfig does) then > > > you don't make this option depend on . If FF-A is only > > > for use here, you make FF-A depend on this, and the FF-A specific > > > variable depend on ARM_FFA_TRANSPORT. > > > > Abdellatif hinted at what's going on here. When I added this Kconfig > > option to lx2160 FF-A wasn't implemented yet. > > The defconfig has existed since May 2020, which is when you added > EFI_MM_COMM_TEE itself too. So I think it's that no one did the check I > did until now and saw this series was disabling what was on the other > platform. > > > Since FF-A isn't a new > > communication mechanism but builds upon the existing SMCs to build an > > easier API, I asked Abdellatif to hide this complexity. > > We had two options, either make Kconfig options for either FF-A or the > > traditional SMCs and remove the dependencies, or piggyback on FF-As > > discovery mechanism and make the choice at runtime. The latter has a > > small impact on code size, but imho makes developers' life a lot > > easier. > > I'm not sure how much you can do a run-time option here since you're > setting a bunch of default values for FF-A to 0 in Kconfig. If we're > supposed to be able to get them at run time, we shouldn't need a Kconfig > option at all. I'm also not sure how valid a use case it is where we > won't know at build time what the rest of the firmware stack supports > here. >
That's a fair point. FF-A in theory has APIs to discover memory. Abdellatif, why do we need the Kconfigs for shared memory on FF-A?
The statically carved out MM shared buffer address, size and offset cannot be discovered by FF-A ABIs. The MM communication driver in U-Boot could allocate the buffer and share it with the MM SP but we do not implement that support currently in either U-Boot or UEFI.
Ok, that's a bit unfortunate, but Tom is right. Having the FF-A addresses show up is as confusing as having Kconfig options for discrete options. The whole point of my suggestion was to make users' lives easier. Apologies for the confusion but can you bring back the ifdefs? Looking at the patch this should be minimal just use ifdef ARM_FFA_TRANSPORT and ifndef ARM_FFA_TRANSPORT.
Tom you prefer that as well?
Pending an answer to Jens' feedback, yes, going back to #ifdef's is fine, especially since default values of 0 are nonsense in this case (and as Heinrich's patch re SYS_MALLOC_LEN shows, dangerous since 0 != 0x0 once we do string comparisons).
I'd like to give some context why it's important for Corstone-1000 platform that the DT passed to the kernel matches the official kernel DT.
Note that we've set aside the "should this be in DT or not" question.
There is a SystemReady IR 2.0 test checking the DT. It compares the DT passed by U-Boot with a reference DT (the kernel DT) . The test fails if there is a mismatch. So, if we add a DT node in U-Boot and the node is not upstreamed to the kernel DT, the DT test will fail.
This is overall good and progress.
Yes, great to hear.
To be approved by the kernel DT maintainers, the node should have a use case in the kernel which is not the case.
This is, I believe / hope wrong. It needs to be in the dt-schema repository, not strictly "the kernel". For example, bootph-all (etc) are in dt-schema and so can be in the upstream kernel but are not used in the kernel itself.
Indeed. How do we get this message out, since there still seems to be some confusion here. Should we do an announcement on LKML, perhaps?
There is a solution for this which is deleting the node we don't want to pass to the kernel using delete-node in the U-Boot DT.
Something like this will likely be needed, in the end, at least for some cases. But the goal is that everything gets in to dt-schema.
[snip]
With this we can get rid of the configs and the #defines: FFA_SHARED_MM_BUF_ADDR, FFA_SHARED_MM_BUF_OFFSET and FFA_SHARED_MM_BUF_SIZE.
Also, we will avoid setting 0 as default values for the address, size and offset.
We just need to not have default values offered. The symbols just need to depend on FFA so that they aren't asked when not used.
2/ the FF-A specific code in efi_variable_tee.c will try to find the mm-comms-buf reserved memory node. When found, it reads the buffer address, size and offset.
3/ adding #ifdef CONFIG_ARM_FFA_TRANSPORT in lib/efi_loader/efi_variable_tee.c for the FF-A specific code.
4/ make EFI_MM_COMM_TEE depends on OPTEE only
What do you think guys ?
Yes, we need to do 3 and 4.
Regards, Simon

Hi Abdellatif,
On Mon, Jul 31, 2023 at 1:46 PM Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
Hi Ilias,
On Mon, Jul 31, 2023 at 12:38:16PM +0300, Ilias Apalodimas wrote:
... Changelog: ===============
v17:
- show a debug message rather than an error when FF-A is not detected
[snip]
diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index c5835e6ef6..8fbadb9201 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -55,13 +55,53 @@ 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"
depends on OPTEE
bool "UEFI variables storage service via the trusted world"
depends on OPTEE && ARM_FFA_TRANSPORT
You didn't get my changes in here however. If you can do EFI_MM_COMM_TEE without ARM_FFA_TRANSPORT (as lx2160ardb_tfa_stmm_defconfig does) then you don't make this option depend on . If FF-A is only for use here, you make FF-A depend on this, and the FF-A specific variable depend on ARM_FFA_TRANSPORT.
Abdellatif hinted at what's going on here. When I added this Kconfig option to lx2160 FF-A wasn't implemented yet.
The defconfig has existed since May 2020, which is when you added EFI_MM_COMM_TEE itself too. So I think it's that no one did the check I did until now and saw this series was disabling what was on the other platform.
Since FF-A isn't a new communication mechanism but builds upon the existing SMCs to build an easier API, I asked Abdellatif to hide this complexity. We had two options, either make Kconfig options for either FF-A or the traditional SMCs and remove the dependencies, or piggyback on FF-As discovery mechanism and make the choice at runtime. The latter has a small impact on code size, but imho makes developers' life a lot easier.
I'm not sure how much you can do a run-time option here since you're setting a bunch of default values for FF-A to 0 in Kconfig. If we're supposed to be able to get them at run time, we shouldn't need a Kconfig option at all. I'm also not sure how valid a use case it is where we won't know at build time what the rest of the firmware stack supports here.
That's a fair point. FF-A in theory has APIs to discover memory. Abdellatif, why do we need the Kconfigs for shared memory on FF-A?
The statically carved out MM shared buffer address, size and offset cannot be discovered by FF-A ABIs. The MM communication driver in U-Boot could allocate the buffer and share it with the MM SP but we do not implement that support currently in either U-Boot or UEFI.
Simon suggested we use build configs to set the buffer address, size and offset since we don't want a DT node for the MM firmware.
In the OP-TEE driver we're allocating memory to share dynamically using malloc() or memalign(). Why isn't the same approach possible here?
Thanks, Jens

Hi Jens,
On Tue, Aug 01, 2023 at 02:28:34PM +0200, Jens Wiklander wrote:
> ... > Changelog: > =============== > > v17: > > * show a debug message rather than an error when FF-A is not detected [snip] > diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig > index c5835e6ef6..8fbadb9201 100644 > --- a/lib/efi_loader/Kconfig > +++ b/lib/efi_loader/Kconfig > @@ -55,13 +55,53 @@ 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" > - depends on OPTEE > + bool "UEFI variables storage service via the trusted world" > + depends on OPTEE && ARM_FFA_TRANSPORT
You didn't get my changes in here however. If you can do EFI_MM_COMM_TEE without ARM_FFA_TRANSPORT (as lx2160ardb_tfa_stmm_defconfig does) then you don't make this option depend on . If FF-A is only for use here, you make FF-A depend on this, and the FF-A specific variable depend on ARM_FFA_TRANSPORT.
Abdellatif hinted at what's going on here. When I added this Kconfig option to lx2160 FF-A wasn't implemented yet.
The defconfig has existed since May 2020, which is when you added EFI_MM_COMM_TEE itself too. So I think it's that no one did the check I did until now and saw this series was disabling what was on the other platform.
Since FF-A isn't a new communication mechanism but builds upon the existing SMCs to build an easier API, I asked Abdellatif to hide this complexity. We had two options, either make Kconfig options for either FF-A or the traditional SMCs and remove the dependencies, or piggyback on FF-As discovery mechanism and make the choice at runtime. The latter has a small impact on code size, but imho makes developers' life a lot easier.
I'm not sure how much you can do a run-time option here since you're setting a bunch of default values for FF-A to 0 in Kconfig. If we're supposed to be able to get them at run time, we shouldn't need a Kconfig option at all. I'm also not sure how valid a use case it is where we won't know at build time what the rest of the firmware stack supports here.
That's a fair point. FF-A in theory has APIs to discover memory. Abdellatif, why do we need the Kconfigs for shared memory on FF-A?
The statically carved out MM shared buffer address, size and offset cannot be discovered by FF-A ABIs. The MM communication driver in U-Boot could allocate the buffer and share it with the MM SP but we do not implement that support currently in either U-Boot or UEFI.
Simon suggested we use build configs to set the buffer address, size and offset since we don't want a DT node for the MM firmware.
In the OP-TEE driver we're allocating memory to share dynamically using malloc() or memalign(). Why isn't the same approach possible here?
FF-A memory sharing can not be used because the MM SP needs to know the layout of the MM communication buffer during initialisation. This happens before the Normal world has got a chance to run. So we have a chicken and egg and Normal World cannot use FF-A ABIs to share the buffer as things stand.
Cheers, Abdellatif

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 abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v17:
* use savedefconfig to generate corstone1000_defconfig with FF-A MM comms enabled
v16:
* configs/corstone1000_defconfig: enable MM communication by setting the configs: ARM_FFA_TRANSPORT, OPTEE, TEE
v15:
Simon:
* use CONFIG_FFA_SHARED_MM_BUF_* configs in place of FFA_SHARED_MM_BUFFER_*
v13:
* remove FF-A config in the defconfig (because it's enabled automatically by CONFIG_EFI_MM_COMM_TEE)
v9:
* update copyright string
v8:
* drop OP-TEE configs from Corstone-1000 defconfig
v7:
* improve the definition of FFA_SHARED_MM_BUFFER_ADDR and FFA_SHARED_MM_BUFFER_OFFSET * update FFA_SHARED_MM_BUFFER_ADDR value
v6:
* corstone-1000: enable optee driver * corstone-1000: remove CONFIG_ARM_FFA_EFI_RUNTIME_MODE from the defconfig
v4:
* corstone-1000: turn on EFI MM communication
configs/corstone1000_defconfig | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/configs/corstone1000_defconfig b/configs/corstone1000_defconfig index a8a79fd105..67684e114d 100644 --- a/configs/corstone1000_defconfig +++ b/configs/corstone1000_defconfig @@ -28,12 +28,10 @@ CONFIG_CMD_FWU_METADATA=y CONFIG_CMD_BOOTZ=y CONFIG_SYS_BOOTM_LEN=0x800000 # CONFIG_CMD_XIMG is not set -CONFIG_CMD_NVMXIP=y CONFIG_CMD_GPT=y # CONFIG_RANDOM_UUID is not set CONFIG_CMD_LOADM=y # CONFIG_CMD_LOADS is not set -CONFIG_CMD_MMC=y CONFIG_CMD_USB=y # CONFIG_CMD_SETEXPR is not set # CONFIG_CMD_NFS is not set @@ -45,8 +43,7 @@ CONFIG_OF_CONTROL=y CONFIG_VERSION_VARIABLE=y CONFIG_NET_RANDOM_ETHADDR=y CONFIG_REGMAP=y -CONFIG_FWU_MDATA=y -CONFIG_FWU_MDATA_GPT_BLK=y +CONFIG_ARM_FFA_TRANSPORT=y CONFIG_MISC=y # CONFIG_MMC is not set CONFIG_NVMXIP_QSPI=y @@ -59,9 +56,14 @@ 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_ADDR=0x02000000 CONFIG_EFI_CAPSULE_ON_DISK=y CONFIG_EFI_IGNORE_OSINDICATIONS=y CONFIG_FWU_MULTI_BANK_UPDATE=y -CONFIG_ERRNO_STR=y

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 abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
---
Changelog: ===============
v12:
* remove reparenting by making the emulator parent of the FF-A device in the DT * add invoke_ffa_fn() * address nits
v11:
* rename ffa_try_discovery() to sandbox_ffa_discover() * rename sandbox_ffa_query_core_state() to sandbox_query_ffa_emul_state() * store the sandbox emulator pointer in the FF-A device uc_priv (struct ffa_priv) * set the emulator as parent of the sandbox FF-A device
v10:
* split the FF-A sandbox support into an emulator and a driver * read FFA_VERSION and FFA_PARTITION_INFO_GET state using sandbox_ffa_query_core_state() * drop CONFIG_SANDBOX_FFA config * address nits
v9: align FF-A sandbox driver with FF-A discovery through DM
v8: update ffa_bus_prvdata_get() to return a pointer rather than a pointer address
v7: state that sandbox driver supports only 64-bit direct messaging
v4: align sandbox driver with the new FF-A driver interfaces and new way of error handling
v1: introduce the sandbox driver
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 0d960731cf..dcdb0e9362 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -269,12 +269,13 @@ F: configs/cortina_presidio-asic-pnand_defconfig ARM FF-A M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com 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: include/sandbox_arm_ffa.h
ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de diff --git a/arch/sandbox/dts/sandbox.dtsi b/arch/sandbox/dts/sandbox.dtsi index 30a305c4d2..94a08814b8 100644 --- a/arch/sandbox/dts/sandbox.dtsi +++ b/arch/sandbox/dts/sandbox.dtsi @@ -445,6 +445,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 ff9f9222e6..96b5404991 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -1820,6 +1820,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 0000000000..be2790f496 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __SANDBOX_ARM_FFA_H +#define __SANDBOX_ARM_FFA_H + +#include <arm_ffa.h> + +/* + * 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 0000000000..b0881822d7 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __SANDBOX_ARM_FFA_PRV_H +#define __SANDBOX_ARM_FFA_PRV_H + +#include <arm_ffa_priv.h> + +/* 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 98b3e0cda4..72ea3d21ab 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 1ec44d5b33..8269bec879 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -344,3 +344,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 aefd527447..b7c754fa3d 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. @@ -205,6 +208,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 + The armffa command -----------------------------------
diff --git a/doc/arch/sandbox/sandbox.rst b/doc/arch/sandbox/sandbox.rst index 77ca6bc4cc..a3631de749 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 a7d5392859..d75f8b53fd 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 imply CMD_ARMFFA select LIB_UUID select DEVRES @@ -33,5 +33,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 11b1766285..318123a7f4 100644 --- a/drivers/firmware/arm-ffa/Makefile +++ b/drivers/firmware/arm-ffa/Makefile @@ -5,4 +5,12 @@ # Authors: # Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
-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 0000000000..5562bbaac3 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ +#include <common.h> +#include <dm.h> +#include <mapmem.h> +#include <string.h> +#include <asm/global_data.h> +#include <asm/sandbox_arm_ffa.h> +#include <asm/sandbox_arm_ffa_priv.h> +#include <dm/device-internal.h> +#include <dm/lists.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> + +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 4338f9c9b1..0000000000 --- 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 open-source-office@arm.com - * - * Authors: - * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com - */ - -#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 0000000000..ef9491ccea --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ +#include <common.h> +#include <arm_ffa.h> +#include <dm.h> +#include <log.h> +#include <asm/global_data.h> +#include <asm/sandbox_arm_ffa_priv.h> +#include <dm/device-internal.h> +#include <linux/errno.h> + +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 3c6af2e3d2..0432c95c9e 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 */

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 abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org Cc: Heinrich Schuchardt xypron.glpk@gmx.de
---
Changelog: ===============
v12:
* remove use of dscvry_info * drop use of calloc when querying SPs * address nits
v11:
* drop unmapping test (taken care of by the DM when removing the device) * address nits
v10:
* use the FF-A driver Uclass operations * use uclass_first_device() * replace CONFIG_SANDBOX_FFA with CONFIG_ARM_FFA_TRANSPORT * address nits
v9: align FF-A sandbox tests with FF-A discovery through DM
v8:
* update partition_info_get() second argument to be an SP count * pass NULL device pointer to the FF-A bus discovery and operations
v7: set the tests to use 64-bit direct messaging
v4: align sandbox tests with the new FF-A driver interfaces and new way of error handling
v1: introduce sandbox tests
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 dcdb0e9362..8b32e66fa9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -276,6 +276,7 @@ F: doc/arch/arm64.ffa.rst F: doc/usage/cmd/armffa.rst F: drivers/firmware/arm-ffa/ F: include/arm_ffa.h +F: test/dm/ffa.c
ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de diff --git a/doc/arch/arm64.ffa.rst b/doc/arch/arm64.ffa.rst index b7c754fa3d..325fb80346 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 3799b1ae8f..7ed00733c1 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 open-source-office@arm.com +# Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com
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 0000000000..6912666bb4 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <console.h> +#include <dm.h> +#include <asm/sandbox_arm_ffa.h> +#include <asm/sandbox_arm_ffa_priv.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +/* 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);

Add Sandbox test for the armffa command
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Reviewed-by: Simon Glass sjg@chromium.org Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v12:
* address nits
v10:
* replace CMD_RET_SUCCESS with 0 * replace CONFIG_SANDBOX_FFA with CONFIG_ARM_FFA_TRANSPORT
v9: align the test with FF-A discovery through DM
v4: drop use of helper APIs
v1: introduce armffa command sandbox test
MAINTAINERS | 1 + test/cmd/Makefile | 2 ++ test/cmd/armffa.c | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+) create mode 100644 test/cmd/armffa.c
diff --git a/MAINTAINERS b/MAINTAINERS index 8b32e66fa9..1c2d1fcf22 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -276,6 +276,7 @@ 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/test/cmd/Makefile b/test/cmd/Makefile index 055adc65a2..1d1dbb4fbc 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 open-source-office@arm.com
ifdef CONFIG_HUSH_PARSER obj-$(CONFIG_CONSOLE_RECORD) += test_echo.o @@ -23,6 +24,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 0000000000..9a44a397e8 --- /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 open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <string.h> +#include <asm/sandbox_arm_ffa.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +/* Basic test of 'armffa' command */ +static int dm_test_armffa_cmd(struct unit_test_state *uts) +{ + /* armffa getpart <UUID> */ + ut_assertok(run_command("armffa getpart " SANDBOX_SERVICE1_UUID, 0)); + + /* armffa ping <ID> */ + 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);

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.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Signed-off-by: Gowtham Suresh Kumar gowtham.sureshkumar@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v13:
* remove FF-A and Optee ifdefs
v12:
* drop use of calloc when querying SPs * address nits
v11:
* rename select_ffa_mm_comms() to select_mm_comms() * improve the logic of MM transport selection in mm_communicate() * addressing nits
v10:
* use the FF-A driver Uclass operations * use uclass_first_device() * addressing nits
v9: align how FF-A is used with FF-A discovery through DM
v8:
* isolate the compilation choices between FF-A and OP-TEE * update partition_info_get() second argument to be an SP count * pass NULL device pointer to the FF-A bus discovery and operations
v7:
* set the MM door bell event to use 64-bit direct messaging * issue a compile time error when one of these macros are not found : FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_OFFSET, FFA_SHARED_MM_BUFFER_ADDR * make mm_sp_svc_uuid static * replace EINVAL with ENOMEM in ffa_discover_mm_sp_id() when calloc() fails * improve use of unmap_sysmem() in ffa_mm_communicate()
v6:
* add FF-A runtime discovery at MM communication level * drop EFI runtime support for FF-A MM communication * revert the changes in include/mm_communication.h for efi_mm_communicate_header and smm_variable_access structures
v4:
* use the new FF-A driver interfaces * discover MM partitions at runtime * copy FF-A driver private data to EFI runtime section at ExitBootServices() * drop use of FFA_ERR_STAT_SUCCESS error code * replace EFI_BUFFER_TOO_SMALL with EFI_OUT_OF_RESOURCES in ffa_mm_communicate(). No need for efi_memcpy_runtime() anymore * revert the error log in mm_communicate() in case of failure * remove packed attribute from efi_mm_communicate_header and smm_variable_communicate_header
v2:
* set default values to 0 for FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_ADDR and MM_SP_UUID_DATA and add warnings
v1:
* introduce FF-A MM communication
include/mm_communication.h | 13 ++ lib/efi_loader/Kconfig | 16 +- lib/efi_loader/efi_variable_tee.c | 260 +++++++++++++++++++++++++++++- 3 files changed, 282 insertions(+), 7 deletions(-)
diff --git a/include/mm_communication.h b/include/mm_communication.h index e65fbde60d..f17847583b 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. sughosh.ganu@linaro.org * Copyright (C) 2020 Linaro Ltd. ilias.apalodimas@linaro.org + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#ifndef _MM_COMMUNICATION_H_ @@ -13,6 +16,9 @@
#include <part_efi.h>
+/* MM service UUID string (big-endian format). This UUID is common across all MM SPs */ +#define MM_SP_UUID "33d532ed-e699-0942-c09c-a798d9cd722d" + /* * Interface to the pseudo Trusted Application (TA), which provides a * communication channel with the Standalone MM (Management Mode) @@ -248,4 +254,11 @@ struct smm_variable_var_check_property { u16 name[]; };
+/* supported MM transports */ +enum mm_comms_select { + MM_COMMS_UNDEFINED, + MM_COMMS_FFA, + MM_COMMS_OPTEE +}; + #endif /* _MM_COMMUNICATION_H_ */ diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index c5835e6ef6..cb26e110fd 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -55,13 +55,25 @@ 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" - depends on OPTEE + bool "UEFI variables storage service via the trusted world" + select ARM_FFA_TRANSPORT + select TEE + select 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 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 dfef18435d..b4d1b979b7 100644 --- a/lib/efi_loader/efi_variable_tee.c +++ b/lib/efi_loader/efi_variable_tee.c @@ -4,17 +4,45 @@ * * Copyright (C) 2019 Linaro Ltd. sughosh.ganu@linaro.org * Copyright (C) 2019 Linaro Ltd. ilias.apalodimas@linaro.org + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> +#include <arm_ffa.h> +#include <cpu_func.h> +#include <dm.h> #include <efi.h> #include <efi_api.h> #include <efi_loader.h> #include <efi_variable.h> -#include <tee.h> #include <malloc.h> +#include <mapmem.h> #include <mm_communication.h> +#include <tee.h> + +#ifndef FFA_SHARED_MM_BUFFER_SIZE +#error "FFA_SHARED_MM_BUFFER_SIZE must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_SIZE 0 +#endif + +#ifndef FFA_SHARED_MM_BUFFER_OFFSET +#error "FFA_SHARED_MM_BUFFER_OFFSET must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_OFFSET 0 +#endif + +#ifndef FFA_SHARED_MM_BUFFER_ADDR +#error "FFA_SHARED_MM_BUFFER_ADDR must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_ADDR 0 +#endif + +/* MM return codes */ +#define MM_SUCCESS (0)
+static const char *mm_sp_svc_uuid = MM_SP_UUID; +static u16 mm_sp_id; extern struct efi_var_file __efi_runtime_data *efi_var_buf; static efi_uintn_t max_buffer_size; /* comm + var + func + data */ static efi_uintn_t max_payload_size; /* func + data */ @@ -145,16 +173,226 @@ static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize) }
/** - * 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 = -1; + 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 = FFA_SHARED_MM_BUFFER_OFFSET; /* x3 */ + + ret = ffa_sync_send_receive(dev, mm_sp_id, &msg, 1); + if (ret) + return ret; + + sp_event_ret = msg.data0; /* x3 */ + + if (sp_event_ret == MM_SUCCESS) + return 0; + + /* Failure to notify the MM SP */ + + return -EACCES; +} + +/** + * 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 > FFA_SHARED_MM_BUFFER_SIZE) + return EFI_INVALID_PARAMETER; + + /* Copy the data to the shared buffer */ + + virt_shared_buf = map_sysmem((phys_addr_t)FFA_SHARED_MM_BUFFER_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_err("EFI: Cannot find FF-A bus device, trying Optee comms\n"); + return MM_COMMS_OPTEE; + } + + return MM_COMMS_FFA; +} + +/** + * 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) { efi_status_t ret; + enum mm_comms_select mm_comms; struct efi_mm_communicate_header *mm_hdr; struct smm_variable_communicate_header *var_hdr;
@@ -162,7 +400,12 @@ static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) 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); + 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); + if (ret != EFI_SUCCESS) { log_err("%s failed!\n", __func__); return ret; @@ -232,6 +475,7 @@ static u8 *setup_mm_hdr(void **dptr, efi_uintn_t payload_size, */ efi_status_t EFIAPI get_max_payload(efi_uintn_t *size) { + enum mm_comms_select mm_comms; struct smm_variable_payload_size *var_payload = NULL; efi_uintn_t payload_size; u8 *comm_buf = NULL; @@ -258,6 +502,12 @@ efi_status_t EFIAPI get_max_payload(efi_uintn_t *size) goto out; } *size = var_payload->size; + + mm_comms = get_mm_comms(); + if (mm_comms == MM_COMMS_FFA && *size > FFA_SHARED_MM_BUFFER_SIZE) + *size = FFA_SHARED_MM_BUFFER_SIZE - MM_COMMUNICATE_HEADER_SIZE - + MM_VARIABLE_COMMUNICATE_SIZE; + /* * There seems to be a bug in EDK2 miscalculating the boundaries and * size checks, so deduct 2 more bytes to fulfill this requirement. Fix @@ -697,7 +947,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);
/*

On Fri, 16 Jun 2023 at 16:28, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
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.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Signed-off-by: Gowtham Suresh Kumar gowtham.sureshkumar@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v13:
- remove FF-A and Optee ifdefs
v12:
- drop use of calloc when querying SPs
- address nits
v11:
- rename select_ffa_mm_comms() to select_mm_comms()
- improve the logic of MM transport selection in mm_communicate()
- addressing nits
v10:
- use the FF-A driver Uclass operations
- use uclass_first_device()
- addressing nits
v9: align how FF-A is used with FF-A discovery through DM
v8:
- isolate the compilation choices between FF-A and OP-TEE
- update partition_info_get() second argument to be an SP count
- pass NULL device pointer to the FF-A bus discovery and operations
v7:
- set the MM door bell event to use 64-bit direct messaging
- issue a compile time error when one of these macros are not found : FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_OFFSET, FFA_SHARED_MM_BUFFER_ADDR
- make mm_sp_svc_uuid static
- replace EINVAL with ENOMEM in ffa_discover_mm_sp_id() when calloc() fails
- improve use of unmap_sysmem() in ffa_mm_communicate()
v6:
- add FF-A runtime discovery at MM communication level
- drop EFI runtime support for FF-A MM communication
- revert the changes in include/mm_communication.h for efi_mm_communicate_header and smm_variable_access structures
v4:
- use the new FF-A driver interfaces
- discover MM partitions at runtime
- copy FF-A driver private data to EFI runtime section at ExitBootServices()
- drop use of FFA_ERR_STAT_SUCCESS error code
- replace EFI_BUFFER_TOO_SMALL with EFI_OUT_OF_RESOURCES in ffa_mm_communicate(). No need for efi_memcpy_runtime() anymore
- revert the error log in mm_communicate() in case of failure
- remove packed attribute from efi_mm_communicate_header and smm_variable_communicate_header
v2:
- set default values to 0 for FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_ADDR and MM_SP_UUID_DATA and add warnings
v1:
- introduce FF-A MM communication
include/mm_communication.h | 13 ++ lib/efi_loader/Kconfig | 16 +- lib/efi_loader/efi_variable_tee.c | 260 +++++++++++++++++++++++++++++- 3 files changed, 282 insertions(+), 7 deletions(-)
Reviewed-by: Simon Glass sjg@chromium.org

Hi Abdellatif,
On Fri, Jun 16, 2023 at 04:28:16PM +0100, Abdellatif El Khlifi wrote:
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.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Signed-off-by: Gowtham Suresh Kumar gowtham.sureshkumar@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v13:
- remove FF-A and Optee ifdefs
Thanks this looks a lot saner now. I got one last nit and I think this patch is ready
- Return:
- 0 on success
- */
+static int ffa_notify_mm_sp(void) +{
- struct ffa_send_direct_data msg = {0};
- int ret;
- int sp_event_ret = -1;
- 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 = FFA_SHARED_MM_BUFFER_OFFSET; /* x3 */
- ret = ffa_sync_send_receive(dev, mm_sp_id, &msg, 1);
- if (ret)
return ret;
- sp_event_ret = msg.data0; /* x3 */
- if (sp_event_ret == MM_SUCCESS)
return 0;
- /* Failure to notify the MM SP */
- return -EACCES;
Doesn't FFA and the SMM_GATEWAY have discrete returns results that would make more sense? Your other patches only define MM_SUCCESS but in ffa_mm_communicate() you are trying to map ernnos to EFI return codes. I think we should map errnos to ffa errors as well in a similar fashion.
You can look at optee_mm_communicate() which already does that.
+}
+/**
- ffa_discover_mm_sp_id() - Query the MM partition ID
+/**
- 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_err("EFI: Cannot find FF-A bus device, trying Optee comms\n");
return MM_COMMS_OPTEE;
- }
- return MM_COMMS_FFA;
+}
+/**
- 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) { efi_status_t ret;
- enum mm_comms_select mm_comms; struct efi_mm_communicate_header *mm_hdr; struct smm_variable_communicate_header *var_hdr;
@@ -162,7 +400,12 @@ static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) 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);
- 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);
- if (ret != EFI_SUCCESS) { log_err("%s failed!\n", __func__); return ret;
@@ -232,6 +475,7 @@ static u8 *setup_mm_hdr(void **dptr, efi_uintn_t payload_size, */ efi_status_t EFIAPI get_max_payload(efi_uintn_t *size) {
- enum mm_comms_select mm_comms; struct smm_variable_payload_size *var_payload = NULL; efi_uintn_t payload_size; u8 *comm_buf = NULL;
@@ -258,6 +502,12 @@ efi_status_t EFIAPI get_max_payload(efi_uintn_t *size) goto out; } *size = var_payload->size;
- mm_comms = get_mm_comms();
- if (mm_comms == MM_COMMS_FFA && *size > FFA_SHARED_MM_BUFFER_SIZE)
*size = FFA_SHARED_MM_BUFFER_SIZE - MM_COMMUNICATE_HEADER_SIZE -
MM_VARIABLE_COMMUNICATE_SIZE;
Can you please move this check? The check preceding this is generic -- it tries to make sure there's space for at least a variable. This is ffa specific, so is there any reason ffa_mm_communicate() doesn't return the corrected size?
/* * There seems to be a bug in EDK2 miscalculating the boundaries and * size checks, so deduct 2 more bytes to fulfill this requirement. Fix @@ -697,7 +947,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);
/*
-- 2.25.1
Thanks /Ilias

turn on EFI MM communication
On corstone1000 platform MM communication between u-boot and the secure world (Optee) is done using the FF-A bus.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v13:
* remove FF-A config in the defconfig (because it's enabled automatically by CONFIG_EFI_MM_COMM_TEE)
v9:
* update copyright string
v8:
* drop OP-TEE configs from Corstone-1000 defconfig
v7:
* improve the definition of FFA_SHARED_MM_BUFFER_ADDR and FFA_SHARED_MM_BUFFER_OFFSET * update FFA_SHARED_MM_BUFFER_ADDR value
v6:
* corstone-1000: enable optee driver * corstone-1000: remove CONFIG_ARM_FFA_EFI_RUNTIME_MODE from the defconfig
v4:
* corstone-1000: turn on EFI MM communication
configs/corstone1000_defconfig | 1 + include/configs/corstone1000.h | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/configs/corstone1000_defconfig b/configs/corstone1000_defconfig index 2d391048cd..e1f63237c4 100644 --- a/configs/corstone1000_defconfig +++ b/configs/corstone1000_defconfig @@ -53,3 +53,4 @@ CONFIG_DM_SERIAL=y CONFIG_USB=y CONFIG_USB_ISP1760=y CONFIG_ERRNO_STR=y +CONFIG_EFI_MM_COMM_TEE=y diff --git a/include/configs/corstone1000.h b/include/configs/corstone1000.h index 3347c11792..4ef1f05e40 100644 --- a/include/configs/corstone1000.h +++ b/include/configs/corstone1000.h @@ -1,9 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0+ */ /* - * (C) Copyright 2022 ARM Limited * (C) Copyright 2022 Linaro * Rui Miguel Silva rui.silva@linaro.org - * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + * Copyright 2022-2023 Arm Limited and/or its affiliates open-source-office@arm.com + * + * Authors: + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com * * Configuration for Corstone1000. Parts were derived from other ARM * configurations. @@ -14,6 +16,15 @@
#include <linux/sizes.h>
+#define FFA_SHARED_MM_BUFFER_SIZE SZ_4K /* 4 KB */ + +/* + * shared buffer physical address used for communication between + * u-boot and the MM SP + */ +#define FFA_SHARED_MM_BUFFER_ADDR 0x02000000UL +#define FFA_SHARED_MM_BUFFER_OFFSET 0 + #define V2M_BASE 0x80000000
#define CFG_PL011_CLOCK 50000000

Hi Abdellatif,
On Thu, Jan 19, 2023 at 04:31:57PM +0000, Abdellatif El Khlifi wrote:
Hi Simon, Tom,
The FF-A transport is a SW bus and is not associated to any HW peripheral or undiscoverable base address.
There is only 1 way of discovering the FF-A bus and it's through the FF-A SW interfaces. The FF-A spec [1] describes this in details.
Discovering means gathering information about the FF-A framework such as: the FF-A version, supported features, secure partitions number and attributes.
Please refer to the following paragraphs for more details: [2], [3], [4], [5]
The core driver provided by this patchset implements the Setup and discovery interfaces in addition to direct messaging.
The driver provides ffa_bus_discover() API that allows to discover the FF-A bus as described by the spec and in the FF-A driver readme [6].
We expect and highly recommend FF-A users to always discover the FF-A bus using ffa_bus_discover() API.
Thanks for the details. But IIRC this discussion is not about the FF-A bus and device(partitions) discovery, but the support for FF-A itself. The discussion is about where to have a device node to represent the existence of FF-A support on a platform. If we are talking about individual partitions (devices) in the device tree, then that is pure stupidity as it goes out of since with the firmware the moment a partition is added or removed in the firmware.
IIUC, the whole discussion was around whether to use FFA_VERSION as the discovery mechanism for existence of FF-A support on a platform or you have a device node to specify the same.
Just to be clear, even if it is decided to add a device node, the FFA_VERSION must be used to detect the presence of FF-A support and return error otherwise. DT node presence is just to satisfy the design and must be treated as no auto-confirmation for the presence of FF-A support. We are just arguing the device node presence is just redundant, but as mentioned before it is up to U-Boot community to make a call on what is best.
-- Regards, Sudeep

Hi Sudeep,
On Thu, 19 Jan 2023 at 09:46, Sudeep Holla sudeep.holla@arm.com wrote:
Hi Abdellatif,
On Thu, Jan 19, 2023 at 04:31:57PM +0000, Abdellatif El Khlifi wrote:
Hi Simon, Tom,
The FF-A transport is a SW bus and is not associated to any HW peripheral or undiscoverable base address.
There is only 1 way of discovering the FF-A bus and it's through the FF-A SW interfaces. The FF-A spec [1] describes this in details.
Discovering means gathering information about the FF-A framework such as: the FF-A version, supported features, secure partitions number and attributes.
Please refer to the following paragraphs for more details: [2], [3], [4], [5]
The core driver provided by this patchset implements the Setup and discovery interfaces in addition to direct messaging.
The driver provides ffa_bus_discover() API that allows to discover the FF-A bus as described by the spec and in the FF-A driver readme [6].
We expect and highly recommend FF-A users to always discover the FF-A bus using ffa_bus_discover() API.
Thanks for the details. But IIRC this discussion is not about the FF-A bus and device(partitions) discovery, but the support for FF-A itself. The discussion is about where to have a device node to represent the existence of FF-A support on a platform. If we are talking about individual partitions (devices) in the device tree, then that is pure stupidity as it goes out of since with the firmware the moment a partition is added or removed in the firmware.
IIUC, the whole discussion was around whether to use FFA_VERSION as the discovery mechanism for existence of FF-A support on a platform or you have a device node to specify the same.
No, with respect, that is not quite the situation here.
Just to be clear, even if it is decided to add a device node, the FFA_VERSION must be used to detect the presence of FF-A support and return error otherwise. DT node presence is just to satisfy the design and must be treated as no auto-confirmation for the presence of FF-A support. We are just arguing the device node presence is just redundant, but as mentioned before it is up to U-Boot community to make a call on what is best.
U-Boot driver model design already supports this. You can have a device that binds (from DT) but will not probe because it is not present / wrong version. Perhaps this was missed in the conversion to Linux:
https://u-boot.readthedocs.io/en/latest/develop/driver-model/design.html#dri...
So there is nothing clever needed here at all and anything you do just adds confusion and bad precedent.
Regards, Simon

On Thu, Jan 19, 2023 at 09:54:29AM -0700, Simon Glass wrote:
Hi Sudeep,
On Thu, 19 Jan 2023 at 09:46, Sudeep Holla sudeep.holla@arm.com wrote:
Hi Abdellatif,
On Thu, Jan 19, 2023 at 04:31:57PM +0000, Abdellatif El Khlifi wrote:
Hi Simon, Tom,
The FF-A transport is a SW bus and is not associated to any HW peripheral or undiscoverable base address.
There is only 1 way of discovering the FF-A bus and it's through the FF-A SW interfaces. The FF-A spec [1] describes this in details.
Discovering means gathering information about the FF-A framework such as: the FF-A version, supported features, secure partitions number and attributes.
Please refer to the following paragraphs for more details: [2], [3], [4], [5]
The core driver provided by this patchset implements the Setup and discovery interfaces in addition to direct messaging.
The driver provides ffa_bus_discover() API that allows to discover the FF-A bus as described by the spec and in the FF-A driver readme [6].
We expect and highly recommend FF-A users to always discover the FF-A bus using ffa_bus_discover() API.
Thanks for the details. But IIRC this discussion is not about the FF-A bus and device(partitions) discovery, but the support for FF-A itself. The discussion is about where to have a device node to represent the existence of FF-A support on a platform. If we are talking about individual partitions (devices) in the device tree, then that is pure stupidity as it goes out of since with the firmware the moment a partition is added or removed in the firmware.
IIUC, the whole discussion was around whether to use FFA_VERSION as the discovery mechanism for existence of FF-A support on a platform or you have a device node to specify the same.
No, with respect, that is not quite the situation here.
Just to be clear, even if it is decided to add a device node, the FFA_VERSION must be used to detect the presence of FF-A support and return error otherwise. DT node presence is just to satisfy the design and must be treated as no auto-confirmation for the presence of FF-A support. We are just arguing the device node presence is just redundant, but as mentioned before it is up to U-Boot community to make a call on what is best.
U-Boot driver model design already supports this. You can have a device that binds (from DT) but will not probe because it is not present / wrong version. Perhaps this was missed in the conversion to Linux:
https://u-boot.readthedocs.io/en/latest/develop/driver-model/design.html#dri...
So there is nothing clever needed here at all and anything you do just adds confusion and bad precedent.
But it's also true that at run-time, within U-Boot, we can modify the device tree we have, with live tree yes? So, the whole series in question here can be done without modifying the base DT and getting in to the further discussions that doing so entails. The assertion is that the software discoverable bus here is sufficient to not need DT, so, OK, lets go.

On Thu, Jan 19, 2023 at 11:57:44AM -0500, Tom Rini wrote:
But it's also true that at run-time, within U-Boot, we can modify the device tree we have, with live tree yes? So, the whole series in question here can be done without modifying the base DT and getting in to the further discussions that doing so entails. The assertion is that the software discoverable bus here is sufficient to not need DT, so, OK, lets go.
OK, may be I am not up-to-date on the U-Boot. IIUC, the modifications done in the DT by U-Boot is mostly for consumption by the next stage loader/OS and not for self-consumption. But if it is for self consumption, then good. It helps especially for the subnodes(as Simon referred) or the partitions that can be discovered at run-time using FF-A interface.
As mentioned I am not again DT, it is just not needed and especially for subnodes it could result in inconsistency b/w what is in DT and what the firmware provides. As mentioned in previous response, having a simple node that Simon provided as example earlier is fine by me if that is the only option to make progress as I just feel it is redundant and one can say not scalable(but that is debatable again 😄).
In short, I am not concerned about having simple node, just don't like to see entire FF-A bus enumerated in DT as subnodes for reasons mentioned already.
-- Regards, Sudeep

On Thu, Jan 19, 2023 at 05:09:45PM +0000, Sudeep Holla wrote:
On Thu, Jan 19, 2023 at 11:57:44AM -0500, Tom Rini wrote:
But it's also true that at run-time, within U-Boot, we can modify the device tree we have, with live tree yes? So, the whole series in question here can be done without modifying the base DT and getting in to the further discussions that doing so entails. The assertion is that the software discoverable bus here is sufficient to not need DT, so, OK, lets go.
OK, may be I am not up-to-date on the U-Boot. IIUC, the modifications done in the DT by U-Boot is mostly for consumption by the next stage loader/OS and not for self-consumption. But if it is for self consumption, then good. It helps especially for the subnodes(as Simon referred) or the partitions that can be discovered at run-time using FF-A interface.
As mentioned I am not again DT, it is just not needed and especially for subnodes it could result in inconsistency b/w what is in DT and what the firmware provides. As mentioned in previous response, having a simple node that Simon provided as example earlier is fine by me if that is the only option to make progress as I just feel it is redundant and one can say not scalable(but that is debatable again 😄).
In short, I am not concerned about having simple node, just don't like to see entire FF-A bus enumerated in DT as subnodes for reasons mentioned already.
So there's two parts to this discussion. The first of which has been, do Simon and I agree with the direction of defining a software discoverable bus rather than using device tree to describe it. To which the answer is no, and also neither camp seems likely to convince the other. The second part of the discussion is what can we practically do about it. To which the answer is that I believe the live tree support (see https://u-boot.readthedocs.io/en/latest/develop/driver-model/livetree.html) should allow for the FF-A bus support to be integrated with U-Boot, without having to add something to the base device tree. And we can evaluate down the line if our fears (or at least mine) were unfounded or not.

Hi Sudeep,
On Thu, 19 Jan 2023 at 10:09, Sudeep Holla sudeep.holla@arm.com wrote:
On Thu, Jan 19, 2023 at 11:57:44AM -0500, Tom Rini wrote:
But it's also true that at run-time, within U-Boot, we can modify the device tree we have, with live tree yes? So, the whole series in question here can be done without modifying the base DT and getting in to the further discussions that doing so entails. The assertion is that the software discoverable bus here is sufficient to not need DT, so, OK, lets go.
OK, may be I am not up-to-date on the U-Boot. IIUC, the modifications done in the DT by U-Boot is mostly for consumption by the next stage loader/OS and not for self-consumption. But if it is for self consumption, then good. It helps especially for the subnodes(as Simon referred) or the partitions that can be discovered at run-time using FF-A interface.
It's really just dodging the issue though, because you need a compatible string and you might as well add it to the DT in the source as do it at runtime.
As mentioned I am not again DT, it is just not needed and especially for subnodes it could result in inconsistency b/w what is in DT and what the firmware provides. As mentioned in previous response, having a simple node that Simon provided as example earlier is fine by me if that is the only option to make progress as I just feel it is redundant and one can say not scalable(but that is debatable again 😄).
Gosh, how many of these things are you going to add? I believe the inconsistency argument is dealt with by the bind/probe explanation. It may be redundant in Linux but I doubt it would hurt there either.
In short, I am not concerned about having simple node, just don't like to see entire FF-A bus enumerated in DT as subnodes for reasons mentioned already.
Fair enough, and that is agreed on my side. I'll note that with PCI we sometimes do add nodes in order to provide parameters to the driver, there being no other sensible way to do this in U-Boot, for example:
https://github.com/u-boot/u-boot/blob/master/arch/x86/dts/chromebook_coral.d...
Regards, Simon

On Thu, Jan 19, 2023 at 10:22:07AM -0700, Simon Glass wrote:
Hi Sudeep,
On Thu, 19 Jan 2023 at 10:09, Sudeep Holla sudeep.holla@arm.com wrote:
On Thu, Jan 19, 2023 at 11:57:44AM -0500, Tom Rini wrote:
But it's also true that at run-time, within U-Boot, we can modify the device tree we have, with live tree yes? So, the whole series in question here can be done without modifying the base DT and getting in to the further discussions that doing so entails. The assertion is that the software discoverable bus here is sufficient to not need DT, so, OK, lets go.
OK, may be I am not up-to-date on the U-Boot. IIUC, the modifications done in the DT by U-Boot is mostly for consumption by the next stage loader/OS and not for self-consumption. But if it is for self consumption, then good. It helps especially for the subnodes(as Simon referred) or the partitions that can be discovered at run-time using FF-A interface.
It's really just dodging the issue though, because you need a compatible string and you might as well add it to the DT in the source as do it at runtime.
Well that is one of the argument assuming DT node is a must. But adding DT node requirement when it is not needed can be seen an issue itself if one has to play devil's advocate here.
As mentioned I am not again DT, it is just not needed and especially for subnodes it could result in inconsistency b/w what is in DT and what the firmware provides. As mentioned in previous response, having a simple node that Simon provided as example earlier is fine by me if that is the only option to make progress as I just feel it is redundant and one can say not scalable(but that is debatable again 😄).
Gosh, how many of these things are you going to add? I believe the inconsistency argument is dealt with by the bind/probe explanation. It may be redundant in Linux but I doubt it would hurt there either.
Not really. Currently the number of partitions is static and all of them are bundled into on or few binaries. But we could have dynamic partitions in the future. More over it is pain to update the DT for each possible configuration especially during development where you want to experiment things around. Having to deal with the DT for every small change in the config is just annoying. Yes one could have universal list of partitions and defer it to be dealt at probe/bind, but not sure if it is possible to have such a universal list during development. We may have it handy for production but we also want to ease development here.
In short, I have had quite a lot of issues with DT and firmware inconsistency due to duplication of same data at the beginning and the things diverged. So I am simply not ready to go back there and I am sure quite a few in the kernel community have their fingers bitten because of such inconsistency created by *unnecessary static addition of data* to the DT. So, let me be clear, I wouldn't use the information from the DT if I can get more accurate one dynamically from the firmware in the kernel driver.

Hi Sudeep,
On Fri, 20 Jan 2023 at 04:17, Sudeep Holla sudeep.holla@arm.com wrote:
On Thu, Jan 19, 2023 at 10:22:07AM -0700, Simon Glass wrote:
Hi Sudeep,
On Thu, 19 Jan 2023 at 10:09, Sudeep Holla sudeep.holla@arm.com wrote:
On Thu, Jan 19, 2023 at 11:57:44AM -0500, Tom Rini wrote:
But it's also true that at run-time, within U-Boot, we can modify the device tree we have, with live tree yes? So, the whole series in question here can be done without modifying the base DT and getting in to the further discussions that doing so entails. The assertion is that the software discoverable bus here is sufficient to not need DT, so, OK, lets go.
OK, may be I am not up-to-date on the U-Boot. IIUC, the modifications done in the DT by U-Boot is mostly for consumption by the next stage loader/OS and not for self-consumption. But if it is for self consumption, then good. It helps especially for the subnodes(as Simon referred) or the partitions that can be discovered at run-time using FF-A interface.
It's really just dodging the issue though, because you need a compatible string and you might as well add it to the DT in the source as do it at runtime.
Well that is one of the argument assuming DT node is a must. But adding DT node requirement when it is not needed can be seen an issue itself if one has to play devil's advocate here.
As mentioned I am not again DT, it is just not needed and especially for subnodes it could result in inconsistency b/w what is in DT and what the firmware provides. As mentioned in previous response, having a simple node that Simon provided as example earlier is fine by me if that is the only option to make progress as I just feel it is redundant and one can say not scalable(but that is debatable again 😄).
Gosh, how many of these things are you going to add? I believe the inconsistency argument is dealt with by the bind/probe explanation. It may be redundant in Linux but I doubt it would hurt there either.
Not really. Currently the number of partitions is static and all of them are bundled into on or few binaries. But we could have dynamic partitions in the future. More over it is pain to update the DT for each possible configuration especially during development where you want to experiment things around. Having to deal with the DT for every small change in the config is just annoying. Yes one could have universal list of partitions and defer it to be dealt at probe/bind, but not sure if it is possible to have such a universal list during development. We may have it handy for production but we also want to ease development here.
In short, I have had quite a lot of issues with DT and firmware inconsistency due to duplication of same data at the beginning and the things diverged. So I am simply not ready to go back there and I am sure quite a few in the kernel community have their fingers bitten because of such inconsistency created by *unnecessary static addition of data* to the DT. So, let me be clear, I wouldn't use the information from the DT if I can get more accurate one dynamically from the firmware in the kernel driver.
I'd like to see DT defined across firmware and OS, not just be a Linux thing. It is a better approach than having little fiefdoms everywhere with their own config mechanisms.
It seems that you have lots of build-time config in the ARM components. I suggest looking at how you can move this to runtime config, if you are not planning to upstream the functionality to U-Boot.
Regards, Simon

Hi Simon,
On Mon, Jan 23, 2023 at 09:32:19AM -0700, Simon Glass wrote:
Hi Sudeep,
I'd like to see DT defined across firmware and OS, not just be a Linux thing.
Fair enough.
It is a better approach than having little fiefdoms everywhere with their own config mechanisms.
Agreed.
It seems that you have lots of build-time config in the ARM components.
Not really. I think I have not conveyed the setup details properly. Let me try to explain with 2 hopefully simple examples:
1. Consider a system with a power controller running its own firmware and OS(or any other application software including U-Boot) interacting with it to get the required power resource access/modification.
One such thing is CPU operating points(OPP). Typically on old systems, these are listing in the DT and OSPM drives them completely. Now on the newer systems like the one I am presenting, ideally we need to get the list of OPP from the f/w at runtime and use the information to drive it.
Suppose we have these information statically built into DT, then if the firmware is upgraded and there is change in the OPP set or if the f/w detects some configuration(bios/similar) that requires change in the OPP set presented to the OSPM(or any other users), then the static info built in the DT is stale or wrong. And hence the use of DT in such a configuation or system is not correct unless DT is populated on the fly interacting with the firmware before DT is consumed by the users.
This was one example which I was referring when I said I don't want to use the DT info over live firmware information.
2. Now the FF-A case, if the list of partitions on the system is listed in the DT and there is change in that list due to various reasons(firmware upgrade, reconfiguration, resource disabled for a specific reason,...etc) then the list will be stale when presented to the user(OSPM/Linux/U-Boot) if it is not updated on the fly before the DT data is consumed.
Since in both the cases the firmware provides interface to the users to get the information at runtime, I am against using the DT as source of information.

Hi Sudeep,
On Tue, 24 Jan 2023 at 04:30, Sudeep Holla sudeep.holla@arm.com wrote:
Hi Simon,
On Mon, Jan 23, 2023 at 09:32:19AM -0700, Simon Glass wrote:
Hi Sudeep,
I'd like to see DT defined across firmware and OS, not just be a Linux thing.
Fair enough.
It is a better approach than having little fiefdoms everywhere with their own config mechanisms.
Agreed.
It seems that you have lots of build-time config in the ARM components.
Not really. I think I have not conveyed the setup details properly. Let me try to explain with 2 hopefully simple examples:
Consider a system with a power controller running its own firmware and OS(or any other application software including U-Boot) interacting with it to get the required power resource access/modification.
One such thing is CPU operating points(OPP). Typically on old systems, these are listing in the DT and OSPM drives them completely. Now on the newer systems like the one I am presenting, ideally we need to get the list of OPP from the f/w at runtime and use the information to drive it.
Suppose we have these information statically built into DT, then if the firmware is upgraded and there is change in the OPP set or if the f/w detects some configuration(bios/similar) that requires change in the OPP set presented to the OSPM(or any other users), then the static info built in the DT is stale or wrong. And hence the use of DT in such a configuation or system is not correct unless DT is populated on the fly interacting with the firmware before DT is consumed by the users.
This was one example which I was referring when I said I don't want to use the DT info over live firmware information.
So you need to rebuild the firmware component to update the information? That's not nice.
These parameters need to be factored out of the code. The best way to do that is to put them in the DT. The DT should configure all of firmware. It should not be necessary to rebuild all the code to update a few parameters in the build.
Also these parameters should not be 'buried' in the code. I'd really like you to consider the difference between code and data, with the (configurable) data being in DT.
- Now the FF-A case, if the list of partitions on the system is listed in the DT and there is change in that list due to various reasons(firmware upgrade, reconfiguration, resource disabled for a specific reason,...etc) then the list will be stale when presented to the user(OSPM/Linux/U-Boot) if it is not updated on the fly before the DT data is consumed.
U-Boot can set this up in the DT if that is what you want, but I think you are confusing two things:
- what is available to use (i.e. drivers or code that is compiled in) - what we actually want to use and how each piece if configured (this should be handled at runtime)
Imagine your code is a Linux kernel. We don't hard-code these sorts of settings in Linux.
Since in both the cases the firmware provides interface to the users to get the information at runtime, I am against using the DT as source of information.
Well we should figure out a time or place to talk about this, as I believe this is heading down the wrong path. As firmware gets more complex, we need a unified configuration mechanism and proper visibility into each component and what it is doing. They need to act as a unified whole, not as a collection of blobs that might conflict.
Regards, Simon

On Tue, Jan 24, 2023 at 03:44:53PM -0700, Simon Glass wrote:
Hi Sudeep,
On Tue, 24 Jan 2023 at 04:30, Sudeep Holla sudeep.holla@arm.com wrote:
Hi Simon,
[...]
Not really. I think I have not conveyed the setup details properly. Let me try to explain with 2 hopefully simple examples:
Consider a system with a power controller running its own firmware and OS(or any other application software including U-Boot) interacting with it to get the required power resource access/modification.
One such thing is CPU operating points(OPP). Typically on old systems, these are listing in the DT and OSPM drives them completely. Now on the newer systems like the one I am presenting, ideally we need to get the list of OPP from the f/w at runtime and use the information to drive it.
Suppose we have these information statically built into DT, then if the firmware is upgraded and there is change in the OPP set or if the f/w detects some configuration(bios/similar) that requires change in the OPP set presented to the OSPM(or any other users), then the static info built in the DT is stale or wrong. And hence the use of DT in such a configuation or system is not correct unless DT is populated on the fly interacting with the firmware before DT is consumed by the users.
This was one example which I was referring when I said I don't want to use the DT info over live firmware information.
So you need to rebuild the firmware component to update the information? That's not nice.
Well, I understand the firmware component itself can fetch these data from the DT, but not all firmware are built with DT support to start with(for simple reasons like they need to run with very limited ram in orders of few kB).
Even if they did, they might have independent DT blob with the data required for the firmware to function.
The holy grail of system DT is yet to see the reality IMO.
And this rebuild is very much needed during development and updating DT for the data makes no sense to me when the Linux kernel can query the same through standard interfaces defined by the firmware. There can be no argument to no use the interface to fetch the data runtime as it will be most accurate than any other data. DT may have more data or data that firmware decided not to support runtime for various reasons(I think I mentioned couple earlier). So, strict no to static data from the DT and even if some boot code updates it, I trust getting it from the firmware if and when possible. The interface has been added considering all these. It is agnostic of firmware designs and implementation. Assuming DT support in the firmware just makes these unnecessarily hard.
These parameters need to be factored out of the code. The best way to do that is to put them in the DT. The DT should configure all of firmware. It should not be necessary to rebuild all the code to update a few parameters in the build.
Completely agreed in terms of design, but in reality not all implementations want to follow that for reasons of their own.
Also these parameters should not be 'buried' in the code. I'd really like you to consider the difference between code and data, with the (configurable) data being in DT.
Again agreed, but this is something we are enforcing in the Linux kernel and U-Boot. It is not universal and IMO it may never be. The firmware interfaces are defined to cope up with these and they are bound to stay. DT can never replace it. Further like ACPI, these firmware interface don't just provide static data, it also supports run-time. So there is some advantage and are here to stay. Preferring DT data for sake of uniformity and simplicity are not going to sway the wind away from these interface to the DT. I am definitely not for it for all the reasons I have already mentioned.
- Now the FF-A case, if the list of partitions on the system is listed in the DT and there is change in that list due to various reasons(firmware upgrade, reconfiguration, resource disabled for a specific reason,...etc) then the list will be stale when presented to the user(OSPM/Linux/U-Boot) if it is not updated on the fly before the DT data is consumed.
U-Boot can set this up in the DT if that is what you want, but I think you are confusing two things:
- what is available to use (i.e. drivers or code that is compiled in)
- what we actually want to use and how each piece if configured (this
should be handled at runtime)
No I understand all these and advantages you have been mentioning. But there is also other side of the story and that's what I have been trying to group as firmware interface.
Imagine your code is a Linux kernel. We don't hard-code these sorts of settings in Linux.
Yes definitely and I agree as mentioned quite a few times above. But that doesn't change a bit as already mentioned.
Since in both the cases the firmware provides interface to the users to get the information at runtime, I am against using the DT as source of information.
Well we should figure out a time or place to talk about this, as I believe this is heading down the wrong path.
If you strongly believe that, then you need to speak with architects who are designing and standardising these interfaces taking with the various Arm silicon vendors/partners. I am also sure your employer is one of them and have their means to get in touch with the right people.
As firmware gets more complex,
Indeed, they are already getting complex and it is necessitating the need for such interfaces as we are dealing with dynamic component as well not just static to use DT as standard and unified mechanism.
we need a unified configuration mechanism and proper visibility into each component and what it is doing. They need to act as a unified whole, not as a collection of blobs that might conflict.
Well that would be ideal and I would have agreed with that few years back but not now as I have seen the evolution in the firmware space(that I am involved and visible to me) and involved in the development of some of these interface.
Main point based on these discussion is that you are looking at just the static data needed and how DT can be used. The interfaces are also dealing with the dynamic configurations and while at it they better deal with static data as well instead of using alternatives like DT. I am not against DT for the reasons you think it must be used, it is just that here it is not sufficient.
-- Regards, Sudeep

Hi Tom,
On Thu, 19 Jan 2023 at 09:57, Tom Rini trini@konsulko.com wrote:
On Thu, Jan 19, 2023 at 09:54:29AM -0700, Simon Glass wrote:
Hi Sudeep,
On Thu, 19 Jan 2023 at 09:46, Sudeep Holla sudeep.holla@arm.com wrote:
Hi Abdellatif,
On Thu, Jan 19, 2023 at 04:31:57PM +0000, Abdellatif El Khlifi wrote:
Hi Simon, Tom,
The FF-A transport is a SW bus and is not associated to any HW peripheral or undiscoverable base address.
There is only 1 way of discovering the FF-A bus and it's through the FF-A SW interfaces. The FF-A spec [1] describes this in details.
Discovering means gathering information about the FF-A framework such as: the FF-A version, supported features, secure partitions number and attributes.
Please refer to the following paragraphs for more details: [2], [3], [4], [5]
The core driver provided by this patchset implements the Setup and discovery interfaces in addition to direct messaging.
The driver provides ffa_bus_discover() API that allows to discover the FF-A bus as described by the spec and in the FF-A driver readme [6].
We expect and highly recommend FF-A users to always discover the FF-A bus using ffa_bus_discover() API.
Thanks for the details. But IIRC this discussion is not about the FF-A bus and device(partitions) discovery, but the support for FF-A itself. The discussion is about where to have a device node to represent the existence of FF-A support on a platform. If we are talking about individual partitions (devices) in the device tree, then that is pure stupidity as it goes out of since with the firmware the moment a partition is added or removed in the firmware.
IIUC, the whole discussion was around whether to use FFA_VERSION as the discovery mechanism for existence of FF-A support on a platform or you have a device node to specify the same.
No, with respect, that is not quite the situation here.
Just to be clear, even if it is decided to add a device node, the FFA_VERSION must be used to detect the presence of FF-A support and return error otherwise. DT node presence is just to satisfy the design and must be treated as no auto-confirmation for the presence of FF-A support. We are just arguing the device node presence is just redundant, but as mentioned before it is up to U-Boot community to make a call on what is best.
U-Boot driver model design already supports this. You can have a device that binds (from DT) but will not probe because it is not present / wrong version. Perhaps this was missed in the conversion to Linux:
https://u-boot.readthedocs.io/en/latest/develop/driver-model/design.html#dri...
So there is nothing clever needed here at all and anything you do just adds confusion and bad precedent.
But it's also true that at run-time, within U-Boot, we can modify the device tree we have, with live tree yes? So, the whole series in question here can be done without modifying the base DT and getting in to the further discussions that doing so entails. The assertion is that the software discoverable bus here is sufficient to not need DT, so, OK, lets go.
One of the reasons that I find this all so frustrating is that it is circular logic:
1. Device-tree bindings are controlled by Linux; U-Boot cannot upstream bindings 2. We can only have upstreamed bindings in any device tree
We have invented a whole u-boot.dtsi feature in U-Boot to hold modifications from Linux. Board vendors have been suffering with this for years.
It is not fair and it really needs to stop. I am doing what I can to upstream some basic U-Boot bindings and I hope that will work and can lead to a healthier relationship here.
Regards, Simon

On Thu, Jan 19, 2023 at 10:21:07AM -0700, Simon Glass wrote:
Hi Tom,
On Thu, 19 Jan 2023 at 09:57, Tom Rini trini@konsulko.com wrote:
On Thu, Jan 19, 2023 at 09:54:29AM -0700, Simon Glass wrote:
Hi Sudeep,
On Thu, 19 Jan 2023 at 09:46, Sudeep Holla sudeep.holla@arm.com wrote:
Hi Abdellatif,
On Thu, Jan 19, 2023 at 04:31:57PM +0000, Abdellatif El Khlifi wrote:
Hi Simon, Tom,
The FF-A transport is a SW bus and is not associated to any HW peripheral or undiscoverable base address.
There is only 1 way of discovering the FF-A bus and it's through the FF-A SW interfaces. The FF-A spec [1] describes this in details.
Discovering means gathering information about the FF-A framework such as: the FF-A version, supported features, secure partitions number and attributes.
Please refer to the following paragraphs for more details: [2], [3], [4], [5]
The core driver provided by this patchset implements the Setup and discovery interfaces in addition to direct messaging.
The driver provides ffa_bus_discover() API that allows to discover the FF-A bus as described by the spec and in the FF-A driver readme [6].
We expect and highly recommend FF-A users to always discover the FF-A bus using ffa_bus_discover() API.
Thanks for the details. But IIRC this discussion is not about the FF-A bus and device(partitions) discovery, but the support for FF-A itself. The discussion is about where to have a device node to represent the existence of FF-A support on a platform. If we are talking about individual partitions (devices) in the device tree, then that is pure stupidity as it goes out of since with the firmware the moment a partition is added or removed in the firmware.
IIUC, the whole discussion was around whether to use FFA_VERSION as the discovery mechanism for existence of FF-A support on a platform or you have a device node to specify the same.
No, with respect, that is not quite the situation here.
Just to be clear, even if it is decided to add a device node, the FFA_VERSION must be used to detect the presence of FF-A support and return error otherwise. DT node presence is just to satisfy the design and must be treated as no auto-confirmation for the presence of FF-A support. We are just arguing the device node presence is just redundant, but as mentioned before it is up to U-Boot community to make a call on what is best.
U-Boot driver model design already supports this. You can have a device that binds (from DT) but will not probe because it is not present / wrong version. Perhaps this was missed in the conversion to Linux:
https://u-boot.readthedocs.io/en/latest/develop/driver-model/design.html#dri...
So there is nothing clever needed here at all and anything you do just adds confusion and bad precedent.
But it's also true that at run-time, within U-Boot, we can modify the device tree we have, with live tree yes? So, the whole series in question here can be done without modifying the base DT and getting in to the further discussions that doing so entails. The assertion is that the software discoverable bus here is sufficient to not need DT, so, OK, lets go.
One of the reasons that I find this all so frustrating is that it is circular logic:
- Device-tree bindings are controlled by Linux; U-Boot cannot upstream bindings
- We can only have upstreamed bindings in any device tree
We have invented a whole u-boot.dtsi feature in U-Boot to hold modifications from Linux. Board vendors have been suffering with this for years.
It is not fair and it really needs to stop. I am doing what I can to upstream some basic U-Boot bindings and I hope that will work and can lead to a healthier relationship here.
Yes, but this is a problem outside of that scope. The argument here is that one does not need a device tree node to work. So lets see just how clean and nice the code can be without what you and I have been insisting would lead to the cleanest result.

Hi Tom,
On Thu, 19 Jan 2023 at 10:24, Tom Rini trini@konsulko.com wrote:
On Thu, Jan 19, 2023 at 10:21:07AM -0700, Simon Glass wrote:
Hi Tom,
On Thu, 19 Jan 2023 at 09:57, Tom Rini trini@konsulko.com wrote:
On Thu, Jan 19, 2023 at 09:54:29AM -0700, Simon Glass wrote:
Hi Sudeep,
On Thu, 19 Jan 2023 at 09:46, Sudeep Holla sudeep.holla@arm.com wrote:
Hi Abdellatif,
On Thu, Jan 19, 2023 at 04:31:57PM +0000, Abdellatif El Khlifi wrote:
Hi Simon, Tom,
The FF-A transport is a SW bus and is not associated to any HW peripheral or undiscoverable base address.
There is only 1 way of discovering the FF-A bus and it's through the FF-A SW interfaces. The FF-A spec [1] describes this in details.
Discovering means gathering information about the FF-A framework such as: the FF-A version, supported features, secure partitions number and attributes.
Please refer to the following paragraphs for more details: [2], [3], [4], [5]
The core driver provided by this patchset implements the Setup and discovery interfaces in addition to direct messaging.
The driver provides ffa_bus_discover() API that allows to discover the FF-A bus as described by the spec and in the FF-A driver readme [6].
We expect and highly recommend FF-A users to always discover the FF-A bus using ffa_bus_discover() API.
Thanks for the details. But IIRC this discussion is not about the FF-A bus and device(partitions) discovery, but the support for FF-A itself. The discussion is about where to have a device node to represent the existence of FF-A support on a platform. If we are talking about individual partitions (devices) in the device tree, then that is pure stupidity as it goes out of since with the firmware the moment a partition is added or removed in the firmware.
IIUC, the whole discussion was around whether to use FFA_VERSION as the discovery mechanism for existence of FF-A support on a platform or you have a device node to specify the same.
No, with respect, that is not quite the situation here.
Just to be clear, even if it is decided to add a device node, the FFA_VERSION must be used to detect the presence of FF-A support and return error otherwise. DT node presence is just to satisfy the design and must be treated as no auto-confirmation for the presence of FF-A support. We are just arguing the device node presence is just redundant, but as mentioned before it is up to U-Boot community to make a call on what is best.
U-Boot driver model design already supports this. You can have a device that binds (from DT) but will not probe because it is not present / wrong version. Perhaps this was missed in the conversion to Linux:
https://u-boot.readthedocs.io/en/latest/develop/driver-model/design.html#dri...
So there is nothing clever needed here at all and anything you do just adds confusion and bad precedent.
But it's also true that at run-time, within U-Boot, we can modify the device tree we have, with live tree yes? So, the whole series in question here can be done without modifying the base DT and getting in to the further discussions that doing so entails. The assertion is that the software discoverable bus here is sufficient to not need DT, so, OK, lets go.
One of the reasons that I find this all so frustrating is that it is circular logic:
- Device-tree bindings are controlled by Linux; U-Boot cannot upstream bindings
- We can only have upstreamed bindings in any device tree
We have invented a whole u-boot.dtsi feature in U-Boot to hold modifications from Linux. Board vendors have been suffering with this for years.
It is not fair and it really needs to stop. I am doing what I can to upstream some basic U-Boot bindings and I hope that will work and can lead to a healthier relationship here.
Yes, but this is a problem outside of that scope. The argument here is that one does not need a device tree node to work. So lets see just how clean and nice the code can be without what you and I have been insisting would lead to the cleanest result.
Well we already have the code, right...?
The entire device tree is optional. We could just use platform data or even C function calls to set up devices. We could call a C function for each board which builds a device tree early in boot. Etc... As I remember it, no one wanted to use DT and it was only Linus Torvalds' refusal to accept another ARM pull request that made people move away from platform data and other ad-hoc mechanisms. I'll note that U-Boot adopted DT on ARM in 2011, the same year as Linux [1] [2]. Since then huge strides have been made in many ways, e.g. loads of useful bindings and Rob Herring's validation stuff.
IMO the problem here is exactly because of the point I mentioned. I suspect the reason no one wants to add a compatible string for the top-level FF-A device is that it will be rejected by Linux and they don't want another case of PTSD (I am willing to take that on if it helps). I'm sorry that we are in this situation, but it is not going to be fixed by ignoring the problem. It is causing all sorts of work-arounds [3], is bad for project interoperability (and therefore the open source industry as a whole), wastes a huge amount of time in discussion and gives device tree a bad name[4]. It really, really needs to stop.
Can we all agree to work together on this and see device tree as a shared technology and resource, across firmware and OS? Could this be the last thread on this topic?
Regards, Simon
[1] U-Boot 45ba8077f3e ("fdt: ARM: Add device tree control of U-Boot (CONFIG_OF_CONTROL)") [2] Linux 9eb8f6743b076b ("arm/dt: Allow CONFIG_OF on ARM") [3] I won't attempt to list them but I could probably find >100 separate U-Boot mailing-list threads trying to work around the inability to have U-Boot bindings upstream [4] I suspect it is a major reason why ARM adopted ACPI on servers

Hi Simon,
On Thu, Jan 19, 2023 at 11:04:16AM -0700, Simon Glass wrote: [...]
Well we already have the code, right...?
Correct, that what we would like to see.
The entire device tree is optional. We could just use platform data or even C function calls to set up devices. We could call a C function for each board which builds a device tree early in boot. Etc... As I remember it, no one wanted to use DT and it was only Linus Torvalds' refusal to accept another ARM pull request that made people move away from platform data and other ad-hoc mechanisms. I'll note that U-Boot adopted DT on ARM in 2011, the same year as Linux [1] [2]. Since then huge strides have been made in many ways, e.g. loads of useful bindings and Rob Herring's validation stuff.
OK here is where I differ from your understanding. DT for mainly to replace platform specific data. And though firmware is *platform specific*, the firmware interface itself is not and can be compared to architecture features. Do we define all Arm architecture specific features in the DT, the answer is clearly no, unless there is something missed in the architecture specification and too late to add anything to support platforms in the wild.
Similarly, the some of these standard firmware interfaces are to avoid such platform description in DT or ACPI. There are self discoverable like some of the architecture features. It is just in software rather than the hardware.
IMO the problem here is exactly because of the point I mentioned. I suspect the reason no one wants to add a compatible string for the top-level FF-A device is that it will be rejected by Linux and they don't want another case of PTSD (I am willing to take that on if it helps). I'm sorry that we are in this situation, but it is not going to be fixed by ignoring the problem. It is causing all sorts of work-arounds [3], is bad for project interoperability (and therefore the open source industry as a whole), wastes a huge amount of time in discussion and gives device tree a bad name[4]. It really, really needs to stop.
I am not sure if that is the reason for ACPI adoption TBH. IMO both have their own advantage and disadvantage. The idea is to try to minimise the deviation between the two(not on a wider scope though). The firmware interfaces like FF-A avoids the needs for any description of the feature in DT or ACPI all together and hence avoids any such deviation problems.
Can we all agree to work together on this and see device tree as a shared technology and resource, across firmware and OS? Could this be the last thread on this topic?
With the mention of ACPI now I just can't think how do you propose to use DT as shared tech/ resource across f/w and OS when ACPI is used.
For sure if something is not discoverable, they need to be described in DT and hence in ACPI too, but FF-A is not one such feature and I have no justification to add it in ACPI other than "DT has it, so lets add even if it is unnecessary".
In summary, look at FF-A as software architecture feature that can be discovered and not as a platform specific feature that needs platform specific data from DT or ACPI.
-- Regards, Sudeep

On Thu, Jan 19, 2023 at 09:54:29AM -0700, Simon Glass wrote:
Hi Sudeep,
On Thu, 19 Jan 2023 at 09:46, Sudeep Holla sudeep.holla@arm.com wrote:
Thanks for the details. But IIRC this discussion is not about the FF-A bus and device(partitions) discovery, but the support for FF-A itself. The discussion is about where to have a device node to represent the existence of FF-A support on a platform. If we are talking about individual partitions (devices) in the device tree, then that is pure stupidity as it goes out of since with the firmware the moment a partition is added or removed in the firmware.
IIUC, the whole discussion was around whether to use FFA_VERSION as the discovery mechanism for existence of FF-A support on a platform or you have a device node to specify the same.
No, with respect, that is not quite the situation here.
Hmm, not sure what you mean by that. Based on your earlier response, I thought we are in agreement but you sound to differ here. Am I missing something ?
Just to be clear, even if it is decided to add a device node, the FFA_VERSION must be used to detect the presence of FF-A support and return error otherwise. DT node presence is just to satisfy the design and must be treated as no auto-confirmation for the presence of FF-A support. We are just arguing the device node presence is just redundant, but as mentioned before it is up to U-Boot community to make a call on what is best.
U-Boot driver model design already supports this. You can have a device that binds (from DT) but will not probe because it is not present / wrong version. Perhaps this was missed in the conversion to Linux:
https://u-boot.readthedocs.io/en/latest/develop/driver-model/design.html#dri...
So there is nothing clever needed here at all and anything you do just adds confusion and bad precedent.
OK, I will give that a read.

unmap RX/TX buffers at ExitBootServices()
Unmapping the RX/TX buffers created by u-boot is needed before EFI runtime.
At EFI runtime the linux kernel takes care of allocating its own RX/TX buffers and registering them with the secure world.
Secure world should be using the RX/TX buffers created by the kernel. So, RX/TX buffers created by u-boot must be unmapped.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v8: pass NULL device pointer to the FF-A bus operation v7: replace debug() by log_err()
lib/efi_loader/efi_boottime.c | 13 +++++++++++++ 1 file changed, 13 insertions(+)
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 253f9f75ef..8949aca250 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -3,6 +3,9 @@ * EFI application boot time services * * Copyright (c) 2016 Alexander Graf + * + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> @@ -23,6 +26,10 @@ #include <asm/setjmp.h> #include <linux/libfdt_env.h>
+#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT) +#include <arm_ffa.h> +#endif + DECLARE_GLOBAL_DATA_PTR;
/* Task priority level */ @@ -2178,6 +2185,12 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL); }
+#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT) + /* unmap FF-A RX/TX buffers */ + if (ffa_bus_ops_get()->rxtx_unmap(NULL)) + log_err("Can't unmap FF-A RX/TX buffers\n"); +#endif + /* Patch out unsupported runtime function */ efi_runtime_detach();

Hi Abdellatif,
On Tue, 22 Nov 2022 at 06:18, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
unmap RX/TX buffers at ExitBootServices()
Unmapping the RX/TX buffers created by u-boot is needed before EFI runtime.
U-Boot
At EFI runtime the linux kernel takes care of allocating its own RX/TX buffers and registering them with the secure world.
Secure world should be using the RX/TX buffers created by the kernel. So, RX/TX buffers created by u-boot must be unmapped.
U-Boot
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v8: pass NULL device pointer to the FF-A bus operation v7: replace debug() by log_err()
lib/efi_loader/efi_boottime.c | 13 +++++++++++++ 1 file changed, 13 insertions(+)
diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 253f9f75ef..8949aca250 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -3,6 +3,9 @@
- EFI application boot time services
- Copyright (c) 2016 Alexander Graf
- (C) Copyright 2022 ARM Limited
*/
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
#include <common.h> @@ -23,6 +26,10 @@ #include <asm/setjmp.h> #include <linux/libfdt_env.h>
+#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT) +#include <arm_ffa.h> +#endif
Can we include it always?
DECLARE_GLOBAL_DATA_PTR;
/* Task priority level */ @@ -2178,6 +2185,12 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL); }
+#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT)
Can this use if() ?
I am not sure what this is being done in EFI code, though. We can use device_remove(...DM_REMOVE_ACTIVE_ALL) which is designed to handle this.
/* unmap FF-A RX/TX buffers */
if (ffa_bus_ops_get()->rxtx_unmap(NULL))
log_err("Can't unmap FF-A RX/TX buffers\n");
+#endif
/* Patch out unsupported runtime function */ efi_runtime_detach();
-- 2.17.1
Regards, SImon

Provide armffa command showcasing the use of the FF-A driver
The armffa command allows to query secure partitions data from the secure world and exchanging messages with the partitions using 64-bit FF-A direct messaging.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v8:
* update partition_info_get() second argument to be an SP count * pass NULL device pointer to the FF-A bus discovery and operations
v7:
* adapt do_ffa_dev_list() following the recent update on uclass_first_device/uclass_next_device functions (they return void now) * set armffa command to use 64-bit direct messaging
v4:
* remove pattern data in do_ffa_msg_send_direct_req
v3:
* use the new driver interfaces (partition_info_get, sync_send_receive) in armffa command
v2:
* replace use of ffa_helper_init_device function by ffa_helper_bus_discover
v1:
* introduce armffa command
MAINTAINERS | 1 + cmd/Kconfig | 10 ++ cmd/Makefile | 2 + cmd/armffa.c | 237 +++++++++++++++++++++++++++++++ drivers/firmware/arm-ffa/Kconfig | 1 + 5 files changed, 251 insertions(+) create mode 100644 cmd/armffa.c
diff --git a/MAINTAINERS b/MAINTAINERS index dcd32cf83a..aa4e87d9f8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -266,6 +266,7 @@ F: configs/cortina_presidio-asic-pnand_defconfig ARM FF-A M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com S: Maintained +F: cmd/armffa.c F: doc/arch/arm64.ffa.rst F: drivers/firmware/arm-ffa/ F: include/arm_ffa.h diff --git a/cmd/Kconfig b/cmd/Kconfig index 1092fb9c91..060d87375f 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -924,6 +924,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 Arm FF-A driver + 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 2444d116c0..c600d90d39 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -12,6 +12,8 @@ obj-y += panic.o obj-y += version.o
# command + +obj-$(CONFIG_CMD_ARMFFA) += armffa.o obj-$(CONFIG_CMD_ACPI) += acpi.o obj-$(CONFIG_CMD_ADDRMAP) += addrmap.o obj-$(CONFIG_CMD_AES) += aes.o diff --git a/cmd/armffa.c b/cmd/armffa.c new file mode 100644 index 0000000000..d2e8687bfb --- /dev/null +++ b/cmd/armffa.c @@ -0,0 +1,237 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <arm_ffa.h> +#include <asm/io.h> +#include <common.h> +#include <command.h> +#include <dm.h> +#include <mapmem.h> +#include <stdlib.h> + +/** + * do_ffa_get_singular_partition_info - implementation of the getpart subcommand + * @cmdtp: Command Table + * @flag: flags + * @argc: number of arguments + * @argv: arguments + * + * This function queries the secure partition information which the UUID is provided + * as an argument. The function uses the arm_ffa driver partition_info_get operation + * 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_get_singular_partition_info(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + u32 count = 0; + int ret; + struct ffa_partition_info *parts_info; + u32 info_idx; + + if (argc != 1) + return -EINVAL; + + /* Mode 1: getting the number of secure partitions */ + ret = ffa_bus_ops_get()->partition_info_get(NULL, argv[0], &count, NULL); + if (ret != 0) { + ffa_err("Failure in querying partitions count (error code: %d)", ret); + return ret; + } + + if (!count) { + ffa_info("No secure partition found"); + return ret; + } + + /* + * pre-allocate a buffer to be filled by the driver + * with ffa_partition_info structs + */ + + ffa_info("Pre-allocating %d partition(s) info structures", count); + + parts_info = calloc(count, sizeof(struct ffa_partition_info)); + if (!parts_info) + return -EINVAL; + + /* + * ask the driver to fill the buffer with the SPs info + */ + + ret = ffa_bus_ops_get()->partition_info_get(NULL, argv[0], &count, parts_info); + if (ret != 0) { + ffa_err("Failure in querying partition(s) info (error code: %d)", ret); + free(parts_info); + return ret; + } + + /* + * SPs found , show the partition information + */ + for (info_idx = 0; info_idx < count ; info_idx++) { + ffa_info("Partition: id = 0x%x , exec_ctxt 0x%x , properties 0x%x", + parts_info[info_idx].id, + parts_info[info_idx].exec_ctxt, + parts_info[info_idx].properties); + } + + free(parts_info); + + return 0; +} + +/** + * do_ffa_msg_send_direct_req - implementation of the ping subcommand + * @cmdtp: Command Table + * @flag: flags + * @argc: number of arguments + * @argv: arguments + * + * This function sends data to the secure partition which the ID is provided + * as an argument. The function uses the arm_ffa driver sync_send_receive operation + * to send data. + * + * Return: + * + * CMD_RET_SUCCESS: on success, otherwise failure + */ +int do_ffa_msg_send_direct_req(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; + + if (argc != 1) + return -EINVAL; + + errno = 0; + part_id = strtoul(argv[0], NULL, 16); + + if (errno) { + ffa_err("Invalid partition ID"); + return -EINVAL; + } + + ret = ffa_bus_ops_get()->sync_send_receive(NULL, part_id, &msg, 1); + if (ret == 0) { + u8 cnt; + + ffa_info("SP response:\n[LSB]"); + for (cnt = 0; + cnt < sizeof(struct ffa_send_direct_data) / sizeof(u64); + cnt++) + ffa_info("0x%llx", ((u64 *)&msg)[cnt]); + } else { + ffa_err("Sending direct request error (%d)", ret); + } + + return ret; +} + +/** + *do_ffa_dev_list - implementation of the devlist subcommand + * @cmdtp: [in] Command Table + * @flag: flags + * @argc: number of arguments + * @argv: arguments + * + * This function queries the devices belonging to the UCLASS_FFA + * class. Currently, one device is expected to show up: the arm_ffa device + * + * Return: + * + * CMD_RET_SUCCESS: on success, otherwise failure + */ +int do_ffa_dev_list(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct udevice *dev = NULL; + int i; + + ffa_info("arm_ffa uclass entries:"); + + for (i = 0, uclass_first_device(UCLASS_FFA, &dev); + dev; + uclass_next_device(&dev), i++) { + ffa_info("entry %d - instance %08x, ops %08x, plat %08x", + i, + (u32)map_to_sysmem(dev), + (u32)map_to_sysmem(dev->driver->ops), + (u32)map_to_sysmem(dev_get_plat(dev))); + } + + return 0; +} + +static struct cmd_tbl armffa_commands[] = { + U_BOOT_CMD_MKENT(getpart, 1, 1, do_ffa_get_singular_partition_info, "", ""), + U_BOOT_CMD_MKENT(ping, 1, 1, do_ffa_msg_send_direct_req, "", ""), + U_BOOT_CMD_MKENT(devlist, 0, 1, do_ffa_dev_list, "", ""), +}; + +/** + * do_armffa - the armffa command main function + * @cmdtp: Command Table + * @flag: flags + * @argc: number of arguments + * @argv: arguments + * + * This function identifies which armffa subcommand to run. + * Then, it makes sure the arm_ffa device is probed and + * ready for use. + * Then, it runs the subcommand. + * + * Return: + * + * CMD_RET_SUCCESS: on success, otherwise failure + */ +static int do_armffa(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct cmd_tbl *armffa_cmd; + int ret; + + if (argc < 2) + return CMD_RET_USAGE; + + armffa_cmd = find_cmd_tbl(argv[1], armffa_commands, ARRAY_SIZE(armffa_commands)); + + argc -= 2; + argv += 2; + + if (!armffa_cmd || argc > armffa_cmd->maxargs) + return CMD_RET_USAGE; + + ret = ffa_bus_discover(NULL); + if (ret != 0) + return cmd_process_error(cmdtp, ret); + + if (!ffa_bus_ops_get()) + return -EINVAL; + + ret = armffa_cmd->cmd(armffa_cmd, flag, argc, argv); + + return cmd_process_error(armffa_cmd, ret); +} + +U_BOOT_CMD(armffa, 4, 1, do_armffa, + "Arm FF-A operations test command", + "getpart <partition UUID>\n" + " - lists the partition(s) info\n" + "ping <partition ID>\n" + " - sends a data pattern to the specified partition\n" + "devlist\n" + " - displays the arm_ffa device info\n"); diff --git a/drivers/firmware/arm-ffa/Kconfig b/drivers/firmware/arm-ffa/Kconfig index e4914b9bc7..be4df89d23 100644 --- a/drivers/firmware/arm-ffa/Kconfig +++ b/drivers/firmware/arm-ffa/Kconfig @@ -4,6 +4,7 @@ config ARM_FFA_TRANSPORT bool "Enable Arm Firmware Framework for Armv8-A driver" depends on DM && ARM64 select ARM_SMCCC + select CMD_ARMFFA select LIB_UUID select DEVRES help

Hi Abdellatif,
On Tue, 22 Nov 2022 at 06:18, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
Provide armffa command showcasing the use of the FF-A driver
The armffa command allows to query secure partitions data from the secure world and exchanging messages with the partitions using 64-bit FF-A direct messaging.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v8:
- update partition_info_get() second argument to be an SP count
- pass NULL device pointer to the FF-A bus discovery and operations
v7:
- adapt do_ffa_dev_list() following the recent update on uclass_first_device/uclass_next_device functions (they return void now)
- set armffa command to use 64-bit direct messaging
v4:
- remove pattern data in do_ffa_msg_send_direct_req
v3:
- use the new driver interfaces (partition_info_get, sync_send_receive) in armffa command
v2:
- replace use of ffa_helper_init_device function by
ffa_helper_bus_discover
v1:
- introduce armffa command
MAINTAINERS | 1 + cmd/Kconfig | 10 ++ cmd/Makefile | 2 + cmd/armffa.c | 237 +++++++++++++++++++++++++++++++ drivers/firmware/arm-ffa/Kconfig | 1 + 5 files changed, 251 insertions(+) create mode 100644 cmd/armffa.c
needs doc/usage also
diff --git a/MAINTAINERS b/MAINTAINERS index dcd32cf83a..aa4e87d9f8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -266,6 +266,7 @@ F: configs/cortina_presidio-asic-pnand_defconfig ARM FF-A M: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com S: Maintained +F: cmd/armffa.c F: doc/arch/arm64.ffa.rst F: drivers/firmware/arm-ffa/ F: include/arm_ffa.h diff --git a/cmd/Kconfig b/cmd/Kconfig index 1092fb9c91..060d87375f 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -924,6 +924,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 Arm FF-A driver
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 2444d116c0..c600d90d39 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -12,6 +12,8 @@ obj-y += panic.o obj-y += version.o
# command
+obj-$(CONFIG_CMD_ARMFFA) += armffa.o obj-$(CONFIG_CMD_ACPI) += acpi.o obj-$(CONFIG_CMD_ADDRMAP) += addrmap.o obj-$(CONFIG_CMD_AES) += aes.o diff --git a/cmd/armffa.c b/cmd/armffa.c new file mode 100644 index 0000000000..d2e8687bfb --- /dev/null +++ b/cmd/armffa.c @@ -0,0 +1,237 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include <arm_ffa.h> +#include <asm/io.h> +#include <common.h> +#include <command.h> +#include <dm.h> +#include <mapmem.h> +#include <stdlib.h>
+/**
- do_ffa_get_singular_partition_info - implementation of the getpart subcommand
do_ffa_get_singular_partition_info is too long!
- @cmdtp: Command Table
- @flag: flags
- @argc: number of arguments
- @argv: arguments
- This function queries the secure partition information which the UUID is provided
- as an argument. The function uses the arm_ffa driver partition_info_get operation
- 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_get_singular_partition_info(struct cmd_tbl *cmdtp, int flag, int argc,
char *const argv[])
+{
u32 count = 0;
int ret;
struct ffa_partition_info *parts_info;
u32 info_idx;
if (argc != 1)
return -EINVAL;
/* Mode 1: getting the number of secure partitions */
ret = ffa_bus_ops_get()->partition_info_get(NULL, argv[0], &count, NULL);
if (ret != 0) {
ffa_err("Failure in querying partitions count (error code: %d)", ret);
return ret;
}
if (!count) {
ffa_info("No secure partition found");
return ret;
}
/*
* pre-allocate a buffer to be filled by the driver
* with ffa_partition_info structs
*/
ffa_info("Pre-allocating %d partition(s) info structures", count);
parts_info = calloc(count, sizeof(struct ffa_partition_info));
if (!parts_info)
return -EINVAL;
/*
* ask the driver to fill the buffer with the SPs info
*/
ret = ffa_bus_ops_get()->partition_info_get(NULL, argv[0], &count, parts_info);
if (ret != 0) {
ffa_err("Failure in querying partition(s) info (error code: %d)", ret);
free(parts_info);
return ret;
}
/*
* SPs found , show the partition information
*/
for (info_idx = 0; info_idx < count ; info_idx++) {
ffa_info("Partition: id = 0x%x , exec_ctxt 0x%x , properties 0x%x",
parts_info[info_idx].id,
parts_info[info_idx].exec_ctxt,
parts_info[info_idx].properties);
}
free(parts_info);
return 0;
+}
+/**
- do_ffa_msg_send_direct_req - implementation of the ping subcommand
- @cmdtp: Command Table
- @flag: flags
- @argc: number of arguments
- @argv: arguments
- This function sends data to the secure partition which the ID is provided
- as an argument. The function uses the arm_ffa driver sync_send_receive operation
- to send data.
- Return:
- CMD_RET_SUCCESS: on success, otherwise failure
- */
+int do_ffa_msg_send_direct_req(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;
if (argc != 1)
return -EINVAL;
errno = 0;
part_id = strtoul(argv[0], NULL, 16);
if (errno) {
ffa_err("Invalid partition ID");
return -EINVAL;
}
ret = ffa_bus_ops_get()->sync_send_receive(NULL, part_id, &msg, 1);
This needs to use driver model properly. Get a pointer to the device, then call the uclass function in the header, which is implemented in ffa-uclass.c - see how it is done in other uclasses.
if (ret == 0) {
!ret
u8 cnt;
ffa_info("SP response:\n[LSB]");
for (cnt = 0;
cnt < sizeof(struct ffa_send_direct_data) / sizeof(u64);
cnt++)
ffa_info("0x%llx", ((u64 *)&msg)[cnt]);
} else {
ffa_err("Sending direct request error (%d)", ret);
}
return ret;
+}
+/**
- *do_ffa_dev_list - implementation of the devlist subcommand
- @cmdtp: [in] Command Table
- @flag: flags
- @argc: number of arguments
- @argv: arguments
- This function queries the devices belonging to the UCLASS_FFA
- class. Currently, one device is expected to show up: the arm_ffa device
- Return:
- CMD_RET_SUCCESS: on success, otherwise failure
- */
+int do_ffa_dev_list(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{
struct udevice *dev = NULL;
int i;
ffa_info("arm_ffa uclass entries:");
for (i = 0, uclass_first_device(UCLASS_FFA, &dev);
dev;
uclass_next_device(&dev), i++) {
ffa_info("entry %d - instance %08x, ops %08x, plat %08x",
i,
(u32)map_to_sysmem(dev),
(u32)map_to_sysmem(dev->driver->ops),
(u32)map_to_sysmem(dev_get_plat(dev)));
}
return 0;
+}
+static struct cmd_tbl armffa_commands[] = {
U_BOOT_CMD_MKENT(getpart, 1, 1, do_ffa_get_singular_partition_info, "", ""),
do_getpart() is enough
U_BOOT_CMD_MKENT(ping, 1, 1, do_ffa_msg_send_direct_req, "", ""),
U_BOOT_CMD_MKENT(devlist, 0, 1, do_ffa_dev_list, "", ""),
+};
+/**
- do_armffa - the armffa command main function
- @cmdtp: Command Table
- @flag: flags
- @argc: number of arguments
- @argv: arguments
- This function identifies which armffa subcommand to run.
- Then, it makes sure the arm_ffa device is probed and
- ready for use.
- Then, it runs the subcommand.
- Return:
- CMD_RET_SUCCESS: on success, otherwise failure
- */
+static int do_armffa(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{
struct cmd_tbl *armffa_cmd;
int ret;
if (argc < 2)
return CMD_RET_USAGE;
armffa_cmd = find_cmd_tbl(argv[1], armffa_commands, ARRAY_SIZE(armffa_commands));
argc -= 2;
argv += 2;
if (!armffa_cmd || argc > armffa_cmd->maxargs)
return CMD_RET_USAGE;
ret = ffa_bus_discover(NULL);
No, this should be handled by uclass_first_device(UCLASS_FFFA) etc.
if (ret != 0)
return cmd_process_error(cmdtp, ret);
if (!ffa_bus_ops_get())
return -EINVAL;
ret = armffa_cmd->cmd(armffa_cmd, flag, argc, argv);
return cmd_process_error(armffa_cmd, ret);
+}
+U_BOOT_CMD(armffa, 4, 1, do_armffa,
"Arm FF-A operations test command",
"getpart <partition UUID>\n"
" - lists the partition(s) info\n"
"ping <partition ID>\n"
" - sends a data pattern to the specified partition\n"
"devlist\n"
" - displays the arm_ffa device info\n");
diff --git a/drivers/firmware/arm-ffa/Kconfig b/drivers/firmware/arm-ffa/Kconfig index e4914b9bc7..be4df89d23 100644 --- a/drivers/firmware/arm-ffa/Kconfig +++ b/drivers/firmware/arm-ffa/Kconfig @@ -4,6 +4,7 @@ config ARM_FFA_TRANSPORT bool "Enable Arm Firmware Framework for Armv8-A driver" depends on DM && ARM64 select ARM_SMCCC
select CMD_ARMFFA
imply would be better, unless turning it off breaks things?
select LIB_UUID select DEVRES help
-- 2.17.1
Regards, Simon

Provide a Sandbox driver to emulate the FF-A ABIs
The emulated ABIs are those supported by the FF-A core driver and according to FF-A specification v1.0.
The Sandbox driver provides operations allowing the test application to read the status of all the inspected ABIs and perform functional tests based on that.
sandbox driver supports only 64-bit direct messaging.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v8: update ffa_bus_prvdata_get() to return a pointer rather than a pointer address
v7: state that sandbox driver supports only 64-bit direct messaging
v4: align sandbox driver with the new FF-A driver interfaces and new way of error handling
v1: introduce the sandbox driver
MAINTAINERS | 1 + configs/sandbox64_defconfig | 2 + configs/sandbox_defconfig | 2 + doc/arch/sandbox/sandbox.rst | 1 + drivers/firmware/arm-ffa/Kconfig | 9 +- drivers/firmware/arm-ffa/Makefile | 1 + drivers/firmware/arm-ffa/arm_ffa_prv.h | 15 +- drivers/firmware/arm-ffa/core.c | 22 +- drivers/firmware/arm-ffa/sandbox.c | 659 ++++++++++++++++++ .../firmware/arm-ffa/sandbox_arm_ffa_prv.h | 144 ++++ include/arm_ffa.h | 2 +- include/sandbox_arm_ffa.h | 91 +++ lib/efi_loader/efi_boottime.c | 2 +- 13 files changed, 938 insertions(+), 13 deletions(-) create mode 100644 drivers/firmware/arm-ffa/sandbox.c create mode 100644 drivers/firmware/arm-ffa/sandbox_arm_ffa_prv.h create mode 100644 include/sandbox_arm_ffa.h
diff --git a/MAINTAINERS b/MAINTAINERS index aa4e87d9f8..9197344df4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -270,6 +270,7 @@ F: cmd/armffa.c 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 sbabic@denx.de diff --git a/configs/sandbox64_defconfig b/configs/sandbox64_defconfig index cff166b1c1..141ffd1f85 100644 --- a/configs/sandbox64_defconfig +++ b/configs/sandbox64_defconfig @@ -258,3 +258,5 @@ CONFIG_FWU_MULTI_BANK_UPDATE=y CONFIG_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y +CONFIG_ARM_FFA_TRANSPORT=y +CONFIG_SANDBOX_FFA=y \ No newline at end of file diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig index 07fe0c3a7a..a146a6bf1b 100644 --- a/configs/sandbox_defconfig +++ b/configs/sandbox_defconfig @@ -333,3 +333,5 @@ CONFIG_TEST_FDTDEC=y CONFIG_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y +CONFIG_ARM_FFA_TRANSPORT=y +CONFIG_SANDBOX_FFA=y \ No newline at end of file diff --git a/doc/arch/sandbox/sandbox.rst b/doc/arch/sandbox/sandbox.rst index 34c4e06d9b..a42409a7b4 100644 --- a/doc/arch/sandbox/sandbox.rst +++ b/doc/arch/sandbox/sandbox.rst @@ -203,6 +203,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 be4df89d23..b86f16d778 100644 --- a/drivers/firmware/arm-ffa/Kconfig +++ b/drivers/firmware/arm-ffa/Kconfig @@ -2,8 +2,8 @@
config ARM_FFA_TRANSPORT bool "Enable Arm Firmware Framework for Armv8-A driver" - depends on DM && ARM64 - select ARM_SMCCC + depends on DM && (ARM64 || SANDBOX) + select ARM_SMCCC if !SANDBOX select CMD_ARMFFA select LIB_UUID select DEVRES @@ -29,3 +29,8 @@ config ARM_FFA_TRANSPORT
For more details about the FF-A driver, please refer to doc/arch/arm64.ffa.rst
+config SANDBOX_FFA + bool "FF-A Sandbox driver" + depends on ARM_FFA_TRANSPORT && SANDBOX + help + This emulates the FF-A handling under Sandbox and allows to test the FF-A driver diff --git a/drivers/firmware/arm-ffa/Makefile b/drivers/firmware/arm-ffa/Makefile index 043a8915be..0d21d6b47a 100644 --- a/drivers/firmware/arm-ffa/Makefile +++ b/drivers/firmware/arm-ffa/Makefile @@ -4,3 +4,4 @@ # Abdellatif El Khlifi, Arm Limited, abdellatif.elkhlifi@arm.com.
obj-y += arm-ffa-uclass.o core.o +obj-$(CONFIG_SANDBOX_FFA) += sandbox.o diff --git a/drivers/firmware/arm-ffa/arm_ffa_prv.h b/drivers/firmware/arm-ffa/arm_ffa_prv.h index 4eea7dc036..bbc8b87069 100644 --- a/drivers/firmware/arm-ffa/arm_ffa_prv.h +++ b/drivers/firmware/arm-ffa/arm_ffa_prv.h @@ -19,6 +19,16 @@ /* FF-A core driver name */ #define FFA_DRV_NAME "arm_ffa"
+/* The FF-A SMC function definitions */ + +#if CONFIG_IS_ENABLED(SANDBOX_FFA) +#include "sandbox_arm_ffa.h" +#else +typedef struct arm_smccc_1_2_regs ffa_value_t; +#endif + +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) @@ -103,11 +113,6 @@ struct ffa_abi_errmap { #define FFA_ERRMAP_COUNT (FFA_LAST_ID - FFA_FIRST_ID + 1) #define FFA_ID_TO_ERRMAP_ID(ffa_id) ((ffa_id) - FFA_FIRST_ID)
-/* The FF-A SMC function definitions */ - -typedef struct arm_smccc_1_2_regs ffa_value_t; -typedef void (*invoke_ffa_fn_t)(ffa_value_t args, ffa_value_t *res); - /* * struct ffa_partition_uuid - 16 bytes UUID transmitted by FFA_PARTITION_INFO_GET * @a1-4: 32-bit words access to the UUID data diff --git a/drivers/firmware/arm-ffa/core.c b/drivers/firmware/arm-ffa/core.c index 0b1f8e6a07..560603b28b 100644 --- a/drivers/firmware/arm-ffa/core.c +++ b/drivers/firmware/arm-ffa/core.c @@ -1072,6 +1072,7 @@ static int ffa_msg_send_direct_req(struct udevice *dev, u16 dst_part_id, return ffa_to_std_errno(ffa_errno); }
+#if !CONFIG_IS_ENABLED(SANDBOX_FFA) /** * __arm_ffa_fn_smc - SMC wrapper * @args: FF-A ABI arguments to be copied to Xn registers @@ -1085,6 +1086,7 @@ void __arm_ffa_fn_smc(ffa_value_t args, ffa_value_t *res) { arm_smccc_1_2_smc(&args, res); } +#endif
/** * ffa_set_smc_conduit - Set the SMC conduit @@ -1098,7 +1100,12 @@ void __arm_ffa_fn_smc(ffa_value_t args, ffa_value_t *res) */ static int ffa_set_smc_conduit(void) { - ffa_priv_data->invoke_ffa_fn = __arm_ffa_fn_smc; +#if CONFIG_IS_ENABLED(SANDBOX_FFA) + ffa_priv_data->invoke_ffa_fn = sandbox_arm_ffa_smccc_smc; + ffa_info("Using SMC emulation"); +#else + ffa_priv_data->invoke_ffa_fn = __arm_ffa_fn_smc; +#endif
if (!ffa_priv_data->invoke_ffa_fn) { ffa_err("failure to set the invoke function"); @@ -1275,17 +1282,18 @@ struct ffa_prvdata *ffa_bus_prvdata_get(void) }
/** - * ffa_bus_discover - discover FF-A bus and probe arm_ffa device + * ffa_bus_discover - discover FF-A bus and probe arm_ffa and sandbox_arm_ffa devices * @pdev: the address of a device pointer (to be filled when the arm_ffa bus device is created * successfully) * * This function makes sure the FF-A bus is discoverable. - * When probing succeeds FF-A discovery is done. The arm_ffa device is ready to use. + * When probing succeeds FF-A discovery is done. The arm_ffa and sandbox_arm_ffa devices + * are ready to use. * * When the bus was already discovered successfully the discovery will not run again. * * Arm FF-A transport is implemented through arm_ffa u-boot device managing the FF-A - * communication. + * communication. In Sandbox mode sandbox_arm_ffa is used to test arm_ffa driver. * All FF-A clients should use the arm_ffa device to use the FF-A transport. * * Return: @@ -1299,6 +1307,12 @@ int ffa_bus_discover(struct udevice **pdev) if (!ffa_priv_data) { ret = ffa_device_get(pdev);
+#if CONFIG_IS_ENABLED(SANDBOX_FFA) + if (ret == 0) + ret = sandbox_ffa_device_get(); +#endif + } + return ret; }
diff --git a/drivers/firmware/arm-ffa/sandbox.c b/drivers/firmware/arm-ffa/sandbox.c new file mode 100644 index 0000000000..16f1ca926e --- /dev/null +++ b/drivers/firmware/arm-ffa/sandbox.c @@ -0,0 +1,659 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include "sandbox_arm_ffa_prv.h" +#include <asm/global_data.h> +#include <common.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <dm/root.h> +#include <linux/errno.h> +#include <linux/sizes.h> +#include <mapmem.h> +#include <string.h> + +DECLARE_GLOBAL_DATA_PTR; + +/** + * The device private data structure containing all the emulated secure world data + */ +static struct sandbox_ffa_prvdata sandbox_ffa_priv_data = {0}; + +/* 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_SP2_ID, .exec_ctxt = 0x9587, .properties = 0x45325621 }, + .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_SP3_ID, .exec_ctxt = 0x7687, .properties = 0x23325621 }, + .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, + } + } + +}; + +/* + * Driver functions + */ + +/** + * sandbox_ffa_get_device - probes the sandbox_arm_ffa device + * + * This function makes sure the sandbox_arm_ffa device is probed + * This function makes sure the sandbox_arm_ffa device is + * created, bound to this driver, probed and ready to use. + * + * sandbox_arm_ffa depends on arm_ffa device. This dependency is + * handled by ffa_bus_discover function. arm_ffa is probed first then + * sandbox_arm_ffa. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int sandbox_ffa_device_get(void) +{ + int ret; + + if (sandbox_ffa_priv_data.dev) + return 0; + + ret = device_bind(dm_root(), + DM_DRIVER_GET(sandbox_arm_ffa), + FFA_SANDBOX_DRV_NAME, + NULL, + ofnode_null(), + &sandbox_ffa_priv_data.dev); + if (ret) { + sandbox_ffa_priv_data.dev = NULL; + return ret; + } + + ret = device_probe(sandbox_ffa_priv_data.dev); + if (ret) { + ffa_err("[Sandbox] can not probe the device"); + device_unbind(sandbox_ffa_priv_data.dev); + sandbox_ffa_priv_data.dev = NULL; + return ret; + } + + return 0; +} + +/** + * sandbox_ffa_version - Emulated FFA_VERSION handler function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_VERSION FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_version) +{ + sandbox_ffa_priv_data.fwk_version = FFA_VERSION_1_0; + res->a0 = sandbox_ffa_priv_data.fwk_version; + + /* x1-x7 MBZ */ + memset(FFA_X1X7_MBZ_REG_START, 0, FFA_X1X7_MBZ_CNT * sizeof(unsigned long)); + + return 0; +} + +/** + * sandbox_ffa_id_get - Emulated FFA_ID_GET handler function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_ID_GET FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_id_get) +{ + res->a0 = FFA_SMC_32(FFA_SUCCESS); + res->a1 = 0; + + sandbox_ffa_priv_data.id = NS_PHYS_ENDPOINT_ID; + res->a2 = sandbox_ffa_priv_data.id; + + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, 0, FFA_X3X7_MBZ_CNT * sizeof(unsigned long)); + + return 0; +} + +/** + * sandbox_ffa_features - Emulated FFA_FEATURES handler function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_FEATURES FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_features) +{ + 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(unsigned long)); + } else { + res->a0 = FFA_SMC_32(FFA_ERROR); + res->a2 = FFA_ERR_STAT_NOT_SUPPORTED; + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, + 0, FFA_X3X7_MBZ_CNT * sizeof(unsigned long)); + ffa_err("[Sandbox] FF-A interface 0x%lx not implemented", pargs->a1); + } + + res->a1 = 0; + + return 0; +} + +/** + * sandbox_ffa_partition_info_get - Emulated FFA_PARTITION_INFO_GET handler function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_PARTITION_INFO_GET FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_partition_info_get) +{ + struct ffa_partition_info *rxbuf_desc_info = NULL; + u32 descs_cnt; + u32 descs_size_bytes; + + res->a0 = FFA_SMC_32(FFA_ERROR); + + if (!sandbox_ffa_priv_data.pair.rxbuf) { + res->a2 = FFA_ERR_STAT_DENIED; + goto cleanup; + } + + if (sandbox_ffa_priv_data.pair_info.rxbuf_owned) { + res->a2 = FFA_ERR_STAT_BUSY; + goto cleanup; + } + + if (!sandbox_ffa_priv_data.partitions.descs) { + sandbox_ffa_priv_data.partitions.descs = sandbox_partitions; + sandbox_ffa_priv_data.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 descriptors buffer size */ + if ((sandbox_ffa_priv_data.pair_info.rxtx_buf_size * SZ_4K) < descs_size_bytes) { + res->a2 = FFA_ERR_STAT_NO_MEMORY; + goto cleanup; + } + + rxbuf_desc_info = (struct ffa_partition_info *)sandbox_ffa_priv_data.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++) = + sandbox_ffa_priv_data.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 */ + sandbox_ffa_priv_data.pair_info.rxbuf_owned = 1; + + goto cleanup; + } + + /* + * A UUID is specified. Return the information of all partitions matching + * the UUID + */ + + for (descs_cnt = 0 ; descs_cnt < SANDBOX_PARTITIONS_CNT ; descs_cnt++) + if (pargs->a1 == sandbox_ffa_priv_data.partitions.descs[descs_cnt].sp_uuid.a1 && + pargs->a2 == sandbox_ffa_priv_data.partitions.descs[descs_cnt].sp_uuid.a2 && + pargs->a3 == sandbox_ffa_priv_data.partitions.descs[descs_cnt].sp_uuid.a3 && + pargs->a4 == sandbox_ffa_priv_data.partitions.descs[descs_cnt].sp_uuid.a4) { + *(rxbuf_desc_info++) = + sandbox_ffa_priv_data.partitions.descs[descs_cnt].info; + } + + if (rxbuf_desc_info != ((struct ffa_partition_info *)sandbox_ffa_priv_data.pair.rxbuf)) { + res->a0 = FFA_SMC_32(FFA_SUCCESS); + /* store the partitions count */ + res->a2 = (unsigned long) + (rxbuf_desc_info - (struct ffa_partition_info *) + sandbox_ffa_priv_data.pair.rxbuf); + + /* transfer ownership to the consumer: the non secure world */ + sandbox_ffa_priv_data.pair_info.rxbuf_owned = 1; + } else { + /* Unrecognized UUID */ + res->a2 = FFA_ERR_STAT_INVALID_PARAMETERS; + } + +cleanup: + + ffa_err("[Sandbox] FFA_PARTITION_INFO_GET (%ld)", res->a2); + + res->a1 = 0; + + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, 0, FFA_X3X7_MBZ_CNT * sizeof(unsigned long)); + + return 0; +} + +/** + * sandbox_ffa_rxtx_map - Emulated FFA_RXTX_MAP handler function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_RXTX_MAP FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_rxtx_map) +{ + res->a0 = FFA_SMC_32(FFA_ERROR); + + if (sandbox_ffa_priv_data.pair.txbuf && sandbox_ffa_priv_data.pair.rxbuf) { + res->a2 = FFA_ERR_STAT_DENIED; + goto feedback; + } + + if (pargs->a3 >= RXTX_BUFFERS_MIN_PAGES && pargs->a1 && pargs->a2) { + sandbox_ffa_priv_data.pair.txbuf = pargs->a1; + sandbox_ffa_priv_data.pair.rxbuf = pargs->a2; + sandbox_ffa_priv_data.pair_info.rxtx_buf_size = pargs->a3; + sandbox_ffa_priv_data.pair_info.rxbuf_mapped = 1; + res->a0 = FFA_SMC_32(FFA_SUCCESS); + res->a2 = 0; + goto feedback; + } + + if (!pargs->a1 || !pargs->a2) + res->a2 = FFA_ERR_STAT_INVALID_PARAMETERS; + else + res->a2 = FFA_ERR_STAT_NO_MEMORY; + + ffa_err("[Sandbox] error in FFA_RXTX_MAP arguments (%d)", (int)res->a2); + +feedback: + + res->a1 = 0; + + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, + 0, FFA_X3X7_MBZ_CNT * sizeof(unsigned long)); + + return 0; +} + +/** + * sandbox_ffa_rxtx_unmap - Emulated FFA_RXTX_UNMAP handler function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_RXTX_UNMAP FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_rxtx_unmap) +{ + res->a0 = FFA_SMC_32(FFA_ERROR); + res->a2 = FFA_ERR_STAT_INVALID_PARAMETERS; + + if (GET_NS_PHYS_ENDPOINT_ID(pargs->a1) != sandbox_ffa_priv_data.id) + goto feedback; + + if (sandbox_ffa_priv_data.pair.txbuf && sandbox_ffa_priv_data.pair.rxbuf) { + sandbox_ffa_priv_data.pair.txbuf = 0; + sandbox_ffa_priv_data.pair.rxbuf = 0; + sandbox_ffa_priv_data.pair_info.rxtx_buf_size = 0; + sandbox_ffa_priv_data.pair_info.rxbuf_mapped = 0; + res->a0 = FFA_SMC_32(FFA_SUCCESS); + res->a2 = 0; + goto feedback; + } + + ffa_err("[Sandbox] No buffer pair registered on behalf of the caller"); + +feedback: + + res->a1 = 0; + + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, + 0, FFA_X3X7_MBZ_CNT * sizeof(unsigned long)); + + return 0; +} + +/** + * sandbox_ffa_rx_release - Emulated FFA_RX_RELEASE handler function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_RX_RELEASE FF-A function. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +SANDBOX_SMC_FFA_ABI(ffa_rx_release) +{ + if (!sandbox_ffa_priv_data.pair_info.rxbuf_owned) { + res->a0 = FFA_SMC_32(FFA_ERROR); + res->a2 = FFA_ERR_STAT_DENIED; + } else { + sandbox_ffa_priv_data.pair_info.rxbuf_owned = 0; + res->a0 = FFA_SMC_32(FFA_SUCCESS); + res->a2 = 0; + } + + res->a1 = 0; + + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, + 0, FFA_X3X7_MBZ_CNT * sizeof(unsigned long)); + + return 0; +} + +/** + * sandbox_ffa_sp_valid - Checks SP validity + * @part_id: partition ID to check + * + * This is the function searches the input ID in the descriptors table. + * + * Return: + * + * 1 on success (Partition found). Otherwise, failure + */ +static int sandbox_ffa_sp_valid(u16 part_id) +{ + u32 descs_cnt; + + for (descs_cnt = 0 ; descs_cnt < SANDBOX_PARTITIONS_CNT ; descs_cnt++) + if (sandbox_ffa_priv_data.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 function + * @{a0-a7} , res: The SMC call arguments and return structure. + * + * This is the function that emulates FFA_MSG_SEND_DIRECT_{REQ,RESP} + * FF-A functions. 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 + */ +SANDBOX_SMC_FFA_ABI(ffa_msg_send_direct_req) +{ + u16 part_id; + + part_id = GET_DST_SP_ID(pargs->a1); + + if ((GET_NS_PHYS_ENDPOINT_ID(pargs->a1) != sandbox_ffa_priv_data.id) || + !sandbox_ffa_sp_valid(part_id) || + pargs->a2) { + res->a0 = FFA_SMC_32(FFA_ERROR); + res->a1 = 0; + res->a2 = FFA_ERR_STAT_INVALID_PARAMETERS; + + /* x3-x7 MBZ */ + memset(FFA_X3_MBZ_REG_START, + 0, FFA_X3X7_MBZ_CNT * sizeof(unsigned long)); + + return 0; + } + + res->a0 = FFA_SMC_64(FFA_MSG_SEND_DIRECT_RESP); + + res->a1 = PREP_SRC_SP_ID(part_id) | + PREP_NS_PHYS_ENDPOINT_ID(sandbox_ffa_priv_data.id); + + res->a2 = 0; + + /* + * return 0xff bytes as a response + */ + res->a3 = 0xffffffffffffffff; + res->a4 = 0xffffffffffffffff; + res->a5 = 0xffffffffffffffff; + res->a6 = 0xffffffffffffffff; + res->a7 = 0xffffffffffffffff; + + return 0; +} + +/** + * sandbox_ffa_get_prv_data - Returns the pointer to FF-A core pivate data + * @func_data: Pointer to the FF-A function arguments container structure + * + * This is the handler that returns the address of the FF-A core pivate data. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int sandbox_ffa_get_prv_data(struct ffa_sandbox_data *func_data) +{ + if (!func_data) + return -EINVAL; + + if (!func_data->data0 || func_data->data0_size != sizeof(struct ffa_prvdata *)) + return -EINVAL; + + if (!func_data->data1 || func_data->data1_size != sizeof(struct sandbox_ffa_prvdata *)) + return -EINVAL; + + *((struct ffa_prvdata **)func_data->data0) = ffa_bus_prvdata_get(); + *((struct sandbox_ffa_prvdata **)func_data->data1) = &sandbox_ffa_priv_data; + + return 0; +} + +/** + * sandbox_ffa_get_rxbuf_flags - Reading the mapping/ownership flags + * @queried_func_id: The FF-A function to be queried + * @func_data: Pointer to the FF-A function arguments container structure + * + * This is the handler that queries 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(u32 queried_func_id, struct ffa_sandbox_data *func_data) +{ + 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) = sandbox_ffa_priv_data.pair_info.rxbuf_mapped; + return 0; + case FFA_RX_RELEASE: + *((u8 *)func_data->data0) = sandbox_ffa_priv_data.pair_info.rxbuf_owned; + return 0; + default: + ffa_err("[Sandbox] The querried FF-A interface flag (%d) undefined", + queried_func_id); + return -EINVAL; + } +} + +/** + * sandbox_ffa_query_core_state - The driver dispatcher function + * @queried_func_id: The FF-A function to be queried + * @func_data: Pointer to the FF-A function arguments container structure + * + * Queries the status of FF-A ABI specified in the input argument. + * + * Return: + * + * 0 on success. Otherwise, failure + */ +int sandbox_ffa_query_core_state(u32 queried_func_id, struct ffa_sandbox_data *func_data) +{ + switch (queried_func_id) { + case FFA_VERSION: + case FFA_ID_GET: + case FFA_FEATURES: + return sandbox_ffa_get_prv_data(func_data); + case FFA_RXTX_MAP: + case FFA_RXTX_UNMAP: + case FFA_RX_RELEASE: + return sandbox_ffa_get_rxbuf_flags(queried_func_id, func_data); + default: + ffa_err("[Sandbox] The querried FF-A interface (%d) undefined", 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 + * + * Sandbox driver emulates the FF-A ABIs SMC call using this function. + * 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; + + switch (args.a0) { + case FFA_SMC_32(FFA_VERSION): + ret = sandbox_ffa_version(&args, res); + break; + case FFA_SMC_32(FFA_PARTITION_INFO_GET): + ret = sandbox_ffa_partition_info_get(&args, res); + break; + case FFA_SMC_32(FFA_RXTX_UNMAP): + ret = sandbox_ffa_rxtx_unmap(&args, res); + break; + case FFA_SMC_64(FFA_MSG_SEND_DIRECT_REQ): + ret = sandbox_ffa_msg_send_direct_req(&args, res); + break; + case FFA_SMC_32(FFA_ID_GET): + ret = sandbox_ffa_id_get(&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(&args, res); + break; + case FFA_SMC_32(FFA_RX_RELEASE): + ret = sandbox_ffa_rx_release(&args, res); + break; + default: + ffa_err("[Sandbox] Undefined FF-A interface (0x%lx)", args.a0); + } + + if (ret != 0) + ffa_err("[Sandbox] FF-A ABI internal failure (%d)", ret); +} + +/** + * sandbox_ffa_probe - The driver probe function + * @dev: the sandbox_arm_ffa device + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int sandbox_ffa_probe(struct udevice *dev) +{ + return 0; +} + +/** + * sandbox_ffa_remove - The driver remove function + * @dev: the sandbox_arm_ffa device + * + * Return: + * + * 0 on success. Otherwise, failure + */ +static int sandbox_ffa_remove(struct udevice *dev) +{ + ffa_info("[Sandbox] removing the device"); + memset(&sandbox_ffa_priv_data, 0, sizeof(sandbox_ffa_priv_data)); + return 0; +} + +/** + * Declaring the sandbox_arm_ffa driver under UCLASS_FFA + */ +U_BOOT_DRIVER(sandbox_arm_ffa) = { + .name = FFA_SANDBOX_DRV_NAME, + .id = UCLASS_FFA, + .probe = sandbox_ffa_probe, + .remove = sandbox_ffa_remove, +}; diff --git a/drivers/firmware/arm-ffa/sandbox_arm_ffa_prv.h b/drivers/firmware/arm-ffa/sandbox_arm_ffa_prv.h new file mode 100644 index 0000000000..4db57f5092 --- /dev/null +++ b/drivers/firmware/arm-ffa/sandbox_arm_ffa_prv.h @@ -0,0 +1,144 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __SANDBOX_ARM_FFA_PRV_H +#define __SANDBOX_ARM_FFA_PRV_H + +#include "arm_ffa_prv.h" +#include <sandbox_arm_ffa.h> + +/* + * This header is private. It is exclusively used by the Sandbox FF-A driver + */ + +/* FF-A core driver name */ +#define FFA_SANDBOX_DRV_NAME "sandbox_arm_ffa" + +/* FF-A ABIs internal error codes (as defined by the spec) */ + +#define FFA_ERR_STAT_NOT_SUPPORTED -1 +#define FFA_ERR_STAT_INVALID_PARAMETERS -2 +#define FFA_ERR_STAT_NO_MEMORY -3 +#define FFA_ERR_STAT_BUSY -4 +#define FFA_ERR_STAT_DENIED -6 + +/* Providing Arm SMCCC declarations to sandbox */ + +#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)) + +/* 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) + +/* secure partitions count */ +#define SANDBOX_PARTITIONS_CNT (4) + +/* 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: the consumer) + * @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 as set by the FF-A core driver + * + * Data structure hosting 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_prvdata - the driver private data structure + * + * @dev: The arm_ffa device under u-boot driver model + * @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 + * @conduit: The selected conduit + * + * The driver data structure hosting all the emulated secure world data. + */ +struct sandbox_ffa_prvdata { + struct udevice *dev; + u32 fwk_version; + u16 id; + struct ffa_partitions partitions; + struct ffa_rxtxpair pair; + struct ffa_rxtxpair_info pair_info; +}; + +#define SANDBOX_SMC_FFA_ABI(ffabi) static int sandbox_##ffabi(ffa_value_t *pargs, ffa_value_t *res) + +#endif diff --git a/include/arm_ffa.h b/include/arm_ffa.h index 74b16174c2..b88904fe50 100644 --- a/include/arm_ffa.h +++ b/include/arm_ffa.h @@ -90,7 +90,7 @@ struct ffa_bus_ops { const struct ffa_bus_ops *ffa_bus_ops_get(void);
/** - * ffa_bus_discover - discover FF-A bus and probes the arm_ffa device + * ffa_bus_discover - discover FF-A bus and probes the arm_ffa and sandbox_arm_ffa devices */ int ffa_bus_discover(struct udevice **pdev);
diff --git a/include/sandbox_arm_ffa.h b/include/sandbox_arm_ffa.h new file mode 100644 index 0000000000..d5df16f282 --- /dev/null +++ b/include/sandbox_arm_ffa.h @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#ifndef __SANDBOX_ARM_FFA_H +#define __SANDBOX_ARM_FFA_H + +#include <arm_ffa.h> + +/** + * struct sandbox_smccc_1_2_regs - Arguments for or Results from emulated SMC call + * @a0-a17 argument values from registers 0 to 17 + */ +struct sandbox_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; +}; + +typedef struct sandbox_smccc_1_2_regs ffa_value_t; + +/* UUIDs of services supported by the sandbox driver */ +#define SANDBOX_SERVICE1_UUID "ed32d533-4209-99e6-2d72-cdd998a79cc0" +#define SANDBOX_SERVICE2_UUID "ed32d544-4209-99e6-2d72-cdd998a79cc0" +#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" + +#define SANDBOX_SP_COUNT_PER_VALID_SERVICE 2 + +/** + * struct ffa_sandbox_data - generic data structure used to exchange + * data between test cases and the sandbox driver + * @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 + * + * Using this structure sandbox test cases can pass various types of data with different sizes. + */ +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 driver public functions + */ + +/** + * sandbox_ffa_query_core_state - Queries the status of FF-A ABIs + */ +int sandbox_ffa_query_core_state(u32 queried_func_id, struct ffa_sandbox_data *func_data); + +/** + * sandbox_ffa_get_device - create, bind and probe the sandbox_arm_ffa device + */ +int sandbox_ffa_device_get(void); + +/** + * sandbox_arm_ffa_smccc_smc - FF-A SMC call emulation + */ +void sandbox_arm_ffa_smccc_smc(ffa_value_t args, ffa_value_t *res); + +#endif diff --git a/lib/efi_loader/efi_boottime.c b/lib/efi_loader/efi_boottime.c index 8949aca250..9cf089f162 100644 --- a/lib/efi_loader/efi_boottime.c +++ b/lib/efi_loader/efi_boottime.c @@ -2185,7 +2185,7 @@ static efi_status_t EFIAPI efi_exit_boot_services(efi_handle_t image_handle, dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL); }
-#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT) +#if CONFIG_IS_ENABLED(ARM_FFA_TRANSPORT) && !CONFIG_IS_ENABLED(SANDBOX_FFA) /* unmap FF-A RX/TX buffers */ if (ffa_bus_ops_get()->rxtx_unmap(NULL)) log_err("Can't unmap FF-A RX/TX buffers\n");

Hi Abdellatif,
On Tue, 22 Nov 2022 at 06:18, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
Provide a Sandbox driver to emulate the FF-A ABIs
The emulated ABIs are those supported by the FF-A core driver and according to FF-A specification v1.0.
The Sandbox driver provides operations allowing the test application to read the status of all the inspected ABIs and perform functional tests based on that.
sandbox driver supports only 64-bit direct messaging.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v8: update ffa_bus_prvdata_get() to return a pointer rather than a pointer address
v7: state that sandbox driver supports only 64-bit direct messaging
v4: align sandbox driver with the new FF-A driver interfaces and new way of error handling
v1: introduce the sandbox driver
MAINTAINERS | 1 + configs/sandbox64_defconfig | 2 + configs/sandbox_defconfig | 2 + doc/arch/sandbox/sandbox.rst | 1 + drivers/firmware/arm-ffa/Kconfig | 9 +- drivers/firmware/arm-ffa/Makefile | 1 + drivers/firmware/arm-ffa/arm_ffa_prv.h | 15 +-
Can those changes be done in the previous patch where you introduced this file?
drivers/firmware/arm-ffa/core.c | 22 +- drivers/firmware/arm-ffa/sandbox.c | 659 ++++++++++++++++++ .../firmware/arm-ffa/sandbox_arm_ffa_prv.h | 144 ++++ include/arm_ffa.h | 2 +- include/sandbox_arm_ffa.h | 91 +++ lib/efi_loader/efi_boottime.c | 2 +- 13 files changed, 938 insertions(+), 13 deletions(-) create mode 100644 drivers/firmware/arm-ffa/sandbox.c create mode 100644 drivers/firmware/arm-ffa/sandbox_arm_ffa_prv.h create mode 100644 include/sandbox_arm_ffa.h
diff --git a/MAINTAINERS b/MAINTAINERS index aa4e87d9f8..9197344df4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -270,6 +270,7 @@ F: cmd/armffa.c 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 sbabic@denx.de diff --git a/configs/sandbox64_defconfig b/configs/sandbox64_defconfig index cff166b1c1..141ffd1f85 100644 --- a/configs/sandbox64_defconfig +++ b/configs/sandbox64_defconfig @@ -258,3 +258,5 @@ CONFIG_FWU_MULTI_BANK_UPDATE=y CONFIG_UNIT_TEST=y CONFIG_UT_TIME=y CONFIG_UT_DM=y +CONFIG_ARM_FFA_TRANSPORT=y +CONFIG_SANDBOX_FFA=y \ No newline at end of file
Please add one
[..]
diff --git a/drivers/firmware/arm-ffa/core.c b/drivers/firmware/arm-ffa/core.c index 0b1f8e6a07..560603b28b 100644 --- a/drivers/firmware/arm-ffa/core.c +++ b/drivers/firmware/arm-ffa/core.c @@ -1072,6 +1072,7 @@ static int ffa_msg_send_direct_req(struct udevice *dev, u16 dst_part_id, return ffa_to_std_errno(ffa_errno); }
+#if !CONFIG_IS_ENABLED(SANDBOX_FFA) /**
- __arm_ffa_fn_smc - SMC wrapper
- @args: FF-A ABI arguments to be copied to Xn registers
@@ -1085,6 +1086,7 @@ void __arm_ffa_fn_smc(ffa_value_t args, ffa_value_t *res) { arm_smccc_1_2_smc(&args, res); } +#endif
/**
- ffa_set_smc_conduit - Set the SMC conduit
@@ -1098,7 +1100,12 @@ void __arm_ffa_fn_smc(ffa_value_t args, ffa_value_t *res) */ static int ffa_set_smc_conduit(void) {
ffa_priv_data->invoke_ffa_fn = __arm_ffa_fn_smc;
+#if CONFIG_IS_ENABLED(SANDBOX_FFA)
ffa_priv_data->invoke_ffa_fn = sandbox_arm_ffa_smccc_smc;
ffa_info("Using SMC emulation");
+#else
ffa_priv_data->invoke_ffa_fn = __arm_ffa_fn_smc;
+#endif
if (!ffa_priv_data->invoke_ffa_fn) { ffa_err("failure to set the invoke function");
@@ -1275,17 +1282,18 @@ struct ffa_prvdata *ffa_bus_prvdata_get(void) }
/**
- ffa_bus_discover - discover FF-A bus and probe arm_ffa device
- ffa_bus_discover - discover FF-A bus and probe arm_ffa and sandbox_arm_ffa devices
- @pdev: the address of a device pointer (to be filled when the arm_ffa bus device is created
successfully)
- This function makes sure the FF-A bus is discoverable.
- When probing succeeds FF-A discovery is done. The arm_ffa device is ready to use.
- When probing succeeds FF-A discovery is done. The arm_ffa and sandbox_arm_ffa devices
- are ready to use.
- When the bus was already discovered successfully the discovery will not run again.
- Arm FF-A transport is implemented through arm_ffa u-boot device managing the FF-A
- communication.
- communication. In Sandbox mode sandbox_arm_ffa is used to test arm_ffa driver.
- All FF-A clients should use the arm_ffa device to use the FF-A transport.
- Return:
@@ -1299,6 +1307,12 @@ int ffa_bus_discover(struct udevice **pdev) if (!ffa_priv_data) { ret = ffa_device_get(pdev);
Against this needs to use driver model properly. There appears to be no actual device??
+#if CONFIG_IS_ENABLED(SANDBOX_FFA)
if (ret == 0)
ret = sandbox_ffa_device_get();
+#endif
}
return ret;
}
diff --git a/drivers/firmware/arm-ffa/sandbox.c b/drivers/firmware/arm-ffa/sandbox.c new file mode 100644 index 0000000000..16f1ca926e --- /dev/null +++ b/drivers/firmware/arm-ffa/sandbox.c @@ -0,0 +1,659 @@
This seems OK, but I am still confused as to why the device handling is done outside driver model.
[..]
Regards, Simon

Add functional test cases for the FF-A core driver
These tests rely on the FF-A Sandbox driver which helps in inspecting the FF-A core driver.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v8:
* update partition_info_get() second argument to be an SP count * pass NULL device pointer to the FF-A bus discovery and operations
v7: set the tests to use 64-bit direct messaging
v4: align sandbox tests with the new FF-A driver interfaces and new way of error handling
v1: introduce sandbox tests
MAINTAINERS | 1 + test/dm/Makefile | 2 + test/dm/ffa.c | 392 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 395 insertions(+) create mode 100644 test/dm/ffa.c
diff --git a/MAINTAINERS b/MAINTAINERS index 9197344df4..7a2cabfb2d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -271,6 +271,7 @@ F: doc/arch/arm64.ffa.rst F: drivers/firmware/arm-ffa/ F: include/arm_ffa.h F: include/sandbox_arm_ffa.h +F: test/dm/ffa.c
ARM FREESCALE IMX M: Stefano Babic sbabic@denx.de diff --git a/test/dm/Makefile b/test/dm/Makefile index 7a79b6e1a2..85e99e1c12 100644 --- a/test/dm/Makefile +++ b/test/dm/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0+ # # Copyright (c) 2013 Google, Inc +# (C) Copyright 2022 ARM Limited
obj-$(CONFIG_UT_DM) += test-dm.o
@@ -85,6 +86,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_SANDBOX_FFA) += 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 0000000000..128d8626a7 --- /dev/null +++ b/test/dm/ffa.c @@ -0,0 +1,392 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Functional tests for UCLASS_FFA class + * + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <console.h> +#include <dm.h> +#include <dm/test.h> +#include "../../drivers/firmware/arm-ffa/sandbox_arm_ffa_prv.h" +#include <sandbox_arm_ffa.h> +#include <test/test.h> +#include <test/ut.h> + +/* Macros */ + +#define LOG_MSG_SZ (100) +#define LOG_CMD_SZ (LOG_MSG_SZ * 2) + +/* Functional tests for the UCLASS_FFA */ + +static int dm_test_ffa_log(struct unit_test_state *uts, char *msg) +{ + char cmd[LOG_CMD_SZ] = {0}; + + console_record_reset(); + + snprintf(cmd, LOG_CMD_SZ, "echo "%s"", msg); + run_command(cmd, 0); + + ut_assert_console_end(); + + return CMD_RET_SUCCESS; +} + +static int check_fwk_version(struct ffa_prvdata *prvdata, struct sandbox_ffa_prvdata *sdx_prvdata, + struct unit_test_state *uts) +{ + if (prvdata->fwk_version != sdx_prvdata->fwk_version) { + char msg[LOG_MSG_SZ] = {0}; + + snprintf(msg, LOG_MSG_SZ, + "[%s]: Error: framework version: core = 0x%x , sandbox = 0x%x", __func__, + prvdata->fwk_version, + sdx_prvdata->fwk_version); + + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + return CMD_RET_SUCCESS; +} + +static int check_endpoint_id(struct ffa_prvdata *prvdata, struct unit_test_state *uts) +{ + if (prvdata->id) { + char msg[LOG_MSG_SZ] = {0}; + + snprintf(msg, LOG_MSG_SZ, + "[%s]: Error: endpoint id: core = 0x%x", __func__, prvdata->id); + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + return CMD_RET_SUCCESS; +} + +static int check_core_dev(struct ffa_prvdata *prvdata, struct unit_test_state *uts) +{ + if (!prvdata->dev) { + char msg[LOG_MSG_SZ] = {0}; + + snprintf(msg, LOG_MSG_SZ, "[%s]: Error: core device NULL", __func__); + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + return CMD_RET_SUCCESS; +} + +static int check_sandbox_dev(struct sandbox_ffa_prvdata *sdx_prvdata, struct unit_test_state *uts) +{ + if (!sdx_prvdata->dev) { + char msg[LOG_MSG_SZ] = {0}; + + snprintf(msg, LOG_MSG_SZ, "[%s]: Error: sandbox device NULL", __func__); + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + return CMD_RET_SUCCESS; +} + +static int check_rxtxbuf(struct ffa_prvdata *prvdata, struct unit_test_state *uts) +{ + if (!prvdata->pair.rxbuf && prvdata->pair.txbuf) { + char msg[LOG_MSG_SZ] = {0}; + + snprintf(msg, LOG_MSG_SZ, "[%s]: Error: rxbuf = 0x%llx txbuf = 0x%llx", __func__, + prvdata->pair.rxbuf, + prvdata->pair.txbuf); + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + return CMD_RET_SUCCESS; +} + +static int check_features(struct ffa_prvdata *prvdata, struct unit_test_state *uts) +{ + char msg[LOG_MSG_SZ] = {0}; + + if (prvdata->pair.rxtx_min_pages != RXTX_4K && + prvdata->pair.rxtx_min_pages != RXTX_16K && + prvdata->pair.rxtx_min_pages != RXTX_64K) { + snprintf(msg, + LOG_MSG_SZ, + "[%s]: Error: FFA_RXTX_MAP features = 0x%lx", + __func__, + prvdata->pair.rxtx_min_pages); + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + + return CMD_RET_SUCCESS; +} + +static int check_rxbuf_mapped_flag(u32 queried_func_id, + u8 rxbuf_mapped, + struct unit_test_state *uts) +{ + char msg[LOG_MSG_SZ] = {0}; + + switch (queried_func_id) { + case FFA_RXTX_MAP: + { + if (rxbuf_mapped) + return CMD_RET_SUCCESS; + break; + } + case FFA_RXTX_UNMAP: + { + if (!rxbuf_mapped) + return CMD_RET_SUCCESS; + break; + } + default: + return CMD_RET_FAILURE; + } + + snprintf(msg, LOG_MSG_SZ, "[%s]: Error: %s mapping issue", __func__, + (queried_func_id == FFA_RXTX_MAP ? "FFA_RXTX_MAP" : "FFA_RXTX_UNMAP")); + dm_test_ffa_log(uts, msg); + + return CMD_RET_FAILURE; +} + +static int check_rxbuf_release_flag(u8 rxbuf_owned, struct unit_test_state *uts) +{ + if (rxbuf_owned) { + char msg[LOG_MSG_SZ] = {0}; + + snprintf(msg, LOG_MSG_SZ, "[%s]: Error: RX buffer not released", __func__); + dm_test_ffa_log(uts, msg); + return CMD_RET_FAILURE; + } + return CMD_RET_SUCCESS; +} + +static int test_ffa_msg_send_direct_req(u16 part_id, struct unit_test_state *uts) +{ + struct ffa_send_direct_data msg = {0}; + u8 cnt; + + ut_assertok(ffa_bus_ops_get()->sync_send_receive(NULL, part_id, &msg, 1)); + + for (cnt = 0; cnt < sizeof(struct ffa_send_direct_data) / sizeof(u64); cnt++) + ut_assertok(((u64 *)&msg)[cnt] != 0xffffffffffffffff); + + return CMD_RET_SUCCESS; +} + +static int test_partitions_and_comms(const char *service_uuid, + struct sandbox_ffa_prvdata *sdx_prvdata, + struct unit_test_state *uts) +{ + u32 count = 0; + struct ffa_partition_info *parts_info; + u32 info_idx, exp_info_idx; + int ret; + + /* + * get from the driver the count of the SPs matching the UUID + */ + ret = ffa_bus_ops_get()->partition_info_get(NULL, service_uuid, &count, NULL); + /* make sure partitions are detected */ + ut_assertok(ret != 0); + ut_assertok(count != SANDBOX_SP_COUNT_PER_VALID_SERVICE); + + /* + * pre-allocate a buffer to be filled by the driver + * with ffa_partition_info structs + */ + + parts_info = calloc(count, sizeof(struct ffa_partition_info)); + ut_assertok(!parts_info); + + /* + * ask the driver to fill the buffer with the SPs info + */ + ret = ffa_bus_ops_get()->partition_info_get(NULL, service_uuid, &count, parts_info); + if (ret != 0) { + free(parts_info); + ut_assertok(ret != 0); + } + + /* + * SPs found , verify the partitions information + */ + + ret = CMD_RET_FAILURE; + + for (info_idx = 0; info_idx < count ; info_idx++) { + for (exp_info_idx = 0; + exp_info_idx < sdx_prvdata->partitions.count; + exp_info_idx++) { + if (parts_info[info_idx].id == + sdx_prvdata->partitions.descs[exp_info_idx].info.id) { + ret = memcmp(&parts_info[info_idx], + &sdx_prvdata->partitions.descs[exp_info_idx] + .info, + sizeof(struct ffa_partition_info)); + if (ret) + free(parts_info); + ut_assertok(ret != 0); + /* send and receive data from the current partition */ + test_ffa_msg_send_direct_req(parts_info[info_idx].id, uts); + } + ret = CMD_RET_SUCCESS; + } + } + + free(parts_info); + + /* Verify expected partitions found in the emulated secure world*/ + ut_assertok(ret != CMD_RET_SUCCESS); + + return CMD_RET_SUCCESS; +} + +static int dm_test_ffa_ack(struct unit_test_state *uts) +{ + struct ffa_prvdata *prvdata = NULL; + struct sandbox_ffa_prvdata *sdx_prvdata = NULL; + struct ffa_sandbox_data func_data = {0}; + u8 rxbuf_flag = 0; + const char *svc1_uuid = SANDBOX_SERVICE1_UUID; + const char *svc2_uuid = SANDBOX_SERVICE2_UUID; + int ret; + + /* test probing FF-A devices */ + ut_assertok(ffa_bus_discover(NULL)); + + /* get a pointer to the FF-A core and sandbox drivers private data */ + func_data.data0 = &prvdata; + func_data.data0_size = sizeof(prvdata); + func_data.data1 = &sdx_prvdata; + func_data.data1_size = sizeof(sdx_prvdata); + + ut_assertok(sandbox_ffa_query_core_state(FFA_VERSION, &func_data)); + + /* make sure private data pointers are retrieved */ + ut_assertok(prvdata == 0); + ut_assertok(sdx_prvdata == 0); + + /* make sure dev devices created */ + ut_assertok(check_core_dev(prvdata, uts)); + ut_assertok(check_sandbox_dev(sdx_prvdata, uts)); + + /* test FFA_VERSION */ + ut_assertok(check_fwk_version(prvdata, sdx_prvdata, uts)); + + /* test FFA_ID_GET */ + ut_assertok(check_endpoint_id(prvdata, uts)); + + /* test FFA_FEATURES */ + ut_assertok(check_features(prvdata, uts)); + + /* test core RX/TX buffers */ + ut_assertok(check_rxtxbuf(prvdata, uts)); + + /* test FFA_RXTX_MAP */ + func_data.data0 = &rxbuf_flag; + func_data.data0_size = sizeof(rxbuf_flag); + + rxbuf_flag = 0; + ut_assertok(sandbox_ffa_query_core_state(FFA_RXTX_MAP, &func_data)); + ut_assertok(check_rxbuf_mapped_flag(FFA_RXTX_MAP, rxbuf_flag, uts)); + + /* FFA_PARTITION_INFO_GET / FFA_MSG_SEND_DIRECT_REQ */ + ret = test_partitions_and_comms(svc1_uuid, sdx_prvdata, uts); + ut_assertok(ret != CMD_RET_SUCCESS); + + /* test FFA_RX_RELEASE */ + rxbuf_flag = 1; + ut_assertok(sandbox_ffa_query_core_state(FFA_RX_RELEASE, &func_data)); + ut_assertok(check_rxbuf_release_flag(rxbuf_flag, uts)); + + /* FFA_PARTITION_INFO_GET / FFA_MSG_SEND_DIRECT_REQ */ + ret = test_partitions_and_comms(svc2_uuid, sdx_prvdata, uts); + ut_assertok(ret != CMD_RET_SUCCESS); + + /* test FFA_RX_RELEASE */ + rxbuf_flag = 1; + ut_assertok(sandbox_ffa_query_core_state(FFA_RX_RELEASE, &func_data)); + ut_assertok(check_rxbuf_release_flag(rxbuf_flag, uts)); + + /* test FFA_RXTX_UNMAP */ + ut_assertok(ffa_bus_ops_get()->rxtx_unmap(NULL)); + + rxbuf_flag = 1; + ut_assertok(sandbox_ffa_query_core_state(FFA_RXTX_UNMAP, &func_data)); + ut_assertok(check_rxbuf_mapped_flag(FFA_RXTX_UNMAP, rxbuf_flag, uts)); + + return CMD_RET_SUCCESS; +} + +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_prvdata *prvdata = NULL; + struct sandbox_ffa_prvdata *sdx_prvdata = NULL; + struct ffa_sandbox_data func_data = {0}; + 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 = {0}; + int ret; + u32 count = 0; + u16 part_id = 0; + + /* test probing FF-A devices */ + ut_assertok(ffa_bus_discover(NULL)); + + /* get a pointer to the FF-A core and sandbox drivers private data */ + func_data.data0 = &prvdata; + func_data.data0_size = sizeof(prvdata); + func_data.data1 = &sdx_prvdata; + func_data.data1_size = sizeof(sdx_prvdata); + + ut_assertok(sandbox_ffa_query_core_state(FFA_VERSION, &func_data)); + + /* make sure private data pointers are retrieved */ + ut_assertok(prvdata == 0); + ut_assertok(sdx_prvdata == 0); + + /* make sure dev devices created */ + ut_assertok(check_core_dev(prvdata, uts)); + ut_assertok(check_sandbox_dev(sdx_prvdata, uts)); + + /* query partitions count using invalid arguments */ + ret = ffa_bus_ops_get()->partition_info_get(NULL, unvalid_svc_uuid, NULL, NULL); + ut_assertok(ret != -EINVAL); + + /* query partitions count using an invalid UUID string */ + ret = ffa_bus_ops_get()->partition_info_get(NULL, unvalid_svc_uuid_str, &count, NULL); + ut_assertok(ret != -EINVAL); + + /* query partitions count using an invalid UUID (no matching SP) */ + count = 0; + ret = ffa_bus_ops_get()->partition_info_get(NULL, unvalid_svc_uuid, &count, NULL); + ut_assertok(count != 0); + + /* query partitions count using a valid UUID */ + count = 0; + ret = ffa_bus_ops_get()->partition_info_get(NULL, valid_svc_uuid, &count, NULL); + /* make sure partitions are detected */ + ut_assertok(ret != 0); + ut_assertok(count != SANDBOX_SP_COUNT_PER_VALID_SERVICE); + + /* send data to an invalid partition */ + ret = ffa_bus_ops_get()->sync_send_receive(NULL, part_id, &msg, 1); + ut_assertok(ret != -EINVAL); + + /* send data to a valid partition */ + part_id = prvdata->partitions.descs[0].info.id; + ret = ffa_bus_ops_get()->sync_send_receive(NULL, part_id, &msg, 1); + ut_assertok(ret != 0); + + return CMD_RET_SUCCESS; +} + +DM_TEST(dm_test_ffa_nack, UT_TESTF_SCAN_FDT | UT_TESTF_CONSOLE_REC);

Hi Abdellatif,
On Tue, 22 Nov 2022 at 06:18, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
Add functional test cases for the FF-A core driver
These tests rely on the FF-A Sandbox driver which helps in inspecting the FF-A core driver.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v8:
- update partition_info_get() second argument to be an SP count
- pass NULL device pointer to the FF-A bus discovery and operations
v7: set the tests to use 64-bit direct messaging
v4: align sandbox tests with the new FF-A driver interfaces and new way of error handling
v1: introduce sandbox tests
MAINTAINERS | 1 + test/dm/Makefile | 2 + test/dm/ffa.c | 392 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 395 insertions(+) create mode 100644 test/dm/ffa.c
This looks OK to me, but for sorting out how devices are bound / probed and how to call into a uclass.
Also, just return 0, not CMD_RET_SUCCESS. This isn't actually a command :-) Even if it were, 0 is better.
Regards, Simon

Add Sandbox test for the armffa command
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v4: drop use of helper APIs
v1: introduce armffa command sandbox test
MAINTAINERS | 1 + test/cmd/Makefile | 2 ++ test/cmd/armffa.c | 39 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+) create mode 100644 test/cmd/armffa.c
diff --git a/MAINTAINERS b/MAINTAINERS index 7a2cabfb2d..2f1684ece7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -271,6 +271,7 @@ F: doc/arch/arm64.ffa.rst F: drivers/firmware/arm-ffa/ F: include/arm_ffa.h F: include/sandbox_arm_ffa.h +F: test/cmd/armffa.c F: test/dm/ffa.c
ARM FREESCALE IMX diff --git a/test/cmd/Makefile b/test/cmd/Makefile index 6dd6e81875..4c40aefb3b 100644 --- a/test/cmd/Makefile +++ b/test/cmd/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0+ # # Copyright (c) 2013 Google, Inc +# (C) Copyright 2022 ARM Limited
ifdef CONFIG_HUSH_PARSER obj-$(CONFIG_CONSOLE_RECORD) += test_echo.o @@ -18,5 +19,6 @@ obj-$(CONFIG_CMD_PINMUX) += pinmux.o obj-$(CONFIG_CMD_PWM) += pwm.o ifdef CONFIG_SANDBOX obj-$(CONFIG_CMD_SETEXPR) += setexpr.o +obj-$(CONFIG_SANDBOX_FFA) += armffa.o endif obj-$(CONFIG_CMD_TEMPERATURE) += temperature.o diff --git a/test/cmd/armffa.c b/test/cmd/armffa.c new file mode 100644 index 0000000000..e04363ba63 --- /dev/null +++ b/test/cmd/armffa.c @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test for armffa command + * + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com + */ + +#include <common.h> +#include <dm/test.h> +#include <sandbox_arm_ffa.h> +#include <string.h> +#include <test/test.h> +#include <test/ut.h> + +#define PING_CMD_SIZE 19 + +/* Basic test of 'armffa' command */ +static int dm_test_armffa_cmd(struct unit_test_state *uts) +{ + char ping_cmd[PING_CMD_SIZE] = {0}; + + ut_assertok(ffa_bus_discover(NULL)); + + /* armffa getpart <UUID> */ + ut_assertok(run_command("armffa getpart " SANDBOX_SERVICE1_UUID, 0)); + + snprintf(ping_cmd, PING_CMD_SIZE, "armffa ping 0x%x", SANDBOX_SP1_ID); + + /* armffa ping <ID> */ + ut_assertok(run_command(ping_cmd, 0)); + + /* armffa devlist */ + ut_assertok(run_command("armffa devlist", 0)); + + return CMD_RET_SUCCESS; +} + +DM_TEST(dm_test_armffa_cmd, UT_TESTF_SCAN_FDT | UT_TESTF_CONSOLE_REC);

On Tue, 22 Nov 2022 at 06:18, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
Add Sandbox test for the armffa command
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
Changelog:
v4: drop use of helper APIs
v1: introduce armffa command sandbox test
MAINTAINERS | 1 + test/cmd/Makefile | 2 ++ test/cmd/armffa.c | 39 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+) create mode 100644 test/cmd/armffa.c
Looks good except for the discovery.
diff --git a/MAINTAINERS b/MAINTAINERS index 7a2cabfb2d..2f1684ece7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -271,6 +271,7 @@ F: doc/arch/arm64.ffa.rst F: drivers/firmware/arm-ffa/ F: include/arm_ffa.h F: include/sandbox_arm_ffa.h +F: test/cmd/armffa.c F: test/dm/ffa.c
ARM FREESCALE IMX diff --git a/test/cmd/Makefile b/test/cmd/Makefile index 6dd6e81875..4c40aefb3b 100644 --- a/test/cmd/Makefile +++ b/test/cmd/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0+ # # Copyright (c) 2013 Google, Inc +# (C) Copyright 2022 ARM Limited
ifdef CONFIG_HUSH_PARSER obj-$(CONFIG_CONSOLE_RECORD) += test_echo.o @@ -18,5 +19,6 @@ obj-$(CONFIG_CMD_PINMUX) += pinmux.o obj-$(CONFIG_CMD_PWM) += pwm.o ifdef CONFIG_SANDBOX obj-$(CONFIG_CMD_SETEXPR) += setexpr.o +obj-$(CONFIG_SANDBOX_FFA) += armffa.o endif obj-$(CONFIG_CMD_TEMPERATURE) += temperature.o diff --git a/test/cmd/armffa.c b/test/cmd/armffa.c new file mode 100644 index 0000000000..e04363ba63 --- /dev/null +++ b/test/cmd/armffa.c @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Test for armffa command
- (C) Copyright 2022 ARM Limited
- Abdellatif El Khlifi abdellatif.elkhlifi@arm.com
- */
+#include <common.h> +#include <dm/test.h> +#include <sandbox_arm_ffa.h> +#include <string.h> +#include <test/test.h> +#include <test/ut.h>
+#define PING_CMD_SIZE 19
+/* Basic test of 'armffa' command */ +static int dm_test_armffa_cmd(struct unit_test_state *uts) +{
char ping_cmd[PING_CMD_SIZE] = {0};
ut_assertok(ffa_bus_discover(NULL));
/* armffa getpart <UUID> */
ut_assertok(run_command("armffa getpart " SANDBOX_SERVICE1_UUID, 0));
snprintf(ping_cmd, PING_CMD_SIZE, "armffa ping 0x%x", SANDBOX_SP1_ID);
/* armffa ping <ID> */
ut_assertok(run_command(ping_cmd, 0));
/* armffa devlist */
ut_assertok(run_command("armffa devlist", 0));
return CMD_RET_SUCCESS;
+}
+DM_TEST(dm_test_armffa_cmd, UT_TESTF_SCAN_FDT | UT_TESTF_CONSOLE_REC);
2.17.1

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.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Signed-off-by: Gowtham Suresh Kumar gowtham.sureshkumar@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v8:
* isolate the compilation choices between FF-A and OP-TEE * update partition_info_get() second argument to be an SP count * pass NULL device pointer to the FF-A bus discovery and operations
v7:
* set the MM door bell event to use 64-bit direct messaging * issue a compile time error when one of these macros are not found : FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_OFFSET, FFA_SHARED_MM_BUFFER_ADDR * make mm_sp_svc_uuid static * replace EINVAL with ENOMEM in ffa_discover_mm_sp_id() when calloc() fails * improve use of unmap_sysmem() in ffa_mm_communicate()
v6:
* add FF-A runtime discovery at MM communication level * drop EFI runtime support for FF-A MM communication * revert the changes in include/mm_communication.h for efi_mm_communicate_header and smm_variable_access structures
v4:
* use the new FF-A driver interfaces * discover MM partitions at runtime * copy FF-A driver private data to EFI runtime section at ExitBootServices() * drop use of FFA_ERR_STAT_SUCCESS error code * replace EFI_BUFFER_TOO_SMALL with EFI_OUT_OF_RESOURCES in ffa_mm_communicate(). No need for efi_memcpy_runtime() anymore * revert the error log in mm_communicate() in case of failure * remove packed attribute from efi_mm_communicate_header and smm_variable_communicate_header
v2:
* set default values to 0 for FFA_SHARED_MM_BUFFER_SIZE, FFA_SHARED_MM_BUFFER_ADDR and MM_SP_UUID_DATA and add warnings
v1:
* introduce FF-A MM communication
include/mm_communication.h | 5 + lib/efi_loader/Kconfig | 14 +- lib/efi_loader/efi_variable_tee.c | 294 +++++++++++++++++++++++++++++- 3 files changed, 307 insertions(+), 6 deletions(-)
diff --git a/include/mm_communication.h b/include/mm_communication.h index e65fbde60d..d409bed777 100644 --- a/include/mm_communication.h +++ b/include/mm_communication.h @@ -6,6 +6,8 @@ * Copyright (c) 2017, Intel Corporation. All rights reserved. * Copyright (C) 2020 Linaro Ltd. sughosh.ganu@linaro.org * Copyright (C) 2020 Linaro Ltd. ilias.apalodimas@linaro.org + * (C) Copyright 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#ifndef _MM_COMMUNICATION_H_ @@ -13,6 +15,9 @@
#include <part_efi.h>
+/* MM service UUID string (big-endian format). This UUID is common across all MM SPs */ +#define MM_SP_UUID "33d532ed-e699-0942-c09c-a798d9cd722d" + /* * Interface to the pseudo Trusted Application (TA), which provides a * communication channel with the Standalone MM (Management Mode) diff --git a/lib/efi_loader/Kconfig b/lib/efi_loader/Kconfig index e2b643871b..c0f2603a45 100644 --- a/lib/efi_loader/Kconfig +++ b/lib/efi_loader/Kconfig @@ -55,13 +55,23 @@ 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" - depends on OPTEE + bool "UEFI variables storage service via the trusted world" + depends on OPTEE || ARM_FFA_TRANSPORT 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 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 dfef18435d..3933a24e8c 100644 --- a/lib/efi_loader/efi_variable_tee.c +++ b/lib/efi_loader/efi_variable_tee.c @@ -4,9 +4,12 @@ * * Copyright (C) 2019 Linaro Ltd. sughosh.ganu@linaro.org * Copyright (C) 2019 Linaro Ltd. ilias.apalodimas@linaro.org + * Copyright (C) 2022 ARM Limited + * Abdellatif El Khlifi abdellatif.elkhlifi@arm.com */
#include <common.h> +#include <dm.h> #include <efi.h> #include <efi_api.h> #include <efi_loader.h> @@ -15,6 +18,36 @@ #include <malloc.h> #include <mm_communication.h>
+#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) + +#include <arm_ffa.h> +#include <cpu_func.h> +#include <mapmem.h> + +#ifndef FFA_SHARED_MM_BUFFER_SIZE +#error "FFA_SHARED_MM_BUFFER_SIZE must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_SIZE 0 +#endif + +#ifndef FFA_SHARED_MM_BUFFER_OFFSET +#error "FFA_SHARED_MM_BUFFER_OFFSET must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_OFFSET 0 +#endif + +#ifndef FFA_SHARED_MM_BUFFER_ADDR +#error "FFA_SHARED_MM_BUFFER_ADDR must be defined in include/configs/<board>.h" +#define FFA_SHARED_MM_BUFFER_ADDR 0 +#endif + +/* MM return codes */ +#define MM_SUCCESS (0) + +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 */ static efi_uintn_t max_payload_size; /* func + data */ @@ -24,6 +57,7 @@ struct mm_connection { u32 session; };
+#if (IS_ENABLED(CONFIG_OPTEE)) /** * get_connection() - Retrieve OP-TEE session for a specific UUID. * @@ -143,13 +177,248 @@ static efi_status_t optee_mm_communicate(void *comm_buf, ulong dsize)
return ret; } +#endif + +#if (IS_ENABLED(CONFIG_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 + * + * Notifies 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 = -1; + + if (!ffa_bus_ops_get()) + return -EINVAL; + + msg.data0 = FFA_SHARED_MM_BUFFER_OFFSET; /* x3 */ + + ret = ffa_bus_ops_get()->sync_send_receive(NULL, mm_sp_id, &msg, 1); + if (ret != 0) + return ret; + + sp_event_ret = msg.data0; /* x3 */ + + if (sp_event_ret == MM_SUCCESS) + return 0; + + /* + * Failure to notify the MM SP + */ + + return -EACCES; +} + +/** + * 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_info *parts_info; + + if (!ffa_bus_ops_get()) + return -EINVAL; + + /* + * get from the driver the count of the SPs matching the UUID + */ + ret = ffa_bus_ops_get()->partition_info_get(NULL, mm_sp_svc_uuid, &count, NULL); + if (ret != 0) { + log_err("EFI: Failure in querying partitions count (error code: %d)\n", ret); + return ret; + } + + if (!count) { + log_info("EFI: No MM partition found\n"); + return ret; + } + + /* + * pre-allocate a buffer to be filled by the driver + * with ffa_partition_info structs + */ + + log_info("EFI: Pre-allocating %d partition(s) info structures\n", count); + + parts_info = calloc(count, sizeof(*parts_info)); + if (!parts_info) + return -ENOMEM; + + /* + * ask the driver to fill the + * buffer with the SPs info + */ + ret = ffa_bus_ops_get()->partition_info_get(NULL, mm_sp_svc_uuid, &count, parts_info); + if (ret) { + log_err("EFI: Failure in querying partition(s) info (error code: %d)\n", ret); + free(parts_info); + return ret; + } + + /* + * MM SPs found , use the first one + */ + + mm_sp_id = parts_info[0].id; + + log_info("EFI: MM partition ID 0x%x\n", mm_sp_id); + + free(parts_info); + + 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 + * + * Issues 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() != 0) { + log_err("EFI: Failure to discover MM partition ID at boot time\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 > FFA_SHARED_MM_BUFFER_SIZE) + return EFI_INVALID_PARAMETER; + + /* Copy the data to the shared buffer */ + + virt_shared_buf = (void *)map_sysmem((phys_addr_t)FFA_SHARED_MM_BUFFER_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; +} +#endif + +/** + * select_ffa_mm_comms() - checks FF-A support availability + * + * Making sure FF-A is compiled in. If that's the case try to discover + * the FF-A bus. + * + * Return: + * + * 0: FF-A ready for use. Otherwise, failure + */ +static efi_status_t select_ffa_mm_comms(void) +{ + efi_status_t ret = EFI_UNSUPPORTED; +#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) + ret = ffa_bus_discover(NULL); + if (ret) + ret = EFI_NOT_READY; +#endif + return ret; +} + +/** + * 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) @@ -162,7 +431,17 @@ static efi_status_t mm_communicate(u8 *comm_buf, efi_uintn_t dsize) 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); + ret = select_ffa_mm_comms(); + if (ret != EFI_SUCCESS) { +#if (IS_ENABLED(CONFIG_OPTEE)) + ret = optee_mm_communicate(comm_buf, dsize); +#endif + } else { +#if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) + ret = ffa_mm_communicate(comm_buf, dsize); +#endif + } + if (ret != EFI_SUCCESS) { log_err("%s failed!\n", __func__); return ret; @@ -258,6 +537,13 @@ efi_status_t EFIAPI get_max_payload(efi_uintn_t *size) goto out; } *size = var_payload->size; + + #if (IS_ENABLED(CONFIG_ARM_FFA_TRANSPORT)) + if (*size > FFA_SHARED_MM_BUFFER_SIZE) + *size = FFA_SHARED_MM_BUFFER_SIZE - MM_COMMUNICATE_HEADER_SIZE - + MM_VARIABLE_COMMUNICATE_SIZE; + #endif + /* * There seems to be a bug in EDK2 miscalculating the boundaries and * size checks, so deduct 2 more bytes to fulfill this requirement. Fix @@ -697,7 +983,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);
/*

turn on EFI MM communication
On corstone1000 platform MM communication between u-boot and the secure world (Optee) is done using the FF-A bus.
Signed-off-by: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com Cc: Tom Rini trini@konsulko.com Cc: Simon Glass sjg@chromium.org Cc: Ilias Apalodimas ilias.apalodimas@linaro.org Cc: Jens Wiklander jens.wiklander@linaro.org
---
Changelog: ===============
v8:
* drop OP-TEE configs from Corstone-1000 defconfig
v7:
* improve the definition of FFA_SHARED_MM_BUFFER_ADDR and FFA_SHARED_MM_BUFFER_OFFSET * update FFA_SHARED_MM_BUFFER_ADDR value
v6:
* corstone-1000: enable optee driver * corstone-1000: remove CONFIG_ARM_FFA_EFI_RUNTIME_MODE from the defconfig
v4:
* corstone-1000: turn on EFI MM communication
configs/corstone1000_defconfig | 2 ++ include/configs/corstone1000.h | 9 +++++++++ 2 files changed, 11 insertions(+)
diff --git a/configs/corstone1000_defconfig b/configs/corstone1000_defconfig index dddfa27507..d1dc06c86c 100644 --- a/configs/corstone1000_defconfig +++ b/configs/corstone1000_defconfig @@ -52,3 +52,5 @@ CONFIG_DM_SERIAL=y CONFIG_USB=y CONFIG_USB_ISP1760=y CONFIG_ERRNO_STR=y +CONFIG_EFI_MM_COMM_TEE=y +CONFIG_ARM_FFA_TRANSPORT=y diff --git a/include/configs/corstone1000.h b/include/configs/corstone1000.h index 8e0230c135..0362d29ac2 100644 --- a/include/configs/corstone1000.h +++ b/include/configs/corstone1000.h @@ -14,6 +14,15 @@
#include <linux/sizes.h>
+#define FFA_SHARED_MM_BUFFER_SIZE SZ_4K /* 4 KB */ + +/* + * shared buffer physical address used for communication between + * u-boot and the MM SP + */ +#define FFA_SHARED_MM_BUFFER_ADDR 0x02000000UL +#define FFA_SHARED_MM_BUFFER_OFFSET 0 + #define V2M_BASE 0x80000000
#define CONFIG_PL011_CLOCK 50000000

Hi,
On Wed, 13 Apr 2022 at 10:46, Tom Rini trini@konsulko.com wrote:
On Wed, Apr 13, 2022 at 03:20:23PM +0100, Abdellatif El Khlifi wrote:
On Tue, Apr 12, 2022 at 08:28:42AM -0500, Rob Herring wrote:
On Tue, Apr 12, 2022 at 7:01 AM Tom Rini trini@konsulko.com wrote:
On Tue, Apr 12, 2022 at 12:43:15PM +0100, Abdellatif El Khlifi wrote:
On Thu, Apr 07, 2022 at 08:58:11AM -0400, Tom Rini wrote:
On Thu, Apr 07, 2022 at 01:54:24PM +0100, Abdellatif El Khlifi wrote: > On Wed, Apr 06, 2022 at 03:47:11PM -0400, Tom Rini wrote: > > On Tue, Mar 29, 2022 at 04:16:53PM +0100, abdellatif.elkhlifi@arm.com wrote: > > > From: Abdellatif El Khlifi abdellatif.elkhlifi@arm.com > > > > > > This patchset adds support for Arm FF-A (Arm Firmware Framework for Armv8-A v1.0). > > > > > > FF-A support is generic by design and can be used by any Arm platform. > > > > > > The features added are as follows: > > > > > > 1/ FF-A device driver > > > 2/ armffa command > > > 3/ FF-A Sandbox driver > > > 4/ FF-A Sandbox test cases > > > 5/ FF-A MM communication > > > > > > > > > The suggested design sees FF-A as a data bus allowing data exchange with the firmware > > > running under TrustZone HW (such as Optee). The same approach was followed in the > > > FF-A driver in Linux kernel (https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/driv...)) > > > > > > u-boot boards using FF-A can provide a device tree node in a <board>-u-boot.dtsi file. > > > Since the node can not be hosted in Linux device tree, we suggest using u-boot device tree. > > > > Why can't the node be in the upstream tree? It should be, so that it > > can be shared between all users. Especially since there's in-Linux > > users? > > > > -- > > Tom > > Linux already has an FF-A bus driver and doesn't use a device tree node for FF-A. > > The Linux driver registers FF-A as a bus: > > int arm_ffa_bus_init(void) > { > return bus_register(&ffa_bus_type); > } > > https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/driv... > > So, there is no user for the node in Linux. That's why we suggest hosting the node in the u-boot device tree (a u-boot.dtsi file)
OK, but you can still push it upstream as it's not required to have an in tree user.
During the review of Corstone-1000 patchset, Rui Silva had a discussion with the Linux device tree maintainer (Rob Herring). Rob is not in favour of an FFA node in the kernel device tree. This is why we are including the FFA node in u-boot device tree (u-boot.dtsi files).
Sigh. There is not a 'kernel device tree' and a 'u-boot device tree'. There is only 1. For SystemReadyIR compliance, that is a hard requirement.
I'm a bit confused now, can you please link to the kernel thread? Or Rob, can you chime in here please?
The FFA DT binding was rejected in favor of making FFA discoverable. The FFA spec was amended to address that. DT is only for what we failed to make discoverable. For hardware, we're stuck with it. We shouldn't repeat that for software interfaces.
Rob
Guys,
Since we can not add an FFA node in the device tree, we will make FFA a discoverable bus. So, we will manually create the udevice, binding it to the driver and probing it. Manually means directly calling device_bind and device_probe APIs.
Any thoughts about this approach ?
How is it both discoverable and doesn't have a device tree node, in the kernel?
Also, if it is discoverable, we can still use U-Boot to discover it and then pass the info to Linux in the DT.
I am seeing several series which don't have 'proper' DT bindings in Linux. First I heard it was for legacy reasons, but now I am hearing something different. For U-Boot, we really do need to have DT bindings for devices. All this ad-hoc creation of stuff makes things hard to discover, adds to code size and makes things like of-platdata impossible.
Furthermore, if the bindings affect U-Boot, then the U-Boot project should have a say in what is being done there, not just be downstream of all such changes.
Regards, Simon

On Mon, Aug 01, 2022 at 01:13:23PM -0600, Simon Glass wrote:
On Wed, 13 Apr 2022 at 10:46, Tom Rini trini@konsulko.com wrote:
How is it both discoverable and doesn't have a device tree node, in the kernel?
Also, if it is discoverable, we can still use U-Boot to discover it and then pass the info to Linux in the DT.
Why ? Linux can discover the presence of the feature with a simple SMCCC based query. We don't need any DT bindings for this particular feature. Not sure if you are talking in general or in the context of $subject feature in the kernel.
I am seeing several series which don't have 'proper' DT bindings in Linux. First I heard it was for legacy reasons, but now I am hearing something different. For U-Boot, we really do need to have DT bindings for devices. All this ad-hoc creation of stuff makes things hard to discover, adds to code size and makes things like of-platdata impossible.
I may not have the complete picture here. If you are saying that every feature in the u-boot needs DT for some reason, then that's U-boot's limitation or restriction. But just the presence of node means nothing until the corresponding feature is queried and confirmed to be present in the firmware. That is very important as we can't skip the query stage just because of presence of some DT node for this.
Furthermore, if the bindings affect U-Boot, then the U-Boot project should have a say in what is being done there, not just be downstream of all such changes.
I still think you talking about some issue in general and it doesn't apply in this case. The new firmware interfaces are designed to be discoverable which is the main advantage over any non discoverable hardware and/or firmware interface. One main advantage I see is that we don't need any DT bindings which makes the firmware upgrades must simpler as the users can query the interface and know exactly what they need instead of relying on DT node which may get stale if not updated with the firmware update. I am not sure if whatever I am writing here is relevant to what your concerns are as I think I haven't understood them fully.
-- Regards, Sudeep

Hi Sudeep,
On Mon, 1 Aug 2022 at 13:28, Sudeep Holla sudeep.holla@arm.com wrote:
On Mon, Aug 01, 2022 at 01:13:23PM -0600, Simon Glass wrote:
On Wed, 13 Apr 2022 at 10:46, Tom Rini trini@konsulko.com wrote:
How is it both discoverable and doesn't have a device tree node, in the kernel?
Also, if it is discoverable, we can still use U-Boot to discover it and then pass the info to Linux in the DT.
Why ? Linux can discover the presence of the feature with a simple SMCCC based query. We don't need any DT bindings for this particular feature. Not sure if you are talking in general or in the context of $subject feature in the kernel.
Oh well my understanding of Open Firmware was that the firmware did the probing and passed the info to the OS.
I am seeing several series which don't have 'proper' DT bindings in Linux. First I heard it was for legacy reasons, but now I am hearing something different. For U-Boot, we really do need to have DT bindings for devices. All this ad-hoc creation of stuff makes things hard to discover, adds to code size and makes things like of-platdata impossible.
I may not have the complete picture here. If you are saying that every feature in the u-boot needs DT for some reason, then that's U-boot's limitation or restriction. But just the presence of node means nothing
Yes it is something I am becoming more and more concerned about.
until the corresponding feature is queried and confirmed to be present in the firmware. That is very important as we can't skip the query stage just because of presence of some DT node for this.
Furthermore, if the bindings affect U-Boot, then the U-Boot project should have a say in what is being done there, not just be downstream of all such changes.
I still think you talking about some issue in general and it doesn't apply in this case. The new firmware interfaces are designed to be discoverable which is the main advantage over any non discoverable hardware and/or firmware interface. One main advantage I see is that we don't need any DT bindings which makes the firmware upgrades must simpler as the users can query the interface and know exactly what they need instead of relying on DT node which may get stale if not updated with the firmware update. I am not sure if whatever I am writing here is relevant to what your concerns are as I think I haven't understood them fully.
I'm not sure either. In particular I'm not sure why it would be easier to update whatever the FF-A software is than to update the DT, since presumably they are both in the firmware.
I am talking about an issue in general and the same issue in particular with this series.
Can I suggest resending this series with a change log for each patch. Also please try to avoid #ifdefs and make sure to include documentation in doc/ including how this relates to the UEFI firmware-update effort that ARM/Linaro is undertaking. Also, what happened to the tests / sandbox driver?
Regards, Simon

Hi Simon,
On Mon, Aug 01, 2022 at 09:08:00PM -0600, Simon Glass wrote:
Hi Sudeep,
I'm not sure either. In particular I'm not sure why it would be easier to update whatever the FF-A software is than to update the DT, since presumably they are both in the firmware.
No, that is not the point. Someone who integrates must be aware of the fact that since the firmware is updated, there is a dependency(though created unnecessarily in this case) to even update the DT. Missing that may result in spending time and finding it hard way. It may not happen often but still happens and worth saving time and effort if this unneeded dependency is wasn't there.
I am talking about an issue in general and the same issue in particular with this series.
Since I might be missing to fully understand the issue, I may not be of much help I am afraid.
Can I suggest resending this series with a change log for each patch. Also please try to avoid #ifdefs and make sure to include documentation in doc/ including how this relates to the UEFI firmware-update effort that ARM/Linaro is undertaking. Also, what happened to the tests / sandbox driver?
To give a extremely brief summary on FF-A, it is an interface that enables standard way of communication between different partitions in the system. Partitions(also referred as VMs sometimes) can be either secure or non-secure. Though it can be use to communicate between 2 non-secure VMs, it is unlikely to be use there as it is not arch agnostic. So it will be mainly used between secure and non-secure partitions and amongst secure partitions.
-- Regards, Sudeep

On Mon, Aug 01, 2022 at 09:08:00PM -0600, Simon Glass wrote:
Hi Sudeep,
On Mon, 1 Aug 2022 at 13:28, Sudeep Holla sudeep.holla@arm.com wrote:
On Mon, Aug 01, 2022 at 01:13:23PM -0600, Simon Glass wrote:
On Wed, 13 Apr 2022 at 10:46, Tom Rini trini@konsulko.com wrote:
How is it both discoverable and doesn't have a device tree node, in the kernel?
Also, if it is discoverable, we can still use U-Boot to discover it and then pass the info to Linux in the DT.
Why ? Linux can discover the presence of the feature with a simple SMCCC based query. We don't need any DT bindings for this particular feature. Not sure if you are talking in general or in the context of $subject feature in the kernel.
Oh well my understanding of Open Firmware was that the firmware did the probing and passed the info to the OS.
I am seeing several series which don't have 'proper' DT bindings in Linux. First I heard it was for legacy reasons, but now I am hearing something different. For U-Boot, we really do need to have DT bindings for devices. All this ad-hoc creation of stuff makes things hard to discover, adds to code size and makes things like of-platdata impossible.
I may not have the complete picture here. If you are saying that every feature in the u-boot needs DT for some reason, then that's U-boot's limitation or restriction. But just the presence of node means nothing
Yes it is something I am becoming more and more concerned about.
until the corresponding feature is queried and confirmed to be present in the firmware. That is very important as we can't skip the query stage just because of presence of some DT node for this.
Furthermore, if the bindings affect U-Boot, then the U-Boot project should have a say in what is being done there, not just be downstream of all such changes.
I still think you talking about some issue in general and it doesn't apply in this case. The new firmware interfaces are designed to be discoverable which is the main advantage over any non discoverable hardware and/or firmware interface. One main advantage I see is that we don't need any DT bindings which makes the firmware upgrades must simpler as the users can query the interface and know exactly what they need instead of relying on DT node which may get stale if not updated with the firmware update. I am not sure if whatever I am writing here is relevant to what your concerns are as I think I haven't understood them fully.
I'm not sure either. In particular I'm not sure why it would be easier to update whatever the FF-A software is than to update the DT, since presumably they are both in the firmware.
I am talking about an issue in general and the same issue in particular with this series.
Can I suggest resending this series with a change log for each patch. Also please try to avoid #ifdefs and make sure to include documentation in doc/ including how this relates to the UEFI firmware-update effort that ARM/Linaro is undertaking. Also, what happened to the tests / sandbox driver?
Regards, Simon
Hi Simon,
OK, the next patchset update gonna include:
- A change log for each patch - Less #ifdefs - A detailed readme under doc/
The full history of what each patchset version brings are listed in v3 cover letter: please see [1]. It also contains a note about sandbox, saying that updating sandbox driver and tests is work in progress.
A note regarding the use of #ifdefs:
In some cases we need to use them. For example:
- Including the driver header which exports APIs and data structures only relevant when FF-A is enabled. This needs to be decided at build time. Some platforms don't want to build FF-A code at all. - At initcall level like done for other busses. an #ifdef is used to add an initcall for bus discovery. When FF-A is not needed, the discovery function will not be build at all (saves memory).
[1] https://lore.kernel.org/all/20220801172053.20163-1-abdellatif.elkhlifi@arm.c...
Cheers, Abdellatif

Hi Abdellatif,
On Fri, 5 Aug 2022 at 05:15, Abdellatif El Khlifi abdellatif.elkhlifi@arm.com wrote:
On Mon, Aug 01, 2022 at 09:08:00PM -0600, Simon Glass wrote:
Hi Sudeep,
On Mon, 1 Aug 2022 at 13:28, Sudeep Holla sudeep.holla@arm.com wrote:
On Mon, Aug 01, 2022 at 01:13:23PM -0600, Simon Glass wrote:
On Wed, 13 Apr 2022 at 10:46, Tom Rini trini@konsulko.com wrote:
How is it both discoverable and doesn't have a device tree node, in the kernel?
Also, if it is discoverable, we can still use U-Boot to discover it and then pass the info to Linux in the DT.
Why ? Linux can discover the presence of the feature with a simple SMCCC based query. We don't need any DT bindings for this particular feature. Not sure if you are talking in general or in the context of $subject feature in the kernel.
Oh well my understanding of Open Firmware was that the firmware did the probing and passed the info to the OS.
I am seeing several series which don't have 'proper' DT bindings in Linux. First I heard it was for legacy reasons, but now I am hearing something different. For U-Boot, we really do need to have DT bindings for devices. All this ad-hoc creation of stuff makes things hard to discover, adds to code size and makes things like of-platdata impossible.
I may not have the complete picture here. If you are saying that every feature in the u-boot needs DT for some reason, then that's U-boot's limitation or restriction. But just the presence of node means nothing
Yes it is something I am becoming more and more concerned about.
until the corresponding feature is queried and confirmed to be present in the firmware. That is very important as we can't skip the query stage just because of presence of some DT node for this.
Furthermore, if the bindings affect U-Boot, then the U-Boot project should have a say in what is being done there, not just be downstream of all such changes.
I still think you talking about some issue in general and it doesn't apply in this case. The new firmware interfaces are designed to be discoverable which is the main advantage over any non discoverable hardware and/or firmware interface. One main advantage I see is that we don't need any DT bindings which makes the firmware upgrades must simpler as the users can query the interface and know exactly what they need instead of relying on DT node which may get stale if not updated with the firmware update. I am not sure if whatever I am writing here is relevant to what your concerns are as I think I haven't understood them fully.
I'm not sure either. In particular I'm not sure why it would be easier to update whatever the FF-A software is than to update the DT, since presumably they are both in the firmware.
I am talking about an issue in general and the same issue in particular with this series.
Can I suggest resending this series with a change log for each patch. Also please try to avoid #ifdefs and make sure to include documentation in doc/ including how this relates to the UEFI firmware-update effort that ARM/Linaro is undertaking. Also, what happened to the tests / sandbox driver?
Regards, Simon
Hi Simon,
OK, the next patchset update gonna include:
- A change log for each patch
- Less #ifdefs
- A detailed readme under doc/
The full history of what each patchset version brings are listed in v3 cover letter: please see [1]. It also contains a note about sandbox, saying that updating sandbox driver and tests is work in progress.
Thanks for the email. OK, let's get that work completed before this goes in.
Also, if you use patman (for both U-Boot and Linux) it does the change logs for you and combines them all into the cover letter too, among other things.
A note regarding the use of #ifdefs:
In some cases we need to use them. For example:
- Including the driver header which exports APIs and data structures only relevant when FF-A is enabled. This needs to be decided at build time. Some platforms don't want to build FF-A code at all.
I think it is fine in header files when needed, as you say. We should try to avoid them in C files.
- At initcall level like done for other busses. an #ifdef is used to add an initcall for bus discovery. When FF-A is not needed, the discovery function will not be build at all (saves memory).
We'll need to discuss this as driver model handles devices in U-Boot. Will wait to see what you come up with.
[1] https://lore.kernel.org/all/20220801172053.20163-1-abdellatif.elkhlifi@arm.c...
Regards, Simon

On Mon, Aug 01, 2022 at 08:28:08PM +0100, Sudeep Holla wrote:
On Mon, Aug 01, 2022 at 01:13:23PM -0600, Simon Glass wrote:
On Wed, 13 Apr 2022 at 10:46, Tom Rini trini@konsulko.com wrote:
How is it both discoverable and doesn't have a device tree node, in the kernel?
Also, if it is discoverable, we can still use U-Boot to discover it and then pass the info to Linux in the DT.
Why ? Linux can discover the presence of the feature with a simple SMCCC based query. We don't need any DT bindings for this particular feature. Not sure if you are talking in general or in the context of $subject feature in the kernel.
I am seeing several series which don't have 'proper' DT bindings in Linux. First I heard it was for legacy reasons, but now I am hearing something different. For U-Boot, we really do need to have DT bindings for devices. All this ad-hoc creation of stuff makes things hard to discover, adds to code size and makes things like of-platdata impossible.
I may not have the complete picture here. If you are saying that every feature in the u-boot needs DT for some reason, then that's U-boot's limitation or restriction. But just the presence of node means nothing until the corresponding feature is queried and confirmed to be present in the firmware. That is very important as we can't skip the query stage just because of presence of some DT node for this.
Furthermore, if the bindings affect U-Boot, then the U-Boot project should have a say in what is being done there, not just be downstream of all such changes.
I still think you talking about some issue in general and it doesn't apply in this case. The new firmware interfaces are designed to be discoverable which is the main advantage over any non discoverable hardware and/or firmware interface. One main advantage I see is that we don't need any DT bindings which makes the firmware upgrades must simpler as the users can query the interface and know exactly what they need instead of relying on DT node which may get stale if not updated with the firmware update. I am not sure if whatever I am writing here is relevant to what your concerns are as I think I haven't understood them fully.
Part of the problem I think here is who does have a more complete picture of things? Saying there's a DT binding available is generally the firmware interface for discovering something exists somewhere else (excepting buses that define a discovery protocol). Except not in this case where there's a different interface. So who defined that interface and where is it specified? What's already been accepted upstream to other projects?
We're on I think v4 of this series now, and it was this email here that explained that an SMCCC call is how Linux finds out this exists (or doesn't exist).

On Tue, Aug 02, 2022 at 08:22:19AM -0400, Tom Rini wrote:
On Mon, Aug 01, 2022 at 08:28:08PM +0100, Sudeep Holla wrote:
On Mon, Aug 01, 2022 at 01:13:23PM -0600, Simon Glass wrote:
On Wed, 13 Apr 2022 at 10:46, Tom Rini trini@konsulko.com wrote:
How is it both discoverable and doesn't have a device tree node, in the kernel?
Also, if it is discoverable, we can still use U-Boot to discover it and then pass the info to Linux in the DT.
Why ? Linux can discover the presence of the feature with a simple SMCCC based query. We don't need any DT bindings for this particular feature. Not sure if you are talking in general or in the context of $subject feature in the kernel.
I am seeing several series which don't have 'proper' DT bindings in Linux. First I heard it was for legacy reasons, but now I am hearing something different. For U-Boot, we really do need to have DT bindings for devices. All this ad-hoc creation of stuff makes things hard to discover, adds to code size and makes things like of-platdata impossible.
I may not have the complete picture here. If you are saying that every feature in the u-boot needs DT for some reason, then that's U-boot's limitation or restriction. But just the presence of node means nothing until the corresponding feature is queried and confirmed to be present in the firmware. That is very important as we can't skip the query stage just because of presence of some DT node for this.
Furthermore, if the bindings affect U-Boot, then the U-Boot project should have a say in what is being done there, not just be downstream of all such changes.
I still think you talking about some issue in general and it doesn't apply in this case. The new firmware interfaces are designed to be discoverable which is the main advantage over any non discoverable hardware and/or firmware interface. One main advantage I see is that we don't need any DT bindings which makes the firmware upgrades must simpler as the users can query the interface and know exactly what they need instead of relying on DT node which may get stale if not updated with the firmware update. I am not sure if whatever I am writing here is relevant to what your concerns are as I think I haven't understood them fully.
Part of the problem I think here is who does have a more complete picture of things? Saying there's a DT binding available is generally the firmware interface for discovering something exists somewhere else (excepting buses that define a discovery protocol). Except not in this case where there's a different interface. So who defined that interface and where is it specified? What's already been accepted upstream to other projects?
Understood and agreed. Definitely need more background to sell some new feature.
We're on I think v4 of this series now, and it was this email here that explained that an SMCCC call is how Linux finds out this exists (or doesn't exist).
Ah that explains some of the confusion. Sorry I was not fully involved in u-boot support in general and didn't pay much attention to the previous versions. I would have presented the pointer to the specification to start with instead of just the reference to the kernel implementation as that might be biasing as well as the requirements may differ between u-boot and the kernel.
Anyways, here is the specification[1]. Also in general, there are quite a few new firmware interface based on SMCCC in the recent years and most of them take care to ensure they can be discovered.

On Tue, Aug 02, 2022 at 08:22:19AM -0400, Tom Rini wrote:
On Mon, Aug 01, 2022 at 08:28:08PM +0100, Sudeep Holla wrote:
On Mon, Aug 01, 2022 at 01:13:23PM -0600, Simon Glass wrote:
On Wed, 13 Apr 2022 at 10:46, Tom Rini trini@konsulko.com wrote:
How is it both discoverable and doesn't have a device tree node, in the kernel?
Also, if it is discoverable, we can still use U-Boot to discover it and then pass the info to Linux in the DT.
Why ? Linux can discover the presence of the feature with a simple SMCCC based query. We don't need any DT bindings for this particular feature. Not sure if you are talking in general or in the context of $subject feature in the kernel.
I am seeing several series which don't have 'proper' DT bindings in Linux. First I heard it was for legacy reasons, but now I am hearing something different. For U-Boot, we really do need to have DT bindings for devices. All this ad-hoc creation of stuff makes things hard to discover, adds to code size and makes things like of-platdata impossible.
I may not have the complete picture here. If you are saying that every feature in the u-boot needs DT for some reason, then that's U-boot's limitation or restriction. But just the presence of node means nothing until the corresponding feature is queried and confirmed to be present in the firmware. That is very important as we can't skip the query stage just because of presence of some DT node for this.
Furthermore, if the bindings affect U-Boot, then the U-Boot project should have a say in what is being done there, not just be downstream of all such changes.
I still think you talking about some issue in general and it doesn't apply in this case. The new firmware interfaces are designed to be discoverable which is the main advantage over any non discoverable hardware and/or firmware interface. One main advantage I see is that we don't need any DT bindings which makes the firmware upgrades must simpler as the users can query the interface and know exactly what they need instead of relying on DT node which may get stale if not updated with the firmware update. I am not sure if whatever I am writing here is relevant to what your concerns are as I think I haven't understood them fully.
Part of the problem I think here is who does have a more complete picture of things? Saying there's a DT binding available is generally the firmware interface for discovering something exists somewhere else (excepting buses that define a discovery protocol). Except not in this case where there's a different interface. So who defined that interface and where is it specified? What's already been accepted upstream to other projects?
We're on I think v4 of this series now, and it was this email here that explained that an SMCCC call is how Linux finds out this exists (or doesn't exist).
-- Tom
Hi guys,
All your feedbacks are valuable and more than welcome. Thanks for all of you.
Sorry for any confusion, I'd like to highlight the following points:
1/ FF-A spec:
cover letter v1 already states that this work provides support for Arm Firmware Framework for Armv8-A v1.0 (please see [A])
Also, in the v1 patchset the documentation of the main config enabling the driver states that we are using the FF-A spec and the link is provided:
in ARM_FFA_TRANSPORT config: + This driver 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
Please refer to [B] for more details.
2/ Linux:
The linux kernel driver has been referred to for showing that in linux FF-A is considered as a data bus and not just as a FW interface.
3/ FF-A bus discovery in u-boot:
In patchset v1 the FF-A bus was probed through a device tree node. Then, we got feedbacks from the device tree maintainer stating this won't be accepted.
This is why v2 version comes into the picture by making FF-A bus discoverable using device_{bind, probe} API
FF-A bus discovery in u-boot succeeds when all these operations are successful:
- querying the FF-A framework version - querying from secure world the u-boot endpoint ID - querying from secure world the supported features of the specified FF-A calls - mapping the RX/TX buffers - querying from secure world all the partitions information
These are done at ffa_probe level.
+/** + * ffa_probe - The driver probe function + * @dev: the arm_ffa device + * + * Probing is done at boot time and triggered by the uclass device discovery. + * At probe level the following actions are done: + * - setting the conduit + * - querying the FF-A framework version + * - querying from secure world the u-boot endpoint ID + * - querying from secure world the supported features of the specified FF-A calls + * - mapping the RX/TX buffers + * - querying from secure world all the partitions information
In patchset v2 , ffa_bus_discover has been introduced and documented in the header (please refer to [D] and check the documentation of the functions: ffa_bus_discover , ffa_probe, ffa_get_device)
+ /* The FF-A bus discovery succeeds when probing is successful */ + ret = device_probe(ffa_priv_data.dev);
4/ Feedbacks from Linaro:
We got new feedbacks from Linaro and we agreed to align the driver interfaces with those used in the kernel driver. Furthermore, we got a new requirement to make the driver independent from EFI. v3 patchset addresses that.
5/ in v3 cover letter, the full history of what each version brings are listed (please see [C])
6/ A detailed readme document has recently been written and will be added in the upcoming v4 patchset
cover letter 1: [A] https://lore.kernel.org/all/20220329151659.16894-1-abdellatif.elkhlifi@arm.c...
kconfig of the driver: [B] https://lore.kernel.org/all/20220329151659.16894-2-abdellatif.elkhlifi@arm.c...
cover letter 3: [C] https://lore.kernel.org/all/20220801172053.20163-1-abdellatif.elkhlifi@arm.c...
patch introducing FF-A discovery: [D] https://lore.kernel.org/all/20220415122803.16666-2-abdellatif.elkhlifi@arm.c...
participants (12)
-
Abdellatif El Khlifi
-
abdellatif.elkhlifi@arm.com
-
Anders Dellien
-
Gowtham Suresh Kumar
-
Heinrich Schuchardt
-
Ilias Apalodimas
-
Jens Wiklander
-
Rob Herring
-
Simon Glass
-
Simon Glass
-
Sudeep Holla
-
Tom Rini