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