⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 time.c

📁 xen虚拟机源代码安装包
💻 C
📖 第 1 页 / 共 3 页
字号:
/****************************************************************************** * arch/x86/time.c *  * Per-CPU time calibration and management. *  * Copyright (c) 2002-2005, K A Fraser *  * Portions from Linux are: * Copyright (c) 1991, 1992, 1995  Linus Torvalds */#include <xen/config.h>#include <xen/errno.h>#include <xen/event.h>#include <xen/sched.h>#include <xen/lib.h>#include <xen/config.h>#include <xen/init.h>#include <xen/time.h>#include <xen/timer.h>#include <xen/smp.h>#include <xen/irq.h>#include <xen/softirq.h>#include <asm/io.h>#include <asm/msr.h>#include <asm/mpspec.h>#include <asm/processor.h>#include <asm/fixmap.h>#include <asm/mc146818rtc.h>#include <asm/div64.h>#include <asm/hpet.h>#include <io_ports.h>/* opt_clocksource: Force clocksource to one of: pit, hpet, cyclone, acpi. */static char opt_clocksource[10];string_param("clocksource", opt_clocksource);unsigned long cpu_khz;  /* CPU clock frequency in kHz. */DEFINE_SPINLOCK(rtc_lock);unsigned long pit0_ticks;static u32 wc_sec, wc_nsec; /* UTC time at last 'time update'. */static DEFINE_SPINLOCK(wc_lock);struct time_scale {    int shift;    u32 mul_frac;};struct cpu_time {    u64 local_tsc_stamp;    u64 cstate_tsc_stamp;    s_time_t stime_local_stamp;    s_time_t stime_master_stamp;    struct time_scale tsc_scale;    u64 cstate_plt_count_stamp;};struct platform_timesource {    char *name;    u64 frequency;    u64 (*read_counter)(void);    int counter_bits;};static DEFINE_PER_CPU(struct cpu_time, cpu_time);/* Calibrate all CPUs to platform timer every EPOCH. */#define EPOCH MILLISECS(1000)static struct timer calibration_timer;/* TSC is invariant on C state entry? */static bool_t tsc_invariant;/* * We simulate a 32-bit platform timer from the 16-bit PIT ch2 counter. * Otherwise overflow happens too quickly (~50ms) for us to guarantee that * softirq handling will happen in time. *  * The pit_lock protects the 16- and 32-bit stamp fields as well as the  */static DEFINE_SPINLOCK(pit_lock);static u16 pit_stamp16;static u32 pit_stamp32;static int using_pit;/* * 32-bit division of integer dividend and integer divisor yielding * 32-bit fractional quotient. */static inline u32 div_frac(u32 dividend, u32 divisor){    u32 quotient, remainder;    ASSERT(dividend < divisor);    asm (         "divl %4"        : "=a" (quotient), "=d" (remainder)        : "0" (0), "1" (dividend), "r" (divisor) );    return quotient;}/* * 32-bit multiplication of multiplicand and fractional multiplier * yielding 32-bit product (radix point at same position as in multiplicand). */static inline u32 mul_frac(u32 multiplicand, u32 multiplier){    u32 product_int, product_frac;    asm (        "mul %3"        : "=a" (product_frac), "=d" (product_int)        : "0" (multiplicand), "r" (multiplier) );    return product_int;}/* * Scale a 64-bit delta by scaling and multiplying by a 32-bit fraction, * yielding a 64-bit result. */static inline u64 scale_delta(u64 delta, struct time_scale *scale){    u64 product;#ifdef CONFIG_X86_32    u32 tmp1, tmp2;#endif    if ( scale->shift < 0 )        delta >>= -scale->shift;    else        delta <<= scale->shift;#ifdef CONFIG_X86_32    asm (        "mul  %5       ; "        "mov  %4,%%eax ; "        "mov  %%edx,%4 ; "        "mul  %5       ; "        "xor  %5,%5    ; "        "add  %4,%%eax ; "        "adc  %5,%%edx ; "        : "=A" (product), "=r" (tmp1), "=r" (tmp2)        : "a" ((u32)delta), "1" ((u32)(delta >> 32)), "2" (scale->mul_frac) );#else    asm (        "mul %%rdx ; shrd $32,%%rdx,%%rax"        : "=a" (product) : "0" (delta), "d" ((u64)scale->mul_frac) );#endif    return product;}/* * cpu_mask that denotes the CPUs that needs timer interrupt coming in as * IPIs in place of local APIC timers */extern int xen_cpuidle;static cpumask_t pit_broadcast_mask;static void smp_send_timer_broadcast_ipi(void){    int cpu = smp_processor_id();    cpumask_t mask;    cpus_and(mask, cpu_online_map, pit_broadcast_mask);    if ( cpu_isset(cpu, mask) )    {        cpu_clear(cpu, mask);        raise_softirq(TIMER_SOFTIRQ);    }    if ( !cpus_empty(mask) )    {        cpumask_raise_softirq(mask, TIMER_SOFTIRQ);    }}static void timer_interrupt(int irq, void *dev_id, struct cpu_user_regs *regs){    ASSERT(local_irq_is_enabled());    if ( hpet_legacy_irq_tick() )        return;    /* Only for start-of-day interruopt tests in io_apic.c. */    (*(volatile unsigned long *)&pit0_ticks)++;    /* Rough hack to allow accurate timers to sort-of-work with no APIC. */    if ( !cpu_has_apic )        raise_softirq(TIMER_SOFTIRQ);    if ( xen_cpuidle )        smp_send_timer_broadcast_ipi();    /* Emulate a 32-bit PIT counter. */    if ( using_pit )    {        u16 count;        spin_lock_irq(&pit_lock);        outb(0x80, PIT_MODE);        count  = inb(PIT_CH2);        count |= inb(PIT_CH2) << 8;        pit_stamp32 += (u16)(pit_stamp16 - count);        pit_stamp16 = count;        spin_unlock_irq(&pit_lock);    }}static struct irqaction irq0 = { timer_interrupt, "timer", NULL };/* ------ Calibrate the TSC -------  * Return processor ticks per second / CALIBRATE_FRAC. */#define CLOCK_TICK_RATE 1193182 /* system crystal frequency (Hz) */#define CALIBRATE_FRAC  20      /* calibrate over 50ms */#define CALIBRATE_LATCH ((CLOCK_TICK_RATE+(CALIBRATE_FRAC/2))/CALIBRATE_FRAC)static u64 init_pit_and_calibrate_tsc(void){    u64 start, end;    unsigned long count;    /* Set PIT channel 0 to HZ Hz. */#define LATCH (((CLOCK_TICK_RATE)+(HZ/2))/HZ)    outb_p(0x34, PIT_MODE);        /* binary, mode 2, LSB/MSB, ch 0 */    outb_p(LATCH & 0xff, PIT_CH0); /* LSB */    outb(LATCH >> 8, PIT_CH0);     /* MSB */    /* Set the Gate high, disable speaker */    outb((inb(0x61) & ~0x02) | 0x01, 0x61);    /*     * Now let's take care of CTC channel 2     *     * Set the Gate high, program CTC channel 2 for mode 0, (interrupt on     * terminal count mode), binary count, load 5 * LATCH count, (LSB and MSB)     * to begin countdown.     */    outb(0xb0, PIT_MODE);           /* binary, mode 0, LSB/MSB, Ch 2 */    outb(CALIBRATE_LATCH & 0xff, PIT_CH2); /* LSB of count */    outb(CALIBRATE_LATCH >> 8, PIT_CH2);   /* MSB of count */    rdtscll(start);    for ( count = 0; (inb(0x61) & 0x20) == 0; count++ )        continue;    rdtscll(end);    /* Error if the CTC doesn't behave itself. */    if ( count == 0 )        return 0;    return ((end - start) * (u64)CALIBRATE_FRAC);}static void set_time_scale(struct time_scale *ts, u64 ticks_per_sec){    u64 tps64 = ticks_per_sec;    u32 tps32;    int shift = 0;    ASSERT(tps64 != 0);    while ( tps64 > (MILLISECS(1000)*2) )    {        tps64 >>= 1;        shift--;    }    tps32 = (u32)tps64;    while ( tps32 <= (u32)MILLISECS(1000) )    {        tps32 <<= 1;        shift++;    }    ts->mul_frac = div_frac(MILLISECS(1000), tps32);    ts->shift    = shift;}static atomic_t tsc_calibrate_gang = ATOMIC_INIT(0);static unsigned int tsc_calibrate_status = 0;void calibrate_tsc_bp(void){    while ( atomic_read(&tsc_calibrate_gang) != (num_booting_cpus() - 1) )        mb();    outb(CALIBRATE_LATCH & 0xff, PIT_CH2);    outb(CALIBRATE_LATCH >> 8, PIT_CH2);    tsc_calibrate_status = 1;    wmb();    while ( (inb(0x61) & 0x20) == 0 )        continue;    tsc_calibrate_status = 2;    wmb();    while ( atomic_read(&tsc_calibrate_gang) != 0 )        mb();}void calibrate_tsc_ap(void){    u64 t1, t2, ticks_per_sec;    atomic_inc(&tsc_calibrate_gang);    while ( tsc_calibrate_status < 1 )        mb();    rdtscll(t1);    while ( tsc_calibrate_status < 2 )        mb();    rdtscll(t2);    ticks_per_sec = (t2 - t1) * (u64)CALIBRATE_FRAC;    set_time_scale(&this_cpu(cpu_time).tsc_scale, ticks_per_sec);    atomic_dec(&tsc_calibrate_gang);}static char *freq_string(u64 freq){    static char s[20];    unsigned int x, y;    y = (unsigned int)do_div(freq, 1000000) / 1000;    x = (unsigned int)freq;    snprintf(s, sizeof(s), "%u.%03uMHz", x, y);    return s;}/************************************************************ * PLATFORM TIMER 1: PROGRAMMABLE INTERVAL TIMER (LEGACY PIT) */static u64 read_pit_count(void){    u16 count16;    u32 count32;    unsigned long flags;    spin_lock_irqsave(&pit_lock, flags);    outb(0x80, PIT_MODE);    count16  = inb(PIT_CH2);    count16 |= inb(PIT_CH2) << 8;    count32 = pit_stamp32 + (u16)(pit_stamp16 - count16);    spin_unlock_irqrestore(&pit_lock, flags);    return count32;}static void init_pit(struct platform_timesource *pts){    pts->name = "PIT";    pts->frequency = CLOCK_TICK_RATE;    pts->read_counter = read_pit_count;    pts->counter_bits = 32;    using_pit = 1;}/************************************************************ * PLATFORM TIMER 2: HIGH PRECISION EVENT TIMER (HPET) */static u64 read_hpet_count(void){    return hpet_read32(HPET_COUNTER);}static int init_hpet(struct platform_timesource *pts){    u64 hpet_rate = hpet_setup();    if ( hpet_rate == 0 )        return 0;    pts->name = "HPET";    pts->frequency = hpet_rate;    pts->read_counter = read_hpet_count;    pts->counter_bits = 32;    return 1;}/************************************************************ * PLATFORM TIMER 3: IBM 'CYCLONE' TIMER */int use_cyclone;/* * Although the counter is read via a 64-bit register, I believe it is actually * a 40-bit counter. Since this will wrap, I read only the low 32 bits and * periodically fold into a 64-bit software counter, just as for PIT and HPET. */#define CYCLONE_CBAR_ADDR   0xFEB00CD0#define CYCLONE_PMCC_OFFSET 0x51A0#define CYCLONE_MPMC_OFFSET 0x51D0#define CYCLONE_MPCS_OFFSET 0x51A8#define CYCLONE_TIMER_FREQ  100000000/* Cyclone MPMC0 register. */static volatile u32 *cyclone_timer;static u64 read_cyclone_count(void){    return *cyclone_timer;}static volatile u32 *map_cyclone_reg(unsigned long regaddr){    unsigned long pageaddr = regaddr &  PAGE_MASK;    unsigned long offset   = regaddr & ~PAGE_MASK;    set_fixmap_nocache(FIX_CYCLONE_TIMER, pageaddr);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -