1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 /* 26 * Copyright (c) 2010, Intel Corporation. 27 * All rights reserved. 28 */ 29 /* 30 * Copyright 2013 Nexenta Systems, Inc. All rights reserved. 31 */ 32 33 #include <sys/processor.h> 34 #include <sys/time.h> 35 #include <sys/psm.h> 36 #include <sys/smp_impldefs.h> 37 #include <sys/cram.h> 38 #include <sys/acpi/acpi.h> 39 #include <sys/acpica.h> 40 #include <sys/psm_common.h> 41 #include <sys/pit.h> 42 #include <sys/ddi.h> 43 #include <sys/sunddi.h> 44 #include <sys/ddi_impldefs.h> 45 #include <sys/pci.h> 46 #include <sys/promif.h> 47 #include <sys/x86_archext.h> 48 #include <sys/cpc_impl.h> 49 #include <sys/uadmin.h> 50 #include <sys/panic.h> 51 #include <sys/debug.h> 52 #include <sys/archsystm.h> 53 #include <sys/trap.h> 54 #include <sys/machsystm.h> 55 #include <sys/sysmacros.h> 56 #include <sys/cpuvar.h> 57 #include <sys/rm_platter.h> 58 #include <sys/privregs.h> 59 #include <sys/note.h> 60 #include <sys/pci_intr_lib.h> 61 #include <sys/spl.h> 62 #include <sys/clock.h> 63 #include <sys/dditypes.h> 64 #include <sys/sunddi.h> 65 #include <sys/x_call.h> 66 #include <sys/reboot.h> 67 #include <sys/apix.h> 68 69 static int apix_get_avail_vector_oncpu(uint32_t, int, int); 70 static apix_vector_t *apix_init_vector(processorid_t, uchar_t); 71 static void apix_cleanup_vector(apix_vector_t *); 72 static void apix_insert_av(apix_vector_t *, void *, avfunc, caddr_t, caddr_t, 73 uint64_t *, int, dev_info_t *); 74 static void apix_remove_av(apix_vector_t *, struct autovec *); 75 static void apix_clear_dev_map(dev_info_t *, int, int); 76 static boolean_t apix_is_cpu_enabled(processorid_t); 77 static void apix_wait_till_seen(processorid_t, int); 78 79 #define GET_INTR_INUM(ihdlp) \ 80 (((ihdlp) != NULL) ? ((ddi_intr_handle_impl_t *)(ihdlp))->ih_inum : 0) 81 82 apix_rebind_info_t apix_rebindinfo = {0, 0, 0, NULL, 0, NULL}; 83 84 /* 85 * Allocate IPI 86 * 87 * Return vector number or 0 on error 88 */ 89 uchar_t 90 apix_alloc_ipi(int ipl) 91 { 92 apix_vector_t *vecp; 93 uchar_t vector; 94 int cpun; 95 int nproc; 96 97 APIX_ENTER_CPU_LOCK(0); 98 99 vector = apix_get_avail_vector_oncpu(0, APIX_IPI_MIN, APIX_IPI_MAX); 100 if (vector == 0) { 101 APIX_LEAVE_CPU_LOCK(0); 102 cmn_err(CE_WARN, "apix: no available IPI\n"); 103 apic_error |= APIC_ERR_GET_IPIVECT_FAIL; 104 return (0); 105 } 106 107 nproc = max(apic_nproc, apic_max_nproc); 108 for (cpun = 0; cpun < nproc; cpun++) { 109 vecp = xv_vector(cpun, vector); 110 if (vecp == NULL) { 111 vecp = kmem_zalloc(sizeof (apix_vector_t), KM_NOSLEEP); 112 if (vecp == NULL) { 113 cmn_err(CE_WARN, "apix: No memory for ipi"); 114 goto fail; 115 } 116 xv_vector(cpun, vector) = vecp; 117 } 118 vecp->v_state = APIX_STATE_ALLOCED; 119 vecp->v_type = APIX_TYPE_IPI; 120 vecp->v_cpuid = vecp->v_bound_cpuid = cpun; 121 vecp->v_vector = vector; 122 vecp->v_pri = ipl; 123 } 124 APIX_LEAVE_CPU_LOCK(0); 125 return (vector); 126 127 fail: 128 while (--cpun >= 0) 129 apix_cleanup_vector(xv_vector(cpun, vector)); 130 APIX_LEAVE_CPU_LOCK(0); 131 return (0); 132 } 133 134 /* 135 * Add IPI service routine 136 */ 137 static int 138 apix_add_ipi(int ipl, avfunc xxintr, char *name, int vector, 139 caddr_t arg1, caddr_t arg2) 140 { 141 int cpun; 142 apix_vector_t *vecp; 143 int nproc; 144 145 ASSERT(vector >= APIX_IPI_MIN && vector <= APIX_IPI_MAX); 146 147 nproc = max(apic_nproc, apic_max_nproc); 148 for (cpun = 0; cpun < nproc; cpun++) { 149 APIX_ENTER_CPU_LOCK(cpun); 150 vecp = xv_vector(cpun, vector); 151 apix_insert_av(vecp, NULL, xxintr, arg1, arg2, NULL, ipl, NULL); 152 vecp->v_state = APIX_STATE_ENABLED; 153 APIX_LEAVE_CPU_LOCK(cpun); 154 } 155 156 APIC_VERBOSE(IPI, (CE_CONT, "apix: add ipi for %s, vector %x " 157 "ipl %x\n", name, vector, ipl)); 158 159 return (1); 160 } 161 162 /* 163 * Find and return first free vector in range (start, end) 164 */ 165 static int 166 apix_get_avail_vector_oncpu(uint32_t cpuid, int start, int end) 167 { 168 int i; 169 apix_impl_t *apixp = apixs[cpuid]; 170 171 for (i = start; i <= end; i++) { 172 if (APIC_CHECK_RESERVE_VECTORS(i)) 173 continue; 174 if (IS_VECT_FREE(apixp->x_vectbl[i])) 175 return (i); 176 } 177 178 return (0); 179 } 180 181 /* 182 * Allocate a vector on specified cpu 183 * 184 * Return NULL on error 185 */ 186 static apix_vector_t * 187 apix_alloc_vector_oncpu(uint32_t cpuid, dev_info_t *dip, int inum, int type) 188 { 189 processorid_t tocpu = cpuid & ~IRQ_USER_BOUND; 190 apix_vector_t *vecp; 191 int vector; 192 193 ASSERT(APIX_CPU_LOCK_HELD(tocpu)); 194 195 /* find free vector */ 196 vector = apix_get_avail_vector_oncpu(tocpu, APIX_AVINTR_MIN, 197 APIX_AVINTR_MAX); 198 if (vector == 0) 199 return (NULL); 200 201 vecp = apix_init_vector(tocpu, vector); 202 vecp->v_type = (ushort_t)type; 203 vecp->v_inum = inum; 204 vecp->v_flags = (cpuid & IRQ_USER_BOUND) ? APIX_VECT_USER_BOUND : 0; 205 206 if (dip != NULL) 207 apix_set_dev_map(vecp, dip, inum); 208 209 return (vecp); 210 } 211 212 /* 213 * Allocates "count" contiguous MSI vectors starting at the proper alignment. 214 * Caller needs to make sure that count has to be power of 2 and should not 215 * be < 1. 216 * 217 * Return first vector number 218 */ 219 apix_vector_t * 220 apix_alloc_nvectors_oncpu(uint32_t cpuid, dev_info_t *dip, int inum, 221 int count, int type) 222 { 223 int i, msibits, start = 0, navail = 0; 224 apix_vector_t *vecp, *startp = NULL; 225 processorid_t tocpu = cpuid & ~IRQ_USER_BOUND; 226 uint_t flags; 227 228 ASSERT(APIX_CPU_LOCK_HELD(tocpu)); 229 230 /* 231 * msibits is the no. of lower order message data bits for the 232 * allocated MSI vectors and is used to calculate the aligned 233 * starting vector 234 */ 235 msibits = count - 1; 236 237 /* It has to be contiguous */ 238 for (i = APIX_AVINTR_MIN; i <= APIX_AVINTR_MAX; i++) { 239 if (!IS_VECT_FREE(xv_vector(tocpu, i))) 240 continue; 241 242 /* 243 * starting vector has to be aligned accordingly for 244 * multiple MSIs 245 */ 246 if (msibits) 247 i = (i + msibits) & ~msibits; 248 249 for (navail = 0, start = i; i <= APIX_AVINTR_MAX; i++) { 250 if (!IS_VECT_FREE(xv_vector(tocpu, i))) 251 break; 252 if (APIC_CHECK_RESERVE_VECTORS(i)) 253 break; 254 if (++navail == count) 255 goto done; 256 } 257 } 258 259 return (NULL); 260 261 done: 262 flags = (cpuid & IRQ_USER_BOUND) ? APIX_VECT_USER_BOUND : 0; 263 264 for (i = 0; i < count; i++) { 265 if ((vecp = apix_init_vector(tocpu, start + i)) == NULL) 266 goto fail; 267 268 vecp->v_type = (ushort_t)type; 269 vecp->v_inum = inum + i; 270 vecp->v_flags = flags; 271 272 if (dip != NULL) 273 apix_set_dev_map(vecp, dip, inum + i); 274 275 if (i == 0) 276 startp = vecp; 277 } 278 279 return (startp); 280 281 fail: 282 while (i-- > 0) { /* Free allocated vectors */ 283 vecp = xv_vector(tocpu, start + i); 284 apix_clear_dev_map(dip, inum + i, type); 285 apix_cleanup_vector(vecp); 286 } 287 return (NULL); 288 } 289 290 #define APIX_WRITE_MSI_DATA(_hdl, _cap, _ctrl, _v)\ 291 do {\ 292 if ((_ctrl) & PCI_MSI_64BIT_MASK)\ 293 pci_config_put16((_hdl), (_cap) + PCI_MSI_64BIT_DATA, (_v));\ 294 else\ 295 pci_config_put16((_hdl), (_cap) + PCI_MSI_32BIT_DATA, (_v));\ 296 _NOTE(CONSTCOND)} while (0) 297 298 static void 299 apix_pci_msi_enable_vector(apix_vector_t *vecp, dev_info_t *dip, int type, 300 int inum, int count, uchar_t vector, int target_apic_id) 301 { 302 uint64_t msi_addr, msi_data; 303 ushort_t msi_ctrl; 304 int i, cap_ptr = i_ddi_get_msi_msix_cap_ptr(dip); 305 ddi_acc_handle_t handle = i_ddi_get_pci_config_handle(dip); 306 msi_regs_t msi_regs; 307 void *intrmap_tbl[PCI_MSI_MAX_INTRS]; 308 309 DDI_INTR_IMPLDBG((CE_CONT, "apix_pci_msi_enable_vector: dip=0x%p\n" 310 "\tdriver = %s, inum=0x%x vector=0x%x apicid=0x%x\n", (void *)dip, 311 ddi_driver_name(dip), inum, vector, target_apic_id)); 312 313 ASSERT((handle != NULL) && (cap_ptr != 0)); 314 315 msi_regs.mr_data = vector; 316 msi_regs.mr_addr = target_apic_id; 317 318 for (i = 0; i < count; i++) 319 intrmap_tbl[i] = xv_intrmap_private(vecp->v_cpuid, vector + i); 320 apic_vt_ops->apic_intrmap_alloc_entry(intrmap_tbl, dip, type, 321 count, 0xff); 322 for (i = 0; i < count; i++) 323 xv_intrmap_private(vecp->v_cpuid, vector + i) = intrmap_tbl[i]; 324 325 apic_vt_ops->apic_intrmap_map_entry(vecp->v_intrmap_private, 326 (void *)&msi_regs, type, count); 327 apic_vt_ops->apic_intrmap_record_msi(vecp->v_intrmap_private, 328 &msi_regs); 329 330 /* MSI Address */ 331 msi_addr = msi_regs.mr_addr; 332 333 /* MSI Data: MSI is edge triggered according to spec */ 334 msi_data = msi_regs.mr_data; 335 336 DDI_INTR_IMPLDBG((CE_CONT, "apix_pci_msi_enable_vector: addr=0x%lx " 337 "data=0x%lx\n", (long)msi_addr, (long)msi_data)); 338 339 if (type == APIX_TYPE_MSI) { 340 msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSI_CTRL); 341 342 /* Set the bits to inform how many MSIs are enabled */ 343 msi_ctrl |= ((highbit(count) - 1) << PCI_MSI_MME_SHIFT); 344 pci_config_put16(handle, cap_ptr + PCI_MSI_CTRL, msi_ctrl); 345 346 if ((vecp->v_flags & APIX_VECT_MASKABLE) == 0) 347 APIX_WRITE_MSI_DATA(handle, cap_ptr, msi_ctrl, 348 APIX_RESV_VECTOR); 349 350 pci_config_put32(handle, 351 cap_ptr + PCI_MSI_ADDR_OFFSET, msi_addr); 352 if (msi_ctrl & PCI_MSI_64BIT_MASK) 353 pci_config_put32(handle, 354 cap_ptr + PCI_MSI_ADDR_OFFSET + 4, msi_addr >> 32); 355 356 APIX_WRITE_MSI_DATA(handle, cap_ptr, msi_ctrl, msi_data); 357 } else if (type == APIX_TYPE_MSIX) { 358 uintptr_t off; 359 ddi_intr_msix_t *msix_p = i_ddi_get_msix(dip); 360 361 /* Offset into the "inum"th entry in the MSI-X table */ 362 off = (uintptr_t)msix_p->msix_tbl_addr + 363 (inum * PCI_MSIX_VECTOR_SIZE); 364 365 ddi_put32(msix_p->msix_tbl_hdl, 366 (uint32_t *)(off + PCI_MSIX_DATA_OFFSET), msi_data); 367 ddi_put64(msix_p->msix_tbl_hdl, 368 (uint64_t *)(off + PCI_MSIX_LOWER_ADDR_OFFSET), msi_addr); 369 } 370 } 371 372 static void 373 apix_pci_msi_enable_mode(dev_info_t *dip, int type, int inum) 374 { 375 ushort_t msi_ctrl; 376 int cap_ptr = i_ddi_get_msi_msix_cap_ptr(dip); 377 ddi_acc_handle_t handle = i_ddi_get_pci_config_handle(dip); 378 379 ASSERT((handle != NULL) && (cap_ptr != 0)); 380 381 if (type == APIX_TYPE_MSI) { 382 msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSI_CTRL); 383 if ((msi_ctrl & PCI_MSI_ENABLE_BIT)) 384 return; 385 386 msi_ctrl |= PCI_MSI_ENABLE_BIT; 387 pci_config_put16(handle, cap_ptr + PCI_MSI_CTRL, msi_ctrl); 388 389 } else if (type == DDI_INTR_TYPE_MSIX) { 390 uintptr_t off; 391 uint32_t mask; 392 ddi_intr_msix_t *msix_p; 393 394 msix_p = i_ddi_get_msix(dip); 395 396 /* Offset into "inum"th entry in the MSI-X table & clear mask */ 397 off = (uintptr_t)msix_p->msix_tbl_addr + (inum * 398 PCI_MSIX_VECTOR_SIZE) + PCI_MSIX_VECTOR_CTRL_OFFSET; 399 400 mask = ddi_get32(msix_p->msix_tbl_hdl, (uint32_t *)off); 401 402 ddi_put32(msix_p->msix_tbl_hdl, (uint32_t *)off, (mask & ~1)); 403 404 msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSIX_CTRL); 405 406 if (!(msi_ctrl & PCI_MSIX_ENABLE_BIT)) { 407 msi_ctrl |= PCI_MSIX_ENABLE_BIT; 408 pci_config_put16(handle, cap_ptr + PCI_MSIX_CTRL, 409 msi_ctrl); 410 } 411 } 412 } 413 414 /* 415 * Setup interrupt, pogramming IO-APIC or MSI/X address/data. 416 */ 417 void 418 apix_enable_vector(apix_vector_t *vecp) 419 { 420 int tocpu = vecp->v_cpuid, type = vecp->v_type; 421 apic_cpus_info_t *cpu_infop; 422 ulong_t iflag; 423 424 ASSERT(tocpu < apic_nproc); 425 426 cpu_infop = &apic_cpus[tocpu]; 427 if (vecp->v_flags & APIX_VECT_USER_BOUND) 428 cpu_infop->aci_bound++; 429 else 430 cpu_infop->aci_temp_bound++; 431 432 iflag = intr_clear(); 433 lock_set(&apic_ioapic_lock); 434 435 if (!DDI_INTR_IS_MSI_OR_MSIX(type)) { /* fixed */ 436 apix_intx_enable(vecp->v_inum); 437 } else { 438 int inum = vecp->v_inum; 439 dev_info_t *dip = APIX_GET_DIP(vecp); 440 int count = i_ddi_intr_get_current_nintrs(dip); 441 442 if (type == APIX_TYPE_MSI) { /* MSI */ 443 if (inum == apix_get_max_dev_inum(dip, type)) { 444 /* last one */ 445 uchar_t start_inum = inum + 1 - count; 446 uchar_t start_vect = vecp->v_vector + 1 - count; 447 apix_vector_t *start_vecp = 448 xv_vector(vecp->v_cpuid, start_vect); 449 450 APIC_VERBOSE(INTR, (CE_CONT, "apix: call " 451 "apix_pci_msi_enable_vector\n")); 452 apix_pci_msi_enable_vector(start_vecp, dip, 453 type, start_inum, count, start_vect, 454 cpu_infop->aci_local_id); 455 456 APIC_VERBOSE(INTR, (CE_CONT, "apix: call " 457 "apix_pci_msi_enable_mode\n")); 458 apix_pci_msi_enable_mode(dip, type, inum); 459 } 460 } else { /* MSI-X */ 461 apix_pci_msi_enable_vector(vecp, dip, 462 type, inum, 1, vecp->v_vector, 463 cpu_infop->aci_local_id); 464 apix_pci_msi_enable_mode(dip, type, inum); 465 } 466 } 467 vecp->v_state = APIX_STATE_ENABLED; 468 apic_redist_cpu_skip &= ~(1 << tocpu); 469 470 lock_clear(&apic_ioapic_lock); 471 intr_restore(iflag); 472 } 473 474 /* 475 * Disable the interrupt 476 */ 477 void 478 apix_disable_vector(apix_vector_t *vecp) 479 { 480 struct autovec *avp = vecp->v_autovect; 481 ulong_t iflag; 482 483 ASSERT(avp != NULL); 484 485 iflag = intr_clear(); 486 lock_set(&apic_ioapic_lock); 487 488 switch (vecp->v_type) { 489 case APIX_TYPE_MSI: 490 ASSERT(avp->av_vector != NULL && avp->av_dip != NULL); 491 /* 492 * Disable the MSI vector 493 * Make sure we only disable on the last 494 * of the multi-MSI support 495 */ 496 if (i_ddi_intr_get_current_nenables(avp->av_dip) == 1) { 497 apic_pci_msi_disable_mode(avp->av_dip, 498 DDI_INTR_TYPE_MSI); 499 } 500 break; 501 case APIX_TYPE_MSIX: 502 ASSERT(avp->av_vector != NULL && avp->av_dip != NULL); 503 /* 504 * Disable the MSI-X vector 505 * needs to clear its mask and addr/data for each MSI-X 506 */ 507 apic_pci_msi_unconfigure(avp->av_dip, DDI_INTR_TYPE_MSIX, 508 vecp->v_inum); 509 /* 510 * Make sure we only disable on the last MSI-X 511 */ 512 if (i_ddi_intr_get_current_nenables(avp->av_dip) == 1) { 513 apic_pci_msi_disable_mode(avp->av_dip, 514 DDI_INTR_TYPE_MSIX); 515 } 516 break; 517 default: 518 apix_intx_disable(vecp->v_inum); 519 break; 520 } 521 522 if (!(apic_cpus[vecp->v_cpuid].aci_status & APIC_CPU_SUSPEND)) 523 vecp->v_state = APIX_STATE_DISABLED; 524 apic_vt_ops->apic_intrmap_free_entry(&vecp->v_intrmap_private); 525 vecp->v_intrmap_private = NULL; 526 527 lock_clear(&apic_ioapic_lock); 528 intr_restore(iflag); 529 } 530 531 /* 532 * Mark vector as obsoleted or freed. The vector is marked 533 * obsoleted if there are pending requests on it. Otherwise, 534 * free the vector. The obsoleted vectors get freed after 535 * being serviced. 536 * 537 * Return 1 on being obosoleted and 0 on being freed. 538 */ 539 #define INTR_BUSY(_avp)\ 540 ((((volatile ushort_t)(_avp)->av_flags) &\ 541 (AV_PENTRY_PEND | AV_PENTRY_ONPROC)) != 0) 542 #define LOCAL_WITH_INTR_DISABLED(_cpuid)\ 543 ((_cpuid) == psm_get_cpu_id() && !interrupts_enabled()) 544 static uint64_t dummy_tick; 545 546 int 547 apix_obsolete_vector(apix_vector_t *vecp) 548 { 549 struct autovec *avp = vecp->v_autovect; 550 int repeats, tries, ipl, busy = 0, cpuid = vecp->v_cpuid; 551 apix_impl_t *apixp = apixs[cpuid]; 552 553 ASSERT(APIX_CPU_LOCK_HELD(cpuid)); 554 555 for (avp = vecp->v_autovect; avp != NULL; avp = avp->av_link) { 556 if (avp->av_vector == NULL) 557 continue; 558 559 if (LOCAL_WITH_INTR_DISABLED(cpuid)) { 560 int bit, index, irr; 561 562 if (INTR_BUSY(avp)) { 563 busy++; 564 continue; 565 } 566 567 /* check IRR for pending interrupts */ 568 index = vecp->v_vector / 32; 569 bit = vecp->v_vector % 32; 570 irr = apic_reg_ops->apic_read(APIC_IRR_REG + index); 571 if ((irr & (1 << bit)) != 0) 572 busy++; 573 574 if (!busy) 575 apix_remove_av(vecp, avp); 576 577 continue; 578 } 579 580 repeats = 0; 581 do { 582 repeats++; 583 for (tries = 0; tries < apic_max_reps_clear_pending; 584 tries++) 585 if (!INTR_BUSY(avp)) 586 break; 587 } while (INTR_BUSY(avp) && 588 (repeats < apic_max_reps_clear_pending)); 589 590 if (INTR_BUSY(avp)) 591 busy++; 592 else { 593 /* 594 * Interrupt is not in pending list or being serviced. 595 * However it might be cached in Local APIC's IRR 596 * register. It's impossible to check another CPU's 597 * IRR register. Then wait till lower levels finish 598 * running. 599 */ 600 for (ipl = 1; ipl < MIN(LOCK_LEVEL, vecp->v_pri); ipl++) 601 apix_wait_till_seen(cpuid, ipl); 602 if (INTR_BUSY(avp)) 603 busy++; 604 } 605 606 if (!busy) 607 apix_remove_av(vecp, avp); 608 } 609 610 if (busy) { 611 apix_vector_t *tp = apixp->x_obsoletes; 612 613 if (vecp->v_state == APIX_STATE_OBSOLETED) 614 return (1); 615 616 vecp->v_state = APIX_STATE_OBSOLETED; 617 vecp->v_next = NULL; 618 if (tp == NULL) 619 apixp->x_obsoletes = vecp; 620 else { 621 while (tp->v_next != NULL) 622 tp = tp->v_next; 623 tp->v_next = vecp; 624 } 625 return (1); 626 } 627 628 /* interrupt is not busy */ 629 if (vecp->v_state == APIX_STATE_OBSOLETED) { 630 /* remove from obsoleted list */ 631 apixp->x_obsoletes = vecp->v_next; 632 vecp->v_next = NULL; 633 } 634 apix_cleanup_vector(vecp); 635 return (0); 636 } 637 638 /* 639 * Duplicate number of continuous vectors to specified target vectors. 640 */ 641 static void 642 apix_dup_vectors(apix_vector_t *oldp, apix_vector_t *newp, int count) 643 { 644 struct autovec *avp; 645 apix_vector_t *fromp, *top; 646 processorid_t oldcpu = oldp->v_cpuid, newcpu = newp->v_cpuid; 647 uchar_t oldvec = oldp->v_vector, newvec = newp->v_vector; 648 int i, inum; 649 650 ASSERT(oldp->v_type != APIX_TYPE_IPI); 651 652 for (i = 0; i < count; i++) { 653 fromp = xv_vector(oldcpu, oldvec + i); 654 top = xv_vector(newcpu, newvec + i); 655 ASSERT(fromp != NULL && top != NULL); 656 657 /* copy over original one */ 658 top->v_state = fromp->v_state; 659 top->v_type = fromp->v_type; 660 top->v_bound_cpuid = fromp->v_bound_cpuid; 661 top->v_inum = fromp->v_inum; 662 top->v_flags = fromp->v_flags; 663 top->v_intrmap_private = fromp->v_intrmap_private; 664 665 for (avp = fromp->v_autovect; avp != NULL; avp = avp->av_link) { 666 if (avp->av_vector == NULL) 667 continue; 668 669 apix_insert_av(top, avp->av_intr_id, avp->av_vector, 670 avp->av_intarg1, avp->av_intarg2, avp->av_ticksp, 671 avp->av_prilevel, avp->av_dip); 672 673 if (fromp->v_type == APIX_TYPE_FIXED && 674 avp->av_dip != NULL) { 675 inum = GET_INTR_INUM(avp->av_intr_id); 676 apix_set_dev_map(top, avp->av_dip, inum); 677 } 678 } 679 680 if (DDI_INTR_IS_MSI_OR_MSIX(fromp->v_type) && 681 fromp->v_devp != NULL) 682 apix_set_dev_map(top, fromp->v_devp->dv_dip, 683 fromp->v_devp->dv_inum); 684 } 685 } 686 687 static apix_vector_t * 688 apix_init_vector(processorid_t cpuid, uchar_t vector) 689 { 690 apix_impl_t *apixp = apixs[cpuid]; 691 apix_vector_t *vecp = apixp->x_vectbl[vector]; 692 693 ASSERT(IS_VECT_FREE(vecp)); 694 695 if (vecp == NULL) { 696 vecp = kmem_zalloc(sizeof (apix_vector_t), KM_NOSLEEP); 697 if (vecp == NULL) { 698 cmn_err(CE_WARN, "apix: no memory to allocate vector"); 699 return (NULL); 700 } 701 apixp->x_vectbl[vector] = vecp; 702 } 703 vecp->v_state = APIX_STATE_ALLOCED; 704 vecp->v_cpuid = vecp->v_bound_cpuid = cpuid; 705 vecp->v_vector = vector; 706 707 return (vecp); 708 } 709 710 static void 711 apix_cleanup_vector(apix_vector_t *vecp) 712 { 713 ASSERT(vecp->v_share == 0); 714 vecp->v_bound_cpuid = IRQ_UNINIT; 715 vecp->v_state = APIX_STATE_FREED; 716 vecp->v_type = 0; 717 vecp->v_flags = 0; 718 vecp->v_busy = 0; 719 vecp->v_intrmap_private = NULL; 720 } 721 722 static void 723 apix_dprint_vector(apix_vector_t *vecp, dev_info_t *dip, int count) 724 { 725 #ifdef DEBUG 726 major_t major; 727 char *name, *drv_name; 728 int instance, len, t_len; 729 char mesg[1024] = "apix: "; 730 731 t_len = sizeof (mesg); 732 len = strlen(mesg); 733 if (dip != NULL) { 734 name = ddi_get_name(dip); 735 major = ddi_name_to_major(name); 736 drv_name = ddi_major_to_name(major); 737 instance = ddi_get_instance(dip); 738 (void) snprintf(mesg + len, t_len - len, "%s (%s) instance %d ", 739 name, drv_name, instance); 740 } 741 len = strlen(mesg); 742 743 switch (vecp->v_type) { 744 case APIX_TYPE_FIXED: 745 (void) snprintf(mesg + len, t_len - len, "irqno %d", 746 vecp->v_inum); 747 break; 748 case APIX_TYPE_MSI: 749 (void) snprintf(mesg + len, t_len - len, 750 "msi inum %d (count %d)", vecp->v_inum, count); 751 break; 752 case APIX_TYPE_MSIX: 753 (void) snprintf(mesg + len, t_len - len, "msi-x inum %d", 754 vecp->v_inum); 755 break; 756 default: 757 break; 758 759 } 760 761 APIC_VERBOSE(ALLOC, (CE_CONT, "%s allocated with vector 0x%x on " 762 "cpu %d\n", mesg, vecp->v_vector, vecp->v_cpuid)); 763 #endif /* DEBUG */ 764 } 765 766 /* 767 * Operations on avintr 768 */ 769 770 #define INIT_AUTOVEC(p, intr_id, f, arg1, arg2, ticksp, ipl, dip) \ 771 do { \ 772 (p)->av_intr_id = intr_id; \ 773 (p)->av_vector = f; \ 774 (p)->av_intarg1 = arg1; \ 775 (p)->av_intarg2 = arg2; \ 776 (p)->av_ticksp = ticksp; \ 777 (p)->av_prilevel = ipl; \ 778 (p)->av_dip = dip; \ 779 (p)->av_flags = 0; \ 780 _NOTE(CONSTCOND)} while (0) 781 782 /* 783 * Insert an interrupt service routine into chain by its priority from 784 * high to low 785 */ 786 static void 787 apix_insert_av(apix_vector_t *vecp, void *intr_id, avfunc f, caddr_t arg1, 788 caddr_t arg2, uint64_t *ticksp, int ipl, dev_info_t *dip) 789 { 790 struct autovec *p, *prep, *mem; 791 792 APIC_VERBOSE(INTR, (CE_CONT, "apix_insert_av: dip %p, vector 0x%x, " 793 "cpu %d\n", (void *)dip, vecp->v_vector, vecp->v_cpuid)); 794 795 mem = kmem_zalloc(sizeof (struct autovec), KM_SLEEP); 796 INIT_AUTOVEC(mem, intr_id, f, arg1, arg2, ticksp, ipl, dip); 797 if (vecp->v_type == APIX_TYPE_FIXED && apic_level_intr[vecp->v_inum]) 798 mem->av_flags |= AV_PENTRY_LEVEL; 799 800 vecp->v_share++; 801 vecp->v_pri = (ipl > vecp->v_pri) ? ipl : vecp->v_pri; 802 if (vecp->v_autovect == NULL) { /* Nothing on list - put it at head */ 803 vecp->v_autovect = mem; 804 return; 805 } 806 807 if (DDI_INTR_IS_MSI_OR_MSIX(vecp->v_type)) { /* MSI/X */ 808 ASSERT(vecp->v_share == 1); /* No sharing for MSI/X */ 809 810 INIT_AUTOVEC(vecp->v_autovect, intr_id, f, arg1, arg2, ticksp, 811 ipl, dip); 812 prep = vecp->v_autovect->av_link; 813 vecp->v_autovect->av_link = NULL; 814 815 /* Free the following autovect chain */ 816 while (prep != NULL) { 817 ASSERT(prep->av_vector == NULL); 818 819 p = prep; 820 prep = prep->av_link; 821 kmem_free(p, sizeof (struct autovec)); 822 } 823 824 kmem_free(mem, sizeof (struct autovec)); 825 return; 826 } 827 828 /* find where it goes in list */ 829 prep = NULL; 830 for (p = vecp->v_autovect; p != NULL; p = p->av_link) { 831 if (p->av_vector && p->av_prilevel <= ipl) 832 break; 833 prep = p; 834 } 835 if (prep != NULL) { 836 if (prep->av_vector == NULL) { /* freed struct available */ 837 INIT_AUTOVEC(prep, intr_id, f, arg1, arg2, 838 ticksp, ipl, dip); 839 prep->av_flags = mem->av_flags; 840 kmem_free(mem, sizeof (struct autovec)); 841 return; 842 } 843 844 mem->av_link = prep->av_link; 845 prep->av_link = mem; 846 } else { 847 /* insert new intpt at beginning of chain */ 848 mem->av_link = vecp->v_autovect; 849 vecp->v_autovect = mem; 850 } 851 } 852 853 /* 854 * After having made a change to an autovector list, wait until we have 855 * seen specified cpu not executing an interrupt at that level--so we 856 * know our change has taken effect completely (no old state in registers, 857 * etc). 858 */ 859 #define APIX_CPU_ENABLED(_cp) \ 860 (quiesce_active == 0 && \ 861 (((_cp)->cpu_flags & (CPU_QUIESCED|CPU_OFFLINE)) == 0)) 862 863 static void 864 apix_wait_till_seen(processorid_t cpuid, int ipl) 865 { 866 struct cpu *cp = cpu[cpuid]; 867 868 if (cp == NULL || LOCAL_WITH_INTR_DISABLED(cpuid)) 869 return; 870 871 /* 872 * Don't wait if the CPU is quiesced or offlined. This can happen 873 * when a CPU is running pause thread but hardware triggered an 874 * interrupt and the interrupt gets queued. 875 */ 876 for (;;) { 877 if (!INTR_ACTIVE((volatile struct cpu *)cpu[cpuid], ipl) && 878 (!APIX_CPU_ENABLED(cp) || 879 !INTR_PENDING((volatile apix_impl_t *)apixs[cpuid], ipl))) 880 return; 881 } 882 } 883 884 static void 885 apix_remove_av(apix_vector_t *vecp, struct autovec *target) 886 { 887 int hi_pri = 0; 888 struct autovec *p; 889 890 if (target == NULL) 891 return; 892 893 APIC_VERBOSE(INTR, (CE_CONT, "apix_remove_av: dip %p, vector 0x%x, " 894 "cpu %d\n", (void *)target->av_dip, vecp->v_vector, vecp->v_cpuid)); 895 896 for (p = vecp->v_autovect; p; p = p->av_link) { 897 if (p == target || p->av_vector == NULL) 898 continue; 899 hi_pri = (p->av_prilevel > hi_pri) ? p->av_prilevel : hi_pri; 900 } 901 902 vecp->v_share--; 903 vecp->v_pri = hi_pri; 904 905 /* 906 * This drops the handler from the chain, it can no longer be called. 907 * However, there is no guarantee that the handler is not currently 908 * still executing. 909 */ 910 target->av_vector = NULL; 911 /* 912 * There is a race where we could be just about to pick up the ticksp 913 * pointer to increment it after returning from the service routine 914 * in av_dispatch_autovect. Rather than NULL it out let's just point 915 * it off to something safe so that any final tick update attempt 916 * won't fault. 917 */ 918 target->av_ticksp = &dummy_tick; 919 apix_wait_till_seen(vecp->v_cpuid, target->av_prilevel); 920 } 921 922 static struct autovec * 923 apix_find_av(apix_vector_t *vecp, void *intr_id, avfunc f) 924 { 925 struct autovec *p; 926 927 for (p = vecp->v_autovect; p; p = p->av_link) { 928 if ((p->av_vector == f) && (p->av_intr_id == intr_id)) { 929 /* found the handler */ 930 return (p); 931 } 932 } 933 934 return (NULL); 935 } 936 937 static apix_vector_t * 938 apix_find_vector_by_avintr(void *intr_id, avfunc f) 939 { 940 apix_vector_t *vecp; 941 processorid_t n; 942 uchar_t v; 943 944 for (n = 0; n < apic_nproc; n++) { 945 if (!apix_is_cpu_enabled(n)) 946 continue; 947 948 for (v = APIX_AVINTR_MIN; v <= APIX_AVINTR_MIN; v++) { 949 vecp = xv_vector(n, v); 950 if (vecp == NULL || 951 vecp->v_state <= APIX_STATE_OBSOLETED) 952 continue; 953 954 if (apix_find_av(vecp, intr_id, f) != NULL) 955 return (vecp); 956 } 957 } 958 959 return (NULL); 960 } 961 962 /* 963 * Add interrupt service routine. 964 * 965 * For legacy interrupts (HPET timer, ACPI SCI), the vector is actually 966 * IRQ no. A vector is then allocated. Otherwise, the vector is already 967 * allocated. The input argument virt_vect is virtual vector of format 968 * APIX_VIRTVEC_VECTOR(cpuid, vector). 969 * 970 * Return 1 on success, 0 on failure. 971 */ 972 int 973 apix_add_avintr(void *intr_id, int ipl, avfunc xxintr, char *name, 974 int virt_vect, caddr_t arg1, caddr_t arg2, uint64_t *ticksp, 975 dev_info_t *dip) 976 { 977 int cpuid; 978 uchar_t v = (uchar_t)APIX_VIRTVEC_VECTOR(virt_vect); 979 apix_vector_t *vecp; 980 981 if (xxintr == NULL) { 982 cmn_err(CE_WARN, "Attempt to add null for %s " 983 "on vector 0x%x,0x%x", name, 984 APIX_VIRTVEC_CPU(virt_vect), 985 APIX_VIRTVEC_VECTOR(virt_vect)); 986 return (0); 987 } 988 989 if (v >= APIX_IPI_MIN) /* IPIs */ 990 return (apix_add_ipi(ipl, xxintr, name, v, arg1, arg2)); 991 992 if (!APIX_IS_VIRTVEC(virt_vect)) { /* got irq */ 993 int irqno = virt_vect; 994 int inum = GET_INTR_INUM(intr_id); 995 996 /* 997 * Senarios include: 998 * a. add_avintr() is called before irqp initialized (legacy) 999 * b. irqp is initialized, vector is not allocated (fixed) 1000 * c. irqp is initialized, vector is allocated (fixed & shared) 1001 */ 1002 if ((vecp = apix_alloc_intx(dip, inum, irqno)) == NULL) 1003 return (0); 1004 1005 cpuid = vecp->v_cpuid; 1006 v = vecp->v_vector; 1007 virt_vect = APIX_VIRTVECTOR(cpuid, v); 1008 } else { /* got virtual vector */ 1009 cpuid = APIX_VIRTVEC_CPU(virt_vect); 1010 vecp = xv_vector(cpuid, v); 1011 ASSERT(vecp != NULL); 1012 } 1013 1014 lock_set(&apix_lock); 1015 if (vecp->v_state <= APIX_STATE_OBSOLETED) { 1016 vecp = NULL; 1017 1018 /* 1019 * Basically the allocated but not enabled interrupts 1020 * will not get re-targeted. But MSIs in allocated state 1021 * could be re-targeted due to group re-targeting. 1022 */ 1023 if (intr_id != NULL && dip != NULL) { 1024 ddi_intr_handle_impl_t *hdlp = intr_id; 1025 vecp = apix_get_dev_map(dip, hdlp->ih_inum, 1026 hdlp->ih_type); 1027 ASSERT(vecp->v_state == APIX_STATE_ALLOCED); 1028 } 1029 if (vecp == NULL) { 1030 lock_clear(&apix_lock); 1031 cmn_err(CE_WARN, "Invalid interrupt 0x%x,0x%x " 1032 " for %p to add", cpuid, v, intr_id); 1033 return (0); 1034 } 1035 cpuid = vecp->v_cpuid; 1036 virt_vect = APIX_VIRTVECTOR(cpuid, vecp->v_vector); 1037 } 1038 1039 APIX_ENTER_CPU_LOCK(cpuid); 1040 apix_insert_av(vecp, intr_id, xxintr, arg1, arg2, ticksp, ipl, dip); 1041 APIX_LEAVE_CPU_LOCK(cpuid); 1042 1043 (void) apix_addspl(virt_vect, ipl, 0, 0); 1044 1045 lock_clear(&apix_lock); 1046 1047 return (1); 1048 } 1049 1050 /* 1051 * Remove avintr 1052 * 1053 * For fixed, if it's the last one of shared interrupts, free the vector. 1054 * For msi/x, only disable the interrupt but not free the vector, which 1055 * is freed by PSM_XXX_FREE_XXX. 1056 */ 1057 void 1058 apix_rem_avintr(void *intr_id, int ipl, avfunc xxintr, int virt_vect) 1059 { 1060 avfunc f; 1061 apix_vector_t *vecp; 1062 struct autovec *avp; 1063 processorid_t cpuid; 1064 1065 if ((f = xxintr) == NULL) 1066 return; 1067 1068 lock_set(&apix_lock); 1069 1070 if (!APIX_IS_VIRTVEC(virt_vect)) { /* got irq */ 1071 vecp = apix_intx_get_vector(virt_vect); 1072 virt_vect = APIX_VIRTVECTOR(vecp->v_cpuid, vecp->v_vector); 1073 } else /* got virtual vector */ 1074 vecp = xv_vector(APIX_VIRTVEC_CPU(virt_vect), 1075 APIX_VIRTVEC_VECTOR(virt_vect)); 1076 1077 if (vecp == NULL) { 1078 lock_clear(&apix_lock); 1079 cmn_err(CE_CONT, "Invalid interrupt 0x%x,0x%x to remove", 1080 APIX_VIRTVEC_CPU(virt_vect), 1081 APIX_VIRTVEC_VECTOR(virt_vect)); 1082 return; 1083 } 1084 1085 if (vecp->v_state <= APIX_STATE_OBSOLETED || 1086 ((avp = apix_find_av(vecp, intr_id, f)) == NULL)) { 1087 /* 1088 * It's possible that the interrupt is rebound to a 1089 * different cpu before rem_avintr() is called. Search 1090 * through all vectors once it happens. 1091 */ 1092 if ((vecp = apix_find_vector_by_avintr(intr_id, f)) 1093 == NULL) { 1094 lock_clear(&apix_lock); 1095 cmn_err(CE_CONT, "Unknown interrupt 0x%x,0x%x " 1096 "for %p to remove", APIX_VIRTVEC_CPU(virt_vect), 1097 APIX_VIRTVEC_VECTOR(virt_vect), intr_id); 1098 return; 1099 } 1100 virt_vect = APIX_VIRTVECTOR(vecp->v_cpuid, vecp->v_vector); 1101 avp = apix_find_av(vecp, intr_id, f); 1102 } 1103 cpuid = vecp->v_cpuid; 1104 1105 /* disable interrupt */ 1106 (void) apix_delspl(virt_vect, ipl, 0, 0); 1107 1108 /* remove ISR entry */ 1109 APIX_ENTER_CPU_LOCK(cpuid); 1110 apix_remove_av(vecp, avp); 1111 APIX_LEAVE_CPU_LOCK(cpuid); 1112 1113 lock_clear(&apix_lock); 1114 } 1115 1116 /* 1117 * Device to vector mapping table 1118 */ 1119 1120 static void 1121 apix_clear_dev_map(dev_info_t *dip, int inum, int type) 1122 { 1123 char *name; 1124 major_t major; 1125 apix_dev_vector_t *dvp, *prev = NULL; 1126 int found = 0; 1127 1128 name = ddi_get_name(dip); 1129 major = ddi_name_to_major(name); 1130 1131 mutex_enter(&apix_mutex); 1132 1133 for (dvp = apix_dev_vector[major]; dvp != NULL; 1134 prev = dvp, dvp = dvp->dv_next) { 1135 if (dvp->dv_dip == dip && dvp->dv_inum == inum && 1136 dvp->dv_type == type) { 1137 found++; 1138 break; 1139 } 1140 } 1141 1142 if (!found) { 1143 mutex_exit(&apix_mutex); 1144 return; 1145 } 1146 1147 if (prev != NULL) 1148 prev->dv_next = dvp->dv_next; 1149 1150 if (apix_dev_vector[major] == dvp) 1151 apix_dev_vector[major] = dvp->dv_next; 1152 1153 dvp->dv_vector->v_devp = NULL; 1154 1155 mutex_exit(&apix_mutex); 1156 1157 kmem_free(dvp, sizeof (apix_dev_vector_t)); 1158 } 1159 1160 void 1161 apix_set_dev_map(apix_vector_t *vecp, dev_info_t *dip, int inum) 1162 { 1163 apix_dev_vector_t *dvp; 1164 char *name; 1165 major_t major; 1166 uint32_t found = 0; 1167 1168 ASSERT(dip != NULL); 1169 name = ddi_get_name(dip); 1170 major = ddi_name_to_major(name); 1171 1172 mutex_enter(&apix_mutex); 1173 1174 for (dvp = apix_dev_vector[major]; dvp != NULL; 1175 dvp = dvp->dv_next) { 1176 if (dvp->dv_dip == dip && dvp->dv_inum == inum && 1177 dvp->dv_type == vecp->v_type) { 1178 found++; 1179 break; 1180 } 1181 } 1182 1183 if (found == 0) { /* not found */ 1184 dvp = kmem_zalloc(sizeof (apix_dev_vector_t), KM_SLEEP); 1185 dvp->dv_dip = dip; 1186 dvp->dv_inum = inum; 1187 dvp->dv_type = vecp->v_type; 1188 1189 dvp->dv_next = apix_dev_vector[major]; 1190 apix_dev_vector[major] = dvp; 1191 } 1192 dvp->dv_vector = vecp; 1193 vecp->v_devp = dvp; 1194 1195 mutex_exit(&apix_mutex); 1196 1197 DDI_INTR_IMPLDBG((CE_CONT, "apix_set_dev_map: dip=0x%p " 1198 "inum=0x%x vector=0x%x/0x%x\n", 1199 (void *)dip, inum, vecp->v_cpuid, vecp->v_vector)); 1200 } 1201 1202 apix_vector_t * 1203 apix_get_dev_map(dev_info_t *dip, int inum, int type) 1204 { 1205 char *name; 1206 major_t major; 1207 apix_dev_vector_t *dvp; 1208 apix_vector_t *vecp; 1209 1210 name = ddi_get_name(dip); 1211 if ((major = ddi_name_to_major(name)) == DDI_MAJOR_T_NONE) 1212 return (NULL); 1213 1214 mutex_enter(&apix_mutex); 1215 for (dvp = apix_dev_vector[major]; dvp != NULL; 1216 dvp = dvp->dv_next) { 1217 if (dvp->dv_dip == dip && dvp->dv_inum == inum && 1218 dvp->dv_type == type) { 1219 vecp = dvp->dv_vector; 1220 mutex_exit(&apix_mutex); 1221 return (vecp); 1222 } 1223 } 1224 mutex_exit(&apix_mutex); 1225 1226 return (NULL); 1227 } 1228 1229 /* 1230 * Get minimum inum for specified device, used for MSI 1231 */ 1232 int 1233 apix_get_min_dev_inum(dev_info_t *dip, int type) 1234 { 1235 char *name; 1236 major_t major; 1237 apix_dev_vector_t *dvp; 1238 int inum = -1; 1239 1240 name = ddi_get_name(dip); 1241 major = ddi_name_to_major(name); 1242 1243 mutex_enter(&apix_mutex); 1244 for (dvp = apix_dev_vector[major]; dvp != NULL; 1245 dvp = dvp->dv_next) { 1246 if (dvp->dv_dip == dip && dvp->dv_type == type) { 1247 if (inum == -1) 1248 inum = dvp->dv_inum; 1249 else 1250 inum = (dvp->dv_inum < inum) ? 1251 dvp->dv_inum : inum; 1252 } 1253 } 1254 mutex_exit(&apix_mutex); 1255 1256 return (inum); 1257 } 1258 1259 int 1260 apix_get_max_dev_inum(dev_info_t *dip, int type) 1261 { 1262 char *name; 1263 major_t major; 1264 apix_dev_vector_t *dvp; 1265 int inum = -1; 1266 1267 name = ddi_get_name(dip); 1268 major = ddi_name_to_major(name); 1269 1270 mutex_enter(&apix_mutex); 1271 for (dvp = apix_dev_vector[major]; dvp != NULL; 1272 dvp = dvp->dv_next) { 1273 if (dvp->dv_dip == dip && dvp->dv_type == type) { 1274 if (inum == -1) 1275 inum = dvp->dv_inum; 1276 else 1277 inum = (dvp->dv_inum > inum) ? 1278 dvp->dv_inum : inum; 1279 } 1280 } 1281 mutex_exit(&apix_mutex); 1282 1283 return (inum); 1284 } 1285 1286 /* 1287 * Major to cpu binding, for INTR_ROUND_ROBIN_WITH_AFFINITY cpu 1288 * binding policy 1289 */ 1290 1291 static uint32_t 1292 apix_get_dev_binding(dev_info_t *dip) 1293 { 1294 major_t major; 1295 char *name; 1296 uint32_t cpu = IRQ_UNINIT; 1297 1298 name = ddi_get_name(dip); 1299 major = ddi_name_to_major(name); 1300 if (major < devcnt) { 1301 mutex_enter(&apix_mutex); 1302 cpu = apix_major_to_cpu[major]; 1303 mutex_exit(&apix_mutex); 1304 } 1305 1306 return (cpu); 1307 } 1308 1309 static void 1310 apix_set_dev_binding(dev_info_t *dip, uint32_t cpu) 1311 { 1312 major_t major; 1313 char *name; 1314 1315 /* setup major to cpu mapping */ 1316 name = ddi_get_name(dip); 1317 major = ddi_name_to_major(name); 1318 if (apix_major_to_cpu[major] == IRQ_UNINIT) { 1319 mutex_enter(&apix_mutex); 1320 apix_major_to_cpu[major] = cpu; 1321 mutex_exit(&apix_mutex); 1322 } 1323 } 1324 1325 /* 1326 * return the cpu to which this intr should be bound. 1327 * Check properties or any other mechanism to see if user wants it 1328 * bound to a specific CPU. If so, return the cpu id with high bit set. 1329 * If not, use the policy to choose a cpu and return the id. 1330 */ 1331 uint32_t 1332 apix_bind_cpu(dev_info_t *dip) 1333 { 1334 int instance, instno, prop_len, bind_cpu, count; 1335 uint_t i, rc; 1336 major_t major; 1337 char *name, *drv_name, *prop_val, *cptr; 1338 char prop_name[32]; 1339 1340 lock_set(&apix_lock); 1341 1342 if (apic_intr_policy == INTR_LOWEST_PRIORITY) { 1343 cmn_err(CE_WARN, "apix: unsupported interrupt binding policy " 1344 "LOWEST PRIORITY, use ROUND ROBIN instead"); 1345 apic_intr_policy = INTR_ROUND_ROBIN; 1346 } 1347 1348 if (apic_nproc == 1) { 1349 lock_clear(&apix_lock); 1350 return (0); 1351 } 1352 1353 drv_name = NULL; 1354 rc = DDI_PROP_NOT_FOUND; 1355 major = (major_t)-1; 1356 if (dip != NULL) { 1357 name = ddi_get_name(dip); 1358 major = ddi_name_to_major(name); 1359 drv_name = ddi_major_to_name(major); 1360 instance = ddi_get_instance(dip); 1361 if (apic_intr_policy == INTR_ROUND_ROBIN_WITH_AFFINITY) { 1362 bind_cpu = apix_get_dev_binding(dip); 1363 if (bind_cpu != IRQ_UNINIT) { 1364 lock_clear(&apix_lock); 1365 return (bind_cpu); 1366 } 1367 } 1368 /* 1369 * search for "drvname"_intpt_bind_cpus property first, the 1370 * syntax of the property should be "a[,b,c,...]" where 1371 * instance 0 binds to cpu a, instance 1 binds to cpu b, 1372 * instance 3 binds to cpu c... 1373 * ddi_getlongprop() will search /option first, then / 1374 * if "drvname"_intpt_bind_cpus doesn't exist, then find 1375 * intpt_bind_cpus property. The syntax is the same, and 1376 * it applies to all the devices if its "drvname" specific 1377 * property doesn't exist 1378 */ 1379 (void) strcpy(prop_name, drv_name); 1380 (void) strcat(prop_name, "_intpt_bind_cpus"); 1381 rc = ddi_getlongprop(DDI_DEV_T_ANY, dip, 0, prop_name, 1382 (caddr_t)&prop_val, &prop_len); 1383 if (rc != DDI_PROP_SUCCESS) { 1384 rc = ddi_getlongprop(DDI_DEV_T_ANY, dip, 0, 1385 "intpt_bind_cpus", (caddr_t)&prop_val, &prop_len); 1386 } 1387 } 1388 if (rc == DDI_PROP_SUCCESS) { 1389 for (i = count = 0; i < (prop_len - 1); i++) 1390 if (prop_val[i] == ',') 1391 count++; 1392 if (prop_val[i-1] != ',') 1393 count++; 1394 /* 1395 * if somehow the binding instances defined in the 1396 * property are not enough for this instno., then 1397 * reuse the pattern for the next instance until 1398 * it reaches the requested instno 1399 */ 1400 instno = instance % count; 1401 i = 0; 1402 cptr = prop_val; 1403 while (i < instno) 1404 if (*cptr++ == ',') 1405 i++; 1406 bind_cpu = stoi(&cptr); 1407 kmem_free(prop_val, prop_len); 1408 /* if specific cpu is bogus, then default to cpu 0 */ 1409 if (bind_cpu >= apic_nproc) { 1410 cmn_err(CE_WARN, "apix: %s=%s: CPU %d not present", 1411 prop_name, prop_val, bind_cpu); 1412 bind_cpu = 0; 1413 } else { 1414 /* indicate that we are bound at user request */ 1415 bind_cpu |= IRQ_USER_BOUND; 1416 } 1417 /* 1418 * no need to check apic_cpus[].aci_status, if specific cpu is 1419 * not up, then post_cpu_start will handle it. 1420 */ 1421 } else { 1422 bind_cpu = apic_get_next_bind_cpu(); 1423 } 1424 1425 lock_clear(&apix_lock); 1426 1427 return ((uint32_t)bind_cpu); 1428 } 1429 1430 static boolean_t 1431 apix_is_cpu_enabled(processorid_t cpuid) 1432 { 1433 apic_cpus_info_t *cpu_infop; 1434 1435 cpu_infop = &apic_cpus[cpuid]; 1436 1437 if ((cpu_infop->aci_status & APIC_CPU_INTR_ENABLE) == 0) 1438 return (B_FALSE); 1439 1440 return (B_TRUE); 1441 } 1442 1443 /* 1444 * Must be called with apix_lock held. This function can be 1445 * called from above lock level by apix_intr_redistribute(). 1446 * 1447 * Arguments: 1448 * vecp : Vector to be rebound 1449 * tocpu : Target cpu. IRQ_UNINIT means target is vecp->v_cpuid. 1450 * count : Number of continuous vectors 1451 * 1452 * Return new vector being bound to 1453 */ 1454 apix_vector_t * 1455 apix_rebind(apix_vector_t *vecp, processorid_t newcpu, int count) 1456 { 1457 apix_vector_t *newp, *oldp; 1458 processorid_t oldcpu = vecp->v_cpuid; 1459 uchar_t newvec, oldvec = vecp->v_vector; 1460 int i; 1461 1462 ASSERT(LOCK_HELD(&apix_lock) && count > 0); 1463 1464 if (!apix_is_cpu_enabled(newcpu)) 1465 return (NULL); 1466 1467 if (vecp->v_cpuid == newcpu) /* rebind to the same cpu */ 1468 return (vecp); 1469 1470 APIX_ENTER_CPU_LOCK(oldcpu); 1471 APIX_ENTER_CPU_LOCK(newcpu); 1472 1473 /* allocate vector */ 1474 if (count == 1) 1475 newp = apix_alloc_vector_oncpu(newcpu, NULL, 0, vecp->v_type); 1476 else { 1477 ASSERT(vecp->v_type == APIX_TYPE_MSI); 1478 newp = apix_alloc_nvectors_oncpu(newcpu, NULL, 0, count, 1479 vecp->v_type); 1480 } 1481 if (newp == NULL) { 1482 APIX_LEAVE_CPU_LOCK(newcpu); 1483 APIX_LEAVE_CPU_LOCK(oldcpu); 1484 return (NULL); 1485 } 1486 1487 newvec = newp->v_vector; 1488 apix_dup_vectors(vecp, newp, count); 1489 1490 APIX_LEAVE_CPU_LOCK(newcpu); 1491 APIX_LEAVE_CPU_LOCK(oldcpu); 1492 1493 if (!DDI_INTR_IS_MSI_OR_MSIX(vecp->v_type)) { 1494 ASSERT(count == 1); 1495 if (apix_intx_rebind(vecp->v_inum, newcpu, newvec) != 0) { 1496 struct autovec *avp; 1497 int inum; 1498 1499 /* undo duplication */ 1500 APIX_ENTER_CPU_LOCK(oldcpu); 1501 APIX_ENTER_CPU_LOCK(newcpu); 1502 for (avp = newp->v_autovect; avp != NULL; 1503 avp = avp->av_link) { 1504 if (avp->av_dip != NULL) { 1505 inum = GET_INTR_INUM(avp->av_intr_id); 1506 apix_set_dev_map(vecp, avp->av_dip, 1507 inum); 1508 } 1509 apix_remove_av(newp, avp); 1510 } 1511 apix_cleanup_vector(newp); 1512 APIX_LEAVE_CPU_LOCK(newcpu); 1513 APIX_LEAVE_CPU_LOCK(oldcpu); 1514 APIC_VERBOSE(REBIND, (CE_CONT, "apix: rebind fixed " 1515 "interrupt 0x%x to cpu %d failed\n", 1516 vecp->v_inum, newcpu)); 1517 return (NULL); 1518 } 1519 1520 APIX_ENTER_CPU_LOCK(oldcpu); 1521 (void) apix_obsolete_vector(vecp); 1522 APIX_LEAVE_CPU_LOCK(oldcpu); 1523 APIC_VERBOSE(REBIND, (CE_CONT, "apix: rebind fixed interrupt" 1524 " 0x%x/0x%x to 0x%x/0x%x\n", 1525 oldcpu, oldvec, newcpu, newvec)); 1526 return (newp); 1527 } 1528 1529 for (i = 0; i < count; i++) { 1530 oldp = xv_vector(oldcpu, oldvec + i); 1531 newp = xv_vector(newcpu, newvec + i); 1532 1533 if (newp->v_share > 0) { 1534 APIX_SET_REBIND_INFO(oldp, newp); 1535 1536 apix_enable_vector(newp); 1537 1538 APIX_CLR_REBIND_INFO(); 1539 } 1540 1541 APIX_ENTER_CPU_LOCK(oldcpu); 1542 (void) apix_obsolete_vector(oldp); 1543 APIX_LEAVE_CPU_LOCK(oldcpu); 1544 } 1545 APIC_VERBOSE(REBIND, (CE_CONT, "apix: rebind vector 0x%x/0x%x " 1546 "to 0x%x/0x%x, count=%d\n", 1547 oldcpu, oldvec, newcpu, newvec, count)); 1548 1549 return (xv_vector(newcpu, newvec)); 1550 } 1551 1552 /* 1553 * Senarios include: 1554 * a. add_avintr() is called before irqp initialized (legacy) 1555 * b. irqp is initialized, vector is not allocated (fixed interrupts) 1556 * c. irqp is initialized, vector is allocated (shared interrupts) 1557 */ 1558 apix_vector_t * 1559 apix_alloc_intx(dev_info_t *dip, int inum, int irqno) 1560 { 1561 apic_irq_t *irqp; 1562 apix_vector_t *vecp; 1563 1564 /* 1565 * Allocate IRQ. Caller is later responsible for the 1566 * initialization 1567 */ 1568 mutex_enter(&airq_mutex); 1569 if ((irqp = apic_irq_table[irqno]) == NULL) { 1570 /* allocate irq */ 1571 irqp = kmem_zalloc(sizeof (apic_irq_t), KM_SLEEP); 1572 irqp->airq_mps_intr_index = FREE_INDEX; 1573 apic_irq_table[irqno] = irqp; 1574 } 1575 if (irqp->airq_mps_intr_index == FREE_INDEX) { 1576 irqp->airq_mps_intr_index = DEFAULT_INDEX; 1577 irqp->airq_cpu = IRQ_UNINIT; 1578 irqp->airq_origirq = (uchar_t)irqno; 1579 } 1580 1581 mutex_exit(&airq_mutex); 1582 1583 /* 1584 * allocate vector 1585 */ 1586 if (irqp->airq_cpu == IRQ_UNINIT) { 1587 uint32_t bindcpu, cpuid; 1588 1589 /* select cpu by system policy */ 1590 bindcpu = apix_bind_cpu(dip); 1591 cpuid = bindcpu & ~IRQ_USER_BOUND; 1592 1593 /* allocate vector */ 1594 APIX_ENTER_CPU_LOCK(cpuid); 1595 1596 if ((vecp = apix_alloc_vector_oncpu(bindcpu, dip, inum, 1597 APIX_TYPE_FIXED)) == NULL) { 1598 cmn_err(CE_WARN, "No interrupt vector for irq %x", 1599 irqno); 1600 APIX_LEAVE_CPU_LOCK(cpuid); 1601 return (NULL); 1602 } 1603 vecp->v_inum = irqno; 1604 vecp->v_flags |= APIX_VECT_MASKABLE; 1605 1606 apix_intx_set_vector(irqno, vecp->v_cpuid, vecp->v_vector); 1607 1608 APIX_LEAVE_CPU_LOCK(cpuid); 1609 } else { 1610 vecp = xv_vector(irqp->airq_cpu, irqp->airq_vector); 1611 ASSERT(!IS_VECT_FREE(vecp)); 1612 1613 if (dip != NULL) 1614 apix_set_dev_map(vecp, dip, inum); 1615 } 1616 1617 if ((dip != NULL) && 1618 (apic_intr_policy == INTR_ROUND_ROBIN_WITH_AFFINITY) && 1619 ((vecp->v_flags & APIX_VECT_USER_BOUND) == 0)) 1620 apix_set_dev_binding(dip, vecp->v_cpuid); 1621 1622 apix_dprint_vector(vecp, dip, 1); 1623 1624 return (vecp); 1625 } 1626 1627 int 1628 apix_alloc_msi(dev_info_t *dip, int inum, int count, int behavior) 1629 { 1630 int i, cap_ptr, rcount = count; 1631 apix_vector_t *vecp; 1632 processorid_t bindcpu, cpuid; 1633 ushort_t msi_ctrl; 1634 ddi_acc_handle_t handle; 1635 1636 DDI_INTR_IMPLDBG((CE_CONT, "apix_alloc_msi_vectors: dip=0x%p " 1637 "inum=0x%x count=0x%x behavior=%d\n", 1638 (void *)dip, inum, count, behavior)); 1639 1640 if (count > 1) { 1641 if (behavior == DDI_INTR_ALLOC_STRICT && 1642 apic_multi_msi_enable == 0) 1643 return (0); 1644 if (apic_multi_msi_enable == 0) 1645 count = 1; 1646 } 1647 1648 /* Check whether it supports per-vector masking */ 1649 cap_ptr = i_ddi_get_msi_msix_cap_ptr(dip); 1650 handle = i_ddi_get_pci_config_handle(dip); 1651 msi_ctrl = pci_config_get16(handle, cap_ptr + PCI_MSI_CTRL); 1652 1653 /* bind to cpu */ 1654 bindcpu = apix_bind_cpu(dip); 1655 cpuid = bindcpu & ~IRQ_USER_BOUND; 1656 1657 /* if not ISP2, then round it down */ 1658 if (!ISP2(rcount)) 1659 rcount = 1 << (highbit(rcount) - 1); 1660 1661 APIX_ENTER_CPU_LOCK(cpuid); 1662 for (vecp = NULL; rcount > 0; rcount >>= 1) { 1663 vecp = apix_alloc_nvectors_oncpu(bindcpu, dip, inum, rcount, 1664 APIX_TYPE_MSI); 1665 if (vecp != NULL || behavior == DDI_INTR_ALLOC_STRICT) 1666 break; 1667 } 1668 for (i = 0; vecp && i < rcount; i++) 1669 xv_vector(vecp->v_cpuid, vecp->v_vector + i)->v_flags |= 1670 (msi_ctrl & PCI_MSI_PVM_MASK) ? APIX_VECT_MASKABLE : 0; 1671 APIX_LEAVE_CPU_LOCK(cpuid); 1672 if (vecp == NULL) { 1673 APIC_VERBOSE(INTR, (CE_CONT, 1674 "apix_alloc_msi: no %d cont vectors found on cpu 0x%x\n", 1675 count, bindcpu)); 1676 return (0); 1677 } 1678 1679 /* major to cpu binding */ 1680 if ((apic_intr_policy == INTR_ROUND_ROBIN_WITH_AFFINITY) && 1681 ((vecp->v_flags & APIX_VECT_USER_BOUND) == 0)) 1682 apix_set_dev_binding(dip, vecp->v_cpuid); 1683 1684 apix_dprint_vector(vecp, dip, rcount); 1685 1686 return (rcount); 1687 } 1688 1689 int 1690 apix_alloc_msix(dev_info_t *dip, int inum, int count, int behavior) 1691 { 1692 apix_vector_t *vecp; 1693 processorid_t bindcpu, cpuid; 1694 int i; 1695 1696 for (i = 0; i < count; i++) { 1697 /* select cpu by system policy */ 1698 bindcpu = apix_bind_cpu(dip); 1699 cpuid = bindcpu & ~IRQ_USER_BOUND; 1700 1701 /* allocate vector */ 1702 APIX_ENTER_CPU_LOCK(cpuid); 1703 if ((vecp = apix_alloc_vector_oncpu(bindcpu, dip, inum + i, 1704 APIX_TYPE_MSIX)) == NULL) { 1705 APIX_LEAVE_CPU_LOCK(cpuid); 1706 APIC_VERBOSE(INTR, (CE_CONT, "apix_alloc_msix: " 1707 "allocate msix for device dip=%p, inum=%d on" 1708 " cpu %d failed", (void *)dip, inum + i, bindcpu)); 1709 break; 1710 } 1711 vecp->v_flags |= APIX_VECT_MASKABLE; 1712 APIX_LEAVE_CPU_LOCK(cpuid); 1713 1714 /* major to cpu mapping */ 1715 if ((i == 0) && 1716 (apic_intr_policy == INTR_ROUND_ROBIN_WITH_AFFINITY) && 1717 ((vecp->v_flags & APIX_VECT_USER_BOUND) == 0)) 1718 apix_set_dev_binding(dip, vecp->v_cpuid); 1719 1720 apix_dprint_vector(vecp, dip, 1); 1721 } 1722 1723 if (i < count && behavior == DDI_INTR_ALLOC_STRICT) { 1724 APIC_VERBOSE(INTR, (CE_WARN, "apix_alloc_msix: " 1725 "strictly allocate %d vectors failed, got %d\n", 1726 count, i)); 1727 apix_free_vectors(dip, inum, i, APIX_TYPE_MSIX); 1728 i = 0; 1729 } 1730 1731 return (i); 1732 } 1733 1734 /* 1735 * A rollback free for vectors allocated by apix_alloc_xxx(). 1736 */ 1737 void 1738 apix_free_vectors(dev_info_t *dip, int inum, int count, int type) 1739 { 1740 int i, cpuid; 1741 apix_vector_t *vecp; 1742 1743 DDI_INTR_IMPLDBG((CE_CONT, "apix_free_vectors: dip: %p inum: %x " 1744 "count: %x type: %x\n", 1745 (void *)dip, inum, count, type)); 1746 1747 lock_set(&apix_lock); 1748 1749 for (i = 0; i < count; i++, inum++) { 1750 if ((vecp = apix_get_dev_map(dip, inum, type)) == NULL) { 1751 lock_clear(&apix_lock); 1752 DDI_INTR_IMPLDBG((CE_CONT, "apix_free_vectors: " 1753 "dip=0x%p inum=0x%x type=0x%x apix_find_intr() " 1754 "failed\n", (void *)dip, inum, type)); 1755 continue; 1756 } 1757 1758 APIX_ENTER_CPU_LOCK(vecp->v_cpuid); 1759 cpuid = vecp->v_cpuid; 1760 1761 DDI_INTR_IMPLDBG((CE_CONT, "apix_free_vectors: " 1762 "dip=0x%p inum=0x%x type=0x%x vector 0x%x (share %d)\n", 1763 (void *)dip, inum, type, vecp->v_vector, vecp->v_share)); 1764 1765 /* tear down device interrupt to vector mapping */ 1766 apix_clear_dev_map(dip, inum, type); 1767 1768 if (vecp->v_type == APIX_TYPE_FIXED) { 1769 if (vecp->v_share > 0) { /* share IRQ line */ 1770 APIX_LEAVE_CPU_LOCK(cpuid); 1771 continue; 1772 } 1773 1774 /* Free apic_irq_table entry */ 1775 apix_intx_free(vecp->v_inum); 1776 } 1777 1778 /* free vector */ 1779 apix_cleanup_vector(vecp); 1780 1781 APIX_LEAVE_CPU_LOCK(cpuid); 1782 } 1783 1784 lock_clear(&apix_lock); 1785 } 1786 1787 /* 1788 * Must be called with apix_lock held 1789 */ 1790 apix_vector_t * 1791 apix_setup_io_intr(apix_vector_t *vecp) 1792 { 1793 processorid_t bindcpu; 1794 int ret; 1795 1796 ASSERT(LOCK_HELD(&apix_lock)); 1797 1798 /* 1799 * Interrupts are enabled on the CPU, programme IOAPIC RDT 1800 * entry or MSI/X address/data to enable the interrupt. 1801 */ 1802 if (apix_is_cpu_enabled(vecp->v_cpuid)) { 1803 apix_enable_vector(vecp); 1804 return (vecp); 1805 } 1806 1807 /* 1808 * CPU is not up or interrupts are disabled. Fall back to the 1809 * first avialable CPU. 1810 */ 1811 bindcpu = apic_find_cpu(APIC_CPU_INTR_ENABLE); 1812 1813 if (vecp->v_type == APIX_TYPE_MSI) 1814 return (apix_grp_set_cpu(vecp, bindcpu, &ret)); 1815 1816 return (apix_set_cpu(vecp, bindcpu, &ret)); 1817 } 1818 1819 /* 1820 * For interrupts which call add_avintr() before apic is initialized. 1821 * ioapix_setup_intr() will 1822 * - allocate vector 1823 * - copy over ISR 1824 */ 1825 static void 1826 ioapix_setup_intr(int irqno, iflag_t *flagp) 1827 { 1828 extern struct av_head autovect[]; 1829 apix_vector_t *vecp; 1830 apic_irq_t *irqp; 1831 uchar_t ioapicindex, ipin; 1832 ulong_t iflag; 1833 struct autovec *avp; 1834 1835 ioapicindex = acpi_find_ioapic(irqno); 1836 ASSERT(ioapicindex != 0xFF); 1837 ipin = irqno - apic_io_vectbase[ioapicindex]; 1838 1839 mutex_enter(&airq_mutex); 1840 irqp = apic_irq_table[irqno]; 1841 1842 /* 1843 * The irq table entry should not exist unless the interrupts are shared. 1844 * In that case, make sure it matches what we would initialize it to. 1845 */ 1846 if (irqp != NULL) { 1847 ASSERT(irqp->airq_mps_intr_index == ACPI_INDEX); 1848 ASSERT(irqp->airq_intin_no == ipin && 1849 irqp->airq_ioapicindex == ioapicindex); 1850 vecp = xv_vector(irqp->airq_cpu, irqp->airq_vector); 1851 ASSERT(!IS_VECT_FREE(vecp)); 1852 mutex_exit(&airq_mutex); 1853 } else { 1854 irqp = kmem_zalloc(sizeof (apic_irq_t), KM_SLEEP); 1855 1856 irqp->airq_cpu = IRQ_UNINIT; 1857 irqp->airq_origirq = (uchar_t)irqno; 1858 irqp->airq_mps_intr_index = ACPI_INDEX; 1859 irqp->airq_ioapicindex = ioapicindex; 1860 irqp->airq_intin_no = ipin; 1861 irqp->airq_iflag = *flagp; 1862 irqp->airq_share++; 1863 1864 apic_irq_table[irqno] = irqp; 1865 mutex_exit(&airq_mutex); 1866 1867 vecp = apix_alloc_intx(NULL, 0, irqno); 1868 } 1869 1870 /* copy over autovect */ 1871 for (avp = autovect[irqno].avh_link; avp; avp = avp->av_link) 1872 apix_insert_av(vecp, avp->av_intr_id, avp->av_vector, 1873 avp->av_intarg1, avp->av_intarg2, avp->av_ticksp, 1874 avp->av_prilevel, avp->av_dip); 1875 1876 /* Program I/O APIC */ 1877 iflag = intr_clear(); 1878 lock_set(&apix_lock); 1879 1880 (void) apix_setup_io_intr(vecp); 1881 1882 lock_clear(&apix_lock); 1883 intr_restore(iflag); 1884 1885 APIC_VERBOSE_IOAPIC((CE_CONT, "apix: setup ioapic, irqno %x " 1886 "(ioapic %x, ipin %x) is bound to cpu %x, vector %x\n", 1887 irqno, ioapicindex, ipin, irqp->airq_cpu, irqp->airq_vector)); 1888 } 1889 1890 void 1891 ioapix_init_intr(int mask_apic) 1892 { 1893 int ioapicindex; 1894 int i, j; 1895 1896 /* mask interrupt vectors */ 1897 for (j = 0; j < apic_io_max && mask_apic; j++) { 1898 int intin_max; 1899 1900 ioapicindex = j; 1901 /* Bits 23-16 define the maximum redirection entries */ 1902 intin_max = (ioapic_read(ioapicindex, APIC_VERS_CMD) >> 16) 1903 & 0xff; 1904 for (i = 0; i <= intin_max; i++) 1905 ioapic_write(ioapicindex, APIC_RDT_CMD + 2 * i, 1906 AV_MASK); 1907 } 1908 1909 /* 1910 * Hack alert: deal with ACPI SCI interrupt chicken/egg here 1911 */ 1912 if (apic_sci_vect > 0) 1913 ioapix_setup_intr(apic_sci_vect, &apic_sci_flags); 1914 1915 /* 1916 * Hack alert: deal with ACPI HPET interrupt chicken/egg here. 1917 */ 1918 if (apic_hpet_vect > 0) 1919 ioapix_setup_intr(apic_hpet_vect, &apic_hpet_flags); 1920 }