1 /*
2 * PSCI tests
3 *
4 * Copyright (C) 2017, Red Hat, Inc.
5 * Author: Levente Kurusa <lkurusa@redhat.com>
6 * Author: Andrew Jones <drjones@redhat.com>
7 *
8 * This work is licensed under the terms of the GNU LGPL, version 2.
9 */
10 #include <errata.h>
11 #include <libcflat.h>
12
13 #include <asm/delay.h>
14 #include <asm/mmu.h>
15 #include <asm/processor.h>
16 #include <asm/psci.h>
17 #include <asm/smp.h>
18
19 static bool invalid_function_exception;
20
21 #ifdef __arm__
invalid_function_handler(struct pt_regs * regs __unused)22 static void invalid_function_handler(struct pt_regs *regs __unused)
23 {
24 invalid_function_exception = true;
25 }
26 #else
invalid_function_handler(struct pt_regs * regs,unsigned int esr __unused)27 static void invalid_function_handler(struct pt_regs *regs, unsigned int esr __unused)
28 {
29 invalid_function_exception = true;
30 regs->pc += 4;
31 }
32 #endif
33
install_invalid_function_handler(exception_fn handler)34 static void install_invalid_function_handler(exception_fn handler)
35 {
36 #ifdef __arm__
37 install_exception_handler(EXCPTN_UND, handler);
38 #else
39 install_exception_handler(EL1H_SYNC, ESR_EL1_EC_UNKNOWN, handler);
40 #endif
41 }
42
psci_invalid_function(void)43 static bool psci_invalid_function(void)
44 {
45 bool pass;
46
47 install_invalid_function_handler(invalid_function_handler);
48
49 pass = psci_invoke(1337, 0, 0, 0) == PSCI_RET_NOT_SUPPORTED || invalid_function_exception;
50
51 install_invalid_function_handler(NULL);
52 return pass;
53 }
54
psci_affinity_info(unsigned long target_affinity,uint32_t lowest_affinity_level)55 static int psci_affinity_info(unsigned long target_affinity, uint32_t lowest_affinity_level)
56 {
57 #ifdef __arm__
58 return psci_invoke(PSCI_0_2_FN_AFFINITY_INFO, target_affinity, lowest_affinity_level, 0);
59 #else
60 return psci_invoke(PSCI_0_2_FN64_AFFINITY_INFO, target_affinity, lowest_affinity_level, 0);
61 #endif
62 }
63
psci_affinity_info_on(void)64 static bool psci_affinity_info_on(void)
65 {
66 return psci_affinity_info(cpus[0], 0) == PSCI_0_2_AFFINITY_LEVEL_ON;
67 }
68
psci_affinity_info_off(void)69 static bool psci_affinity_info_off(void)
70 {
71 return psci_affinity_info(cpus[1], 0) == PSCI_0_2_AFFINITY_LEVEL_OFF;
72 }
73
74 static int cpu_on_ret[NR_CPUS];
75 static cpumask_t cpu_on_ready, cpu_on_done, cpu_off_done;
76 static volatile int cpu_on_start;
77 static volatile int cpu_off_start;
78
79 extern void secondary_entry(void);
cpu_on_do_wake_target(void)80 static void cpu_on_do_wake_target(void)
81 {
82 int cpu = smp_processor_id();
83
84 cpumask_set_cpu(cpu, &cpu_on_ready);
85 while (!cpu_on_start)
86 cpu_relax();
87 cpu_on_ret[cpu] = psci_cpu_on(cpus[1], __pa(secondary_entry));
88 cpumask_set_cpu(cpu, &cpu_on_done);
89 }
90
cpu_on_target(void)91 static void cpu_on_target(void)
92 {
93 int cpu = smp_processor_id();
94
95 cpumask_set_cpu(cpu, &cpu_on_done);
96 }
97
98 extern struct secondary_data secondary_data;
99
100 /* Open code the setup part from smp_boot_secondary(). */
psci_cpu_on_prepare_secondary(int cpu,secondary_entry_fn entry)101 static void psci_cpu_on_prepare_secondary(int cpu, secondary_entry_fn entry)
102 {
103 secondary_data.stack = thread_stack_alloc();
104 secondary_data.entry = entry;
105 mmu_mark_disabled(cpu);
106 }
107
psci_cpu_on_test(void)108 static bool psci_cpu_on_test(void)
109 {
110 bool failed = false;
111 int ret_success = 0;
112 int i, cpu;
113
114 for_each_present_cpu(cpu) {
115 if (cpu < 2)
116 continue;
117 smp_boot_secondary(cpu, cpu_on_do_wake_target);
118 }
119
120 cpumask_set_cpu(0, &cpu_on_ready);
121 cpumask_set_cpu(1, &cpu_on_ready);
122 while (!cpumask_full(&cpu_on_ready))
123 cpu_relax();
124
125 /*
126 * Configure CPU 1 after all secondaries are online to avoid
127 * secondary_data being overwritten.
128 */
129 psci_cpu_on_prepare_secondary(1, cpu_on_target);
130
131 cpu_on_start = 1;
132 smp_mb();
133
134 cpu_on_ret[0] = psci_cpu_on(cpus[1], __pa(secondary_entry));
135 cpumask_set_cpu(0, &cpu_on_done);
136
137 report_info("waiting for CPU1 to come online...");
138 for (i = 0; i < 100; i++) {
139 mdelay(10);
140 if (cpumask_full(&cpu_on_done))
141 break;
142 }
143
144 if (!cpumask_full(&cpu_on_done)) {
145 for_each_present_cpu(cpu) {
146 if (!cpumask_test_cpu(cpu, &cpu_on_done)) {
147 if (cpu == 1)
148 report_info("CPU1 failed to come online");
149 else
150 report_info("CPU%d failed to online CPU1", cpu);
151 }
152 }
153 failed = true;
154 }
155
156 for_each_cpu(cpu, &cpu_on_done) {
157 if (cpu == 1)
158 continue;
159 if (cpu_on_ret[cpu] == PSCI_RET_SUCCESS) {
160 ret_success++;
161 } else if (cpu_on_ret[cpu] != PSCI_RET_ALREADY_ON) {
162 report_info("unexpected cpu_on return value: caller=CPU%d, ret=%d", cpu, cpu_on_ret[cpu]);
163 failed = true;
164 }
165 }
166
167 if (ret_success != 1) {
168 report_info("got %d CPU_ON success", ret_success);
169 failed = true;
170 }
171
172 return !failed;
173 }
174
cpu_off_secondary_entry(void * data)175 static void cpu_off_secondary_entry(void *data)
176 {
177 int cpu = smp_processor_id();
178
179 while (!cpu_off_start)
180 cpu_relax();
181 cpumask_set_cpu(cpu, &cpu_off_done);
182 cpu_psci_cpu_die();
183 }
184
psci_cpu_off_test(void)185 static bool psci_cpu_off_test(void)
186 {
187 bool failed = false;
188 int i, count, cpu;
189
190 for_each_present_cpu(cpu) {
191 if (cpu == 0)
192 continue;
193 on_cpu_async(cpu, cpu_off_secondary_entry, NULL);
194 }
195
196 cpumask_set_cpu(0, &cpu_off_done);
197
198 cpu_off_start = 1;
199 report_info("waiting for the CPUs to be offlined...");
200 while (!cpumask_full(&cpu_off_done))
201 cpu_relax();
202
203 /* Allow all the other CPUs to complete the operation */
204 for (i = 0; i < 100; i++) {
205 mdelay(10);
206
207 count = 0;
208 for_each_present_cpu(cpu) {
209 if (cpu == 0)
210 continue;
211 if (psci_affinity_info(cpus[cpu], 0) != PSCI_0_2_AFFINITY_LEVEL_OFF)
212 count++;
213 }
214 if (count == 0)
215 break;
216 }
217
218 /* Try to catch CPUs that return from CPU_OFF. */
219 if (count == 0)
220 mdelay(100);
221
222 for_each_present_cpu(cpu) {
223 if (cpu == 0)
224 continue;
225 if (cpu_idle(cpu)) {
226 report_info("CPU%d failed to be offlined", cpu);
227 if (psci_affinity_info(cpus[cpu], 0) == PSCI_0_2_AFFINITY_LEVEL_OFF)
228 report_info("AFFINITY_INFO incorrectly reports CPU%d as offline", cpu);
229 failed = true;
230 }
231 }
232
233 return !failed;
234 }
235
main(void)236 int main(void)
237 {
238 int ver = psci_invoke(PSCI_0_2_FN_PSCI_VERSION, 0, 0, 0);
239
240 report_prefix_push("psci");
241
242 if (nr_cpus < 2) {
243 report_skip("At least 2 cpus required");
244 goto done;
245 }
246
247 report_info("PSCI version %d.%d", PSCI_VERSION_MAJOR(ver),
248 PSCI_VERSION_MINOR(ver));
249 report(psci_invalid_function(), "invalid-function");
250 report(psci_affinity_info_on(), "affinity-info-on");
251 report(psci_affinity_info_off(), "affinity-info-off");
252
253 if (ERRATA(6c7a5dce22b3))
254 report(psci_cpu_on_test(), "cpu-on");
255 else
256 report_skip("Skipping unsafe cpu-on test. Set ERRATA_6c7a5dce22b3=y to enable.");
257
258 assert(!cpu_idle(0));
259
260 if (!ERRATA(6c7a5dce22b3) || cpumask_weight(&cpu_idle_mask) == nr_cpus - 1)
261 report(psci_cpu_off_test(), "cpu-off");
262 else
263 report_skip("Skipping cpu-off test because the cpu-on test failed");
264
265 done:
266 #if 0
267 report_summary();
268 psci_invoke(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0);
269 report_fail("system-off");
270 return 1; /* only reaches here if system-off fails */
271 #else
272 return report_summary();
273 #endif
274 }
275