xref: /kvm-unit-tests/lib/powerpc/smp.c (revision f0ca153cc2d1e6a670b3571a8ac24c2ef7042594)
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 <devicetree.h>
10 #include <asm/setup.h>
11 #include <asm/rtas.h>
12 #include <asm/smp.h>
13 
14 int nr_threads;
15 
16 struct secondary_entry_data {
17 	secondary_entry_fn entry;
18 	uint64_t r3;
19 	int nr_started;
20 };
21 
22 /*
23  * Start stopped thread cpu_id at entry
24  * Returns:	<0 on failure to start stopped cpu
25  *		0  on success
26  *		>0 on cpu not in stopped state
27  */
28 int start_thread(int cpu_id, secondary_entry_fn entry, uint32_t r3)
29 {
30 	int query_token, start_token, outputs[1], ret;
31 
32 	query_token = rtas_token("query-cpu-stopped-state");
33 	start_token = rtas_token("start-cpu");
34 	assert(query_token != RTAS_UNKNOWN_SERVICE &&
35 		start_token != RTAS_UNKNOWN_SERVICE);
36 
37 	ret = rtas_call(query_token, 1, 2, outputs, cpu_id);
38 	if (ret) {
39 		printf("query-cpu-stopped-state failed for cpu %d\n", cpu_id);
40 	} else if (!outputs[0]) { /* cpu in stopped state */
41 		ret = rtas_call(start_token, 3, 1, NULL, cpu_id, entry, r3);
42 		if (ret)
43 			printf("failed to start cpu %d\n", cpu_id);
44 	} else { /* cpu not in stopped state */
45 		ret = outputs[0];
46 	}
47 
48 	return ret;
49 }
50 
51 /*
52  * Start all stopped threads (vcpus) on cpu_node
53  * Returns: Number of stopped cpus which were successfully started
54  */
55 struct start_threads start_cpu(int cpu_node, secondary_entry_fn entry,
56 			       uint32_t r3)
57 {
58 	int len, i, nr_threads, nr_started = 0;
59 	const struct fdt_property *prop;
60 	u32 *threads;
61 
62 	/* Get the id array of threads on this cpu_node */
63 	prop = fdt_get_property(dt_fdt(), cpu_node,
64 				"ibm,ppc-interrupt-server#s", &len);
65 	assert(prop);
66 
67 	nr_threads = len >> 2; /* Divide by 4 since 4 bytes per thread */
68 	threads = (u32 *)prop->data; /* Array of valid ids */
69 
70 	for (i = 0; i < nr_threads; i++) {
71 		if (!start_thread(fdt32_to_cpu(threads[i]), entry, r3))
72 			nr_started++;
73 	}
74 
75 	return (struct start_threads) { nr_threads, nr_started };
76 }
77 
78 static void start_each_secondary(int fdtnode, u32 regval __unused, void *info)
79 {
80 	struct secondary_entry_data *datap = info;
81 	struct start_threads ret = start_cpu(fdtnode, datap->entry, datap->r3);
82 
83 	nr_threads += ret.nr_threads;
84 	datap->nr_started += ret.nr_started;
85 }
86 
87 /*
88  * Start all stopped cpus on the guest at entry with register 3 set to r3
89  * We expect that we come in with only one thread currently started
90  * Returns:	TRUE on success
91  *		FALSE on failure
92  */
93 bool start_all_cpus(secondary_entry_fn entry, uint32_t r3)
94 {
95 	struct secondary_entry_data data = { entry, r3,	0 };
96 	int ret;
97 
98 	ret = dt_for_each_cpu_node(start_each_secondary, &data);
99 	assert(ret == 0);
100 
101 	/* We expect that we come in with one thread already started */
102 	return data.nr_started == nr_threads - 1;
103 }
104