/****************************************************************************** * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* opt_hpet_force: If true, force HPET configuration via PCI space. */ /* NB. This is a gross hack. Mainly useful for HPET testing. */ static int opt_hpet_force = 0; boolean_param("hpet_force", opt_hpet_force); #define EPOCH MILLISECS(1000) unsigned long cpu_khz; /* CPU clock frequency in kHz. */ unsigned long hpet_address; spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED; int timer_ack = 0; unsigned long volatile jiffies; static u32 wc_sec, wc_nsec; /* UTC time at last 'time update'. */ static spinlock_t wc_lock = SPIN_LOCK_UNLOCKED; struct time_scale { int shift; u32 mul_frac; }; struct cpu_time { u64 local_tsc_stamp; s_time_t stime_local_stamp; s_time_t stime_master_stamp; struct time_scale tsc_scale; struct ac_timer calibration_timer; } __cacheline_aligned; static struct cpu_time cpu_time[NR_CPUS]; /* * Protected by platform_timer_lock, which must be acquired with interrupts * disabled because pit_overflow() is called from PIT ch0 interrupt context. */ static s_time_t stime_platform_stamp; static u64 platform_timer_stamp; static struct time_scale platform_timer_scale; static spinlock_t platform_timer_lock = SPIN_LOCK_UNLOCKED; static u64 (*read_platform_count)(void); /* * Folding 16-bit PIT into 64-bit software counter is a really critical * operation! We therefore do it directly in PIT ch0 interrupt handler, * based on this flag. */ static int using_pit; static void pit_overflow(void); /* * 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; } void timer_interrupt(int irq, void *dev_id, struct cpu_user_regs *regs) { ASSERT(local_irq_is_enabled()); if ( timer_ack ) { extern spinlock_t i8259A_lock; spin_lock_irq(&i8259A_lock); outb(0x0c, 0x20); /* Ack the IRQ; AEOI will end it automatically. */ inb(0x20); spin_unlock_irq(&i8259A_lock); } /* Update jiffies counter. */ (*(unsigned long *)&jiffies)++; /* Rough hack to allow accurate timers to sort-of-work with no APIC. */ if ( !cpu_has_apic ) raise_softirq(AC_TIMER_SOFTIRQ); if ( using_pit ) pit_overflow(); } static struct irqaction irq0 = { timer_interrupt, "timer", NULL}; /* ------ Calibrate the TSC ------- * Return processor ticks per second / CALIBRATE_FRAC. */ #define CLOCK_TICK_RATE 1193180 /* system crystal frequency (Hz) */ #define CALIBRATE_FRAC 20 /* calibrate over 50ms */ #define CALIBRATE_LATCH ((CLOCK_TICK_RATE+(CALIBRATE_FRAC/2))/CALIBRATE_FRAC) static u64 calibrate_boot_tsc(void) { u64 start, end; unsigned long count; /* 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
#! /bin/sh
# A little script I whipped up to make it easy to
# patch source trees and have sane error handling
# -Erik
#
# (c) 2002 Erik Andersen <andersen@codepoet.org>

# Set directories from arguments, or use defaults.
targetdir=${1-.}
patchdir=${2-../kernel-patches}
patchpattern=${3-*}

if [ ! -d "${targetdir}" ] ; then
    echo "Aborting.  '${targetdir}' is not a directory."
    exit 1
fi
if [ ! -d "${patchdir}" ] ; then
    echo "Aborting.  '${patchdir}' is not a directory."
    exit 1
fi
    
for i in ${patchdir}/${patchpattern} ; do 
    case "$i" in
	*.gz)
	type="gzip"; uncomp="gunzip -dc"; ;; 
	*.bz)
	type="bzip"; uncomp="bunzip -dc"; ;; 
	*.bz2)
	type="bzip2"; uncomp="bunzip2 -dc"; ;; 
	*.zip)
	type="zip"; uncomp="unzip -d"; ;; 
	*.Z)
	type="compress"; uncomp="uncompress -c"; ;; 
	*)
	type="plaintext"; uncomp="cat"; ;; 
    esac
    [ -d "${i}" ] && echo "Ignoring subdirectory ${i}" && continue	
    echo ""
    echo "Applying ${i} using ${type}: " 
    ${uncomp} ${i} | ${PATCH:-patch} -f -p1 -E -d ${targetdir} 
    if [ $? != 0 ] ; then
        echo "Patch failed!  Please fix $i!"
	exit 1
    fi
done

# Check for rejects...
if [ "`find $targetdir/ '(' -name '*.rej' -o -name '.*.rej' ')' -print`" ] ; then
    echo "Aborting.  Reject files found."
    exit 1
fi

# Remove backup files
find $targetdir/ '(' -name '*.orig' -o -name '.*.orig' ')' -exec rm -f {} \;
(delta, &t->tsc_scale); return now; } static inline void version_update_begin(u32 *version) { /* Explicitly OR with 1 just in case version number gets out of sync. */ *version = (*version + 1) | 1; wmb(); } static inline void version_update_end(u32 *version) { wmb(); (*version)++; } static inline void __update_dom_time(struct vcpu *v) { struct cpu_time *t = &cpu_time[smp_processor_id()]; struct vcpu_time_info *u = &v->domain->shared_info->vcpu_time[v->vcpu_id]; version_update_begin(&u->version); u->tsc_timestamp = t->local_tsc_stamp; u->system_time = t->stime_local_stamp; u->tsc_to_system_mul = t->tsc_scale.mul_frac; u->tsc_shift = (s8)t->tsc_scale.shift; version_update_end(&u->version); } void update_dom_time(struct vcpu *v) { if ( v->domain->shared_info->vcpu_time[v->vcpu_id].tsc_timestamp != cpu_time[smp_processor_id()].local_tsc_stamp ) __update_dom_time(v); } /* Set clock to after 00:00:00 UTC, 1 January, 1970. */ void do_settime(unsigned long secs, unsigned long nsecs, u64 system_time_base) { u64 x; u32 y, _wc_sec, _wc_nsec; struct domain *d; shared_info_t *s; x = (secs * 1000000000ULL) + (u64)nsecs - system_time_base; y = do_div(x, 1000000000); wc_sec = _wc_sec = (u32)x; wc_nsec = _wc_nsec = (u32)y; read_lock(&domlist_lock); spin_lock(&wc_lock); for_each_domain ( d ) { s = d->shared_info; version_update_begin(&s->wc_version); s->wc_sec = _wc_sec; s->wc_nsec = _wc_nsec; version_update_end(&s->wc_version); } spin_unlock(&wc_lock); read_unlock(&domlist_lock); } void init_domain_time(struct domain *d) { spin_lock(&wc_lock); version_update_begin(&d->shared_info->wc_version); d->shared_info->wc_sec = wc_sec; d->shared_info->wc_nsec = wc_nsec; version_update_end(&d->shared_info->wc_version); spin_unlock(&wc_lock); } static void local_time_calibration(void *unused) { unsigned int cpu = smp_processor_id(); /* * System timestamps, extrapolated from local and master oscillators, * taken during this calibration and the previous calibration. */ s_time_t prev_local_stime, curr_local_stime; s_time_t prev_master_stime, curr_master_stime; /* TSC timestamps taken during this calibration and prev calibration. */ u64 prev_tsc, curr_tsc; /* * System time and TSC ticks elapsed during the previous calibration * 'epoch'. These values are down-shifted to fit in 32 bits. */ u64 stime_elapsed64, tsc_elapsed64; u32 stime_elapsed32, tsc_elapsed32; /* The accumulated error in the local estimate. */ u64 local_stime_err; /* Error correction to slow down a fast local clock. */ u32 error_factor = 0; /* Calculated TSC shift to ensure 32-bit scale multiplier. */ int tsc_shift = 0; /* The overall calibration scale multiplier. */ u32 calibration_mul_frac; prev_tsc = cpu_time[cpu].local_tsc_stamp; prev_local_stime = cpu_time[cpu].stime_local_stamp; prev_master_stime = cpu_time[cpu].stime_master_stamp; /* Disable IRQs to get 'instantaneous' current timestamps. */ local_irq_disable(); rdtscll(curr_tsc); curr_local_stime = get_s_time(); curr_master_stime = read_platform_stime(); local_irq_enable(); #if 0 printk("PRE%d: tsc=%lld stime=%lld master=%lld\n", cpu, prev_tsc, prev_local_stime, prev_master_stime); printk("CUR%d: tsc=%lld stime=%lld master=%lld -> %lld\n", cpu, curr_tsc, curr_local_stime, curr_master_stime, curr_master_stime - curr_local_stime); #endif /* Local time warps forward if it lags behind master time. */ if ( curr_local_stime < curr_master_stime ) curr_local_stime = curr_master_stime; stime_elapsed64 = curr_master_stime - prev_master_stime; tsc_elapsed64 = curr_tsc - prev_tsc; /* * Weirdness can happen if we lose sync with the platform timer. * We could be smarter here: resync platform timer with local timer? */ if ( ((s64)stime_elapsed64 < (EPOCH / 2)) ) goto out; /* * Calculate error-correction factor. This only slows down a fast local * clock (slow clocks are warped forwards). The scale factor is clamped * to >= 0.5. */ if ( curr_local_stime != curr_master_stime ) { local_stime_err = curr_local_stime - curr_master_stime; if ( local_stime_err > EPOCH ) local_stime_err = EPOCH; error_factor = div_frac(EPOCH, EPOCH + (u32)local_stime_err); } /* * We require 0 < stime_elapsed < 2^31. * This allows us to binary shift a 32-bit tsc_elapsed such that: * stime_elapsed < tsc_elapsed <= 2*stime_elapsed */ while ( ((u32)stime_elapsed64 != stime_elapsed64) || ((s32)stime_elapsed64 < 0) ) { stime_elapsed64 >>= 1; tsc_elapsed64 >>= 1; } /* stime_master_diff now fits in a 32-bit word. */ stime_elapsed32 = (u32)stime_elapsed64; /* tsc_elapsed <= 2*stime_elapsed */ while ( tsc_elapsed64 > (stime_elapsed32 * 2) ) { tsc_elapsed64 >>= 1; tsc_shift--; } /* Local difference must now fit in 32 bits. */ ASSERT((u32)tsc_elapsed64 == tsc_elapsed64); tsc_elapsed32 = (u32)tsc_elapsed64; /* tsc_elapsed > stime_elapsed */ ASSERT(tsc_elapsed32 != 0); while ( tsc_elapsed32 <= stime_elapsed32 ) { tsc_elapsed32 <<= 1; tsc_shift++; } calibration_mul_frac = div_frac(stime_elapsed32, tsc_elapsed32); if ( error_factor != 0 ) calibration_mul_frac = mul_frac(calibration_mul_frac, error_factor); #if 0 printk("---%d: %08x %08x %d\n", cpu, error_factor, calibration_mul_frac, tsc_shift); #endif /* Record new timestamp information. */ cpu_time[cpu].tsc_scale.mul_frac = calibration_mul_frac; cpu_time[cpu].tsc_scale.shift = tsc_shift; cpu_time[cpu].local_tsc_stamp = curr_tsc; cpu_time[cpu].stime_local_stamp = curr_local_stime; cpu_time[cpu].stime_master_stamp = curr_master_stime; out: set_ac_timer(&cpu_time[cpu].calibration_timer, NOW() + EPOCH); if ( cpu == 0 ) platform_time_calibration(); } void init_percpu_time(void) { unsigned int cpu = smp_processor_id(); unsigned long flags; s_time_t now; local_irq_save(flags); rdtscll(cpu_time[cpu].local_tsc_stamp); now = (cpu == 0) ? 0 : read_platform_stime(); local_irq_restore(flags); cpu_time[cpu].stime_master_stamp = now; cpu_time[cpu].stime_local_stamp = now; init_ac_timer(&cpu_time[cpu].calibration_timer, local_time_calibration, NULL, cpu); set_ac_timer(&cpu_time[cpu].calibration_timer, NOW() + EPOCH); } /* Late init function (after all CPUs are booted). */ int __init init_xen_time(void) { wc_sec = get_cmos_time(); local_irq_disable(); init_percpu_time(); stime_platform_stamp = 0; init_platform_timer(); local_irq_enable(); return 0; } /* Early init function. */ void __init early_time_init(void) { u64 tmp = calibrate_boot_tsc(); set_time_scale(&cpu_time[0].tsc_scale, tmp); do_div(tmp, 1000); cpu_khz = (unsigned long)tmp; printk("Detected %lu.%03lu MHz processor.\n", cpu_khz / 1000, cpu_khz % 1000); setup_irq(0, &irq0); } /* * Local variables: * mode: C * c-set-style: "BSD" * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: */