1 /* $NetBSD: cpu_subr.c,v 1.6 2025/09/06 02:53:23 riastradh Exp $ */ 2 3 /*- 4 * Copyright (c) 2020 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Nick Hudson 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include "opt_ddb.h" 33 #include "opt_multiprocessor.h" 34 #include "opt_riscv_debug.h" 35 36 #include <sys/cdefs.h> 37 __KERNEL_RCSID(0, "$NetBSD: cpu_subr.c,v 1.6 2025/09/06 02:53:23 riastradh Exp $"); 38 39 #include <sys/param.h> 40 #include <sys/atomic.h> 41 #include <sys/cpu.h> 42 #include <sys/kernel.h> 43 #include <sys/reboot.h> 44 #include <sys/paravirt_membar.h> 45 #include <sys/xcall.h> 46 47 #include <machine/db_machdep.h> 48 #include <machine/sbi.h> 49 50 #ifdef DDB 51 #include <ddb/db_output.h> 52 #endif 53 54 #ifdef VERBOSE_INIT_RISCV 55 #define VPRINTF(...) printf(__VA_ARGS__) 56 #else 57 #define VPRINTF(...) __nothing 58 #endif 59 60 unsigned int cpu_hartindex[MAXCPUS] = { 61 [0 ... MAXCPUS - 1] = ~0U, 62 }; 63 64 cpuid_t cpu_bphartid = ~0UL; 65 66 #ifdef MULTIPROCESSOR 67 68 kcpuset_t *cpus_halted; 69 kcpuset_t *cpus_hatched; 70 kcpuset_t *cpus_paused; 71 kcpuset_t *cpus_resumed; 72 kcpuset_t *cpus_running; 73 74 #define CPUINDEX_DIVISOR (sizeof(u_long) * NBBY) 75 76 #define N howmany(MAXCPUS, CPUINDEX_DIVISOR) 77 78 /* cpu_hatch_ipi needs fixing for > 1 */ 79 CTASSERT(N == 1); 80 volatile u_long riscv_cpu_hatched[N] __cacheline_aligned = { }; 81 volatile u_long riscv_cpu_mbox[N] __cacheline_aligned = { }; 82 83 /* IPI all APs to GO! */ 84 static void 85 cpu_ipi_aps(void) 86 { 87 unsigned long hartmask = 0; 88 89 // BP is index 0 90 for (size_t i = 1; i < ncpu; i++) { 91 const struct cpu_info * const ci = &cpu_info_store[i]; 92 const cpuid_t hartid = ci->ci_cpuid; 93 KASSERT(hartid < sizeof(unsigned long) * NBBY); 94 hartmask |= __BIT(hartid); 95 } 96 struct sbiret sbiret = sbi_send_ipi(hartmask, 0); 97 98 KASSERT(sbiret.error == SBI_SUCCESS); 99 } 100 101 void 102 cpu_boot_secondary_processors(void) 103 { 104 if ((boothowto & RB_MD1) != 0) 105 return; 106 107 VPRINTF("%s: starting secondary processors\n", __func__); 108 109 /* 110 * send mbox to have secondary processors do cpu_hatch() 111 * store-release matches locore.S 112 */ 113 asm volatile("fence rw,w"); 114 for (size_t n = 0; n < __arraycount(riscv_cpu_mbox); n++) 115 atomic_or_ulong(&riscv_cpu_mbox[n], riscv_cpu_hatched[n]); 116 cpu_ipi_aps(); 117 118 /* wait for all cpus to have done cpu_hatch() */ 119 for (u_int cpuindex = 1; cpuindex < ncpu; cpuindex++) { 120 if (!cpu_hatched_p(cpuindex)) 121 continue; 122 123 const size_t off = cpuindex / CPUINDEX_DIVISOR; 124 const u_long bit = __BIT(cpuindex % CPUINDEX_DIVISOR); 125 126 /* load-acquire matches cpu_clr_mbox */ 127 while (atomic_load_acquire(&riscv_cpu_mbox[off]) & bit) { 128 /* spin - it shouldn't be long */ 129 ; 130 } 131 struct cpu_info *ci = &cpu_info_store[cpuindex]; 132 atomic_or_ulong(&ci->ci_flags, CPUF_RUNNING); 133 kcpuset_set(cpus_running, cpu_index(ci)); 134 } 135 136 VPRINTF("%s: secondary processors hatched. %d running\n", __func__, 137 kcpuset_countset(cpus_running)); 138 } 139 140 bool 141 cpu_hatched_p(u_int cpuindex) 142 { 143 const u_int off = cpuindex / CPUINDEX_DIVISOR; 144 const u_int bit = cpuindex % CPUINDEX_DIVISOR; 145 146 /* load-acquire matches cpu_set_hatched */ 147 return (atomic_load_acquire(&riscv_cpu_hatched[off]) & __BIT(bit)) != 0; 148 } 149 150 151 void 152 cpu_set_hatched(u_int cpuindex) 153 { 154 155 const size_t off = cpuindex / CPUINDEX_DIVISOR; 156 const u_long bit = __BIT(cpuindex % CPUINDEX_DIVISOR); 157 158 /* store-release matches cpu_hatched_p */ 159 asm volatile("fence rw, w" ::: "memory"); 160 atomic_or_ulong(&riscv_cpu_hatched[off], bit); 161 162 asm volatile("fence w, rw" ::: "memory"); 163 } 164 165 void 166 cpu_clr_mbox(u_int cpuindex) 167 { 168 169 const size_t off = cpuindex / CPUINDEX_DIVISOR; 170 const u_long bit = __BIT(cpuindex % CPUINDEX_DIVISOR); 171 172 /* store-release matches locore.S */ 173 asm volatile("fence rw,w" ::: "memory"); 174 atomic_and_ulong(&riscv_cpu_mbox[off], ~bit); 175 176 asm volatile("fence w, rw" ::: "memory"); 177 } 178 179 180 void 181 cpu_broadcast_ipi(int tag) 182 { 183 184 /* 185 * No reason to remove ourselves since multicast_ipi will do that 186 * for us. 187 */ 188 cpu_multicast_ipi(cpus_running, tag); 189 } 190 191 void 192 cpu_multicast_ipi(const kcpuset_t *kcp, int tag) 193 { 194 struct cpu_info * const ci = curcpu(); 195 kcpuset_t *kcp2 = ci->ci_multicastcpus; 196 197 if (kcpuset_match(cpus_running, ci->ci_data.cpu_kcpuset)) 198 return; 199 200 kcpuset_copy(kcp2, kcp); 201 kcpuset_remove(kcp2, ci->ci_data.cpu_kcpuset); 202 for (unsigned int cii; (cii = kcpuset_ffs(kcp2)) != 0; ) { 203 kcpuset_clear(kcp2, --cii); 204 (void)cpu_send_ipi(cpu_lookup(cii), tag); 205 } 206 } 207 208 static void 209 cpu_ipi_wait(const char *s, const kcpuset_t *watchset, const kcpuset_t *wanted) 210 { 211 bool done = false; 212 struct cpu_info * const ci = curcpu(); 213 kcpuset_t *kcp = ci->ci_watchcpus; 214 215 /* some finite amount of time */ 216 for (u_long limit = ci->ci_cpu_freq /* / 10 */; !done && limit--; ) { 217 kcpuset_copy(kcp, watchset); 218 kcpuset_intersect(kcp, wanted); 219 done = kcpuset_match(kcp, wanted); 220 } 221 222 if (!done) { 223 cpuid_t cii; 224 kcpuset_copy(kcp, wanted); 225 kcpuset_remove(kcp, watchset); 226 if ((cii = kcpuset_ffs(kcp)) != 0) { 227 printf("Failed to %s:", s); 228 do { 229 kcpuset_clear(kcp, --cii); 230 printf(" cpu%lu", cii); 231 } while ((cii = kcpuset_ffs(kcp)) != 0); 232 printf("\n"); 233 } 234 } 235 } 236 237 /* 238 * Halt this cpu 239 */ 240 void 241 cpu_halt(void) 242 { 243 cpuid_t cii = cpu_index(curcpu()); 244 245 printf("cpu%lu: shutting down\n", cii); 246 kcpuset_atomic_set(cpus_halted, cii); 247 spl0(); /* allow interrupts e.g. further ipi ? */ 248 for (;;) ; /* spin */ 249 250 /* NOTREACHED */ 251 } 252 253 /* 254 * Halt all running cpus, excluding current cpu. 255 */ 256 void 257 cpu_halt_others(void) 258 { 259 kcpuset_t *kcp; 260 261 // If we are the only CPU running, there's nothing to do. 262 if (kcpuset_match(cpus_running, curcpu()->ci_data.cpu_kcpuset)) 263 return; 264 265 // Get all running CPUs 266 kcpuset_clone(&kcp, cpus_running); 267 // Remove ourself 268 kcpuset_remove(kcp, curcpu()->ci_data.cpu_kcpuset); 269 // Remove any halted CPUs 270 kcpuset_remove(kcp, cpus_halted); 271 // If there are CPUs left, send the IPIs 272 if (!kcpuset_iszero(kcp)) { 273 cpu_multicast_ipi(kcp, IPI_HALT); 274 cpu_ipi_wait("halt", cpus_halted, kcp); 275 } 276 kcpuset_destroy(kcp); 277 278 /* 279 * TBD 280 * Depending on available firmware methods, other cpus will 281 * either shut down themselves, or spin and wait for us to 282 * stop them. 283 */ 284 } 285 286 /* 287 * Pause this cpu 288 */ 289 void 290 cpu_pause(void) 291 { 292 const int s = splhigh(); 293 cpuid_t cii = cpu_index(curcpu()); 294 295 if (__predict_false(cold)) { 296 splx(s); 297 return; 298 } 299 300 do { 301 kcpuset_atomic_set(cpus_paused, cii); 302 do { 303 ; 304 } while (kcpuset_isset(cpus_paused, cii)); 305 kcpuset_atomic_set(cpus_resumed, cii); 306 #if defined(DDB) 307 if (ddb_running_on_this_cpu_p()) 308 cpu_Debugger(); 309 if (ddb_running_on_any_cpu_p()) 310 continue; 311 #endif 312 } while (false); 313 314 splx(s); 315 } 316 317 /* 318 * Pause all running cpus, excluding current cpu. 319 */ 320 void 321 cpu_pause_others(void) 322 { 323 struct cpu_info * const ci = curcpu(); 324 325 if (cold || kcpuset_match(cpus_running, ci->ci_data.cpu_kcpuset)) 326 return; 327 328 kcpuset_t *kcp = ci->ci_ddbcpus; 329 330 kcpuset_copy(kcp, cpus_running); 331 kcpuset_remove(kcp, ci->ci_data.cpu_kcpuset); 332 kcpuset_remove(kcp, cpus_paused); 333 334 cpu_broadcast_ipi(IPI_SUSPEND); 335 cpu_ipi_wait("pause", cpus_paused, kcp); 336 } 337 338 /* 339 * Resume a single cpu 340 */ 341 void 342 cpu_resume(cpuid_t cii) 343 { 344 345 if (__predict_false(cold)) 346 return; 347 348 struct cpu_info * const ci = curcpu(); 349 kcpuset_t *kcp = ci->ci_ddbcpus; 350 351 kcpuset_zero(kcp); 352 kcpuset_set(kcp, cii); 353 kcpuset_atomicly_remove(cpus_resumed, cpus_resumed); 354 kcpuset_atomic_clear(cpus_paused, cii); 355 356 cpu_ipi_wait("resume", cpus_resumed, kcp); 357 } 358 359 /* 360 * Resume all paused cpus. 361 */ 362 void 363 cpu_resume_others(void) 364 { 365 366 if (__predict_false(cold)) 367 return; 368 369 struct cpu_info * const ci = curcpu(); 370 kcpuset_t *kcp = ci->ci_ddbcpus; 371 372 kcpuset_atomicly_remove(cpus_resumed, cpus_resumed); 373 kcpuset_copy(kcp, cpus_paused); 374 kcpuset_atomicly_remove(cpus_paused, cpus_paused); 375 376 /* CPUs awake on cpus_paused clear */ 377 cpu_ipi_wait("resume", cpus_resumed, kcp); 378 } 379 380 bool 381 cpu_is_paused(cpuid_t cii) 382 { 383 384 return !cold && kcpuset_isset(cpus_paused, cii); 385 } 386 387 #ifdef DDB 388 void 389 cpu_debug_dump(void) 390 { 391 CPU_INFO_ITERATOR cii; 392 struct cpu_info *ci; 393 char running, hatched, paused, resumed, halted; 394 db_printf("CPU CPUID STATE CPUINFO CPL INT MTX IPIS(A/R)\n"); 395 for (CPU_INFO_FOREACH(cii, ci)) { 396 hatched = (kcpuset_isset(cpus_hatched, cpu_index(ci)) ? 'H' : '-'); 397 running = (kcpuset_isset(cpus_running, cpu_index(ci)) ? 'R' : '-'); 398 paused = (kcpuset_isset(cpus_paused, cpu_index(ci)) ? 'P' : '-'); 399 resumed = (kcpuset_isset(cpus_resumed, cpu_index(ci)) ? 'r' : '-'); 400 halted = (kcpuset_isset(cpus_halted, cpu_index(ci)) ? 'h' : '-'); 401 db_printf("%3d 0x%03lx%c%c%c%c%c%c%c %p " 402 "%3d %3d %3d 0x%02lx/0x%02lx\n", 403 cpu_index(ci), ci->ci_cpuid, 404 ci == curcpu() ? '<' : ' ', 405 CPU_IS_PRIMARY(ci) ? '*' : ' ', 406 hatched, running, paused, resumed, halted, 407 ci, ci->ci_cpl, ci->ci_intr_depth, ci->ci_mtx_count, 408 ci->ci_active_ipis, ci->ci_request_ipis); 409 } 410 } 411 #endif 412 413 void 414 xc_send_ipi(struct cpu_info *ci) 415 { 416 KASSERT(kpreempt_disabled()); 417 KASSERT(curcpu() != ci); 418 419 cpu_send_ipi(ci, IPI_XCALL); 420 } 421 422 void 423 cpu_ipi(struct cpu_info *ci) 424 { 425 KASSERT(kpreempt_disabled()); 426 KASSERT(curcpu() != ci); 427 428 cpu_send_ipi(ci, IPI_GENERIC); 429 } 430 431 #endif 432 433 void 434 paravirt_membar_sync(void) 435 { 436 437 /* 438 * Store-before-load ordering with respect to matching logic 439 * on the hypervisor side. 440 * 441 * This is the same as membar_sync, but guaranteed never to be 442 * conditionalized or hotpatched away even on uniprocessor 443 * builds and boots -- because under virtualization, we still 444 * have to coordinate with a `device' backed by a hypervisor 445 * that is potentially on another physical CPU even if we 446 * observe only one virtual CPU as the guest. 447 */ 448 __asm volatile("fence rw,rw"); 449 } 450