blob: 49733cfb6ba7b02917c246d12d8369908d74801f [file] [log] [blame]
/*
* Copyright (c) 2018, Arm Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
/*
* This file contains tests that measure the round trip latency of an SMC.
* The SMC calls used are simple ones (PSCI_VERSION and the Standard Service
* UID) that involve almost no handling on the EL3 firmware's side so that we
* come close to measuring the overhead of the SMC itself.
*/
#include <arch_helpers.h>
#include <arm_arch_svc.h>
#include <debug.h>
#include <psci.h>
#include <smccc.h>
#include <std_svc.h>
#include <string.h>
#include <tftf_lib.h>
#include <utils_def.h>
#define ITERATIONS_CNT 1000
static unsigned long long raw_results[ITERATIONS_CNT];
/* Latency information in nano-seconds */
struct latency_info {
unsigned long long min;
unsigned long long max;
unsigned long long avg;
};
static inline unsigned long long cycles_to_ns(unsigned long long cycles)
{
unsigned long long freq = read_cntfrq_el0();
return (cycles * 1000000000) / freq;
}
/*
* Send the given SMC 'ITERATIONS_CNT' times, measure the time it takes to
* return back from the SMC call each time, and gather some statistics across
* the whole series.
*
* The statistics consist of:
* - minimum latency (i.e the shortest duration across the whole series);
* - maximum latency (i.e the longest duration across the whole series);
* - average latency.
*
* These statistics are stored in the latency_info structure whose address
* is passed as an argument.
*
* This function also prints some additional, intermediate information, like the
* number of cycles for each SMC and the average number of cycles for an SMC
* round trip.
*/
static void test_measure_smc_latency(const smc_args *smc_args,
struct latency_info *latency)
{
unsigned long long cycles;
unsigned long long min_cycles;
unsigned long long max_cycles;
unsigned long long avg_cycles;
unsigned long long cycles_sum = 0;
min_cycles = UINT64_MAX;
max_cycles = 0;
memset(raw_results, 0, sizeof(raw_results));
for (unsigned int i = 0; i < ITERATIONS_CNT; ++i) {
cycles = read_cntpct_el0();
tftf_smc(smc_args);
cycles = read_cntpct_el0() - cycles;
min_cycles = MIN(min_cycles, cycles);
max_cycles = MAX(max_cycles, cycles);
cycles_sum += cycles;
raw_results[i] = cycles;
}
avg_cycles = cycles_sum / ITERATIONS_CNT;
tftf_testcase_printf("Average number of cycles: %llu\n",
(unsigned long long) avg_cycles);
latency->min = cycles_to_ns(min_cycles);
latency->max = cycles_to_ns(max_cycles);
latency->avg = cycles_to_ns(avg_cycles);
NOTICE("Raw results:\n");
for (unsigned int i = 0; i < ITERATIONS_CNT; ++i) {
NOTICE("%llu cycles\t%llu ns\n",
raw_results[i], cycles_to_ns(raw_results[i]));
}
}
/*
* Measure the latency of the PSCI_VERSION SMC and print the result.
* This test always succeed.
*/
test_result_t smc_psci_version_latency(void)
{
struct latency_info latency;
smc_args args = { SMC_PSCI_VERSION };
test_measure_smc_latency(&args, &latency);
tftf_testcase_printf(
"Average time: %llu ns (ranging from %llu to %llu)\n",
(unsigned long long) latency.avg,
(unsigned long long) latency.min,
(unsigned long long) latency.max);
return TEST_RESULT_SUCCESS;
}
/*
* Measure the latency of the Standard Service Call UID SMC and print the
* result.
* This test always succeed.
*/
test_result_t smc_std_svc_call_uid_latency(void)
{
struct latency_info latency;
smc_args args = { SMC_STD_SVC_UID };
test_measure_smc_latency(&args, &latency);
tftf_testcase_printf(
"Average time: %llu ns (ranging from %llu to %llu)\n",
(unsigned long long) latency.avg,
(unsigned long long) latency.min,
(unsigned long long) latency.max);
return TEST_RESULT_SUCCESS;
}
test_result_t smc_arch_workaround_1(void)
{
struct latency_info latency;
smc_args args;
smc_ret_values ret;
int32_t expected_ver;
/* Check if SMCCC version is at least v1.1 */
expected_ver = MAKE_SMCCC_VERSION(1, 1);
memset(&args, 0, sizeof(args));
args.fid = SMCCC_VERSION;
ret = tftf_smc(&args);
if ((int32_t)ret.ret0 < expected_ver) {
printf("Unexpected SMCCC version: 0x%x\n",
(int)ret.ret0);
return TEST_RESULT_SKIPPED;
}
/* Check if SMCCC_ARCH_WORKAROUND_1 is implemented */
memset(&args, 0, sizeof(args));
args.fid = SMCCC_ARCH_FEATURES;
args.arg1 = SMCCC_ARCH_WORKAROUND_1;
ret = tftf_smc(&args);
if ((int)ret.ret0 == -1) {
printf("SMCCC_ARCH_WORKAROUND_1 is not implemented\n");
return TEST_RESULT_SKIPPED;
}
memset(&args, 0, sizeof(args));
args.fid = SMCCC_ARCH_WORKAROUND_1;
test_measure_smc_latency(&args, &latency);
tftf_testcase_printf(
"Average time: %llu ns (ranging from %llu to %llu)\n",
(unsigned long long) latency.avg,
(unsigned long long) latency.min,
(unsigned long long) latency.max);
return TEST_RESULT_SUCCESS;
}