aboutsummaryrefslogtreecommitdiff
path: root/tftf/tests/runtime_services/secure_service/test_ffa_direct_messaging.c
blob: 0a722e4978f158658e85ebbca7c7b9043c05934e (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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
/*
 * Copyright (c) 2018-2021, Arm Limited. All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <debug.h>
#include <smccc.h>

#include <arch_helpers.h>
#include <cactus_test_cmds.h>
#include <ffa_endpoints.h>
#include <ffa_svc.h>
#include <lib/events.h>
#include <lib/power_management.h>
#include <platform.h>
#include <test_helpers.h>

#define ECHO_VAL1 U(0xa0a0a0a0)
#define ECHO_VAL2 U(0xb0b0b0b0)
#define ECHO_VAL3 U(0xc0c0c0c0)

static const struct ffa_uuid expected_sp_uuids[] = {
		{PRIMARY_UUID}, {SECONDARY_UUID}, {TERTIARY_UUID}
	};


static event_t cpu_booted[PLATFORM_CORE_COUNT];

static test_result_t send_cactus_echo_cmd(ffa_vm_id_t sender,
					  ffa_vm_id_t dest,
					  uint64_t value)
{
	smc_ret_values ret;
	ret = cactus_echo_send_cmd(sender, dest, value);

	/*
	 * Return responses may be FFA_MSG_SEND_DIRECT_RESP or FFA_INTERRUPT,
	 * but only expect the former. Expect SMC32 convention from SP.
	 */
	if (!is_ffa_direct_response(ret)) {
		return TEST_RESULT_FAIL;
	}

	if (cactus_get_response(ret) != CACTUS_SUCCESS ||
	    cactus_echo_get_val(ret) != value) {
		ERROR("Echo Failed!\n");
		return TEST_RESULT_FAIL;
	}

	return TEST_RESULT_SUCCESS;
}

test_result_t test_ffa_direct_messaging(void)
{
	test_result_t result;

	/**********************************************************************
	 * Check SPMC has ffa_version and expected FFA endpoints are deployed.
	 **********************************************************************/
	CHECK_SPMC_TESTING_SETUP(1, 0, expected_sp_uuids);

	/**********************************************************************
	 * Send a message to SP1 through direct messaging
	 **********************************************************************/
	result = send_cactus_echo_cmd(HYP_ID, SP_ID(1), ECHO_VAL1);
	if (result != TEST_RESULT_SUCCESS) {
		return result;
	}

	/**********************************************************************
	 * Send a message to SP2 through direct messaging
	 **********************************************************************/
	result = send_cactus_echo_cmd(HYP_ID, SP_ID(2), ECHO_VAL2);
	if (result != TEST_RESULT_SUCCESS) {
		return result;
	}

	/**********************************************************************
	 * Send a message to SP1 through direct messaging
	 **********************************************************************/
	result = send_cactus_echo_cmd(HYP_ID, SP_ID(1), ECHO_VAL3);

	return result;
}

/**
 * The 'send_cactus_req_echo_cmd' sends a CACTUS_REQ_ECHO_CMD to a cactus SP.
 * Handling this command, cactus should then send CACTUS_ECHO_CMD to
 * the specified SP according to 'echo_dest'. If the CACTUS_ECHO_CMD is resolved
 * successfully, cactus will reply to tftf with CACTUS_SUCCESS, or CACTUS_ERROR
 * otherwise.
 * For the CACTUS_SUCCESS response, the test returns TEST_RESULT_SUCCESS.
 */
static test_result_t send_cactus_req_echo_cmd(ffa_vm_id_t sender,
					      ffa_vm_id_t dest,
					      ffa_vm_id_t echo_dest,
					      uint64_t value)
{
	smc_ret_values ret;

	ret = cactus_req_echo_send_cmd(sender, dest, echo_dest, value);

	if (!is_ffa_direct_response(ret)) {
		return TEST_RESULT_FAIL;
	}

	if (cactus_get_response(ret) == CACTUS_ERROR) {
		return TEST_RESULT_FAIL;
	}

	return TEST_RESULT_SUCCESS;
}

test_result_t test_ffa_sp_to_sp_direct_messaging(void)
{
	test_result_t result;

	CHECK_SPMC_TESTING_SETUP(1, 0, expected_sp_uuids);

	result = send_cactus_req_echo_cmd(HYP_ID, SP_ID(1), SP_ID(2),
					  ECHO_VAL1);
	if (result != TEST_RESULT_SUCCESS) {
		return result;
	}

	/*
	 * The following the tests are intended to test the handling of a
	 * direct message request with a VM's ID as a the sender.
	 */
	result = send_cactus_req_echo_cmd(HYP_ID + 1, SP_ID(2), SP_ID(3),
					  ECHO_VAL2);
	if (result != TEST_RESULT_SUCCESS) {
		return result;
	}

	result = send_cactus_req_echo_cmd(HYP_ID + 2, SP_ID(3), SP_ID(1),
					  ECHO_VAL3);

	return result;
}

test_result_t test_ffa_sp_to_sp_deadlock(void)
{
	smc_ret_values ret;

	/**********************************************************************
	 * Check SPMC has ffa_version and expected FFA endpoints are deployed.
	 **********************************************************************/
	CHECK_SPMC_TESTING_SETUP(1, 0, expected_sp_uuids);

	ret = cactus_req_deadlock_send_cmd(HYP_ID, SP_ID(1), SP_ID(2), SP_ID(3));

	if (is_ffa_direct_response(ret) == false) {
		return TEST_RESULT_FAIL;
	}

	if (cactus_get_response(ret) == CACTUS_ERROR) {
		return TEST_RESULT_FAIL;
	}

	return TEST_RESULT_SUCCESS;
}

/**
 * Handler that is passed during tftf_cpu_on to individual CPU cores.
 * Runs a specific core and send a direct message request.
 * Expects core_pos | SP_ID as a response.
 */
static test_result_t cpu_on_handler(void)
{
	unsigned int mpid = read_mpidr_el1() & MPID_MASK;
	unsigned int core_pos = platform_get_core_pos(mpid);
	test_result_t ret = TEST_RESULT_SUCCESS;
	smc_ret_values ffa_ret;

	/*
	 * Send a direct message request to SP1 (MP SP) from current physical
	 * CPU. Notice SP1 ECs are already woken as a result of the PSCI_CPU_ON
	 * invocation so they already reached the message loop.
	 * The SPMC uses the MP pinned context corresponding to the physical
	 * CPU emitting the request.
	 */
	ret = send_cactus_echo_cmd(HYP_ID, SP_ID(1), ECHO_VAL1);
	if (ret != TEST_RESULT_SUCCESS) {
		goto out;
	}

	/*
	 * Secure Partitions beyond the first SP only have their first
	 * EC (or vCPU0) woken up at boot time by the SPMC.
	 * Other ECs need one round of ffa_run to reach the message loop.
	 */
	ffa_ret = ffa_run(SP_ID(2), core_pos);
	if (ffa_func_id(ffa_ret) != FFA_MSG_WAIT) {
		ERROR("Failed to run SP%x on core %u\n", SP_ID(2),
				core_pos);
		ret = TEST_RESULT_FAIL;
		goto out;
	}

	/*
	 * Send a direct message request to SP2 (MP SP) from current physical
	 * CPU. The SPMC uses the MP pinned context corresponding to the
	 * physical CPU emitting the request.
	 */
	ret = send_cactus_echo_cmd(HYP_ID, SP_ID(2), ECHO_VAL2);
	if (ret != TEST_RESULT_SUCCESS) {
		goto out;
	}

	/*
	 * Send a direct message request to SP3 (UP SP) from current physical CPU.
	 * The SPMC uses the single vCPU migrated to the new physical core.
	 * The single SP vCPU may receive requests from multiple physical CPUs.
	 * Thus it is possible one message is being processed on one core while
	 * another (or multiple) cores attempt sending a new direct message
	 * request. In such case the cores attempting the new request receive
	 * a busy response from the SPMC. To handle this case a retry loop is
	 * implemented permitting some fairness.
	 */
	uint32_t trial_loop = 5U;
	while (trial_loop--) {
		ffa_ret = cactus_echo_send_cmd(HYP_ID, SP_ID(3), ECHO_VAL3);
		if ((ffa_func_id(ffa_ret) == FFA_ERROR) &&
		    (ffa_error_code(ffa_ret) == FFA_ERROR_BUSY)) {
			VERBOSE("%s(%u) trial %u\n", __func__, core_pos, trial_loop);
			waitms(1);
			continue;
		}

		if (is_ffa_direct_response(ffa_ret) == true) {
			if (cactus_get_response(ffa_ret) != CACTUS_SUCCESS ||
				cactus_echo_get_val(ffa_ret) != ECHO_VAL3) {
				ERROR("Echo Failed!\n");
				ret = TEST_RESULT_FAIL;
			}

			goto out;
		}
	}

	ret = TEST_RESULT_FAIL;

out:
	/* Tell the lead CPU that the calling CPU has completed the test */
	tftf_send_event(&cpu_booted[core_pos]);

	return ret;
}

/**
 * Test direct messaging in multicore setup. Runs SPs on all the cores and sends
 * direct messages to SPs.
 */
test_result_t test_ffa_secondary_core_direct_msg(void)
{
	unsigned int lead_mpid = read_mpidr_el1() & MPID_MASK;
	unsigned int core_pos, cpu_node, mpidr;
	int32_t ret;

	/**********************************************************************
	 * Check SPMC has ffa_version and expected FFA endpoints are deployed.
	 **********************************************************************/
	CHECK_SPMC_TESTING_SETUP(1, 0, expected_sp_uuids);

	for (unsigned int i = 0U; i < PLATFORM_CORE_COUNT; i++) {
		tftf_init_event(&cpu_booted[i]);
	}

	for_each_cpu(cpu_node) {
		mpidr = tftf_get_mpidr_from_node(cpu_node);
		if (mpidr == lead_mpid) {
			continue;
		}

		ret = tftf_cpu_on(mpidr, (uintptr_t)cpu_on_handler, 0U);
		if (ret != 0) {
			ERROR("tftf_cpu_on mpidr 0x%x returns %d\n", mpidr, ret);
		}
	}

	VERBOSE("Waiting secondary CPUs to turn off ...\n");

	for_each_cpu(cpu_node) {
		mpidr = tftf_get_mpidr_from_node(cpu_node);
		if (mpidr == lead_mpid) {
			continue;
		}

		core_pos = platform_get_core_pos(mpidr);
		tftf_wait_for_event(&cpu_booted[core_pos]);
	}

	VERBOSE("Done exiting.\n");

	/**********************************************************************
	 * All tests passed.
	 **********************************************************************/

	return TEST_RESULT_SUCCESS;
}