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