xref: /illumos-gate/usr/src/uts/i86pc/io/pcplusmp/apic_regops.c (revision 2ef50f010f7a3a07eb5a9f6001b9843fd868e26b)
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 #include <sys/cpuvar.h>
27 #include <sys/psm.h>
28 #include <sys/archsystm.h>
29 #include <sys/apic.h>
30 #include <sys/sunddi.h>
31 #include <sys/ddi_impldefs.h>
32 #include <sys/mach_intr.h>
33 #include <sys/sysmacros.h>
34 #include <sys/trap.h>
35 #include <sys/x86_archext.h>
36 #include <sys/privregs.h>
37 #include <sys/psm_common.h>
38 
39 /* Function prototypes of local apic and X2APIC */
40 static uint64_t local_apic_read(uint32_t reg);
41 static void local_apic_write(uint32_t reg, uint64_t value);
42 static int get_local_apic_pri(void);
43 static void local_apic_write_task_reg(uint64_t value);
44 static void local_apic_write_int_cmd(uint32_t cpu_id, uint32_t cmd1);
45 static uint64_t local_x2apic_read(uint32_t msr);
46 static void local_x2apic_write(uint32_t msr, uint64_t value);
47 static int get_local_x2apic_pri(void);
48 static void local_x2apic_write_task_reg(uint64_t value);
49 static void local_x2apic_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 int	x2apic_enable = 1;
64 int 	apic_mode = LOCAL_APIC;		/* Default mode is Local APIC */
65 
66 /* Uses MMIO (Memory Mapped IO) */
67 static apic_reg_ops_t local_apic_regs_ops = {
68 	local_apic_read,
69 	local_apic_write,
70 	get_local_apic_pri,
71 	local_apic_write_task_reg,
72 	local_apic_write_int_cmd,
73 	apic_send_EOI,
74 };
75 
76 /* X2APIC : Uses RDMSR/WRMSR instructions to access APIC registers */
77 static apic_reg_ops_t x2apic_regs_ops = {
78 	local_x2apic_read,
79 	local_x2apic_write,
80 	get_local_x2apic_pri,
81 	local_x2apic_write_task_reg,
82 	local_x2apic_write_int_cmd,
83 	apic_send_EOI,
84 };
85 
86 extern int apic_have_32bit_cr8;
87 
88 /* The default ops is local APIC (Memory Mapped IO) */
89 apic_reg_ops_t *apic_reg_ops = &local_apic_regs_ops;
90 
91 /*
92  * APIC register ops related data sturctures and functions.
93  */
94 int	apic_direct_EOI = 0;			/* Directed EOI Support */
95 
96 void apic_send_EOI();
97 void apic_send_directed_EOI(uint32_t irq);
98 
99 #define	X2APIC_CPUID_BIT	21
100 #define	X2APIC_ENABLE_BIT	10
101 
102 /*
103  * Local APIC Implementation
104  */
105 static uint64_t
106 local_apic_read(uint32_t reg)
107 {
108 	return ((uint32_t)apicadr[reg]);
109 }
110 
111 static void
112 local_apic_write(uint32_t reg, uint64_t value)
113 {
114 	apicadr[reg] = (uint32_t)value;
115 }
116 
117 static int
118 get_local_apic_pri(void)
119 {
120 #if defined(__amd64)
121 	return ((int)getcr8());
122 #else
123 	if (apic_have_32bit_cr8)
124 		return ((int)getcr8());
125 	return (apicadr[APIC_TASK_REG]);
126 #endif
127 }
128 
129 static void
130 local_apic_write_task_reg(uint64_t value)
131 {
132 #if defined(__amd64)
133 	setcr8((ulong_t)(value >> APIC_IPL_SHIFT));
134 #else
135 	if (apic_have_32bit_cr8)
136 		setcr8((ulong_t)(value >> APIC_IPL_SHIFT));
137 	else
138 		apicadr[APIC_TASK_REG] = (uint32_t)value;
139 #endif
140 }
141 
142 static void
143 local_apic_write_int_cmd(uint32_t cpu_id, uint32_t cmd1)
144 {
145 	apicadr[APIC_INT_CMD2] = cpu_id << APIC_ICR_ID_BIT_OFFSET;
146 	apicadr[APIC_INT_CMD1] = cmd1;
147 }
148 
149 /*
150  * X2APIC Implementation.
151  */
152 static uint64_t
153 local_x2apic_read(uint32_t msr)
154 {
155 	uint64_t i;
156 
157 	i = (uint64_t)(rdmsr(REG_X2APIC_BASE_MSR + (msr >> 2)) & 0xffffffff);
158 	return (i);
159 }
160 
161 static void
162 local_x2apic_write(uint32_t msr, uint64_t value)
163 {
164 	uint64_t tmp;
165 
166 	if (msr != APIC_EOI_REG) {
167 		tmp = rdmsr(REG_X2APIC_BASE_MSR + (msr >> 2));
168 		tmp = (tmp & 0xffffffff00000000) | value;
169 	} else {
170 		tmp = 0;
171 	}
172 
173 	wrmsr((REG_X2APIC_BASE_MSR + (msr >> 2)), tmp);
174 }
175 
176 static int
177 get_local_x2apic_pri(void)
178 {
179 	return (rdmsr(REG_X2APIC_BASE_MSR + (APIC_TASK_REG >> 2)));
180 }
181 
182 static void
183 local_x2apic_write_task_reg(uint64_t value)
184 {
185 	X2APIC_WRITE(APIC_TASK_REG, value);
186 }
187 
188 static void
189 local_x2apic_write_int_cmd(uint32_t cpu_id, uint32_t cmd1)
190 {
191 	wrmsr((REG_X2APIC_BASE_MSR + (APIC_INT_CMD1 >> 2)),
192 	    (((uint64_t)cpu_id << 32) | cmd1));
193 }
194 
195 /*ARGSUSED*/
196 void
197 apic_send_EOI(uint32_t irq)
198 {
199 	apic_reg_ops->apic_write(APIC_EOI_REG, 0);
200 }
201 
202 /*
203  * Support for Directed EOI capability is available in both the xAPIC
204  * and x2APIC mode.
205  */
206 void
207 apic_send_directed_EOI(uint32_t irq)
208 {
209 	uchar_t ioapicindex;
210 	uchar_t vector;
211 	apic_irq_t *apic_irq;
212 	short intr_index;
213 
214 	/*
215 	 * Following the EOI to the local APIC unit, perform a directed
216 	 * EOI to the IOxAPIC generating the interrupt by writing to its
217 	 * EOI register.
218 	 *
219 	 * A broadcast EOI is not generated.
220 	 */
221 	apic_reg_ops->apic_write(APIC_EOI_REG, 0);
222 
223 	apic_irq = apic_irq_table[irq];
224 	while (apic_irq) {
225 		intr_index = apic_irq->airq_mps_intr_index;
226 		if (intr_index == ACPI_INDEX || intr_index >= 0) {
227 			ioapicindex = apic_irq->airq_ioapicindex;
228 			vector = apic_irq->airq_vector;
229 			ioapic_write_eoi(ioapicindex, vector);
230 		}
231 		apic_irq = apic_irq->airq_next;
232 	}
233 }
234 
235 int
236 apic_detect_x2apic(void)
237 {
238 	struct cpuid_regs cp;
239 
240 	if (x2apic_enable == 0)
241 		return (0);
242 
243 	cp.cp_eax = 1;
244 	(void) __cpuid_insn(&cp);
245 
246 	return ((cp.cp_ecx & (0x1 << X2APIC_CPUID_BIT)) ? 1 : 0);
247 }
248 
249 void
250 apic_enable_x2apic(void)
251 {
252 	uint64_t apic_base_msr;
253 
254 	if (apic_local_mode() == LOCAL_X2APIC) {
255 		/* BIOS apparently has enabled X2APIC */
256 		if (apic_mode != LOCAL_X2APIC)
257 			x2apic_update_psm();
258 		return;
259 	}
260 
261 	/*
262 	 * This is the first time we are enabling X2APIC on this CPU
263 	 */
264 	apic_base_msr = rdmsr(REG_APIC_BASE_MSR);
265 	apic_base_msr = apic_base_msr | (0x1 << X2APIC_ENABLE_BIT);
266 	wrmsr(REG_APIC_BASE_MSR, apic_base_msr);
267 
268 	if (apic_mode != LOCAL_X2APIC)
269 		x2apic_update_psm();
270 }
271 
272 /*
273  * Determine which mode the current CPU is in. See the table above.
274  * (IA32_APIC_BASE[11])   (IA32_APIC_BASE[10])
275  */
276 int
277 apic_local_mode(void)
278 {
279 	uint64_t apic_base_msr;
280 	int bit = ((0x1 << (X2APIC_ENABLE_BIT + 1)) |
281 	    (0x1 << X2APIC_ENABLE_BIT));
282 
283 	apic_base_msr = rdmsr(REG_APIC_BASE_MSR);
284 
285 	if ((apic_base_msr & bit) == bit)
286 		return (LOCAL_X2APIC);
287 	else
288 		return (LOCAL_APIC);
289 }
290 
291 void
292 apic_change_eoi()
293 {
294 	apic_reg_ops->apic_send_eoi = apic_send_directed_EOI;
295 }
296 
297 /*
298  * Change apic_reg_ops depending upon the apic_mode.
299  */
300 void
301 apic_change_ops()
302 {
303 	if (apic_mode == LOCAL_APIC)
304 		apic_reg_ops = &local_apic_regs_ops;
305 	else if (apic_mode == LOCAL_X2APIC)
306 		apic_reg_ops = &x2apic_regs_ops;
307 }
308 
309 /*
310  * Generates an interprocessor interrupt to another CPU when X2APIC mode is
311  * enabled.
312  */
313 void
314 x2apic_send_ipi(int cpun, int ipl)
315 {
316 	int vector;
317 	ulong_t flag;
318 
319 	ASSERT(apic_mode == LOCAL_X2APIC);
320 
321 	/*
322 	 * With X2APIC, Intel relaxed the semantics of the
323 	 * WRMSR instruction such that references to the X2APIC
324 	 * MSR registers are no longer serializing instructions.
325 	 * The code that initiates IPIs assumes that some sort
326 	 * of memory serialization occurs. The old APIC code
327 	 * did a write to uncachable memory mapped registers.
328 	 * Any reference to uncached memory is a serializing
329 	 * operation. To mimic those semantics here, we do an
330 	 * atomic operation, which translates to a LOCK OR instruction,
331 	 * which is serializing.
332 	 */
333 	atomic_or_ulong(&flag, 1);
334 
335 	vector = apic_resv_vector[ipl];
336 
337 	flag = intr_clear();
338 
339 	/*
340 	 * According to X2APIC specification in section '2.3.5.1' of
341 	 * Interrupt Command Register Semantics, the semantics of
342 	 * programming Interrupt Command Register to dispatch an interrupt
343 	 * is simplified. A single MSR write to the 64-bit ICR is required
344 	 * for dispatching an interrupt. Specifically with the 64-bit MSR
345 	 * interface to ICR, system software is not required to check the
346 	 * status of the delivery status bit prior to writing to the ICR
347 	 * to send an IPI. With the removal of the Delivery Status bit,
348 	 * system software no longer has a reason to read the ICR. It remains
349 	 * readable only to aid in debugging.
350 	 */
351 #ifdef	DEBUG
352 	APIC_AV_PENDING_SET();
353 #endif	/* DEBUG */
354 
355 	if ((cpun == psm_get_cpu_id())) {
356 		X2APIC_WRITE(X2APIC_SELF_IPI, vector);
357 	} else {
358 		apic_reg_ops->apic_write_int_cmd(
359 		    apic_cpus[cpun].aci_local_id, vector);
360 	}
361 
362 	intr_restore(flag);
363 }
364 
365 /*
366  * Generates IPI to another CPU depending on the local APIC mode.
367  * apic_send_ipi() and x2apic_send_ipi() depends on the configured
368  * mode of the local APIC, but that may not match the actual mode
369  * early in CPU startup.
370  *
371  * Any changes made to this routine must be accompanied by similar
372  * changes to apic_send_ipi().
373  */
374 void
375 apic_common_send_ipi(int cpun, int ipl)
376 {
377 	int vector;
378 	ulong_t flag;
379 	int mode = apic_local_mode();
380 
381 	if (mode == LOCAL_X2APIC) {
382 		x2apic_send_ipi(cpun, ipl);
383 		return;
384 	}
385 
386 	ASSERT(mode == LOCAL_APIC);
387 
388 	vector = apic_resv_vector[ipl];
389 	ASSERT((vector >= APIC_BASE_VECT) && (vector <= APIC_SPUR_INTR));
390 	flag = intr_clear();
391 	while (local_apic_regs_ops.apic_read(APIC_INT_CMD1) & AV_PENDING)
392 		apic_ret();
393 	local_apic_regs_ops.apic_write_int_cmd(apic_cpus[cpun].aci_local_id,
394 	    vector);
395 	intr_restore(flag);
396 }
397