xref: /kvm-unit-tests/arm/psci.c (revision 7e9737739f738c9a2e555947082a59edbc5b49b9)
1bec58c1cSLevente Kurusa /*
2bec58c1cSLevente Kurusa  * PSCI tests
3bec58c1cSLevente Kurusa  *
4bec58c1cSLevente Kurusa  * Copyright (C) 2017, Red Hat, Inc.
5bec58c1cSLevente Kurusa  * Author: Levente Kurusa <lkurusa@redhat.com>
6bec58c1cSLevente Kurusa  * Author: Andrew Jones <drjones@redhat.com>
7bec58c1cSLevente Kurusa  *
8bec58c1cSLevente Kurusa  * This work is licensed under the terms of the GNU LGPL, version 2.
9bec58c1cSLevente Kurusa  */
10f082e37fSAndrew Jones #include <errata.h>
116afb9481SAlexandru Elisei #include <libcflat.h>
126afb9481SAlexandru Elisei 
136afb9481SAlexandru Elisei #include <asm/delay.h>
146afb9481SAlexandru Elisei #include <asm/mmu.h>
15bbf5c84bSAndrew Jones #include <asm/processor.h>
16bec58c1cSLevente Kurusa #include <asm/psci.h>
176afb9481SAlexandru Elisei #include <asm/smp.h>
18bec58c1cSLevente Kurusa 
19bbf5c84bSAndrew Jones static bool invalid_function_exception;
20bbf5c84bSAndrew Jones 
21bbf5c84bSAndrew Jones #ifdef __arm__
invalid_function_handler(struct pt_regs * regs __unused)22bbf5c84bSAndrew Jones static void invalid_function_handler(struct pt_regs *regs __unused)
23bbf5c84bSAndrew Jones {
24bbf5c84bSAndrew Jones 	invalid_function_exception = true;
25bbf5c84bSAndrew Jones }
26bbf5c84bSAndrew Jones #else
invalid_function_handler(struct pt_regs * regs,unsigned int esr __unused)27bbf5c84bSAndrew Jones static void invalid_function_handler(struct pt_regs *regs, unsigned int esr __unused)
28bbf5c84bSAndrew Jones {
29bbf5c84bSAndrew Jones 	invalid_function_exception = true;
30bbf5c84bSAndrew Jones 	regs->pc += 4;
31bbf5c84bSAndrew Jones }
32bbf5c84bSAndrew Jones #endif
33bbf5c84bSAndrew Jones 
install_invalid_function_handler(exception_fn handler)34bbf5c84bSAndrew Jones static void install_invalid_function_handler(exception_fn handler)
35bbf5c84bSAndrew Jones {
36bbf5c84bSAndrew Jones #ifdef __arm__
37bbf5c84bSAndrew Jones 	install_exception_handler(EXCPTN_UND, handler);
38bbf5c84bSAndrew Jones #else
39bbf5c84bSAndrew Jones 	install_exception_handler(EL1H_SYNC, ESR_EL1_EC_UNKNOWN, handler);
40bbf5c84bSAndrew Jones #endif
41bbf5c84bSAndrew Jones }
42bbf5c84bSAndrew Jones 
psci_invalid_function(void)43bec58c1cSLevente Kurusa static bool psci_invalid_function(void)
44bec58c1cSLevente Kurusa {
45bbf5c84bSAndrew Jones 	bool pass;
46bbf5c84bSAndrew Jones 
47bbf5c84bSAndrew Jones 	install_invalid_function_handler(invalid_function_handler);
48bbf5c84bSAndrew Jones 
49bbf5c84bSAndrew Jones 	pass = psci_invoke(1337, 0, 0, 0) == PSCI_RET_NOT_SUPPORTED || invalid_function_exception;
50bbf5c84bSAndrew Jones 
51bbf5c84bSAndrew Jones 	install_invalid_function_handler(NULL);
52bbf5c84bSAndrew Jones 	return pass;
53bec58c1cSLevente Kurusa }
54bec58c1cSLevente Kurusa 
psci_affinity_info(unsigned long target_affinity,uint32_t lowest_affinity_level)55bec58c1cSLevente Kurusa static int psci_affinity_info(unsigned long target_affinity, uint32_t lowest_affinity_level)
56bec58c1cSLevente Kurusa {
57bec58c1cSLevente Kurusa #ifdef __arm__
58bec58c1cSLevente Kurusa 	return psci_invoke(PSCI_0_2_FN_AFFINITY_INFO, target_affinity, lowest_affinity_level, 0);
59bec58c1cSLevente Kurusa #else
60bec58c1cSLevente Kurusa 	return psci_invoke(PSCI_0_2_FN64_AFFINITY_INFO, target_affinity, lowest_affinity_level, 0);
61bec58c1cSLevente Kurusa #endif
62bec58c1cSLevente Kurusa }
63bec58c1cSLevente Kurusa 
psci_affinity_info_on(void)64bec58c1cSLevente Kurusa static bool psci_affinity_info_on(void)
65bec58c1cSLevente Kurusa {
66bec58c1cSLevente Kurusa 	return psci_affinity_info(cpus[0], 0) == PSCI_0_2_AFFINITY_LEVEL_ON;
67bec58c1cSLevente Kurusa }
68bec58c1cSLevente Kurusa 
psci_affinity_info_off(void)69bec58c1cSLevente Kurusa static bool psci_affinity_info_off(void)
70bec58c1cSLevente Kurusa {
71bec58c1cSLevente Kurusa 	return psci_affinity_info(cpus[1], 0) == PSCI_0_2_AFFINITY_LEVEL_OFF;
72bec58c1cSLevente Kurusa }
73bec58c1cSLevente Kurusa 
74bec58c1cSLevente Kurusa static int cpu_on_ret[NR_CPUS];
75*a33d0007SNikita Venkatesh static cpumask_t cpu_on_ready, cpu_on_done, cpu_off_done;
76bec58c1cSLevente Kurusa static volatile int cpu_on_start;
77*a33d0007SNikita Venkatesh static volatile int cpu_off_start;
78bec58c1cSLevente Kurusa 
796afb9481SAlexandru Elisei extern void secondary_entry(void);
cpu_on_do_wake_target(void)806afb9481SAlexandru Elisei static void cpu_on_do_wake_target(void)
81bec58c1cSLevente Kurusa {
82bec58c1cSLevente Kurusa 	int cpu = smp_processor_id();
83bec58c1cSLevente Kurusa 
84bec58c1cSLevente Kurusa 	cpumask_set_cpu(cpu, &cpu_on_ready);
85bec58c1cSLevente Kurusa 	while (!cpu_on_start)
86bec58c1cSLevente Kurusa 		cpu_relax();
876afb9481SAlexandru Elisei 	cpu_on_ret[cpu] = psci_cpu_on(cpus[1], __pa(secondary_entry));
88bec58c1cSLevente Kurusa 	cpumask_set_cpu(cpu, &cpu_on_done);
89bec58c1cSLevente Kurusa }
90bec58c1cSLevente Kurusa 
cpu_on_target(void)916afb9481SAlexandru Elisei static void cpu_on_target(void)
926afb9481SAlexandru Elisei {
936afb9481SAlexandru Elisei 	int cpu = smp_processor_id();
946afb9481SAlexandru Elisei 
956afb9481SAlexandru Elisei 	cpumask_set_cpu(cpu, &cpu_on_done);
966afb9481SAlexandru Elisei }
976afb9481SAlexandru Elisei 
986afb9481SAlexandru Elisei extern struct secondary_data secondary_data;
996afb9481SAlexandru Elisei 
1006afb9481SAlexandru Elisei /* Open code the setup part from smp_boot_secondary(). */
psci_cpu_on_prepare_secondary(int cpu,secondary_entry_fn entry)1016afb9481SAlexandru Elisei static void psci_cpu_on_prepare_secondary(int cpu, secondary_entry_fn entry)
1026afb9481SAlexandru Elisei {
1036afb9481SAlexandru Elisei 	secondary_data.stack = thread_stack_alloc();
1046afb9481SAlexandru Elisei 	secondary_data.entry = entry;
1056afb9481SAlexandru Elisei 	mmu_mark_disabled(cpu);
1066afb9481SAlexandru Elisei }
1076afb9481SAlexandru Elisei 
psci_cpu_on_test(void)108bec58c1cSLevente Kurusa static bool psci_cpu_on_test(void)
109bec58c1cSLevente Kurusa {
110bec58c1cSLevente Kurusa 	bool failed = false;
111e14e6ba5SAlexandru Elisei 	int ret_success = 0;
1126afb9481SAlexandru Elisei 	int i, cpu;
113bec58c1cSLevente Kurusa 
114bec58c1cSLevente Kurusa 	for_each_present_cpu(cpu) {
115bec58c1cSLevente Kurusa 		if (cpu < 2)
116bec58c1cSLevente Kurusa 			continue;
1176afb9481SAlexandru Elisei 		smp_boot_secondary(cpu, cpu_on_do_wake_target);
118bec58c1cSLevente Kurusa 	}
119bec58c1cSLevente Kurusa 
120bec58c1cSLevente Kurusa 	cpumask_set_cpu(0, &cpu_on_ready);
1216afb9481SAlexandru Elisei 	cpumask_set_cpu(1, &cpu_on_ready);
122bec58c1cSLevente Kurusa 	while (!cpumask_full(&cpu_on_ready))
123bec58c1cSLevente Kurusa 		cpu_relax();
124bec58c1cSLevente Kurusa 
1256afb9481SAlexandru Elisei 	/*
1266afb9481SAlexandru Elisei 	 * Configure CPU 1 after all secondaries are online to avoid
1276afb9481SAlexandru Elisei 	 * secondary_data being overwritten.
1286afb9481SAlexandru Elisei 	 */
1296afb9481SAlexandru Elisei 	psci_cpu_on_prepare_secondary(1, cpu_on_target);
1306afb9481SAlexandru Elisei 
131bec58c1cSLevente Kurusa 	cpu_on_start = 1;
132bec58c1cSLevente Kurusa 	smp_mb();
133bec58c1cSLevente Kurusa 
1346afb9481SAlexandru Elisei 	cpu_on_ret[0] = psci_cpu_on(cpus[1], __pa(secondary_entry));
135bec58c1cSLevente Kurusa 	cpumask_set_cpu(0, &cpu_on_done);
136bec58c1cSLevente Kurusa 
1376afb9481SAlexandru Elisei 	report_info("waiting for CPU1 to come online...");
1386afb9481SAlexandru Elisei 	for (i = 0; i < 100; i++) {
1396afb9481SAlexandru Elisei 		mdelay(10);
1406afb9481SAlexandru Elisei 		if (cpumask_full(&cpu_on_done))
1416afb9481SAlexandru Elisei 			break;
1426afb9481SAlexandru Elisei 	}
143bec58c1cSLevente Kurusa 
1446afb9481SAlexandru Elisei 	if (!cpumask_full(&cpu_on_done)) {
145bec58c1cSLevente Kurusa 		for_each_present_cpu(cpu) {
1466afb9481SAlexandru Elisei 			if (!cpumask_test_cpu(cpu, &cpu_on_done)) {
1476afb9481SAlexandru Elisei 				if (cpu == 1)
1486afb9481SAlexandru Elisei 					report_info("CPU1 failed to come online");
1496afb9481SAlexandru Elisei 				else
1506afb9481SAlexandru Elisei 					report_info("CPU%d failed to online CPU1", cpu);
1516afb9481SAlexandru Elisei 			}
1526afb9481SAlexandru Elisei 		}
1536afb9481SAlexandru Elisei 		failed = true;
1546afb9481SAlexandru Elisei 	}
1556afb9481SAlexandru Elisei 
1566afb9481SAlexandru Elisei 	for_each_cpu(cpu, &cpu_on_done) {
157bec58c1cSLevente Kurusa 		if (cpu == 1)
158bec58c1cSLevente Kurusa 			continue;
159e14e6ba5SAlexandru Elisei 		if (cpu_on_ret[cpu] == PSCI_RET_SUCCESS) {
160e14e6ba5SAlexandru Elisei 			ret_success++;
161e14e6ba5SAlexandru Elisei 		} else if (cpu_on_ret[cpu] != PSCI_RET_ALREADY_ON) {
162bec58c1cSLevente Kurusa 			report_info("unexpected cpu_on return value: caller=CPU%d, ret=%d", cpu, cpu_on_ret[cpu]);
163bec58c1cSLevente Kurusa 			failed = true;
164bec58c1cSLevente Kurusa 		}
165bec58c1cSLevente Kurusa 	}
166bec58c1cSLevente Kurusa 
167e14e6ba5SAlexandru Elisei 	if (ret_success != 1) {
168e14e6ba5SAlexandru Elisei 		report_info("got %d CPU_ON success", ret_success);
169e14e6ba5SAlexandru Elisei 		failed = true;
170e14e6ba5SAlexandru Elisei 	}
171e14e6ba5SAlexandru Elisei 
172bec58c1cSLevente Kurusa 	return !failed;
173bec58c1cSLevente Kurusa }
174bec58c1cSLevente Kurusa 
cpu_off_secondary_entry(void * data)175*a33d0007SNikita Venkatesh static void cpu_off_secondary_entry(void *data)
176*a33d0007SNikita Venkatesh {
177*a33d0007SNikita Venkatesh 	int cpu = smp_processor_id();
178*a33d0007SNikita Venkatesh 
179*a33d0007SNikita Venkatesh 	while (!cpu_off_start)
180*a33d0007SNikita Venkatesh 		cpu_relax();
181*a33d0007SNikita Venkatesh 	cpumask_set_cpu(cpu, &cpu_off_done);
182*a33d0007SNikita Venkatesh 	cpu_psci_cpu_die();
183*a33d0007SNikita Venkatesh }
184*a33d0007SNikita Venkatesh 
psci_cpu_off_test(void)185*a33d0007SNikita Venkatesh static bool psci_cpu_off_test(void)
186*a33d0007SNikita Venkatesh {
187*a33d0007SNikita Venkatesh 	bool failed = false;
188*a33d0007SNikita Venkatesh 	int i, count, cpu;
189*a33d0007SNikita Venkatesh 
190*a33d0007SNikita Venkatesh 	for_each_present_cpu(cpu) {
191*a33d0007SNikita Venkatesh 		if (cpu == 0)
192*a33d0007SNikita Venkatesh 			continue;
193*a33d0007SNikita Venkatesh 		on_cpu_async(cpu, cpu_off_secondary_entry, NULL);
194*a33d0007SNikita Venkatesh 	}
195*a33d0007SNikita Venkatesh 
196*a33d0007SNikita Venkatesh 	cpumask_set_cpu(0, &cpu_off_done);
197*a33d0007SNikita Venkatesh 
198*a33d0007SNikita Venkatesh 	cpu_off_start = 1;
199*a33d0007SNikita Venkatesh 	report_info("waiting for the CPUs to be offlined...");
200*a33d0007SNikita Venkatesh 	while (!cpumask_full(&cpu_off_done))
201*a33d0007SNikita Venkatesh 		cpu_relax();
202*a33d0007SNikita Venkatesh 
203*a33d0007SNikita Venkatesh 	/* Allow all the other CPUs to complete the operation */
204*a33d0007SNikita Venkatesh 	for (i = 0; i < 100; i++) {
205*a33d0007SNikita Venkatesh 		mdelay(10);
206*a33d0007SNikita Venkatesh 
207*a33d0007SNikita Venkatesh 		count = 0;
208*a33d0007SNikita Venkatesh 		for_each_present_cpu(cpu) {
209*a33d0007SNikita Venkatesh 			if (cpu == 0)
210*a33d0007SNikita Venkatesh 				continue;
211*a33d0007SNikita Venkatesh 			if (psci_affinity_info(cpus[cpu], 0) != PSCI_0_2_AFFINITY_LEVEL_OFF)
212*a33d0007SNikita Venkatesh 				count++;
213*a33d0007SNikita Venkatesh 		}
214*a33d0007SNikita Venkatesh 		if (count == 0)
215*a33d0007SNikita Venkatesh 			break;
216*a33d0007SNikita Venkatesh 	}
217*a33d0007SNikita Venkatesh 
218*a33d0007SNikita Venkatesh 	/* Try to catch CPUs that return from CPU_OFF. */
219*a33d0007SNikita Venkatesh 	if (count == 0)
220*a33d0007SNikita Venkatesh 		mdelay(100);
221*a33d0007SNikita Venkatesh 
222*a33d0007SNikita Venkatesh 	for_each_present_cpu(cpu) {
223*a33d0007SNikita Venkatesh 		if (cpu == 0)
224*a33d0007SNikita Venkatesh 			continue;
225*a33d0007SNikita Venkatesh 		if (cpu_idle(cpu)) {
226*a33d0007SNikita Venkatesh 			report_info("CPU%d failed to be offlined", cpu);
227*a33d0007SNikita Venkatesh 			if (psci_affinity_info(cpus[cpu], 0) == PSCI_0_2_AFFINITY_LEVEL_OFF)
228*a33d0007SNikita Venkatesh 				report_info("AFFINITY_INFO incorrectly reports CPU%d as offline", cpu);
229*a33d0007SNikita Venkatesh 			failed = true;
230*a33d0007SNikita Venkatesh 		}
231*a33d0007SNikita Venkatesh 	}
232*a33d0007SNikita Venkatesh 
233*a33d0007SNikita Venkatesh 	return !failed;
234*a33d0007SNikita Venkatesh }
235*a33d0007SNikita Venkatesh 
main(void)236bec58c1cSLevente Kurusa int main(void)
237bec58c1cSLevente Kurusa {
238bec58c1cSLevente Kurusa 	int ver = psci_invoke(PSCI_0_2_FN_PSCI_VERSION, 0, 0, 0);
239bec58c1cSLevente Kurusa 
240da5b8576SAndre Przywara 	report_prefix_push("psci");
241da5b8576SAndre Przywara 
242bec58c1cSLevente Kurusa 	if (nr_cpus < 2) {
243bec58c1cSLevente Kurusa 		report_skip("At least 2 cpus required");
244bec58c1cSLevente Kurusa 		goto done;
245bec58c1cSLevente Kurusa 	}
246bec58c1cSLevente Kurusa 
247bec58c1cSLevente Kurusa 	report_info("PSCI version %d.%d", PSCI_VERSION_MAJOR(ver),
248bec58c1cSLevente Kurusa 					  PSCI_VERSION_MINOR(ver));
249a299895bSThomas Huth 	report(psci_invalid_function(), "invalid-function");
250a299895bSThomas Huth 	report(psci_affinity_info_on(), "affinity-info-on");
251a299895bSThomas Huth 	report(psci_affinity_info_off(), "affinity-info-off");
252f082e37fSAndrew Jones 
253f082e37fSAndrew Jones 	if (ERRATA(6c7a5dce22b3))
254a299895bSThomas Huth 		report(psci_cpu_on_test(), "cpu-on");
255f082e37fSAndrew Jones 	else
256f082e37fSAndrew Jones 		report_skip("Skipping unsafe cpu-on test. Set ERRATA_6c7a5dce22b3=y to enable.");
257bec58c1cSLevente Kurusa 
258*a33d0007SNikita Venkatesh 	assert(!cpu_idle(0));
259*a33d0007SNikita Venkatesh 
260*a33d0007SNikita Venkatesh 	if (!ERRATA(6c7a5dce22b3) || cpumask_weight(&cpu_idle_mask) == nr_cpus - 1)
261*a33d0007SNikita Venkatesh 		report(psci_cpu_off_test(), "cpu-off");
262*a33d0007SNikita Venkatesh 	else
263*a33d0007SNikita Venkatesh 		report_skip("Skipping cpu-off test because the cpu-on test failed");
264*a33d0007SNikita Venkatesh 
265bec58c1cSLevente Kurusa done:
266bec58c1cSLevente Kurusa #if 0
267bec58c1cSLevente Kurusa 	report_summary();
268bec58c1cSLevente Kurusa 	psci_invoke(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0);
269198dfd0eSJanis Schoetterl-Glausch 	report_fail("system-off");
270bec58c1cSLevente Kurusa 	return 1; /* only reaches here if system-off fails */
271bec58c1cSLevente Kurusa #else
272bec58c1cSLevente Kurusa 	return report_summary();
273bec58c1cSLevente Kurusa #endif
274bec58c1cSLevente Kurusa }
275