summaryrefslogtreecommitdiff
path: root/tftf/tests/runtime_services/arm_arch_svc/smccc_arch_workaround_1.c
blob: 90a43f33b1f55434b8bc594f7bc30bf1d78d4ed1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/*
 * Copyright (c) 2018, Arm Limited. All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <arch_helpers.h>
#include <arm_arch_svc.h>
#include <debug.h>
#include <plat_topology.h>
#include <power_management.h>
#include <psci.h>
#include <smccc.h>
#include <string.h>
#include <tftf_lib.h>

#ifdef AARCH64
#define CORTEX_A57_MIDR	0x410FD070
#define CORTEX_A72_MIDR	0x410FD080
#define CORTEX_A73_MIDR 0x410FD090
#define CORTEX_A75_MIDR 0x410FD0A0

static int cortex_a57_test(void);
static int csv2_test(void);

static struct ent {
	unsigned int midr;
	int (*wa_required)(void);
} entries[] = {
	{ .midr = CORTEX_A57_MIDR, .wa_required = cortex_a57_test },
	{ .midr = CORTEX_A72_MIDR, .wa_required = csv2_test },
	{ .midr = CORTEX_A73_MIDR, .wa_required = csv2_test },
	{ .midr = CORTEX_A75_MIDR, .wa_required = csv2_test },
};

static int cortex_a57_test(void)
{
	return 1;
}

static int csv2_test(void)
{
	uint64_t pfr0;

	pfr0 = read_id_aa64pfr0_el1() >> ID_AA64PFR0_CSV2_SHIFT;
	if ((pfr0 & ID_AA64PFR0_CSV2_MASK) == 1)
		return 0;
	return 1;
}

static test_result_t test_smccc_entrypoint(void)
{
	smc_args args;
	smc_ret_values ret;
	int32_t expected_ver;
	unsigned int my_midr, midr_mask;
	int wa_required;
	size_t i;

	/* 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) {
		tftf_testcase_printf("Unexpected SMCCC version: 0x%x\n",
		       (int)ret.ret0);
		return TEST_RESULT_SKIPPED;
	}

	/* Check if SMCCC_ARCH_WORKAROUND_1 is required or not */
	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) {
		tftf_testcase_printf("SMCCC_ARCH_WORKAROUND_1 is not implemented\n");
		return TEST_RESULT_SKIPPED;
	}

	/* If the call returns 0, it means the workaround is required */
	if ((int)ret.ret0 == 0)
		wa_required = 1;
	else
		wa_required = 0;

	/* Check if the SMC return value matches our expectations */
	my_midr = (unsigned int)read_midr_el1();
	midr_mask = (MIDR_IMPL_MASK << MIDR_IMPL_SHIFT) |
		(MIDR_PN_MASK << MIDR_PN_SHIFT);
	for (i = 0; i < ARRAY_SIZE(entries); i++) {
		struct ent *entp = &entries[i];

		if ((my_midr & midr_mask) == (entp->midr & midr_mask)) {
			if (entp->wa_required() != wa_required)
				return TEST_RESULT_FAIL;
			break;
		}
	}
	if (i == ARRAY_SIZE(entries) && wa_required) {
		tftf_testcase_printf("TFTF workaround table out of sync with TF\n");
		return TEST_RESULT_FAIL;
	}

	/* Invoke the workaround to make sure nothing nasty happens */
	memset(&args, 0, sizeof(args));
	args.fid = SMCCC_ARCH_WORKAROUND_1;
	tftf_smc(&args);
	return TEST_RESULT_SUCCESS;
}

test_result_t test_smccc_arch_workaround_1(void)
{
	u_register_t lead_mpid, target_mpid;
	int cpu_node, ret;

	lead_mpid = read_mpidr_el1() & MPID_MASK;

	/* Power on all the non-lead cores. */
	for_each_cpu(cpu_node) {
		target_mpid = tftf_get_mpidr_from_node(cpu_node);
		if (lead_mpid == target_mpid)
			continue;
		ret = tftf_cpu_on(target_mpid,
		    (uintptr_t)test_smccc_entrypoint, 0);
		if (ret != PSCI_E_SUCCESS) {
			ERROR("CPU ON failed for 0x%llx\n",
			    (unsigned long long)target_mpid);
			return TEST_RESULT_FAIL;
		}
		/*
		 * Wait for test_smccc_entrypoint to return
		 * and the CPU to power down
		 */
		while (tftf_psci_affinity_info(target_mpid, MPIDR_AFFLVL0) !=
			PSCI_STATE_OFF)
			continue;
	}

	return test_smccc_entrypoint();
}
#else
test_result_t test_smccc_arch_workaround_1(void)
{
	INFO("%s skipped on AArch32\n", __func__);
	return TEST_RESULT_SKIPPED;
}
#endif