1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 1996, by Steve Passe 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. The name of the developer may NOT be used to endorse or promote products 13 * derived from this software without specific prior written permission. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <sys/cdefs.h> 29 __FBSDID("$FreeBSD$"); 30 31 #include "opt_acpi.h" 32 #include "opt_apic.h" 33 #include "opt_cpu.h" 34 #include "opt_kstack_pages.h" 35 #include "opt_pmap.h" 36 #include "opt_sched.h" 37 #include "opt_smp.h" 38 39 #if !defined(lint) 40 #if !defined(SMP) 41 #error How did you get here? 42 #endif 43 44 #ifndef DEV_APIC 45 #error The apic device is required for SMP, add "device apic" to your config file. 46 #endif 47 #endif /* not lint */ 48 49 #include <sys/param.h> 50 #include <sys/systm.h> 51 #include <sys/bus.h> 52 #include <sys/cons.h> /* cngetc() */ 53 #include <sys/cpuset.h> 54 #ifdef GPROF 55 #include <sys/gmon.h> 56 #endif 57 #include <sys/kdb.h> 58 #include <sys/kernel.h> 59 #include <sys/ktr.h> 60 #include <sys/lock.h> 61 #include <sys/malloc.h> 62 #include <sys/memrange.h> 63 #include <sys/mutex.h> 64 #include <sys/pcpu.h> 65 #include <sys/proc.h> 66 #include <sys/sched.h> 67 #include <sys/smp.h> 68 #include <sys/sysctl.h> 69 70 #include <vm/vm.h> 71 #include <vm/vm_param.h> 72 #include <vm/pmap.h> 73 #include <vm/vm_kern.h> 74 #include <vm/vm_extern.h> 75 76 #include <x86/apicreg.h> 77 #include <machine/clock.h> 78 #include <machine/cpu.h> 79 #include <machine/cputypes.h> 80 #include <x86/mca.h> 81 #include <machine/md_var.h> 82 #include <machine/pcb.h> 83 #include <machine/psl.h> 84 #include <machine/smp.h> 85 #include <machine/specialreg.h> 86 #include <x86/ucode.h> 87 88 #ifdef DEV_ACPI 89 #include <contrib/dev/acpica/include/acpi.h> 90 #include <dev/acpica/acpivar.h> 91 #endif 92 93 #define WARMBOOT_TARGET 0 94 #define WARMBOOT_OFF (PMAP_MAP_LOW + 0x0467) 95 #define WARMBOOT_SEG (PMAP_MAP_LOW + 0x0469) 96 97 #define CMOS_REG (0x70) 98 #define CMOS_DATA (0x71) 99 #define BIOS_RESET (0x0f) 100 #define BIOS_WARM (0x0a) 101 102 /* 103 * this code MUST be enabled here and in mpboot.s. 104 * it follows the very early stages of AP boot by placing values in CMOS ram. 105 * it NORMALLY will never be needed and thus the primitive method for enabling. 106 * 107 #define CHECK_POINTS 108 */ 109 110 #if defined(CHECK_POINTS) 111 #define CHECK_READ(A) (outb(CMOS_REG, (A)), inb(CMOS_DATA)) 112 #define CHECK_WRITE(A,D) (outb(CMOS_REG, (A)), outb(CMOS_DATA, (D))) 113 114 #define CHECK_INIT(D); \ 115 CHECK_WRITE(0x34, (D)); \ 116 CHECK_WRITE(0x35, (D)); \ 117 CHECK_WRITE(0x36, (D)); \ 118 CHECK_WRITE(0x37, (D)); \ 119 CHECK_WRITE(0x38, (D)); \ 120 CHECK_WRITE(0x39, (D)); 121 122 #define CHECK_PRINT(S); \ 123 printf("%s: %d, %d, %d, %d, %d, %d\n", \ 124 (S), \ 125 CHECK_READ(0x34), \ 126 CHECK_READ(0x35), \ 127 CHECK_READ(0x36), \ 128 CHECK_READ(0x37), \ 129 CHECK_READ(0x38), \ 130 CHECK_READ(0x39)); 131 132 #else /* CHECK_POINTS */ 133 134 #define CHECK_INIT(D) 135 #define CHECK_PRINT(S) 136 #define CHECK_WRITE(A, D) 137 138 #endif /* CHECK_POINTS */ 139 140 /* 141 * Local data and functions. 142 */ 143 144 static void install_ap_tramp(void); 145 static int start_all_aps(void); 146 static int start_ap(int apic_id); 147 148 static char *ap_copyout_buf; 149 static char *ap_tramp_stack_base; 150 151 unsigned int boot_address; 152 153 #define MiB(v) (v ## ULL << 20) 154 155 /* Allocate memory for the AP trampoline. */ 156 void 157 alloc_ap_trampoline(vm_paddr_t *physmap, unsigned int *physmap_idx) 158 { 159 unsigned int i; 160 bool allocated; 161 162 allocated = false; 163 for (i = *physmap_idx; i <= *physmap_idx; i -= 2) { 164 /* 165 * Find a memory region big enough and below the 1MB boundary 166 * for the trampoline code. 167 * NB: needs to be page aligned. 168 */ 169 if (physmap[i] >= MiB(1) || 170 (trunc_page(physmap[i + 1]) - round_page(physmap[i])) < 171 round_page(bootMP_size)) 172 continue; 173 174 allocated = true; 175 /* 176 * Try to steal from the end of the region to mimic previous 177 * behaviour, else fallback to steal from the start. 178 */ 179 if (physmap[i + 1] < MiB(1)) { 180 boot_address = trunc_page(physmap[i + 1]); 181 if ((physmap[i + 1] - boot_address) < bootMP_size) 182 boot_address -= round_page(bootMP_size); 183 physmap[i + 1] = boot_address; 184 } else { 185 boot_address = round_page(physmap[i]); 186 physmap[i] = boot_address + round_page(bootMP_size); 187 } 188 if (physmap[i] == physmap[i + 1] && *physmap_idx != 0) { 189 memmove(&physmap[i], &physmap[i + 2], 190 sizeof(*physmap) * (*physmap_idx - i + 2)); 191 *physmap_idx -= 2; 192 } 193 break; 194 } 195 196 if (!allocated) { 197 boot_address = basemem * 1024 - bootMP_size; 198 if (bootverbose) 199 printf( 200 "Cannot find enough space for the boot trampoline, placing it at %#x", 201 boot_address); 202 } 203 } 204 205 /* 206 * Initialize the IPI handlers and start up the AP's. 207 */ 208 void 209 cpu_mp_start(void) 210 { 211 int i; 212 213 /* Initialize the logical ID to APIC ID table. */ 214 for (i = 0; i < MAXCPU; i++) { 215 cpu_apic_ids[i] = -1; 216 } 217 218 /* Install an inter-CPU IPI for TLB invalidation */ 219 setidt(IPI_INVLTLB, IDTVEC(invltlb), 220 SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); 221 setidt(IPI_INVLPG, IDTVEC(invlpg), 222 SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); 223 setidt(IPI_INVLRNG, IDTVEC(invlrng), 224 SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); 225 226 /* Install an inter-CPU IPI for cache invalidation. */ 227 setidt(IPI_INVLCACHE, IDTVEC(invlcache), 228 SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); 229 230 /* Install an inter-CPU IPI for all-CPU rendezvous */ 231 setidt(IPI_RENDEZVOUS, IDTVEC(rendezvous), 232 SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); 233 234 /* Install generic inter-CPU IPI handler */ 235 setidt(IPI_BITMAP_VECTOR, IDTVEC(ipi_intr_bitmap_handler), 236 SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); 237 238 /* Install an inter-CPU IPI for CPU stop/restart */ 239 setidt(IPI_STOP, IDTVEC(cpustop), 240 SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); 241 242 /* Install an inter-CPU IPI for CPU suspend/resume */ 243 setidt(IPI_SUSPEND, IDTVEC(cpususpend), 244 SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); 245 246 /* Install an IPI for calling delayed SWI */ 247 setidt(IPI_SWI, IDTVEC(ipi_swi), 248 SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); 249 250 /* Set boot_cpu_id if needed. */ 251 if (boot_cpu_id == -1) { 252 boot_cpu_id = PCPU_GET(apic_id); 253 cpu_info[boot_cpu_id].cpu_bsp = 1; 254 } else 255 KASSERT(boot_cpu_id == PCPU_GET(apic_id), 256 ("BSP's APIC ID doesn't match boot_cpu_id")); 257 258 /* Probe logical/physical core configuration. */ 259 topo_probe(); 260 261 assign_cpu_ids(); 262 263 /* Start each Application Processor */ 264 start_all_aps(); 265 266 set_interrupt_apic_ids(); 267 268 #if defined(DEV_ACPI) && MAXMEMDOM > 1 269 acpi_pxm_set_cpu_locality(); 270 #endif 271 } 272 273 /* 274 * AP CPU's call this to initialize themselves. 275 */ 276 void 277 init_secondary(void) 278 { 279 struct pcpu *pc; 280 struct i386tss *common_tssp; 281 struct region_descriptor r_gdt, r_idt; 282 int gsel_tss, myid, x; 283 u_int cr0; 284 285 /* bootAP is set in start_ap() to our ID. */ 286 myid = bootAP; 287 288 /* Update microcode before doing anything else. */ 289 ucode_load_ap(myid); 290 291 /* Get per-cpu data */ 292 pc = &__pcpu[myid]; 293 294 /* prime data page for it to use */ 295 pcpu_init(pc, myid, sizeof(struct pcpu)); 296 dpcpu_init(dpcpu, myid); 297 pc->pc_apic_id = cpu_apic_ids[myid]; 298 pc->pc_prvspace = pc; 299 pc->pc_curthread = 0; 300 pc->pc_common_tssp = common_tssp = &(__pcpu[0].pc_common_tssp)[myid]; 301 302 fix_cpuid(); 303 304 gdt_segs[GPRIV_SEL].ssd_base = (int)pc; 305 gdt_segs[GPROC0_SEL].ssd_base = (int)common_tssp; 306 gdt_segs[GLDT_SEL].ssd_base = (int)ldt; 307 308 for (x = 0; x < NGDT; x++) { 309 ssdtosd(&gdt_segs[x], &gdt[myid * NGDT + x].sd); 310 } 311 312 r_gdt.rd_limit = NGDT * sizeof(gdt[0]) - 1; 313 r_gdt.rd_base = (int) &gdt[myid * NGDT]; 314 lgdt(&r_gdt); /* does magic intra-segment return */ 315 316 r_idt.rd_limit = sizeof(struct gate_descriptor) * NIDT - 1; 317 r_idt.rd_base = (int)idt; 318 lidt(&r_idt); 319 320 lldt(_default_ldt); 321 PCPU_SET(currentldt, _default_ldt); 322 323 PCPU_SET(trampstk, (uintptr_t)ap_tramp_stack_base + TRAMP_STACK_SZ - 324 VM86_STACK_SPACE); 325 326 gsel_tss = GSEL(GPROC0_SEL, SEL_KPL); 327 gdt[myid * NGDT + GPROC0_SEL].sd.sd_type = SDT_SYS386TSS; 328 common_tssp->tss_esp0 = PCPU_GET(trampstk); 329 common_tssp->tss_ss0 = GSEL(GDATA_SEL, SEL_KPL); 330 common_tssp->tss_ioopt = sizeof(struct i386tss) << 16; 331 PCPU_SET(tss_gdt, &gdt[myid * NGDT + GPROC0_SEL].sd); 332 PCPU_SET(common_tssd, *PCPU_GET(tss_gdt)); 333 ltr(gsel_tss); 334 335 PCPU_SET(fsgs_gdt, &gdt[myid * NGDT + GUFS_SEL].sd); 336 PCPU_SET(copyout_buf, ap_copyout_buf); 337 338 /* 339 * Set to a known state: 340 * Set by mpboot.s: CR0_PG, CR0_PE 341 * Set by cpu_setregs: CR0_NE, CR0_MP, CR0_TS, CR0_WP, CR0_AM 342 */ 343 cr0 = rcr0(); 344 cr0 &= ~(CR0_CD | CR0_NW | CR0_EM); 345 load_cr0(cr0); 346 CHECK_WRITE(0x38, 5); 347 348 /* signal our startup to the BSP. */ 349 mp_naps++; 350 CHECK_WRITE(0x39, 6); 351 352 /* Spin until the BSP releases the AP's. */ 353 while (atomic_load_acq_int(&aps_ready) == 0) 354 ia32_pause(); 355 356 /* BSP may have changed PTD while we were waiting */ 357 invltlb(); 358 359 #if defined(I586_CPU) && !defined(NO_F00F_HACK) 360 lidt(&r_idt); 361 #endif 362 363 init_secondary_tail(); 364 } 365 366 /* 367 * start each AP in our list 368 */ 369 #define TMPMAP_START 1 370 static int 371 start_all_aps(void) 372 { 373 u_char mpbiosreason; 374 u_int32_t mpbioswarmvec; 375 int apic_id, cpu; 376 377 mtx_init(&ap_boot_mtx, "ap boot", NULL, MTX_SPIN); 378 379 pmap_remap_lower(true); 380 381 /* install the AP 1st level boot code */ 382 install_ap_tramp(); 383 384 /* save the current value of the warm-start vector */ 385 mpbioswarmvec = *((u_int32_t *) WARMBOOT_OFF); 386 outb(CMOS_REG, BIOS_RESET); 387 mpbiosreason = inb(CMOS_DATA); 388 389 /* take advantage of the P==V mapping for PTD[0] for AP boot */ 390 391 /* start each AP */ 392 for (cpu = 1; cpu < mp_ncpus; cpu++) { 393 apic_id = cpu_apic_ids[cpu]; 394 395 /* allocate and set up a boot stack data page */ 396 bootstacks[cpu] = (char *)kmem_malloc(kstack_pages * PAGE_SIZE, 397 M_WAITOK | M_ZERO); 398 dpcpu = (void *)kmem_malloc(DPCPU_SIZE, M_WAITOK | M_ZERO); 399 /* setup a vector to our boot code */ 400 *((volatile u_short *) WARMBOOT_OFF) = WARMBOOT_TARGET; 401 *((volatile u_short *) WARMBOOT_SEG) = (boot_address >> 4); 402 outb(CMOS_REG, BIOS_RESET); 403 outb(CMOS_DATA, BIOS_WARM); /* 'warm-start' */ 404 405 bootSTK = (char *)bootstacks[cpu] + kstack_pages * 406 PAGE_SIZE - 4; 407 bootAP = cpu; 408 409 ap_tramp_stack_base = pmap_trm_alloc(TRAMP_STACK_SZ, M_NOWAIT); 410 ap_copyout_buf = pmap_trm_alloc(TRAMP_COPYOUT_SZ, M_NOWAIT); 411 412 /* attempt to start the Application Processor */ 413 CHECK_INIT(99); /* setup checkpoints */ 414 if (!start_ap(apic_id)) { 415 printf("AP #%d (PHY# %d) failed!\n", cpu, apic_id); 416 CHECK_PRINT("trace"); /* show checkpoints */ 417 /* better panic as the AP may be running loose */ 418 printf("panic y/n? [y] "); 419 if (cngetc() != 'n') 420 panic("bye-bye"); 421 } 422 CHECK_PRINT("trace"); /* show checkpoints */ 423 424 CPU_SET(cpu, &all_cpus); /* record AP in CPU map */ 425 } 426 427 pmap_remap_lower(false); 428 429 /* restore the warmstart vector */ 430 *(u_int32_t *) WARMBOOT_OFF = mpbioswarmvec; 431 432 outb(CMOS_REG, BIOS_RESET); 433 outb(CMOS_DATA, mpbiosreason); 434 435 /* number of APs actually started */ 436 return mp_naps; 437 } 438 439 /* 440 * load the 1st level AP boot code into base memory. 441 */ 442 443 /* targets for relocation */ 444 extern void bigJump(void); 445 extern void bootCodeSeg(void); 446 extern void bootDataSeg(void); 447 extern void MPentry(void); 448 extern u_int MP_GDT; 449 extern u_int mp_gdtbase; 450 451 static void 452 install_ap_tramp(void) 453 { 454 int x; 455 int size = *(int *) ((u_long) & bootMP_size); 456 vm_offset_t va = boot_address; 457 u_char *src = (u_char *) ((u_long) bootMP); 458 u_char *dst = (u_char *) va; 459 u_int boot_base = (u_int) bootMP; 460 u_int8_t *dst8; 461 u_int16_t *dst16; 462 u_int32_t *dst32; 463 464 KASSERT (size <= PAGE_SIZE, 465 ("'size' do not fit into PAGE_SIZE, as expected.")); 466 pmap_kenter(va, boot_address); 467 pmap_invalidate_page (kernel_pmap, va); 468 for (x = 0; x < size; ++x) 469 *dst++ = *src++; 470 471 /* 472 * modify addresses in code we just moved to basemem. unfortunately we 473 * need fairly detailed info about mpboot.s for this to work. changes 474 * to mpboot.s might require changes here. 475 */ 476 477 /* boot code is located in KERNEL space */ 478 dst = (u_char *) va; 479 480 /* modify the lgdt arg */ 481 dst32 = (u_int32_t *) (dst + ((u_int) & mp_gdtbase - boot_base)); 482 *dst32 = boot_address + ((u_int) & MP_GDT - boot_base); 483 484 /* modify the ljmp target for MPentry() */ 485 dst32 = (u_int32_t *) (dst + ((u_int) bigJump - boot_base) + 1); 486 *dst32 = (u_int)MPentry; 487 488 /* modify the target for boot code segment */ 489 dst16 = (u_int16_t *) (dst + ((u_int) bootCodeSeg - boot_base)); 490 dst8 = (u_int8_t *) (dst16 + 1); 491 *dst16 = (u_int) boot_address & 0xffff; 492 *dst8 = ((u_int) boot_address >> 16) & 0xff; 493 494 /* modify the target for boot data segment */ 495 dst16 = (u_int16_t *) (dst + ((u_int) bootDataSeg - boot_base)); 496 dst8 = (u_int8_t *) (dst16 + 1); 497 *dst16 = (u_int) boot_address & 0xffff; 498 *dst8 = ((u_int) boot_address >> 16) & 0xff; 499 } 500 501 /* 502 * This function starts the AP (application processor) identified 503 * by the APIC ID 'physicalCpu'. It does quite a "song and dance" 504 * to accomplish this. This is necessary because of the nuances 505 * of the different hardware we might encounter. It isn't pretty, 506 * but it seems to work. 507 */ 508 static int 509 start_ap(int apic_id) 510 { 511 int vector, ms; 512 int cpus; 513 514 /* calculate the vector */ 515 vector = (boot_address >> 12) & 0xff; 516 517 /* used as a watchpoint to signal AP startup */ 518 cpus = mp_naps; 519 520 ipi_startup(apic_id, vector); 521 522 /* Wait up to 5 seconds for it to start. */ 523 for (ms = 0; ms < 5000; ms++) { 524 if (mp_naps > cpus) 525 return 1; /* return SUCCESS */ 526 DELAY(1000); 527 } 528 return 0; /* return FAILURE */ 529 } 530 531 /* 532 * Flush the TLB on other CPU's 533 */ 534 535 /* Variables needed for SMP tlb shootdown. */ 536 vm_offset_t smp_tlb_addr1, smp_tlb_addr2; 537 pmap_t smp_tlb_pmap; 538 volatile uint32_t smp_tlb_generation; 539 540 /* 541 * Used by pmap to request cache or TLB invalidation on local and 542 * remote processors. Mask provides the set of remote CPUs which are 543 * to be signalled with the invalidation IPI. Vector specifies which 544 * invalidation IPI is used. As an optimization, the curcpu_cb 545 * callback is invoked on the calling CPU while waiting for remote 546 * CPUs to complete the operation. 547 * 548 * The callback function is called unconditionally on the caller's 549 * underlying processor, even when this processor is not set in the 550 * mask. So, the callback function must be prepared to handle such 551 * spurious invocations. 552 */ 553 static void 554 smp_targeted_tlb_shootdown(cpuset_t mask, u_int vector, pmap_t pmap, 555 vm_offset_t addr1, vm_offset_t addr2, smp_invl_cb_t curcpu_cb) 556 { 557 cpuset_t other_cpus; 558 volatile uint32_t *p_cpudone; 559 uint32_t generation; 560 int cpu; 561 562 /* 563 * It is not necessary to signal other CPUs while booting or 564 * when in the debugger. 565 */ 566 if (kdb_active || KERNEL_PANICKED() || !smp_started) { 567 curcpu_cb(pmap, addr1, addr2); 568 return; 569 } 570 571 sched_pin(); 572 573 /* 574 * Check for other cpus. Return if none. 575 */ 576 if (CPU_ISFULLSET(&mask)) { 577 if (mp_ncpus <= 1) 578 goto nospinexit; 579 } else { 580 CPU_CLR(PCPU_GET(cpuid), &mask); 581 if (CPU_EMPTY(&mask)) 582 goto nospinexit; 583 } 584 585 KASSERT((read_eflags() & PSL_I) != 0, 586 ("smp_targeted_tlb_shootdown: interrupts disabled")); 587 mtx_lock_spin(&smp_ipi_mtx); 588 smp_tlb_addr1 = addr1; 589 smp_tlb_addr2 = addr2; 590 smp_tlb_pmap = pmap; 591 generation = ++smp_tlb_generation; 592 if (CPU_ISFULLSET(&mask)) { 593 ipi_all_but_self(vector); 594 other_cpus = all_cpus; 595 CPU_CLR(PCPU_GET(cpuid), &other_cpus); 596 } else { 597 other_cpus = mask; 598 ipi_selected(mask, vector); 599 } 600 curcpu_cb(pmap, addr1, addr2); 601 CPU_FOREACH_ISSET(cpu, &other_cpus) { 602 p_cpudone = &cpuid_to_pcpu[cpu]->pc_smp_tlb_done; 603 while (*p_cpudone != generation) 604 ia32_pause(); 605 } 606 mtx_unlock_spin(&smp_ipi_mtx); 607 sched_unpin(); 608 return; 609 610 nospinexit: 611 curcpu_cb(pmap, addr1, addr2); 612 sched_unpin(); 613 } 614 615 void 616 smp_masked_invltlb(cpuset_t mask, pmap_t pmap, smp_invl_cb_t curcpu_cb) 617 { 618 smp_targeted_tlb_shootdown(mask, IPI_INVLTLB, pmap, 0, 0, curcpu_cb); 619 #ifdef COUNT_XINVLTLB_HITS 620 ipi_global++; 621 #endif 622 } 623 624 void 625 smp_masked_invlpg(cpuset_t mask, vm_offset_t addr, pmap_t pmap, 626 smp_invl_cb_t curcpu_cb) 627 { 628 smp_targeted_tlb_shootdown(mask, IPI_INVLPG, pmap, addr, 0, curcpu_cb); 629 #ifdef COUNT_XINVLTLB_HITS 630 ipi_page++; 631 #endif 632 } 633 634 void 635 smp_masked_invlpg_range(cpuset_t mask, vm_offset_t addr1, vm_offset_t addr2, 636 pmap_t pmap, smp_invl_cb_t curcpu_cb) 637 { 638 smp_targeted_tlb_shootdown(mask, IPI_INVLRNG, pmap, addr1, addr2, 639 curcpu_cb); 640 #ifdef COUNT_XINVLTLB_HITS 641 ipi_range++; 642 ipi_range_size += (addr2 - addr1) / PAGE_SIZE; 643 #endif 644 } 645 646 void 647 smp_cache_flush(smp_invl_cb_t curcpu_cb) 648 { 649 smp_targeted_tlb_shootdown(all_cpus, IPI_INVLCACHE, NULL, 0, 0, 650 curcpu_cb); 651 } 652 653 /* 654 * Handlers for TLB related IPIs 655 */ 656 void 657 invltlb_handler(void) 658 { 659 uint32_t generation; 660 661 #ifdef COUNT_XINVLTLB_HITS 662 xhits_gbl[PCPU_GET(cpuid)]++; 663 #endif /* COUNT_XINVLTLB_HITS */ 664 #ifdef COUNT_IPIS 665 (*ipi_invltlb_counts[PCPU_GET(cpuid)])++; 666 #endif /* COUNT_IPIS */ 667 668 /* 669 * Reading the generation here allows greater parallelism 670 * since invalidating the TLB is a serializing operation. 671 */ 672 generation = smp_tlb_generation; 673 if (smp_tlb_pmap == kernel_pmap) 674 invltlb_glob(); 675 PCPU_SET(smp_tlb_done, generation); 676 } 677 678 void 679 invlpg_handler(void) 680 { 681 uint32_t generation; 682 683 #ifdef COUNT_XINVLTLB_HITS 684 xhits_pg[PCPU_GET(cpuid)]++; 685 #endif /* COUNT_XINVLTLB_HITS */ 686 #ifdef COUNT_IPIS 687 (*ipi_invlpg_counts[PCPU_GET(cpuid)])++; 688 #endif /* COUNT_IPIS */ 689 690 generation = smp_tlb_generation; /* Overlap with serialization */ 691 if (smp_tlb_pmap == kernel_pmap) 692 invlpg(smp_tlb_addr1); 693 PCPU_SET(smp_tlb_done, generation); 694 } 695 696 void 697 invlrng_handler(void) 698 { 699 vm_offset_t addr, addr2; 700 uint32_t generation; 701 702 #ifdef COUNT_XINVLTLB_HITS 703 xhits_rng[PCPU_GET(cpuid)]++; 704 #endif /* COUNT_XINVLTLB_HITS */ 705 #ifdef COUNT_IPIS 706 (*ipi_invlrng_counts[PCPU_GET(cpuid)])++; 707 #endif /* COUNT_IPIS */ 708 709 addr = smp_tlb_addr1; 710 addr2 = smp_tlb_addr2; 711 generation = smp_tlb_generation; /* Overlap with serialization */ 712 if (smp_tlb_pmap == kernel_pmap) { 713 do { 714 invlpg(addr); 715 addr += PAGE_SIZE; 716 } while (addr < addr2); 717 } 718 719 PCPU_SET(smp_tlb_done, generation); 720 } 721 722 void 723 invlcache_handler(void) 724 { 725 uint32_t generation; 726 727 #ifdef COUNT_IPIS 728 (*ipi_invlcache_counts[PCPU_GET(cpuid)])++; 729 #endif /* COUNT_IPIS */ 730 731 /* 732 * Reading the generation here allows greater parallelism 733 * since wbinvd is a serializing instruction. Without the 734 * temporary, we'd wait for wbinvd to complete, then the read 735 * would execute, then the dependent write, which must then 736 * complete before return from interrupt. 737 */ 738 generation = smp_tlb_generation; 739 wbinvd(); 740 PCPU_SET(smp_tlb_done, generation); 741 } 742