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(cpu_psci_cpu_die)); 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 cpu; 90 91 cpumask_set_cpu(1, &cpu_on_ready); 92 cpumask_set_cpu(1, &cpu_on_done); 93 94 for_each_present_cpu(cpu) { 95 if (cpu < 2) 96 continue; 97 smp_boot_secondary(cpu, cpu_on_secondary_entry); 98 } 99 100 cpumask_set_cpu(0, &cpu_on_ready); 101 while (!cpumask_full(&cpu_on_ready)) 102 cpu_relax(); 103 104 cpu_on_start = 1; 105 smp_mb(); 106 107 cpu_on_ret[0] = psci_cpu_on(cpus[1], __pa(cpu_psci_cpu_die)); 108 cpumask_set_cpu(0, &cpu_on_done); 109 110 while (!cpumask_full(&cpu_on_done)) 111 cpu_relax(); 112 113 for_each_present_cpu(cpu) { 114 if (cpu == 1) 115 continue; 116 if (cpu_on_ret[cpu] != PSCI_RET_SUCCESS && cpu_on_ret[cpu] != PSCI_RET_ALREADY_ON) { 117 report_info("unexpected cpu_on return value: caller=CPU%d, ret=%d", cpu, cpu_on_ret[cpu]); 118 failed = true; 119 } 120 } 121 122 return !failed; 123 } 124 125 int main(void) 126 { 127 int ver = psci_invoke(PSCI_0_2_FN_PSCI_VERSION, 0, 0, 0); 128 129 if (nr_cpus < 2) { 130 report_skip("At least 2 cpus required"); 131 goto done; 132 } 133 134 report_info("PSCI version %d.%d", PSCI_VERSION_MAJOR(ver), 135 PSCI_VERSION_MINOR(ver)); 136 report("invalid-function", psci_invalid_function()); 137 report("affinity-info-on", psci_affinity_info_on()); 138 report("affinity-info-off", psci_affinity_info_off()); 139 140 if (ERRATA(6c7a5dce22b3)) 141 report("cpu-on", psci_cpu_on_test()); 142 else 143 report_skip("Skipping unsafe cpu-on test. Set ERRATA_6c7a5dce22b3=y to enable."); 144 145 done: 146 #if 0 147 report_summary(); 148 psci_invoke(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0); 149 report("system-off", false); 150 return 1; /* only reaches here if system-off fails */ 151 #else 152 return report_summary(); 153 #endif 154 } 155