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