aboutsummaryrefslogtreecommitdiff
path: root/tftf
diff options
context:
space:
mode:
authorSoby Mathew <soby.mathew@arm.com>2019-10-04 10:21:36 +0000
committerTrustedFirmware Code Review <review@review.trustedfirmware.org>2019-10-04 10:21:36 +0000
commit7edf1a5ef3153f761c094c035da3eb10501b7247 (patch)
tree49fc71545c6e941eebdeb6d5601884a87a7f944a /tftf
parente73248e004d971adb6259000d463c929f756a345 (diff)
parentf68ebdb9b45cc7a58f816f153f5e626c898dc0cf (diff)
downloadtf-a-tests-7edf1a5ef3153f761c094c035da3eb10501b7247.tar.gz
Merge "Try to leak counter values from secure world."
Diffstat (limited to 'tftf')
-rw-r--r--tftf/tests/misc_tests/test_pmu_leakage.c378
-rw-r--r--tftf/tests/tests-extensive.xml2
-rw-r--r--tftf/tests/tests-pmu-leakage.mk9
-rw-r--r--tftf/tests/tests-pmu-leakage.xml16
-rw-r--r--tftf/tests/tests-standard.mk3
-rw-r--r--tftf/tests/tests-standard.xml4
6 files changed, 410 insertions, 2 deletions
diff --git a/tftf/tests/misc_tests/test_pmu_leakage.c b/tftf/tests/misc_tests/test_pmu_leakage.c
new file mode 100644
index 00000000..a0ac82d7
--- /dev/null
+++ b/tftf/tests/misc_tests/test_pmu_leakage.c
@@ -0,0 +1,378 @@
+/*
+ * Copyright (c) 2019, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+/*
+ * This file contains tests that try to leak information from the secure world
+ * to the non-secure world (EL2) by using the PMU counters.
+ *
+ * The tests assume that the PMU (PMUv3) is implemented on the target, since
+ * TF-A performs initialization of the PMU and guards against PMU counter
+ * leakage.
+ *
+ * The non-secure world can use system registers to configure the PMU such that
+ * it increments counters in the secure world. Depending on the implemented
+ * features, the secure world can prohibit counting via the following:
+ * -v8.2 Debug not implemented:
+ * |-- Prohibit general event counters and the cycle counter:
+ * MDCR_EL3.SPME == 0 && !ExternalSecureNoninvasiveDebugEnabled()
+ * Since ExternalSecureNoninvasiveDebugEnabled() is a hardware
+ * line, it is not available on FVP and will therefore cause the
+ * tests to fail.
+ * The only other way is to disable the PMCR_EL0.E bit. This will
+ * disable counting altogether, but since this fix is not desired
+ * in TF-A, the tests have to be skipped if v8.2 Debug is not
+ * implemented.
+ *
+ * -v8.2 Debug implemented:
+ * |-- Prohibit general event counters: MDCR_EL3.SPME == 0. This bit
+ * resets to 0, so by default general events should not be counted
+ * in the secure world.
+ * |-- Prohibit cycle counter: MDCR_EL3.SPME == 0 && PMCR_EL0.DP == 1.
+ * This counter is only affected by MDCR_EL3.SPME if the
+ * PMCR_EL0.DP bit is set.
+ *
+ * -v8.5 implemented:
+ * |-- Prohibit general event counters: as in v8.2 Debug.
+ * |-- Prohibit cycle counter: MDCR_EL3.SCCD == 1
+ */
+
+#include <drivers/arm/arm_gic.h>
+#include <irq.h>
+#include <platform.h>
+#include <power_management.h>
+#include <sgi.h>
+#include <string.h>
+#include <test_helpers.h>
+
+#ifdef AARCH64
+#define ITERATIONS_CNT 1000
+
+/*
+ * A maximum of +10% deviation in event counts is tolerated.
+ * This is useful for testing on real hardware where event counts are usually
+ * not the same between runs. The large iteration count should cause the
+ * average event count to converge to values very close to baseline when the
+ * secure world successfully prohibits PMU counters from incrementing.
+ */
+#define ALLOWED_DEVIATION 10
+
+/*
+ * An invalid SMC function number.
+ * Used to establish a base value for PMU counters on each test.
+ */
+#define INVALID_FN 0x666
+
+struct pmu_event_info {
+ unsigned long long min;
+ unsigned long long max;
+ unsigned long long avg;
+};
+
+static inline void configure_pmu_cntr0(const uint32_t event)
+{
+ /*
+ * Disabling the P bit tells the counter to increment at EL1.
+ * Setting the NSK bit to be different from the P bit further tells the
+ * counter NOT to increment at non-secure EL1. Combined with the P bit,
+ * the effect is to tell the counter to increment at secure EL1.
+ * Setting the M bit to be equal to the P bit tells the counter to
+ * increment at EL3.
+ * Disabling the NSH bit tells the counter NOT to increment at
+ * non-secure EL2.
+ * Setting the SH bit to be different to the NSH bit tells the counter
+ * to increment at secure EL2.
+ * The counter therefore is told to count only at secure EL1, secure EL2
+ * and EL3. This is to ensure maximum accuracy of the results, since we
+ * are only interested if the secure world is leaking PMU counters.
+ */
+ write_pmevtyper0_el0(
+ (read_pmevtyper0_el0() | PMEVTYPER_EL0_NSK_BIT |
+ PMEVTYPER_EL0_SH_BIT) &
+ ~(PMEVTYPER_EL0_P_BIT | PMEVTYPER_EL0_NSH_BIT |
+ PMEVTYPER_EL0_M_BIT));
+
+ /*
+ * Write to the EVTCOUNT bits to tell the counter which event to
+ * monitor.
+ */
+ write_pmevtyper0_el0(
+ (read_pmevtyper0_el0() & ~PMEVTYPER_EL0_EVTCOUNT_BITS) | event);
+
+ /* Setting the P[n] bit enables counter n */
+ write_pmcntenset_el0(
+ read_pmcntenset_el0() | PMCNTENSET_EL0_P_BIT(0));
+}
+
+static inline void configure_pmu_cycle_cntr(void)
+{
+ /*
+ * Disabling the P bit tells the counter to increment at EL1.
+ * Setting the NSK bit to be different from the P bit further tells the
+ * counter NOT to increment at non-secure EL1. Combined with the P bit,
+ * the effect is to tell the counter to increment at secure EL1.
+ * Setting the M bit to be equal to the P bit tells the counter to
+ * increment at EL3.
+ * Disabling the NSH bit tells the counter NOT to increment at
+ * non-secure EL2.
+ * Setting the SH bit to be different to the NSH bit tells the counter
+ * to increment at secure EL2.
+ * The counter therefore is told to count only at secure EL1, secure EL2
+ * and EL3. This is to ensure maximum accuracy of the results, since we
+ * are only interested if the secure world is leaking PMU counters.
+ */
+ write_pmccfiltr_el0(
+ (read_pmccfiltr_el0() | PMCCFILTR_EL0_NSK_BIT |
+ PMCCFILTR_EL0_SH_BIT) &
+ ~(PMCCFILTR_EL0_P_BIT | PMCCFILTR_EL0_NSH_BIT |
+ PMCCFILTR_EL0_M_BIT));
+
+ /* Setting the C bit enables the cycle counter in the PMU */
+ write_pmcntenset_el0(
+ read_pmcntenset_el0() | PMCNTENSET_EL0_C_BIT);
+
+ /*
+ * Disabling the DP bit makes the cycle counter increment where
+ * prohibited by MDCR_EL3.SPME. If higher execution levels don't save
+ * and restore PMCR_EL0, then PMU information will be leaked.
+ */
+ write_pmcr_el0(read_pmcr_el0() & ~PMCR_EL0_DP_BIT);
+}
+
+static inline void pmu_enable_counting(void)
+{
+ /*
+ * Setting the E bit gives [fine-grained] control to the PMCNTENSET_EL0
+ * register, which controls which counters can increment.
+ */
+ write_pmcr_el0(read_pmcr_el0() | PMCR_EL0_E_BIT);
+}
+
+static unsigned long long profile_invalid_smc(u_register_t (*read_cntr_f)(void))
+{
+ unsigned long long evt_cnt;
+ smc_args args = { INVALID_FN };
+
+ evt_cnt = (*read_cntr_f)();
+ tftf_smc(&args);
+ evt_cnt = (*read_cntr_f)() - evt_cnt;
+
+ return evt_cnt;
+}
+
+static unsigned long long profile_cpu_suspend(u_register_t (*read_cntr_f)(void))
+{
+ unsigned long long evt_cnt;
+ unsigned int power_state;
+ unsigned int stateid;
+
+ tftf_psci_make_composite_state_id(MPIDR_AFFLVL0,
+ PSTATE_TYPE_STANDBY, &stateid);
+ power_state = tftf_make_psci_pstate(MPIDR_AFFLVL0,
+ PSTATE_TYPE_STANDBY, stateid);
+
+ tftf_irq_enable(IRQ_NS_SGI_0, GIC_HIGHEST_NS_PRIORITY);
+
+ /*
+ * Mask IRQ to prevent the interrupt handler being invoked
+ * and clearing the interrupt. A pending interrupt will cause this
+ * CPU to wake-up from suspend.
+ */
+ disable_irq();
+
+ /* Configure an SGI to wake-up from suspend */
+ tftf_send_sgi(IRQ_NS_SGI_0,
+ platform_get_core_pos(read_mpidr_el1() & MPID_MASK));
+
+ evt_cnt = (*read_cntr_f)();
+ tftf_cpu_suspend(power_state);
+ evt_cnt = (*read_cntr_f)() - evt_cnt;
+
+ /* Unmask the IRQ to let the interrupt handler to execute */
+ enable_irq();
+ isb();
+
+ tftf_irq_disable(IRQ_NS_SGI_0);
+
+ return evt_cnt;
+}
+
+static unsigned long long profile_fast_smc_add(u_register_t (*read_cntr_f)(void))
+{
+ unsigned long long evt_cnt;
+ smc_args args = { TSP_FAST_FID(TSP_ADD), 4, 6 };
+
+ evt_cnt = (*read_cntr_f)();
+ tftf_smc(&args);
+ evt_cnt = (*read_cntr_f)() - evt_cnt;
+
+ return evt_cnt;
+}
+
+static void measure_event(u_register_t (*read_cntr_func)(void),
+ unsigned long long (*profile_func)(u_register_t (*read_cntr_f)(void)),
+ struct pmu_event_info *info)
+{
+ unsigned long long evt_cnt;
+ unsigned long long min_cnt;
+ unsigned long long max_cnt;
+ unsigned long long avg_cnt;
+ unsigned long long cnt_sum = 0;
+
+ min_cnt = UINT64_MAX;
+ max_cnt = 0;
+
+ for (unsigned int i = 0; i < ITERATIONS_CNT; ++i) {
+ evt_cnt = (*profile_func)(read_cntr_func);
+
+ min_cnt = MIN(min_cnt, evt_cnt);
+ max_cnt = MAX(max_cnt, evt_cnt);
+
+ cnt_sum += evt_cnt;
+
+ tftf_irq_disable(IRQ_NS_SGI_0);
+ }
+
+ avg_cnt = cnt_sum / ITERATIONS_CNT;
+
+ info->avg = avg_cnt;
+ info->min = min_cnt;
+ info->max = max_cnt;
+
+ tftf_testcase_printf(
+ "Average count: %llu (ranging from %llu to %llu)\n",
+ avg_cnt,
+ min_cnt,
+ max_cnt);
+}
+
+/*
+ * Measure the number of retired writes to the PC in the PSCI_SUSPEND SMC.
+ * This test only succeeds if no useful information about the PMU counters has
+ * been leaked.
+ */
+test_result_t smc_psci_suspend_pc_write_retired(void)
+{
+ struct pmu_event_info baseline, cpu_suspend;
+
+ SKIP_TEST_IF_ARCH_DEBUG_VERSION_LESS_THAN(
+ ID_AA64DFR0_V8_2_DEBUG_ARCH_SUPPORTED);
+
+ configure_pmu_cntr0(PMU_EV_PC_WRITE_RETIRED);
+ pmu_enable_counting();
+
+ tftf_testcase_printf("Getting baseline event count:\n");
+ measure_event(read_pmevcntr0_el0, profile_invalid_smc, &baseline);
+ tftf_testcase_printf("Profiling PSCI_SUSPEND_PC:\n");
+ measure_event(read_pmevcntr0_el0, profile_cpu_suspend, &cpu_suspend);
+
+ if (cpu_suspend.avg - baseline.avg > baseline.avg / ALLOWED_DEVIATION)
+ return TEST_RESULT_FAIL;
+ return TEST_RESULT_SUCCESS;
+}
+
+/*
+ * Measure the CPU cycles count of the PSCI_SUSPEND SMC.
+ * This test only succeeds if no useful information about the PMU counters has
+ * been leaked.
+ */
+test_result_t smc_psci_suspend_cycles(void)
+{
+ struct pmu_event_info baseline, cpu_suspend;
+
+ SKIP_TEST_IF_ARCH_DEBUG_VERSION_LESS_THAN(
+ ID_AA64DFR0_V8_2_DEBUG_ARCH_SUPPORTED);
+
+ configure_pmu_cycle_cntr();
+ pmu_enable_counting();
+
+ tftf_testcase_printf("Getting baseline event count:\n");
+ measure_event(read_pmccntr_el0, profile_invalid_smc, &baseline);
+ tftf_testcase_printf("Profiling PSCI_SUSPEND_PC:\n");
+ measure_event(read_pmccntr_el0, profile_cpu_suspend, &cpu_suspend);
+
+ if (cpu_suspend.avg - baseline.avg > baseline.avg / ALLOWED_DEVIATION)
+ return TEST_RESULT_FAIL;
+ return TEST_RESULT_SUCCESS;
+}
+
+/*
+ * Measure the number of retired writes to the PC in the fast add SMC.
+ * This test only succeeds if no useful information about the PMU counters has
+ * been leaked.
+ */
+test_result_t fast_smc_add_pc_write_retired(void)
+{
+ struct pmu_event_info baseline, fast_smc_add;
+
+ SKIP_TEST_IF_ARCH_DEBUG_VERSION_LESS_THAN(
+ ID_AA64DFR0_V8_2_DEBUG_ARCH_SUPPORTED);
+
+ SKIP_TEST_IF_TSP_NOT_PRESENT();
+
+ configure_pmu_cntr0(PMU_EV_PC_WRITE_RETIRED);
+ pmu_enable_counting();
+
+ tftf_testcase_printf("Getting baseline event count:\n");
+ measure_event(read_pmevcntr0_el0, profile_invalid_smc, &baseline);
+ tftf_testcase_printf("Profiling Fast Add SMC:\n");
+ measure_event(read_pmevcntr0_el0, profile_fast_smc_add, &fast_smc_add);
+
+ if (fast_smc_add.avg - baseline.avg > baseline.avg / ALLOWED_DEVIATION)
+ return TEST_RESULT_FAIL;
+ return TEST_RESULT_SUCCESS;
+}
+
+/*
+ * Measure the CPU cycles count of the fast add SMC.
+ * This test only succeeds if no useful information about the PMU counters has
+ * been leaked.
+ */
+test_result_t fast_smc_add_cycles(void)
+{
+ struct pmu_event_info baseline, fast_smc_add;
+
+ SKIP_TEST_IF_ARCH_DEBUG_VERSION_LESS_THAN(
+ ID_AA64DFR0_V8_2_DEBUG_ARCH_SUPPORTED);
+
+ SKIP_TEST_IF_TSP_NOT_PRESENT();
+
+ configure_pmu_cycle_cntr();
+ pmu_enable_counting();
+
+ tftf_testcase_printf("Getting baseline event count:\n");
+ measure_event(read_pmccntr_el0, profile_invalid_smc, &baseline);
+ tftf_testcase_printf("Profiling Fast Add SMC:\n");
+ measure_event(read_pmccntr_el0, profile_fast_smc_add, &fast_smc_add);
+
+ if (fast_smc_add.avg - baseline.avg > baseline.avg / ALLOWED_DEVIATION)
+ return TEST_RESULT_FAIL;
+ return TEST_RESULT_SUCCESS;
+}
+#else
+test_result_t smc_psci_suspend_pc_write_retired(void)
+{
+ INFO("%s skipped on AArch32\n", __func__);
+ return TEST_RESULT_SKIPPED;
+}
+
+test_result_t smc_psci_suspend_cycles(void)
+{
+ INFO("%s skipped on AArch32\n", __func__);
+ return TEST_RESULT_SKIPPED;
+}
+
+test_result_t fast_smc_add_pc_write_retired(void)
+{
+ INFO("%s skipped on AArch32\n", __func__);
+ return TEST_RESULT_SKIPPED;
+}
+
+test_result_t fast_smc_add_cycles(void)
+{
+ INFO("%s skipped on AArch32\n", __func__);
+ return TEST_RESULT_SKIPPED;
+}
+#endif
diff --git a/tftf/tests/tests-extensive.xml b/tftf/tests/tests-extensive.xml
index 67d2346b..773c19e0 100644
--- a/tftf/tests/tests-extensive.xml
+++ b/tftf/tests/tests-extensive.xml
@@ -22,6 +22,7 @@
<!ENTITY tests-cpu-extensions SYSTEM "tests-cpu-extensions.xml">
<!ENTITY tests-performance SYSTEM "tests-performance.xml">
<!ENTITY tests-smc SYSTEM "tests-smc.xml">
+ <!ENTITY tests-pmu-leakage SYSTEM "tests-pmu-leakage.xml">
]>
<testsuites>
@@ -39,5 +40,6 @@
&tests-cpu-extensions;
&tests-performance;
&tests-smc;
+ &tests-pmu-leakage;
</testsuites>
diff --git a/tftf/tests/tests-pmu-leakage.mk b/tftf/tests/tests-pmu-leakage.mk
new file mode 100644
index 00000000..2d46073f
--- /dev/null
+++ b/tftf/tests/tests-pmu-leakage.mk
@@ -0,0 +1,9 @@
+#
+# Copyright (c) 2019, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+TESTS_SOURCES += $(addprefix tftf/tests/misc_tests/, \
+ test_pmu_leakage.c \
+ )
diff --git a/tftf/tests/tests-pmu-leakage.xml b/tftf/tests/tests-pmu-leakage.xml
new file mode 100644
index 00000000..932c21fc
--- /dev/null
+++ b/tftf/tests/tests-pmu-leakage.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ Copyright (c) 2018, Arm Limited. All rights reserved.
+
+ SPDX-License-Identifier: BSD-3-Clause
+-->
+
+<testsuites>
+ <testsuite name="PMU Leakage" description="Increment PMU counters in the secure world">
+ <testcase name="Leak PMU PC_WRITE_RETIRED counter values from EL3 on PSCI suspend SMC" function="smc_psci_suspend_pc_write_retired" />
+ <testcase name="Leak PMU CYCLE counter values from EL3 on PSCI suspend SMC" function="smc_psci_suspend_cycles" />
+ <testcase name="Leak PMU PC_WRITE_RETIRED counter values from S_EL1 on fast SMC add" function="fast_smc_add_pc_write_retired" />
+ <testcase name="Leak PMU CYCLE counter values from S_EL1 on fast SMC add" function="fast_smc_add_cycles" />
+ </testsuite>
+</testsuites>
diff --git a/tftf/tests/tests-standard.mk b/tftf/tests/tests-standard.mk
index f249a373..9ef75bb9 100644
--- a/tftf/tests/tests-standard.mk
+++ b/tftf/tests/tests-standard.mk
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2018, Arm Limited. All rights reserved.
+# Copyright (c) 2018-2019, Arm Limited. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
@@ -10,6 +10,7 @@ TESTS_MAKEFILE := $(addprefix tftf/tests/, \
tests-cpu-extensions.mk \
tests-el3-power-state.mk \
tests-performance.mk \
+ tests-pmu-leakage.mk \
tests-psci.mk \
tests-runtime-instrumentation.mk \
tests-sdei.mk \
diff --git a/tftf/tests/tests-standard.xml b/tftf/tests/tests-standard.xml
index a1323d55..fa576217 100644
--- a/tftf/tests/tests-standard.xml
+++ b/tftf/tests/tests-standard.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (c) 2018, Arm Limited. All rights reserved.
+ Copyright (c) 2018-2019, Arm Limited. All rights reserved.
SPDX-License-Identifier: BSD-3-Clause
-->
@@ -20,6 +20,7 @@
<!ENTITY tests-cpu-extensions SYSTEM "tests-cpu-extensions.xml">
<!ENTITY tests-performance SYSTEM "tests-performance.xml">
<!ENTITY tests-smc SYSTEM "tests-smc.xml">
+ <!ENTITY tests-pmu-leakage SYSTEM "tests-pmu-leakage.xml">
]>
<testsuites>
@@ -35,5 +36,6 @@
&tests-cpu-extensions;
&tests-performance;
&tests-smc;
+ &tests-pmu-leakage;
</testsuites>