[BACK]Return to lapic.c CVS log [TXT][DIR] Up to [local] / sys / arch / i386 / i386

Annotation of sys/arch/i386/i386/lapic.c, Revision 1.1.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