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
local_apic_read(uint32_t reg)91 local_apic_read(uint32_t reg)
92 {
93 return ((uint32_t)apicadr[reg]);
94 }
95
96 static void
local_apic_write(uint32_t reg,uint64_t value)97 local_apic_write(uint32_t reg, uint64_t value)
98 {
99 apicadr[reg] = (uint32_t)value;
100 }
101
102 static int
get_local_apic_pri(void)103 get_local_apic_pri(void)
104 {
105 return ((int)getcr8());
106 }
107
108 static void
local_apic_write_task_reg(uint64_t value)109 local_apic_write_task_reg(uint64_t value)
110 {
111 setcr8((ulong_t)(value >> APIC_IPL_SHIFT));
112 }
113
114 static void
local_apic_write_int_cmd(uint32_t cpu_id,uint32_t cmd1)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
apic_send_EOI(uint32_t irq)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
apic_send_directed_EOI(uint32_t irq)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
apic_local_mode(void)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
apic_set_directed_EOI_handler()182 apic_set_directed_EOI_handler()
183 {
184 apic_reg_ops->apic_send_eoi = apic_send_directed_EOI;
185 }
186
187 int
apic_directed_EOI_supported()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