📄 time.c
字号:
/****************************************************************************** * 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 + -