Print this page
8626 make pcplusmp and apix warning-free
Reviewed by: Robert Mustacchi <rm@joyent.com>
Reviewed by: Jerry Jelinek <jerry.jelinek@joyent.com>


   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  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  23  * Copyright 2013 Pluribus Networks, Inc.

  24  */
  25 
  26 /*
  27  * apic_introp.c:
  28  *      Has code for Advanced DDI interrupt framework support.
  29  */
  30 
  31 #include <sys/cpuvar.h>
  32 #include <sys/psm.h>
  33 #include <sys/archsystm.h>
  34 #include <sys/apic.h>
  35 #include <sys/sunddi.h>
  36 #include <sys/ddi_impldefs.h>
  37 #include <sys/mach_intr.h>
  38 #include <sys/sysmacros.h>
  39 #include <sys/trap.h>
  40 #include <sys/pci.h>
  41 #include <sys/pci_intr_lib.h>
  42 #include <sys/apic_common.h>
  43 


  44 extern struct av_head autovect[];
  45 
  46 /*
  47  *      Local Function Prototypes
  48  */
  49 apic_irq_t      *apic_find_irq(dev_info_t *, struct intrspec *, int);
  50 
  51 /*
  52  * apic_pci_msi_enable_vector:
  53  *      Set the address/data fields in the MSI/X capability structure
  54  *      XXX: MSI-X support
  55  */
  56 /* ARGSUSED */
  57 void
  58 apic_pci_msi_enable_vector(apic_irq_t *irq_ptr, int type, int inum, int vector,
  59     int count, int target_apic_id)
  60 {
  61         uint64_t                msi_addr, msi_data;
  62         ushort_t                msi_ctrl;
  63         dev_info_t              *dip = irq_ptr->airq_dip;


 209          * starting vector
 210          */
 211         msibits = count - 1;
 212 
 213         /* It has to be contiguous */
 214         for (i = lowest; i <= highest; i++) {
 215                 navail = 0;
 216 
 217                 /*
 218                  * starting vector has to be aligned accordingly for
 219                  * multiple MSIs
 220                  */
 221                 if (msibits)
 222                         i = (i + msibits) & ~msibits;
 223                 start = i;
 224                 while ((apic_vector_to_irq[i] == APIC_RESV_IRQ) &&
 225                     (i <= highest)) {
 226                         if (APIC_CHECK_RESERVE_VECTORS(i))
 227                                 break;
 228                         navail++;
 229                         if (navail >= count)
 230                                 return (start);


 231                         i++;
 232                 }
 233         }
 234         return (0);
 235 }
 236 
 237 
 238 /*
 239  * It finds the apic_irq_t associates with the dip, ispec and type.
 240  */
 241 apic_irq_t *
 242 apic_find_irq(dev_info_t *dip, struct intrspec *ispec, int type)
 243 {
 244         apic_irq_t      *irqp;
 245         int i;
 246 
 247         DDI_INTR_IMPLDBG((CE_CONT, "apic_find_irq: dip=0x%p vec=0x%x "
 248             "ipl=0x%x type=0x%x\n", (void *)dip, ispec->intrspec_vec,
 249             ispec->intrspec_pri, type));
 250 


 488                 return (PSM_FAILURE);
 489         }
 490         /*
 491          * keep tracking the default interrupt cpu binding
 492          */
 493         irqp->airq_cpu = cpu;
 494 
 495         *result = 0;
 496         return (PSM_SUCCESS);
 497 }
 498 
 499 static int
 500 apic_grp_set_cpu(int irqno, int new_cpu, int *result)
 501 {
 502         dev_info_t *orig_dip;
 503         uint32_t orig_cpu;
 504         ulong_t iflag;
 505         apic_irq_t *irqps[PCI_MSI_MAX_INTRS];
 506         int i;
 507         int cap_ptr;
 508         int msi_mask_off;
 509         ushort_t msi_ctrl;
 510         uint32_t msi_pvm;
 511         ddi_acc_handle_t handle;
 512         int num_vectors = 0;
 513         uint32_t vector;
 514 
 515         DDI_INTR_IMPLDBG((CE_CONT, "APIC_GRP_SET_CPU\n"));
 516 
 517         /*
 518          * Take mutex to insure that table doesn't change out from underneath
 519          * us while we're playing with it.
 520          */
 521         mutex_enter(&airq_mutex);
 522         irqps[0] = apic_irq_table[irqno];
 523         orig_cpu = irqps[0]->airq_temp_cpu;
 524         orig_dip = irqps[0]->airq_dip;
 525         num_vectors = irqps[0]->airq_intin_no;
 526         vector = irqps[0]->airq_vector;
 527 
 528         /* A "group" of 1 */
 529         if (num_vectors == 1) {
 530                 mutex_exit(&airq_mutex);


 624         /* Reenable vectors if per vector masking is supported. */
 625         if (msi_ctrl & PCI_MSI_PVM_MASK) {
 626                 pci_config_put32(handle, msi_mask_off, msi_pvm);
 627                 DDI_INTR_IMPLDBG((CE_CONT,
 628                     "set_grp: pvm supported.  Mask restored to 0x%x\n",
 629                     pci_config_get32(handle, msi_mask_off)));
 630         }
 631 
 632 set_grp_intr_done:
 633         if (*result != 0)
 634                 return (PSM_FAILURE);
 635 
 636         return (PSM_SUCCESS);
 637 }
 638 
 639 int
 640 apic_get_vector_intr_info(int vecirq, apic_get_intr_t *intr_params_p)
 641 {
 642         struct autovec *av_dev;
 643         uchar_t irqno;
 644         int i;
 645         apic_irq_t *irq_p;
 646 
 647         /* Sanity check the vector/irq argument. */
 648         ASSERT((vecirq >= 0) || (vecirq <= APIC_MAX_VECTOR));
 649 
 650         mutex_enter(&airq_mutex);
 651 
 652         /*
 653          * Convert the vecirq arg to an irq using vector_to_irq table
 654          * if the arg is a vector.  Pass thru if already an irq.
 655          */
 656         if ((intr_params_p->avgi_req_flags & PSMGI_INTRBY_FLAGS) ==
 657             PSMGI_INTRBY_VEC)
 658                 irqno = apic_vector_to_irq[vecirq];
 659         else
 660                 irqno = vecirq;
 661 
 662         irq_p = apic_irq_table[irqno];
 663 
 664         if ((irq_p == NULL) ||
 665             ((irq_p->airq_mps_intr_index != RESERVE_INDEX) &&
 666             ((irq_p->airq_temp_cpu == IRQ_UNBOUND) ||
 667             (irq_p->airq_temp_cpu == IRQ_UNINIT)))) {
 668                 mutex_exit(&airq_mutex);
 669                 return (PSM_FAILURE);
 670         }
 671 
 672         if (intr_params_p->avgi_req_flags & PSMGI_REQ_CPUID) {
 673 
 674                 /* Get the (temp) cpu from apic_irq table, indexed by irq. */
 675                 intr_params_p->avgi_cpu_id = irq_p->airq_temp_cpu;
 676 
 677                 /* Return user bound info for intrd. */
 678                 if (intr_params_p->avgi_cpu_id & IRQ_USER_BOUND) {
 679                         intr_params_p->avgi_cpu_id &= ~IRQ_USER_BOUND;
 680                         intr_params_p->avgi_cpu_id |= PSMGI_CPU_USER_BOUND;


 683 
 684         if (intr_params_p->avgi_req_flags & PSMGI_REQ_VECTOR)
 685                 intr_params_p->avgi_vector = irq_p->airq_vector;
 686 
 687         if (intr_params_p->avgi_req_flags &
 688             (PSMGI_REQ_NUM_DEVS | PSMGI_REQ_GET_DEVS))
 689                 /* Get number of devices from apic_irq table shared field. */
 690                 intr_params_p->avgi_num_devs = irq_p->airq_share;
 691 
 692         if (intr_params_p->avgi_req_flags &  PSMGI_REQ_GET_DEVS) {
 693 
 694                 intr_params_p->avgi_req_flags  |= PSMGI_REQ_NUM_DEVS;
 695 
 696                 /* Some devices have NULL dip.  Don't count these. */
 697                 if (intr_params_p->avgi_num_devs > 0) {
 698                         for (i = 0, av_dev = autovect[irqno].avh_link;
 699                             av_dev; av_dev = av_dev->av_link)
 700                                 if (av_dev->av_vector && av_dev->av_dip)
 701                                         i++;
 702                         intr_params_p->avgi_num_devs =
 703                             MIN(intr_params_p->avgi_num_devs, i);
 704                 }
 705 
 706                 /* There are no viable dips to return. */
 707                 if (intr_params_p->avgi_num_devs == 0)
 708                         intr_params_p->avgi_dip_list = NULL;
 709 
 710                 else {  /* Return list of dips */
 711 
 712                         /* Allocate space in array for that number of devs. */
 713                         intr_params_p->avgi_dip_list = kmem_zalloc(
 714                             intr_params_p->avgi_num_devs *
 715                             sizeof (dev_info_t *),
 716                             KM_SLEEP);
 717 
 718                         /*
 719                          * Loop through the device list of the autovec table
 720                          * filling in the dip array.
 721                          *
 722                          * Note that the autovect table may have some special
 723                          * entries which contain NULL dips.  These will be




   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  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  23  * Copyright 2013 Pluribus Networks, Inc.
  24  * Copyright 2017 Joyent, Inc.
  25  */
  26 
  27 /*
  28  * apic_introp.c:
  29  *      Has code for Advanced DDI interrupt framework support.
  30  */
  31 
  32 #include <sys/cpuvar.h>
  33 #include <sys/psm.h>
  34 #include <sys/archsystm.h>
  35 #include <sys/apic.h>
  36 #include <sys/sunddi.h>
  37 #include <sys/ddi_impldefs.h>
  38 #include <sys/mach_intr.h>
  39 #include <sys/sysmacros.h>
  40 #include <sys/trap.h>
  41 #include <sys/pci.h>
  42 #include <sys/pci_intr_lib.h>
  43 #include <sys/apic_common.h>
  44 
  45 #define UCHAR_MAX       UINT8_MAX
  46 
  47 extern struct av_head autovect[];
  48 
  49 /*
  50  *      Local Function Prototypes
  51  */
  52 apic_irq_t      *apic_find_irq(dev_info_t *, struct intrspec *, int);
  53 
  54 /*
  55  * apic_pci_msi_enable_vector:
  56  *      Set the address/data fields in the MSI/X capability structure
  57  *      XXX: MSI-X support
  58  */
  59 /* ARGSUSED */
  60 void
  61 apic_pci_msi_enable_vector(apic_irq_t *irq_ptr, int type, int inum, int vector,
  62     int count, int target_apic_id)
  63 {
  64         uint64_t                msi_addr, msi_data;
  65         ushort_t                msi_ctrl;
  66         dev_info_t              *dip = irq_ptr->airq_dip;


 212          * starting vector
 213          */
 214         msibits = count - 1;
 215 
 216         /* It has to be contiguous */
 217         for (i = lowest; i <= highest; i++) {
 218                 navail = 0;
 219 
 220                 /*
 221                  * starting vector has to be aligned accordingly for
 222                  * multiple MSIs
 223                  */
 224                 if (msibits)
 225                         i = (i + msibits) & ~msibits;
 226                 start = i;
 227                 while ((apic_vector_to_irq[i] == APIC_RESV_IRQ) &&
 228                     (i <= highest)) {
 229                         if (APIC_CHECK_RESERVE_VECTORS(i))
 230                                 break;
 231                         navail++;
 232                         if (navail >= count) {
 233                                 ASSERT(start >= 0 && start <= UCHAR_MAX);
 234                                 return ((uchar_t)start);
 235                         }
 236                         i++;
 237                 }
 238         }
 239         return (0);
 240 }
 241 
 242 
 243 /*
 244  * It finds the apic_irq_t associates with the dip, ispec and type.
 245  */
 246 apic_irq_t *
 247 apic_find_irq(dev_info_t *dip, struct intrspec *ispec, int type)
 248 {
 249         apic_irq_t      *irqp;
 250         int i;
 251 
 252         DDI_INTR_IMPLDBG((CE_CONT, "apic_find_irq: dip=0x%p vec=0x%x "
 253             "ipl=0x%x type=0x%x\n", (void *)dip, ispec->intrspec_vec,
 254             ispec->intrspec_pri, type));
 255 


 493                 return (PSM_FAILURE);
 494         }
 495         /*
 496          * keep tracking the default interrupt cpu binding
 497          */
 498         irqp->airq_cpu = cpu;
 499 
 500         *result = 0;
 501         return (PSM_SUCCESS);
 502 }
 503 
 504 static int
 505 apic_grp_set_cpu(int irqno, int new_cpu, int *result)
 506 {
 507         dev_info_t *orig_dip;
 508         uint32_t orig_cpu;
 509         ulong_t iflag;
 510         apic_irq_t *irqps[PCI_MSI_MAX_INTRS];
 511         int i;
 512         int cap_ptr;
 513         int msi_mask_off = 0;
 514         ushort_t msi_ctrl;
 515         uint32_t msi_pvm = 0;
 516         ddi_acc_handle_t handle;
 517         int num_vectors = 0;
 518         uint32_t vector;
 519 
 520         DDI_INTR_IMPLDBG((CE_CONT, "APIC_GRP_SET_CPU\n"));
 521 
 522         /*
 523          * Take mutex to insure that table doesn't change out from underneath
 524          * us while we're playing with it.
 525          */
 526         mutex_enter(&airq_mutex);
 527         irqps[0] = apic_irq_table[irqno];
 528         orig_cpu = irqps[0]->airq_temp_cpu;
 529         orig_dip = irqps[0]->airq_dip;
 530         num_vectors = irqps[0]->airq_intin_no;
 531         vector = irqps[0]->airq_vector;
 532 
 533         /* A "group" of 1 */
 534         if (num_vectors == 1) {
 535                 mutex_exit(&airq_mutex);


 629         /* Reenable vectors if per vector masking is supported. */
 630         if (msi_ctrl & PCI_MSI_PVM_MASK) {
 631                 pci_config_put32(handle, msi_mask_off, msi_pvm);
 632                 DDI_INTR_IMPLDBG((CE_CONT,
 633                     "set_grp: pvm supported.  Mask restored to 0x%x\n",
 634                     pci_config_get32(handle, msi_mask_off)));
 635         }
 636 
 637 set_grp_intr_done:
 638         if (*result != 0)
 639                 return (PSM_FAILURE);
 640 
 641         return (PSM_SUCCESS);
 642 }
 643 
 644 int
 645 apic_get_vector_intr_info(int vecirq, apic_get_intr_t *intr_params_p)
 646 {
 647         struct autovec *av_dev;
 648         uchar_t irqno;
 649         uint i;
 650         apic_irq_t *irq_p;
 651 
 652         /* Sanity check the vector/irq argument. */
 653         ASSERT((vecirq >= 0) || (vecirq <= APIC_MAX_VECTOR));
 654 
 655         mutex_enter(&airq_mutex);
 656 
 657         /*
 658          * Convert the vecirq arg to an irq using vector_to_irq table
 659          * if the arg is a vector.  Pass thru if already an irq.
 660          */
 661         if ((intr_params_p->avgi_req_flags & PSMGI_INTRBY_FLAGS) ==
 662             PSMGI_INTRBY_VEC)
 663                 irqno = apic_vector_to_irq[vecirq];
 664         else
 665                 irqno = (uchar_t)vecirq;
 666 
 667         irq_p = apic_irq_table[irqno];
 668 
 669         if ((irq_p == NULL) ||
 670             ((irq_p->airq_mps_intr_index != RESERVE_INDEX) &&
 671             ((irq_p->airq_temp_cpu == IRQ_UNBOUND) ||
 672             (irq_p->airq_temp_cpu == IRQ_UNINIT)))) {
 673                 mutex_exit(&airq_mutex);
 674                 return (PSM_FAILURE);
 675         }
 676 
 677         if (intr_params_p->avgi_req_flags & PSMGI_REQ_CPUID) {
 678 
 679                 /* Get the (temp) cpu from apic_irq table, indexed by irq. */
 680                 intr_params_p->avgi_cpu_id = irq_p->airq_temp_cpu;
 681 
 682                 /* Return user bound info for intrd. */
 683                 if (intr_params_p->avgi_cpu_id & IRQ_USER_BOUND) {
 684                         intr_params_p->avgi_cpu_id &= ~IRQ_USER_BOUND;
 685                         intr_params_p->avgi_cpu_id |= PSMGI_CPU_USER_BOUND;


 688 
 689         if (intr_params_p->avgi_req_flags & PSMGI_REQ_VECTOR)
 690                 intr_params_p->avgi_vector = irq_p->airq_vector;
 691 
 692         if (intr_params_p->avgi_req_flags &
 693             (PSMGI_REQ_NUM_DEVS | PSMGI_REQ_GET_DEVS))
 694                 /* Get number of devices from apic_irq table shared field. */
 695                 intr_params_p->avgi_num_devs = irq_p->airq_share;
 696 
 697         if (intr_params_p->avgi_req_flags &  PSMGI_REQ_GET_DEVS) {
 698 
 699                 intr_params_p->avgi_req_flags  |= PSMGI_REQ_NUM_DEVS;
 700 
 701                 /* Some devices have NULL dip.  Don't count these. */
 702                 if (intr_params_p->avgi_num_devs > 0) {
 703                         for (i = 0, av_dev = autovect[irqno].avh_link;
 704                             av_dev; av_dev = av_dev->av_link)
 705                                 if (av_dev->av_vector && av_dev->av_dip)
 706                                         i++;
 707                         intr_params_p->avgi_num_devs =
 708                             (uchar_t)MIN(intr_params_p->avgi_num_devs, i);
 709                 }
 710 
 711                 /* There are no viable dips to return. */
 712                 if (intr_params_p->avgi_num_devs == 0)
 713                         intr_params_p->avgi_dip_list = NULL;
 714 
 715                 else {  /* Return list of dips */
 716 
 717                         /* Allocate space in array for that number of devs. */
 718                         intr_params_p->avgi_dip_list = kmem_zalloc(
 719                             intr_params_p->avgi_num_devs *
 720                             sizeof (dev_info_t *),
 721                             KM_SLEEP);
 722 
 723                         /*
 724                          * Loop through the device list of the autovec table
 725                          * filling in the dip array.
 726                          *
 727                          * Note that the autovect table may have some special
 728                          * entries which contain NULL dips.  These will be