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