xref: /kvm-unit-tests/lib/powerpc/smp.c (revision c76b0d0a3842ba312a2d8512f7a3728f4598bf94)
1 /*
2  * Secondary cpu support
3  *
4  * Copyright 2016 Suraj Jitindar Singh, IBM.
5  *
6  * This work is licensed under the terms of the GNU LGPL, version 2.
7  */
8 
9 #include <alloc.h>
10 #include <devicetree.h>
11 #include <asm/atomic.h>
12 #include <asm/barrier.h>
13 #include <asm/processor.h>
14 #include <asm/time.h>
15 #include <asm/setup.h>
16 #include <asm/opal.h>
17 #include <asm/hcall.h>
18 #include <asm/rtas.h>
19 #include <asm/smp.h>
20 
21 struct secondary_entry_data {
22 	secondary_entry_fn entry;
23 };
24 
25 int nr_cpus_online = 1;
26 
stop_self(int cpu_id)27 static void stop_self(int cpu_id)
28 {
29 	if (machine_is_powernv()) {
30 		if (opal_call(OPAL_RETURN_CPU, 0, 0, 0) != OPAL_SUCCESS) {
31 			printf("OPAL_RETURN_CPU failed\n");
32 		}
33 	} else {
34 		rtas_stop_self();
35 	}
36 
37 	printf("failed to stop cpu %d\n", cpu_id);
38 	assert(0);
39 }
40 
41 void main_secondary(struct cpu *cpu);
main_secondary(struct cpu * cpu)42 void main_secondary(struct cpu *cpu)
43 {
44 	mtspr(SPR_SPRG0, (unsigned long)cpu);
45 	__current_cpu = cpu;
46 
47 	enable_mcheck();
48 
49 	cpu_init_ipis();
50 
51 	atomic_fetch_inc(&nr_cpus_online);
52 
53 	cpu->entry(cpu->server_no);
54 
55 	mb();
56 	atomic_fetch_dec(&nr_cpus_online);
57 
58 	stop_self(cpu->server_no);
59 }
60 
61 enum OpalThreadStatus {
62         OPAL_THREAD_INACTIVE = 0x0,
63         OPAL_THREAD_STARTED = 0x1,
64         OPAL_THREAD_UNAVAILABLE = 0x2 /* opal-v3 */
65 };
66 
67 #define H_EOI		0x64
68 #define H_CPPR		0x68
69 #define H_IPI		0x6c
70 #define H_XIRR		0x74
71 
72 static void (*ipi_fn)(struct pt_regs *regs, void *data);
73 
dbell_handler(struct pt_regs * regs,void * data)74 static void dbell_handler(struct pt_regs *regs, void *data)
75 {
76 	/* sync */
77 	ipi_fn(regs, data);
78 }
79 
extint_handler(struct pt_regs * regs,void * data)80 static void extint_handler(struct pt_regs *regs, void *data)
81 {
82 	int32_t xirr;
83 	int32_t xisr;
84 	int64_t rc;
85 
86 	asm volatile("mr r3,%1 ; sc 1 ; mr %0,r4" : "=r"(xirr) : "r"(H_XIRR));
87 	xisr = xirr & 0xffffff;
88 
89 	if (xisr == 2) { /* IPI */
90 		rc = hcall(H_IPI, smp_processor_id(), 0xff);
91 		assert(rc == H_SUCCESS);
92 	}
93 
94 	xirr |= (5 << 24);
95 	rc = hcall(H_EOI, xirr);
96 	assert(rc == H_SUCCESS);
97 
98 	/* lower IPI */
99 	ipi_fn(regs, data);
100 }
101 
cpu_init_ipis(void)102 void cpu_init_ipis(void)
103 {
104 	if (machine_is_powernv()) {
105 		/* skiboot can leave some messages set */
106 		unsigned long rb = (5 << (63-36));
107 		asm volatile("msgclr	%0" :: "r"(rb) : "memory");
108 	}
109 }
110 
local_ipi_enable(void)111 void local_ipi_enable(void)
112 {
113 	if (machine_is_pseries()) {
114 		hcall(H_CPPR, 5);
115 	}
116 }
117 
local_ipi_disable(void)118 void local_ipi_disable(void)
119 {
120 	if (machine_is_pseries()) {
121 		hcall(H_CPPR, 0);
122 	}
123 }
124 
register_ipi(void (* fn)(struct pt_regs *,void *),void * data)125 void register_ipi(void (*fn)(struct pt_regs *, void *), void *data)
126 {
127 	ipi_fn = fn;
128 	if (machine_is_powernv()) {
129 		handle_exception(0xe80, &dbell_handler, data);
130 	} else {
131 		handle_exception(0x500, &extint_handler, data);
132 	}
133 }
134 
unregister_ipi(void)135 void unregister_ipi(void)
136 {
137 	if (machine_is_powernv()) {
138 		handle_exception(0xe80, NULL, NULL);
139 	} else {
140 		handle_exception(0x500, NULL, NULL);
141 	}
142 }
143 
send_ipi(int cpu_id)144 void send_ipi(int cpu_id)
145 {
146 	if (machine_is_powernv()) {
147 		unsigned long rb = (5 << (63-36)) | cpu_id;
148 		asm volatile("lwsync" ::: "memory");
149 		asm volatile("msgsnd	%0" :: "r"(rb) : "memory");
150 	} else {
151 		hcall(H_IPI, cpu_id, 4);
152 	}
153 }
154 
155 static int nr_started = 1;
156 
157 extern void start_secondary(uint64_t server_no); /* asm entry point */
158 
cpu_is_running(int cpu_id)159 static bool cpu_is_running(int cpu_id)
160 {
161 	if (machine_is_powernv()) {
162 		int64_t ret;
163 		uint8_t status;
164 
165 		ret = opal_call(OPAL_QUERY_CPU_STATUS, cpu_id, (unsigned long)&status, 0);
166 		if (ret != OPAL_SUCCESS) {
167 			printf("OPAL_QUERY_CPU_STATUS failed for cpu %d\n", cpu_id);
168 			return false;
169 		}
170 		return (status != OPAL_THREAD_INACTIVE);
171 	} else {
172 		uint32_t query_token;
173 		int outputs[1], ret;
174 
175 		ret = rtas_token("query-cpu-stopped-state", &query_token);
176 		if (ret != 0) {
177 			printf("rtas token query-cpu-stopped-state failed\n");
178 			return false;
179 		}
180 
181 		ret = rtas_call(query_token, 1, 2, outputs, cpu_id);
182 		if (ret) {
183 			printf("query-cpu-stopped-state failed for cpu %d\n", cpu_id);
184 			return ret;
185 		}
186 		if (outputs[0]) /* cpu not in stopped state */
187 			return true;
188 		return false;
189 	}
190 }
191 
192 /*
193  * Start stopped thread cpu_id at entry
194  * Returns:	<0 on failure to start stopped cpu
195  *		0  on success
196  *		>0 on cpu not in stopped state
197  */
start_thread(int cpu_id,secondary_entry_fn entry)198 static int start_thread(int cpu_id, secondary_entry_fn entry)
199 {
200 	struct cpu *cpu;
201 	uint64_t tb;
202 
203 	if (nr_started >= NR_CPUS) {
204 		/* Reached limit */
205 		return -1;
206 	}
207 
208 	if (cpu_id == smp_processor_id()) {
209 		/* Boot CPU already started */
210 		return -1;
211 	}
212 
213 	tb = get_tb();
214 	while (cpu_is_running(cpu_id)) {
215 		if (get_tb() - tb > 3*tb_hz) {
216 			printf("Unable to start running CPU:%d\n", cpu_id);
217 			return 1;
218 		}
219 	}
220 
221 	cpu = &cpus[nr_started];
222 	nr_started++;
223 
224 	cpu_init(cpu, cpu_id);
225 	cpu->entry = entry;
226 
227 	if (machine_is_powernv()) {
228 		if (opal_call(OPAL_START_CPU, cpu_id, (unsigned long)start_secondary, 0) != OPAL_SUCCESS) {
229 			printf("failed to start cpu %d\n", cpu_id);
230 			return -1;
231 		}
232 	} else {
233 		uint32_t start_token;
234 		int ret;
235 
236 		ret = rtas_token("start-cpu", &start_token);
237 		assert(ret == 0);
238 
239 		ret = rtas_call(start_token, 3, 1, NULL, cpu_id, start_secondary, cpu_id);
240 		if (ret) {
241 			printf("failed to start cpu %d\n", cpu_id);
242 			return ret;
243 		}
244 	}
245 
246 	return 0;
247 }
248 
249 /*
250  * Start all stopped threads (vcpus) on cpu_node
251  * Returns: Number of stopped cpus which were successfully started
252  */
start_core(int cpu_node,secondary_entry_fn entry)253 static void start_core(int cpu_node, secondary_entry_fn entry)
254 {
255 	int len, i, nr_threads;
256 	const struct fdt_property *prop;
257 	u32 *threads;
258 
259 	/* Get the id array of threads on this cpu_node */
260 	prop = fdt_get_property(dt_fdt(), cpu_node,
261 				"ibm,ppc-interrupt-server#s", &len);
262 	assert(prop);
263 
264 	nr_threads = len >> 2; /* Divide by 4 since 4 bytes per thread */
265 
266 	threads = (u32 *)prop->data; /* Array of valid ids */
267 
268 	for (i = 0; i < nr_threads; i++)
269 		start_thread(fdt32_to_cpu(threads[i]), entry);
270 }
271 
start_each_secondary(int fdtnode,u64 regval __unused,void * info)272 static void start_each_secondary(int fdtnode, u64 regval __unused, void *info)
273 {
274 	struct secondary_entry_data *datap = info;
275 
276 	start_core(fdtnode, datap->entry);
277 }
278 
279 /*
280  * Start all stopped cpus on the guest at entry with register 3 set to r3
281  * We expect that we come in with only one thread currently started
282  * Returns:	TRUE on success
283  *		FALSE on failure
284  */
start_all_cpus(secondary_entry_fn entry)285 bool start_all_cpus(secondary_entry_fn entry)
286 {
287 	struct secondary_entry_data data = { entry };
288 	uint64_t tb;
289 	int ret;
290 
291 	assert(nr_cpus_online == 1);
292 	assert(nr_started == 1);
293 	ret = dt_for_each_cpu_node(start_each_secondary, &data);
294 	assert(ret == 0);
295 	assert(nr_started == nr_cpus_present);
296 
297 	tb = get_tb();
298 	while (nr_cpus_online < nr_cpus_present) {
299 		if (get_tb() - tb > 3*tb_hz) {
300 			printf("failed to start all secondaries\n");
301 			assert(0);
302 		}
303 		cpu_relax();
304 	}
305 
306 	return 1;
307 }
308 
309 /*
310  * Start stopped thread cpu_id at entry
311  * Returns:	<0 on failure to start stopped cpu
312  *		0  on success
313  *		>0 on cpu not in stopped state
314  */
wait_thread(int cpu_id)315 static int wait_thread(int cpu_id)
316 {
317 	uint64_t tb;
318 
319 	/* Skip the caller */
320 	if (cpu_id == smp_processor_id()) {
321 		return 0;
322 	}
323 
324 	tb = get_tb();
325 	while (cpu_is_running(cpu_id)) {
326 		if (get_tb() - tb > 3*tb_hz) {
327 			printf("Timeout waiting to stop CPU:%d\n", cpu_id);
328 			return 1;
329 		}
330 	}
331 
332 	return 0;
333 }
334 
335 /*
336  * Wait for running threads (vcpus) on cpu_node to stop
337  */
wait_core(int cpu_node)338 static void wait_core(int cpu_node)
339 {
340 	int len, i, nr_threads;
341 	const struct fdt_property *prop;
342 	u32 *threads;
343 
344 	/* Get the id array of threads on this cpu_node */
345 	prop = fdt_get_property(dt_fdt(), cpu_node,
346 				"ibm,ppc-interrupt-server#s", &len);
347 	assert(prop);
348 
349 	nr_threads = len >> 2; /* Divide by 4 since 4 bytes per thread */
350 
351 	threads = (u32 *)prop->data; /* Array of valid ids */
352 
353 	for (i = 0; i < nr_threads; i++)
354 		wait_thread(fdt32_to_cpu(threads[i]));
355 }
356 
wait_each_secondary(int fdtnode,u64 regval __unused,void * info)357 static void wait_each_secondary(int fdtnode, u64 regval __unused, void *info)
358 {
359 	wait_core(fdtnode);
360 }
361 
stop_all_cpus(void)362 void stop_all_cpus(void)
363 {
364 	while (nr_cpus_online > 1)
365 		cpu_relax();
366 
367 	dt_for_each_cpu_node(wait_each_secondary, NULL);
368 	mb();
369 	nr_started = 1;
370 }
371