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 }