1b6917abeSmishra /*
2b6917abeSmishra * CDDL HEADER START
3b6917abeSmishra *
4b6917abeSmishra * The contents of this file are subject to the terms of the
5b6917abeSmishra * Common Development and Distribution License (the "License").
6b6917abeSmishra * You may not use this file except in compliance with the License.
7b6917abeSmishra *
8b6917abeSmishra * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9b6917abeSmishra * or http://www.opensolaris.org/os/licensing.
10b6917abeSmishra * See the License for the specific language governing permissions
11b6917abeSmishra * and limitations under the License.
12b6917abeSmishra *
13b6917abeSmishra * When distributing Covered Code, include this CDDL HEADER in each
14b6917abeSmishra * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15b6917abeSmishra * If applicable, add the following below this CDDL HEADER, with the
16b6917abeSmishra * fields enclosed by brackets "[]" replaced with your own identifying
17b6917abeSmishra * information: Portions Copyright [yyyy] [name of copyright owner]
18b6917abeSmishra *
19b6917abeSmishra * CDDL HEADER END
20b6917abeSmishra */
21b6917abeSmishra /*
22d23e508cSEdward Gillett * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23b6917abeSmishra * Use is subject to license terms.
24b6917abeSmishra */
25*6eedf6a5SJosef 'Jeff' Sipek /*
26*6eedf6a5SJosef 'Jeff' Sipek * Copyright 2014 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
27*6eedf6a5SJosef 'Jeff' Sipek */
28b6917abeSmishra
29b6917abeSmishra #include <sys/cpuvar.h>
30b6917abeSmishra #include <sys/psm.h>
31b6917abeSmishra #include <sys/archsystm.h>
32b6917abeSmishra #include <sys/apic.h>
33b6917abeSmishra #include <sys/sunddi.h>
34b6917abeSmishra #include <sys/ddi_impldefs.h>
35b6917abeSmishra #include <sys/mach_intr.h>
36b6917abeSmishra #include <sys/sysmacros.h>
37b6917abeSmishra #include <sys/trap.h>
38b6917abeSmishra #include <sys/x86_archext.h>
39b6917abeSmishra #include <sys/privregs.h>
40b6917abeSmishra #include <sys/psm_common.h>
41b6917abeSmishra
42325e77f4SSaurabh Misra /* Function prototypes of local apic and X2APIC */
43b6917abeSmishra static uint64_t local_apic_read(uint32_t reg);
44b6917abeSmishra static void local_apic_write(uint32_t reg, uint64_t value);
45b6917abeSmishra static int get_local_apic_pri(void);
46b6917abeSmishra static void local_apic_write_task_reg(uint64_t value);
47b6917abeSmishra static void local_apic_write_int_cmd(uint32_t cpu_id, uint32_t cmd1);
48b6917abeSmishra static uint64_t local_x2apic_read(uint32_t msr);
49b6917abeSmishra static void local_x2apic_write(uint32_t msr, uint64_t value);
50b6917abeSmishra static int get_local_x2apic_pri(void);
51b6917abeSmishra static void local_x2apic_write_task_reg(uint64_t value);
52b6917abeSmishra static void local_x2apic_write_int_cmd(uint32_t cpu_id, uint32_t cmd1);
53b6917abeSmishra
54b6917abeSmishra /*
55325e77f4SSaurabh Misra * According to the X2APIC specification:
56b6917abeSmishra *
57325e77f4SSaurabh Misra * xAPIC global enable X2APIC enable Description
58b6917abeSmishra * (IA32_APIC_BASE[11]) (IA32_APIC_BASE[10])
59b6917abeSmishra * -----------------------------------------------------------
60b6917abeSmishra * 0 0 APIC is disabled
61b6917abeSmishra * 0 1 Invalid
62b6917abeSmishra * 1 0 APIC is enabled in xAPIC mode
63325e77f4SSaurabh Misra * 1 1 APIC is enabled in X2APIC mode
64b6917abeSmishra * -----------------------------------------------------------
65b6917abeSmishra */
66b6917abeSmishra int x2apic_enable = 1;
679b1d70f8SJosef 'Jeff' Sipek apic_mode_t apic_mode = LOCAL_APIC; /* Default mode is Local APIC */
68b6917abeSmishra
69b6917abeSmishra /* Uses MMIO (Memory Mapped IO) */
70b6917abeSmishra static apic_reg_ops_t local_apic_regs_ops = {
71b6917abeSmishra local_apic_read,
72b6917abeSmishra local_apic_write,
73b6917abeSmishra get_local_apic_pri,
74b6917abeSmishra local_apic_write_task_reg,
75b6917abeSmishra local_apic_write_int_cmd,
76b6917abeSmishra apic_send_EOI,
77b6917abeSmishra };
78b6917abeSmishra
79325e77f4SSaurabh Misra /* X2APIC : Uses RDMSR/WRMSR instructions to access APIC registers */
80b6917abeSmishra static apic_reg_ops_t x2apic_regs_ops = {
81b6917abeSmishra local_x2apic_read,
82b6917abeSmishra local_x2apic_write,
83b6917abeSmishra get_local_x2apic_pri,
84b6917abeSmishra local_x2apic_write_task_reg,
85b6917abeSmishra local_x2apic_write_int_cmd,
86b6917abeSmishra apic_send_EOI,
87b6917abeSmishra };
88b6917abeSmishra
894c1c9391SJoe Bonasera int apic_have_32bit_cr8 = 0;
90b6917abeSmishra
91b6917abeSmishra /* The default ops is local APIC (Memory Mapped IO) */
92b6917abeSmishra apic_reg_ops_t *apic_reg_ops = &local_apic_regs_ops;
93b6917abeSmishra
94b6917abeSmishra /*
95b6917abeSmishra * APIC register ops related data sturctures and functions.
96b6917abeSmishra */
97b6917abeSmishra void apic_send_EOI();
98b6917abeSmishra void apic_send_directed_EOI(uint32_t irq);
99b6917abeSmishra
100b6917abeSmishra #define X2APIC_ENABLE_BIT 10
101b6917abeSmishra
102b6917abeSmishra /*
103b6917abeSmishra * Local APIC Implementation
104b6917abeSmishra */
105b6917abeSmishra static uint64_t
local_apic_read(uint32_t reg)106b6917abeSmishra local_apic_read(uint32_t reg)
107b6917abeSmishra {
108b6917abeSmishra return ((uint32_t)apicadr[reg]);
109b6917abeSmishra }
110b6917abeSmishra
111b6917abeSmishra static void
local_apic_write(uint32_t reg,uint64_t value)112b6917abeSmishra local_apic_write(uint32_t reg, uint64_t value)
113b6917abeSmishra {
114b6917abeSmishra apicadr[reg] = (uint32_t)value;
115b6917abeSmishra }
116b6917abeSmishra
117b6917abeSmishra static int
get_local_apic_pri(void)118b6917abeSmishra get_local_apic_pri(void)
119b6917abeSmishra {
120b6917abeSmishra #if defined(__amd64)
121b6917abeSmishra return ((int)getcr8());
122b6917abeSmishra #else
1232ef50f01SJoe Bonasera if (apic_have_32bit_cr8)
1242ef50f01SJoe Bonasera return ((int)getcr8());
125b6917abeSmishra return (apicadr[APIC_TASK_REG]);
126b6917abeSmishra #endif
127b6917abeSmishra }
128b6917abeSmishra
129b6917abeSmishra static void
local_apic_write_task_reg(uint64_t value)130b6917abeSmishra local_apic_write_task_reg(uint64_t value)
131b6917abeSmishra {
132b6917abeSmishra #if defined(__amd64)
133b6917abeSmishra setcr8((ulong_t)(value >> APIC_IPL_SHIFT));
134b6917abeSmishra #else
1352ef50f01SJoe Bonasera if (apic_have_32bit_cr8)
1362ef50f01SJoe Bonasera setcr8((ulong_t)(value >> APIC_IPL_SHIFT));
1372ef50f01SJoe Bonasera else
138b6917abeSmishra apicadr[APIC_TASK_REG] = (uint32_t)value;
139b6917abeSmishra #endif
140b6917abeSmishra }
141b6917abeSmishra
142b6917abeSmishra static void
local_apic_write_int_cmd(uint32_t cpu_id,uint32_t cmd1)143b6917abeSmishra local_apic_write_int_cmd(uint32_t cpu_id, uint32_t cmd1)
144b6917abeSmishra {
145b6917abeSmishra apicadr[APIC_INT_CMD2] = cpu_id << APIC_ICR_ID_BIT_OFFSET;
146b6917abeSmishra apicadr[APIC_INT_CMD1] = cmd1;
147b6917abeSmishra }
148b6917abeSmishra
149b6917abeSmishra /*
150325e77f4SSaurabh Misra * X2APIC Implementation.
151b6917abeSmishra */
152b6917abeSmishra static uint64_t
local_x2apic_read(uint32_t msr)153b6917abeSmishra local_x2apic_read(uint32_t msr)
154b6917abeSmishra {
155b6917abeSmishra uint64_t i;
156b6917abeSmishra
157b6917abeSmishra i = (uint64_t)(rdmsr(REG_X2APIC_BASE_MSR + (msr >> 2)) & 0xffffffff);
158b6917abeSmishra return (i);
159b6917abeSmishra }
160b6917abeSmishra
161b6917abeSmishra static void
local_x2apic_write(uint32_t msr,uint64_t value)162b6917abeSmishra local_x2apic_write(uint32_t msr, uint64_t value)
163b6917abeSmishra {
164b6917abeSmishra uint64_t tmp;
165b6917abeSmishra
166b6917abeSmishra if (msr != APIC_EOI_REG) {
167b6917abeSmishra tmp = rdmsr(REG_X2APIC_BASE_MSR + (msr >> 2));
168b6917abeSmishra tmp = (tmp & 0xffffffff00000000) | value;
169f9c480cdSSaurabh Misra } else {
170f9c480cdSSaurabh Misra tmp = 0;
171b6917abeSmishra }
172b6917abeSmishra
173b6917abeSmishra wrmsr((REG_X2APIC_BASE_MSR + (msr >> 2)), tmp);
174b6917abeSmishra }
175b6917abeSmishra
176b6917abeSmishra static int
get_local_x2apic_pri(void)177b6917abeSmishra get_local_x2apic_pri(void)
178b6917abeSmishra {
1795d8efbbcSSaurabh Misra return (rdmsr(REG_X2APIC_BASE_MSR + (APIC_TASK_REG >> 2)));
180b6917abeSmishra }
181b6917abeSmishra
182b6917abeSmishra static void
local_x2apic_write_task_reg(uint64_t value)183b6917abeSmishra local_x2apic_write_task_reg(uint64_t value)
184b6917abeSmishra {
185b6917abeSmishra X2APIC_WRITE(APIC_TASK_REG, value);
186b6917abeSmishra }
187b6917abeSmishra
188b6917abeSmishra static void
local_x2apic_write_int_cmd(uint32_t cpu_id,uint32_t cmd1)189b6917abeSmishra local_x2apic_write_int_cmd(uint32_t cpu_id, uint32_t cmd1)
190b6917abeSmishra {
191b6917abeSmishra wrmsr((REG_X2APIC_BASE_MSR + (APIC_INT_CMD1 >> 2)),
192b6917abeSmishra (((uint64_t)cpu_id << 32) | cmd1));
193b6917abeSmishra }
194b6917abeSmishra
195b6917abeSmishra /*ARGSUSED*/
196b6917abeSmishra void
apic_send_EOI(uint32_t irq)197b6917abeSmishra apic_send_EOI(uint32_t irq)
198b6917abeSmishra {
199b6917abeSmishra apic_reg_ops->apic_write(APIC_EOI_REG, 0);
200b6917abeSmishra }
201b6917abeSmishra
202d23e508cSEdward Gillett /*
203d23e508cSEdward Gillett * Support for Directed EOI capability is available in both the xAPIC
204d23e508cSEdward Gillett * and x2APIC mode.
205d23e508cSEdward Gillett */
206b6917abeSmishra void
apic_send_directed_EOI(uint32_t irq)207b6917abeSmishra apic_send_directed_EOI(uint32_t irq)
208b6917abeSmishra {
209b6917abeSmishra uchar_t ioapicindex;
210b6917abeSmishra uchar_t vector;
211b6917abeSmishra apic_irq_t *apic_irq;
212b6917abeSmishra short intr_index;
213b6917abeSmishra
214d23e508cSEdward Gillett /*
215d23e508cSEdward Gillett * Following the EOI to the local APIC unit, perform a directed
216d23e508cSEdward Gillett * EOI to the IOxAPIC generating the interrupt by writing to its
217d23e508cSEdward Gillett * EOI register.
218d23e508cSEdward Gillett *
219d23e508cSEdward Gillett * A broadcast EOI is not generated.
220d23e508cSEdward Gillett */
221d23e508cSEdward Gillett apic_reg_ops->apic_write(APIC_EOI_REG, 0);
222b6917abeSmishra
223b6917abeSmishra apic_irq = apic_irq_table[irq];
224b6917abeSmishra while (apic_irq) {
225b6917abeSmishra intr_index = apic_irq->airq_mps_intr_index;
226b6917abeSmishra if (intr_index == ACPI_INDEX || intr_index >= 0) {
227b6917abeSmishra ioapicindex = apic_irq->airq_ioapicindex;
228b6917abeSmishra vector = apic_irq->airq_vector;
229b6917abeSmishra ioapic_write_eoi(ioapicindex, vector);
230b6917abeSmishra }
231b6917abeSmishra apic_irq = apic_irq->airq_next;
232b6917abeSmishra }
233b6917abeSmishra }
234b6917abeSmishra
235b6917abeSmishra int
apic_detect_x2apic(void)236b6917abeSmishra apic_detect_x2apic(void)
237b6917abeSmishra {
238b6917abeSmishra if (x2apic_enable == 0)
239b6917abeSmishra return (0);
240b6917abeSmishra
241*6eedf6a5SJosef 'Jeff' Sipek return (is_x86_feature(x86_featureset, X86FSET_X2APIC));
242b6917abeSmishra }
243b6917abeSmishra
244b6917abeSmishra void
apic_enable_x2apic(void)245b6917abeSmishra apic_enable_x2apic(void)
246b6917abeSmishra {
247b6917abeSmishra uint64_t apic_base_msr;
248b6917abeSmishra
249325e77f4SSaurabh Misra if (apic_local_mode() == LOCAL_X2APIC) {
250325e77f4SSaurabh Misra /* BIOS apparently has enabled X2APIC */
251325e77f4SSaurabh Misra if (apic_mode != LOCAL_X2APIC)
252b6917abeSmishra x2apic_update_psm();
253325e77f4SSaurabh Misra return;
254b6917abeSmishra }
255b6917abeSmishra
256b6917abeSmishra /*
257325e77f4SSaurabh Misra * This is the first time we are enabling X2APIC on this CPU
258325e77f4SSaurabh Misra */
259325e77f4SSaurabh Misra apic_base_msr = rdmsr(REG_APIC_BASE_MSR);
260325e77f4SSaurabh Misra apic_base_msr = apic_base_msr | (0x1 << X2APIC_ENABLE_BIT);
261325e77f4SSaurabh Misra wrmsr(REG_APIC_BASE_MSR, apic_base_msr);
262325e77f4SSaurabh Misra
263325e77f4SSaurabh Misra if (apic_mode != LOCAL_X2APIC)
264325e77f4SSaurabh Misra x2apic_update_psm();
265325e77f4SSaurabh Misra }
266325e77f4SSaurabh Misra
267325e77f4SSaurabh Misra /*
268325e77f4SSaurabh Misra * Determine which mode the current CPU is in. See the table above.
269325e77f4SSaurabh Misra * (IA32_APIC_BASE[11]) (IA32_APIC_BASE[10])
270325e77f4SSaurabh Misra */
271325e77f4SSaurabh Misra int
apic_local_mode(void)272325e77f4SSaurabh Misra apic_local_mode(void)
273325e77f4SSaurabh Misra {
274325e77f4SSaurabh Misra uint64_t apic_base_msr;
275325e77f4SSaurabh Misra int bit = ((0x1 << (X2APIC_ENABLE_BIT + 1)) |
276325e77f4SSaurabh Misra (0x1 << X2APIC_ENABLE_BIT));
277325e77f4SSaurabh Misra
278325e77f4SSaurabh Misra apic_base_msr = rdmsr(REG_APIC_BASE_MSR);
279325e77f4SSaurabh Misra
280325e77f4SSaurabh Misra if ((apic_base_msr & bit) == bit)
281325e77f4SSaurabh Misra return (LOCAL_X2APIC);
282325e77f4SSaurabh Misra else
283325e77f4SSaurabh Misra return (LOCAL_APIC);
284325e77f4SSaurabh Misra }
285325e77f4SSaurabh Misra
286325e77f4SSaurabh Misra void
apic_set_directed_EOI_handler()287e511d54dSSaurabh Misra apic_set_directed_EOI_handler()
288325e77f4SSaurabh Misra {
289325e77f4SSaurabh Misra apic_reg_ops->apic_send_eoi = apic_send_directed_EOI;
290325e77f4SSaurabh Misra }
291325e77f4SSaurabh Misra
292e511d54dSSaurabh Misra int
apic_directed_EOI_supported()293e511d54dSSaurabh Misra apic_directed_EOI_supported()
294e511d54dSSaurabh Misra {
295e511d54dSSaurabh Misra uint32_t ver;
296e511d54dSSaurabh Misra
297e511d54dSSaurabh Misra ver = apic_reg_ops->apic_read(APIC_VERS_REG);
298e511d54dSSaurabh Misra if (ver & APIC_DIRECTED_EOI_BIT)
299e511d54dSSaurabh Misra return (1);
300e511d54dSSaurabh Misra
301e511d54dSSaurabh Misra return (0);
302e511d54dSSaurabh Misra }
303e511d54dSSaurabh Misra
304325e77f4SSaurabh Misra /*
305325e77f4SSaurabh Misra * Change apic_reg_ops depending upon the apic_mode.
306325e77f4SSaurabh Misra */
307325e77f4SSaurabh Misra void
apic_change_ops()308325e77f4SSaurabh Misra apic_change_ops()
309325e77f4SSaurabh Misra {
310325e77f4SSaurabh Misra if (apic_mode == LOCAL_APIC)
311325e77f4SSaurabh Misra apic_reg_ops = &local_apic_regs_ops;
312325e77f4SSaurabh Misra else if (apic_mode == LOCAL_X2APIC)
313325e77f4SSaurabh Misra apic_reg_ops = &x2apic_regs_ops;
314325e77f4SSaurabh Misra }
315325e77f4SSaurabh Misra
316325e77f4SSaurabh Misra /*
317325e77f4SSaurabh Misra * Generates an interprocessor interrupt to another CPU when X2APIC mode is
318b6917abeSmishra * enabled.
319b6917abeSmishra */
320b6917abeSmishra void
x2apic_send_ipi(int cpun,int ipl)321b6917abeSmishra x2apic_send_ipi(int cpun, int ipl)
322b6917abeSmishra {
323b6917abeSmishra int vector;
324b6917abeSmishra ulong_t flag;
3255d8efbbcSSaurabh Misra
32687cc6269SSaurabh Misra ASSERT(apic_mode == LOCAL_X2APIC);
32787cc6269SSaurabh Misra
3285d8efbbcSSaurabh Misra /*
3295d8efbbcSSaurabh Misra * With X2APIC, Intel relaxed the semantics of the
3305d8efbbcSSaurabh Misra * WRMSR instruction such that references to the X2APIC
3315d8efbbcSSaurabh Misra * MSR registers are no longer serializing instructions.
3325d8efbbcSSaurabh Misra * The code that initiates IPIs assumes that some sort
3335d8efbbcSSaurabh Misra * of memory serialization occurs. The old APIC code
3345d8efbbcSSaurabh Misra * did a write to uncachable memory mapped registers.
3355d8efbbcSSaurabh Misra * Any reference to uncached memory is a serializing
3365d8efbbcSSaurabh Misra * operation. To mimic those semantics here, we do an
3375d8efbbcSSaurabh Misra * atomic operation, which translates to a LOCK OR instruction,
3385d8efbbcSSaurabh Misra * which is serializing.
3395d8efbbcSSaurabh Misra */
3405d8efbbcSSaurabh Misra atomic_or_ulong(&flag, 1);
3415d8efbbcSSaurabh Misra
342b6917abeSmishra vector = apic_resv_vector[ipl];
343b6917abeSmishra
344b6917abeSmishra flag = intr_clear();
345b6917abeSmishra
3465d8efbbcSSaurabh Misra /*
347325e77f4SSaurabh Misra * According to X2APIC specification in section '2.3.5.1' of
3485d8efbbcSSaurabh Misra * Interrupt Command Register Semantics, the semantics of
3495d8efbbcSSaurabh Misra * programming Interrupt Command Register to dispatch an interrupt
3505d8efbbcSSaurabh Misra * is simplified. A single MSR write to the 64-bit ICR is required
3515d8efbbcSSaurabh Misra * for dispatching an interrupt. Specifically with the 64-bit MSR
3525d8efbbcSSaurabh Misra * interface to ICR, system software is not required to check the
3535d8efbbcSSaurabh Misra * status of the delivery status bit prior to writing to the ICR
3545d8efbbcSSaurabh Misra * to send an IPI. With the removal of the Delivery Status bit,
3555d8efbbcSSaurabh Misra * system software no longer has a reason to read the ICR. It remains
3565d8efbbcSSaurabh Misra * readable only to aid in debugging.
3575d8efbbcSSaurabh Misra */
3585d8efbbcSSaurabh Misra #ifdef DEBUG
3595d8efbbcSSaurabh Misra APIC_AV_PENDING_SET();
3605d8efbbcSSaurabh Misra #endif /* DEBUG */
361b6917abeSmishra
36287cc6269SSaurabh Misra if ((cpun == psm_get_cpu_id())) {
36387cc6269SSaurabh Misra X2APIC_WRITE(X2APIC_SELF_IPI, vector);
36487cc6269SSaurabh Misra } else {
365b6917abeSmishra apic_reg_ops->apic_write_int_cmd(
366b6917abeSmishra apic_cpus[cpun].aci_local_id, vector);
36787cc6269SSaurabh Misra }
368b6917abeSmishra
369b6917abeSmishra intr_restore(flag);
370b6917abeSmishra }
371b6917abeSmishra
372325e77f4SSaurabh Misra /*
373325e77f4SSaurabh Misra * Generates IPI to another CPU depending on the local APIC mode.
374325e77f4SSaurabh Misra * apic_send_ipi() and x2apic_send_ipi() depends on the configured
375325e77f4SSaurabh Misra * mode of the local APIC, but that may not match the actual mode
376325e77f4SSaurabh Misra * early in CPU startup.
377325e77f4SSaurabh Misra *
378325e77f4SSaurabh Misra * Any changes made to this routine must be accompanied by similar
379325e77f4SSaurabh Misra * changes to apic_send_ipi().
380325e77f4SSaurabh Misra */
381b6917abeSmishra void
apic_common_send_ipi(int cpun,int ipl)382325e77f4SSaurabh Misra apic_common_send_ipi(int cpun, int ipl)
383b6917abeSmishra {
384325e77f4SSaurabh Misra int vector;
385325e77f4SSaurabh Misra ulong_t flag;
386325e77f4SSaurabh Misra int mode = apic_local_mode();
387325e77f4SSaurabh Misra
388325e77f4SSaurabh Misra if (mode == LOCAL_X2APIC) {
389325e77f4SSaurabh Misra x2apic_send_ipi(cpun, ipl);
390325e77f4SSaurabh Misra return;
391325e77f4SSaurabh Misra }
392325e77f4SSaurabh Misra
393325e77f4SSaurabh Misra ASSERT(mode == LOCAL_APIC);
394325e77f4SSaurabh Misra
395325e77f4SSaurabh Misra vector = apic_resv_vector[ipl];
396325e77f4SSaurabh Misra ASSERT((vector >= APIC_BASE_VECT) && (vector <= APIC_SPUR_INTR));
397325e77f4SSaurabh Misra flag = intr_clear();
398325e77f4SSaurabh Misra while (local_apic_regs_ops.apic_read(APIC_INT_CMD1) & AV_PENDING)
399325e77f4SSaurabh Misra apic_ret();
400325e77f4SSaurabh Misra local_apic_regs_ops.apic_write_int_cmd(apic_cpus[cpun].aci_local_id,
401325e77f4SSaurabh Misra vector);
402325e77f4SSaurabh Misra intr_restore(flag);
403b6917abeSmishra }
404