Annotation of sys/arch/i386/i386/lapic.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: lapic.c,v 1.17 2007/08/01 13:18:18 martin Exp $ */
! 2: /* $NetBSD: lapic.c,v 1.1.2.8 2000/02/23 06:10:50 sommerfeld 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: #include <sys/timetc.h>
! 48:
! 49: #include <uvm/uvm_extern.h>
! 50:
! 51: #include <machine/cpu.h>
! 52: #include <machine/cpufunc.h>
! 53: #include <machine/cpuvar.h>
! 54: #include <machine/pmap.h>
! 55: #include <machine/vmparam.h>
! 56: #include <machine/mpbiosvar.h>
! 57: #include <machine/pcb.h>
! 58: #include <machine/specialreg.h>
! 59: #include <machine/segments.h>
! 60:
! 61: #include <machine/apicvar.h>
! 62: #include <machine/i82489reg.h>
! 63: #include <machine/i82489var.h>
! 64: #include <machine/pctr.h>
! 65:
! 66: #include <dev/ic/i8253reg.h>
! 67:
! 68: struct evcount clk_count;
! 69: struct evcount ipi_count;
! 70:
! 71: void lapic_delay(int);
! 72: void lapic_microtime(struct timeval *);
! 73: static __inline u_int32_t lapic_gettick(void);
! 74: void lapic_clockintr(void *);
! 75: void lapic_initclocks(void);
! 76: void lapic_map(paddr_t);
! 77:
! 78: void
! 79: lapic_map(lapic_base)
! 80: paddr_t lapic_base;
! 81: {
! 82: int s;
! 83: pt_entry_t *pte;
! 84: vaddr_t va = (vaddr_t)&local_apic;
! 85:
! 86: disable_intr();
! 87: s = lapic_tpr;
! 88:
! 89: /*
! 90: * Map local apic. If we have a local apic, it's safe to assume
! 91: * we're on a 486 or better and can use invlpg and non-cacheable PTE's
! 92: *
! 93: * Whap the PTE "by hand" rather than calling pmap_kenter_pa because
! 94: * the latter will attempt to invoke TLB shootdown code just as we
! 95: * might have changed the value of cpu_number()..
! 96: */
! 97:
! 98: pte = kvtopte(va);
! 99: *pte = lapic_base | PG_RW | PG_V | PG_N;
! 100: invlpg(va);
! 101:
! 102: #ifdef MULTIPROCESSOR
! 103: cpu_init_first(); /* catch up to changed cpu_number() */
! 104: #endif
! 105:
! 106: lapic_tpr = s;
! 107: enable_intr();
! 108: }
! 109:
! 110: /*
! 111: * enable local apic
! 112: */
! 113: void
! 114: lapic_enable()
! 115: {
! 116: i82489_writereg(LAPIC_SVR, LAPIC_SVR_ENABLE | LAPIC_SPURIOUS_VECTOR);
! 117: }
! 118:
! 119: void
! 120: lapic_set_softvectors()
! 121: {
! 122: idt_vec_set(LAPIC_SOFTCLOCK_VECTOR, Xintrsoftclock);
! 123: idt_vec_set(LAPIC_SOFTNET_VECTOR, Xintrsoftnet);
! 124: idt_vec_set(LAPIC_SOFTTTY_VECTOR, Xintrsofttty);
! 125: idt_vec_set(LAPIC_SOFTAST_VECTOR, Xintrsoftast);
! 126: }
! 127:
! 128: void
! 129: lapic_set_lvt()
! 130: {
! 131: struct cpu_info *ci = curcpu();
! 132: int i;
! 133: struct mp_intr_map *mpi;
! 134:
! 135: #ifdef MULTIPROCESSOR
! 136: if (mp_verbose) {
! 137: apic_format_redir(ci->ci_dev.dv_xname, "prelint", 0, 0,
! 138: i82489_readreg(LAPIC_LVINT0));
! 139: apic_format_redir(ci->ci_dev.dv_xname, "prelint", 1, 0,
! 140: i82489_readreg(LAPIC_LVINT1));
! 141: }
! 142: #endif
! 143:
! 144: for (i = 0; i < mp_nintrs; i++) {
! 145: mpi = &mp_intrs[i];
! 146: if (mpi->ioapic == NULL && (mpi->cpu_id == MPS_ALL_APICS
! 147: || mpi->cpu_id == ci->ci_apicid)) {
! 148: #ifdef DIAGNOSTIC
! 149: if (mpi->ioapic_pin > 1)
! 150: panic("lapic_set_lvt: bad pin value %d",
! 151: mpi->ioapic_pin);
! 152: #endif
! 153: if (mpi->ioapic_pin == 0)
! 154: i82489_writereg(LAPIC_LVINT0, mpi->redir);
! 155: else
! 156: i82489_writereg(LAPIC_LVINT1, mpi->redir);
! 157: }
! 158: }
! 159:
! 160: #ifdef MULTIPROCESSOR
! 161: if (mp_verbose) {
! 162: apic_format_redir(ci->ci_dev.dv_xname, "timer", 0, 0,
! 163: i82489_readreg(LAPIC_LVTT));
! 164: apic_format_redir(ci->ci_dev.dv_xname, "pcint", 0, 0,
! 165: i82489_readreg(LAPIC_PCINT));
! 166: apic_format_redir(ci->ci_dev.dv_xname, "lint", 0, 0,
! 167: i82489_readreg(LAPIC_LVINT0));
! 168: apic_format_redir(ci->ci_dev.dv_xname, "lint", 1, 0,
! 169: i82489_readreg(LAPIC_LVINT1));
! 170: apic_format_redir(ci->ci_dev.dv_xname, "err", 0, 0,
! 171: i82489_readreg(LAPIC_LVERR));
! 172: }
! 173: #endif
! 174: }
! 175:
! 176: /*
! 177: * Initialize fixed idt vectors for use by local apic.
! 178: */
! 179: void
! 180: lapic_boot_init(paddr_t lapic_base)
! 181: {
! 182: static int clk_irq = 0;
! 183: static int ipi_irq = 0;
! 184:
! 185: lapic_map(lapic_base);
! 186:
! 187: #ifdef MULTIPROCESSOR
! 188: idt_vec_set(LAPIC_IPI_VECTOR, Xintripi);
! 189: idt_vec_set(LAPIC_IPI_AST, Xintripi_ast);
! 190: idt_vec_set(LAPIC_IPI_INVLTLB, Xintripi_invltlb);
! 191: idt_vec_set(LAPIC_IPI_INVLPG, Xintripi_invlpg);
! 192: idt_vec_set(LAPIC_IPI_INVLRANGE, Xintripi_invlrange);
! 193: #endif
! 194: idt_vec_set(LAPIC_SPURIOUS_VECTOR, Xintrspurious);
! 195: idt_vec_set(LAPIC_TIMER_VECTOR, Xintrltimer);
! 196:
! 197: evcount_attach(&clk_count, "clock", (void *)&clk_irq, &evcount_intr);
! 198: evcount_attach(&ipi_count, "ipi", (void *)&ipi_irq, &evcount_intr);
! 199: }
! 200:
! 201: static __inline u_int32_t
! 202: lapic_gettick()
! 203: {
! 204: return (i82489_readreg(LAPIC_CCR_TIMER));
! 205: }
! 206:
! 207: #include <sys/kernel.h> /* for hz */
! 208:
! 209: u_int32_t lapic_tval;
! 210:
! 211: /*
! 212: * this gets us up to a 4GHz busclock....
! 213: */
! 214: u_int32_t lapic_per_second;
! 215: u_int32_t lapic_frac_usec_per_cycle;
! 216: u_int64_t lapic_frac_cycle_per_usec;
! 217: u_int32_t lapic_delaytab[26];
! 218: u_int64_t scaled_pentium_mhz;
! 219:
! 220: void
! 221: lapic_clockintr(arg)
! 222: void *arg;
! 223: {
! 224: struct cpu_info *ci = curcpu();
! 225: struct clockframe *frame = arg;
! 226:
! 227: if (CPU_IS_PRIMARY(ci)) {
! 228: ci->ci_tscbase = rdtsc();
! 229: i386_broadcast_ipi(I386_IPI_MICROSET);
! 230: }
! 231: hardclock(frame);
! 232:
! 233: clk_count.ec_count++;
! 234: }
! 235:
! 236: void
! 237: lapic_initclocks()
! 238: {
! 239: /*
! 240: * Start local apic countdown timer running, in repeated mode.
! 241: *
! 242: * Mask the clock interrupt and set mode,
! 243: * then set divisor,
! 244: * then unmask and set the vector.
! 245: */
! 246: i82489_writereg(LAPIC_LVTT, LAPIC_LVTT_TM|LAPIC_LVTT_M);
! 247: i82489_writereg(LAPIC_DCR_TIMER, LAPIC_DCRT_DIV1);
! 248: i82489_writereg(LAPIC_ICR_TIMER, lapic_tval);
! 249: i82489_writereg(LAPIC_LVTT, LAPIC_LVTT_TM|LAPIC_TIMER_VECTOR);
! 250: }
! 251:
! 252: extern int gettick(void); /* XXX put in header file */
! 253: extern void (*initclock_func)(void); /* XXX put in header file */
! 254:
! 255: /*
! 256: * Calibrate the local apic count-down timer (which is running at
! 257: * bus-clock speed) vs. the i8254 counter/timer (which is running at
! 258: * a fixed rate).
! 259: *
! 260: * The Intel MP spec says: "An MP operating system may use the IRQ8
! 261: * real-time clock as a reference to determine the actual APIC timer clock
! 262: * speed."
! 263: *
! 264: * We're actually using the IRQ0 timer. Hmm.
! 265: */
! 266: void
! 267: lapic_calibrate_timer(struct cpu_info *ci)
! 268: {
! 269: unsigned int starttick, tick1, tick2, endtick;
! 270: unsigned int startapic, apic1, apic2, endapic;
! 271: u_int64_t dtick, dapic, tmp;
! 272: int i;
! 273: char tbuf[9];
! 274:
! 275: if (mp_verbose)
! 276: printf("%s: calibrating local timer\n", ci->ci_dev.dv_xname);
! 277:
! 278: /*
! 279: * Configure timer to one-shot, interrupt masked,
! 280: * large positive number.
! 281: */
! 282: i82489_writereg(LAPIC_LVTT, LAPIC_LVTT_M);
! 283: i82489_writereg(LAPIC_DCR_TIMER, LAPIC_DCRT_DIV1);
! 284: i82489_writereg(LAPIC_ICR_TIMER, 0x80000000);
! 285:
! 286: starttick = gettick();
! 287: startapic = lapic_gettick();
! 288:
! 289: DELAY(2); /* using "old" delay here.. */
! 290:
! 291: for (i=0; i<hz; i++) {
! 292: do {
! 293: tick1 = gettick();
! 294: apic1 = lapic_gettick();
! 295: } while (tick1 < starttick);
! 296:
! 297: do {
! 298: tick2 = gettick();
! 299: apic2 = lapic_gettick();
! 300: } while (tick2 > starttick);
! 301: }
! 302:
! 303: endtick = gettick();
! 304: endapic = lapic_gettick();
! 305:
! 306: dtick = hz * TIMER_DIV(hz) + (starttick-endtick);
! 307: dapic = startapic-endapic;
! 308:
! 309: /*
! 310: * there are TIMER_FREQ ticks per second.
! 311: * in dtick ticks, there are dapic bus clocks.
! 312: */
! 313: tmp = (TIMER_FREQ * dapic) / dtick;
! 314:
! 315: lapic_per_second = tmp;
! 316:
! 317: #if 0
! 318: humanize_number(tbuf, sizeof(tbuf), tmp, "Hz", 1000);
! 319: #else /* XXX: from NetBSD sources... sigh. */
! 320: {
! 321: /* prefixes are: (none), Kilo, Mega, Giga, Tera, Peta, Exa */
! 322: static const char prefixes[] = " KMGTPE";
! 323:
! 324: int i;
! 325: u_int64_t max;
! 326: size_t suffixlen;
! 327:
! 328: if (tbuf == NULL)
! 329: goto out;
! 330: if (sizeof(tbuf) > 0)
! 331: tbuf[0] = '\0';
! 332: suffixlen = sizeof "Hz" - 1;
! 333: /* check if enough room for `x y' + suffix + `\0' */
! 334: if (sizeof(tbuf) < 4 + suffixlen)
! 335: goto out;
! 336:
! 337: max = 1;
! 338: for (i = 0; i < sizeof(tbuf) - suffixlen - 3; i++)
! 339: max *= 10;
! 340: for (i = 0; tmp >= max && i < sizeof(prefixes); i++)
! 341: tmp /= 1000;
! 342:
! 343: snprintf(tbuf, sizeof(tbuf), "%qu%s%c%s",
! 344: (unsigned long long)tmp, i == 0 ? "" : " ", prefixes[i],
! 345: "Hz");
! 346: out:
! 347: ;
! 348: }
! 349: #endif
! 350:
! 351: printf("%s: apic clock running at %s\n", ci->ci_dev.dv_xname, tbuf);
! 352:
! 353: if (lapic_per_second != 0) {
! 354: /*
! 355: * reprogram the apic timer to run in periodic mode.
! 356: * XXX need to program timer on other cpu's, too.
! 357: */
! 358: lapic_tval = (lapic_per_second * 2) / hz;
! 359: lapic_tval = (lapic_tval / 2) + (lapic_tval & 0x1);
! 360:
! 361: i82489_writereg(LAPIC_LVTT, LAPIC_LVTT_TM | LAPIC_LVTT_M |
! 362: LAPIC_TIMER_VECTOR);
! 363: i82489_writereg(LAPIC_DCR_TIMER, LAPIC_DCRT_DIV1);
! 364: i82489_writereg(LAPIC_ICR_TIMER, lapic_tval);
! 365:
! 366: /*
! 367: * Compute fixed-point ratios between cycles and
! 368: * microseconds to avoid having to do any division
! 369: * in lapic_delay and lapic_microtime.
! 370: */
! 371:
! 372: tmp = (1000000 * (u_int64_t)1 << 32) / lapic_per_second;
! 373: lapic_frac_usec_per_cycle = tmp;
! 374:
! 375: tmp = (lapic_per_second * (u_int64_t)1 << 32) / 1000000;
! 376:
! 377: lapic_frac_cycle_per_usec = tmp;
! 378:
! 379: scaled_pentium_mhz = (1ULL << 32) / cpuspeed;
! 380:
! 381: /*
! 382: * Compute delay in cycles for likely short delays in usec.
! 383: */
! 384: for (i = 0; i < 26; i++)
! 385: lapic_delaytab[i] = (lapic_frac_cycle_per_usec * i) >>
! 386: 32;
! 387:
! 388: /*
! 389: * Now that the timer's calibrated, use the apic timer routines
! 390: * for all our timing needs..
! 391: */
! 392: delay_func = lapic_delay;
! 393: initclock_func = lapic_initclocks;
! 394: }
! 395: }
! 396:
! 397: /*
! 398: * delay for N usec.
! 399: */
! 400:
! 401: void
! 402: lapic_delay(int usec)
! 403: {
! 404: int32_t tick, otick;
! 405: int64_t deltat; /* XXX may want to be 64bit */
! 406:
! 407: otick = lapic_gettick();
! 408:
! 409: if (usec <= 0)
! 410: return;
! 411: if (usec <= 25)
! 412: deltat = lapic_delaytab[usec];
! 413: else
! 414: deltat = (lapic_frac_cycle_per_usec * usec) >> 32;
! 415:
! 416: while (deltat > 0) {
! 417: tick = lapic_gettick();
! 418: if (tick > otick)
! 419: deltat -= lapic_tval - (tick - otick);
! 420: else
! 421: deltat -= otick - tick;
! 422: otick = tick;
! 423: }
! 424: }
! 425:
! 426: #define LAPIC_TICK_THRESH 200
! 427:
! 428: /*
! 429: * An IPI handler to record current timer value
! 430: */
! 431: void
! 432: i386_ipi_microset(struct cpu_info *ci)
! 433: {
! 434: ci->ci_tscbase = rdtsc();
! 435: }
! 436:
! 437: #if 0
! 438: /*
! 439: * XXX need to make work correctly on other than cpu 0.
! 440: */
! 441: void
! 442: lapic_microtime(tv)
! 443: struct timeval *tv;
! 444: {
! 445: struct cpu_info *ci = curcpu();
! 446: struct timeval now;
! 447: u_int64_t tmp;
! 448:
! 449: disable_intr();
! 450: now = time;
! 451: tmp = rdtsc() - ci->ci_tscbase;
! 452: enable_intr();
! 453:
! 454: now.tv_usec += (tmp * scaled_pentium_mhz) >> 32;
! 455:
! 456: while (now.tv_usec >= 1000000) {
! 457: now.tv_sec += 1;
! 458: now.tv_usec -= 1000000;
! 459: }
! 460:
! 461: *tv = now;
! 462: }
! 463: #endif
! 464:
! 465: /*
! 466: * XXX the following belong mostly or partly elsewhere..
! 467: */
! 468:
! 469: int
! 470: i386_ipi_init(target)
! 471: int target;
! 472: {
! 473: unsigned j;
! 474:
! 475: if ((target & LAPIC_DEST_MASK) == 0)
! 476: i82489_writereg(LAPIC_ICRHI, target << LAPIC_ID_SHIFT);
! 477:
! 478: i82489_writereg(LAPIC_ICRLO, (target & LAPIC_DEST_MASK) |
! 479: LAPIC_DLMODE_INIT | LAPIC_LVL_ASSERT );
! 480:
! 481: for (j = 100000; j > 0; j--) {
! 482: __asm __volatile("pause": : :"memory");
! 483: if ((i82489_readreg(LAPIC_ICRLO) & LAPIC_DLSTAT_BUSY) == 0)
! 484: break;
! 485: }
! 486:
! 487: delay(10000);
! 488:
! 489: i82489_writereg(LAPIC_ICRLO, (target & LAPIC_DEST_MASK) |
! 490: LAPIC_DLMODE_INIT | LAPIC_LVL_TRIG | LAPIC_LVL_DEASSERT);
! 491:
! 492: for (j = 100000; j > 0; j--) {
! 493: __asm __volatile("pause": : :"memory");
! 494: if ((i82489_readreg(LAPIC_ICRLO) & LAPIC_DLSTAT_BUSY) == 0)
! 495: break;
! 496: }
! 497:
! 498: return (i82489_readreg(LAPIC_ICRLO) & LAPIC_DLSTAT_BUSY)?EBUSY:0;
! 499: }
! 500:
! 501: int
! 502: i386_ipi(vec,target,dl)
! 503: int vec,target,dl;
! 504: {
! 505: unsigned j;
! 506:
! 507: if ((target & LAPIC_DEST_MASK) == 0)
! 508: i82489_writereg(LAPIC_ICRHI, target << LAPIC_ID_SHIFT);
! 509:
! 510: i82489_writereg(LAPIC_ICRLO,
! 511: (target & LAPIC_DEST_MASK) | vec | dl | LAPIC_LVL_ASSERT);
! 512:
! 513: for (j = 100000;
! 514: j > 0 && (i82489_readreg(LAPIC_ICRLO) & LAPIC_DLSTAT_BUSY);
! 515: j--)
! 516: SPINLOCK_SPIN_HOOK;
! 517:
! 518: return (i82489_readreg(LAPIC_ICRLO) & LAPIC_DLSTAT_BUSY) ? EBUSY : 0;
! 519: }
CVSweb