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