diff options
Diffstat (limited to 'tftf')
-rw-r--r-- | tftf/framework/main.c | 20 | ||||
-rw-r--r-- | tftf/tests/extensions/pauth/test_pauth.c (renamed from tftf/tests/extensions/ptrauth/test_ptrauth.c) | 0 | ||||
-rw-r--r-- | tftf/tests/misc_tests/test_pmu_leakage.c | 378 | ||||
-rw-r--r-- | tftf/tests/tests-cpu-extensions.mk | 2 | ||||
-rw-r--r-- | tftf/tests/tests-extensive.xml | 2 | ||||
-rw-r--r-- | tftf/tests/tests-pmu-leakage.mk | 9 | ||||
-rw-r--r-- | tftf/tests/tests-pmu-leakage.xml | 16 | ||||
-rw-r--r-- | tftf/tests/tests-standard.mk | 3 | ||||
-rw-r--r-- | tftf/tests/tests-standard.xml | 4 |
9 files changed, 418 insertions, 16 deletions
diff --git a/tftf/framework/main.c b/tftf/framework/main.c index e84e45070..a2e84b7ea 100644 --- a/tftf/framework/main.c +++ b/tftf/framework/main.c @@ -12,6 +12,7 @@ #include <irq.h> #include <mmio.h> #include <nvm.h> +#include <pauth.h> #include <plat_topology.h> #include <platform.h> #include <platform_def.h> @@ -528,19 +529,12 @@ void __dead2 tftf_cold_boot_main(void) #if ENABLE_PAUTH assert(is_armv8_3_pauth_apa_api_present()); - uint64_t *apiakey = plat_init_apiakey(); - - write_apiakeylo_el1(apiakey[0]); - write_apiakeyhi_el1(apiakey[1]); - - if (IS_IN_EL2()) { - write_sctlr_el2(read_sctlr_el2() | SCTLR_EnIA_BIT); - } else { - assert(IS_IN_EL1()); - write_sctlr_el1(read_sctlr_el1() | SCTLR_EnIA_BIT); - } - - isb(); + /* + * Program APIAKey_EL1 key and enable ARMv8.3-PAuth here as this + * function doesn't return, and RETAA instuction won't be executed, + * what would cause translation fault otherwise. + */ + pauth_init_enable(); #endif /* ENABLE_PAUTH */ tftf_platform_setup(); diff --git a/tftf/tests/extensions/ptrauth/test_ptrauth.c b/tftf/tests/extensions/pauth/test_pauth.c index 564c56d9f..564c56d9f 100644 --- a/tftf/tests/extensions/ptrauth/test_ptrauth.c +++ b/tftf/tests/extensions/pauth/test_pauth.c 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 000000000..a0ac82d7e --- /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-cpu-extensions.mk b/tftf/tests/tests-cpu-extensions.mk index daf9528ab..cb4c1f961 100644 --- a/tftf/tests/tests-cpu-extensions.mk +++ b/tftf/tests/tests-cpu-extensions.mk @@ -10,5 +10,5 @@ TESTS_SOURCES += $(addprefix tftf/tests/, \ extensions/sve/test_sve.c \ runtime_services/arm_arch_svc/smccc_arch_workaround_1.c \ runtime_services/arm_arch_svc/smccc_arch_workaround_2.c \ - extensions/ptrauth/test_ptrauth.c \ + extensions/pauth/test_pauth.c \ ) diff --git a/tftf/tests/tests-extensive.xml b/tftf/tests/tests-extensive.xml index 67d2346be..773c19e0f 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 000000000..2d46073fc --- /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 000000000..932c21fc4 --- /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 f249a373c..9ef75bb94 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 a1323d555..fa5762173 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> |