diff options
author | kfraser@localhost.localdomain <kfraser@localhost.localdomain> | 2007-03-19 13:48:00 +0000 |
---|---|---|
committer | kfraser@localhost.localdomain <kfraser@localhost.localdomain> | 2007-03-19 13:48:00 +0000 |
commit | f8b68fe2dad1e6bf4f55483dd023561d3ee008cb (patch) | |
tree | b52472f7c81308c900445a76db8ef1cebc50e949 /xen/arch/x86/hvm/hpet.c | |
parent | 70fe04c768682d517d887d27f7dc195e616a38f5 (diff) | |
download | xen-f8b68fe2dad1e6bf4f55483dd023561d3ee008cb.tar.gz xen-f8b68fe2dad1e6bf4f55483dd023561d3ee008cb.tar.bz2 xen-f8b68fe2dad1e6bf4f55483dd023561d3ee008cb.zip |
hvm: Lower HPET frequency to 1/32 of the TSC.
The frequency of HPET device model is defined to be the same as TSC's,
but this doesn't work well with calibrate_tsc_hpet() in Linux kernel
2.6.16-33, causing some IA32 Linux HVM guests to failt o boot.
Calibrate_tsc_hpet() tries to figure out how many HPET ticks a TSC
cycle equals; it magnifies the result by scale of 2^32, trying to get
a more accurate result since it assumes the frequency of HPET in real
world is usually less than 1/100 of TSC, so the result of "(2^32 *
hpet_freq) / tsc_freq" may exceed 32bits, then a "divide error
(overflow)" would occur!
The result doesn't overflow every time because hpet_freq/tsc_freq may
less than 1.0 due to the little inaccuracy in the implementation of
HVM timer virtualization.
Signed-off-by: Dexuan Cui <dexuan.cui@intel.com>
Diffstat (limited to 'xen/arch/x86/hvm/hpet.c')
-rw-r--r-- | xen/arch/x86/hvm/hpet.c | 24 |
1 files changed, 15 insertions, 9 deletions
diff --git a/xen/arch/x86/hvm/hpet.c b/xen/arch/x86/hvm/hpet.c index 419a886eba..b3fb6ffe29 100644 --- a/xen/arch/x86/hvm/hpet.c +++ b/xen/arch/x86/hvm/hpet.c @@ -29,6 +29,10 @@ #define S_TO_NS 1000000000ULL /* 1s = 10^9 ns */ #define S_TO_FS 1000000000000000ULL /* 1s = 10^15 fs */ +/* Frequency_of_TSC / frequency_of_HPET = 32 */ +#define TSC_PER_HPET_TICK 32 +#define guest_time_hpet(v) (hvm_get_guest_time(v) / TSC_PER_HPET_TICK) + #define HPET_ID 0x000 #define HPET_PERIOD 0x004 #define HPET_CFG 0x010 @@ -67,7 +71,9 @@ #define HPET_TN_INT_ROUTE_CAP_MASK (0xffffffffULL \ << HPET_TN_INT_ROUTE_CAP_SHIFT) -#define hpet_tick_to_ns(h, tick) ((s_time_t)(tick)*S_TO_NS/h->tsc_freq) +#define hpet_tick_to_ns(h, tick) ((s_time_t)(tick)* \ + (S_TO_NS*TSC_PER_HPET_TICK)/h->tsc_freq) + #define timer_config(h, n) (h->hpet.timers[n].config) #define timer_enabled(h, n) (timer_config(h, n) & HPET_TN_ENABLE) #define timer_is_periodic(h, n) (timer_config(h, n) & HPET_TN_PERIODIC) @@ -108,7 +114,7 @@ static inline int hpet_check_access_length( static inline uint64_t hpet_read_maincounter(HPETState *h) { if ( hpet_enabled(h) ) - return hvm_get_guest_time(h->vcpu) + h->mc_offset; + return guest_time_hpet(h->vcpu) + h->mc_offset; else return h->hpet.mc64; } @@ -144,7 +150,7 @@ static void hpet_stop_timer(HPETState *h, unsigned int tn) /* the number of HPET tick that stands for * 1/(2^10) second, namely, 0.9765625 milliseconds */ -#define HPET_TINY_TIME_SPAN (h->tsc_freq >> 10) +#define HPET_TINY_TIME_SPAN ((h->tsc_freq >> 10) / TSC_PER_HPET_TICK) static void hpet_set_timer(HPETState *h, unsigned int tn) { @@ -225,14 +231,14 @@ static void hpet_write( if ( !(old_val & HPET_CFG_ENABLE) && (new_val & HPET_CFG_ENABLE) ) { /* Enable main counter and interrupt generation. */ - h->mc_offset = h->hpet.mc64 - hvm_get_guest_time(h->vcpu); + h->mc_offset = h->hpet.mc64 - guest_time_hpet(h->vcpu); for ( i = 0; i < HPET_TIMER_NUM; i++ ) hpet_set_timer(h, i); } else if ( (old_val & HPET_CFG_ENABLE) && !(new_val & HPET_CFG_ENABLE) ) { /* Halt main counter and disable interrupt generation. */ - h->hpet.mc64 = h->mc_offset + hvm_get_guest_time(h->vcpu); + h->hpet.mc64 = h->mc_offset + guest_time_hpet(h->vcpu); for ( i = 0; i < HPET_TIMER_NUM; i++ ) hpet_stop_timer(h, i); } @@ -384,7 +390,7 @@ static int hpet_save(struct domain *d, hvm_domain_context_t *h) HPETState *hp = &d->arch.hvm_domain.pl_time.vhpet; /* Write the proper value into the main counter */ - hp->hpet.mc64 = hp->mc_offset + hvm_get_guest_time(hp->vcpu); + hp->hpet.mc64 = hp->mc_offset + guest_time_hpet(hp->vcpu); /* Save the HPET registers */ return hvm_save_entry(HPET, 0, h, &hp->hpet); @@ -400,7 +406,7 @@ static int hpet_load(struct domain *d, hvm_domain_context_t *h) return -EINVAL; /* Recalculate the offset between the main counter and guest time */ - hp->mc_offset = hp->hpet.mc64 - hvm_get_guest_time(hp->vcpu); + hp->mc_offset = hp->hpet.mc64 - guest_time_hpet(hp->vcpu); /* Restart the timers */ for ( i = 0; i < HPET_TIMER_NUM; i++ ) @@ -425,8 +431,8 @@ void hpet_init(struct vcpu *v) h->hpet.capability = 0x8086A201ULL; /* This is the number of femptoseconds per HPET tick. */ - /* Here we define HPET's frequency to be the same as the TSC's. */ - h->hpet.capability |= ((S_TO_FS/h->tsc_freq) << 32); + /* Here we define HPET's frequency to be 1/32 of the TSC's */ + h->hpet.capability |= ((S_TO_FS*TSC_PER_HPET_TICK/h->tsc_freq) << 32); for ( i = 0; i < HPET_TIMER_NUM; i++ ) { |