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__ 22 static void invalid_function_handler(struct pt_regs *regs __unused) 23 { 24 invalid_function_exception = true; 25 } 26 #else 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 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 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 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 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 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); 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 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(). */ 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 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 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 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 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