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 <asm/processor.h> 12 #include <asm/smp.h> 13 #include <asm/psci.h> 14 15 static bool invalid_function_exception; 16 17 #ifdef __arm__ 18 static void invalid_function_handler(struct pt_regs *regs __unused) 19 { 20 invalid_function_exception = true; 21 } 22 #else 23 static void invalid_function_handler(struct pt_regs *regs, unsigned int esr __unused) 24 { 25 invalid_function_exception = true; 26 regs->pc += 4; 27 } 28 #endif 29 30 static void install_invalid_function_handler(exception_fn handler) 31 { 32 #ifdef __arm__ 33 install_exception_handler(EXCPTN_UND, handler); 34 #else 35 install_exception_handler(EL1H_SYNC, ESR_EL1_EC_UNKNOWN, handler); 36 #endif 37 } 38 39 static bool psci_invalid_function(void) 40 { 41 bool pass; 42 43 install_invalid_function_handler(invalid_function_handler); 44 45 pass = psci_invoke(1337, 0, 0, 0) == PSCI_RET_NOT_SUPPORTED || invalid_function_exception; 46 47 install_invalid_function_handler(NULL); 48 return pass; 49 } 50 51 static int psci_affinity_info(unsigned long target_affinity, uint32_t lowest_affinity_level) 52 { 53 #ifdef __arm__ 54 return psci_invoke(PSCI_0_2_FN_AFFINITY_INFO, target_affinity, lowest_affinity_level, 0); 55 #else 56 return psci_invoke(PSCI_0_2_FN64_AFFINITY_INFO, target_affinity, lowest_affinity_level, 0); 57 #endif 58 } 59 60 static bool psci_affinity_info_on(void) 61 { 62 return psci_affinity_info(cpus[0], 0) == PSCI_0_2_AFFINITY_LEVEL_ON; 63 } 64 65 static bool psci_affinity_info_off(void) 66 { 67 return psci_affinity_info(cpus[1], 0) == PSCI_0_2_AFFINITY_LEVEL_OFF; 68 } 69 70 static int cpu_on_ret[NR_CPUS]; 71 static cpumask_t cpu_on_ready, cpu_on_done; 72 static volatile int cpu_on_start; 73 74 static void cpu_on_secondary_entry(void) 75 { 76 int cpu = smp_processor_id(); 77 78 cpumask_set_cpu(cpu, &cpu_on_ready); 79 while (!cpu_on_start) 80 cpu_relax(); 81 cpu_on_ret[cpu] = psci_cpu_on(cpus[1], __pa(halt)); 82 cpumask_set_cpu(cpu, &cpu_on_done); 83 halt(); 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(halt)); 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 report("cpu-on", psci_cpu_on_test()); 140 141 done: 142 #if 0 143 report_summary(); 144 psci_invoke(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0); 145 report("system-off", false); 146 return 1; /* only reaches here if system-off fails */ 147 #else 148 return report_summary(); 149 #endif 150 } 151