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 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); 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 74 static void dbell_handler(struct pt_regs *regs, void *data) 75 { 76 /* sync */ 77 ipi_fn(regs, data); 78 } 79 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 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 111 void local_ipi_enable(void) 112 { 113 if (machine_is_pseries()) { 114 hcall(H_CPPR, 5); 115 } 116 } 117 118 void local_ipi_disable(void) 119 { 120 if (machine_is_pseries()) { 121 hcall(H_CPPR, 0); 122 } 123 } 124 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 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 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 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 */ 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 */ 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 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 */ 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 */ 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 */ 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 357 static void wait_each_secondary(int fdtnode, u64 regval __unused, void *info) 358 { 359 wait_core(fdtnode); 360 } 361 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