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 */
45static uint64_t local_apic_read(uint32_t reg);
46static void local_apic_write(uint32_t reg, uint64_t value);
47static int get_local_apic_pri(void);
48static void local_apic_write_task_reg(uint64_t value);
49static 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 */
63apic_mode_t apic_mode = LOCAL_APIC;	/* Default mode is Local APIC */
64
65/* See apic_directed_EOI_supported().  Currently 3-state variable. */
66volatile int apic_directed_eoi_state = 2;
67
68/* Uses MMIO (Memory Mapped IO) */
69apic_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
78int apic_have_32bit_cr8 = 0;
79
80/* The default ops is local APIC (Memory Mapped IO) */
81apic_reg_ops_t *apic_reg_ops = &local_apic_regs_ops;
82
83/*
84 * APIC register ops related data sturctures and functions.
85 */
86void apic_send_EOI();
87void apic_send_directed_EOI(uint32_t irq);
88
89/*
90 * Local APIC Implementation
91 */
92static uint64_t
93local_apic_read(uint32_t reg)
94{
95	return ((uint32_t)apicadr[reg]);
96}
97
98static void
99local_apic_write(uint32_t reg, uint64_t value)
100{
101	apicadr[reg] = (uint32_t)value;
102}
103
104static int
105get_local_apic_pri(void)
106{
107#if defined(__amd64)
108	return ((int)getcr8());
109#else
110	if (apic_have_32bit_cr8)
111		return ((int)getcr8());
112	return (apicadr[APIC_TASK_REG]);
113#endif
114}
115
116static void
117local_apic_write_task_reg(uint64_t value)
118{
119#if defined(__amd64)
120	setcr8((ulong_t)(value >> APIC_IPL_SHIFT));
121#else
122	if (apic_have_32bit_cr8)
123		setcr8((ulong_t)(value >> APIC_IPL_SHIFT));
124	else
125		apicadr[APIC_TASK_REG] = (uint32_t)value;
126#endif
127}
128
129static void
130local_apic_write_int_cmd(uint32_t cpu_id, uint32_t cmd1)
131{
132	apicadr[APIC_INT_CMD2] = cpu_id << APIC_ICR_ID_BIT_OFFSET;
133	apicadr[APIC_INT_CMD1] = cmd1;
134}
135
136
137/*ARGSUSED*/
138void
139apic_send_EOI(uint32_t irq)
140{
141	apic_reg_ops->apic_write(APIC_EOI_REG, 0);
142}
143
144/*
145 * Support for Directed EOI capability is available in both the xAPIC
146 * and x2APIC mode.
147 */
148void
149apic_send_directed_EOI(uint32_t irq)
150{
151	uchar_t ioapicindex;
152	uchar_t vector;
153	apic_irq_t *apic_irq;
154	short intr_index;
155
156	/*
157	 * Following the EOI to the local APIC unit, perform a directed
158	 * EOI to the IOxAPIC generating the interrupt by writing to its
159	 * EOI register.
160	 *
161	 * A broadcast EOI is not generated.
162	 */
163	apic_reg_ops->apic_write(APIC_EOI_REG, 0);
164
165	apic_irq = apic_irq_table[irq];
166	while (apic_irq) {
167		intr_index = apic_irq->airq_mps_intr_index;
168		if (intr_index == ACPI_INDEX || intr_index >= 0) {
169			ioapicindex = apic_irq->airq_ioapicindex;
170			vector = apic_irq->airq_vector;
171			ioapic_write_eoi(ioapicindex, vector);
172		}
173		apic_irq = apic_irq->airq_next;
174	}
175}
176
177/*
178 * Determine which mode the current CPU is in. See the table above.
179 * (IA32_APIC_BASE[11])   (IA32_APIC_BASE[10])
180 */
181int
182apic_local_mode(void)
183{
184	uint64_t apic_base_msr;
185	int bit = ((0x1 << (X2APIC_ENABLE_BIT + 1)) |
186	    (0x1 << X2APIC_ENABLE_BIT));
187
188	apic_base_msr = rdmsr(REG_APIC_BASE_MSR);
189
190	if ((apic_base_msr & bit) == bit)
191		return (LOCAL_X2APIC);
192	else
193		return (LOCAL_APIC);
194}
195
196void
197apic_set_directed_EOI_handler()
198{
199	apic_reg_ops->apic_send_eoi = apic_send_directed_EOI;
200}
201
202int
203apic_directed_EOI_supported()
204{
205	uint32_t ver;
206
207	/*
208	 * There are some known issues with some versions of Linux KVM and QEMU
209	 * where by directed EOIs do not properly function and instead get
210	 * coalesced at the hypervisor, causing the host not to see interrupts.
211	 * Thus, when the platform is KVM, we would like to disable it by
212	 * default, but keep it available otherwise.
213	 *
214	 * We use a three-state variable (apic_directed_eoi_state) to determine
215	 * how we handle directed EOI.
216	 *
217	 * 0 --> Don't do directed EOI at all.
218	 * 1 --> Do directed EOI if available, no matter the HW environment.
219	 * 2 --> Don't do directed EOI on KVM, but do it otherwise if available.
220	 *
221	 * If some grinning weirdo put something else in there, treat it as '2'
222	 * (i.e. the current default).
223	 *
224	 * Note, at this time illumos KVM does not identify as KVM. If it does,
225	 * we'll need to do some work to determine if it should be caught by
226	 * this or if it should show up as its own value of platform_type.
227	 */
228	switch (apic_directed_eoi_state) {
229	case 0:
230		/* Don't do it at all. */
231		return (0);
232	case 1:
233		break;
234	case 2:
235	default:
236		/* Only do it if we aren't on KVM. */
237		if (get_hwenv() == HW_KVM)
238			return (0);
239		/* FALLTHRU */
240	}
241
242	ver = apic_reg_ops->apic_read(APIC_VERS_REG);
243	if (ver & APIC_DIRECTED_EOI_BIT)
244		return (1);
245
246	return (0);
247}
248