Message ID | 1554821161-4623-1-git-send-email-marek.bykowski@gmail.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | drivers/firmware: arch64: Add SMC call testing module | expand |
Hi Marek, On Tue, Apr 09, 2019 at 04:46:01PM +0200, marek.bykowski@gmail.com wrote: > From: Marek Bykowski <marek.bykowski@gmail.com> > > The module measures the round-trip latency for both PSCI_VERSION and > SMCCC_ARCH_WORKAROUND_1 conforming to SMC Calling Convention v1.0 and v1.1 > respectively for each of the CPUs. Knowing these latencies is becoming > critical in quantifying the performance impact resulting in from execution > of the mitigation to CVE-2017-5715. Generally the module application can be > extended into measuring an arbitrary SMC call duration. While this is easy to quantify, the overhead in practice will depend on your specific application, how it is scheduled, etc. I'd generally hope that people would benchmark the typical workload that they care about. I can see that this is useful for firmware developers, and it looks like the trusted firmware test suite already has performance tests for exactly these two cases: https://git.trustedfirmware.org/TF-A/tf-a-tests.git/tree/tftf/tests/performance_tests/smc_latencies.c ... given that, I'm not sure if we really need this in Linux. The code in this patch is missing necessary checks (e.g. that SMCCC is present and uses SMC), and as such isn't safe on all platforms. Given it duplicates functionality we have elsewhere in the arm64 errata handling framework, I'd rather the benchmarking lived there if we actually need it. I'm not going to review the code below in detail, but I note that there are unusued structure definitions, includes that don't exist in mainline, and vestigal function names from another module. So as-is, I don't think this is suitable for mainline. Thanks, Mark. > The sample results for a SoC based on ARM Cortex-A57 MPCore Processor > running 16 CPUs is as follows: > > SMC_VARIANT(0): smc_call_v10 [smc_time] > CPU0 CPU1 CPU2 CPU3 CPU4 CPU5 CPU6 CPU7 CPU8 CPU9 CPU10 CPU11 CPU12 CPU13 CPU14 CPU15 > 4812 5140 5039 5199 5265 5242 4571 4715 4879 4891 4886 4910 4016 4215 4015 4051 > SMC_VARIANT(1): smc_call_v11 [smc_time] > CPU0 CPU1 CPU2 CPU3 CPU4 CPU5 CPU6 CPU7 CPU8 CPU9 CPU10 CPU11 CPU12 CPU13 CPU14 CPU15 > 476 481 489 489 496 496 496 496 496 496 504 496 480 481 469 477 > > Signed-off-by: Marek Bykowski <marek.bykowski@gmail.com> > --- > drivers/firmware/Kconfig | 9 ++ > drivers/firmware/Makefile | 1 + > drivers/firmware/smc_latency.c | 220 +++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 230 insertions(+) > create mode 100644 drivers/firmware/smc_latency.c > > diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig > index bca172d42c74..30777ac0ce1b 100644 > --- a/drivers/firmware/Kconfig > +++ b/drivers/firmware/Kconfig > @@ -195,6 +195,15 @@ config QCOM_SCM > depends on ARM || ARM64 > select RESET_CONTROLLER > > +config SMC_LATENCY > + tristate "Measure round-trip latency of the SMC calls" > + depends on HAVE_ARM_SMCCC > + default m > + help > + Say Y or M here to enable the measurement of the round-trip latency > + for both PSCI_VERSION and SMCCC_ARCH_WORKAROUND_1 conforming to SMC > + Calling Convention v1.0 and v1.1 respectively for each of the CPUs. > + > config QCOM_SCM_32 > def_bool y > depends on QCOM_SCM && ARM > diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile > index 898ac41fa8b3..382bf01470f7 100644 > --- a/drivers/firmware/Makefile > +++ b/drivers/firmware/Makefile > @@ -19,6 +19,7 @@ obj-$(CONFIG_FW_CFG_SYSFS) += qemu_fw_cfg.o > obj-$(CONFIG_QCOM_SCM) += qcom_scm.o > obj-$(CONFIG_QCOM_SCM_64) += qcom_scm-64.o > obj-$(CONFIG_QCOM_SCM_32) += qcom_scm-32.o > +obj-$(CONFIG_SMC_LATENCY) += smc_latency.o > CFLAGS_qcom_scm-32.o :=$(call as-instr,.arch armv7-a\n.arch_extension sec,-DREQUIRES_SEC=1) -march=armv7-a > > obj-y += broadcom/ > diff --git a/drivers/firmware/smc_latency.c b/drivers/firmware/smc_latency.c > new file mode 100644 > index 000000000000..fc3697697ed6 > --- /dev/null > +++ b/drivers/firmware/smc_latency.c > @@ -0,0 +1,220 @@ > +/* > + * > + * SMC testing module > + * > + * Copyright (C) 2019 Intel Corporation > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 as > + * published by the Free Software Foundation. > + */ > +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt > + > +#include <linux/module.h> > +#include <linux/of.h> > +#include <linux/proc_fs.h> > +#include <linux/slab.h> > +#include <linux/linkage.h> > +#include <linux/axxia-oem.h> > +#include <linux/uaccess.h> > +#include <linux/arm-smccc.h> > +#include <asm/smp_plat.h> > +#include <linux/seq_file.h> > +#include <uapi/linux/psci.h> > + > +#define SMC_DEBUG 0 > +#if SMC_DEBUG > +#define smc_dbg(...) pr_info(__VA_ARGS__) > +#define seq_printf(...) > +#define seq_puts(...) > +#else > +#define smc_dbg(...) > +#define seq_printf(arg0, ...) seq_printf(arg0, __VA_ARGS__) > +#define seq_puts(arg0, ...) seq_puts(arg0, __VA_ARGS__) > +#endif > + > +struct _pt_regs { > + unsigned long elr; > + unsigned long regs[31]; > +}; > + > +typedef void (*smc_cb_t)(void *); > + > +static bool all_cpus; > +module_param(all_cpus, bool, 0644); > +MODULE_PARM_DESC(all_cpus, "Run smc call on executing or all cpus (default: all"); > + > +void smc_call_v10(void *arg); > +void smc_call_v11(void *arg); > + > +smc_cb_t smc_call[] = { > + smc_call_v10, > + smc_call_v11 > +}; > + > +#define SMC_VARIANTS ARRAY_SIZE(smc_call) > +#define SMC_VARIANT(n) n > +#define for_each_smc(smc) \ > + for ((smc) = 0; (smc) < SMC_VARIANTS; (smc)++) > + > +unsigned long __percpu *time[SMC_VARIANTS]; > + > +/* SMCCC 1.0 */ > +void smc_call_v10(void *arg) > +{ > + struct pt_regs _regs = {0}, *regs = &_regs; > + int smc = *(unsigned int *)arg; > + struct arm_smccc_res res; > + ktime_t start, end; > + > + regs->regs[0] = PSCI_0_2_FN_PSCI_VERSION; > + > + start = ktime_get(); > + arm_smccc_smc(regs->regs[0], regs->regs[1], regs->regs[2], > + regs->regs[3], 0, 0, 0, 0, &res); > + end = ktime_get(); > + > + this_cpu_write(*time[smc], ktime_to_ns(ktime_sub(end, start))); > + smc_dbg("cpu%d %lu ns (ret %#lx)\n", > + smc, > + this_cpu_read(*time[smc]), > + res.a0); > +} > + > +/* SMCCC 1.1 */ > +void smc_call_v11(void *arg) > +{ > + int smc = *(unsigned int *)arg; > + ktime_t start, end; > + > + start = ktime_get(); > + arm_smccc_1_1_smc(ARM_SMCCC_ARCH_WORKAROUND_1, NULL); > + end = ktime_get(); > + > + this_cpu_write(*time[smc], ktime_to_ns(ktime_sub(end, start))); > + smc_dbg("cpu%d %lu ns (ret %s)\n", > + smc, > + this_cpu_read(*time[smc]), > + "RET0 in smc call"); > +} > + > +void > +zeroise_times(void) > +{ > + int cpu, smc; > + > + for_each_smc(smc) { > + for_each_online_cpu(cpu) { > + *per_cpu_ptr(time[smc], cpu) = 0; > + } > + } > +} > + > +static void run_test(void) > +{ > + int smc; > + > + /* init the smc call regs before the next call round */ > + zeroise_times(); > + > + switch (all_cpus) { > + case true: > + for_each_smc(smc) { > + int cpu; > + > + for_each_online_cpu(cpu) { > + int ret; > + > + ret = smp_call_function_single(cpu, smc_call[smc], > + (void *)&smc, 1); > + if (ret) > + WARN_ON(ret); > + } > + } > + break; > + case false: > + for_each_smc(smc) > + smc_call[smc]((void *)&smc); > + break; > + } > +} > + > + > +static int proc_smc_show(struct seq_file *m, void *v) > +{ > + int smc; > + > + run_test(); > + > + for_each_smc(smc) { > + int cpu; > + > + seq_printf(m, "SMC_VARIANT(%u): %pf\n", smc, smc_call[smc]); > + for_each_online_cpu(cpu) > + seq_printf(m, "CPU%-2d ", cpu); > + seq_puts(m, "\n"); > + for_each_online_cpu(cpu) > + seq_printf(m, "%-5lu ", > + *per_cpu_ptr(time[SMC_VARIANT(smc)], cpu)); > + seq_puts(m, "\n"); > + } > + > + return 0; > +} > + > +static int proc_smc_open(struct inode *inode, struct file *file); > +static const struct file_operations proc_smc_operations = { > + .open = proc_smc_open, > + .read = seq_read, > + .llseek = seq_lseek, > + .release = single_release, > +}; > + > +static int proc_smc_open(struct inode *inode, struct file *file) > +{ > + return single_open(file, proc_smc_show, NULL); > +} > + > +static struct proc_dir_entry *procent; > +static int __init proc_dma_init(void) > +{ > + procent = proc_create("axxia_smccall", 0444, NULL, > + &proc_smc_operations); > + return 0; > +} > + > + > +static int __init smctest_init(void) > +{ > + unsigned int smc; > + > + for_each_smc(smc) > + time[smc] = alloc_percpu(unsigned long); > + > + zeroise_times(); > + proc_dma_init(); > + > + return 0; > +} > +late_initcall(smctest_init); > + > +static void __exit dmatest_exit(void) > +{ > + unsigned int smc; > + > + for_each_smc(smc) > + if (time[smc]) { > + free_percpu(time[smc]); > + time[smc] = NULL; > + } > + > + if (procent) { > + proc_remove(procent); > + procent = NULL; > + } > +} > + > +module_exit(dmatest_exit); > + > +MODULE_AUTHOR("Marek Bykowski"); > +MODULE_LICENSE("GPL v2"); > -- > 2.16.2 > > > _______________________________________________ > linux-arm-kernel mailing list > linux-arm-kernel@lists.infradead.org > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
[Apology I didn't respond in the plain text so resending to the mailing list] Hi Mark, Thanks for looking through and the posting. Honestly I didn't know a test framework for ATF as this exists even though I'm actually part time working on Uboot/Secure Monitor around the Axxia ARM boards. Actually the patch of myslef is a result of getting the SMCCC upgraded from 1.0 to 1.1 around the Axxia's TF and testing it from within the kernel. For the SMC test in tf-a-tests https://git.trustedfirmware.org/TF-A/tf-a-tests.git/tree/tftf/tests/performance_tests/smc_latencies.c as far as I understood the test module built replaces BL33 at EL2 (non-secure state only) to test. First, integration of that framework is not easy for I guess many platforms, eg. for ours wouldn't be, for the reason we have a mixture of bootloaders off various places; ROM bootloader is ours, Secure Monitor is taken off ATF, the BL3 and BL33 is SPL Uboot and Uboot respectively. Secondly I don't think it tests the SMC latencies for all the CPUs but only for the boot CPU/CPU0. For us testing the (upgraded) SMC calls on all the CPUs was important as it turned out the MMU wasn't enabled for the secondary CPUs at EL3 and a go at invalidating the BTB by turning the MMU off and on (aka execution of the mitigation to CVE-2017-5715) led to board hang (less important now the reason but for the reason the Dcache was on by reset in SCTLR_EL3 when turning the MMU on for the code sitting in (static) memory non-cachable). If you reckon the test module I patched could be acceptable if it is cleaned for the mainstream, the aforementioned missing checks if SMCCC is supported, duplicated/unused structs, includes not existing in the mainline, etc. I could work on it. I already know our Customers are interested in this module that is why I thought I'd get the ARM community to consider if it could be pushed to the mainline. Thanks, Marek On Tue, 9 Apr 2019 at 18:07, Mark Rutland <mark.rutland@arm.com> wrote: > > Hi Marek, > > On Tue, Apr 09, 2019 at 04:46:01PM +0200, marek.bykowski@gmail.com wrote: > > From: Marek Bykowski <marek.bykowski@gmail.com> > > > > The module measures the round-trip latency for both PSCI_VERSION and > > SMCCC_ARCH_WORKAROUND_1 conforming to SMC Calling Convention v1.0 and v1.1 > > respectively for each of the CPUs. Knowing these latencies is becoming > > critical in quantifying the performance impact resulting in from execution > > of the mitigation to CVE-2017-5715. Generally the module application can be > > extended into measuring an arbitrary SMC call duration. > > While this is easy to quantify, the overhead in practice will depend on > your specific application, how it is scheduled, etc. I'd generally hope > that people would benchmark the typical workload that they care about. > > I can see that this is useful for firmware developers, and it looks like > the trusted firmware test suite already has performance tests for > exactly these two cases: > > https://git.trustedfirmware.org/TF-A/tf-a-tests.git/tree/tftf/tests/performance_tests/smc_latencies.c > > ... given that, I'm not sure if we really need this in Linux. > > The code in this patch is missing necessary checks (e.g. that SMCCC is > present and uses SMC), and as such isn't safe on all platforms. Given it > duplicates functionality we have elsewhere in the arm64 errata handling > framework, I'd rather the benchmarking lived there if we actually need > it. > > I'm not going to review the code below in detail, but I note that there > are unusued structure definitions, includes that don't exist in > mainline, and vestigal function names from another module. So as-is, I > don't think this is suitable for mainline. > > Thanks, > Mark. > > > The sample results for a SoC based on ARM Cortex-A57 MPCore Processor > > running 16 CPUs is as follows: > > > > SMC_VARIANT(0): smc_call_v10 [smc_time] > > CPU0 CPU1 CPU2 CPU3 CPU4 CPU5 CPU6 CPU7 CPU8 CPU9 CPU10 CPU11 CPU12 CPU13 CPU14 CPU15 > > 4812 5140 5039 5199 5265 5242 4571 4715 4879 4891 4886 4910 4016 4215 4015 4051 > > SMC_VARIANT(1): smc_call_v11 [smc_time] > > CPU0 CPU1 CPU2 CPU3 CPU4 CPU5 CPU6 CPU7 CPU8 CPU9 CPU10 CPU11 CPU12 CPU13 CPU14 CPU15 > > 476 481 489 489 496 496 496 496 496 496 504 496 480 481 469 477 > > > > Signed-off-by: Marek Bykowski <marek.bykowski@gmail.com> > > --- > > drivers/firmware/Kconfig | 9 ++ > > drivers/firmware/Makefile | 1 + > > drivers/firmware/smc_latency.c | 220 +++++++++++++++++++++++++++++++++++++++++ > > 3 files changed, 230 insertions(+) > > create mode 100644 drivers/firmware/smc_latency.c > > > > diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig > > index bca172d42c74..30777ac0ce1b 100644 > > --- a/drivers/firmware/Kconfig > > +++ b/drivers/firmware/Kconfig > > @@ -195,6 +195,15 @@ config QCOM_SCM > > depends on ARM || ARM64 > > select RESET_CONTROLLER > > > > +config SMC_LATENCY > > + tristate "Measure round-trip latency of the SMC calls" > > + depends on HAVE_ARM_SMCCC > > + default m > > + help > > + Say Y or M here to enable the measurement of the round-trip latency > > + for both PSCI_VERSION and SMCCC_ARCH_WORKAROUND_1 conforming to SMC > > + Calling Convention v1.0 and v1.1 respectively for each of the CPUs. > > + > > config QCOM_SCM_32 > > def_bool y > > depends on QCOM_SCM && ARM > > diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile > > index 898ac41fa8b3..382bf01470f7 100644 > > --- a/drivers/firmware/Makefile > > +++ b/drivers/firmware/Makefile > > @@ -19,6 +19,7 @@ obj-$(CONFIG_FW_CFG_SYSFS) += qemu_fw_cfg.o > > obj-$(CONFIG_QCOM_SCM) += qcom_scm.o > > obj-$(CONFIG_QCOM_SCM_64) += qcom_scm-64.o > > obj-$(CONFIG_QCOM_SCM_32) += qcom_scm-32.o > > +obj-$(CONFIG_SMC_LATENCY) += smc_latency.o > > CFLAGS_qcom_scm-32.o :=$(call as-instr,.arch armv7-a\n.arch_extension sec,-DREQUIRES_SEC=1) -march=armv7-a > > > > obj-y += broadcom/ > > diff --git a/drivers/firmware/smc_latency.c b/drivers/firmware/smc_latency.c > > new file mode 100644 > > index 000000000000..fc3697697ed6 > > --- /dev/null > > +++ b/drivers/firmware/smc_latency.c > > @@ -0,0 +1,220 @@ > > +/* > > + * > > + * SMC testing module > > + * > > + * Copyright (C) 2019 Intel Corporation > > + * > > + * This program is free software; you can redistribute it and/or modify > > + * it under the terms of the GNU General Public License version 2 as > > + * published by the Free Software Foundation. > > + */ > > +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt > > + > > +#include <linux/module.h> > > +#include <linux/of.h> > > +#include <linux/proc_fs.h> > > +#include <linux/slab.h> > > +#include <linux/linkage.h> > > +#include <linux/axxia-oem.h> > > +#include <linux/uaccess.h> > > +#include <linux/arm-smccc.h> > > +#include <asm/smp_plat.h> > > +#include <linux/seq_file.h> > > +#include <uapi/linux/psci.h> > > + > > +#define SMC_DEBUG 0 > > +#if SMC_DEBUG > > +#define smc_dbg(...) pr_info(__VA_ARGS__) > > +#define seq_printf(...) > > +#define seq_puts(...) > > +#else > > +#define smc_dbg(...) > > +#define seq_printf(arg0, ...) seq_printf(arg0, __VA_ARGS__) > > +#define seq_puts(arg0, ...) seq_puts(arg0, __VA_ARGS__) > > +#endif > > + > > +struct _pt_regs { > > + unsigned long elr; > > + unsigned long regs[31]; > > +}; > > + > > +typedef void (*smc_cb_t)(void *); > > + > > +static bool all_cpus; > > +module_param(all_cpus, bool, 0644); > > +MODULE_PARM_DESC(all_cpus, "Run smc call on executing or all cpus (default: all"); > > + > > +void smc_call_v10(void *arg); > > +void smc_call_v11(void *arg); > > + > > +smc_cb_t smc_call[] = { > > + smc_call_v10, > > + smc_call_v11 > > +}; > > + > > +#define SMC_VARIANTS ARRAY_SIZE(smc_call) > > +#define SMC_VARIANT(n) n > > +#define for_each_smc(smc) \ > > + for ((smc) = 0; (smc) < SMC_VARIANTS; (smc)++) > > + > > +unsigned long __percpu *time[SMC_VARIANTS]; > > + > > +/* SMCCC 1.0 */ > > +void smc_call_v10(void *arg) > > +{ > > + struct pt_regs _regs = {0}, *regs = &_regs; > > + int smc = *(unsigned int *)arg; > > + struct arm_smccc_res res; > > + ktime_t start, end; > > + > > + regs->regs[0] = PSCI_0_2_FN_PSCI_VERSION; > > + > > + start = ktime_get(); > > + arm_smccc_smc(regs->regs[0], regs->regs[1], regs->regs[2], > > + regs->regs[3], 0, 0, 0, 0, &res); > > + end = ktime_get(); > > + > > + this_cpu_write(*time[smc], ktime_to_ns(ktime_sub(end, start))); > > + smc_dbg("cpu%d %lu ns (ret %#lx)\n", > > + smc, > > + this_cpu_read(*time[smc]), > > + res.a0); > > +} > > + > > +/* SMCCC 1.1 */ > > +void smc_call_v11(void *arg) > > +{ > > + int smc = *(unsigned int *)arg; > > + ktime_t start, end; > > + > > + start = ktime_get(); > > + arm_smccc_1_1_smc(ARM_SMCCC_ARCH_WORKAROUND_1, NULL); > > + end = ktime_get(); > > + > > + this_cpu_write(*time[smc], ktime_to_ns(ktime_sub(end, start))); > > + smc_dbg("cpu%d %lu ns (ret %s)\n", > > + smc, > > + this_cpu_read(*time[smc]), > > + "RET0 in smc call"); > > +} > > + > > +void > > +zeroise_times(void) > > +{ > > + int cpu, smc; > > + > > + for_each_smc(smc) { > > + for_each_online_cpu(cpu) { > > + *per_cpu_ptr(time[smc], cpu) = 0; > > + } > > + } > > +} > > + > > +static void run_test(void) > > +{ > > + int smc; > > + > > + /* init the smc call regs before the next call round */ > > + zeroise_times(); > > + > > + switch (all_cpus) { > > + case true: > > + for_each_smc(smc) { > > + int cpu; > > + > > + for_each_online_cpu(cpu) { > > + int ret; > > + > > + ret = smp_call_function_single(cpu, smc_call[smc], > > + (void *)&smc, 1); > > + if (ret) > > + WARN_ON(ret); > > + } > > + } > > + break; > > + case false: > > + for_each_smc(smc) > > + smc_call[smc]((void *)&smc); > > + break; > > + } > > +} > > + > > + > > +static int proc_smc_show(struct seq_file *m, void *v) > > +{ > > + int smc; > > + > > + run_test(); > > + > > + for_each_smc(smc) { > > + int cpu; > > + > > + seq_printf(m, "SMC_VARIANT(%u): %pf\n", smc, smc_call[smc]); > > + for_each_online_cpu(cpu) > > + seq_printf(m, "CPU%-2d ", cpu); > > + seq_puts(m, "\n"); > > + for_each_online_cpu(cpu) > > + seq_printf(m, "%-5lu ", > > + *per_cpu_ptr(time[SMC_VARIANT(smc)], cpu)); > > + seq_puts(m, "\n"); > > + } > > + > > + return 0; > > +} > > + > > +static int proc_smc_open(struct inode *inode, struct file *file); > > +static const struct file_operations proc_smc_operations = { > > + .open = proc_smc_open, > > + .read = seq_read, > > + .llseek = seq_lseek, > > + .release = single_release, > > +}; > > + > > +static int proc_smc_open(struct inode *inode, struct file *file) > > +{ > > + return single_open(file, proc_smc_show, NULL); > > +} > > + > > +static struct proc_dir_entry *procent; > > +static int __init proc_dma_init(void) > > +{ > > + procent = proc_create("axxia_smccall", 0444, NULL, > > + &proc_smc_operations); > > + return 0; > > +} > > + > > + > > +static int __init smctest_init(void) > > +{ > > + unsigned int smc; > > + > > + for_each_smc(smc) > > + time[smc] = alloc_percpu(unsigned long); > > + > > + zeroise_times(); > > + proc_dma_init(); > > + > > + return 0; > > +} > > +late_initcall(smctest_init); > > + > > +static void __exit dmatest_exit(void) > > +{ > > + unsigned int smc; > > + > > + for_each_smc(smc) > > + if (time[smc]) { > > + free_percpu(time[smc]); > > + time[smc] = NULL; > > + } > > + > > + if (procent) { > > + proc_remove(procent); > > + procent = NULL; > > + } > > +} > > + > > +module_exit(dmatest_exit); > > + > > +MODULE_AUTHOR("Marek Bykowski"); > > +MODULE_LICENSE("GPL v2"); > > -- > > 2.16.2 > > > > > > _______________________________________________ > > linux-arm-kernel mailing list > > linux-arm-kernel@lists.infradead.org > > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel -- Slán, Marek
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index bca172d42c74..30777ac0ce1b 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -195,6 +195,15 @@ config QCOM_SCM depends on ARM || ARM64 select RESET_CONTROLLER +config SMC_LATENCY + tristate "Measure round-trip latency of the SMC calls" + depends on HAVE_ARM_SMCCC + default m + help + Say Y or M here to enable the measurement of the round-trip latency + for both PSCI_VERSION and SMCCC_ARCH_WORKAROUND_1 conforming to SMC + Calling Convention v1.0 and v1.1 respectively for each of the CPUs. + config QCOM_SCM_32 def_bool y depends on QCOM_SCM && ARM diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile index 898ac41fa8b3..382bf01470f7 100644 --- a/drivers/firmware/Makefile +++ b/drivers/firmware/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_FW_CFG_SYSFS) += qemu_fw_cfg.o obj-$(CONFIG_QCOM_SCM) += qcom_scm.o obj-$(CONFIG_QCOM_SCM_64) += qcom_scm-64.o obj-$(CONFIG_QCOM_SCM_32) += qcom_scm-32.o +obj-$(CONFIG_SMC_LATENCY) += smc_latency.o CFLAGS_qcom_scm-32.o :=$(call as-instr,.arch armv7-a\n.arch_extension sec,-DREQUIRES_SEC=1) -march=armv7-a obj-y += broadcom/ diff --git a/drivers/firmware/smc_latency.c b/drivers/firmware/smc_latency.c new file mode 100644 index 000000000000..fc3697697ed6 --- /dev/null +++ b/drivers/firmware/smc_latency.c @@ -0,0 +1,220 @@ +/* + * + * SMC testing module + * + * Copyright (C) 2019 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h> +#include <linux/of.h> +#include <linux/proc_fs.h> +#include <linux/slab.h> +#include <linux/linkage.h> +#include <linux/axxia-oem.h> +#include <linux/uaccess.h> +#include <linux/arm-smccc.h> +#include <asm/smp_plat.h> +#include <linux/seq_file.h> +#include <uapi/linux/psci.h> + +#define SMC_DEBUG 0 +#if SMC_DEBUG +#define smc_dbg(...) pr_info(__VA_ARGS__) +#define seq_printf(...) +#define seq_puts(...) +#else +#define smc_dbg(...) +#define seq_printf(arg0, ...) seq_printf(arg0, __VA_ARGS__) +#define seq_puts(arg0, ...) seq_puts(arg0, __VA_ARGS__) +#endif + +struct _pt_regs { + unsigned long elr; + unsigned long regs[31]; +}; + +typedef void (*smc_cb_t)(void *); + +static bool all_cpus; +module_param(all_cpus, bool, 0644); +MODULE_PARM_DESC(all_cpus, "Run smc call on executing or all cpus (default: all"); + +void smc_call_v10(void *arg); +void smc_call_v11(void *arg); + +smc_cb_t smc_call[] = { + smc_call_v10, + smc_call_v11 +}; + +#define SMC_VARIANTS ARRAY_SIZE(smc_call) +#define SMC_VARIANT(n) n +#define for_each_smc(smc) \ + for ((smc) = 0; (smc) < SMC_VARIANTS; (smc)++) + +unsigned long __percpu *time[SMC_VARIANTS]; + +/* SMCCC 1.0 */ +void smc_call_v10(void *arg) +{ + struct pt_regs _regs = {0}, *regs = &_regs; + int smc = *(unsigned int *)arg; + struct arm_smccc_res res; + ktime_t start, end; + + regs->regs[0] = PSCI_0_2_FN_PSCI_VERSION; + + start = ktime_get(); + arm_smccc_smc(regs->regs[0], regs->regs[1], regs->regs[2], + regs->regs[3], 0, 0, 0, 0, &res); + end = ktime_get(); + + this_cpu_write(*time[smc], ktime_to_ns(ktime_sub(end, start))); + smc_dbg("cpu%d %lu ns (ret %#lx)\n", + smc, + this_cpu_read(*time[smc]), + res.a0); +} + +/* SMCCC 1.1 */ +void smc_call_v11(void *arg) +{ + int smc = *(unsigned int *)arg; + ktime_t start, end; + + start = ktime_get(); + arm_smccc_1_1_smc(ARM_SMCCC_ARCH_WORKAROUND_1, NULL); + end = ktime_get(); + + this_cpu_write(*time[smc], ktime_to_ns(ktime_sub(end, start))); + smc_dbg("cpu%d %lu ns (ret %s)\n", + smc, + this_cpu_read(*time[smc]), + "RET0 in smc call"); +} + +void +zeroise_times(void) +{ + int cpu, smc; + + for_each_smc(smc) { + for_each_online_cpu(cpu) { + *per_cpu_ptr(time[smc], cpu) = 0; + } + } +} + +static void run_test(void) +{ + int smc; + + /* init the smc call regs before the next call round */ + zeroise_times(); + + switch (all_cpus) { + case true: + for_each_smc(smc) { + int cpu; + + for_each_online_cpu(cpu) { + int ret; + + ret = smp_call_function_single(cpu, smc_call[smc], + (void *)&smc, 1); + if (ret) + WARN_ON(ret); + } + } + break; + case false: + for_each_smc(smc) + smc_call[smc]((void *)&smc); + break; + } +} + + +static int proc_smc_show(struct seq_file *m, void *v) +{ + int smc; + + run_test(); + + for_each_smc(smc) { + int cpu; + + seq_printf(m, "SMC_VARIANT(%u): %pf\n", smc, smc_call[smc]); + for_each_online_cpu(cpu) + seq_printf(m, "CPU%-2d ", cpu); + seq_puts(m, "\n"); + for_each_online_cpu(cpu) + seq_printf(m, "%-5lu ", + *per_cpu_ptr(time[SMC_VARIANT(smc)], cpu)); + seq_puts(m, "\n"); + } + + return 0; +} + +static int proc_smc_open(struct inode *inode, struct file *file); +static const struct file_operations proc_smc_operations = { + .open = proc_smc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int proc_smc_open(struct inode *inode, struct file *file) +{ + return single_open(file, proc_smc_show, NULL); +} + +static struct proc_dir_entry *procent; +static int __init proc_dma_init(void) +{ + procent = proc_create("axxia_smccall", 0444, NULL, + &proc_smc_operations); + return 0; +} + + +static int __init smctest_init(void) +{ + unsigned int smc; + + for_each_smc(smc) + time[smc] = alloc_percpu(unsigned long); + + zeroise_times(); + proc_dma_init(); + + return 0; +} +late_initcall(smctest_init); + +static void __exit dmatest_exit(void) +{ + unsigned int smc; + + for_each_smc(smc) + if (time[smc]) { + free_percpu(time[smc]); + time[smc] = NULL; + } + + if (procent) { + proc_remove(procent); + procent = NULL; + } +} + +module_exit(dmatest_exit); + +MODULE_AUTHOR("Marek Bykowski"); +MODULE_LICENSE("GPL v2");