]>
Commit | Line | Data |
---|---|---|
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 */ | |
77 | unsigned long lapic_id_initdata = 0; | |
78 | unsigned long lapic_id = (unsigned long)&lapic_id_initdata; | |
79 | vm_offset_t lapic_start; | |
80 | ||
81 | static 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 */ | |
84 | static boolean_t lapic_os_enabled = FALSE; | |
85 | ||
c910b4d9 A |
86 | static boolean_t lapic_errors_masked = FALSE; |
87 | static uint64_t lapic_last_master_error = 0; | |
88 | static uint64_t lapic_error_time_threshold = 0; | |
89 | static unsigned lapic_master_error_count = 0; | |
90 | static unsigned lapic_error_count_threshold = 5; | |
91 | static boolean_t lapic_dont_panic = FALSE; | |
92 | ||
93 | extern int debug_boot_arg; | |
94 | ||
593a1d5f A |
95 | /* Base vector for local APIC interrupt sources */ |
96 | int lapic_interrupt_base = LAPIC_DEFAULT_INTERRUPT_BASE; | |
97 | ||
98 | int lapic_to_cpu[MAX_CPUS]; | |
99 | int cpu_to_lapic[MAX_CPUS]; | |
100 | ||
101 | static void | |
102 | lapic_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 | ||
112 | void | |
113 | lapic_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 | ||
126 | uint32_t | |
127 | ml_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 |
137 | uint32_t |
138 | ml_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 |
150 | static void | |
151 | lapic_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 | ||
170 | void | |
171 | lapic_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 | ||
225 | static int | |
226 | lapic_esr_read(void) | |
227 | { | |
228 | /* write-read register */ | |
229 | LAPIC_WRITE(ERROR_STATUS, 0); | |
230 | return LAPIC_READ(ERROR_STATUS); | |
231 | } | |
232 | ||
233 | static void | |
234 | lapic_esr_clear(void) | |
235 | { | |
236 | LAPIC_WRITE(ERROR_STATUS, 0); | |
237 | LAPIC_WRITE(ERROR_STATUS, 0); | |
238 | } | |
239 | ||
240 | static const char *DM_str[8] = { | |
241 | "Fixed", | |
242 | "Lowest Priority", | |
243 | "Invalid", | |
244 | "Invalid", | |
245 | "NMI", | |
246 | "Reset", | |
247 | "Invalid", | |
248 | "ExtINT"}; | |
249 | ||
250 | void | |
251 | lapic_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 | */ | |
353 | void | |
354 | db_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 | ||
367 | boolean_t | |
368 | lapic_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 | ||
403 | void | |
404 | lapic_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 | ||
444 | void | |
445 | lapic_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 | ||
495 | void | |
496 | lapic_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 | ||
516 | void | |
517 | lapic_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 | ||
538 | static inline void | |
539 | _lapic_end_of_interrupt(void) | |
540 | { | |
541 | LAPIC_WRITE(EOI, 0); | |
542 | } | |
543 | ||
544 | void | |
545 | lapic_end_of_interrupt(void) | |
546 | { | |
547 | _lapic_end_of_interrupt(); | |
548 | } | |
549 | ||
b0d623f7 A |
550 | void lapic_unmask_perfcnt_interrupt(void) { |
551 | LAPIC_WRITE(LVT_PERFCNT, LAPIC_VECTOR(PERFCNT)); | |
552 | } | |
553 | ||
593a1d5f A |
554 | void |
555 | lapic_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 | ||
576 | int | |
b0d623f7 | 577 | lapic_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 | ||
692 | void | |
693 | lapic_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 |
725 | void |
726 | lapic_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 | } |