[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     ! 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