xref: /kvm-unit-tests/arm/psci.c (revision 7430e2c554924e8ddb6156161708fa402c478b6f)
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