diff options
author | Jan Beulich <none@none> | 2012-09-12 13:24:28 +0200 |
---|---|---|
committer | Jan Beulich <none@none> | 2012-09-12 13:24:28 +0200 |
commit | b314cd733413babc5978b819793ad5c77f094adf (patch) | |
tree | 99c53f795049072267f8d4ca89abe1843afd0f4b /xen/arch/x86/hvm/rtc.c | |
parent | 6da1e2af7d2bc70d697fcc605745df038b078eb6 (diff) | |
download | xen-b314cd733413babc5978b819793ad5c77f094adf.tar.gz xen-b314cd733413babc5978b819793ad5c77f094adf.tar.bz2 xen-b314cd733413babc5978b819793ad5c77f094adf.zip |
Revert 25843:51090fe1ab97 (x86/HVM: assorted RTC emulation adjustments)
This was found to cause RHEL6 HVM guests to hang during shutdown.
Diffstat (limited to 'xen/arch/x86/hvm/rtc.c')
-rw-r--r-- | xen/arch/x86/hvm/rtc.c | 111 |
1 files changed, 50 insertions, 61 deletions
diff --git a/xen/arch/x86/hvm/rtc.c b/xen/arch/x86/hvm/rtc.c index f34a84e0dc..ea5179a4b4 100644 --- a/xen/arch/x86/hvm/rtc.c +++ b/xen/arch/x86/hvm/rtc.c @@ -50,24 +50,11 @@ static void rtc_set_time(RTCState *s); static inline int from_bcd(RTCState *s, int a); static inline int convert_hour(RTCState *s, int hour); -static void rtc_toggle_irq(RTCState *s) -{ - struct domain *d = vrtc_domain(s); - - ASSERT(spin_is_locked(&s->lock)); - s->hw.cmos_data[RTC_REG_C] |= RTC_IRQF; - hvm_isa_irq_deassert(d, RTC_IRQ); - hvm_isa_irq_assert(d, RTC_IRQ); -} - -void rtc_periodic_interrupt(void *opaque) +static void rtc_periodic_cb(struct vcpu *v, void *opaque) { RTCState *s = opaque; - spin_lock(&s->lock); - s->hw.cmos_data[RTC_REG_C] |= RTC_PF; - if ( s->hw.cmos_data[RTC_REG_B] & RTC_PIE ) - rtc_toggle_irq(s); + s->hw.cmos_data[RTC_REG_C] |= 0xc0; spin_unlock(&s->lock); } @@ -81,25 +68,19 @@ static void rtc_timer_update(RTCState *s) ASSERT(spin_is_locked(&s->lock)); period_code = s->hw.cmos_data[RTC_REG_A] & RTC_RATE_SELECT; - switch ( s->hw.cmos_data[RTC_REG_A] & RTC_DIV_CTL ) + if ( (period_code != 0) && (s->hw.cmos_data[RTC_REG_B] & RTC_PIE) ) { - case RTC_REF_CLCK_32KHZ: - if ( (period_code != 0) && (period_code <= 2) ) + if ( period_code <= 2 ) period_code += 7; - /* fall through */ - case RTC_REF_CLCK_1MHZ: - case RTC_REF_CLCK_4MHZ: - if ( period_code != 0 ) - { - period = 1 << (period_code - 1); /* period in 32 Khz cycles */ - period = DIV_ROUND(period * 1000000000ULL, 32768); /* in ns */ - create_periodic_time(v, &s->pt, period, period, RTC_IRQ, NULL, s); - break; - } - /* fall through */ - default: + + period = 1 << (period_code - 1); /* period in 32 Khz cycles */ + period = DIV_ROUND((period * 1000000000ULL), 32768); /* period in ns */ + create_periodic_time(v, &s->pt, period, period, RTC_IRQ, + rtc_periodic_cb, s); + } + else + { destroy_periodic_time(&s->pt); - break; } } @@ -121,7 +102,7 @@ static void check_update_timer(RTCState *s) guest_usec = get_localtime_us(d) % USEC_PER_SEC; if (guest_usec >= (USEC_PER_SEC - 244)) { - /* RTC is in update cycle */ + /* RTC is in update cycle when enabling UIE */ s->hw.cmos_data[RTC_REG_A] |= RTC_UIP; next_update_time = (USEC_PER_SEC - guest_usec) * NS_PER_USEC; expire_time = NOW() + next_update_time; @@ -163,6 +144,7 @@ static void rtc_update_timer(void *opaque) static void rtc_update_timer2(void *opaque) { RTCState *s = opaque; + struct domain *d = vrtc_domain(s); spin_lock(&s->lock); if (!(s->hw.cmos_data[RTC_REG_B] & RTC_SET)) @@ -170,7 +152,11 @@ static void rtc_update_timer2(void *opaque) s->hw.cmos_data[RTC_REG_C] |= RTC_UF; s->hw.cmos_data[RTC_REG_A] &= ~RTC_UIP; if ((s->hw.cmos_data[RTC_REG_B] & RTC_UIE)) - rtc_toggle_irq(s); + { + s->hw.cmos_data[RTC_REG_C] |= RTC_IRQF; + hvm_isa_irq_deassert(d, RTC_IRQ); + hvm_isa_irq_assert(d, RTC_IRQ); + } check_update_timer(s); } spin_unlock(&s->lock); @@ -189,18 +175,21 @@ static void alarm_timer_update(RTCState *s) stop_timer(&s->alarm_timer); - if ( !(s->hw.cmos_data[RTC_REG_B] & RTC_SET) ) + if ((s->hw.cmos_data[RTC_REG_B] & RTC_AIE) && + !(s->hw.cmos_data[RTC_REG_B] & RTC_SET)) { s->current_tm = gmtime(get_localtime(d)); rtc_copy_date(s); alarm_sec = from_bcd(s, s->hw.cmos_data[RTC_SECONDS_ALARM]); alarm_min = from_bcd(s, s->hw.cmos_data[RTC_MINUTES_ALARM]); - alarm_hour = convert_hour(s, s->hw.cmos_data[RTC_HOURS_ALARM]); + alarm_hour = from_bcd(s, s->hw.cmos_data[RTC_HOURS_ALARM]); + alarm_hour = convert_hour(s, alarm_hour); cur_sec = from_bcd(s, s->hw.cmos_data[RTC_SECONDS]); cur_min = from_bcd(s, s->hw.cmos_data[RTC_MINUTES]); - cur_hour = convert_hour(s, s->hw.cmos_data[RTC_HOURS]); + cur_hour = from_bcd(s, s->hw.cmos_data[RTC_HOURS]); + cur_hour = convert_hour(s, cur_hour); next_update_time = USEC_PER_SEC - (get_localtime_us(d) % USEC_PER_SEC); next_update_time = next_update_time * NS_PER_USEC + NOW(); @@ -354,6 +343,7 @@ static void alarm_timer_update(RTCState *s) static void rtc_alarm_cb(void *opaque) { RTCState *s = opaque; + struct domain *d = vrtc_domain(s); spin_lock(&s->lock); if (!(s->hw.cmos_data[RTC_REG_B] & RTC_SET)) @@ -361,7 +351,11 @@ static void rtc_alarm_cb(void *opaque) s->hw.cmos_data[RTC_REG_C] |= RTC_AF; /* alarm interrupt */ if (s->hw.cmos_data[RTC_REG_B] & RTC_AIE) - rtc_toggle_irq(s); + { + s->hw.cmos_data[RTC_REG_C] |= RTC_IRQF; + hvm_isa_irq_deassert(d, RTC_IRQ); + hvm_isa_irq_assert(d, RTC_IRQ); + } alarm_timer_update(s); } spin_unlock(&s->lock); @@ -371,7 +365,7 @@ static int rtc_ioport_write(void *opaque, uint32_t addr, uint32_t data) { RTCState *s = opaque; struct domain *d = vrtc_domain(s); - uint32_t orig, mask; + uint32_t orig; spin_lock(&s->lock); @@ -423,7 +417,7 @@ static int rtc_ioport_write(void *opaque, uint32_t addr, uint32_t data) /* set mode: reset UIP mode */ s->hw.cmos_data[RTC_REG_A] &= ~RTC_UIP; /* adjust cmos before stopping */ - if (!(orig & RTC_SET)) + if (!(s->hw.cmos_data[RTC_REG_B] & RTC_SET)) { s->current_tm = gmtime(get_localtime(d)); rtc_copy_date(s); @@ -432,26 +426,22 @@ static int rtc_ioport_write(void *opaque, uint32_t addr, uint32_t data) else { /* if disabling set mode, update the time */ - if ( orig & RTC_SET ) + if ( s->hw.cmos_data[RTC_REG_B] & RTC_SET ) rtc_set_time(s); } - /* - * If the interrupt is already set when the interrupt becomes - * enabled, raise an interrupt immediately. - * NB: RTC_{A,P,U}IE == RTC_{A,P,U}F respectively. - */ - for ( mask = RTC_UIE; mask <= RTC_PIE; mask <<= 1 ) - if ( (data & mask) && !(orig & mask) && - (s->hw.cmos_data[RTC_REG_C] & mask) ) + /* if the interrupt is already set when the interrupt become + * enabled, raise an interrupt immediately*/ + if ((data & RTC_UIE) && !(s->hw.cmos_data[RTC_REG_B] & RTC_UIE)) + if (s->hw.cmos_data[RTC_REG_C] & RTC_UF) { - rtc_toggle_irq(s); - break; + hvm_isa_irq_deassert(d, RTC_IRQ); + hvm_isa_irq_assert(d, RTC_IRQ); } s->hw.cmos_data[RTC_REG_B] = data; - if ( (data ^ orig) & RTC_SET ) - check_update_timer(s); - if ( (data ^ orig) & (RTC_24H | RTC_DM_BINARY | RTC_SET) ) - alarm_timer_update(s); + if ( (data ^ orig) & RTC_PIE ) + rtc_timer_update(s); + check_update_timer(s); + alarm_timer_update(s); break; case RTC_REG_C: case RTC_REG_D: @@ -466,7 +456,7 @@ static int rtc_ioport_write(void *opaque, uint32_t addr, uint32_t data) static inline int to_bcd(RTCState *s, int a) { - if ( s->hw.cmos_data[RTC_REG_B] & RTC_DM_BINARY ) + if ( s->hw.cmos_data[RTC_REG_B] & 0x04 ) return a; else return ((a / 10) << 4) | (a % 10); @@ -474,7 +464,7 @@ static inline int to_bcd(RTCState *s, int a) static inline int from_bcd(RTCState *s, int a) { - if ( s->hw.cmos_data[RTC_REG_B] & RTC_DM_BINARY ) + if ( s->hw.cmos_data[RTC_REG_B] & 0x04 ) return a; else return ((a >> 4) * 10) + (a & 0x0f); @@ -482,14 +472,12 @@ static inline int from_bcd(RTCState *s, int a) /* Hours in 12 hour mode are in 1-12 range, not 0-11. * So we need convert it before using it*/ -static inline int convert_hour(RTCState *s, int raw) +static inline int convert_hour(RTCState *s, int hour) { - int hour = from_bcd(s, raw & 0x7f); - if (!(s->hw.cmos_data[RTC_REG_B] & RTC_24H)) { hour %= 12; - if (raw & 0x80) + if (s->hw.cmos_data[RTC_HOURS] & 0x80) hour += 12; } return hour; @@ -508,7 +496,8 @@ static void rtc_set_time(RTCState *s) tm->tm_sec = from_bcd(s, s->hw.cmos_data[RTC_SECONDS]); tm->tm_min = from_bcd(s, s->hw.cmos_data[RTC_MINUTES]); - tm->tm_hour = convert_hour(s, s->hw.cmos_data[RTC_HOURS]); + tm->tm_hour = from_bcd(s, s->hw.cmos_data[RTC_HOURS] & 0x7f); + tm->tm_hour = convert_hour(s, tm->tm_hour); tm->tm_wday = from_bcd(s, s->hw.cmos_data[RTC_DAY_OF_WEEK]); tm->tm_mday = from_bcd(s, s->hw.cmos_data[RTC_DAY_OF_MONTH]); tm->tm_mon = from_bcd(s, s->hw.cmos_data[RTC_MONTH]) - 1; |