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; 76 static volatile int cpu_on_start; 77 78 extern void secondary_entry(void); 79 static void cpu_on_do_wake_target(void) 80 { 81 int cpu = smp_processor_id(); 82 83 cpumask_set_cpu(cpu, &cpu_on_ready); 84 while (!cpu_on_start) 85 cpu_relax(); 86 cpu_on_ret[cpu] = psci_cpu_on(cpus[1], __pa(secondary_entry)); 87 cpumask_set_cpu(cpu, &cpu_on_done); 88 } 89 90 static void cpu_on_target(void) 91 { 92 int cpu = smp_processor_id(); 93 94 cpumask_set_cpu(cpu, &cpu_on_done); 95 } 96 97 extern struct secondary_data secondary_data; 98 99 /* Open code the setup part from smp_boot_secondary(). */ 100 static void psci_cpu_on_prepare_secondary(int cpu, secondary_entry_fn entry) 101 { 102 secondary_data.stack = thread_stack_alloc(); 103 secondary_data.entry = entry; 104 mmu_mark_disabled(cpu); 105 } 106 107 static bool psci_cpu_on_test(void) 108 { 109 bool failed = false; 110 int ret_success = 0; 111 int i, cpu; 112 113 for_each_present_cpu(cpu) { 114 if (cpu < 2) 115 continue; 116 smp_boot_secondary(cpu, cpu_on_do_wake_target); 117 } 118 119 cpumask_set_cpu(0, &cpu_on_ready); 120 cpumask_set_cpu(1, &cpu_on_ready); 121 while (!cpumask_full(&cpu_on_ready)) 122 cpu_relax(); 123 124 /* 125 * Configure CPU 1 after all secondaries are online to avoid 126 * secondary_data being overwritten. 127 */ 128 psci_cpu_on_prepare_secondary(1, cpu_on_target); 129 130 cpu_on_start = 1; 131 smp_mb(); 132 133 cpu_on_ret[0] = psci_cpu_on(cpus[1], __pa(secondary_entry)); 134 cpumask_set_cpu(0, &cpu_on_done); 135 136 report_info("waiting for CPU1 to come online..."); 137 for (i = 0; i < 100; i++) { 138 mdelay(10); 139 if (cpumask_full(&cpu_on_done)) 140 break; 141 } 142 143 if (!cpumask_full(&cpu_on_done)) { 144 for_each_present_cpu(cpu) { 145 if (!cpumask_test_cpu(cpu, &cpu_on_done)) { 146 if (cpu == 1) 147 report_info("CPU1 failed to come online"); 148 else 149 report_info("CPU%d failed to online CPU1", cpu); 150 } 151 } 152 failed = true; 153 } 154 155 for_each_cpu(cpu, &cpu_on_done) { 156 if (cpu == 1) 157 continue; 158 if (cpu_on_ret[cpu] == PSCI_RET_SUCCESS) { 159 ret_success++; 160 } else if (cpu_on_ret[cpu] != PSCI_RET_ALREADY_ON) { 161 report_info("unexpected cpu_on return value: caller=CPU%d, ret=%d", cpu, cpu_on_ret[cpu]); 162 failed = true; 163 } 164 } 165 166 if (ret_success != 1) { 167 report_info("got %d CPU_ON success", ret_success); 168 failed = true; 169 } 170 171 return !failed; 172 } 173 174 int main(void) 175 { 176 int ver = psci_invoke(PSCI_0_2_FN_PSCI_VERSION, 0, 0, 0); 177 178 report_prefix_push("psci"); 179 180 if (nr_cpus < 2) { 181 report_skip("At least 2 cpus required"); 182 goto done; 183 } 184 185 report_info("PSCI version %d.%d", PSCI_VERSION_MAJOR(ver), 186 PSCI_VERSION_MINOR(ver)); 187 report(psci_invalid_function(), "invalid-function"); 188 report(psci_affinity_info_on(), "affinity-info-on"); 189 report(psci_affinity_info_off(), "affinity-info-off"); 190 191 if (ERRATA(6c7a5dce22b3)) 192 report(psci_cpu_on_test(), "cpu-on"); 193 else 194 report_skip("Skipping unsafe cpu-on test. Set ERRATA_6c7a5dce22b3=y to enable."); 195 196 done: 197 #if 0 198 report_summary(); 199 psci_invoke(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0); 200 report_fail("system-off"); 201 return 1; /* only reaches here if system-off fails */ 202 #else 203 return report_summary(); 204 #endif 205 } 206