FreeBSD/Linux Kernel Cross Reference
sys/arch/x86/x86/lapic.c
1 /* $NetBSD: lapic.c,v 1.42 2008/07/03 14:02:25 drochner Exp $ */
2
3 /*-
4 * Copyright (c) 2000, 2008 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by RedBack Networks Inc.
9 *
10 * Author: Bill Sommerfeld
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
25 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
32 */
33
34 #include <sys/cdefs.h>
35 __KERNEL_RCSID(0, "$NetBSD: lapic.c,v 1.42 2008/07/03 14:02:25 drochner Exp $");
36
37 #include "opt_ddb.h"
38 #include "opt_mpbios.h" /* for MPDEBUG */
39 #include "opt_multiprocessor.h"
40 #include "opt_ntp.h"
41
42 #include <sys/param.h>
43 #include <sys/proc.h>
44 #include <sys/user.h>
45 #include <sys/systm.h>
46 #include <sys/device.h>
47 #include <sys/timetc.h>
48
49 #include <uvm/uvm_extern.h>
50
51 #include <dev/ic/i8253reg.h>
52
53 #include <machine/cpu.h>
54 #include <machine/cpu_counter.h>
55 #include <machine/cpufunc.h>
56 #include <machine/cpuvar.h>
57 #include <machine/pmap.h>
58 #include <machine/vmparam.h>
59 #include <machine/mpbiosvar.h>
60 #include <machine/pcb.h>
61 #include <machine/specialreg.h>
62 #include <machine/segments.h>
63 #include <x86/x86/tsc.h>
64 #include <x86/i82093var.h>
65
66 #include <machine/apicvar.h>
67 #include <machine/i82489reg.h>
68 #include <machine/i82489var.h>
69
70 /* Referenced from vector.S */
71 void lapic_clockintr(void *, struct intrframe *);
72
73 static void lapic_delay(unsigned int);
74 static uint32_t lapic_gettick(void);
75 static void lapic_map(paddr_t);
76
77 static void lapic_hwmask(struct pic *, int);
78 static void lapic_hwunmask(struct pic *, int);
79 static void lapic_setup(struct pic *, struct cpu_info *, int, int, int);
80
81 struct pic local_pic = {
82 .pic_name = "lapic",
83 .pic_type = PIC_LAPIC,
84 .pic_lock = __SIMPLELOCK_UNLOCKED,
85 .pic_hwmask = lapic_hwmask,
86 .pic_hwunmask = lapic_hwunmask,
87 .pic_addroute =lapic_setup,
88 .pic_delroute = lapic_setup,
89 };
90
91 static void
92 lapic_map(paddr_t lapic_base)
93 {
94 int s;
95 pt_entry_t *pte;
96 vaddr_t va = (vaddr_t)&local_apic;
97
98 /*
99 * If the CPU has an APIC MSR, use it and ignore the supplied value:
100 * some ACPI implementations have been observed to pass bad values.
101 * Additionally, ensure that the lapic is enabled as we are committed
102 * to using it at this point. Be conservative and assume that the MSR
103 * is not present on the Pentium (is it?).
104 */
105 if (CPUID2FAMILY(curcpu()->ci_signature) >= 6) {
106 lapic_base = (paddr_t)rdmsr(LAPIC_MSR);
107 if ((lapic_base & LAPIC_MSR_ADDR) == 0) {
108 lapic_base |= LAPIC_BASE;
109 }
110 wrmsr(LAPIC_MSR, lapic_base | LAPIC_MSR_ENABLE);
111 lapic_base &= LAPIC_MSR_ADDR;
112 }
113
114 x86_disable_intr();
115 s = lapic_tpr;
116
117 /*
118 * Map local apic. If we have a local apic, it's safe to assume
119 * we're on a 486 or better and can use invlpg and non-cacheable PTE's
120 *
121 * Whap the PTE "by hand" rather than calling pmap_kenter_pa because
122 * the latter will attempt to invoke TLB shootdown code just as we
123 * might have changed the value of cpu_number()..
124 */
125
126 pte = kvtopte(va);
127 *pte = lapic_base | PG_RW | PG_V | PG_N | pmap_pg_g;
128 invlpg(va);
129
130 #ifdef MULTIPROCESSOR
131 cpu_init_first(); /* catch up to changed cpu_number() */
132 #endif
133
134 lapic_tpr = s;
135 x86_enable_intr();
136 }
137
138 /*
139 * enable local apic
140 */
141 void
142 lapic_enable(void)
143 {
144 i82489_writereg(LAPIC_SVR, LAPIC_SVR_ENABLE | LAPIC_SPURIOUS_VECTOR);
145 }
146
147 void
148 lapic_suspend(void)
149 {
150 }
151
152 void
153 lapic_set_lvt(void)
154 {
155 struct cpu_info *ci = curcpu();
156 int i;
157 struct mp_intr_map *mpi;
158 uint32_t lint0, lint1;
159
160 #ifdef MULTIPROCESSOR
161 if (mp_verbose) {
162 apic_format_redir (device_xname(ci->ci_dev), "prelint", 0, 0,
163 i82489_readreg(LAPIC_LVINT0));
164 apic_format_redir (device_xname(ci->ci_dev), "prelint", 1, 0,
165 i82489_readreg(LAPIC_LVINT1));
166 }
167 #endif
168
169 /*
170 * If an I/O APIC has been attached, assume that it is used instead of
171 * the 8259A for interrupt delivery. Otherwise request the LAPIC to
172 * get external interrupts via LINT0 for the primary CPU.
173 */
174 lint0 = LAPIC_DLMODE_EXTINT;
175 if (nioapics > 0 || !CPU_IS_PRIMARY(curcpu()))
176 lint0 |= LAPIC_LVT_MASKED;
177 i82489_writereg(LAPIC_LVINT0, lint0);
178
179 /*
180 * Non Maskable Interrupts are to be delivered to the primary CPU.
181 */
182 lint1 = LAPIC_DLMODE_NMI;
183 if (!CPU_IS_PRIMARY(curcpu()))
184 lint1 |= LAPIC_LVT_MASKED;
185 i82489_writereg(LAPIC_LVINT1, lint1);
186
187 for (i = 0; i < mp_nintr; i++) {
188 mpi = &mp_intrs[i];
189 if (mpi->ioapic == NULL && (mpi->cpu_id == MPS_ALL_APICS ||
190 mpi->cpu_id == ci->ci_cpuid)) {
191 #ifdef DIAGNOSTIC
192 if (mpi->ioapic_pin > 1)
193 panic("lapic_set_lvt: bad pin value %d",
194 mpi->ioapic_pin);
195 #endif
196 if (mpi->ioapic_pin == 0)
197 i82489_writereg(LAPIC_LVINT0, mpi->redir);
198 else
199 i82489_writereg(LAPIC_LVINT1, mpi->redir);
200 }
201 }
202
203 #ifdef MULTIPROCESSOR
204 if (mp_verbose) {
205 apic_format_redir (device_xname(ci->ci_dev), "timer", 0, 0,
206 i82489_readreg(LAPIC_LVTT));
207 apic_format_redir (device_xname(ci->ci_dev), "pcint", 0, 0,
208 i82489_readreg(LAPIC_PCINT));
209 apic_format_redir (device_xname(ci->ci_dev), "lint", 0, 0,
210 i82489_readreg(LAPIC_LVINT0));
211 apic_format_redir (device_xname(ci->ci_dev), "lint", 1, 0,
212 i82489_readreg(LAPIC_LVINT1));
213 apic_format_redir (device_xname(ci->ci_dev), "err", 0, 0,
214 i82489_readreg(LAPIC_LVERR));
215 }
216 #endif
217 }
218
219 /*
220 * Initialize fixed idt vectors for use by local apic.
221 */
222 void
223 lapic_boot_init(paddr_t lapic_base)
224 {
225 lapic_map(lapic_base);
226
227 #ifdef MULTIPROCESSOR
228 idt_vec_reserve(LAPIC_IPI_VECTOR);
229 idt_vec_set(LAPIC_IPI_VECTOR, Xintr_lapic_ipi);
230 idt_vec_reserve(LAPIC_TLB_MCAST_VECTOR);
231 idt_vec_set(LAPIC_TLB_MCAST_VECTOR, Xintr_lapic_tlb_mcast);
232 idt_vec_reserve(LAPIC_TLB_BCAST_VECTOR);
233 idt_vec_set(LAPIC_TLB_BCAST_VECTOR, Xintr_lapic_tlb_bcast);
234 #endif
235 idt_vec_reserve(LAPIC_SPURIOUS_VECTOR);
236 idt_vec_set(LAPIC_SPURIOUS_VECTOR, Xintrspurious);
237
238 idt_vec_reserve(LAPIC_TIMER_VECTOR);
239 idt_vec_set(LAPIC_TIMER_VECTOR, Xintr_lapic_ltimer);
240 }
241
242 static uint32_t
243 lapic_gettick(void)
244 {
245 return i82489_readreg(LAPIC_CCR_TIMER);
246 }
247
248 #include <sys/kernel.h> /* for hz */
249
250 int lapic_timer = 0;
251 uint32_t lapic_tval;
252
253 /*
254 * this gets us up to a 4GHz busclock....
255 */
256 uint32_t lapic_per_second;
257 uint32_t lapic_frac_usec_per_cycle;
258 uint64_t lapic_frac_cycle_per_usec;
259 uint32_t lapic_delaytab[26];
260
261 static u_int
262 lapic_get_timecount(struct timecounter *tc)
263 {
264 struct cpu_info *ci;
265 uint32_t cur_timer;
266 int s;
267
268 s = splhigh();
269 ci = curcpu();
270
271 /*
272 * Check for a race against the clockinterrupt.
273 * The update of ci_lapic_counter is blocked by splhigh() and
274 * the check for a pending clockinterrupt compensates for that.
275 *
276 * If the current tick is almost the Initial Counter, explicitly
277 * check for the pending interrupt bit as the interrupt delivery
278 * could be asynchronious and compensate as well.
279 *
280 * This can't be done without splhigh() as the calling code might
281 * have masked the clockinterrupt already.
282 *
283 * This code assumes that clockinterrupts are not missed.
284 */
285 cur_timer = lapic_gettick();
286 if (cur_timer >= lapic_tval - 1) {
287 uint16_t reg = LAPIC_IRR + LAPIC_TIMER_VECTOR / 32 * 16;
288
289 if (i82489_readreg(reg) & (1 << (LAPIC_TIMER_VECTOR % 32))) {
290 cur_timer -= lapic_tval;
291 }
292 } else if (ci->ci_istate.ipending & (1 << LIR_TIMER))
293 cur_timer = lapic_gettick() - lapic_tval;
294 cur_timer = ci->ci_lapic_counter - cur_timer;
295 splx(s);
296
297 return cur_timer;
298 }
299
300 static struct timecounter lapic_timecounter = {
301 lapic_get_timecount,
302 NULL,
303 ~0u,
304 0,
305 "lapic",
306 #ifndef MULTIPROCESSOR
307 2100,
308 #else
309 -100, /* per CPU state */
310 #endif
311 NULL,
312 NULL,
313 };
314
315 extern u_int i8254_get_timecount(struct timecounter *);
316
317 void
318 lapic_clockintr(void *arg, struct intrframe *frame)
319 {
320 struct cpu_info *ci = curcpu();
321
322 ci->ci_lapic_counter += lapic_tval;
323 ci->ci_isources[LIR_TIMER]->is_evcnt.ev_count++;
324 hardclock((struct clockframe *)frame);
325 }
326
327 void
328 lapic_initclocks(void)
329 {
330 /*
331 * Start local apic countdown timer running, in repeated mode.
332 *
333 * Mask the clock interrupt and set mode,
334 * then set divisor,
335 * then unmask and set the vector.
336 */
337 i82489_writereg (LAPIC_LVTT, LAPIC_LVTT_TM|LAPIC_LVTT_M);
338 i82489_writereg (LAPIC_DCR_TIMER, LAPIC_DCRT_DIV1);
339 i82489_writereg (LAPIC_ICR_TIMER, lapic_tval);
340 i82489_writereg (LAPIC_LVTT, LAPIC_LVTT_TM|LAPIC_TIMER_VECTOR);
341 i82489_writereg (LAPIC_EOI, 0);
342 }
343
344 extern unsigned int gettick(void); /* XXX put in header file */
345 extern int rtclock_tval; /* XXX put in header file */
346 extern void (*initclock_func)(void); /* XXX put in header file */
347
348 /*
349 * Calibrate the local apic count-down timer (which is running at
350 * bus-clock speed) vs. the i8254 counter/timer (which is running at
351 * a fixed rate).
352 *
353 * The Intel MP spec says: "An MP operating system may use the IRQ8
354 * real-time clock as a reference to determine the actual APIC timer clock
355 * speed."
356 *
357 * We're actually using the IRQ0 timer. Hmm.
358 */
359 void
360 lapic_calibrate_timer(struct cpu_info *ci)
361 {
362 unsigned int seen, delta, initial_i8254, initial_lapic;
363 unsigned int cur_i8254, cur_lapic;
364 uint64_t tmp;
365 int i;
366 char tbuf[9];
367
368 aprint_debug_dev(ci->ci_dev, "calibrating local timer\n");
369
370 /*
371 * Configure timer to one-shot, interrupt masked,
372 * large positive number.
373 */
374 i82489_writereg (LAPIC_LVTT, LAPIC_LVTT_M);
375 i82489_writereg (LAPIC_DCR_TIMER, LAPIC_DCRT_DIV1);
376 i82489_writereg (LAPIC_ICR_TIMER, 0x80000000);
377
378 x86_disable_intr();
379
380 initial_lapic = lapic_gettick();
381 initial_i8254 = gettick();
382
383 for (seen = 0; seen < TIMER_FREQ / 100; seen += delta) {
384 cur_i8254 = gettick();
385 if (cur_i8254 > initial_i8254)
386 delta = rtclock_tval - (cur_i8254 - initial_i8254);
387 else
388 delta = initial_i8254 - cur_i8254;
389 initial_i8254 = cur_i8254;
390 }
391 cur_lapic = lapic_gettick();
392
393 x86_enable_intr();
394
395 tmp = initial_lapic - cur_lapic;
396 lapic_per_second = (tmp * TIMER_FREQ + seen / 2) / seen;
397
398 humanize_number(tbuf, sizeof(tbuf), lapic_per_second, "Hz", 1000);
399
400 aprint_debug_dev(ci->ci_dev, "apic clock running at %s\n", tbuf);
401
402 if (lapic_per_second != 0) {
403 /*
404 * reprogram the apic timer to run in periodic mode.
405 * XXX need to program timer on other CPUs, too.
406 */
407 lapic_tval = (lapic_per_second * 2) / hz;
408 lapic_tval = (lapic_tval / 2) + (lapic_tval & 0x1);
409
410 i82489_writereg (LAPIC_LVTT, LAPIC_LVTT_TM|LAPIC_LVTT_M
411 |LAPIC_TIMER_VECTOR);
412 i82489_writereg (LAPIC_DCR_TIMER, LAPIC_DCRT_DIV1);
413 i82489_writereg (LAPIC_ICR_TIMER, lapic_tval);
414
415 /*
416 * Compute fixed-point ratios between cycles and
417 * microseconds to avoid having to do any division
418 * in lapic_delay.
419 */
420
421 tmp = (1000000 * (uint64_t)1<<32) / lapic_per_second;
422 lapic_frac_usec_per_cycle = tmp;
423
424 tmp = (lapic_per_second * (uint64_t)1<<32) / 1000000;
425
426 lapic_frac_cycle_per_usec = tmp;
427
428 /*
429 * Compute delay in cycles for likely short delays in usec.
430 */
431 for (i=0; i<26; i++)
432 lapic_delaytab[i] = (lapic_frac_cycle_per_usec * i) >>
433 32;
434
435 /*
436 * Now that the timer's calibrated, use the apic timer routines
437 * for all our timing needs..
438 */
439 delay_func = lapic_delay;
440 initclock_func = lapic_initclocks;
441 initrtclock(0);
442
443 if (lapic_timecounter.tc_frequency == 0) {
444 /*
445 * Hook up time counter.
446 * This assume that all LAPICs have the same frequency.
447 */
448 lapic_timecounter.tc_frequency = lapic_per_second;
449 tc_init(&lapic_timecounter);
450 }
451 }
452 }
453
454 /*
455 * delay for N usec.
456 */
457
458 static void
459 lapic_delay(unsigned int usec)
460 {
461 int32_t xtick, otick;
462 int64_t deltat; /* XXX may want to be 64bit */
463
464 otick = lapic_gettick();
465
466 if (usec <= 0)
467 return;
468 if (usec <= 25)
469 deltat = lapic_delaytab[usec];
470 else
471 deltat = (lapic_frac_cycle_per_usec * usec) >> 32;
472
473 while (deltat > 0) {
474 xtick = lapic_gettick();
475 if (xtick > otick)
476 deltat -= lapic_tval - (xtick - otick);
477 else
478 deltat -= otick - xtick;
479 otick = xtick;
480
481 x86_pause();
482 }
483 }
484
485 /*
486 * XXX the following belong mostly or partly elsewhere..
487 */
488
489 static void
490 i82489_icr_wait(void)
491 {
492 #ifdef DIAGNOSTIC
493 unsigned j = 100000;
494 #endif /* DIAGNOSTIC */
495
496 while ((i82489_readreg(LAPIC_ICRLO) & LAPIC_DLSTAT_BUSY) != 0) {
497 x86_pause();
498 #ifdef DIAGNOSTIC
499 j--;
500 if (j == 0)
501 panic("i82489_icr_wait: busy");
502 #endif /* DIAGNOSTIC */
503 }
504 }
505
506 int
507 x86_ipi_init(int target)
508 {
509 uint32_t esr;
510
511 i82489_writereg(LAPIC_ESR, 0);
512 (void)i82489_readreg(LAPIC_ESR);
513
514 if ((target&LAPIC_DEST_MASK)==0) {
515 i82489_writereg(LAPIC_ICRHI, target<<LAPIC_ID_SHIFT);
516 }
517 i82489_writereg(LAPIC_ICRLO, (target & LAPIC_DEST_MASK) |
518 LAPIC_DLMODE_INIT | LAPIC_LEVEL_ASSERT );
519 i82489_icr_wait();
520 i8254_delay(10000);
521 i82489_writereg(LAPIC_ICRLO, (target & LAPIC_DEST_MASK) |
522 LAPIC_DLMODE_INIT | LAPIC_TRIGGER_LEVEL | LAPIC_LEVEL_DEASSERT);
523 i82489_icr_wait();
524
525 if ((i82489_readreg(LAPIC_ICRLO) & LAPIC_DLSTAT_BUSY) != 0) {
526 return EBUSY;
527 }
528 esr = i82489_readreg(LAPIC_ESR);
529 if (esr != 0) {
530 aprint_debug("x86_ipi_init: ESR %08x\n", esr);
531 }
532
533 return 0;
534 }
535
536 int
537 x86_ipi_startup(int target, int vec)
538 {
539 uint32_t esr;
540
541 i82489_writereg(LAPIC_ESR, 0);
542 (void)i82489_readreg(LAPIC_ESR);
543
544 i82489_icr_wait();
545 i82489_writereg(LAPIC_ICRHI, target << LAPIC_ID_SHIFT);
546 i82489_writereg(LAPIC_ICRLO, vec | LAPIC_DLMODE_STARTUP |
547 LAPIC_LEVEL_ASSERT);
548 i82489_icr_wait();
549
550 if ((i82489_readreg(LAPIC_ICRLO) & LAPIC_DLSTAT_BUSY) != 0) {
551 return EBUSY;
552 }
553 esr = i82489_readreg(LAPIC_ESR);
554 if (esr != 0) {
555 aprint_debug("x86_ipi_startup: ESR %08x\n", esr);
556 }
557
558 return 0;
559 }
560
561 int
562 x86_ipi(int vec, int target, int dl)
563 {
564 int result, s;
565
566 s = splhigh();
567
568 i82489_icr_wait();
569
570 if ((target & LAPIC_DEST_MASK) == 0)
571 i82489_writereg(LAPIC_ICRHI, target << LAPIC_ID_SHIFT);
572
573 i82489_writereg(LAPIC_ICRLO,
574 (target & LAPIC_DEST_MASK) | vec | dl | LAPIC_LEVEL_ASSERT);
575
576 #ifdef DIAGNOSTIC
577 i82489_icr_wait();
578 result = (i82489_readreg(LAPIC_ICRLO) & LAPIC_DLSTAT_BUSY) ? EBUSY : 0;
579 #else
580 /* Don't wait - if it doesn't go, we're in big trouble anyway. */
581 result = 0;
582 #endif
583 splx(s);
584
585 return result;
586 }
587
588
589 /*
590 * Using 'pin numbers' as:
591 * 0 - timer
592 * 1 - unused
593 * 2 - PCINT
594 * 3 - LVINT0
595 * 4 - LVINT1
596 * 5 - LVERR
597 */
598
599 static void
600 lapic_hwmask(struct pic *pic, int pin)
601 {
602 int reg;
603 uint32_t val;
604
605 reg = LAPIC_LVTT + (pin << 4);
606 val = i82489_readreg(reg);
607 val |= LAPIC_LVT_MASKED;
608 i82489_writereg(reg, val);
609 }
610
611 static void
612 lapic_hwunmask(struct pic *pic, int pin)
613 {
614 int reg;
615 uint32_t val;
616
617 reg = LAPIC_LVTT + (pin << 4);
618 val = i82489_readreg(reg);
619 val &= ~LAPIC_LVT_MASKED;
620 i82489_writereg(reg, val);
621 }
622
623 static void
624 lapic_setup(struct pic *pic, struct cpu_info *ci,
625 int pin, int idtvec, int type)
626 {
627 }
Cache object: d2e9fb2ab64d0e478d7e0260daf6a0e2
|