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