aboutsummaryrefslogtreecommitdiff
path: root/tftf/tests/runtime_services/standard_service/psci/api_tests/affinity_info/test_psci_affinity_info.c
blob: 708ea132f6e134e3dc6872a4c39cef9c8dc39007 (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
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
/*
 * Copyright (c) 2018-2020, Arm Limited. All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <arch.h>
#include <arch_helpers.h>
#include <assert.h>
#include <drivers/arm/arm_gic.h>
#include <events.h>
#include <irq.h>
#include <plat_topology.h>
#include <platform.h>
#include <power_management.h>
#include <psci.h>
#include <sgi.h>
#include <test_helpers.h>
#include <tftf_lib.h>

/* Special value used to terminate the array of expected return values */
#define END_OF_EXPECTED_VALUE	0xDEADBEEF

/* TODO: Remove assumption that affinity levels always map to power levels. */
#define MPIDR_CLUSTER_ID(mpid)	MPIDR_AFF_ID(mpid, 1)
#define MPIDR_CPU_ID(mpid)	MPIDR_AFF_ID(mpid, 0)

/*
 * Event used by test test_affinity_info_level0_powerdown() to synchronise
 * CPUs
 */
static event_t cpu_about_to_suspend;
static unsigned int psci_version;

/*
 * `expected_values` should contain an array of expected return values
 * terminated by `END_OF_EXPECTED_VALUE`. If 'actual_value' exists in
 * one of 'expected_values' then return a test success.
 * Otherwise, print an error message in the test report and report a test
 * failure.
 */
static test_result_t get_test_result(const int *expected_values, int actual_value)
{
	const int *expected_val_list;

	expected_val_list = expected_values;
	while (*expected_val_list != END_OF_EXPECTED_VALUE) {
		if (*expected_val_list == actual_value)
			return TEST_RESULT_SUCCESS;
		expected_val_list++;
	}

	expected_val_list = expected_values;
	tftf_testcase_printf("Unexpected return value: %i Expected values are:",
						actual_value);
	while (*expected_val_list != END_OF_EXPECTED_VALUE) {
		tftf_testcase_printf("%i ", *expected_val_list);
		expected_val_list++;
	}
	tftf_testcase_printf("\n");

	return TEST_RESULT_FAIL;
}

/*
 * @Test_Aim@ Test PSCI AFFINITY_INFO targeted at affinity level 0 on online CPU
 *
 * Call PSCI AFFINITY_INFO targeted at affinity level 0 on lead CPU.
 * Expect the PSCI implementation to report that the affinity instance is on.
 */
test_result_t test_affinity_info_level0_on(void)
{
	unsigned int mpid = read_mpidr_el1() & MPID_MASK;
	int32_t aff_info;
	int expected_values[] = {PSCI_STATE_ON, END_OF_EXPECTED_VALUE};

	aff_info = tftf_psci_affinity_info(mpid, MPIDR_AFFLVL0);
	return get_test_result(expected_values, aff_info);
}

/*
 * @Test_Aim@ Test PSCI AFFINITY_INFO targeted at affinity level 0 on offline CPU
 *
 * Call PSCI AFFINITY_INFO targeted at affinity level 0 on all non-lead CPUs.
 * Expect the PSCI implementation to report that the affinity instances are off.
 *
 * This test needs 2 CPUs to run. It will be skipped on a single core platform.
 */
test_result_t test_affinity_info_level0_off(void)
{
	unsigned int lead_mpid = read_mpidr_el1() & MPID_MASK;
	unsigned int target_mpid, target_node;
	int32_t aff_info;
	test_result_t ret = TEST_RESULT_SUCCESS;
	int expected_values[] = {PSCI_STATE_OFF, END_OF_EXPECTED_VALUE};

	SKIP_TEST_IF_LESS_THAN_N_CPUS(2);

	for_each_cpu(target_node) {
		target_mpid = tftf_get_mpidr_from_node(target_node);
		/* Skip lead CPU, as it is powered on */
		if (target_mpid == lead_mpid)
			continue;

		aff_info = tftf_psci_affinity_info(target_mpid, MPIDR_AFFLVL0);
		if (get_test_result(expected_values, aff_info)
			== TEST_RESULT_FAIL) {
			ret = TEST_RESULT_FAIL;
		}
	}

	return ret;
}

/*
 * @Test_Aim@ Test PSCI AFFINITY_INFO targeted at affinity level 1 on online cluster
 *
 * Call PSCI AFFINITY_INFO targeted at affinity level 1 on the lead cluster
 * (i.e. the cluster to which the lead CPU belongs to).
 * PSCI implementation prior to PSCI 1.0 needs to report that the cluster is on
 * and others can also return INVALID_PARAMETERS.
 */
test_result_t test_affinity_info_level1_on(void)
{
	unsigned int lead_mpid = read_mpidr_el1() & MPID_MASK;
	unsigned int target_mpid;
	int32_t aff_info;
	int expected_values[3];

	/*
	 * Minimum version of PSCI is 0.2, uses this info to decide if
	 * tftf_get_psci_version() needs to be called or not.
	 */
	if (!psci_version)
		psci_version = tftf_get_psci_version();

	/*
	 * From PSCI version 1.0 onwards, Trusted Firmware-A may or may not
	 * track affinity levels greater than zero.
	 */
	if (!(psci_version & PSCI_MAJOR_VER_MASK)) {
		expected_values[0] = PSCI_STATE_ON;
		expected_values[1] = END_OF_EXPECTED_VALUE;
	} else {
		expected_values[0] = PSCI_STATE_ON;
		expected_values[1] = PSCI_E_INVALID_PARAMS;
		expected_values[2] = END_OF_EXPECTED_VALUE;
	}

	/*
	 * Build an MPID corresponding to the lead cluster. Set the affinity
	 * level0 bits to some arbitrary value that doesn't correspond to any
	 * CPU on the platform. The PSCI implementation should ignore the
	 * affinity 0 field.
	 */
	target_mpid = (lead_mpid & MPIDR_CLUSTER_MASK) | 0xE1;
	aff_info = tftf_psci_affinity_info(target_mpid, MPIDR_AFFLVL1);
	return get_test_result(expected_values, aff_info);
}

/*
 * @Test_Aim@ Test PSCI AFFINITY_INFO targeted at affinity level 1 on offline cluster
 *
 * Call PSCI AFFINITY_INFO targeted at affinity level 1 on a non-lead cluster
 * (i.e. another cluster than the one to which the lead CPU belongs to).
 * PSCI implementation prior to PSCI 1.0 needs to report that the cluster is OFF
 * and others can also return INVALID_PARAMETERS.
 *
 * This test needs 2 clusters to run. It will be skipped on a single cluster
 * platform.
 */
test_result_t test_affinity_info_level1_off(void)
{
	unsigned int lead_mpid = read_mpidr_el1() & MPID_MASK;
	unsigned int target_mpid;
	int32_t aff_info;
	unsigned int cluster_id;
	int expected_values[3];

	SKIP_TEST_IF_LESS_THAN_N_CLUSTERS(2);

	for (cluster_id = 0;
	     cluster_id < tftf_get_total_clusters_count();
	     ++cluster_id) {
		if (cluster_id != MPIDR_CLUSTER_ID(lead_mpid))
			break;
	}
	assert(cluster_id != tftf_get_total_clusters_count());


	/*
	 * Minimum version of PSCI is 0.2, uses this info to decide if
	 * tftf_get_psci_version() needs to be called or not.
	 */
	if (!psci_version)
		psci_version = tftf_get_psci_version();

	/*
	 * From PSCI version 1.0 onwards, Trusted Firmware-A may or may not
	 * track affinity levels greater than zero.
	 */
	if (!(psci_version & PSCI_MAJOR_VER_MASK)) {
		expected_values[0] = PSCI_STATE_OFF;
		expected_values[1] = END_OF_EXPECTED_VALUE;
	} else {
		expected_values[0] = PSCI_STATE_OFF;
		expected_values[1] = PSCI_E_INVALID_PARAMS;
		expected_values[2] = END_OF_EXPECTED_VALUE;
	}

	/*
	 * Build an MPID corresponding to a non-lead cluster. Set the affinity
	 * level0 bits to some arbitrary value that doesn't correspond to any
	 * CPU on the platform. The PSCI implementation should ignore the
	 * affinity 0 field.
	 */
#if FVP_MAX_PE_PER_CPU > 1
	target_mpid = make_mpid(cluster_id, 0, 0xE1);
#else
	target_mpid = make_mpid(cluster_id, 0xE1);
#endif
	aff_info = tftf_psci_affinity_info(target_mpid, MPIDR_AFFLVL1);
	return get_test_result(expected_values, aff_info);
}

/*
 * @Test_Aim@ Test PSCI AFFINITY_INFO targeted at affinity level 2
 *
 * For PSCI implementations prior to PSCI 1.0 , the expected return value
 * depends on the the maximum affinity level that the power management
 * operations can apply to on this platform.
 *  - If the platform doesn't have an affinity level 2 then expect the PSCI
 *    implementation to report that it received invalid parameters.
 *  - If affinity level 2 exists then expect the PSCI implementation to report
 *    that the affinity instance is on.
 *
 * From PSCI 1.0 onwards, it can also return either INVALID_PARMETERS
 */
test_result_t test_affinity_info_level2(void)
{
	int expected_values[3];
	unsigned int target_mpid;
	int32_t aff_info;

	/*
	 * Minimum version of PSCI is 0.2, uses this info to decide if
	 * tftf_get_psci_version() needs to be called or not.
	 */
	if (!psci_version)
		psci_version = tftf_get_psci_version();

	expected_values[0] = (PLATFORM_MAX_AFFLVL >= 2)
		? PSCI_STATE_ON
		: PSCI_E_INVALID_PARAMS;

	/*
	 * From PSCI version 1.0 onwards, Trusted Firmware-A may or may not
	 * track affinity levels greater than zero.
	 */
	if (!(psci_version & PSCI_MAJOR_VER_MASK)) {
		expected_values[1] = END_OF_EXPECTED_VALUE;
	} else {
		expected_values[1] = PSCI_E_INVALID_PARAMS;
		expected_values[2] = END_OF_EXPECTED_VALUE;
	}

	/*
	 * Build an MPID corresponding to the lead affinity instance at level 2.
	 * Set the affinity level0 & level1 bits to some arbitrary values that
	 * don't correspond to any affinity instance on the platform. The PSCI
	 * implementation should ignore the affinity 0 & 1 fields.
	 */
	target_mpid = read_mpidr_el1() & (MPIDR_AFFLVL_MASK << MPIDR_AFF_SHIFT(2));
	target_mpid |= 0xAB << MPIDR_AFF1_SHIFT;
	target_mpid |= 0xE1 << MPIDR_AFF0_SHIFT;

	aff_info = tftf_psci_affinity_info(target_mpid, MPIDR_AFFLVL2);
	return get_test_result(expected_values, aff_info);
}

/*
 * @Test_Aim@ Test PSCI AFFINITY_INFO targeted at affinity level 3
 *
 * For PSCI implementations prior to PSCI 1.0 , the expected return value
 * depends on the maximum affinity level that the power management
 * operations can apply to on this platform.
 *  - If the platform doesn't have an affinity level 3 then expect the PSCI
 *    implementation to report that it received invalid parameters.
 *  - If affinity level 3 exists then expect the PSCI implementation to report
 *    that the affinity instance is on.
 *
 * From PSCI 1.0 onwards, it can also return INVALID_PARMETERS
 */
test_result_t test_affinity_info_level3(void)
{
#ifdef __aarch64__
	int expected_values[3];
	uint64_t target_mpid;
	int32_t aff_info;

	/*
	 * Minimum version of PSCI is 0.2, uses this info to decide if
	 * tftf_get_psci_version() needs to be called or not.
	 */
	if (!psci_version)
		psci_version = tftf_get_psci_version();

	expected_values[0] = (PLATFORM_MAX_AFFLVL == 3)
		? PSCI_STATE_ON
		: PSCI_E_INVALID_PARAMS;

	/*
	 * From PSCI version 1.0 onwards, Trusted Firmware-A may or may not
	 * track affinity levels greater than zero.
	 */
	if (!(psci_version & PSCI_MAJOR_VER_MASK)) {
		expected_values[1] = END_OF_EXPECTED_VALUE;
	} else {
		expected_values[1] = PSCI_E_INVALID_PARAMS;
		expected_values[2] = END_OF_EXPECTED_VALUE;
	}

	/*
	 * Build an MPID corresponding to the lead affinity instance at level 3.
	 * Set the affinity level0/level1/level2 bits to some arbitrary values
	 * that don't correspond to any affinity instance on the platform. The
	 * PSCI implementation should ignore the affinity 0, 1 & 2 fields.
	 */
	target_mpid = read_mpidr_el1();
	target_mpid &= ((uint64_t) MPIDR_AFFLVL_MASK) << MPIDR_AFF_SHIFT(3);
	target_mpid |= 0xD2 << MPIDR_AFF2_SHIFT;
	target_mpid |= 0xAB << MPIDR_AFF1_SHIFT;
	target_mpid |= 0xE1 << MPIDR_AFF0_SHIFT;

	aff_info = tftf_psci_affinity_info(target_mpid, MPIDR_AFFLVL3);
	return get_test_result(expected_values, aff_info);
#else
	return TEST_RESULT_SKIPPED;
#endif
}

/*
 * Suspend to powerdown the calling CPU.
 *
 * 1) Enable SGI #0. This SGI will be sent by the lead CPU to wake this CPU.
 * 2) Suspend the CPU.
 * 3) Report success/failure of the suspend operation.
 */
static test_result_t suspend_to_powerdown(void)
{
	uint32_t power_state, stateid;
	int psci_ret, expected_return_val;

	/*
	 * Enable reception of SGI 0 on the calling CPU.
	 * SGI 0 will serve as the wake-up event to come out of suspend.
	 */
	tftf_irq_enable(IRQ_NS_SGI_0, GIC_HIGHEST_NS_PRIORITY);

	expected_return_val = tftf_psci_make_composite_state_id(
			PSTATE_AFF_LVL_0, PSTATE_TYPE_POWERDOWN, &stateid);

	/* Need at least 1 power down state defined at level 0 */
	if (expected_return_val != PSCI_E_SUCCESS)
		return TEST_RESULT_SKIPPED;

	/*
	 * Suspend the calling CPU to the desired affinity level and power state
	 */
	power_state = tftf_make_psci_pstate(PSTATE_AFF_LVL_0,
					  PSTATE_TYPE_POWERDOWN,
					  stateid);

	/*
	 * Notify the lead CPU that the calling CPU is about to suspend itself
	 */
	tftf_send_event(&cpu_about_to_suspend);

	psci_ret = tftf_cpu_suspend(power_state);

	tftf_irq_disable(IRQ_NS_SGI_0);

	if (psci_ret != PSCI_E_SUCCESS) {
		tftf_testcase_printf("Failed to suspend (%i)\n", psci_ret);
		return TEST_RESULT_FAIL;
	}

	return TEST_RESULT_SUCCESS;
}

/*
 * @Test_Aim@ Test PSCI AFFINITY_INFO targeted at affinity level 0 on a
 *            suspended CPU
 *
 * A CPU that has been physically powered down as a result of a call to
 * CPU_SUSPEND must be reported as ON by the AFFINITY_INFO call. This test
 * aims at verifying this behaviour.
 *
 * This test needs 2 CPUs to run. It will be skipped on a single core platform.
 * It will also be skipped if an error is encountered during the bring-up of the
 * non-lead CPU.
 */
test_result_t test_affinity_info_level0_powerdown(void)
{
	unsigned int lead_mpid = read_mpidr_el1() & MPID_MASK;
	unsigned int target_mpid, target_core_pos;
	int psci_ret;
	int32_t aff_info;
	test_result_t ret;
	int expected_values[] = {PSCI_STATE_ON, END_OF_EXPECTED_VALUE};

	SKIP_TEST_IF_LESS_THAN_N_CPUS(2);

	/*
	 * Preparation step:
	 * Find another CPU than the lead CPU and power it on.
	 */
	target_mpid = tftf_find_any_cpu_other_than(lead_mpid);
	assert(target_mpid != INVALID_MPID);
	target_core_pos = platform_get_core_pos(target_mpid);

	psci_ret = tftf_cpu_on(target_mpid, (uintptr_t) suspend_to_powerdown, 0);
	if (psci_ret != PSCI_E_SUCCESS) {
		tftf_testcase_printf("Failed to power on CPU 0x%x (%d)\n",
				     target_mpid, psci_ret);
		return TEST_RESULT_SKIPPED;
	}

	/* Wait for the other CPU to initiate the suspend operation */
	tftf_wait_for_event(&cpu_about_to_suspend);

	/* Wait a bit for the CPU to really enter suspend state */
	waitms(PLAT_SUSPEND_ENTRY_TIME);

	/* Request status of the non-lead CPU while it is suspended */
	aff_info = tftf_psci_affinity_info(target_mpid, MPIDR_AFFLVL0);
	ret = get_test_result(expected_values, aff_info);

	/* Wake up non-lead CPU */
	tftf_send_sgi(IRQ_NS_SGI_0, target_core_pos);

	return ret;
}