1 /* $NetBSD: ipifuncs.c,v 1.57 2022/05/03 20:52:31 andvar Exp $ */ 2 3 /*- 4 * Copyright (c) 2004 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: ipifuncs.c,v 1.57 2022/05/03 20:52:31 andvar Exp $"); 31 32 #include "opt_ddb.h" 33 34 #include <sys/param.h> 35 #include <sys/systm.h> 36 #include <sys/xcall.h> 37 #include <sys/ipi.h> 38 39 #include <machine/db_machdep.h> 40 41 #include <machine/cpu.h> 42 #include <machine/cpu_counter.h> 43 #include <machine/ctlreg.h> 44 #include <machine/pmap.h> 45 #include <machine/sparc64.h> 46 47 #include <sparc64/sparc64/cache.h> 48 49 #if defined(DDB) || defined(KGDB) 50 #ifdef DDB 51 #include <ddb/db_command.h> 52 #include <ddb/db_output.h> 53 #endif 54 #endif 55 56 #define SPARC64_IPI_RETRIES 10000 57 58 /* CPU sets containing halted, paused and resumed cpus */ 59 static volatile sparc64_cpuset_t cpus_halted; 60 static volatile sparc64_cpuset_t cpus_spinning; 61 static volatile sparc64_cpuset_t cpus_paused; 62 static volatile sparc64_cpuset_t cpus_resumed; 63 64 /* IPI handlers. */ 65 static int sparc64_ipi_wait(sparc64_cpuset_t volatile *, sparc64_cpuset_t); 66 static void sparc64_ipi_error(const char *, sparc64_cpuset_t, sparc64_cpuset_t); 67 68 /* Send IPI functions for supported platforms */ 69 static void sparc64_send_ipi_sun4u(int, ipifunc_t, uint64_t, uint64_t); 70 static void sparc64_send_ipi_sun4v(int, ipifunc_t, uint64_t, uint64_t); 71 void (*sparc64_send_ipi)(int, ipifunc_t, uint64_t, uint64_t) = NULL; 72 73 /* 74 * These are the "function" entry points in locore.s/mp_subr.s to handle IPI's. 75 */ 76 void sparc64_ipi_halt(void *, void *); 77 void sparc64_ipi_pause(void *, void *); 78 void sparc64_ipi_flush_pte_us(void *, void *); 79 void sparc64_ipi_flush_pte_usiii(void *, void *); 80 void sparc64_ipi_flush_pte_sun4v(void *, void *); 81 void sparc64_ipi_dcache_flush_page_us(void *, void *); 82 void sparc64_ipi_dcache_flush_page_usiii(void *, void *); 83 void sparc64_ipi_dcache_flush_page_sun4v(void *, void *); 84 void sparc64_ipi_blast_dcache(void *, void *); 85 void sparc64_ipi_ccall(void *, void *); 86 87 /* Function pointers to be setup in sparc64_ipi_init() */ 88 static ipifunc_t smp_tlb_flush_pte_func = NULL; 89 static ipifunc_t sparc64_ipi_dcache_flush_page_func = NULL; 90 91 /* 92 * Process cpu stop-self event. 93 */ 94 void 95 sparc64_ipi_halt_thiscpu(void *arg, void *arg2) 96 { 97 extern void prom_printf(const char *fmt, ...); 98 99 printf("cpu%d: shutting down\n", cpu_number()); 100 if (prom_has_stop_other() || !prom_has_stopself()) { 101 /* 102 * just loop here, the final cpu will stop us later 103 */ 104 CPUSET_ADD(cpus_spinning, cpu_number()); 105 CPUSET_ADD(cpus_halted, cpu_number()); 106 spl0(); 107 while (1) 108 /* nothing */; 109 } else { 110 CPUSET_ADD(cpus_halted, cpu_number()); 111 prom_stopself(); 112 } 113 } 114 115 void 116 sparc64_do_pause(void) 117 { 118 #if defined(DDB) 119 extern bool ddb_running_on_this_cpu(void); 120 extern void db_resume_others(void); 121 #endif 122 123 CPUSET_ADD(cpus_paused, cpu_number()); 124 125 do { 126 membar_Sync(); 127 } while(CPUSET_HAS(cpus_paused, cpu_number())); 128 membar_Sync(); 129 CPUSET_ADD(cpus_resumed, cpu_number()); 130 131 #if defined(DDB) 132 if (ddb_running_on_this_cpu()) { 133 db_command_loop(); 134 db_resume_others(); 135 } 136 #endif 137 } 138 139 /* 140 * Pause cpu. This is called from locore.s after setting up a trapframe. 141 */ 142 void 143 sparc64_ipi_pause_thiscpu(void *arg) 144 { 145 int s; 146 #if defined(DDB) 147 extern void fill_ddb_regs_from_tf(struct trapframe64 *tf); 148 extern void ddb_restore_state(void); 149 150 if (arg) 151 fill_ddb_regs_from_tf(arg); 152 #endif 153 154 s = intr_disable(); 155 sparc64_do_pause(); 156 157 #if defined(DDB) 158 if (arg) { 159 ddb_restore_state(); 160 curcpu()->ci_ddb_regs = NULL; 161 } 162 #endif 163 164 intr_restore(s); 165 } 166 167 /* 168 * Initialize IPI machinery. 169 */ 170 void 171 sparc64_ipi_init(void) 172 { 173 174 /* Clear all cpu sets. */ 175 CPUSET_CLEAR(cpus_halted); 176 CPUSET_CLEAR(cpus_spinning); 177 CPUSET_CLEAR(cpus_paused); 178 CPUSET_CLEAR(cpus_resumed); 179 180 /* 181 * Prepare cpu type dependent function pointers 182 */ 183 184 if (CPU_ISSUN4V) { 185 smp_tlb_flush_pte_func = sparc64_ipi_flush_pte_sun4v; 186 sparc64_ipi_dcache_flush_page_func = 187 sparc64_ipi_dcache_flush_page_sun4v; 188 } 189 else if (CPU_IS_USIII_UP()) { 190 smp_tlb_flush_pte_func = sparc64_ipi_flush_pte_usiii; 191 sparc64_ipi_dcache_flush_page_func = 192 sparc64_ipi_dcache_flush_page_usiii; 193 } 194 else { 195 smp_tlb_flush_pte_func = sparc64_ipi_flush_pte_us; 196 sparc64_ipi_dcache_flush_page_func = 197 sparc64_ipi_dcache_flush_page_us; 198 } 199 200 if (CPU_ISSUN4V) 201 sparc64_send_ipi = sparc64_send_ipi_sun4v; 202 else 203 sparc64_send_ipi = sparc64_send_ipi_sun4u; 204 205 } 206 207 /* 208 * Send an IPI to all in the list but ourselves. 209 */ 210 void 211 sparc64_multicast_ipi(sparc64_cpuset_t cpuset, ipifunc_t func, uint64_t arg1, 212 uint64_t arg2) 213 { 214 struct cpu_info *ci; 215 216 CPUSET_DEL(cpuset, cpu_number()); 217 if (CPUSET_EMPTY(cpuset)) 218 return; 219 220 for (ci = cpus; ci != NULL; ci = ci->ci_next) { 221 if (CPUSET_HAS(cpuset, ci->ci_index)) { 222 CPUSET_DEL(cpuset, ci->ci_index); 223 sparc64_send_ipi(ci->ci_cpuid, func, arg1, arg2); 224 } 225 } 226 } 227 228 /* 229 * Broadcast an IPI to all but ourselves. 230 */ 231 void 232 sparc64_broadcast_ipi(ipifunc_t func, uint64_t arg1, uint64_t arg2) 233 { 234 235 sparc64_multicast_ipi(CPUSET_EXCEPT(cpus_active, cpu_number()), func, 236 arg1, arg2); 237 } 238 239 /* 240 * Send an interprocessor interrupt - sun4u. 241 */ 242 void 243 sparc64_send_ipi_sun4u(int upaid, ipifunc_t func, uint64_t arg1, uint64_t arg2) 244 { 245 int i, ik, shift = 0; 246 uint64_t intr_func; 247 248 KASSERT(upaid != curcpu()->ci_cpuid); 249 250 /* 251 * UltraSPARC-IIIi CPUs select the BUSY/NACK pair based on the 252 * lower two bits of the ITID. 253 */ 254 if (CPU_IS_USIIIi()) 255 shift = (upaid & 0x3) * 2; 256 257 if (ldxa(0, ASI_IDSR) & (IDSR_BUSY << shift)) 258 panic("recursive IPI?"); 259 260 intr_func = (uint64_t)(u_long)func; 261 262 /* Schedule an interrupt. */ 263 for (i = 0; i < 10000; i++) { 264 int s = intr_disable(); 265 266 stxa(IDDR_0H, ASI_INTERRUPT_DISPATCH, intr_func); 267 stxa(IDDR_1H, ASI_INTERRUPT_DISPATCH, arg1); 268 stxa(IDDR_2H, ASI_INTERRUPT_DISPATCH, arg2); 269 stxa(IDCR(upaid), ASI_INTERRUPT_DISPATCH, 0); 270 membar_Sync(); 271 /* Workaround for SpitFire erratum #54, from FreeBSD */ 272 if (CPU_IS_SPITFIRE()) { 273 (void)ldxa(P_DCR_0, ASI_INTERRUPT_RECEIVE_DATA); 274 membar_Sync(); 275 } 276 277 for (ik = 0; ik < 1000000; ik++) { 278 if (ldxa(0, ASI_IDSR) & (IDSR_BUSY << shift)) 279 continue; 280 else 281 break; 282 } 283 intr_restore(s); 284 285 if (ik == 1000000) 286 break; 287 288 if ((ldxa(0, ASI_IDSR) & (IDSR_NACK << shift)) == 0) 289 return; 290 /* 291 * Wait for a while with enabling interrupts to avoid 292 * deadlocks. XXX - random value is better. 293 */ 294 DELAY(1); 295 } 296 297 if (panicstr == NULL) 298 panic("cpu%d: ipi_send: couldn't send ipi to UPAID %u" 299 " (tried %d times)", cpu_number(), upaid, i); 300 } 301 302 /* 303 * Send an interprocessor interrupt - sun4v. 304 */ 305 void 306 sparc64_send_ipi_sun4v(int cpuid, ipifunc_t func, uint64_t arg1, uint64_t arg2) 307 { 308 struct cpu_info *ci = curcpu(); 309 int err, i; 310 311 stha(ci->ci_cpuset, ASI_PHYS_CACHED, cpuid); 312 stxa(ci->ci_mondo, ASI_PHYS_CACHED, (vaddr_t)func); 313 stxa(ci->ci_mondo + 8, ASI_PHYS_CACHED, arg1); 314 stxa(ci->ci_mondo + 16, ASI_PHYS_CACHED, arg2); 315 316 for (i = 0; i < SPARC64_IPI_RETRIES; i++) { 317 err = hv_cpu_mondo_send(1, ci->ci_cpuset, ci->ci_mondo); 318 if (err != H_EWOULDBLOCK) 319 break; 320 delay(10); 321 } 322 if (err != H_EOK) 323 panic("Unable to send mondo %lx to cpu %d: %d", 324 (long unsigned int)func, cpuid, err); 325 } 326 327 /* 328 * Wait for IPI operation to complete. 329 * Return 0 on success. 330 */ 331 int 332 sparc64_ipi_wait(sparc64_cpuset_t volatile *cpus_watchset, sparc64_cpuset_t cpus_mask) 333 { 334 uint64_t limit = gettick() + cpu_frequency(curcpu()); 335 336 while (gettick() < limit) { 337 membar_Sync(); 338 if (CPUSET_EQUAL(*cpus_watchset, cpus_mask)) 339 return 0; 340 } 341 return 1; 342 } 343 344 /* 345 * Halt all cpus but ourselves. 346 */ 347 void 348 mp_halt_cpus(void) 349 { 350 sparc64_cpuset_t cpumask, cpuset; 351 struct cpu_info *ci; 352 353 CPUSET_ASSIGN(cpuset, cpus_active); 354 CPUSET_DEL(cpuset, cpu_number()); 355 CPUSET_ASSIGN(cpumask, cpuset); 356 CPUSET_SUB(cpuset, cpus_halted); 357 358 if (CPUSET_EMPTY(cpuset)) 359 return; 360 361 CPUSET_CLEAR(cpus_spinning); 362 sparc64_multicast_ipi(cpuset, sparc64_ipi_halt, 0, 0); 363 if (sparc64_ipi_wait(&cpus_halted, cpumask)) 364 sparc64_ipi_error("halt", cpumask, cpus_halted); 365 366 /* 367 * Depending on available firmware methods, other cpus will 368 * either shut down themselves, or spin and wait for us to 369 * stop them. 370 */ 371 if (CPUSET_EMPTY(cpus_spinning)) { 372 /* give other cpus a few cycles to actually power down */ 373 delay(10000); 374 return; 375 } 376 /* there are cpus spinning - shut them down if we can */ 377 if (prom_has_stop_other()) { 378 for (ci = cpus; ci != NULL; ci = ci->ci_next) { 379 if (!CPUSET_HAS(cpus_spinning, ci->ci_index)) continue; 380 prom_stop_other(ci->ci_cpuid); 381 } 382 } 383 } 384 385 /* 386 * Pause all cpus but ourselves. 387 */ 388 void 389 mp_pause_cpus(void) 390 { 391 int i = 3; 392 sparc64_cpuset_t cpuset; 393 394 CPUSET_ASSIGN(cpuset, cpus_active); 395 CPUSET_DEL(cpuset, cpu_number()); 396 while (i-- > 0) { 397 if (CPUSET_EMPTY(cpuset)) 398 return; 399 400 sparc64_multicast_ipi(cpuset, sparc64_ipi_pause, 0, 0); 401 if (!sparc64_ipi_wait(&cpus_paused, cpuset)) 402 return; 403 CPUSET_SUB(cpuset, cpus_paused); 404 } 405 sparc64_ipi_error("pause", cpus_paused, cpuset); 406 } 407 408 /* 409 * Resume a single cpu 410 */ 411 void 412 mp_resume_cpu(int cno) 413 { 414 CPUSET_DEL(cpus_paused, cno); 415 membar_Sync(); 416 } 417 418 /* 419 * Resume all paused cpus. 420 */ 421 void 422 mp_resume_cpus(void) 423 { 424 int i = 3; 425 sparc64_cpuset_t cpuset; 426 427 CPUSET_CLEAR(cpuset); /* XXX: gcc -Wuninitialized */ 428 429 while (i-- > 0) { 430 CPUSET_CLEAR(cpus_resumed); 431 CPUSET_ASSIGN(cpuset, cpus_paused); 432 membar_Sync(); 433 CPUSET_CLEAR(cpus_paused); 434 435 /* CPUs awake on cpus_paused clear */ 436 if (!sparc64_ipi_wait(&cpus_resumed, cpuset)) 437 return; 438 } 439 sparc64_ipi_error("resume", cpus_resumed, cpuset); 440 } 441 442 int 443 mp_cpu_is_paused(sparc64_cpuset_t cpunum) 444 { 445 446 return CPUSET_HAS(cpus_paused, cpunum); 447 } 448 449 /* 450 * Flush pte on all active processors. 451 */ 452 void 453 smp_tlb_flush_pte(vaddr_t va, struct pmap * pm) 454 { 455 sparc64_cpuset_t cpuset; 456 struct cpu_info *ci; 457 int ctx; 458 bool kpm = (pm == pmap_kernel()); 459 /* Flush our own TLB */ 460 ctx = pm->pm_ctx[cpu_number()]; 461 KASSERT(ctx >= 0); 462 if (kpm || ctx > 0) 463 sp_tlb_flush_pte(va, ctx); 464 465 CPUSET_ASSIGN(cpuset, cpus_active); 466 CPUSET_DEL(cpuset, cpu_number()); 467 if (CPUSET_EMPTY(cpuset)) 468 return; 469 470 /* Flush others */ 471 for (ci = cpus; ci != NULL; ci = ci->ci_next) { 472 if (CPUSET_HAS(cpuset, ci->ci_index)) { 473 CPUSET_DEL(cpuset, ci->ci_index); 474 ctx = pm->pm_ctx[ci->ci_index]; 475 KASSERT(ctx >= 0); 476 if (!kpm && ctx == 0) 477 continue; 478 sparc64_send_ipi(ci->ci_cpuid, smp_tlb_flush_pte_func, va, ctx); 479 } 480 } 481 } 482 483 /* 484 * Make sure this page is flushed from all/some CPUs. 485 */ 486 void 487 smp_dcache_flush_page_cpuset(paddr_t pa, sparc64_cpuset_t activecpus) 488 { 489 sparc64_multicast_ipi(activecpus, sparc64_ipi_dcache_flush_page_func, pa, dcache_line_size); 490 sp_dcache_flush_page(pa); 491 } 492 493 void 494 smp_dcache_flush_page_allcpu(paddr_t pa) 495 { 496 497 smp_dcache_flush_page_cpuset(pa, cpus_active); 498 } 499 500 /* 501 * Flush the D$ on all CPUs. 502 */ 503 void 504 smp_blast_dcache(void) 505 { 506 507 sparc64_multicast_ipi(cpus_active, sparc64_ipi_blast_dcache, 508 dcache_size, dcache_line_size); 509 sp_blast_dcache(dcache_size, dcache_line_size); 510 } 511 512 /* 513 * Print an error message. 514 */ 515 void 516 sparc64_ipi_error(const char *s, sparc64_cpuset_t cpus_succeeded, 517 sparc64_cpuset_t cpus_expected) 518 { 519 int cpuid; 520 521 CPUSET_DEL(cpus_expected, cpus_succeeded); 522 if (!CPUSET_EMPTY(cpus_expected)) { 523 printf("Failed to %s:", s); 524 do { 525 cpuid = CPUSET_NEXT(cpus_expected); 526 CPUSET_DEL(cpus_expected, cpuid); 527 printf(" cpu%d", cpuid); 528 } while(!CPUSET_EMPTY(cpus_expected)); 529 } 530 531 printf("\n"); 532 } 533 534 /* 535 * MD support for xcall(9) interface. 536 */ 537 538 void 539 sparc64_generic_xcall(struct cpu_info *target, ipi_c_call_func_t func, 540 void *arg) 541 { 542 /* if target == NULL broadcast to everything but curcpu */ 543 if (target) 544 sparc64_send_ipi(target->ci_cpuid, sparc64_ipi_ccall, 545 (uint64_t)(uintptr_t)func, (uint64_t)(uintptr_t)arg); 546 else { 547 548 sparc64_multicast_ipi(cpus_active, sparc64_ipi_ccall, 549 (uint64_t)(uintptr_t)func, (uint64_t)(uintptr_t)arg); 550 } 551 } 552 553 void 554 xc_send_ipi(struct cpu_info *target) 555 { 556 557 sparc64_generic_xcall(target, (ipi_c_call_func_t)xc_ipi_handler, NULL); 558 } 559 560 void 561 cpu_ipi(struct cpu_info *target) 562 { 563 564 sparc64_generic_xcall(target, (ipi_c_call_func_t)ipi_cpu_handler, NULL); 565 } 566