These changes propose helper functions and macros to consolidate choice of the conduit method among devices communicating with an secure world that complies with SMCCC v1.0 but not SMCCC v1.1 or later. The new helper functions mimic arm_smccc_1_1_*() function but for SMCCC v1.0 compliant firmwares.
This series of changes updates several firmware drivers that each define a conduit method whereas kernel drivers are expected to use the very same conduit. This series obviously does not enforce these drivers to apply the proposed changes but the interest of the first patch is this series is that at least the PSCI driver upgrades as it will allow new drivers to benefit from the early initialized PSCI conduit method.
Etienne Carriere (6): firmware: helper functions for SMCCC v1.0 invocation conduit firmware: psci: set SMCCC v1.0 conduit and use helpers functions tee: optee: use SMCCC v1.0 helper functions firmware: arm_sdei: use SMCCC v1.0 helper functions firmware: stratix10: use SMCCC v1.0 helper functions firmware: zynqmp: use SMCCC v1.0 helper functions
drivers/firmware/Makefile | 1 + drivers/firmware/arm_sdei.c | 79 +++++--------- drivers/firmware/arm_smccc_conduit.c | 147 +++++++++++++++++++++++++++ drivers/firmware/psci/psci.c | 60 ++--------- drivers/firmware/stratix10-svc.c | 97 ++---------------- drivers/firmware/xilinx/zynqmp.c | 87 ++-------------- drivers/tee/optee/call.c | 14 +-- drivers/tee/optee/core.c | 85 ++++------------ drivers/tee/optee/optee_private.h | 4 +- include/linux/arm-smccc.h | 106 +++++++++++++++++++ include/linux/psci.h | 1 - 11 files changed, 337 insertions(+), 344 deletions(-) create mode 100644 drivers/firmware/arm_smccc_conduit.c
Introduce invocation helper functions for driver that interact with firmware supporting Arm SMCCC v1.0 specification based on existing arm_smccc_1_1_*() helpers. The new functions suit device where secure world supports SMCCC v1.0 but not v1.1.
This change allows devices to ensure consistency of the conduit used among drivers defining a conduit method as at runtime all devices are expected to use the very same SMCCC invocation conduit.
Signed-off-by: Etienne Carriere etienne.carriere@linaro.org --- drivers/firmware/Makefile | 1 + drivers/firmware/arm_smccc_conduit.c | 147 +++++++++++++++++++++++++++ include/linux/arm-smccc.h | 106 +++++++++++++++++++ 3 files changed, 254 insertions(+) create mode 100644 drivers/firmware/arm_smccc_conduit.c
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile index e9fb838af4df..5a4a72a06346 100644 --- a/drivers/firmware/Makefile +++ b/drivers/firmware/Makefile @@ -5,6 +5,7 @@ obj-$(CONFIG_ARM_SCPI_PROTOCOL) += arm_scpi.o obj-$(CONFIG_ARM_SCPI_POWER_DOMAIN) += scpi_pm_domain.o obj-$(CONFIG_ARM_SDE_INTERFACE) += arm_sdei.o +obj-$(CONFIG_HAVE_ARM_SMCCC) += arm_smccc_conduit.o obj-$(CONFIG_DMI) += dmi_scan.o obj-$(CONFIG_DMI_SYSFS) += dmi-sysfs.o obj-$(CONFIG_EDD) += edd.o diff --git a/drivers/firmware/arm_smccc_conduit.c b/drivers/firmware/arm_smccc_conduit.c new file mode 100644 index 000000000000..37b90e92a4b3 --- /dev/null +++ b/drivers/firmware/arm_smccc_conduit.c @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020, Linaro Limited + */ + +#include <linux/arm-smccc.h> +#include <linux/device.h> +#include <linux/of.h> + +static enum arm_smccc_conduit arm_smccc_1_0_conduit = SMCCC_CONDUIT_NONE; + +/* Helpers for nice trace when called outside a device instance */ +#define PRINT_INFO(dev, ...) \ + do { \ + if (dev) \ + dev_info(dev, __VA_ARGS__); \ + else \ + pr_info(__VA_ARGS__); \ + } while (0) + +#define PRINT_WARN(dev, ...) \ + do { \ + if (dev) \ + dev_warn(dev, __VA_ARGS__); \ + else \ + pr_warn(__VA_ARGS__); \ + } while (0) + +#define PRINT_ERROR(dev, ...) \ + do { \ + if (dev) \ + dev_err(dev, __VA_ARGS__); \ + else \ + pr_err(__VA_ARGS__); \ + } while (0) + +static const char *conduit_str(enum arm_smccc_conduit conduit) +{ + static const char hvc_str[] = "HVC"; + static const char smc_str[] = "SMC"; + static const char unknown[] = "unknown"; + + switch (conduit) { + case SMCCC_CONDUIT_HVC: + return hvc_str; + case SMCCC_CONDUIT_SMC: + return smc_str; + default: + return unknown; + } +} + +static int set_conduit(struct device *dev, enum arm_smccc_conduit conduit) +{ + switch (conduit) { + case SMCCC_CONDUIT_HVC: + case SMCCC_CONDUIT_SMC: + break; + default: + return -EINVAL; + } + + if (arm_smccc_1_0_conduit == SMCCC_CONDUIT_NONE) { + arm_smccc_1_0_conduit = conduit; + return 0; + } + + if (conduit == arm_smccc_1_0_conduit) + return 0; + + PRINT_ERROR(dev, "inconsistent conduits %u (%s) vs %u (%s)\n", + conduit, conduit_str(conduit), + arm_smccc_1_0_conduit, conduit_str(arm_smccc_1_0_conduit)); + + return -EINVAL; +} + +static enum arm_smccc_conduit method_to_conduit(const char *method) +{ + if (!strcmp("hvc", method)) + return SMCCC_CONDUIT_HVC; + else if (!strcmp("smc", method)) + return SMCCC_CONDUIT_SMC; + else + return SMCCC_CONDUIT_NONE; +} + +static int set_conduit_from_node(struct device *dev, struct device_node *np) +{ + const char *method; + + PRINT_INFO(dev, "probing for conduit method from DT.\n"); + + if (!np) + return -EINVAL; + + if (!of_property_read_string(np, "method", &method)) { + enum arm_smccc_conduit dev_conduit = method_to_conduit(method); + + if (dev_conduit == SMCCC_CONDUIT_NONE) { + PRINT_WARN(dev, "invalid "method" property "%s"\n", + method); + return -EINVAL; + } + + return set_conduit(dev, dev_conduit); + } + + if (arm_smccc_1_0_conduit != SMCCC_CONDUIT_NONE) + return 0; + + PRINT_WARN(dev, "missing "method" property\n"); + return -ENXIO; +} + +int devm_arm_smccc_1_0_set_conduit(struct device *dev) +{ + if (!dev || !dev->of_node) + return -EINVAL; + + return set_conduit_from_node(dev, dev->of_node); +} +EXPORT_SYMBOL_GPL(devm_arm_smccc_1_0_set_conduit); + +int of_arm_smccc_1_0_set_conduit(struct device_node *np) +{ + if (!np) + return -EINVAL; + + return set_conduit_from_node(NULL, np); +} +EXPORT_SYMBOL_GPL(of_arm_smccc_1_0_set_conduit); + +int arm_smccc_1_0_set_conduit(enum arm_smccc_conduit conduit) +{ + if (set_conduit(NULL, conduit)) + return -EINVAL; + + return 0; +} +EXPORT_SYMBOL_GPL(arm_smccc_1_0_set_conduit); + +enum arm_smccc_conduit arm_smccc_1_0_get_conduit(void) +{ + return arm_smccc_1_0_conduit; +} +EXPORT_SYMBOL_GPL(arm_smccc_1_0_get_conduit); diff --git a/include/linux/arm-smccc.h b/include/linux/arm-smccc.h index 59494df0f55b..4f5f17b00a6b 100644 --- a/include/linux/arm-smccc.h +++ b/include/linux/arm-smccc.h @@ -377,5 +377,111 @@ asmlinkage void __arm_smccc_hvc(unsigned long a0, unsigned long a1, ARM_SMCCC_OWNER_STANDARD_HYP, \ 0x21)
+/* SMCCC v1.0 compliant invocation helpers */ + +/* + * Like arm_smccc_1_0* but always returns SMCCC_RET_NOT_SUPPORTED. + * Used when the SMCCC conduit is not defined. The empty asm statement + * avoids compiler warnings about unused variables. + */ +#define __fail_smccc_1_0(...) \ + do { \ + __declare_args(7, __VA_ARGS__); \ + asm ("" __constraints(7)); \ + ___res->a0 = SMCCC_RET_NOT_SUPPORTED; \ + } while (0) + +/* + * arm_smccc_1_0_invoke() - make an SMCCC v1.0 compliant call + * + * This is a macro taking eight source arguments and an return structure. + * It uses the SMCCC conduit registered during driver(s) initialization. + * + * @a0-a7: arguments passed in registers 0 to 7 + * @res: result values from registers 0 to 3 + * + * This macro will make either an HVC call or an SMC call depending on the + * specified SMCCC conduit. If no valid conduit is available then -1 + * (SMCCC_RET_NOT_SUPPORTED) is returned in @res.a0. + * + * The return value provides the conduit that was used. + */ +#define arm_smccc_1_0_invoke(...) ({ \ + enum arm_smccc_conduit conduit = arm_smccc_1_0_get_conduit(); \ + switch (conduit) { \ + case SMCCC_CONDUIT_HVC: \ + arm_smccc_hvc(__VA_ARGS__); \ + break; \ + case SMCCC_CONDUIT_SMC: \ + arm_smccc_smc(__VA_ARGS__); \ + break; \ + default: \ + __fail_smccc_1_0(__VA_ARGS__); \ + conduit = SMCCC_CONDUIT_NONE; \ + } \ + conduit; \ + }) + +struct device; +struct device_node; + +#ifdef CONFIG_HAVE_ARM_SMCCC +/** + * arm_smccc_1_0_get_conduit() - Return registered SMCCC conduit + */ +enum arm_smccc_conduit arm_smccc_1_0_get_conduit(void); + +/** + * arm_smccc_1_0_set_conduit - Register SMCCC invocation conduit + * @conduit: conduit to register + * + * Return 0 on success and -EINVAL on failure. + */ +int arm_smccc_1_0_set_conduit(enum arm_smccc_conduit); + +/** + * devm_arm_smccc_1_0_set_conduit() - Set SMCCC v1.0 conduit if found in device + * @dev: Device instance + * + * Set the SMCCC invocation conduit based on device node if it has a "methhod" + * property that defines the SMCCC conduit to be used. If it has not, check a + * conduit is already registered. + * + * Return 0 on success, -ENXIO if no conduit found, -EINVAL otherwise. + */ +int devm_arm_smccc_1_0_set_conduit(struct device *dev); + +/** + * of_arm_smccc_1_0_set_conduit() - Set SMCCC v1.0 conduit if found in FDT node + * @np: Node instance + * + * Set the SMCCC invocation conduit based on device node if it has a "methhod" + * property that defines the SMCCC conduit to be used. If it has not, check a + * conduit is already registered. + * + * Return 0 on success, -ENXIO if no conduit found, -EINVAL otherwise. + */ +int of_arm_smccc_1_0_set_conduit(struct device_node *np); +#else +static inline enum arm_smccc_conduit arm_smccc_1_0_get_conduit(void) +{ + return SMCCC_CONDUIT_NONE; +} + +static inline int arm_smccc_1_0_set_conduit(enum arm_smccc_conduit); +{ + return -EINVAL; +} + +static inline int devm_arm_smccc_1_0_set_conduit(struct device *dev); +{ + return -EINVAL; +} + +static inline int of_arm_smccc_1_0_set_conduit(struct device_node *np); +{ + return -EINVAL; +} +#endif /* CONFIG_HAVE_ARM_SMCCC */ #endif /*__ASSEMBLY__*/ #endif /*__LINUX_ARM_SMCCC_H*/
On Sun, 19 Apr 2020 at 17:05, Etienne Carriere etienne.carriere@linaro.org wrote:
+#define arm_smccc_1_0_invoke(...) ({ \
enum arm_smccc_conduit conduit = arm_smccc_1_0_get_conduit(); \
switch (conduit) { \
case SMCCC_CONDUIT_HVC: \
arm_smccc_hvc(__VA_ARGS__); \
break; \
case SMCCC_CONDUIT_SMC: \
arm_smccc_smc(__VA_ARGS__); \
break; \
default: \
__fail_smccc_1_0(__VA_ARGS__); \
conduit = SMCCC_CONDUIT_NONE; \
} \
conduit; \
})
Checkpatch complains here (traces below) but I think this is a false positive due to the line breaks in the macro definition.
WARNING: Possible switch case/default not preceded by break or fallthrough comment #231: FILE: include/linux/arm-smccc.h:415: + case SMCCC_CONDUIT_SMC: \
WARNING: Possible switch case/default not preceded by break or fallthrough comment #234: FILE: include/linux/arm-smccc.h:418: + default: \
Regards, Etienne
Change PSCI driver to use SMCCC v1.0 helper functions. As PSCI device is initialized before other devices, it sets the SMCCC conduit used to SMCCC v1.0 compliant calls and other devices can rely on it.
No functional change in the PSCI device itself.
Signed-off-by: Etienne Carriere etienne.carriere@linaro.org --- drivers/firmware/psci/psci.c | 60 +++++------------------------------- include/linux/psci.h | 1 - 2 files changed, 8 insertions(+), 53 deletions(-)
diff --git a/drivers/firmware/psci/psci.c b/drivers/firmware/psci/psci.c index 2937d44b5df4..00bb205cad7c 100644 --- a/drivers/firmware/psci/psci.c +++ b/drivers/firmware/psci/psci.c @@ -53,7 +53,6 @@ bool psci_tos_resident_on(int cpu) }
struct psci_operations psci_ops = { - .conduit = SMCCC_CONDUIT_NONE, .smccc_version = SMCCC_VERSION_1_0, };
@@ -62,13 +61,9 @@ enum arm_smccc_conduit arm_smccc_1_1_get_conduit(void) if (psci_ops.smccc_version < SMCCC_VERSION_1_1) return SMCCC_CONDUIT_NONE;
- return psci_ops.conduit; + return arm_smccc_1_0_get_conduit(); }
-typedef unsigned long (psci_fn)(unsigned long, unsigned long, - unsigned long, unsigned long); -static psci_fn *invoke_psci_fn; - enum psci_function { PSCI_FN_CPU_SUSPEND, PSCI_FN_CPU_ON, @@ -120,23 +115,14 @@ bool psci_power_state_is_valid(u32 state) return !(state & ~valid_mask); }
-static unsigned long __invoke_psci_fn_hvc(unsigned long function_id, - unsigned long arg0, unsigned long arg1, - unsigned long arg2) +static unsigned long invoke_psci_fn(unsigned long function_id, + unsigned long arg0, unsigned long arg1, + unsigned long arg2) { struct arm_smccc_res res;
- arm_smccc_hvc(function_id, arg0, arg1, arg2, 0, 0, 0, 0, &res); - return res.a0; -} + arm_smccc_1_0_invoke(function_id, arg0, arg1, arg2, 0, 0, 0, 0, &res);
-static unsigned long __invoke_psci_fn_smc(unsigned long function_id, - unsigned long arg0, unsigned long arg1, - unsigned long arg2) -{ - struct arm_smccc_res res; - - arm_smccc_smc(function_id, arg0, arg1, arg2, 0, 0, 0, 0, &res); return res.a0; }
@@ -229,41 +215,11 @@ static unsigned long psci_migrate_info_up_cpu(void) 0, 0, 0); }
-static void set_conduit(enum arm_smccc_conduit conduit) -{ - switch (conduit) { - case SMCCC_CONDUIT_HVC: - invoke_psci_fn = __invoke_psci_fn_hvc; - break; - case SMCCC_CONDUIT_SMC: - invoke_psci_fn = __invoke_psci_fn_smc; - break; - default: - WARN(1, "Unexpected PSCI conduit %d\n", conduit); - } - - psci_ops.conduit = conduit; -} - static int get_set_conduit_method(struct device_node *np) { - const char *method; - - pr_info("probing for conduit method from DT.\n"); - - if (of_property_read_string(np, "method", &method)) { - pr_warn("missing "method" property\n"); + if (of_arm_smccc_1_0_set_conduit(np)) return -ENXIO; - }
- if (!strcmp("hvc", method)) { - set_conduit(SMCCC_CONDUIT_HVC); - } else if (!strcmp("smc", method)) { - set_conduit(SMCCC_CONDUIT_SMC); - } else { - pr_warn("invalid "method" property: %s\n", method); - return -EINVAL; - } return 0; }
@@ -605,9 +561,9 @@ int __init psci_acpi_init(void) pr_info("probing for conduit method from ACPI.\n");
if (acpi_psci_use_hvc()) - set_conduit(SMCCC_CONDUIT_HVC); + arm_smccc_1_0_set_conduit(SMCCC_CONDUIT_HVC); else - set_conduit(SMCCC_CONDUIT_SMC); + arm_smccc_1_0_set_conduit(SMCCC_CONDUIT_SMC);
return psci_probe(); } diff --git a/include/linux/psci.h b/include/linux/psci.h index a67712b73b6c..9de579a1d237 100644 --- a/include/linux/psci.h +++ b/include/linux/psci.h @@ -35,7 +35,6 @@ struct psci_operations { int (*affinity_info)(unsigned long target_affinity, unsigned long lowest_affinity_level); int (*migrate_info_type)(void); - enum arm_smccc_conduit conduit; enum smccc_version smccc_version; };
Change OP-TEE driver to use SMCCC v1.0 helper function.
No functional change but initialization traces that are changed from pr_*() traces the device dev_*() traces and info trace "probing for conduit method." being replaced with trace "probing for conduit method from DT."
Signed-off-by: Etienne Carriere etienne.carriere@linaro.org --- drivers/tee/optee/call.c | 14 ++--- drivers/tee/optee/core.c | 85 ++++++++----------------------- drivers/tee/optee/optee_private.h | 4 +- 3 files changed, 30 insertions(+), 73 deletions(-)
diff --git a/drivers/tee/optee/call.c b/drivers/tee/optee/call.c index cf2367ba08d6..706e7814a401 100644 --- a/drivers/tee/optee/call.c +++ b/drivers/tee/optee/call.c @@ -137,9 +137,9 @@ u32 optee_do_call_with_arg(struct tee_context *ctx, phys_addr_t parg) while (true) { struct arm_smccc_res res;
- optee->invoke_fn(param.a0, param.a1, param.a2, param.a3, - param.a4, param.a5, param.a6, param.a7, - &res); + arm_smccc_1_0_invoke(param.a0, param.a1, param.a2, param.a3, + param.a4, param.a5, param.a6, param.a7, + &res);
if (res.a0 == OPTEE_SMC_RETURN_ETHREAD_LIMIT) { /* @@ -395,8 +395,8 @@ void optee_enable_shm_cache(struct optee *optee) while (true) { struct arm_smccc_res res;
- optee->invoke_fn(OPTEE_SMC_ENABLE_SHM_CACHE, 0, 0, 0, 0, 0, 0, - 0, &res); + arm_smccc_1_0_invoke(OPTEE_SMC_ENABLE_SHM_CACHE, + 0, 0, 0, 0, 0, 0, 0, &res); if (res.a0 == OPTEE_SMC_RETURN_OK) break; optee_cq_wait_for_completion(&optee->call_queue, &w); @@ -421,8 +421,8 @@ void optee_disable_shm_cache(struct optee *optee) struct optee_smc_disable_shm_cache_result result; } res;
- optee->invoke_fn(OPTEE_SMC_DISABLE_SHM_CACHE, 0, 0, 0, 0, 0, 0, - 0, &res.smccc); + arm_smccc_1_0_invoke(OPTEE_SMC_DISABLE_SHM_CACHE, + 0, 0, 0, 0, 0, 0, 0, &res.smccc); if (res.result.status == OPTEE_SMC_RETURN_ENOTAVAIL) break; /* All shm's freed */ if (res.result.status == OPTEE_SMC_RETURN_OK) { diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c index 99698b8a3a74..f3bc657d1e97 100644 --- a/drivers/tee/optee/core.c +++ b/drivers/tee/optee/core.c @@ -335,11 +335,11 @@ static const struct tee_desc optee_supp_desc = { .flags = TEE_DESC_PRIVILEGED, };
-static bool optee_msg_api_uid_is_optee_api(optee_invoke_fn *invoke_fn) +static bool optee_msg_api_uid_is_optee_api(void) { struct arm_smccc_res res;
- invoke_fn(OPTEE_SMC_CALLS_UID, 0, 0, 0, 0, 0, 0, 0, &res); + arm_smccc_1_0_invoke(OPTEE_SMC_CALLS_UID, 0, 0, 0, 0, 0, 0, 0, &res);
if (res.a0 == OPTEE_MSG_UID_0 && res.a1 == OPTEE_MSG_UID_1 && res.a2 == OPTEE_MSG_UID_2 && res.a3 == OPTEE_MSG_UID_3) @@ -347,7 +347,7 @@ static bool optee_msg_api_uid_is_optee_api(optee_invoke_fn *invoke_fn) return false; }
-static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn) +static void optee_msg_get_os_revision(void) { union { struct arm_smccc_res smccc; @@ -358,8 +358,8 @@ static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn) } };
- invoke_fn(OPTEE_SMC_CALL_GET_OS_REVISION, 0, 0, 0, 0, 0, 0, 0, - &res.smccc); + arm_smccc_1_0_invoke(OPTEE_SMC_CALL_GET_OS_REVISION, + 0, 0, 0, 0, 0, 0, 0, &res.smccc);
if (res.result.build_id) pr_info("revision %lu.%lu (%08lx)", res.result.major, @@ -368,14 +368,15 @@ static void optee_msg_get_os_revision(optee_invoke_fn *invoke_fn) pr_info("revision %lu.%lu", res.result.major, res.result.minor); }
-static bool optee_msg_api_revision_is_compatible(optee_invoke_fn *invoke_fn) +static bool optee_msg_api_revision_is_compatible(void) { union { struct arm_smccc_res smccc; struct optee_smc_calls_revision_result result; } res;
- invoke_fn(OPTEE_SMC_CALLS_REVISION, 0, 0, 0, 0, 0, 0, 0, &res.smccc); + arm_smccc_1_0_invoke(OPTEE_SMC_CALLS_REVISION, + 0, 0, 0, 0, 0, 0, 0, &res.smccc);
if (res.result.major == OPTEE_MSG_REVISION_MAJOR && (int)res.result.minor >= OPTEE_MSG_REVISION_MINOR) @@ -383,8 +384,7 @@ static bool optee_msg_api_revision_is_compatible(optee_invoke_fn *invoke_fn) return false; }
-static bool optee_msg_exchange_capabilities(optee_invoke_fn *invoke_fn, - u32 *sec_caps) +static bool optee_msg_exchange_capabilities(u32 *sec_caps) { union { struct arm_smccc_res smccc; @@ -400,8 +400,8 @@ static bool optee_msg_exchange_capabilities(optee_invoke_fn *invoke_fn, if (!IS_ENABLED(CONFIG_SMP) || nr_cpu_ids == 1) a1 |= OPTEE_SMC_NSEC_CAP_UNIPROCESSOR;
- invoke_fn(OPTEE_SMC_EXCHANGE_CAPABILITIES, a1, 0, 0, 0, 0, 0, 0, - &res.smccc); + arm_smccc_1_0_invoke(OPTEE_SMC_EXCHANGE_CAPABILITIES, a1, + 0, 0, 0, 0, 0, 0, &res.smccc);
if (res.result.status != OPTEE_SMC_RETURN_OK) return false; @@ -437,8 +437,7 @@ static struct tee_shm_pool *optee_config_dyn_shm(void) return rc; }
-static struct tee_shm_pool * -optee_config_shm_memremap(optee_invoke_fn *invoke_fn, void **memremaped_shm) +static struct tee_shm_pool *optee_config_shm_memremap(void **memremaped_shm) { union { struct arm_smccc_res smccc; @@ -455,7 +454,8 @@ optee_config_shm_memremap(optee_invoke_fn *invoke_fn, void **memremaped_shm) void *rc; const int sz = OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE;
- invoke_fn(OPTEE_SMC_GET_SHM_CONFIG, 0, 0, 0, 0, 0, 0, 0, &res.smccc); + arm_smccc_1_0_invoke(OPTEE_SMC_GET_SHM_CONFIG, + 0, 0, 0, 0, 0, 0, 0, &res.smccc); if (res.result.status != OPTEE_SMC_RETURN_OK) { pr_err("static shm service not available\n"); return ERR_PTR(-ENOENT); @@ -515,45 +515,6 @@ optee_config_shm_memremap(optee_invoke_fn *invoke_fn, void **memremaped_shm) return rc; }
-/* Simple wrapper functions to be able to use a function pointer */ -static void optee_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) -{ - arm_smccc_smc(a0, a1, a2, a3, a4, a5, a6, a7, res); -} - -static void optee_smccc_hvc(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) -{ - arm_smccc_hvc(a0, a1, a2, a3, a4, a5, a6, a7, res); -} - -static optee_invoke_fn *get_invoke_func(struct device *dev) -{ - const char *method; - - pr_info("probing for conduit method.\n"); - - if (device_property_read_string(dev, "method", &method)) { - pr_warn("missing "method" property\n"); - return ERR_PTR(-ENXIO); - } - - if (!strcmp("hvc", method)) - return optee_smccc_hvc; - else if (!strcmp("smc", method)) - return optee_smccc_smc; - - pr_warn("invalid "method" property: %s\n", method); - return ERR_PTR(-EINVAL); -} - static int optee_remove(struct platform_device *pdev) { struct optee *optee = platform_get_drvdata(pdev); @@ -586,7 +547,6 @@ static int optee_remove(struct platform_device *pdev)
static int optee_probe(struct platform_device *pdev) { - optee_invoke_fn *invoke_fn; struct tee_shm_pool *pool = ERR_PTR(-EINVAL); struct optee *optee = NULL; void *memremaped_shm = NULL; @@ -594,23 +554,23 @@ static int optee_probe(struct platform_device *pdev) u32 sec_caps; int rc;
- invoke_fn = get_invoke_func(&pdev->dev); - if (IS_ERR(invoke_fn)) - return PTR_ERR(invoke_fn); + rc = devm_arm_smccc_1_0_set_conduit(&pdev->dev); + if (rc) + return rc;
- if (!optee_msg_api_uid_is_optee_api(invoke_fn)) { + if (!optee_msg_api_uid_is_optee_api()) { pr_warn("api uid mismatch\n"); return -EINVAL; }
- optee_msg_get_os_revision(invoke_fn); + optee_msg_get_os_revision();
- if (!optee_msg_api_revision_is_compatible(invoke_fn)) { + if (!optee_msg_api_revision_is_compatible()) { pr_warn("api revision mismatch\n"); return -EINVAL; }
- if (!optee_msg_exchange_capabilities(invoke_fn, &sec_caps)) { + if (!optee_msg_exchange_capabilities(&sec_caps)) { pr_warn("capabilities mismatch\n"); return -EINVAL; } @@ -625,7 +585,7 @@ static int optee_probe(struct platform_device *pdev) * If dynamic shared memory is not available or failed - try static one */ if (IS_ERR(pool) && (sec_caps & OPTEE_SMC_SEC_CAP_HAVE_RESERVED_SHM)) - pool = optee_config_shm_memremap(invoke_fn, &memremaped_shm); + pool = optee_config_shm_memremap(&memremaped_shm);
if (IS_ERR(pool)) return PTR_ERR(pool); @@ -636,7 +596,6 @@ static int optee_probe(struct platform_device *pdev) goto err; }
- optee->invoke_fn = invoke_fn; optee->sec_caps = sec_caps;
teedev = tee_device_alloc(&optee_desc, NULL, pool, optee); diff --git a/drivers/tee/optee/optee_private.h b/drivers/tee/optee/optee_private.h index d9c5037b4e03..e186e318e157 100644 --- a/drivers/tee/optee/optee_private.h +++ b/drivers/tee/optee/optee_private.h @@ -69,8 +69,7 @@ struct optee_supp { * struct optee - main service struct * @supp_teedev: supplicant device * @teedev: client device - * @invoke_fn: function to issue smc or hvc - * @call_queue: queue of threads waiting to call @invoke_fn + * @call_queue: queue of threads waiting to invoke secure world * @wait_queue: queue of threads from secure world waiting for a * secure world sync object * @supp: supplicant synchronization struct for RPC to supplicant @@ -82,7 +81,6 @@ struct optee_supp { struct optee { struct tee_device *supp_teedev; struct tee_device *teedev; - optee_invoke_fn *invoke_fn; struct optee_call_queue call_queue; struct optee_wait_queue wait_queue; struct optee_supp supp;
Change Arm SDEI firmware driver to use SMCCC v1.0 helper functions.
Functional changes are Arm SDEI firmware driver can find appropriate set conduit if it does not define one but another device has set the kernel conduit for SMCCC calls.
Other functional changes are the trace messages emitted at device probe time that now dev_*() traces instead of pr_*() traces.
Signed-off-by: Etienne Carriere etienne.carriere@linaro.org --- drivers/firmware/arm_sdei.c | 79 +++++++++++++------------------------ 1 file changed, 27 insertions(+), 52 deletions(-)
diff --git a/drivers/firmware/arm_sdei.c b/drivers/firmware/arm_sdei.c index 334c8be0c11f..92f8077e0bde 100644 --- a/drivers/firmware/arm_sdei.c +++ b/drivers/firmware/arm_sdei.c @@ -34,12 +34,9 @@ #include <linux/uaccess.h>
/* - * The call to use to reach the firmware. + * The SMCCC cnduit for the call to use to reach the firmware. */ -static asmlinkage void (*sdei_firmware_call)(unsigned long function_id, - unsigned long arg0, unsigned long arg1, - unsigned long arg2, unsigned long arg3, - unsigned long arg4, struct arm_smccc_res *res); +static enum arm_smccc_conduit sdei_firmware_call_conduit;
/* entry point from firmware to arch asm code */ static unsigned long sdei_entry_point; @@ -144,14 +141,14 @@ static int invoke_sdei_fn(unsigned long function_id, unsigned long arg0, int err = 0; struct arm_smccc_res res;
- if (sdei_firmware_call) { + if (sdei_firmware_call_conduit != SMCCC_CONDUIT_NONE) { sdei_firmware_call(function_id, arg0, arg1, arg2, arg3, arg4, &res); if (sdei_is_err(&res)) err = sdei_to_linux_errno(res.a0); } else { /* - * !sdei_firmware_call means we failed to probe or called + * No SMCCC conduit means we failed to probe or called * sdei_mark_interface_broken(). -EIO is not an error returned * by sdei_to_linux_errno() and is used to suppress messages * from this driver. @@ -364,7 +361,7 @@ static void sdei_mark_interface_broken(void) { pr_err("disabling SDEI firmware interface\n"); on_each_cpu(&_ipi_mask_cpu, NULL, true); - sdei_firmware_call = NULL; + sdei_firmware_call_conduit = SMCCC_CONDUIT_NONE; }
static int sdei_platform_reset(void) @@ -874,23 +871,19 @@ static struct notifier_block sdei_reboot_nb = { .notifier_call = sdei_reboot_notifier, };
-static void sdei_smccc_smc(unsigned long function_id, - unsigned long arg0, unsigned long arg1, - unsigned long arg2, unsigned long arg3, - unsigned long arg4, struct arm_smccc_res *res) -{ - arm_smccc_smc(function_id, arg0, arg1, arg2, arg3, arg4, 0, 0, res); -} -NOKPROBE_SYMBOL(sdei_smccc_smc);
-static void sdei_smccc_hvc(unsigned long function_id, - unsigned long arg0, unsigned long arg1, - unsigned long arg2, unsigned long arg3, - unsigned long arg4, struct arm_smccc_res *res) +static asmlinkage void sdei_firmware_call(unsigned long function_id, + unsigned long arg0, + unsigned long arg1, + unsigned long arg2, + unsigned long arg3, + unsigned long arg4, + struct arm_smccc_res *res) { - arm_smccc_hvc(function_id, arg0, arg1, arg2, arg3, arg4, 0, 0, res); + arm_smccc_1_0_invoke(function_id, arg0, arg1, arg2, arg3, arg4, + 0, 0, res); } -NOKPROBE_SYMBOL(sdei_smccc_hvc); +NOKPROBE_SYMBOL(sdei_firmware_call);
int sdei_register_ghes(struct ghes *ghes, sdei_event_callback *normal_cb, sdei_event_callback *critical_cb) @@ -959,48 +952,30 @@ int sdei_unregister_ghes(struct ghes *ghes) return err; }
-static int sdei_get_conduit(struct platform_device *pdev) +static enum arm_smccc_conduit sdei_get_conduit(struct platform_device *pdev) { - const char *method; - struct device_node *np = pdev->dev.of_node; - - sdei_firmware_call = NULL; - if (np) { - if (of_property_read_string(np, "method", &method)) { - pr_warn("missing "method" property\n"); - return SMCCC_CONDUIT_NONE; - } - - if (!strcmp("hvc", method)) { - sdei_firmware_call = &sdei_smccc_hvc; - return SMCCC_CONDUIT_HVC; - } else if (!strcmp("smc", method)) { - sdei_firmware_call = &sdei_smccc_smc; - return SMCCC_CONDUIT_SMC; - } - - pr_warn("invalid "method" property: %s\n", method); + if (pdev->dev.of_node) { + devm_arm_smccc_1_0_set_conduit(pdev->dev); } else if (IS_ENABLED(CONFIG_ACPI) && !acpi_disabled) { - if (acpi_psci_use_hvc()) { - sdei_firmware_call = &sdei_smccc_hvc; - return SMCCC_CONDUIT_HVC; - } else { - sdei_firmware_call = &sdei_smccc_smc; - return SMCCC_CONDUIT_SMC; - } + if (acpi_psci_use_hvc()) + arm_smccc_1_0_set_conduit(SMCCC_CONDUIT_HVC); + else + arm_smccc_1_0_set_conduit(SMCCC_CONDUIT_SMC); }
- return SMCCC_CONDUIT_NONE; + sdei_firmware_call_conduit = arm_smccc_1_0_get_conduit(); + + return sdei_firmware_call_conduit; }
static int sdei_probe(struct platform_device *pdev) { int err; u64 ver = 0; - int conduit; + enum arm_smccc_conduit conduit;
conduit = sdei_get_conduit(pdev); - if (!sdei_firmware_call) + if (conduit == SMCCC_CONDUIT_NONE) return 0;
err = sdei_api_get_version(&ver);
Change Stratix 10 firmware driver to use SMCCC v1.0 helper function.
Main functional change is that SMCCC conduit for the device is ensured being consistent with the method used by PSCI firmware.
Another functional change is a additional info trace in the device probed sequence: "probing for conduit method from DT.".
Signed-off-by: Etienne Carriere etienne.carriere@linaro.org --- drivers/firmware/stratix10-svc.c | 97 +++----------------------------- 1 file changed, 9 insertions(+), 88 deletions(-)
diff --git a/drivers/firmware/stratix10-svc.c b/drivers/firmware/stratix10-svc.c index d5f0769f3761..132f05f2bcc8 100644 --- a/drivers/firmware/stratix10-svc.c +++ b/drivers/firmware/stratix10-svc.c @@ -41,10 +41,6 @@ /* stratix10 service layer clients */ #define STRATIX10_RSU "stratix10-rsu"
-typedef void (svc_invoke_fn)(unsigned long, unsigned long, unsigned long, - unsigned long, unsigned long, unsigned long, - unsigned long, unsigned long, - struct arm_smccc_res *); struct stratix10_svc_chan;
/** @@ -60,7 +56,6 @@ struct stratix10_svc { * @sync_complete: state for a completion * @addr: physical address of shared memory block * @size: size of shared memory block - * @invoke_fn: function to issue secure monitor or hypervisor call * * This struct is used to save physical address and size of shared memory * block. The shared memory blocked is allocated by secure monitor software @@ -73,7 +68,6 @@ struct stratix10_svc_sh_memory { struct completion sync_complete; unsigned long addr; unsigned long size; - svc_invoke_fn *invoke_fn; };
/** @@ -126,7 +120,6 @@ struct stratix10_svc_data { * @svc_fifo: a queue for storing service message data * @complete_status: state for completion * @svc_fifo_lock: protect access to service message data queue - * @invoke_fn: function to issue secure monitor call or hypervisor call * * This struct is used to create communication channels for service clients, to * handle secure monitor or hypervisor call. @@ -142,7 +135,6 @@ struct stratix10_svc_controller { struct kfifo svc_fifo; struct completion complete_status; spinlock_t svc_fifo_lock; - svc_invoke_fn *invoke_fn; };
/** @@ -206,8 +198,8 @@ static void svc_thread_cmd_data_claim(struct stratix10_svc_controller *ctrl,
pr_debug("%s: claim back the submitted buffer\n", __func__); do { - ctrl->invoke_fn(INTEL_SIP_SMC_FPGA_CONFIG_COMPLETED_WRITE, - 0, 0, 0, 0, 0, 0, 0, &res); + arm_smccc_1_0_invoke(INTEL_SIP_SMC_FPGA_CONFIG_COMPLETED_WRITE, + 0, 0, 0, 0, 0, 0, 0, &res);
if (res.a0 == INTEL_SIP_SMC_STATUS_OK) { if (!res.a1) { @@ -256,8 +248,8 @@ static void svc_thread_cmd_config_status(struct stratix10_svc_controller *ctrl,
count_in_sec = FPGA_CONFIG_STATUS_TIMEOUT_SEC; while (count_in_sec) { - ctrl->invoke_fn(INTEL_SIP_SMC_FPGA_CONFIG_ISDONE, - 0, 0, 0, 0, 0, 0, 0, &res); + arm_smccc_1_0_invoke(INTEL_SIP_SMC_FPGA_CONFIG_ISDONE, + 0, 0, 0, 0, 0, 0, 0, &res); if ((res.a0 == INTEL_SIP_SMC_STATUS_OK) || (res.a0 == INTEL_SIP_SMC_FPGA_CONFIG_STATUS_ERROR)) break; @@ -420,7 +412,7 @@ static int svc_normal_to_secure_thread(void *data) __func__, (unsigned int)a0, (unsigned int)a1); pr_debug(" a2=0x%016x\n", (unsigned int)a2);
- ctrl->invoke_fn(a0, a1, a2, 0, 0, 0, 0, 0, &res); + arm_smccc_1_0_invoke(a0, a1, a2, 0, 0, 0, 0, 0, &res);
pr_debug("%s: after SMC call -- res.a0=0x%016x", __func__, (unsigned int)res.a0); @@ -540,8 +532,8 @@ static int svc_normal_to_secure_shm_thread(void *data) struct arm_smccc_res res;
/* SMC or HVC call to get shared memory info from secure world */ - sh_mem->invoke_fn(INTEL_SIP_SMC_FPGA_CONFIG_GET_MEM, - 0, 0, 0, 0, 0, 0, 0, &res); + arm_smccc_1_0_invoke(INTEL_SIP_SMC_FPGA_CONFIG_GET_MEM, + 0, 0, 0, 0, 0, 0, 0, &res); if (res.a0 == INTEL_SIP_SMC_STATUS_OK) { sh_mem->addr = res.a1; sh_mem->size = res.a2; @@ -661,73 +653,6 @@ svc_create_memory_pool(struct platform_device *pdev, return genpool; }
-/** - * svc_smccc_smc() - secure monitor call between normal and secure world - * @a0: argument passed in registers 0 - * @a1: argument passed in registers 1 - * @a2: argument passed in registers 2 - * @a3: argument passed in registers 3 - * @a4: argument passed in registers 4 - * @a5: argument passed in registers 5 - * @a6: argument passed in registers 6 - * @a7: argument passed in registers 7 - * @res: result values from register 0 to 3 - */ -static void svc_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) -{ - arm_smccc_smc(a0, a1, a2, a3, a4, a5, a6, a7, res); -} - -/** - * svc_smccc_hvc() - hypervisor call between normal and secure world - * @a0: argument passed in registers 0 - * @a1: argument passed in registers 1 - * @a2: argument passed in registers 2 - * @a3: argument passed in registers 3 - * @a4: argument passed in registers 4 - * @a5: argument passed in registers 5 - * @a6: argument passed in registers 6 - * @a7: argument passed in registers 7 - * @res: result values from register 0 to 3 - */ -static void svc_smccc_hvc(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) -{ - arm_smccc_hvc(a0, a1, a2, a3, a4, a5, a6, a7, res); -} - -/** - * get_invoke_func() - invoke SMC or HVC call - * @dev: pointer to device - * - * Return: function pointer to svc_smccc_smc or svc_smccc_hvc. - */ -static svc_invoke_fn *get_invoke_func(struct device *dev) -{ - const char *method; - - if (of_property_read_string(dev->of_node, "method", &method)) { - dev_warn(dev, "missing "method" property\n"); - return ERR_PTR(-ENXIO); - } - - if (!strcmp(method, "smc")) - return svc_smccc_smc; - if (!strcmp(method, "hvc")) - return svc_smccc_hvc; - - dev_warn(dev, "invalid "method" property: %s\n", method); - - return ERR_PTR(-EINVAL); -} - /** * stratix10_svc_request_channel_byname() - request a service channel * @client: pointer to service client @@ -979,20 +904,17 @@ static int stratix10_svc_drv_probe(struct platform_device *pdev) struct stratix10_svc_sh_memory *sh_memory; struct stratix10_svc *svc;
- svc_invoke_fn *invoke_fn; size_t fifo_size; int ret;
- /* get SMC or HVC function */ - invoke_fn = get_invoke_func(dev); - if (IS_ERR(invoke_fn)) + /* get SMC or HVC conduit */ + if (arm_smccc_1_0_set_device_conduit(&pdev->dev)) return -EINVAL;
sh_memory = devm_kzalloc(dev, sizeof(*sh_memory), GFP_KERNEL); if (!sh_memory) return -ENOMEM;
- sh_memory->invoke_fn = invoke_fn; ret = svc_get_sh_memory(pdev, sh_memory); if (ret) return ret; @@ -1017,7 +939,6 @@ static int stratix10_svc_drv_probe(struct platform_device *pdev) controller->chans = chans; controller->genpool = genpool; controller->task = NULL; - controller->invoke_fn = invoke_fn; init_completion(&controller->complete_status);
fifo_size = sizeof(struct stratix10_svc_data) * SVC_NUM_DATA_IN_FIFO;
Change Xilinx Zynmp driver to use Arm SMCCC v1.0 helper functions and ensure platform consistency on SMCCC conduit method.
Main functional change is that SMCCC conduit method for ZynqMP firmware must be consistent with conduit methods registered by PSCI device that is early probed.
Another functional change is an addition info trace emitted by the device probe sequence: "probing for conduit method from DT.".
Signed-off-by: Etienne Carriere etienne.carriere@linaro.org --- drivers/firmware/xilinx/zynqmp.c | 87 ++++---------------------------- 1 file changed, 9 insertions(+), 78 deletions(-)
diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c index 41b65164a367..1fdc468c9204 100644 --- a/drivers/firmware/xilinx/zynqmp.c +++ b/drivers/firmware/xilinx/zynqmp.c @@ -63,51 +63,11 @@ static int zynqmp_pm_ret_code(u32 ret_status) } }
-static noinline int do_fw_call_fail(u64 arg0, u64 arg1, u64 arg2, - u32 *ret_payload) -{ - return -ENODEV; -} - -/* - * PM function call wrapper - * Invoke do_fw_call_smc or do_fw_call_hvc, depending on the configuration - */ -static int (*do_fw_call)(u64, u64, u64, u32 *ret_payload) = do_fw_call_fail; - -/** - * do_fw_call_smc() - Call system-level platform management layer (SMC) - * @arg0: Argument 0 to SMC call - * @arg1: Argument 1 to SMC call - * @arg2: Argument 2 to SMC call - * @ret_payload: Returned value array - * - * Invoke platform management function via SMC call (no hypervisor present). - * - * Return: Returns status, either success or error+reason - */ -static noinline int do_fw_call_smc(u64 arg0, u64 arg1, u64 arg2, - u32 *ret_payload) -{ - struct arm_smccc_res res; - - arm_smccc_smc(arg0, arg1, arg2, 0, 0, 0, 0, 0, &res); - - if (ret_payload) { - ret_payload[0] = lower_32_bits(res.a0); - ret_payload[1] = upper_32_bits(res.a0); - ret_payload[2] = lower_32_bits(res.a1); - ret_payload[3] = upper_32_bits(res.a1); - } - - return zynqmp_pm_ret_code((enum pm_ret_status)res.a0); -} - /** - * do_fw_call_hvc() - Call system-level platform management layer (HVC) - * @arg0: Argument 0 to HVC call - * @arg1: Argument 1 to HVC call - * @arg2: Argument 2 to HVC call + * do_fw_call() - Call system-level platform management layer + * @arg0: Argument 0 to HVC/SMC call + * @arg1: Argument 1 to HVC/SMC call + * @arg2: Argument 2 to HVC/SMC call * @ret_payload: Returned value array * * Invoke platform management function via HVC @@ -116,12 +76,13 @@ static noinline int do_fw_call_smc(u64 arg0, u64 arg1, u64 arg2, * * Return: Returns status, either success or error+reason */ -static noinline int do_fw_call_hvc(u64 arg0, u64 arg1, u64 arg2, - u32 *ret_payload) +static noinline int do_fw_call(u64 arg0, u64 arg1, u64 arg2, u32 *ret_payload) { struct arm_smccc_res res;
- arm_smccc_hvc(arg0, arg1, arg2, 0, 0, 0, 0, 0, &res); + if (arm_smccc_1_0_invoke(arg0, arg1, arg2, 0, 0, 0, 0, 0, &res) == + SMCCC_CONDUIT_NONE) + return -ENODEV;
if (ret_payload) { ret_payload[0] = lower_32_bits(res.a0); @@ -287,36 +248,6 @@ static int zynqmp_pm_get_trustzone_version(u32 *version) return ret; }
-/** - * get_set_conduit_method() - Choose SMC or HVC based communication - * @np: Pointer to the device_node structure - * - * Use SMC or HVC-based functions to communicate with EL2/EL3. - * - * Return: Returns 0 on success or error code - */ -static int get_set_conduit_method(struct device_node *np) -{ - const char *method; - - if (of_property_read_string(np, "method", &method)) { - pr_warn("%s missing "method" property\n", __func__); - return -ENXIO; - } - - if (!strcmp("hvc", method)) { - do_fw_call = do_fw_call_hvc; - } else if (!strcmp("smc", method)) { - do_fw_call = do_fw_call_smc; - } else { - pr_warn("%s Invalid "method" property: %s\n", - __func__, method); - return -EINVAL; - } - - return 0; -} - /** * zynqmp_pm_query_data() - Get query data from firmware * @qdata: Variable to the zynqmp_pm_query_data structure @@ -790,7 +721,7 @@ static int zynqmp_firmware_probe(struct platform_device *pdev) } of_node_put(np);
- ret = get_set_conduit_method(dev->of_node); + ret = devm_arm_smccc_set_conduit(dev); if (ret) return ret;