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.
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.
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]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 * Copyright 2014 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
27 * Copyright (c) 2014 by Delphix. All rights reserved.
30 #include <sys/cpuvar.h>
32 #include <sys/archsystm.h>
34 #include <sys/sunddi.h>
35 #include <sys/ddi_impldefs.h>
36 #include <sys/mach_intr.h>
37 #include <sys/sysmacros.h>
39 #include <sys/x86_archext.h>
40 #include <sys/privregs.h>
41 #include <sys/psm_common.h>
43 /* Function prototypes of local apic and X2APIC */
44 static uint64_t local_apic_read(uint32_t reg
);
45 static void local_apic_write(uint32_t reg
, uint64_t value
);
46 static int get_local_apic_pri(void);
47 static void local_apic_write_task_reg(uint64_t value
);
48 static void local_apic_write_int_cmd(uint32_t cpu_id
, uint32_t cmd1
);
49 static uint64_t local_x2apic_read(uint32_t msr
);
50 static void local_x2apic_write(uint32_t msr
, uint64_t value
);
51 static int get_local_x2apic_pri(void);
52 static void local_x2apic_write_task_reg(uint64_t value
);
53 static void local_x2apic_write_int_cmd(uint32_t cpu_id
, uint32_t cmd1
);
56 * According to the X2APIC specification:
58 * xAPIC global enable X2APIC enable Description
59 * (IA32_APIC_BASE[11]) (IA32_APIC_BASE[10])
60 * -----------------------------------------------------------
61 * 0 0 APIC is disabled
63 * 1 0 APIC is enabled in xAPIC mode
64 * 1 1 APIC is enabled in X2APIC mode
65 * -----------------------------------------------------------
67 int x2apic_enable
= 1;
68 apic_mode_t apic_mode
= LOCAL_APIC
; /* Default mode is Local APIC */
70 /* See apic_directed_EOI_supported(). Currently 3-state variable. */
71 volatile int apic_directed_eoi_state
= 2;
73 /* Uses MMIO (Memory Mapped IO) */
74 static apic_reg_ops_t local_apic_regs_ops
= {
78 local_apic_write_task_reg
,
79 local_apic_write_int_cmd
,
83 /* X2APIC : Uses RDMSR/WRMSR instructions to access APIC registers */
84 static apic_reg_ops_t x2apic_regs_ops
= {
88 local_x2apic_write_task_reg
,
89 local_x2apic_write_int_cmd
,
93 int apic_have_32bit_cr8
= 0;
95 /* The default ops is local APIC (Memory Mapped IO) */
96 apic_reg_ops_t
*apic_reg_ops
= &local_apic_regs_ops
;
99 * APIC register ops related data sturctures and functions.
101 void apic_send_EOI();
102 void apic_send_directed_EOI(uint32_t irq
);
104 #define X2APIC_ENABLE_BIT 10
107 * Local APIC Implementation
110 local_apic_read(uint32_t reg
)
112 return ((uint32_t)apicadr
[reg
]);
116 local_apic_write(uint32_t reg
, uint64_t value
)
118 apicadr
[reg
] = (uint32_t)value
;
122 get_local_apic_pri(void)
125 return ((int)getcr8());
127 if (apic_have_32bit_cr8
)
128 return ((int)getcr8());
129 return (apicadr
[APIC_TASK_REG
]);
134 local_apic_write_task_reg(uint64_t value
)
137 setcr8((ulong_t
)(value
>> APIC_IPL_SHIFT
));
139 if (apic_have_32bit_cr8
)
140 setcr8((ulong_t
)(value
>> APIC_IPL_SHIFT
));
142 apicadr
[APIC_TASK_REG
] = (uint32_t)value
;
147 local_apic_write_int_cmd(uint32_t cpu_id
, uint32_t cmd1
)
149 apicadr
[APIC_INT_CMD2
] = cpu_id
<< APIC_ICR_ID_BIT_OFFSET
;
150 apicadr
[APIC_INT_CMD1
] = cmd1
;
154 * X2APIC Implementation.
157 local_x2apic_read(uint32_t msr
)
161 i
= (uint64_t)(rdmsr(REG_X2APIC_BASE_MSR
+ (msr
>> 2)) & 0xffffffff);
166 local_x2apic_write(uint32_t msr
, uint64_t value
)
170 if (msr
!= APIC_EOI_REG
) {
171 tmp
= rdmsr(REG_X2APIC_BASE_MSR
+ (msr
>> 2));
172 tmp
= (tmp
& 0xffffffff00000000) | value
;
177 wrmsr((REG_X2APIC_BASE_MSR
+ (msr
>> 2)), tmp
);
181 get_local_x2apic_pri(void)
183 return (rdmsr(REG_X2APIC_BASE_MSR
+ (APIC_TASK_REG
>> 2)));
187 local_x2apic_write_task_reg(uint64_t value
)
189 X2APIC_WRITE(APIC_TASK_REG
, value
);
193 local_x2apic_write_int_cmd(uint32_t cpu_id
, uint32_t cmd1
)
195 wrmsr((REG_X2APIC_BASE_MSR
+ (APIC_INT_CMD1
>> 2)),
196 (((uint64_t)cpu_id
<< 32) | cmd1
));
201 apic_send_EOI(uint32_t irq
)
203 apic_reg_ops
->apic_write(APIC_EOI_REG
, 0);
207 * Support for Directed EOI capability is available in both the xAPIC
211 apic_send_directed_EOI(uint32_t irq
)
215 apic_irq_t
*apic_irq
;
219 * Following the EOI to the local APIC unit, perform a directed
220 * EOI to the IOxAPIC generating the interrupt by writing to its
223 * A broadcast EOI is not generated.
225 apic_reg_ops
->apic_write(APIC_EOI_REG
, 0);
227 apic_irq
= apic_irq_table
[irq
];
229 intr_index
= apic_irq
->airq_mps_intr_index
;
230 if (intr_index
== ACPI_INDEX
|| intr_index
>= 0) {
231 ioapicindex
= apic_irq
->airq_ioapicindex
;
232 vector
= apic_irq
->airq_vector
;
233 ioapic_write_eoi(ioapicindex
, vector
);
235 apic_irq
= apic_irq
->airq_next
;
240 apic_detect_x2apic(void)
242 if (x2apic_enable
== 0)
245 return (is_x86_feature(x86_featureset
, X86FSET_X2APIC
));
249 apic_enable_x2apic(void)
251 uint64_t apic_base_msr
;
253 if (apic_local_mode() == LOCAL_X2APIC
) {
254 /* BIOS apparently has enabled X2APIC */
255 if (apic_mode
!= LOCAL_X2APIC
)
261 * This is the first time we are enabling X2APIC on this CPU
263 apic_base_msr
= rdmsr(REG_APIC_BASE_MSR
);
264 apic_base_msr
= apic_base_msr
| (0x1 << X2APIC_ENABLE_BIT
);
265 wrmsr(REG_APIC_BASE_MSR
, apic_base_msr
);
267 if (apic_mode
!= LOCAL_X2APIC
)
272 * Determine which mode the current CPU is in. See the table above.
273 * (IA32_APIC_BASE[11]) (IA32_APIC_BASE[10])
276 apic_local_mode(void)
278 uint64_t apic_base_msr
;
279 int bit
= ((0x1 << (X2APIC_ENABLE_BIT
+ 1)) |
280 (0x1 << X2APIC_ENABLE_BIT
));
282 apic_base_msr
= rdmsr(REG_APIC_BASE_MSR
);
284 if ((apic_base_msr
& bit
) == bit
)
285 return (LOCAL_X2APIC
);
291 apic_set_directed_EOI_handler()
293 apic_reg_ops
->apic_send_eoi
= apic_send_directed_EOI
;
297 apic_directed_EOI_supported()
302 * There are some known issues with some versions of Linux KVM and QEMU
303 * where by directed EOIs do not properly function and instead get
304 * coalesced at the hypervisor, causing the host not to see interrupts.
305 * Thus, when the platform is KVM, we would like to disable it by
306 * default, but keep it available otherwise.
308 * We use a three-state variable (apic_directed_eoi_state) to determine
309 * how we handle directed EOI.
311 * 0 --> Don't do directed EOI at all.
312 * 1 --> Do directed EOI if available, no matter the HW environment.
313 * 2 --> Don't do directed EOI on KVM, but do it otherwise if available.
315 * If some grinning weirdo put something else in there, treat it as '2'
316 * (i.e. the current default).
318 * Note, at this time illumos KVM does not identify as KVM. If it does,
319 * we'll need to do some work to determine if it should be caught by
320 * this or if it should show up as its own value of platform_type.
322 switch (apic_directed_eoi_state
) {
324 /* Don't do it at all. */
330 /* Only do it if we aren't on KVM. */
331 if (get_hwenv() == HW_KVM
)
336 ver
= apic_reg_ops
->apic_read(APIC_VERS_REG
);
337 if (ver
& APIC_DIRECTED_EOI_BIT
)
344 * Change apic_reg_ops depending upon the apic_mode.
349 if (apic_mode
== LOCAL_APIC
)
350 apic_reg_ops
= &local_apic_regs_ops
;
351 else if (apic_mode
== LOCAL_X2APIC
)
352 apic_reg_ops
= &x2apic_regs_ops
;
356 * Generates an interprocessor interrupt to another CPU when X2APIC mode is
360 x2apic_send_ipi(int cpun
, int ipl
)
365 ASSERT(apic_mode
== LOCAL_X2APIC
);
368 * With X2APIC, Intel relaxed the semantics of the
369 * WRMSR instruction such that references to the X2APIC
370 * MSR registers are no longer serializing instructions.
371 * The code that initiates IPIs assumes that some sort
372 * of memory serialization occurs. The old APIC code
373 * did a write to uncachable memory mapped registers.
374 * Any reference to uncached memory is a serializing
375 * operation. To mimic those semantics here, we do an
376 * atomic operation, which translates to a LOCK OR instruction,
377 * which is serializing.
379 atomic_or_ulong(&flag
, 1);
381 vector
= apic_resv_vector
[ipl
];
386 * According to X2APIC specification in section '2.3.5.1' of
387 * Interrupt Command Register Semantics, the semantics of
388 * programming Interrupt Command Register to dispatch an interrupt
389 * is simplified. A single MSR write to the 64-bit ICR is required
390 * for dispatching an interrupt. Specifically with the 64-bit MSR
391 * interface to ICR, system software is not required to check the
392 * status of the delivery status bit prior to writing to the ICR
393 * to send an IPI. With the removal of the Delivery Status bit,
394 * system software no longer has a reason to read the ICR. It remains
395 * readable only to aid in debugging.
398 APIC_AV_PENDING_SET();
401 if ((cpun
== psm_get_cpu_id())) {
402 X2APIC_WRITE(X2APIC_SELF_IPI
, vector
);
404 apic_reg_ops
->apic_write_int_cmd(
405 apic_cpus
[cpun
].aci_local_id
, vector
);
412 * Generates IPI to another CPU depending on the local APIC mode.
413 * apic_send_ipi() and x2apic_send_ipi() depends on the configured
414 * mode of the local APIC, but that may not match the actual mode
415 * early in CPU startup.
417 * Any changes made to this routine must be accompanied by similar
418 * changes to apic_send_ipi().
421 apic_common_send_ipi(int cpun
, int ipl
)
425 int mode
= apic_local_mode();
427 if (mode
== LOCAL_X2APIC
) {
428 x2apic_send_ipi(cpun
, ipl
);
432 ASSERT(mode
== LOCAL_APIC
);
434 vector
= apic_resv_vector
[ipl
];
435 ASSERT((vector
>= APIC_BASE_VECT
) && (vector
<= APIC_SPUR_INTR
));
437 while (local_apic_regs_ops
.apic_read(APIC_INT_CMD1
) & AV_PENDING
)
439 local_apic_regs_ops
.apic_write_int_cmd(apic_cpus
[cpun
].aci_local_id
,