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 /* The default ops is local APIC (Memory Mapped IO) */ 79 apic_reg_ops_t *apic_reg_ops = &local_apic_regs_ops; 80 81 /* 82 * APIC register ops related data sturctures and functions. 83 */ 84 void apic_send_EOI(); 85 void apic_send_directed_EOI(uint32_t irq); 86 87 /* 88 * Local APIC Implementation 89 */ 90 static uint64_t 91 local_apic_read(uint32_t reg) 92 { 93 return ((uint32_t)apicadr[reg]); 94 } 95 96 static void 97 local_apic_write(uint32_t reg, uint64_t value) 98 { 99 apicadr[reg] = (uint32_t)value; 100 } 101 102 static int 103 get_local_apic_pri(void) 104 { 105 return ((int)getcr8()); 106 } 107 108 static void 109 local_apic_write_task_reg(uint64_t value) 110 { 111 setcr8((ulong_t)(value >> APIC_IPL_SHIFT)); 112 } 113 114 static void 115 local_apic_write_int_cmd(uint32_t cpu_id, uint32_t cmd1) 116 { 117 apicadr[APIC_INT_CMD2] = cpu_id << APIC_ICR_ID_BIT_OFFSET; 118 apicadr[APIC_INT_CMD1] = cmd1; 119 } 120 121 122 /*ARGSUSED*/ 123 void 124 apic_send_EOI(uint32_t irq) 125 { 126 apic_reg_ops->apic_write(APIC_EOI_REG, 0); 127 } 128 129 /* 130 * Support for Directed EOI capability is available in both the xAPIC 131 * and x2APIC mode. 132 */ 133 void 134 apic_send_directed_EOI(uint32_t irq) 135 { 136 uchar_t ioapicindex; 137 uchar_t vector; 138 apic_irq_t *apic_irq; 139 short intr_index; 140 141 /* 142 * Following the EOI to the local APIC unit, perform a directed 143 * EOI to the IOxAPIC generating the interrupt by writing to its 144 * EOI register. 145 * 146 * A broadcast EOI is not generated. 147 */ 148 apic_reg_ops->apic_write(APIC_EOI_REG, 0); 149 150 apic_irq = apic_irq_table[irq]; 151 while (apic_irq) { 152 intr_index = apic_irq->airq_mps_intr_index; 153 if (intr_index == ACPI_INDEX || intr_index >= 0) { 154 ioapicindex = apic_irq->airq_ioapicindex; 155 vector = apic_irq->airq_vector; 156 ioapic_write_eoi(ioapicindex, vector); 157 } 158 apic_irq = apic_irq->airq_next; 159 } 160 } 161 162 /* 163 * Determine which mode the current CPU is in. See the table above. 164 * (IA32_APIC_BASE[11]) (IA32_APIC_BASE[10]) 165 */ 166 int 167 apic_local_mode(void) 168 { 169 uint64_t apic_base_msr; 170 int bit = ((0x1 << (X2APIC_ENABLE_BIT + 1)) | 171 (0x1 << X2APIC_ENABLE_BIT)); 172 173 apic_base_msr = rdmsr(REG_APIC_BASE_MSR); 174 175 if ((apic_base_msr & bit) == bit) 176 return (LOCAL_X2APIC); 177 else 178 return (LOCAL_APIC); 179 } 180 181 void 182 apic_set_directed_EOI_handler() 183 { 184 apic_reg_ops->apic_send_eoi = apic_send_directed_EOI; 185 } 186 187 int 188 apic_directed_EOI_supported() 189 { 190 uint32_t ver; 191 192 /* 193 * There are some known issues with some versions of Linux KVM and QEMU 194 * where by directed EOIs do not properly function and instead get 195 * coalesced at the hypervisor, causing the host not to see interrupts. 196 * Thus, when the platform is KVM, we would like to disable it by 197 * default, but keep it available otherwise. 198 * 199 * We use a three-state variable (apic_directed_eoi_state) to determine 200 * how we handle directed EOI. 201 * 202 * 0 --> Don't do directed EOI at all. 203 * 1 --> Do directed EOI if available, no matter the HW environment. 204 * 2 --> Don't do directed EOI on KVM, but do it otherwise if available. 205 * 206 * If some grinning weirdo put something else in there, treat it as '2' 207 * (i.e. the current default). 208 * 209 * Note, at this time illumos KVM does not identify as KVM. If it does, 210 * we'll need to do some work to determine if it should be caught by 211 * this or if it should show up as its own value of platform_type. 212 */ 213 switch (apic_directed_eoi_state) { 214 case 0: 215 /* Don't do it at all. */ 216 return (0); 217 case 1: 218 break; 219 case 2: 220 default: 221 /* Only do it if we aren't on KVM. */ 222 if (get_hwenv() == HW_KVM) 223 return (0); 224 /* FALLTHRU */ 225 } 226 227 ver = apic_reg_ops->apic_read(APIC_VERS_REG); 228 if (ver & APIC_DIRECTED_EOI_BIT) 229 return (1); 230 231 return (0); 232 } 233