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 <libcflat.h> 11 #include <errata.h> 12 #include <asm/processor.h> 13 #include <asm/smp.h> 14 #include <asm/psci.h> 15 16 static bool invalid_function_exception; 17 18 #ifdef __arm__ 19 static void invalid_function_handler(struct pt_regs *regs __unused) 20 { 21 invalid_function_exception = true; 22 } 23 #else 24 static void invalid_function_handler(struct pt_regs *regs, unsigned int esr __unused) 25 { 26 invalid_function_exception = true; 27 regs->pc += 4; 28 } 29 #endif 30 31 static void install_invalid_function_handler(exception_fn handler) 32 { 33 #ifdef __arm__ 34 install_exception_handler(EXCPTN_UND, handler); 35 #else 36 install_exception_handler(EL1H_SYNC, ESR_EL1_EC_UNKNOWN, handler); 37 #endif 38 } 39 40 static bool psci_invalid_function(void) 41 { 42 bool pass; 43 44 install_invalid_function_handler(invalid_function_handler); 45 46 pass = psci_invoke(1337, 0, 0, 0) == PSCI_RET_NOT_SUPPORTED || invalid_function_exception; 47 48 install_invalid_function_handler(NULL); 49 return pass; 50 } 51 52 static int psci_affinity_info(unsigned long target_affinity, uint32_t lowest_affinity_level) 53 { 54 #ifdef __arm__ 55 return psci_invoke(PSCI_0_2_FN_AFFINITY_INFO, target_affinity, lowest_affinity_level, 0); 56 #else 57 return psci_invoke(PSCI_0_2_FN64_AFFINITY_INFO, target_affinity, lowest_affinity_level, 0); 58 #endif 59 } 60 61 static bool psci_affinity_info_on(void) 62 { 63 return psci_affinity_info(cpus[0], 0) == PSCI_0_2_AFFINITY_LEVEL_ON; 64 } 65 66 static bool psci_affinity_info_off(void) 67 { 68 return psci_affinity_info(cpus[1], 0) == PSCI_0_2_AFFINITY_LEVEL_OFF; 69 } 70 71 static int cpu_on_ret[NR_CPUS]; 72 static cpumask_t cpu_on_ready, cpu_on_done; 73 static volatile int cpu_on_start; 74 75 static void cpu_on_secondary_entry(void) 76 { 77 int cpu = smp_processor_id(); 78 79 cpumask_set_cpu(cpu, &cpu_on_ready); 80 while (!cpu_on_start) 81 cpu_relax(); 82 cpu_on_ret[cpu] = psci_cpu_on(cpus[1], __pa(halt)); 83 cpumask_set_cpu(cpu, &cpu_on_done); 84 } 85 86 static bool psci_cpu_on_test(void) 87 { 88 bool failed = false; 89 int ret_success = 0; 90 int cpu; 91 92 cpumask_set_cpu(1, &cpu_on_ready); 93 cpumask_set_cpu(1, &cpu_on_done); 94 95 for_each_present_cpu(cpu) { 96 if (cpu < 2) 97 continue; 98 smp_boot_secondary(cpu, cpu_on_secondary_entry); 99 } 100 101 cpumask_set_cpu(0, &cpu_on_ready); 102 while (!cpumask_full(&cpu_on_ready)) 103 cpu_relax(); 104 105 cpu_on_start = 1; 106 smp_mb(); 107 108 cpu_on_ret[0] = psci_cpu_on(cpus[1], __pa(halt)); 109 cpumask_set_cpu(0, &cpu_on_done); 110 111 while (!cpumask_full(&cpu_on_done)) 112 cpu_relax(); 113 114 for_each_present_cpu(cpu) { 115 if (cpu == 1) 116 continue; 117 if (cpu_on_ret[cpu] == PSCI_RET_SUCCESS) { 118 ret_success++; 119 } else if (cpu_on_ret[cpu] != PSCI_RET_ALREADY_ON) { 120 report_info("unexpected cpu_on return value: caller=CPU%d, ret=%d", cpu, cpu_on_ret[cpu]); 121 failed = true; 122 } 123 } 124 125 if (ret_success != 1) { 126 report_info("got %d CPU_ON success", ret_success); 127 failed = true; 128 } 129 130 return !failed; 131 } 132 133 int main(void) 134 { 135 int ver = psci_invoke(PSCI_0_2_FN_PSCI_VERSION, 0, 0, 0); 136 137 report_prefix_push("psci"); 138 139 if (nr_cpus < 2) { 140 report_skip("At least 2 cpus required"); 141 goto done; 142 } 143 144 report_info("PSCI version %d.%d", PSCI_VERSION_MAJOR(ver), 145 PSCI_VERSION_MINOR(ver)); 146 report(psci_invalid_function(), "invalid-function"); 147 report(psci_affinity_info_on(), "affinity-info-on"); 148 report(psci_affinity_info_off(), "affinity-info-off"); 149 150 if (ERRATA(6c7a5dce22b3)) 151 report(psci_cpu_on_test(), "cpu-on"); 152 else 153 report_skip("Skipping unsafe cpu-on test. Set ERRATA_6c7a5dce22b3=y to enable."); 154 155 done: 156 #if 0 157 report_summary(); 158 psci_invoke(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0); 159 report_fail("system-off"); 160 return 1; /* only reaches here if system-off fails */ 161 #else 162 return report_summary(); 163 #endif 164 } 165