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 /*
23 * Copyright (c) 1993, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25 /*
26 * Copyright (c) 2010, Intel Corporation.
27 * All rights reserved.
28 */
29 /*
30 * Copyright (c) 2013, Joyent, Inc. All rights reserved.
31 */
32
33 /*
34 * To understand how the pcplusmp module interacts with the interrupt subsystem
35 * read the theory statement in uts/i86pc/os/intr.c.
36 */
37
38 /*
39 * PSMI 1.1 extensions are supported only in 2.6 and later versions.
40 * PSMI 1.2 extensions are supported only in 2.7 and later versions.
41 * PSMI 1.3 and 1.4 extensions are supported in Solaris 10.
42 * PSMI 1.5 extensions are supported in Solaris Nevada.
43 * PSMI 1.6 extensions are supported in Solaris Nevada.
44 * PSMI 1.7 extensions are supported in Solaris Nevada.
45 */
46 #define PSMI_1_7
47
48 #include <sys/processor.h>
49 #include <sys/time.h>
50 #include <sys/psm.h>
51 #include <sys/smp_impldefs.h>
52 #include <sys/cram.h>
53 #include <sys/acpi/acpi.h>
54 #include <sys/acpica.h>
55 #include <sys/psm_common.h>
56 #include <sys/apic.h>
57 #include <sys/pit.h>
58 #include <sys/ddi.h>
59 #include <sys/sunddi.h>
60 #include <sys/ddi_impldefs.h>
61 #include <sys/pci.h>
62 #include <sys/promif.h>
63 #include <sys/x86_archext.h>
64 #include <sys/cpc_impl.h>
65 #include <sys/uadmin.h>
66 #include <sys/panic.h>
67 #include <sys/debug.h>
68 #include <sys/archsystm.h>
69 #include <sys/trap.h>
70 #include <sys/machsystm.h>
71 #include <sys/sysmacros.h>
72 #include <sys/cpuvar.h>
73 #include <sys/rm_platter.h>
74 #include <sys/privregs.h>
75 #include <sys/note.h>
76 #include <sys/pci_intr_lib.h>
77 #include <sys/spl.h>
78 #include <sys/clock.h>
79 #include <sys/cyclic.h>
80 #include <sys/dditypes.h>
81 #include <sys/sunddi.h>
82 #include <sys/x_call.h>
83 #include <sys/reboot.h>
84 #include <sys/hpet.h>
85 #include <sys/apic_common.h>
86 #include <sys/apic_timer.h>
87
88 /*
89 * Local Function Prototypes
90 */
91 static void apic_init_intr(void);
92
93 /*
94 * standard MP entries
95 */
96 static int apic_probe(void);
97 static int apic_getclkirq(int ipl);
98 static void apic_init(void);
99 static void apic_picinit(void);
100 static int apic_post_cpu_start(void);
101 static int apic_intr_enter(int ipl, int *vect);
102 static void apic_setspl(int ipl);
103 static void x2apic_setspl(int ipl);
104 static int apic_addspl(int ipl, int vector, int min_ipl, int max_ipl);
105 static int apic_delspl(int ipl, int vector, int min_ipl, int max_ipl);
106 static int apic_disable_intr(processorid_t cpun);
107 static void apic_enable_intr(processorid_t cpun);
108 static int apic_get_ipivect(int ipl, int type);
109 static void apic_post_cyclic_setup(void *arg);
110
111 /*
112 * The following vector assignments influence the value of ipltopri and
113 * vectortoipl. Note that vectors 0 - 0x1f are not used. We can program
114 * idle to 0 and IPL 0 to 0xf to differentiate idle in case
115 * we care to do so in future. Note some IPLs which are rarely used
116 * will share the vector ranges and heavily used IPLs (5 and 6) have
117 * a wide range.
118 *
119 * This array is used to initialize apic_ipls[] (in apic_init()).
120 *
121 * IPL Vector range. as passed to intr_enter
122 * 0 none.
123 * 1,2,3 0x20-0x2f 0x0-0xf
124 * 4 0x30-0x3f 0x10-0x1f
125 * 5 0x40-0x5f 0x20-0x3f
126 * 6 0x60-0x7f 0x40-0x5f
127 * 7,8,9 0x80-0x8f 0x60-0x6f
128 * 10 0x90-0x9f 0x70-0x7f
129 * 11 0xa0-0xaf 0x80-0x8f
130 * ... ...
131 * 15 0xe0-0xef 0xc0-0xcf
132 * 15 0xf0-0xff 0xd0-0xdf
133 */
134 uchar_t apic_vectortoipl[APIC_AVAIL_VECTOR / APIC_VECTOR_PER_IPL] = {
135 3, 4, 5, 5, 6, 6, 9, 10, 11, 12, 13, 14, 15, 15
136 };
137 /*
138 * The ipl of an ISR at vector X is apic_vectortoipl[X>>4]
139 * NOTE that this is vector as passed into intr_enter which is
140 * programmed vector - 0x20 (APIC_BASE_VECT)
141 */
142
143 uchar_t apic_ipltopri[MAXIPL + 1]; /* unix ipl to apic pri */
144 /* The taskpri to be programmed into apic to mask given ipl */
145
146 /*
147 * Correlation of the hardware vector to the IPL in use, initialized
148 * from apic_vectortoipl[] in apic_init(). The final IPLs may not correlate
149 * to the IPLs in apic_vectortoipl on some systems that share interrupt lines
150 * connected to errata-stricken IOAPICs
151 */
152 uchar_t apic_ipls[APIC_AVAIL_VECTOR];
153
154 /*
155 * Patchable global variables.
156 */
157 int apic_enable_hwsoftint = 0; /* 0 - disable, 1 - enable */
158 int apic_enable_bind_log = 1; /* 1 - display interrupt binding log */
159
160 /*
161 * Local static data
162 */
163 static struct psm_ops apic_ops = {
164 apic_probe,
165
166 apic_init,
167 apic_picinit,
168 apic_intr_enter,
169 apic_intr_exit,
170 apic_setspl,
171 apic_addspl,
172 apic_delspl,
173 apic_disable_intr,
174 apic_enable_intr,
175 (int (*)(int))NULL, /* psm_softlvl_to_irq */
176 (void (*)(int))NULL, /* psm_set_softintr */
177
178 apic_set_idlecpu,
179 apic_unset_idlecpu,
180
181 apic_clkinit,
182 apic_getclkirq,
183 (void (*)(void))NULL, /* psm_hrtimeinit */
184 apic_gethrtime,
185
186 apic_get_next_processorid,
187 apic_cpu_start,
188 apic_post_cpu_start,
189 apic_shutdown,
190 apic_get_ipivect,
191 apic_send_ipi,
192
193 (int (*)(dev_info_t *, int))NULL, /* psm_translate_irq */
194 (void (*)(int, char *))NULL, /* psm_notify_error */
195 (void (*)(int))NULL, /* psm_notify_func */
196 apic_timer_reprogram,
197 apic_timer_enable,
198 apic_timer_disable,
199 apic_post_cyclic_setup,
200 apic_preshutdown,
201 apic_intr_ops, /* Advanced DDI Interrupt framework */
202 apic_state, /* save, restore apic state for S3 */
203 apic_cpu_ops, /* CPU control interface. */
204 };
205
206 struct psm_ops *psmops = &apic_ops;
207
208 static struct psm_info apic_psm_info = {
209 PSM_INFO_VER01_7, /* version */
210 PSM_OWN_EXCLUSIVE, /* ownership */
211 (struct psm_ops *)&apic_ops, /* operation */
212 APIC_PCPLUSMP_NAME, /* machine name */
213 "pcplusmp v1.4 compatible",
214 };
215
216 static void *apic_hdlp;
217
218 /* to gather intr data and redistribute */
219 static void apic_redistribute_compute(void);
220
221 /*
222 * This is the loadable module wrapper
223 */
224
225 int
_init(void)226 _init(void)
227 {
228 if (apic_coarse_hrtime)
229 apic_ops.psm_gethrtime = &apic_gettime;
230 return (psm_mod_init(&apic_hdlp, &apic_psm_info));
231 }
232
233 int
_fini(void)234 _fini(void)
235 {
236 return (psm_mod_fini(&apic_hdlp, &apic_psm_info));
237 }
238
239 int
_info(struct modinfo * modinfop)240 _info(struct modinfo *modinfop)
241 {
242 return (psm_mod_info(&apic_hdlp, &apic_psm_info, modinfop));
243 }
244
245 static int
apic_probe(void)246 apic_probe(void)
247 {
248 /* check if apix is initialized */
249 if (apix_enable && apix_loaded())
250 return (PSM_FAILURE);
251 else
252 apix_enable = 0; /* continue using pcplusmp PSM */
253
254 return (apic_probe_common(apic_psm_info.p_mach_idstring));
255 }
256
257 static uchar_t
apic_xlate_vector_by_irq(uchar_t irq)258 apic_xlate_vector_by_irq(uchar_t irq)
259 {
260 if (apic_irq_table[irq] == NULL)
261 return (0);
262
263 return (apic_irq_table[irq]->airq_vector);
264 }
265
266 void
apic_init(void)267 apic_init(void)
268 {
269 int i;
270 int j = 1;
271
272 psm_get_ioapicid = apic_get_ioapicid;
273 psm_get_localapicid = apic_get_localapicid;
274 psm_xlate_vector_by_irq = apic_xlate_vector_by_irq;
275
276 apic_ipltopri[0] = APIC_VECTOR_PER_IPL; /* leave 0 for idle */
277 for (i = 0; i < (APIC_AVAIL_VECTOR / APIC_VECTOR_PER_IPL); i++) {
278 if ((i < ((APIC_AVAIL_VECTOR / APIC_VECTOR_PER_IPL) - 1)) &&
279 (apic_vectortoipl[i + 1] == apic_vectortoipl[i]))
280 /* get to highest vector at the same ipl */
281 continue;
282 for (; j <= apic_vectortoipl[i]; j++) {
283 apic_ipltopri[j] = (i << APIC_IPL_SHIFT) +
284 APIC_BASE_VECT;
285 }
286 }
287 for (; j < MAXIPL + 1; j++)
288 /* fill up any empty ipltopri slots */
289 apic_ipltopri[j] = (i << APIC_IPL_SHIFT) + APIC_BASE_VECT;
290 apic_init_common();
291
292 #if !defined(__amd64)
293 if (cpuid_have_cr8access(CPU))
294 apic_have_32bit_cr8 = 1;
295 #endif
296 }
297
298 static void
apic_init_intr(void)299 apic_init_intr(void)
300 {
301 processorid_t cpun = psm_get_cpu_id();
302 uint_t nlvt;
303 uint32_t svr = AV_UNIT_ENABLE | APIC_SPUR_INTR;
304
305 apic_reg_ops->apic_write_task_reg(APIC_MASK_ALL);
306
307 if (apic_mode == LOCAL_APIC) {
308 /*
309 * We are running APIC in MMIO mode.
310 */
311 if (apic_flat_model) {
312 apic_reg_ops->apic_write(APIC_FORMAT_REG,
313 APIC_FLAT_MODEL);
314 } else {
315 apic_reg_ops->apic_write(APIC_FORMAT_REG,
316 APIC_CLUSTER_MODEL);
317 }
318
319 apic_reg_ops->apic_write(APIC_DEST_REG,
320 AV_HIGH_ORDER >> cpun);
321 }
322
323 if (apic_directed_EOI_supported()) {
324 /*
325 * Setting the 12th bit in the Spurious Interrupt Vector
326 * Register suppresses broadcast EOIs generated by the local
327 * APIC. The suppression of broadcast EOIs happens only when
328 * interrupts are level-triggered.
329 */
330 svr |= APIC_SVR_SUPPRESS_BROADCAST_EOI;
331 }
332
333 /* need to enable APIC before unmasking NMI */
334 apic_reg_ops->apic_write(APIC_SPUR_INT_REG, svr);
335
336 /*
337 * Presence of an invalid vector with delivery mode AV_FIXED can
338 * cause an error interrupt, even if the entry is masked...so
339 * write a valid vector to LVT entries along with the mask bit
340 */
341
342 /* All APICs have timer and LINT0/1 */
343 apic_reg_ops->apic_write(APIC_LOCAL_TIMER, AV_MASK|APIC_RESV_IRQ);
344 apic_reg_ops->apic_write(APIC_INT_VECT0, AV_MASK|APIC_RESV_IRQ);
345 apic_reg_ops->apic_write(APIC_INT_VECT1, AV_NMI); /* enable NMI */
346
347 /*
348 * On integrated APICs, the number of LVT entries is
349 * 'Max LVT entry' + 1; on 82489DX's (non-integrated
350 * APICs), nlvt is "3" (LINT0, LINT1, and timer)
351 */
352
353 if (apic_cpus[cpun].aci_local_ver < APIC_INTEGRATED_VERS) {
354 nlvt = 3;
355 } else {
356 nlvt = ((apic_reg_ops->apic_read(APIC_VERS_REG) >> 16) &
357 0xFF) + 1;
358 }
359
360 if (nlvt >= 5) {
361 /* Enable performance counter overflow interrupt */
362
363 if (!is_x86_feature(x86_featureset, X86FSET_MSR))
364 apic_enable_cpcovf_intr = 0;
365 if (apic_enable_cpcovf_intr) {
366 if (apic_cpcovf_vect == 0) {
367 int ipl = APIC_PCINT_IPL;
368 int irq = apic_get_ipivect(ipl, -1);
369
370 ASSERT(irq != -1);
371 apic_cpcovf_vect =
372 apic_irq_table[irq]->airq_vector;
373 ASSERT(apic_cpcovf_vect);
374 (void) add_avintr(NULL, ipl,
375 (avfunc)kcpc_hw_overflow_intr,
376 "apic pcint", irq, NULL, NULL, NULL, NULL);
377 kcpc_hw_overflow_intr_installed = 1;
378 kcpc_hw_enable_cpc_intr =
379 apic_cpcovf_mask_clear;
380 }
381 apic_reg_ops->apic_write(APIC_PCINT_VECT,
382 apic_cpcovf_vect);
383 }
384 }
385
386 if (nlvt >= 6) {
387 /* Only mask TM intr if the BIOS apparently doesn't use it */
388
389 uint32_t lvtval;
390
391 lvtval = apic_reg_ops->apic_read(APIC_THERM_VECT);
392 if (((lvtval & AV_MASK) == AV_MASK) ||
393 ((lvtval & AV_DELIV_MODE) != AV_SMI)) {
394 apic_reg_ops->apic_write(APIC_THERM_VECT,
395 AV_MASK|APIC_RESV_IRQ);
396 }
397 }
398
399 /* Enable error interrupt */
400
401 if (nlvt >= 4 && apic_enable_error_intr) {
402 if (apic_errvect == 0) {
403 int ipl = 0xf; /* get highest priority intr */
404 int irq = apic_get_ipivect(ipl, -1);
405
406 ASSERT(irq != -1);
407 apic_errvect = apic_irq_table[irq]->airq_vector;
408 ASSERT(apic_errvect);
409 /*
410 * Not PSMI compliant, but we are going to merge
411 * with ON anyway
412 */
413 (void) add_avintr((void *)NULL, ipl,
414 (avfunc)apic_error_intr, "apic error intr",
415 irq, NULL, NULL, NULL, NULL);
416 }
417 apic_reg_ops->apic_write(APIC_ERR_VECT, apic_errvect);
418 apic_reg_ops->apic_write(APIC_ERROR_STATUS, 0);
419 apic_reg_ops->apic_write(APIC_ERROR_STATUS, 0);
420 }
421
422 /* Enable CMCI interrupt */
423 if (cmi_enable_cmci) {
424
425 mutex_enter(&cmci_cpu_setup_lock);
426 if (cmci_cpu_setup_registered == 0) {
427 mutex_enter(&cpu_lock);
428 register_cpu_setup_func(cmci_cpu_setup, NULL);
429 mutex_exit(&cpu_lock);
430 cmci_cpu_setup_registered = 1;
431 }
432 mutex_exit(&cmci_cpu_setup_lock);
433
434 if (apic_cmci_vect == 0) {
435 int ipl = 0x2;
436 int irq = apic_get_ipivect(ipl, -1);
437
438 ASSERT(irq != -1);
439 apic_cmci_vect = apic_irq_table[irq]->airq_vector;
440 ASSERT(apic_cmci_vect);
441
442 (void) add_avintr(NULL, ipl,
443 (avfunc)cmi_cmci_trap,
444 "apic cmci intr", irq, NULL, NULL, NULL, NULL);
445 }
446 apic_reg_ops->apic_write(APIC_CMCI_VECT, apic_cmci_vect);
447 }
448 }
449
450 static void
apic_picinit(void)451 apic_picinit(void)
452 {
453 int i, j;
454 uint_t isr;
455
456 /*
457 * Initialize and enable interrupt remapping before apic
458 * hardware initialization
459 */
460 apic_intrmap_init(apic_mode);
461
462 /*
463 * On UniSys Model 6520, the BIOS leaves vector 0x20 isr
464 * bit on without clearing it with EOI. Since softint
465 * uses vector 0x20 to interrupt itself, so softint will
466 * not work on this machine. In order to fix this problem
467 * a check is made to verify all the isr bits are clear.
468 * If not, EOIs are issued to clear the bits.
469 */
470 for (i = 7; i >= 1; i--) {
471 isr = apic_reg_ops->apic_read(APIC_ISR_REG + (i * 4));
472 if (isr != 0)
473 for (j = 0; ((j < 32) && (isr != 0)); j++)
474 if (isr & (1 << j)) {
475 apic_reg_ops->apic_write(
476 APIC_EOI_REG, 0);
477 isr &= ~(1 << j);
478 apic_error |= APIC_ERR_BOOT_EOI;
479 }
480 }
481
482 /* set a flag so we know we have run apic_picinit() */
483 apic_picinit_called = 1;
484 LOCK_INIT_CLEAR(&apic_gethrtime_lock);
485 LOCK_INIT_CLEAR(&apic_ioapic_lock);
486 LOCK_INIT_CLEAR(&apic_error_lock);
487 LOCK_INIT_CLEAR(&apic_mode_switch_lock);
488
489 picsetup(); /* initialise the 8259 */
490
491 /* add nmi handler - least priority nmi handler */
492 LOCK_INIT_CLEAR(&apic_nmi_lock);
493
494 if (!psm_add_nmintr(0, (avfunc) apic_nmi_intr,
495 "pcplusmp NMI handler", (caddr_t)NULL))
496 cmn_err(CE_WARN, "pcplusmp: Unable to add nmi handler");
497
498 /*
499 * Check for directed-EOI capability in the local APIC.
500 */
501 if (apic_directed_EOI_supported() == 1) {
502 apic_set_directed_EOI_handler();
503 }
504
505 apic_init_intr();
506
507 /* enable apic mode if imcr present */
508 if (apic_imcrp) {
509 outb(APIC_IMCR_P1, (uchar_t)APIC_IMCR_SELECT);
510 outb(APIC_IMCR_P2, (uchar_t)APIC_IMCR_APIC);
511 }
512
513 ioapic_init_intr(IOAPIC_MASK);
514 }
515
516 #ifdef DEBUG
517 void
apic_break(void)518 apic_break(void)
519 {
520 }
521 #endif /* DEBUG */
522
523 /*
524 * platform_intr_enter
525 *
526 * Called at the beginning of the interrupt service routine to
527 * mask all level equal to and below the interrupt priority
528 * of the interrupting vector. An EOI should be given to
529 * the interrupt controller to enable other HW interrupts.
530 *
531 * Return -1 for spurious interrupts
532 *
533 */
534 /*ARGSUSED*/
535 static int
apic_intr_enter(int ipl,int * vectorp)536 apic_intr_enter(int ipl, int *vectorp)
537 {
538 uchar_t vector;
539 int nipl;
540 int irq;
541 ulong_t iflag;
542 apic_cpus_info_t *cpu_infop;
543
544 /*
545 * The real vector delivered is (*vectorp + 0x20), but our caller
546 * subtracts 0x20 from the vector before passing it to us.
547 * (That's why APIC_BASE_VECT is 0x20.)
548 */
549 vector = (uchar_t)*vectorp;
550
551 /* if interrupted by the clock, increment apic_nsec_since_boot */
552 if (vector == apic_clkvect) {
553 if (!apic_oneshot) {
554 /* NOTE: this is not MT aware */
555 apic_hrtime_stamp++;
556 apic_nsec_since_boot += apic_nsec_per_intr;
557 apic_hrtime_stamp++;
558 last_count_read = apic_hertz_count;
559 apic_redistribute_compute();
560 }
561
562 /* We will avoid all the book keeping overhead for clock */
563 nipl = apic_ipls[vector];
564
565 *vectorp = apic_vector_to_irq[vector + APIC_BASE_VECT];
566
567 apic_reg_ops->apic_write_task_reg(apic_ipltopri[nipl]);
568 apic_reg_ops->apic_send_eoi(0);
569
570 return (nipl);
571 }
572
573 cpu_infop = &apic_cpus[psm_get_cpu_id()];
574
575 if (vector == (APIC_SPUR_INTR - APIC_BASE_VECT)) {
576 cpu_infop->aci_spur_cnt++;
577 return (APIC_INT_SPURIOUS);
578 }
579
580 /* Check if the vector we got is really what we need */
581 if (apic_revector_pending) {
582 /*
583 * Disable interrupts for the duration of
584 * the vector translation to prevent a self-race for
585 * the apic_revector_lock. This cannot be done
586 * in apic_xlate_vector because it is recursive and
587 * we want the vector translation to be atomic with
588 * respect to other (higher-priority) interrupts.
589 */
590 iflag = intr_clear();
591 vector = apic_xlate_vector(vector + APIC_BASE_VECT) -
592 APIC_BASE_VECT;
593 intr_restore(iflag);
594 }
595
596 nipl = apic_ipls[vector];
597 *vectorp = irq = apic_vector_to_irq[vector + APIC_BASE_VECT];
598
599 apic_reg_ops->apic_write_task_reg(apic_ipltopri[nipl]);
600
601 cpu_infop->aci_current[nipl] = (uchar_t)irq;
602 cpu_infop->aci_curipl = (uchar_t)nipl;
603 cpu_infop->aci_ISR_in_progress |= 1 << nipl;
604
605 /*
606 * apic_level_intr could have been assimilated into the irq struct.
607 * but, having it as a character array is more efficient in terms of
608 * cache usage. So, we leave it as is.
609 */
610 if (!apic_level_intr[irq]) {
611 apic_reg_ops->apic_send_eoi(0);
612 }
613
614 #ifdef DEBUG
615 APIC_DEBUG_BUF_PUT(vector);
616 APIC_DEBUG_BUF_PUT(irq);
617 APIC_DEBUG_BUF_PUT(nipl);
618 APIC_DEBUG_BUF_PUT(psm_get_cpu_id());
619 if ((apic_stretch_interrupts) && (apic_stretch_ISR & (1 << nipl)))
620 drv_usecwait(apic_stretch_interrupts);
621
622 if (apic_break_on_cpu == psm_get_cpu_id())
623 apic_break();
624 #endif /* DEBUG */
625 return (nipl);
626 }
627
628 /*
629 * This macro is a common code used by MMIO local apic and X2APIC
630 * local apic.
631 */
632 #define APIC_INTR_EXIT() \
633 { \
634 cpu_infop = &apic_cpus[psm_get_cpu_id()]; \
635 if (apic_level_intr[irq]) \
636 apic_reg_ops->apic_send_eoi(irq); \
637 cpu_infop->aci_curipl = (uchar_t)prev_ipl; \
638 /* ISR above current pri could not be in progress */ \
639 cpu_infop->aci_ISR_in_progress &= (2 << prev_ipl) - 1; \
640 }
641
642 /*
643 * Any changes made to this function must also change X2APIC
644 * version of intr_exit.
645 */
646 void
apic_intr_exit(int prev_ipl,int irq)647 apic_intr_exit(int prev_ipl, int irq)
648 {
649 apic_cpus_info_t *cpu_infop;
650
651 apic_reg_ops->apic_write_task_reg(apic_ipltopri[prev_ipl]);
652
653 APIC_INTR_EXIT();
654 }
655
656 /*
657 * Same as apic_intr_exit() except it uses MSR rather than MMIO
658 * to access local apic registers.
659 */
660 void
x2apic_intr_exit(int prev_ipl,int irq)661 x2apic_intr_exit(int prev_ipl, int irq)
662 {
663 apic_cpus_info_t *cpu_infop;
664
665 X2APIC_WRITE(APIC_TASK_REG, apic_ipltopri[prev_ipl]);
666 APIC_INTR_EXIT();
667 }
668
669 intr_exit_fn_t
psm_intr_exit_fn(void)670 psm_intr_exit_fn(void)
671 {
672 if (apic_mode == LOCAL_X2APIC)
673 return (x2apic_intr_exit);
674
675 return (apic_intr_exit);
676 }
677
678 /*
679 * Mask all interrupts below or equal to the given IPL.
680 * Any changes made to this function must also change X2APIC
681 * version of setspl.
682 */
683 static void
apic_setspl(int ipl)684 apic_setspl(int ipl)
685 {
686 apic_reg_ops->apic_write_task_reg(apic_ipltopri[ipl]);
687
688 /* interrupts at ipl above this cannot be in progress */
689 apic_cpus[psm_get_cpu_id()].aci_ISR_in_progress &= (2 << ipl) - 1;
690 /*
691 * this is a patch fix for the ALR QSMP P5 machine, so that interrupts
692 * have enough time to come in before the priority is raised again
693 * during the idle() loop.
694 */
695 if (apic_setspl_delay)
696 (void) apic_reg_ops->apic_get_pri();
697 }
698
699 /*
700 * X2APIC version of setspl.
701 * Mask all interrupts below or equal to the given IPL
702 */
703 static void
x2apic_setspl(int ipl)704 x2apic_setspl(int ipl)
705 {
706 X2APIC_WRITE(APIC_TASK_REG, apic_ipltopri[ipl]);
707
708 /* interrupts at ipl above this cannot be in progress */
709 apic_cpus[psm_get_cpu_id()].aci_ISR_in_progress &= (2 << ipl) - 1;
710 }
711
712 /*ARGSUSED*/
713 static int
apic_addspl(int irqno,int ipl,int min_ipl,int max_ipl)714 apic_addspl(int irqno, int ipl, int min_ipl, int max_ipl)
715 {
716 return (apic_addspl_common(irqno, ipl, min_ipl, max_ipl));
717 }
718
719 static int
apic_delspl(int irqno,int ipl,int min_ipl,int max_ipl)720 apic_delspl(int irqno, int ipl, int min_ipl, int max_ipl)
721 {
722 return (apic_delspl_common(irqno, ipl, min_ipl, max_ipl));
723 }
724
725 static int
apic_post_cpu_start(void)726 apic_post_cpu_start(void)
727 {
728 int cpun;
729 static int cpus_started = 1;
730
731 /* We know this CPU + BSP started successfully. */
732 cpus_started++;
733
734 /*
735 * On BSP we would have enabled X2APIC, if supported by processor,
736 * in acpi_probe(), but on AP we do it here.
737 *
738 * We enable X2APIC mode only if BSP is running in X2APIC & the
739 * local APIC mode of the current CPU is MMIO (xAPIC).
740 */
741 if (apic_mode == LOCAL_X2APIC && apic_detect_x2apic() &&
742 apic_local_mode() == LOCAL_APIC) {
743 apic_enable_x2apic();
744 }
745
746 /*
747 * Switch back to x2apic IPI sending method for performance when target
748 * CPU has entered x2apic mode.
749 */
750 if (apic_mode == LOCAL_X2APIC) {
751 apic_switch_ipi_callback(B_FALSE);
752 }
753
754 splx(ipltospl(LOCK_LEVEL));
755 apic_init_intr();
756
757 /*
758 * since some systems don't enable the internal cache on the non-boot
759 * cpus, so we have to enable them here
760 */
761 setcr0(getcr0() & ~(CR0_CD | CR0_NW));
762
763 #ifdef DEBUG
764 APIC_AV_PENDING_SET();
765 #else
766 if (apic_mode == LOCAL_APIC)
767 APIC_AV_PENDING_SET();
768 #endif /* DEBUG */
769
770 /*
771 * We may be booting, or resuming from suspend; aci_status will
772 * be APIC_CPU_INTR_ENABLE if coming from suspend, so we add the
773 * APIC_CPU_ONLINE flag here rather than setting aci_status completely.
774 */
775 cpun = psm_get_cpu_id();
776 apic_cpus[cpun].aci_status |= APIC_CPU_ONLINE;
777
778 apic_reg_ops->apic_write(APIC_DIVIDE_REG, apic_divide_reg_init);
779 return (PSM_SUCCESS);
780 }
781
782 /*
783 * type == -1 indicates it is an internal request. Do not change
784 * resv_vector for these requests
785 */
786 static int
apic_get_ipivect(int ipl,int type)787 apic_get_ipivect(int ipl, int type)
788 {
789 uchar_t vector;
790 int irq;
791
792 if ((irq = apic_allocate_irq(APIC_VECTOR(ipl))) != -1) {
793 if ((vector = apic_allocate_vector(ipl, irq, 1))) {
794 apic_irq_table[irq]->airq_mps_intr_index =
795 RESERVE_INDEX;
796 apic_irq_table[irq]->airq_vector = vector;
797 if (type != -1) {
798 apic_resv_vector[ipl] = vector;
799 }
800 return (irq);
801 }
802 }
803 apic_error |= APIC_ERR_GET_IPIVECT_FAIL;
804 return (-1); /* shouldn't happen */
805 }
806
807 static int
apic_getclkirq(int ipl)808 apic_getclkirq(int ipl)
809 {
810 int irq;
811
812 if ((irq = apic_get_ipivect(ipl, -1)) == -1)
813 return (-1);
814 /*
815 * Note the vector in apic_clkvect for per clock handling.
816 */
817 apic_clkvect = apic_irq_table[irq]->airq_vector - APIC_BASE_VECT;
818 APIC_VERBOSE_IOAPIC((CE_NOTE, "get_clkirq: vector = %x\n",
819 apic_clkvect));
820 return (irq);
821 }
822
823 /*
824 * Try and disable all interrupts. We just assign interrupts to other
825 * processors based on policy. If any were bound by user request, we
826 * let them continue and return failure. We do not bother to check
827 * for cache affinity while rebinding.
828 */
829
830 static int
apic_disable_intr(processorid_t cpun)831 apic_disable_intr(processorid_t cpun)
832 {
833 int bind_cpu = 0, i, hardbound = 0;
834 apic_irq_t *irq_ptr;
835 ulong_t iflag;
836
837 iflag = intr_clear();
838 lock_set(&apic_ioapic_lock);
839
840 for (i = 0; i <= APIC_MAX_VECTOR; i++) {
841 if (apic_reprogram_info[i].done == B_FALSE) {
842 if (apic_reprogram_info[i].bindcpu == cpun) {
843 /*
844 * CPU is busy -- it's the target of
845 * a pending reprogramming attempt
846 */
847 lock_clear(&apic_ioapic_lock);
848 intr_restore(iflag);
849 return (PSM_FAILURE);
850 }
851 }
852 }
853
854 apic_cpus[cpun].aci_status &= ~APIC_CPU_INTR_ENABLE;
855
856 apic_cpus[cpun].aci_curipl = 0;
857
858 i = apic_min_device_irq;
859 for (; i <= apic_max_device_irq; i++) {
860 /*
861 * If there are bound interrupts on this cpu, then
862 * rebind them to other processors.
863 */
864 if ((irq_ptr = apic_irq_table[i]) != NULL) {
865 ASSERT((irq_ptr->airq_temp_cpu == IRQ_UNBOUND) ||
866 (irq_ptr->airq_temp_cpu == IRQ_UNINIT) ||
867 (apic_cpu_in_range(irq_ptr->airq_temp_cpu)));
868
869 if (irq_ptr->airq_temp_cpu == (cpun | IRQ_USER_BOUND)) {
870 hardbound = 1;
871 continue;
872 }
873
874 if (irq_ptr->airq_temp_cpu == cpun) {
875 do {
876 bind_cpu =
877 apic_find_cpu(APIC_CPU_INTR_ENABLE);
878 } while (apic_rebind_all(irq_ptr, bind_cpu));
879 }
880 }
881 }
882
883 lock_clear(&apic_ioapic_lock);
884 intr_restore(iflag);
885
886 if (hardbound) {
887 cmn_err(CE_WARN, "Could not disable interrupts on %d"
888 "due to user bound interrupts", cpun);
889 return (PSM_FAILURE);
890 }
891 else
892 return (PSM_SUCCESS);
893 }
894
895 /*
896 * Bind interrupts to the CPU's local APIC.
897 * Interrupts should not be bound to a CPU's local APIC until the CPU
898 * is ready to receive interrupts.
899 */
900 static void
apic_enable_intr(processorid_t cpun)901 apic_enable_intr(processorid_t cpun)
902 {
903 int i;
904 apic_irq_t *irq_ptr;
905 ulong_t iflag;
906
907 iflag = intr_clear();
908 lock_set(&apic_ioapic_lock);
909
910 apic_cpus[cpun].aci_status |= APIC_CPU_INTR_ENABLE;
911
912 i = apic_min_device_irq;
913 for (i = apic_min_device_irq; i <= apic_max_device_irq; i++) {
914 if ((irq_ptr = apic_irq_table[i]) != NULL) {
915 if ((irq_ptr->airq_cpu & ~IRQ_USER_BOUND) == cpun) {
916 (void) apic_rebind_all(irq_ptr,
917 irq_ptr->airq_cpu);
918 }
919 }
920 }
921
922 if (apic_cpus[cpun].aci_status & APIC_CPU_SUSPEND)
923 apic_cpus[cpun].aci_status &= ~APIC_CPU_SUSPEND;
924
925 lock_clear(&apic_ioapic_lock);
926 intr_restore(iflag);
927 }
928
929 /*
930 * If this module needs a periodic handler for the interrupt distribution, it
931 * can be added here. The argument to the periodic handler is not currently
932 * used, but is reserved for future.
933 */
934 static void
apic_post_cyclic_setup(void * arg)935 apic_post_cyclic_setup(void *arg)
936 {
937 _NOTE(ARGUNUSED(arg))
938
939 cyc_handler_t cyh;
940 cyc_time_t cyt;
941
942 /* cpu_lock is held */
943 /* set up a periodic handler for intr redistribution */
944
945 /*
946 * In peridoc mode intr redistribution processing is done in
947 * apic_intr_enter during clk intr processing
948 */
949 if (!apic_oneshot)
950 return;
951
952 /*
953 * Register a periodical handler for the redistribution processing.
954 * Though we would generally prefer to use the DDI interface for
955 * periodic handler invocation, ddi_periodic_add(9F), we are
956 * unfortunately already holding cpu_lock, which ddi_periodic_add will
957 * attempt to take for us. Thus, we add our own cyclic directly:
958 */
959 cyh.cyh_func = (void (*)(void *))apic_redistribute_compute;
960 cyh.cyh_arg = NULL;
961 cyh.cyh_level = CY_LOW_LEVEL;
962
963 cyt.cyt_when = 0;
964 cyt.cyt_interval = apic_redistribute_sample_interval;
965
966 apic_cyclic_id = cyclic_add(&cyh, &cyt);
967 }
968
969 static void
apic_redistribute_compute(void)970 apic_redistribute_compute(void)
971 {
972 int i, j, max_busy;
973
974 if (apic_enable_dynamic_migration) {
975 if (++apic_nticks == apic_sample_factor_redistribution) {
976 /*
977 * Time to call apic_intr_redistribute().
978 * reset apic_nticks. This will cause max_busy
979 * to be calculated below and if it is more than
980 * apic_int_busy, we will do the whole thing
981 */
982 apic_nticks = 0;
983 }
984 max_busy = 0;
985 for (i = 0; i < apic_nproc; i++) {
986 if (!apic_cpu_in_range(i))
987 continue;
988
989 /*
990 * Check if curipl is non zero & if ISR is in
991 * progress
992 */
993 if (((j = apic_cpus[i].aci_curipl) != 0) &&
994 (apic_cpus[i].aci_ISR_in_progress & (1 << j))) {
995
996 int irq;
997 apic_cpus[i].aci_busy++;
998 irq = apic_cpus[i].aci_current[j];
999 apic_irq_table[irq]->airq_busy++;
1000 }
1001
1002 if (!apic_nticks &&
1003 (apic_cpus[i].aci_busy > max_busy))
1004 max_busy = apic_cpus[i].aci_busy;
1005 }
1006 if (!apic_nticks) {
1007 if (max_busy > apic_int_busy_mark) {
1008 /*
1009 * We could make the following check be
1010 * skipped > 1 in which case, we get a
1011 * redistribution at half the busy mark (due to
1012 * double interval). Need to be able to collect
1013 * more empirical data to decide if that is a
1014 * good strategy. Punt for now.
1015 */
1016 if (apic_skipped_redistribute) {
1017 apic_cleanup_busy();
1018 apic_skipped_redistribute = 0;
1019 } else {
1020 apic_intr_redistribute();
1021 }
1022 } else
1023 apic_skipped_redistribute++;
1024 }
1025 }
1026 }
1027
1028
1029 /*
1030 * The following functions are in the platform specific file so that they
1031 * can be different functions depending on whether we are running on
1032 * bare metal or a hypervisor.
1033 */
1034
1035 /*
1036 * Check to make sure there are enough irq slots
1037 */
1038 int
apic_check_free_irqs(int count)1039 apic_check_free_irqs(int count)
1040 {
1041 int i, avail;
1042
1043 avail = 0;
1044 for (i = APIC_FIRST_FREE_IRQ; i < APIC_RESV_IRQ; i++) {
1045 if ((apic_irq_table[i] == NULL) ||
1046 apic_irq_table[i]->airq_mps_intr_index == FREE_INDEX) {
1047 if (++avail >= count)
1048 return (PSM_SUCCESS);
1049 }
1050 }
1051 return (PSM_FAILURE);
1052 }
1053
1054 /*
1055 * This function allocates "count" MSI vector(s) for the given "dip/pri/type"
1056 */
1057 int
apic_alloc_msi_vectors(dev_info_t * dip,int inum,int count,int pri,int behavior)1058 apic_alloc_msi_vectors(dev_info_t *dip, int inum, int count, int pri,
1059 int behavior)
1060 {
1061 int rcount, i;
1062 uchar_t start, irqno;
1063 uint32_t cpu;
1064 major_t major;
1065 apic_irq_t *irqptr;
1066
1067 DDI_INTR_IMPLDBG((CE_CONT, "apic_alloc_msi_vectors: dip=0x%p "
1068 "inum=0x%x pri=0x%x count=0x%x behavior=%d\n",
1069 (void *)dip, inum, pri, count, behavior));
1070
1071 if (count > 1) {
1072 if (behavior == DDI_INTR_ALLOC_STRICT &&
1073 apic_multi_msi_enable == 0)
1074 return (0);
1075 if (apic_multi_msi_enable == 0)
1076 count = 1;
1077 }
1078
1079 if ((rcount = apic_navail_vector(dip, pri)) > count)
1080 rcount = count;
1081 else if (rcount == 0 || (rcount < count &&
1082 behavior == DDI_INTR_ALLOC_STRICT))
1083 return (0);
1084
1085 /* if not ISP2, then round it down */
1086 if (!ISP2(rcount))
1087 rcount = 1 << (highbit(rcount) - 1);
1088
1089 mutex_enter(&airq_mutex);
1090
1091 for (start = 0; rcount > 0; rcount >>= 1) {
1092 if ((start = apic_find_multi_vectors(pri, rcount)) != 0 ||
1093 behavior == DDI_INTR_ALLOC_STRICT)
1094 break;
1095 }
1096
1097 if (start == 0) {
1098 /* no vector available */
1099 mutex_exit(&airq_mutex);
1100 return (0);
1101 }
1102
1103 if (apic_check_free_irqs(rcount) == PSM_FAILURE) {
1104 /* not enough free irq slots available */
1105 mutex_exit(&airq_mutex);
1106 return (0);
1107 }
1108
1109 major = (dip != NULL) ? ddi_driver_major(dip) : 0;
1110 for (i = 0; i < rcount; i++) {
1111 if ((irqno = apic_allocate_irq(apic_first_avail_irq)) ==
1112 (uchar_t)-1) {
1113 /*
1114 * shouldn't happen because of the
1115 * apic_check_free_irqs() check earlier
1116 */
1117 mutex_exit(&airq_mutex);
1118 DDI_INTR_IMPLDBG((CE_CONT, "apic_alloc_msi_vectors: "
1119 "apic_allocate_irq failed\n"));
1120 return (i);
1121 }
1122 apic_max_device_irq = max(irqno, apic_max_device_irq);
1123 apic_min_device_irq = min(irqno, apic_min_device_irq);
1124 irqptr = apic_irq_table[irqno];
1125 #ifdef DEBUG
1126 if (apic_vector_to_irq[start + i] != APIC_RESV_IRQ)
1127 DDI_INTR_IMPLDBG((CE_CONT, "apic_alloc_msi_vectors: "
1128 "apic_vector_to_irq is not APIC_RESV_IRQ\n"));
1129 #endif
1130 apic_vector_to_irq[start + i] = (uchar_t)irqno;
1131
1132 irqptr->airq_vector = (uchar_t)(start + i);
1133 irqptr->airq_ioapicindex = (uchar_t)inum; /* start */
1134 irqptr->airq_intin_no = (uchar_t)rcount;
1135 irqptr->airq_ipl = pri;
1136 irqptr->airq_vector = start + i;
1137 irqptr->airq_origirq = (uchar_t)(inum + i);
1138 irqptr->airq_share_id = 0;
1139 irqptr->airq_mps_intr_index = MSI_INDEX;
1140 irqptr->airq_dip = dip;
1141 irqptr->airq_major = major;
1142 if (i == 0) /* they all bound to the same cpu */
1143 cpu = irqptr->airq_cpu = apic_bind_intr(dip, irqno,
1144 0xff, 0xff);
1145 else
1146 irqptr->airq_cpu = cpu;
1147 DDI_INTR_IMPLDBG((CE_CONT, "apic_alloc_msi_vectors: irq=0x%x "
1148 "dip=0x%p vector=0x%x origirq=0x%x pri=0x%x\n", irqno,
1149 (void *)irqptr->airq_dip, irqptr->airq_vector,
1150 irqptr->airq_origirq, pri));
1151 }
1152 mutex_exit(&airq_mutex);
1153 return (rcount);
1154 }
1155
1156 /*
1157 * This function allocates "count" MSI-X vector(s) for the given "dip/pri/type"
1158 */
1159 int
apic_alloc_msix_vectors(dev_info_t * dip,int inum,int count,int pri,int behavior)1160 apic_alloc_msix_vectors(dev_info_t *dip, int inum, int count, int pri,
1161 int behavior)
1162 {
1163 int rcount, i;
1164 major_t major;
1165
1166 mutex_enter(&airq_mutex);
1167
1168 if ((rcount = apic_navail_vector(dip, pri)) > count)
1169 rcount = count;
1170 else if (rcount == 0 || (rcount < count &&
1171 behavior == DDI_INTR_ALLOC_STRICT)) {
1172 rcount = 0;
1173 goto out;
1174 }
1175
1176 if (apic_check_free_irqs(rcount) == PSM_FAILURE) {
1177 /* not enough free irq slots available */
1178 rcount = 0;
1179 goto out;
1180 }
1181
1182 major = (dip != NULL) ? ddi_driver_major(dip) : 0;
1183 for (i = 0; i < rcount; i++) {
1184 uchar_t vector, irqno;
1185 apic_irq_t *irqptr;
1186
1187 if ((irqno = apic_allocate_irq(apic_first_avail_irq)) ==
1188 (uchar_t)-1) {
1189 /*
1190 * shouldn't happen because of the
1191 * apic_check_free_irqs() check earlier
1192 */
1193 DDI_INTR_IMPLDBG((CE_CONT, "apic_alloc_msix_vectors: "
1194 "apic_allocate_irq failed\n"));
1195 rcount = i;
1196 goto out;
1197 }
1198 if ((vector = apic_allocate_vector(pri, irqno, 1)) == 0) {
1199 /*
1200 * shouldn't happen because of the
1201 * apic_navail_vector() call earlier
1202 */
1203 DDI_INTR_IMPLDBG((CE_CONT, "apic_alloc_msix_vectors: "
1204 "apic_allocate_vector failed\n"));
1205 rcount = i;
1206 goto out;
1207 }
1208 apic_max_device_irq = max(irqno, apic_max_device_irq);
1209 apic_min_device_irq = min(irqno, apic_min_device_irq);
1210 irqptr = apic_irq_table[irqno];
1211 irqptr->airq_vector = (uchar_t)vector;
1212 irqptr->airq_ipl = pri;
1213 irqptr->airq_origirq = (uchar_t)(inum + i);
1214 irqptr->airq_share_id = 0;
1215 irqptr->airq_mps_intr_index = MSIX_INDEX;
1216 irqptr->airq_dip = dip;
1217 irqptr->airq_major = major;
1218 irqptr->airq_cpu = apic_bind_intr(dip, irqno, 0xff, 0xff);
1219 }
1220 out:
1221 mutex_exit(&airq_mutex);
1222 return (rcount);
1223 }
1224
1225 /*
1226 * Allocate a free vector for irq at ipl. Takes care of merging of multiple
1227 * IPLs into a single APIC level as well as stretching some IPLs onto multiple
1228 * levels. APIC_HI_PRI_VECTS interrupts are reserved for high priority
1229 * requests and allocated only when pri is set.
1230 */
1231 uchar_t
apic_allocate_vector(int ipl,int irq,int pri)1232 apic_allocate_vector(int ipl, int irq, int pri)
1233 {
1234 int lowest, highest, i;
1235
1236 highest = apic_ipltopri[ipl] + APIC_VECTOR_MASK;
1237 lowest = apic_ipltopri[ipl - 1] + APIC_VECTOR_PER_IPL;
1238
1239 if (highest < lowest) /* Both ipl and ipl - 1 map to same pri */
1240 lowest -= APIC_VECTOR_PER_IPL;
1241
1242 #ifdef DEBUG
1243 if (apic_restrict_vector) /* for testing shared interrupt logic */
1244 highest = lowest + apic_restrict_vector + APIC_HI_PRI_VECTS;
1245 #endif /* DEBUG */
1246 if (pri == 0)
1247 highest -= APIC_HI_PRI_VECTS;
1248
1249 for (i = lowest; i <= highest; i++) {
1250 if (APIC_CHECK_RESERVE_VECTORS(i))
1251 continue;
1252 if (apic_vector_to_irq[i] == APIC_RESV_IRQ) {
1253 apic_vector_to_irq[i] = (uchar_t)irq;
1254 return (i);
1255 }
1256 }
1257
1258 return (0);
1259 }
1260
1261 /* Mark vector as not being used by any irq */
1262 void
apic_free_vector(uchar_t vector)1263 apic_free_vector(uchar_t vector)
1264 {
1265 apic_vector_to_irq[vector] = APIC_RESV_IRQ;
1266 }
1267
1268 /*
1269 * Call rebind to do the actual programming.
1270 * Must be called with interrupts disabled and apic_ioapic_lock held
1271 * 'p' is polymorphic -- if this function is called to process a deferred
1272 * reprogramming, p is of type 'struct ioapic_reprogram_data *', from which
1273 * the irq pointer is retrieved. If not doing deferred reprogramming,
1274 * p is of the type 'apic_irq_t *'.
1275 *
1276 * apic_ioapic_lock must be held across this call, as it protects apic_rebind
1277 * and it protects apic_get_next_bind_cpu() from a race in which a CPU can be
1278 * taken offline after a cpu is selected, but before apic_rebind is called to
1279 * bind interrupts to it.
1280 */
1281 int
apic_setup_io_intr(void * p,int irq,boolean_t deferred)1282 apic_setup_io_intr(void *p, int irq, boolean_t deferred)
1283 {
1284 apic_irq_t *irqptr;
1285 struct ioapic_reprogram_data *drep = NULL;
1286 int rv;
1287
1288 if (deferred) {
1289 drep = (struct ioapic_reprogram_data *)p;
1290 ASSERT(drep != NULL);
1291 irqptr = drep->irqp;
1292 } else
1293 irqptr = (apic_irq_t *)p;
1294
1295 ASSERT(irqptr != NULL);
1296
1297 rv = apic_rebind(irqptr, apic_irq_table[irq]->airq_cpu, drep);
1298 if (rv) {
1299 /*
1300 * CPU is not up or interrupts are disabled. Fall back to
1301 * the first available CPU
1302 */
1303 rv = apic_rebind(irqptr, apic_find_cpu(APIC_CPU_INTR_ENABLE),
1304 drep);
1305 }
1306
1307 return (rv);
1308 }
1309
1310
1311 uchar_t
apic_modify_vector(uchar_t vector,int irq)1312 apic_modify_vector(uchar_t vector, int irq)
1313 {
1314 apic_vector_to_irq[vector] = (uchar_t)irq;
1315 return (vector);
1316 }
1317
1318 char *
apic_get_apic_type(void)1319 apic_get_apic_type(void)
1320 {
1321 return (apic_psm_info.p_mach_idstring);
1322 }
1323
1324 void
x2apic_update_psm(void)1325 x2apic_update_psm(void)
1326 {
1327 struct psm_ops *pops = &apic_ops;
1328
1329 ASSERT(pops != NULL);
1330
1331 pops->psm_intr_exit = x2apic_intr_exit;
1332 pops->psm_setspl = x2apic_setspl;
1333
1334 pops->psm_send_ipi = x2apic_send_ipi;
1335 send_dirintf = pops->psm_send_ipi;
1336
1337 apic_mode = LOCAL_X2APIC;
1338 apic_change_ops();
1339 }
1340