xref: /illumos-gate/usr/src/uts/i86pc/io/pcplusmp/apic_regops.c (revision b6917abefc343244b784f0cc34bc65b01469c3bf)
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 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <sys/cpuvar.h>
29 #include <sys/psm.h>
30 #include <sys/archsystm.h>
31 #include <sys/apic.h>
32 #include <sys/sunddi.h>
33 #include <sys/ddi_impldefs.h>
34 #include <sys/mach_intr.h>
35 #include <sys/sysmacros.h>
36 #include <sys/trap.h>
37 #include <sys/x86_archext.h>
38 #include <sys/privregs.h>
39 #include <sys/psm_common.h>
40 
41 /* Function prototypes of local apic and x2apic */
42 static uint64_t local_apic_read(uint32_t reg);
43 static void local_apic_write(uint32_t reg, uint64_t value);
44 static int get_local_apic_pri(void);
45 static void local_apic_write_task_reg(uint64_t value);
46 static void local_apic_write_int_cmd(uint32_t cpu_id, uint32_t cmd1);
47 static uint64_t local_x2apic_read(uint32_t msr);
48 static void local_x2apic_write(uint32_t msr, uint64_t value);
49 static int get_local_x2apic_pri(void);
50 static void local_x2apic_write_task_reg(uint64_t value);
51 static void local_x2apic_write_int_cmd(uint32_t cpu_id, uint32_t cmd1);
52 
53 /*
54  * According to the x2APIC specification:
55  *
56  *   xAPIC global enable    x2APIC enable         Description
57  *   (IA32_APIC_BASE[11])   (IA32_APIC_BASE[10])
58  * -----------------------------------------------------------
59  *      0 			0 	APIC is disabled
60  * 	0			1	Invalid
61  *	1			0	APIC is enabled in xAPIC mode
62  *	1			1	APIC is enabled in x2APIC mode
63  * -----------------------------------------------------------
64  */
65 int	x2apic_enable = 1;
66 int 	apic_mode = LOCAL_APIC;		/* Default mode is Local APIC */
67 
68 /* Uses MMIO (Memory Mapped IO) */
69 static 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 /* x2APIC : Uses RDMSR/WRMSR instructions to access APIC registers */
79 static apic_reg_ops_t x2apic_regs_ops = {
80 	local_x2apic_read,
81 	local_x2apic_write,
82 	get_local_x2apic_pri,
83 	local_x2apic_write_task_reg,
84 	local_x2apic_write_int_cmd,
85 	apic_send_EOI,
86 };
87 
88 
89 /* The default ops is local APIC (Memory Mapped IO) */
90 apic_reg_ops_t *apic_reg_ops = &local_apic_regs_ops;
91 
92 /*
93  * APIC register ops related data sturctures and functions.
94  */
95 int	apic_direct_EOI = 0;			/* Directed EOI Support */
96 
97 void apic_send_EOI();
98 void apic_send_directed_EOI(uint32_t irq);
99 
100 #define	X2APIC_CPUID_BIT	21
101 #define	X2APIC_ENABLE_BIT	10
102 
103 /*
104  * Local APIC Implementation
105  */
106 static uint64_t
107 local_apic_read(uint32_t reg)
108 {
109 	return ((uint32_t)apicadr[reg]);
110 }
111 
112 static void
113 local_apic_write(uint32_t reg, uint64_t value)
114 {
115 	apicadr[reg] = (uint32_t)value;
116 }
117 
118 static int
119 get_local_apic_pri(void)
120 {
121 #if defined(__amd64)
122 	return ((int)getcr8());
123 #else
124 	return (apicadr[APIC_TASK_REG]);
125 #endif
126 }
127 
128 static void
129 local_apic_write_task_reg(uint64_t value)
130 {
131 #if defined(__amd64)
132 	setcr8((ulong_t)(value >> APIC_IPL_SHIFT));
133 #else
134 	apicadr[APIC_TASK_REG] = (uint32_t)value;
135 #endif
136 }
137 
138 static void
139 local_apic_write_int_cmd(uint32_t cpu_id, uint32_t cmd1)
140 {
141 	apicadr[APIC_INT_CMD2] = cpu_id << APIC_ICR_ID_BIT_OFFSET;
142 	apicadr[APIC_INT_CMD1] = cmd1;
143 }
144 
145 /*
146  * x2APIC Implementation.
147  */
148 static uint64_t
149 local_x2apic_read(uint32_t msr)
150 {
151 	uint64_t i;
152 
153 	i = (uint64_t)(rdmsr(REG_X2APIC_BASE_MSR + (msr >> 2)) & 0xffffffff);
154 	return (i);
155 }
156 
157 static void
158 local_x2apic_write(uint32_t msr, uint64_t value)
159 {
160 	uint64_t tmp;
161 
162 	if (msr != APIC_EOI_REG) {
163 		tmp = rdmsr(REG_X2APIC_BASE_MSR + (msr >> 2));
164 		tmp = (tmp & 0xffffffff00000000) | value;
165 	}
166 
167 	wrmsr((REG_X2APIC_BASE_MSR + (msr >> 2)), tmp);
168 }
169 
170 static int
171 get_local_x2apic_pri(void)
172 {
173 	return (rdmsr(REG_X2APIC_BASE_MSR + (APIC_TASK_REG) >> 2));
174 }
175 
176 static void
177 local_x2apic_write_task_reg(uint64_t value)
178 {
179 	X2APIC_WRITE(APIC_TASK_REG, value);
180 }
181 
182 static void
183 local_x2apic_write_int_cmd(uint32_t cpu_id, uint32_t cmd1)
184 {
185 	wrmsr((REG_X2APIC_BASE_MSR + (APIC_INT_CMD1 >> 2)),
186 	    (((uint64_t)cpu_id << 32) | cmd1));
187 }
188 
189 /*ARGSUSED*/
190 void
191 apic_send_EOI(uint32_t irq)
192 {
193 	apic_reg_ops->apic_write(APIC_EOI_REG, 0);
194 }
195 
196 void
197 apic_send_directed_EOI(uint32_t irq)
198 {
199 	uchar_t ioapicindex;
200 	uchar_t vector;
201 	apic_irq_t *apic_irq;
202 	short intr_index;
203 
204 	ASSERT(apic_mode == LOCAL_X2APIC);
205 
206 	apic_irq = apic_irq_table[irq];
207 
208 	while (apic_irq) {
209 		intr_index = apic_irq->airq_mps_intr_index;
210 		if (intr_index == ACPI_INDEX || intr_index >= 0) {
211 			ioapicindex = apic_irq->airq_ioapicindex;
212 			vector = apic_irq->airq_vector;
213 			ioapic_write_eoi(ioapicindex, vector);
214 		}
215 		apic_irq = apic_irq->airq_next;
216 	}
217 }
218 
219 int
220 apic_detect_x2apic(void)
221 {
222 	struct cpuid_regs cp;
223 
224 	if (x2apic_enable == 0)
225 		return (0);
226 
227 	cp.cp_eax = 1;
228 	(void) __cpuid_insn(&cp);
229 
230 	return ((cp.cp_ecx & (0x1 << X2APIC_CPUID_BIT)) ? 1 : 0);
231 }
232 
233 void
234 apic_enable_x2apic(void)
235 {
236 	uint64_t apic_base_msr;
237 
238 	apic_base_msr = rdmsr(REG_APIC_BASE_MSR);
239 	apic_base_msr = apic_base_msr | (0x1 << X2APIC_ENABLE_BIT);
240 
241 	wrmsr(REG_APIC_BASE_MSR, apic_base_msr);
242 
243 	/* change the mode and ops */
244 	if (apic_mode != LOCAL_X2APIC) {
245 		apic_mode = LOCAL_X2APIC;
246 		apic_reg_ops = &x2apic_regs_ops;
247 		x2apic_update_psm();
248 	}
249 }
250 
251 /*
252  * Generates an interprocessor interrupt to another CPU when x2APIC mode is
253  * enabled.
254  */
255 void
256 x2apic_send_ipi(int cpun, int ipl)
257 {
258 	int vector;
259 	ulong_t flag;
260 	ASSERT(apic_mode == LOCAL_X2APIC);
261 
262 	vector = apic_resv_vector[ipl];
263 
264 	flag = intr_clear();
265 
266 	while (apic_reg_ops->apic_read(APIC_INT_CMD1) & AV_PENDING)
267 		apic_ret();
268 
269 	if ((cpun == psm_get_cpu_id()))
270 		apic_reg_ops->apic_write(X2APIC_SELF_IPI, vector);
271 	else
272 		apic_reg_ops->apic_write_int_cmd(
273 		    apic_cpus[cpun].aci_local_id, vector);
274 
275 	intr_restore(flag);
276 }
277 
278 
279 void
280 apic_change_eoi()
281 {
282 	apic_reg_ops->apic_send_eoi = apic_send_directed_EOI;
283 }
284