Annotation of sys/arch/amd64/amd64/lapic.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: lapic.c,v 1.10 2007/05/25 16:22:11 art Exp $ */
! 2: /* $NetBSD: lapic.c,v 1.2 2003/05/08 01:04:35 fvdl Exp $ */
! 3:
! 4: /*-
! 5: * Copyright (c) 2000 The NetBSD Foundation, Inc.
! 6: * All rights reserved.
! 7: *
! 8: * This code is derived from software contributed to The NetBSD Foundation
! 9: * by RedBack Networks Inc.
! 10: *
! 11: * Author: Bill Sommerfeld
! 12: *
! 13: * Redistribution and use in source and binary forms, with or without
! 14: * modification, are permitted provided that the following conditions
! 15: * are met:
! 16: * 1. Redistributions of source code must retain the above copyright
! 17: * notice, this list of conditions and the following disclaimer.
! 18: * 2. Redistributions in binary form must reproduce the above copyright
! 19: * notice, this list of conditions and the following disclaimer in the
! 20: * documentation and/or other materials provided with the distribution.
! 21: * 3. All advertising materials mentioning features or use of this software
! 22: * must display the following acknowledgement:
! 23: * This product includes software developed by the NetBSD
! 24: * Foundation, Inc. and its contributors.
! 25: * 4. Neither the name of The NetBSD Foundation nor the names of its
! 26: * contributors may be used to endorse or promote products derived
! 27: * from this software without specific prior written permission.
! 28: *
! 29: * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
! 30: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
! 31: * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
! 32: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
! 33: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
! 34: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
! 35: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
! 36: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
! 37: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
! 38: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
! 39: * POSSIBILITY OF SUCH DAMAGE.
! 40: */
! 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:
! 48: #include <uvm/uvm_extern.h>
! 49:
! 50: #include <dev/ic/i8253reg.h>
! 51:
! 52: #include <machine/cpu.h>
! 53: #include <machine/cpufunc.h>
! 54: #include <machine/cpuvar.h>
! 55: #include <machine/pmap.h>
! 56: #include <machine/vmparam.h>
! 57: #include <machine/mpbiosvar.h>
! 58: #include <machine/pcb.h>
! 59: #include <machine/specialreg.h>
! 60: #include <machine/segments.h>
! 61:
! 62: #include <machine/apicvar.h>
! 63: #include <machine/i82489reg.h>
! 64: #include <machine/i82489var.h>
! 65:
! 66: #include "ioapic.h"
! 67:
! 68: #if NIOAPIC > 0
! 69: #include <machine/i82093var.h>
! 70: #endif
! 71:
! 72: struct evcount clk_count;
! 73: struct evcount ipi_count;
! 74:
! 75: void lapic_delay(int);
! 76: void lapic_microtime(struct timeval *);
! 77: static u_int32_t lapic_gettick(void);
! 78: void lapic_clockintr(void *, struct intrframe);
! 79: void lapic_map(paddr_t);
! 80:
! 81: void lapic_hwmask(struct pic *, int);
! 82: void lapic_hwunmask(struct pic *, int);
! 83: void lapic_setup(struct pic *, struct cpu_info *, int, int, int);
! 84:
! 85: extern char idt_allocmap[];
! 86:
! 87: struct pic local_pic = {
! 88: {0, {NULL}, NULL, 0, "lapic", NULL, 0, 0},
! 89: PIC_LAPIC,
! 90: #ifdef MULTIPROCESSOR
! 91: {},
! 92: #endif
! 93: lapic_hwmask,
! 94: lapic_hwunmask,
! 95: lapic_setup,
! 96: lapic_setup,
! 97: };
! 98:
! 99: void
! 100: lapic_map(paddr_t lapic_base)
! 101: {
! 102: int s;
! 103: pt_entry_t *pte;
! 104: vaddr_t va = (vaddr_t)&local_apic;
! 105:
! 106: disable_intr();
! 107: s = lapic_tpr;
! 108:
! 109: /*
! 110: * Map local apic. If we have a local apic, it's safe to assume
! 111: * we're on a 486 or better and can use invlpg and non-cacheable PTE's
! 112: *
! 113: * Whap the PTE "by hand" rather than calling pmap_kenter_pa because
! 114: * the latter will attempt to invoke TLB shootdown code just as we
! 115: * might have changed the value of cpu_number()..
! 116: */
! 117:
! 118: pte = kvtopte(va);
! 119: *pte = lapic_base | PG_RW | PG_V | PG_N | pmap_pg_g;
! 120: invlpg(va);
! 121:
! 122: #ifdef MULTIPROCESSOR
! 123: cpu_init_first(); /* catch up to changed cpu_number() */
! 124: #endif
! 125:
! 126: lapic_tpr = s;
! 127: enable_intr();
! 128: }
! 129:
! 130: /*
! 131: * enable local apic
! 132: */
! 133: void
! 134: lapic_enable(void)
! 135: {
! 136: i82489_writereg(LAPIC_SVR, LAPIC_SVR_ENABLE | LAPIC_SPURIOUS_VECTOR);
! 137: }
! 138:
! 139: void
! 140: lapic_set_lvt(void)
! 141: {
! 142: struct cpu_info *ci = curcpu();
! 143: int i;
! 144: struct mp_intr_map *mpi;
! 145: uint32_t lint0;
! 146:
! 147: #ifdef MULTIPROCESSOR
! 148: if (mp_verbose) {
! 149: apic_format_redir (ci->ci_dev->dv_xname, "prelint", 0, 0,
! 150: i82489_readreg(LAPIC_LVINT0));
! 151: apic_format_redir (ci->ci_dev->dv_xname, "prelint", 1, 0,
! 152: i82489_readreg(LAPIC_LVINT1));
! 153: }
! 154: #endif
! 155:
! 156: #if NIOAPIC > 0
! 157: /*
! 158: * Disable ExtINT by default when using I/O APICs.
! 159: */
! 160: if (nioapics > 0) {
! 161: lint0 = i82489_readreg(LAPIC_LVINT0);
! 162: lint0 |= LAPIC_LVT_MASKED;
! 163: i82489_writereg(LAPIC_LVINT0, lint0);
! 164: }
! 165: #endif
! 166:
! 167: for (i = 0; i < mp_nintr; i++) {
! 168: mpi = &mp_intrs[i];
! 169: if (mpi->ioapic == NULL && (mpi->cpu_id == MPS_ALL_APICS
! 170: || mpi->cpu_id == ci->ci_apicid)) {
! 171: #ifdef DIAGNOSTIC
! 172: if (mpi->ioapic_pin > 1)
! 173: panic("lapic_set_lvt: bad pin value %d",
! 174: mpi->ioapic_pin);
! 175: #endif
! 176: if (mpi->ioapic_pin == 0)
! 177: i82489_writereg(LAPIC_LVINT0, mpi->redir);
! 178: else
! 179: i82489_writereg(LAPIC_LVINT1, mpi->redir);
! 180: }
! 181: }
! 182:
! 183: #ifdef MULTIPROCESSOR
! 184: if (mp_verbose) {
! 185: apic_format_redir (ci->ci_dev->dv_xname, "timer", 0, 0,
! 186: i82489_readreg(LAPIC_LVTT));
! 187: apic_format_redir (ci->ci_dev->dv_xname, "pcint", 0, 0,
! 188: i82489_readreg(LAPIC_PCINT));
! 189: apic_format_redir (ci->ci_dev->dv_xname, "lint", 0, 0,
! 190: i82489_readreg(LAPIC_LVINT0));
! 191: apic_format_redir (ci->ci_dev->dv_xname, "lint", 1, 0,
! 192: i82489_readreg(LAPIC_LVINT1));
! 193: apic_format_redir (ci->ci_dev->dv_xname, "err", 0, 0,
! 194: i82489_readreg(LAPIC_LVERR));
! 195: }
! 196: #endif
! 197: }
! 198:
! 199: /*
! 200: * Initialize fixed idt vectors for use by local apic.
! 201: */
! 202: void
! 203: lapic_boot_init(paddr_t lapic_base)
! 204: {
! 205: static u_int64_t clk_irq = 0;
! 206: static u_int64_t ipi_irq = 0;
! 207:
! 208: lapic_map(lapic_base);
! 209:
! 210: #ifdef MULTIPROCESSOR
! 211: idt_allocmap[LAPIC_IPI_VECTOR] = 1;
! 212: idt_vec_set(LAPIC_IPI_VECTOR, Xintr_lapic_ipi);
! 213: idt_allocmap[LAPIC_IPI_INVLTLB] = 1;
! 214: idt_vec_set(LAPIC_IPI_INVLTLB, Xipi_invltlb);
! 215: idt_allocmap[LAPIC_IPI_INVLPG] = 1;
! 216: idt_vec_set(LAPIC_IPI_INVLPG, Xipi_invlpg);
! 217: idt_allocmap[LAPIC_IPI_INVLRANGE] = 1;
! 218: idt_vec_set(LAPIC_IPI_INVLRANGE, Xipi_invlrange);
! 219: #endif
! 220: idt_allocmap[LAPIC_SPURIOUS_VECTOR] = 1;
! 221: idt_vec_set(LAPIC_SPURIOUS_VECTOR, Xintrspurious);
! 222:
! 223: idt_allocmap[LAPIC_TIMER_VECTOR] = 1;
! 224: idt_vec_set(LAPIC_TIMER_VECTOR, Xintr_lapic_ltimer);
! 225:
! 226: evcount_attach(&clk_count, "clock", (void *)&clk_irq, &evcount_intr);
! 227: evcount_attach(&ipi_count, "ipi", (void *)&ipi_irq, &evcount_intr);
! 228: }
! 229:
! 230: static inline u_int32_t
! 231: lapic_gettick(void)
! 232: {
! 233: return i82489_readreg(LAPIC_CCR_TIMER);
! 234: }
! 235:
! 236: #include <sys/kernel.h> /* for hz */
! 237:
! 238: u_int32_t lapic_tval;
! 239:
! 240: /*
! 241: * this gets us up to a 4GHz busclock....
! 242: */
! 243: u_int32_t lapic_per_second;
! 244: u_int32_t lapic_frac_usec_per_cycle;
! 245: u_int64_t lapic_frac_cycle_per_usec;
! 246: u_int32_t lapic_delaytab[26];
! 247:
! 248: void
! 249: lapic_clockintr(void *arg, struct intrframe frame)
! 250: {
! 251:
! 252: hardclock((struct clockframe *)&frame);
! 253:
! 254: clk_count.ec_count++;
! 255: }
! 256:
! 257: void
! 258: lapic_initclocks(void)
! 259: {
! 260: /*
! 261: * Start local apic countdown timer running, in repeated mode.
! 262: *
! 263: * Mask the clock interrupt and set mode,
! 264: * then set divisor,
! 265: * then unmask and set the vector.
! 266: */
! 267: i82489_writereg (LAPIC_LVTT, LAPIC_LVTT_TM|LAPIC_LVTT_M);
! 268: i82489_writereg (LAPIC_DCR_TIMER, LAPIC_DCRT_DIV1);
! 269: i82489_writereg (LAPIC_ICR_TIMER, lapic_tval);
! 270: i82489_writereg (LAPIC_LVTT, LAPIC_LVTT_TM|LAPIC_TIMER_VECTOR);
! 271: }
! 272:
! 273: extern int gettick(void); /* XXX put in header file */
! 274: extern u_long rtclock_tval; /* XXX put in header file */
! 275: extern void (*initclock_func)(void); /* XXX put in header file */
! 276:
! 277: /*
! 278: * Calibrate the local apic count-down timer (which is running at
! 279: * bus-clock speed) vs. the i8254 counter/timer (which is running at
! 280: * a fixed rate).
! 281: *
! 282: * The Intel MP spec says: "An MP operating system may use the IRQ8
! 283: * real-time clock as a reference to determine the actual APIC timer clock
! 284: * speed."
! 285: *
! 286: * We're actually using the IRQ0 timer. Hmm.
! 287: */
! 288: void
! 289: lapic_calibrate_timer(struct cpu_info *ci)
! 290: {
! 291: unsigned int starttick, tick1, tick2, endtick;
! 292: unsigned int startapic, apic1, apic2, endapic;
! 293: u_int64_t dtick, dapic, tmp;
! 294: int i;
! 295:
! 296: if (mp_verbose)
! 297: printf("%s: calibrating local timer\n", ci->ci_dev->dv_xname);
! 298:
! 299: /*
! 300: * Configure timer to one-shot, interrupt masked,
! 301: * large positive number.
! 302: */
! 303: i82489_writereg (LAPIC_LVTT, LAPIC_LVTT_M);
! 304: i82489_writereg (LAPIC_DCR_TIMER, LAPIC_DCRT_DIV1);
! 305: i82489_writereg (LAPIC_ICR_TIMER, 0x80000000);
! 306:
! 307: starttick = gettick();
! 308: startapic = lapic_gettick();
! 309:
! 310: for (i=0; i<hz; i++) {
! 311: DELAY(2);
! 312: do {
! 313: tick1 = gettick();
! 314: apic1 = lapic_gettick();
! 315: } while (tick1 < starttick);
! 316: DELAY(2);
! 317: do {
! 318: tick2 = gettick();
! 319: apic2 = lapic_gettick();
! 320: } while (tick2 > starttick);
! 321: }
! 322:
! 323: endtick = gettick();
! 324: endapic = lapic_gettick();
! 325:
! 326: dtick = hz * rtclock_tval + (starttick-endtick);
! 327: dapic = startapic-endapic;
! 328:
! 329: /*
! 330: * there are TIMER_FREQ ticks per second.
! 331: * in dtick ticks, there are dapic bus clocks.
! 332: */
! 333: tmp = (TIMER_FREQ * dapic) / dtick;
! 334:
! 335: lapic_per_second = tmp;
! 336:
! 337: printf("%s: apic clock running at %lldMHz\n",
! 338: ci->ci_dev->dv_xname, tmp / (1000 * 1000));
! 339:
! 340: if (lapic_per_second != 0) {
! 341: /*
! 342: * reprogram the apic timer to run in periodic mode.
! 343: * XXX need to program timer on other cpu's, too.
! 344: */
! 345: lapic_tval = (lapic_per_second * 2) / hz;
! 346: lapic_tval = (lapic_tval / 2) + (lapic_tval & 0x1);
! 347:
! 348: i82489_writereg (LAPIC_LVTT, LAPIC_LVTT_TM|LAPIC_LVTT_M
! 349: |LAPIC_TIMER_VECTOR);
! 350: i82489_writereg (LAPIC_DCR_TIMER, LAPIC_DCRT_DIV1);
! 351: i82489_writereg (LAPIC_ICR_TIMER, lapic_tval);
! 352:
! 353: /*
! 354: * Compute fixed-point ratios between cycles and
! 355: * microseconds to avoid having to do any division
! 356: * in lapic_delay and lapic_microtime.
! 357: */
! 358:
! 359: tmp = (1000000 * (u_int64_t)1<<32) / lapic_per_second;
! 360: lapic_frac_usec_per_cycle = tmp;
! 361:
! 362: tmp = (lapic_per_second * (u_int64_t)1<<32) / 1000000;
! 363:
! 364: lapic_frac_cycle_per_usec = tmp;
! 365:
! 366: /*
! 367: * Compute delay in cycles for likely short delays in usec.
! 368: */
! 369: for (i=0; i<26; i++)
! 370: lapic_delaytab[i] = (lapic_frac_cycle_per_usec * i) >>
! 371: 32;
! 372:
! 373: /*
! 374: * Now that the timer's calibrated, use the apic timer routines
! 375: * for all our timing needs..
! 376: */
! 377: delay_func = lapic_delay;
! 378: initclock_func = lapic_initclocks;
! 379: }
! 380: }
! 381:
! 382: /*
! 383: * delay for N usec.
! 384: */
! 385:
! 386: void
! 387: lapic_delay(int usec)
! 388: {
! 389: int32_t tick, otick;
! 390: int64_t deltat; /* XXX may want to be 64bit */
! 391:
! 392: otick = lapic_gettick();
! 393:
! 394: if (usec <= 0)
! 395: return;
! 396: if (usec <= 25)
! 397: deltat = lapic_delaytab[usec];
! 398: else
! 399: deltat = (lapic_frac_cycle_per_usec * usec) >> 32;
! 400:
! 401: while (deltat > 0) {
! 402: tick = lapic_gettick();
! 403: if (tick > otick)
! 404: deltat -= lapic_tval - (tick - otick);
! 405: else
! 406: deltat -= otick - tick;
! 407: otick = tick;
! 408:
! 409: x86_pause();
! 410: }
! 411: }
! 412:
! 413: /*
! 414: * XXX the following belong mostly or partly elsewhere..
! 415: */
! 416:
! 417: static __inline void i82489_icr_wait(void);
! 418:
! 419: static __inline void
! 420: i82489_icr_wait(void)
! 421: {
! 422: #ifdef DIAGNOSTIC
! 423: unsigned j = 100000;
! 424: #endif /* DIAGNOSTIC */
! 425:
! 426: while ((i82489_readreg(LAPIC_ICRLO) & LAPIC_DLSTAT_BUSY) != 0) {
! 427: x86_pause();
! 428: #ifdef DIAGNOSTIC
! 429: j--;
! 430: if (j == 0)
! 431: panic("i82489_icr_wait: busy");
! 432: #endif /* DIAGNOSTIC */
! 433: }
! 434: }
! 435:
! 436: int
! 437: x86_ipi_init(int target)
! 438: {
! 439:
! 440: if ((target&LAPIC_DEST_MASK)==0) {
! 441: i82489_writereg(LAPIC_ICRHI, target<<LAPIC_ID_SHIFT);
! 442: }
! 443:
! 444: i82489_writereg(LAPIC_ICRLO, (target & LAPIC_DEST_MASK) |
! 445: LAPIC_DLMODE_INIT | LAPIC_LVL_ASSERT );
! 446:
! 447: i82489_icr_wait();
! 448:
! 449: delay(10000);
! 450:
! 451: i82489_writereg(LAPIC_ICRLO, (target & LAPIC_DEST_MASK) |
! 452: LAPIC_DLMODE_INIT | LAPIC_LVL_TRIG | LAPIC_LVL_DEASSERT);
! 453:
! 454: i82489_icr_wait();
! 455:
! 456: return (i82489_readreg(LAPIC_ICRLO) & LAPIC_DLSTAT_BUSY)?EBUSY:0;
! 457: }
! 458:
! 459: int
! 460: x86_ipi(int vec, int target, int dl)
! 461: {
! 462: int result, s;
! 463:
! 464: s = splclock();
! 465:
! 466: i82489_icr_wait();
! 467:
! 468: if ((target & LAPIC_DEST_MASK) == 0)
! 469: i82489_writereg(LAPIC_ICRHI, target << LAPIC_ID_SHIFT);
! 470:
! 471: i82489_writereg(LAPIC_ICRLO,
! 472: (target & LAPIC_DEST_MASK) | vec | dl | LAPIC_LVL_ASSERT);
! 473:
! 474: i82489_icr_wait();
! 475:
! 476: result = (i82489_readreg(LAPIC_ICRLO) & LAPIC_DLSTAT_BUSY) ? EBUSY : 0;
! 477:
! 478: splx(s);
! 479:
! 480: return result;
! 481: }
! 482:
! 483:
! 484: /*
! 485: * Using 'pin numbers' as:
! 486: * 0 - timer
! 487: * 1 - unused
! 488: * 2 - PCINT
! 489: * 3 - LVINT0
! 490: * 4 - LVINT1
! 491: * 5 - LVERR
! 492: */
! 493:
! 494: void
! 495: lapic_hwmask(struct pic *pic, int pin)
! 496: {
! 497: int reg;
! 498: u_int32_t val;
! 499:
! 500: reg = LAPIC_LVTT + (pin << 4);
! 501: val = i82489_readreg(reg);
! 502: val |= LAPIC_LVT_MASKED;
! 503: i82489_writereg(reg, val);
! 504: }
! 505:
! 506: void
! 507: lapic_hwunmask(struct pic *pic, int pin)
! 508: {
! 509: int reg;
! 510: u_int32_t val;
! 511:
! 512: reg = LAPIC_LVTT + (pin << 4);
! 513: val = i82489_readreg(reg);
! 514: val &= ~LAPIC_LVT_MASKED;
! 515: i82489_writereg(reg, val);
! 516: }
! 517:
! 518: void
! 519: lapic_setup(struct pic *pic, struct cpu_info *ci, int pin, int idtvec, int type)
! 520: {
! 521: }
CVSweb