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  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 /*
  26  * Copyright 2014 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
  27  * Copyright (c) 2014 by Delphix. All rights reserved.
  28  * Copyright 2017 Joyent, Inc.
  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/x86_archext.h>
  41 #include <sys/privregs.h>
  42 #include <sys/psm_common.h>
  43 
  44 /* Function prototypes of local apic */
  45 static uint64_t local_apic_read(uint32_t reg);
  46 static void local_apic_write(uint32_t reg, uint64_t value);
  47 static int get_local_apic_pri(void);
  48 static void local_apic_write_task_reg(uint64_t value);
  49 static void local_apic_write_int_cmd(uint32_t cpu_id, uint32_t cmd1);
  50 
  51 /*
  52  * According to the X2APIC specification:
  53  *
  54  *   xAPIC global enable    X2APIC enable         Description
  55  *   (IA32_APIC_BASE[11])   (IA32_APIC_BASE[10])
  56  * -----------------------------------------------------------
  57  *      0                       0       APIC is disabled
  58  *      0                       1       Invalid
  59  *      1                       0       APIC is enabled in xAPIC mode
  60  *      1                       1       APIC is enabled in X2APIC mode
  61  * -----------------------------------------------------------
  62  */
  63 apic_mode_t apic_mode = LOCAL_APIC;     /* Default mode is Local APIC */
  64 
  65 /* See apic_directed_EOI_supported().  Currently 3-state variable. */
  66 volatile int apic_directed_eoi_state = 2;
  67 
  68 /* Uses MMIO (Memory Mapped IO) */
  69 apic_reg_ops_t local_apic_regs_ops = {
  70         local_apic_read,
  71         local_apic_write,
  72         get_local_apic_pri,
  73         local_apic_write_task_reg,
  74         local_apic_write_int_cmd,
  75         apic_send_EOI,
  76 };
  77 
  78 int apic_have_32bit_cr8 = 0;
  79 
  80 /* The default ops is local APIC (Memory Mapped IO) */
  81 apic_reg_ops_t *apic_reg_ops = &local_apic_regs_ops;
  82 
  83 /*
  84  * APIC register ops related data sturctures and functions.
  85  */
  86 void apic_send_EOI();
  87 void apic_send_directed_EOI(uint32_t irq);
  88 
  89 /*
  90  * Local APIC Implementation
  91  */
  92 static uint64_t
  93 local_apic_read(uint32_t reg)
  94 {
  95         return ((uint32_t)apicadr[reg]);
  96 }
  97 
  98 static void
  99 local_apic_write(uint32_t reg, uint64_t value)
 100 {
 101         apicadr[reg] = (uint32_t)value;
 102 }
 103 
 104 static int
 105 get_local_apic_pri(void)
 106 {
 107 #if defined(__amd64)
 108         return ((int)getcr8());
 109 #else
 110         if (apic_have_32bit_cr8)
 111                 return ((int)getcr8());
 112         return (apicadr[APIC_TASK_REG]);
 113 #endif
 114 }
 115 
 116 static void
 117 local_apic_write_task_reg(uint64_t value)
 118 {
 119 #if defined(__amd64)
 120         setcr8((ulong_t)(value >> APIC_IPL_SHIFT));
 121 #else
 122         if (apic_have_32bit_cr8)
 123                 setcr8((ulong_t)(value >> APIC_IPL_SHIFT));
 124         else
 125                 apicadr[APIC_TASK_REG] = (uint32_t)value;
 126 #endif
 127 }
 128 
 129 static void
 130 local_apic_write_int_cmd(uint32_t cpu_id, uint32_t cmd1)
 131 {
 132         apicadr[APIC_INT_CMD2] = cpu_id << APIC_ICR_ID_BIT_OFFSET;
 133         apicadr[APIC_INT_CMD1] = cmd1;
 134 }
 135 
 136 
 137 /*ARGSUSED*/
 138 void
 139 apic_send_EOI(uint32_t irq)
 140 {
 141         apic_reg_ops->apic_write(APIC_EOI_REG, 0);
 142 }
 143 
 144 /*
 145  * Support for Directed EOI capability is available in both the xAPIC
 146  * and x2APIC mode.
 147  */
 148 void
 149 apic_send_directed_EOI(uint32_t irq)
 150 {
 151         uchar_t ioapicindex;
 152         uchar_t vector;
 153         apic_irq_t *apic_irq;
 154         short intr_index;
 155 
 156         /*
 157          * Following the EOI to the local APIC unit, perform a directed
 158          * EOI to the IOxAPIC generating the interrupt by writing to its
 159          * EOI register.
 160          *
 161          * A broadcast EOI is not generated.
 162          */
 163         apic_reg_ops->apic_write(APIC_EOI_REG, 0);
 164 
 165         apic_irq = apic_irq_table[irq];
 166         while (apic_irq) {
 167                 intr_index = apic_irq->airq_mps_intr_index;
 168                 if (intr_index == ACPI_INDEX || intr_index >= 0) {
 169                         ioapicindex = apic_irq->airq_ioapicindex;
 170                         vector = apic_irq->airq_vector;
 171                         ioapic_write_eoi(ioapicindex, vector);
 172                 }
 173                 apic_irq = apic_irq->airq_next;
 174         }
 175 }
 176 
 177 /*
 178  * Determine which mode the current CPU is in. See the table above.
 179  * (IA32_APIC_BASE[11])   (IA32_APIC_BASE[10])
 180  */
 181 int
 182 apic_local_mode(void)
 183 {
 184         uint64_t apic_base_msr;
 185         int bit = ((0x1 << (X2APIC_ENABLE_BIT + 1)) |
 186             (0x1 << X2APIC_ENABLE_BIT));
 187 
 188         apic_base_msr = rdmsr(REG_APIC_BASE_MSR);
 189 
 190         if ((apic_base_msr & bit) == bit)
 191                 return (LOCAL_X2APIC);
 192         else
 193                 return (LOCAL_APIC);
 194 }
 195 
 196 void
 197 apic_set_directed_EOI_handler()
 198 {
 199         apic_reg_ops->apic_send_eoi = apic_send_directed_EOI;
 200 }
 201 
 202 int
 203 apic_directed_EOI_supported()
 204 {
 205         uint32_t ver;
 206 
 207         /*
 208          * There are some known issues with some versions of Linux KVM and QEMU
 209          * where by directed EOIs do not properly function and instead get
 210          * coalesced at the hypervisor, causing the host not to see interrupts.
 211          * Thus, when the platform is KVM, we would like to disable it by
 212          * default, but keep it available otherwise.
 213          *
 214          * We use a three-state variable (apic_directed_eoi_state) to determine
 215          * how we handle directed EOI.
 216          *
 217          * 0 --> Don't do directed EOI at all.
 218          * 1 --> Do directed EOI if available, no matter the HW environment.
 219          * 2 --> Don't do directed EOI on KVM, but do it otherwise if available.
 220          *
 221          * If some grinning weirdo put something else in there, treat it as '2'
 222          * (i.e. the current default).
 223          *
 224          * Note, at this time illumos KVM does not identify as KVM. If it does,
 225          * we'll need to do some work to determine if it should be caught by
 226          * this or if it should show up as its own value of platform_type.
 227          */
 228         switch (apic_directed_eoi_state) {
 229         case 0:
 230                 /* Don't do it at all. */
 231                 return (0);
 232         case 1:
 233                 break;
 234         case 2:
 235         default:
 236                 /* Only do it if we aren't on KVM. */
 237                 if (get_hwenv() == HW_KVM)
 238                         return (0);
 239                 /* FALLTHRU */
 240         }
 241 
 242         ver = apic_reg_ops->apic_read(APIC_VERS_REG);
 243         if (ver & APIC_DIRECTED_EOI_BIT)
 244                 return (1);
 245 
 246         return (0);
 247 }