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