ia64/xen-unstable

changeset 14467:0611355e0df2

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>
author kfraser@localhost.localdomain
date Mon Mar 19 13:48:00 2007 +0000 (2007-03-19)
parents 9b9aa9d6603b
children bbda47597917
files xen/arch/x86/hvm/hpet.c
line diff
     1.1 --- a/xen/arch/x86/hvm/hpet.c	Mon Mar 19 13:44:31 2007 +0000
     1.2 +++ b/xen/arch/x86/hvm/hpet.c	Mon Mar 19 13:48:00 2007 +0000
     1.3 @@ -29,6 +29,10 @@
     1.4  #define S_TO_NS  1000000000ULL           /* 1s  = 10^9  ns */
     1.5  #define S_TO_FS  1000000000000000ULL     /* 1s  = 10^15 fs */
     1.6  
     1.7 +/* Frequency_of_TSC / frequency_of_HPET = 32 */
     1.8 +#define TSC_PER_HPET_TICK 32
     1.9 +#define guest_time_hpet(v) (hvm_get_guest_time(v) / TSC_PER_HPET_TICK)
    1.10 +
    1.11  #define HPET_ID         0x000
    1.12  #define HPET_PERIOD     0x004
    1.13  #define HPET_CFG        0x010
    1.14 @@ -67,7 +71,9 @@
    1.15  #define HPET_TN_INT_ROUTE_CAP_MASK (0xffffffffULL \
    1.16                      << HPET_TN_INT_ROUTE_CAP_SHIFT)
    1.17  
    1.18 -#define hpet_tick_to_ns(h, tick) ((s_time_t)(tick)*S_TO_NS/h->tsc_freq)
    1.19 +#define hpet_tick_to_ns(h, tick) ((s_time_t)(tick)* \
    1.20 +                                  (S_TO_NS*TSC_PER_HPET_TICK)/h->tsc_freq)
    1.21 +
    1.22  #define timer_config(h, n)       (h->hpet.timers[n].config)
    1.23  #define timer_enabled(h, n)      (timer_config(h, n) & HPET_TN_ENABLE)
    1.24  #define timer_is_periodic(h, n)  (timer_config(h, n) & HPET_TN_PERIODIC)
    1.25 @@ -108,7 +114,7 @@ static inline int hpet_check_access_leng
    1.26  static inline uint64_t hpet_read_maincounter(HPETState *h)
    1.27  {
    1.28      if ( hpet_enabled(h) )
    1.29 -        return hvm_get_guest_time(h->vcpu) + h->mc_offset;
    1.30 +        return guest_time_hpet(h->vcpu) + h->mc_offset;
    1.31      else 
    1.32          return h->hpet.mc64;
    1.33  }
    1.34 @@ -144,7 +150,7 @@ static void hpet_stop_timer(HPETState *h
    1.35  
    1.36  /* the number of HPET tick that stands for
    1.37   * 1/(2^10) second, namely, 0.9765625 milliseconds */
    1.38 -#define  HPET_TINY_TIME_SPAN  (h->tsc_freq >> 10)
    1.39 +#define  HPET_TINY_TIME_SPAN  ((h->tsc_freq >> 10) / TSC_PER_HPET_TICK)
    1.40  
    1.41  static void hpet_set_timer(HPETState *h, unsigned int tn)
    1.42  {
    1.43 @@ -225,14 +231,14 @@ static void hpet_write(
    1.44          if ( !(old_val & HPET_CFG_ENABLE) && (new_val & HPET_CFG_ENABLE) )
    1.45          {
    1.46              /* Enable main counter and interrupt generation. */
    1.47 -            h->mc_offset = h->hpet.mc64 - hvm_get_guest_time(h->vcpu);
    1.48 +            h->mc_offset = h->hpet.mc64 - guest_time_hpet(h->vcpu);
    1.49              for ( i = 0; i < HPET_TIMER_NUM; i++ )
    1.50                  hpet_set_timer(h, i); 
    1.51          }
    1.52          else if ( (old_val & HPET_CFG_ENABLE) && !(new_val & HPET_CFG_ENABLE) )
    1.53          {
    1.54              /* Halt main counter and disable interrupt generation. */
    1.55 -            h->hpet.mc64 = h->mc_offset + hvm_get_guest_time(h->vcpu);
    1.56 +            h->hpet.mc64 = h->mc_offset + guest_time_hpet(h->vcpu);
    1.57              for ( i = 0; i < HPET_TIMER_NUM; i++ )
    1.58                  hpet_stop_timer(h, i);
    1.59          }
    1.60 @@ -384,7 +390,7 @@ static int hpet_save(struct domain *d, h
    1.61      HPETState *hp = &d->arch.hvm_domain.pl_time.vhpet;
    1.62  
    1.63      /* Write the proper value into the main counter */
    1.64 -    hp->hpet.mc64 = hp->mc_offset + hvm_get_guest_time(hp->vcpu);
    1.65 +    hp->hpet.mc64 = hp->mc_offset + guest_time_hpet(hp->vcpu);
    1.66  
    1.67      /* Save the HPET registers */
    1.68      return hvm_save_entry(HPET, 0, h, &hp->hpet);
    1.69 @@ -400,7 +406,7 @@ static int hpet_load(struct domain *d, h
    1.70          return -EINVAL;
    1.71      
    1.72      /* Recalculate the offset between the main counter and guest time */
    1.73 -    hp->mc_offset = hp->hpet.mc64 - hvm_get_guest_time(hp->vcpu);
    1.74 +    hp->mc_offset = hp->hpet.mc64 - guest_time_hpet(hp->vcpu);
    1.75                  
    1.76      /* Restart the timers */
    1.77      for ( i = 0; i < HPET_TIMER_NUM; i++ )
    1.78 @@ -425,8 +431,8 @@ void hpet_init(struct vcpu *v)
    1.79      h->hpet.capability = 0x8086A201ULL;
    1.80  
    1.81      /* This is the number of femptoseconds per HPET tick. */
    1.82 -    /* Here we define HPET's frequency to be the same as the TSC's. */
    1.83 -    h->hpet.capability |= ((S_TO_FS/h->tsc_freq) << 32);
    1.84 +    /* Here we define HPET's frequency to be 1/32 of the TSC's */
    1.85 +    h->hpet.capability |= ((S_TO_FS*TSC_PER_HPET_TICK/h->tsc_freq) << 32);
    1.86  
    1.87      for ( i = 0; i < HPET_TIMER_NUM; i++ )
    1.88      {