]> git.saurik.com Git - apple/xnu.git/blame - osfmk/i386/lapic.c
xnu-1456.1.26.tar.gz
[apple/xnu.git] / osfmk / i386 / lapic.c
CommitLineData
593a1d5f 1/*
b0d623f7 2 * Copyright (c) 2008-2009 Apple Inc. All rights reserved.
593a1d5f
A
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28/*
29 * @OSF_COPYRIGHT@
30 */
31
32#include <mach/mach_types.h>
33#include <mach/kern_return.h>
34
35#include <kern/kern_types.h>
36#include <kern/cpu_number.h>
37#include <kern/cpu_data.h>
38#include <kern/assert.h>
39#include <kern/machine.h>
40
41#include <vm/vm_map.h>
42#include <vm/vm_kern.h>
43
44#include <i386/lapic.h>
45#include <i386/cpuid.h>
46#include <i386/proc_reg.h>
47#include <i386/machine_cpu.h>
48#include <i386/misc_protos.h>
49#include <i386/mp.h>
50#include <i386/mtrr.h>
51#include <i386/postcode.h>
52#include <i386/cpu_threads.h>
593a1d5f 53#include <i386/machine_routines.h>
b0d623f7 54#if CONFIG_MCA
593a1d5f 55#include <i386/machine_check.h>
b0d623f7
A
56#endif
57
58#if CONFIG_COUNTERS
59#include <pmc/pmc.h>
60#endif
593a1d5f
A
61
62#if MACH_KDB
63#include <machine/db_machdep.h>
64#endif
65
66#include <sys/kdebug.h>
67
68#if MP_DEBUG
69#define PAUSE delay(1000000)
70#define DBG(x...) kprintf(x)
71#else
72#define DBG(x...)
73#define PAUSE
74#endif /* MP_DEBUG */
75
76/* Initialize lapic_id so cpu_number() works on non SMP systems */
77unsigned long lapic_id_initdata = 0;
78unsigned long lapic_id = (unsigned long)&lapic_id_initdata;
79vm_offset_t lapic_start;
80
81static i386_intr_func_t lapic_intr_func[LAPIC_FUNC_TABLE_SIZE];
82
83/* TRUE if local APIC was enabled by the OS not by the BIOS */
84static boolean_t lapic_os_enabled = FALSE;
85
c910b4d9
A
86static boolean_t lapic_errors_masked = FALSE;
87static uint64_t lapic_last_master_error = 0;
88static uint64_t lapic_error_time_threshold = 0;
89static unsigned lapic_master_error_count = 0;
90static unsigned lapic_error_count_threshold = 5;
91static boolean_t lapic_dont_panic = FALSE;
92
93extern int debug_boot_arg;
94
593a1d5f
A
95/* Base vector for local APIC interrupt sources */
96int lapic_interrupt_base = LAPIC_DEFAULT_INTERRUPT_BASE;
97
98int lapic_to_cpu[MAX_CPUS];
99int cpu_to_lapic[MAX_CPUS];
100
101static void
102lapic_cpu_map_init(void)
103{
104 int i;
105
106 for (i = 0; i < MAX_CPUS; i++) {
107 lapic_to_cpu[i] = -1;
108 cpu_to_lapic[i] = -1;
109 }
110}
111
112void
113lapic_cpu_map(int apic_id, int cpu)
114{
115 cpu_to_lapic[cpu] = apic_id;
116 lapic_to_cpu[apic_id] = cpu;
117}
118
119/*
120 * Retrieve the local apic ID a cpu.
121 *
122 * Returns the local apic ID for the given processor.
123 * If the processor does not exist or apic not configured, returns -1.
124 */
125
126uint32_t
127ml_get_apicid(uint32_t cpu)
128{
129 if(cpu >= (uint32_t)MAX_CPUS)
130 return 0xFFFFFFFF; /* Return -1 if cpu too big */
131
132 /* Return the apic ID (or -1 if not configured) */
133 return (uint32_t)cpu_to_lapic[cpu];
134
135}
136
b0d623f7
A
137uint32_t
138ml_get_cpuid(uint32_t lapic_index)
139{
140 if(lapic_index >= (uint32_t)MAX_CPUS)
141 return 0xFFFFFFFF; /* Return -1 if cpu too big */
142
143 /* Return the cpu ID (or -1 if not configured) */
144 return (uint32_t)lapic_to_cpu[lapic_index];
145
146}
147
148
593a1d5f
A
149#ifdef MP_DEBUG
150static void
151lapic_cpu_map_dump(void)
152{
153 int i;
154
155 for (i = 0; i < MAX_CPUS; i++) {
156 if (cpu_to_lapic[i] == -1)
157 continue;
158 kprintf("cpu_to_lapic[%d]: %d\n",
159 i, cpu_to_lapic[i]);
160 }
161 for (i = 0; i < MAX_CPUS; i++) {
162 if (lapic_to_cpu[i] == -1)
163 continue;
164 kprintf("lapic_to_cpu[%d]: %d\n",
165 i, lapic_to_cpu[i]);
166 }
167}
168#endif /* MP_DEBUG */
169
170void
171lapic_init(void)
172{
173 int result;
174 vm_map_entry_t entry;
175 uint32_t lo;
176 uint32_t hi;
177 boolean_t is_boot_processor;
178 boolean_t is_lapic_enabled;
179 vm_offset_t lapic_base;
180
181 /* Examine the local APIC state */
182 rdmsr(MSR_IA32_APIC_BASE, lo, hi);
183 is_boot_processor = (lo & MSR_IA32_APIC_BASE_BSP) != 0;
184 is_lapic_enabled = (lo & MSR_IA32_APIC_BASE_ENABLE) != 0;
185 lapic_base = (lo & MSR_IA32_APIC_BASE_BASE);
b0d623f7 186 kprintf("MSR_IA32_APIC_BASE %p %s %s\n", (void *) lapic_base,
593a1d5f
A
187 is_lapic_enabled ? "enabled" : "disabled",
188 is_boot_processor ? "BSP" : "AP");
189 if (!is_boot_processor || !is_lapic_enabled)
190 panic("Unexpected local APIC state\n");
191
192 /* Establish a map to the local apic */
b0d623f7 193 lapic_start = (vm_offset_t)vm_map_min(kernel_map);
593a1d5f
A
194 result = vm_map_find_space(kernel_map,
195 (vm_map_address_t *) &lapic_start,
196 round_page(LAPIC_SIZE), 0,
197 VM_MAKE_TAG(VM_MEMORY_IOKIT), &entry);
198 if (result != KERN_SUCCESS) {
199 panic("smp_init: vm_map_find_entry FAILED (err=%d)", result);
200 }
201 vm_map_unlock(kernel_map);
202/* Map in the local APIC non-cacheable, as recommended by Intel
203 * in section 8.4.1 of the "System Programming Guide".
204 */
205 pmap_enter(pmap_kernel(),
206 lapic_start,
207 (ppnum_t) i386_btop(lapic_base),
208 VM_PROT_READ|VM_PROT_WRITE,
209 VM_WIMG_IO,
210 TRUE);
211 lapic_id = (unsigned long)(lapic_start + LAPIC_ID);
212
213 if ((LAPIC_READ(VERSION)&LAPIC_VERSION_MASK) < 0x14) {
b0d623f7 214 panic("Local APIC version 0x%x, 0x14 or more expected\n",
593a1d5f
A
215 (LAPIC_READ(VERSION)&LAPIC_VERSION_MASK));
216 }
217
218 /* Set up the lapic_id <-> cpu_number map and add this boot processor */
219 lapic_cpu_map_init();
220 lapic_cpu_map((LAPIC_READ(ID)>>LAPIC_ID_SHIFT)&LAPIC_ID_MASK, 0);
221 kprintf("Boot cpu local APIC id 0x%x\n", cpu_to_lapic[0]);
222}
223
224
225static int
226lapic_esr_read(void)
227{
228 /* write-read register */
229 LAPIC_WRITE(ERROR_STATUS, 0);
230 return LAPIC_READ(ERROR_STATUS);
231}
232
233static void
234lapic_esr_clear(void)
235{
236 LAPIC_WRITE(ERROR_STATUS, 0);
237 LAPIC_WRITE(ERROR_STATUS, 0);
238}
239
240static const char *DM_str[8] = {
241 "Fixed",
242 "Lowest Priority",
243 "Invalid",
244 "Invalid",
245 "NMI",
246 "Reset",
247 "Invalid",
248 "ExtINT"};
249
250void
251lapic_dump(void)
252{
253 int i;
254
255#define BOOL(a) ((a)?' ':'!')
256#define VEC(lvt) \
257 LAPIC_READ(lvt)&LAPIC_LVT_VECTOR_MASK
258#define DS(lvt) \
259 (LAPIC_READ(lvt)&LAPIC_LVT_DS_PENDING)?" SendPending" : "Idle"
260#define DM(lvt) \
261 DM_str[(LAPIC_READ(lvt)>>LAPIC_LVT_DM_SHIFT)&LAPIC_LVT_DM_MASK]
262#define MASK(lvt) \
263 BOOL(LAPIC_READ(lvt)&LAPIC_LVT_MASKED)
264#define TM(lvt) \
265 (LAPIC_READ(lvt)&LAPIC_LVT_TM_LEVEL)? "Level" : "Edge"
266#define IP(lvt) \
267 (LAPIC_READ(lvt)&LAPIC_LVT_IP_PLRITY_LOW)? "Low " : "High"
268
b0d623f7 269 kprintf("LAPIC %d at %p version 0x%x\n",
593a1d5f 270 (LAPIC_READ(ID)>>LAPIC_ID_SHIFT)&LAPIC_ID_MASK,
b0d623f7 271 (void *) lapic_start,
593a1d5f
A
272 LAPIC_READ(VERSION)&LAPIC_VERSION_MASK);
273 kprintf("Priorities: Task 0x%x Arbitration 0x%x Processor 0x%x\n",
274 LAPIC_READ(TPR)&LAPIC_TPR_MASK,
275 LAPIC_READ(APR)&LAPIC_APR_MASK,
276 LAPIC_READ(PPR)&LAPIC_PPR_MASK);
277 kprintf("Destination Format 0x%x Logical Destination 0x%x\n",
278 LAPIC_READ(DFR)>>LAPIC_DFR_SHIFT,
279 LAPIC_READ(LDR)>>LAPIC_LDR_SHIFT);
280 kprintf("%cEnabled %cFocusChecking SV 0x%x\n",
281 BOOL(LAPIC_READ(SVR)&LAPIC_SVR_ENABLE),
282 BOOL(!(LAPIC_READ(SVR)&LAPIC_SVR_FOCUS_OFF)),
283 LAPIC_READ(SVR) & LAPIC_SVR_MASK);
b0d623f7 284#if CONFIG_MCA
c910b4d9
A
285 if (mca_is_cmci_present())
286 kprintf("LVT_CMCI: Vector 0x%02x [%s] %s %cmasked\n",
287 VEC(LVT_CMCI),
288 DM(LVT_CMCI),
289 DS(LVT_CMCI),
290 MASK(LVT_CMCI));
b0d623f7 291#endif
593a1d5f
A
292 kprintf("LVT_TIMER: Vector 0x%02x %s %cmasked %s\n",
293 VEC(LVT_TIMER),
294 DS(LVT_TIMER),
295 MASK(LVT_TIMER),
296 (LAPIC_READ(LVT_TIMER)&LAPIC_LVT_PERIODIC)?"Periodic":"OneShot");
297 kprintf(" Initial Count: 0x%08x \n", LAPIC_READ(TIMER_INITIAL_COUNT));
298 kprintf(" Current Count: 0x%08x \n", LAPIC_READ(TIMER_CURRENT_COUNT));
299 kprintf(" Divide Config: 0x%08x \n", LAPIC_READ(TIMER_DIVIDE_CONFIG));
300 kprintf("LVT_PERFCNT: Vector 0x%02x [%s] %s %cmasked\n",
301 VEC(LVT_PERFCNT),
302 DM(LVT_PERFCNT),
303 DS(LVT_PERFCNT),
304 MASK(LVT_PERFCNT));
305 kprintf("LVT_THERMAL: Vector 0x%02x [%s] %s %cmasked\n",
306 VEC(LVT_THERMAL),
307 DM(LVT_THERMAL),
308 DS(LVT_THERMAL),
309 MASK(LVT_THERMAL));
310 kprintf("LVT_LINT0: Vector 0x%02x [%s][%s][%s] %s %cmasked\n",
311 VEC(LVT_LINT0),
312 DM(LVT_LINT0),
313 TM(LVT_LINT0),
314 IP(LVT_LINT0),
315 DS(LVT_LINT0),
316 MASK(LVT_LINT0));
317 kprintf("LVT_LINT1: Vector 0x%02x [%s][%s][%s] %s %cmasked\n",
318 VEC(LVT_LINT1),
319 DM(LVT_LINT1),
320 TM(LVT_LINT1),
321 IP(LVT_LINT1),
322 DS(LVT_LINT1),
323 MASK(LVT_LINT1));
324 kprintf("LVT_ERROR: Vector 0x%02x %s %cmasked\n",
325 VEC(LVT_ERROR),
326 DS(LVT_ERROR),
327 MASK(LVT_ERROR));
328 kprintf("ESR: %08x \n", lapic_esr_read());
329 kprintf(" ");
330 for(i=0xf; i>=0; i--)
331 kprintf("%x%x%x%x",i,i,i,i);
332 kprintf("\n");
333 kprintf("TMR: 0x");
334 for(i=7; i>=0; i--)
335 kprintf("%08x",LAPIC_READ_OFFSET(TMR_BASE, i*0x10));
336 kprintf("\n");
337 kprintf("IRR: 0x");
338 for(i=7; i>=0; i--)
339 kprintf("%08x",LAPIC_READ_OFFSET(IRR_BASE, i*0x10));
340 kprintf("\n");
341 kprintf("ISR: 0x");
342 for(i=7; i >= 0; i--)
343 kprintf("%08x",LAPIC_READ_OFFSET(ISR_BASE, i*0x10));
344 kprintf("\n");
345}
346
347#if MACH_KDB
348/*
349 * Displays apic junk
350 *
351 * da
352 */
353void
354db_apic(__unused db_expr_t addr,
355 __unused int have_addr,
356 __unused db_expr_t count,
357 __unused char *modif)
358{
359
360 lapic_dump();
361
362 return;
363}
364
365#endif
366
367boolean_t
368lapic_probe(void)
369{
370 uint32_t lo;
371 uint32_t hi;
372
373 if (cpuid_features() & CPUID_FEATURE_APIC)
374 return TRUE;
375
376 if (cpuid_family() == 6 || cpuid_family() == 15) {
377 /*
378 * Mobile Pentiums:
379 * There may be a local APIC which wasn't enabled by BIOS.
380 * So we try to enable it explicitly.
381 */
382 rdmsr(MSR_IA32_APIC_BASE, lo, hi);
383 lo &= ~MSR_IA32_APIC_BASE_BASE;
384 lo |= MSR_IA32_APIC_BASE_ENABLE | LAPIC_START;
385 lo |= MSR_IA32_APIC_BASE_ENABLE;
386 wrmsr(MSR_IA32_APIC_BASE, lo, hi);
387
388 /*
389 * Re-initialize cpu features info and re-check.
390 */
391 cpuid_set_info();
392 if (cpuid_features() & CPUID_FEATURE_APIC) {
393 printf("Local APIC discovered and enabled\n");
394 lapic_os_enabled = TRUE;
395 lapic_interrupt_base = LAPIC_REDUCED_INTERRUPT_BASE;
396 return TRUE;
397 }
398 }
399
400 return FALSE;
401}
402
403void
404lapic_shutdown(void)
405{
406 uint32_t lo;
407 uint32_t hi;
408 uint32_t value;
409
410 /* Shutdown if local APIC was enabled by OS */
411 if (lapic_os_enabled == FALSE)
412 return;
413
414 mp_disable_preemption();
415
416 /* ExtINT: masked */
417 if (get_cpu_number() == master_cpu) {
418 value = LAPIC_READ(LVT_LINT0);
419 value |= LAPIC_LVT_MASKED;
420 LAPIC_WRITE(LVT_LINT0, value);
421 }
422
c910b4d9
A
423 /* Error: masked */
424 LAPIC_WRITE(LVT_ERROR, LAPIC_READ(LVT_ERROR) | LAPIC_LVT_MASKED);
425
593a1d5f
A
426 /* Timer: masked */
427 LAPIC_WRITE(LVT_TIMER, LAPIC_READ(LVT_TIMER) | LAPIC_LVT_MASKED);
428
429 /* Perfmon: masked */
430 LAPIC_WRITE(LVT_PERFCNT, LAPIC_READ(LVT_PERFCNT) | LAPIC_LVT_MASKED);
431
593a1d5f
A
432 /* APIC software disabled */
433 LAPIC_WRITE(SVR, LAPIC_READ(SVR) & ~LAPIC_SVR_ENABLE);
434
435 /* Bypass the APIC completely and update cpu features */
436 rdmsr(MSR_IA32_APIC_BASE, lo, hi);
437 lo &= ~MSR_IA32_APIC_BASE_ENABLE;
438 wrmsr(MSR_IA32_APIC_BASE, lo, hi);
439 cpuid_set_info();
440
441 mp_enable_preemption();
442}
443
444void
445lapic_configure(void)
446{
447 int value;
448
c910b4d9
A
449 if (lapic_error_time_threshold == 0 && cpu_number() == 0) {
450 nanoseconds_to_absolutetime(NSEC_PER_SEC >> 2, &lapic_error_time_threshold);
451 if (!PE_parse_boot_argn("lapic_dont_panic", &lapic_dont_panic, sizeof(lapic_dont_panic))) {
452 lapic_dont_panic = FALSE;
453 }
454 }
455
593a1d5f
A
456 /* Set flat delivery model, logical processor id */
457 LAPIC_WRITE(DFR, LAPIC_DFR_FLAT);
458 LAPIC_WRITE(LDR, (get_cpu_number()) << LAPIC_LDR_SHIFT);
459
460 /* Accept all */
461 LAPIC_WRITE(TPR, 0);
462
463 LAPIC_WRITE(SVR, LAPIC_VECTOR(SPURIOUS) | LAPIC_SVR_ENABLE);
464
465 /* ExtINT */
466 if (get_cpu_number() == master_cpu) {
467 value = LAPIC_READ(LVT_LINT0);
468 value &= ~LAPIC_LVT_MASKED;
469 value |= LAPIC_LVT_DM_EXTINT;
470 LAPIC_WRITE(LVT_LINT0, value);
471 }
472
473 /* Timer: unmasked, one-shot */
474 LAPIC_WRITE(LVT_TIMER, LAPIC_VECTOR(TIMER));
475
476 /* Perfmon: unmasked */
477 LAPIC_WRITE(LVT_PERFCNT, LAPIC_VECTOR(PERFCNT));
478
479 /* Thermal: unmasked */
480 LAPIC_WRITE(LVT_THERMAL, LAPIC_VECTOR(THERMAL));
481
b0d623f7 482#if CONFIG_MCA
c910b4d9
A
483 /* CMCI, if available */
484 if (mca_is_cmci_present())
485 LAPIC_WRITE(LVT_CMCI, LAPIC_VECTOR(CMCI));
b0d623f7 486#endif
593a1d5f 487
c910b4d9
A
488 if (((cpu_number() == master_cpu) && lapic_errors_masked == FALSE) ||
489 (cpu_number() != master_cpu)) {
490 lapic_esr_clear();
491 LAPIC_WRITE(LVT_ERROR, LAPIC_VECTOR(ERROR));
492 }
593a1d5f
A
493}
494
495void
496lapic_set_timer(
b0d623f7 497 boolean_t interrupt_unmasked,
593a1d5f
A
498 lapic_timer_mode_t mode,
499 lapic_timer_divide_t divisor,
500 lapic_timer_count_t initial_count)
501{
502 boolean_t state;
503 uint32_t timer_vector;
504
505 state = ml_set_interrupts_enabled(FALSE);
506 timer_vector = LAPIC_READ(LVT_TIMER);
507 timer_vector &= ~(LAPIC_LVT_MASKED|LAPIC_LVT_PERIODIC);;
b0d623f7 508 timer_vector |= interrupt_unmasked ? 0 : LAPIC_LVT_MASKED;
593a1d5f
A
509 timer_vector |= (mode == periodic) ? LAPIC_LVT_PERIODIC : 0;
510 LAPIC_WRITE(LVT_TIMER, timer_vector);
511 LAPIC_WRITE(TIMER_DIVIDE_CONFIG, divisor);
512 LAPIC_WRITE(TIMER_INITIAL_COUNT, initial_count);
513 ml_set_interrupts_enabled(state);
514}
515
516void
517lapic_get_timer(
518 lapic_timer_mode_t *mode,
519 lapic_timer_divide_t *divisor,
520 lapic_timer_count_t *initial_count,
521 lapic_timer_count_t *current_count)
522{
523 boolean_t state;
524
525 state = ml_set_interrupts_enabled(FALSE);
526 if (mode)
527 *mode = (LAPIC_READ(LVT_TIMER) & LAPIC_LVT_PERIODIC) ?
528 periodic : one_shot;
529 if (divisor)
530 *divisor = LAPIC_READ(TIMER_DIVIDE_CONFIG) & LAPIC_TIMER_DIVIDE_MASK;
531 if (initial_count)
532 *initial_count = LAPIC_READ(TIMER_INITIAL_COUNT);
533 if (current_count)
534 *current_count = LAPIC_READ(TIMER_CURRENT_COUNT);
535 ml_set_interrupts_enabled(state);
536}
537
538static inline void
539_lapic_end_of_interrupt(void)
540{
541 LAPIC_WRITE(EOI, 0);
542}
543
544void
545lapic_end_of_interrupt(void)
546{
547 _lapic_end_of_interrupt();
548}
549
b0d623f7
A
550void lapic_unmask_perfcnt_interrupt(void) {
551 LAPIC_WRITE(LVT_PERFCNT, LAPIC_VECTOR(PERFCNT));
552}
553
593a1d5f
A
554void
555lapic_set_intr_func(int vector, i386_intr_func_t func)
556{
557 if (vector > lapic_interrupt_base)
558 vector -= lapic_interrupt_base;
559
560 switch (vector) {
561 case LAPIC_NMI_INTERRUPT:
562 case LAPIC_INTERPROCESSOR_INTERRUPT:
563 case LAPIC_TIMER_INTERRUPT:
564 case LAPIC_THERMAL_INTERRUPT:
565 case LAPIC_PERFCNT_INTERRUPT:
c910b4d9 566 case LAPIC_CMCI_INTERRUPT:
b0d623f7 567 case LAPIC_PM_INTERRUPT:
593a1d5f
A
568 lapic_intr_func[vector] = func;
569 break;
570 default:
571 panic("lapic_set_intr_func(%d,%p) invalid vector\n",
572 vector, func);
573 }
574}
575
576int
b0d623f7 577lapic_interrupt(int interrupt_num, x86_saved_state_t *state)
593a1d5f
A
578{
579 int retval = 0;
c910b4d9 580 int esr = -1;
593a1d5f 581
b0d623f7
A
582 interrupt_num -= lapic_interrupt_base;
583 if (interrupt_num < 0) {
584 if (interrupt_num == (LAPIC_NMI_INTERRUPT - lapic_interrupt_base) &&
593a1d5f
A
585 lapic_intr_func[LAPIC_NMI_INTERRUPT] != NULL) {
586 retval = (*lapic_intr_func[LAPIC_NMI_INTERRUPT])(state);
587 _lapic_end_of_interrupt();
588 return retval;
589 }
590 else
591 return 0;
592 }
593
b0d623f7 594 switch(interrupt_num) {
593a1d5f
A
595 case LAPIC_TIMER_INTERRUPT:
596 case LAPIC_THERMAL_INTERRUPT:
597 case LAPIC_INTERPROCESSOR_INTERRUPT:
b0d623f7
A
598 case LAPIC_PM_INTERRUPT:
599 if (lapic_intr_func[interrupt_num] != NULL)
600 (void) (*lapic_intr_func[interrupt_num])(state);
593a1d5f
A
601 _lapic_end_of_interrupt();
602 retval = 1;
603 break;
b0d623f7
A
604 case LAPIC_PERFCNT_INTERRUPT:
605 /* If a function has been registered, invoke it. Otherwise,
606 * pass up to IOKit.
607 */
608 if (lapic_intr_func[interrupt_num] != NULL) {
609 (void) (*lapic_intr_func[interrupt_num])(state);
610 /* Unmask the interrupt since we don't expect legacy users
611 * to be responsible for it.
612 */
613 lapic_unmask_perfcnt_interrupt();
614 _lapic_end_of_interrupt();
615 retval = 1;
616 }
617 break;
c910b4d9 618 case LAPIC_CMCI_INTERRUPT:
b0d623f7
A
619 if (lapic_intr_func[interrupt_num] != NULL)
620 (void) (*lapic_intr_func[interrupt_num])(state);
c910b4d9
A
621 /* return 0 for plaform expert to handle */
622 break;
593a1d5f 623 case LAPIC_ERROR_INTERRUPT:
c910b4d9
A
624 /* We treat error interrupts on APs as fatal.
625 * The current interrupt steering scheme directs most
626 * external interrupts to the BSP (HPET interrupts being
627 * a notable exception); hence, such an error
628 * on an AP may signify LVT corruption (with "may" being
629 * the operative word). On the BSP, we adopt a more
630 * lenient approach, in the interests of enhancing
631 * debuggability and reducing fragility.
632 * If "lapic_error_count_threshold" error interrupts
633 * occur within "lapic_error_time_threshold" absolute
634 * time units, we mask the error vector and log. The
635 * error interrupts themselves are likely
636 * side effects of issues which are beyond the purview of
637 * the local APIC interrupt handler, however. The Error
638 * Status Register value (the illegal destination
639 * vector code is one observed in practice) indicates
640 * the immediate cause of the error.
641 */
642 esr = lapic_esr_read();
593a1d5f 643 lapic_dump();
c910b4d9
A
644
645 if ((debug_boot_arg && (lapic_dont_panic == FALSE)) ||
646 cpu_number() != master_cpu) {
647 panic("Local APIC error, ESR: %d\n", esr);
648 }
649
650 if (cpu_number() == master_cpu) {
651 uint64_t abstime = mach_absolute_time();
652 if ((abstime - lapic_last_master_error) < lapic_error_time_threshold) {
653 if (lapic_master_error_count++ > lapic_error_count_threshold) {
654 lapic_errors_masked = TRUE;
655 LAPIC_WRITE(LVT_ERROR, LAPIC_READ(LVT_ERROR) | LAPIC_LVT_MASKED);
656 printf("Local APIC: errors masked\n");
657 }
658 }
659 else {
660 lapic_last_master_error = abstime;
661 lapic_master_error_count = 0;
662 }
663 printf("Local APIC error on master CPU, ESR: %d, error count this run: %d\n", esr, lapic_master_error_count);
664 }
665
593a1d5f
A
666 _lapic_end_of_interrupt();
667 retval = 1;
668 break;
669 case LAPIC_SPURIOUS_INTERRUPT:
670 kprintf("SPIV\n");
671 /* No EOI required here */
672 retval = 1;
673 break;
b0d623f7
A
674 case LAPIC_PMC_SW_INTERRUPT:
675 {
676#if CONFIG_COUNTERS
677 thread_t old, new;
678 ml_get_csw_threads(&old, &new);
679
680 if (pmc_context_switch(old, new) == TRUE) {
681 retval = 1;
682 /* No EOI required for SWI */
683 }
684#endif /* CONFIG_COUNTERS */
685 }
686 break;
593a1d5f
A
687 }
688
689 return retval;
690}
691
692void
693lapic_smm_restore(void)
694{
695 boolean_t state;
696
697 if (lapic_os_enabled == FALSE)
698 return;
699
700 state = ml_set_interrupts_enabled(FALSE);
701
702 if (LAPIC_ISR_IS_SET(LAPIC_REDUCED_INTERRUPT_BASE, TIMER)) {
703 /*
704 * Bogus SMI handler enables interrupts but does not know about
705 * local APIC interrupt sources. When APIC timer counts down to
706 * zero while in SMM, local APIC will end up waiting for an EOI
707 * but no interrupt was delivered to the OS.
708 */
709 _lapic_end_of_interrupt();
710
711 /*
712 * timer is one-shot, trigger another quick countdown to trigger
713 * another timer interrupt.
714 */
715 if (LAPIC_READ(TIMER_CURRENT_COUNT) == 0) {
716 LAPIC_WRITE(TIMER_INITIAL_COUNT, 1);
717 }
718
719 kprintf("lapic_smm_restore\n");
720 }
721
722 ml_set_interrupts_enabled(state);
723}
724
b0d623f7
A
725void
726lapic_send_ipi(int cpu, int vector)
727{
728 boolean_t state;
729
730 if (vector < lapic_interrupt_base)
731 vector += lapic_interrupt_base;
732
733 state = ml_set_interrupts_enabled(FALSE);
734
735 /* Wait for pending outgoing send to complete */
736 while (LAPIC_READ(ICR) & LAPIC_ICR_DS_PENDING) {
737 cpu_pause();
738 }
739
740 LAPIC_WRITE(ICRD, cpu_to_lapic[cpu] << LAPIC_ICRD_DEST_SHIFT);
741 LAPIC_WRITE(ICR, vector | LAPIC_ICR_DM_FIXED);
742
743 (void) ml_set_interrupts_enabled(state);
744}