xref: /illumos-gate/usr/src/uts/i86pc/io/pcplusmp/apic_regops.c (revision 30165b7f6753bc3d48c52319bed7ec7b3ea36b3c)
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 }
248