* [PATCH, v3] x86/HVM: assorted RTC emulation adjustments
@ 2012-09-07 13:01 Jan Beulich
2012-09-11 7:23 ` Keir Fraser
0 siblings, 1 reply; 2+ messages in thread
From: Jan Beulich @ 2012-09-07 13:01 UTC (permalink / raw)
To: xen-devel
[-- Attachment #1: Type: text/plain, Size: 11478 bytes --]
- don't look at RTC_PIE in rtc_timer_update(), and hence don't call the
function on REG_B writes at all
- only call alarm_timer_update() on REG_B writes when relevant bits
change
- only call check_update_timer() on REG_B writes when SET changes
- instead properly handle AF and PF when the guest is not also setting
AIE/PIE respectively (for UF this was already the case, only a
comment was slightly inaccurate)
- raise the RTC IRQ not only when UIE gets set while UF was already
set, but generalize this to cover AIE and PIE as well
- properly mask off bit 7 when retrieving the hour values in
alarm_timer_update(), and properly use RTC_HOURS_ALARM's bit 7 when
converting from 12- to 24-hour value
- also handle the two other possible clock bases
- use RTC_* names in a couple of places where literal numbers were used
so far
Signed-off-by: Jan Beulich <jbeulich@suse.com>
--- a/xen/arch/x86/hvm/rtc.c
+++ b/xen/arch/x86/hvm/rtc.c
@@ -50,11 +50,24 @@ 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_periodic_cb(struct vcpu *v, void *opaque)
+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)
{
RTCState *s = opaque;
+
spin_lock(&s->lock);
- s->hw.cmos_data[RTC_REG_C] |= 0xc0;
+ s->hw.cmos_data[RTC_REG_C] |= RTC_PF;
+ if ( s->hw.cmos_data[RTC_REG_B] & RTC_PIE )
+ rtc_toggle_irq(s);
spin_unlock(&s->lock);
}
@@ -68,19 +81,25 @@ 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;
- if ( (period_code != 0) && (s->hw.cmos_data[RTC_REG_B] & RTC_PIE) )
+ switch ( s->hw.cmos_data[RTC_REG_A] & RTC_DIV_CTL )
{
- if ( period_code <= 2 )
+ case RTC_REF_CLCK_32KHZ:
+ if ( (period_code != 0) && (period_code <= 2) )
period_code += 7;
-
- 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
- {
+ /* 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:
destroy_periodic_time(&s->pt);
+ break;
}
}
@@ -102,7 +121,7 @@ static void check_update_timer(RTCState
guest_usec = get_localtime_us(d) % USEC_PER_SEC;
if (guest_usec >= (USEC_PER_SEC - 244))
{
- /* RTC is in update cycle when enabling UIE */
+ /* RTC is in update cycle */
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;
@@ -144,7 +163,6 @@ static void rtc_update_timer(void *opaqu
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))
@@ -152,11 +170,7 @@ static void rtc_update_timer2(void *opaq
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))
- {
- s->hw.cmos_data[RTC_REG_C] |= RTC_IRQF;
- hvm_isa_irq_deassert(d, RTC_IRQ);
- hvm_isa_irq_assert(d, RTC_IRQ);
- }
+ rtc_toggle_irq(s);
check_update_timer(s);
}
spin_unlock(&s->lock);
@@ -175,21 +189,18 @@ static void alarm_timer_update(RTCState
stop_timer(&s->alarm_timer);
- if ((s->hw.cmos_data[RTC_REG_B] & RTC_AIE) &&
- !(s->hw.cmos_data[RTC_REG_B] & RTC_SET))
+ if ( !(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 = from_bcd(s, s->hw.cmos_data[RTC_HOURS_ALARM]);
- alarm_hour = convert_hour(s, alarm_hour);
+ alarm_hour = convert_hour(s, s->hw.cmos_data[RTC_HOURS_ALARM]);
cur_sec = from_bcd(s, s->hw.cmos_data[RTC_SECONDS]);
cur_min = from_bcd(s, s->hw.cmos_data[RTC_MINUTES]);
- cur_hour = from_bcd(s, s->hw.cmos_data[RTC_HOURS]);
- cur_hour = convert_hour(s, cur_hour);
+ cur_hour = convert_hour(s, s->hw.cmos_data[RTC_HOURS]);
next_update_time = USEC_PER_SEC - (get_localtime_us(d) % USEC_PER_SEC);
next_update_time = next_update_time * NS_PER_USEC + NOW();
@@ -343,7 +354,6 @@ static void alarm_timer_update(RTCState
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))
@@ -351,11 +361,7 @@ 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)
- {
- s->hw.cmos_data[RTC_REG_C] |= RTC_IRQF;
- hvm_isa_irq_deassert(d, RTC_IRQ);
- hvm_isa_irq_assert(d, RTC_IRQ);
- }
+ rtc_toggle_irq(s);
alarm_timer_update(s);
}
spin_unlock(&s->lock);
@@ -365,7 +371,7 @@ static int rtc_ioport_write(void *opaque
{
RTCState *s = opaque;
struct domain *d = vrtc_domain(s);
- uint32_t orig;
+ uint32_t orig, mask;
spin_lock(&s->lock);
@@ -417,7 +423,7 @@ static int rtc_ioport_write(void *opaque
/* set mode: reset UIP mode */
s->hw.cmos_data[RTC_REG_A] &= ~RTC_UIP;
/* adjust cmos before stopping */
- if (!(s->hw.cmos_data[RTC_REG_B] & RTC_SET))
+ if (!(orig & RTC_SET))
{
s->current_tm = gmtime(get_localtime(d));
rtc_copy_date(s);
@@ -426,22 +432,26 @@ static int rtc_ioport_write(void *opaque
else
{
/* if disabling set mode, update the time */
- if ( s->hw.cmos_data[RTC_REG_B] & RTC_SET )
+ if ( orig & RTC_SET )
rtc_set_time(s);
}
- /* 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)
+ /*
+ * 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) )
{
- hvm_isa_irq_deassert(d, RTC_IRQ);
- hvm_isa_irq_assert(d, RTC_IRQ);
+ rtc_toggle_irq(s);
+ break;
}
s->hw.cmos_data[RTC_REG_B] = data;
- if ( (data ^ orig) & RTC_PIE )
- rtc_timer_update(s);
- check_update_timer(s);
- alarm_timer_update(s);
+ if ( (data ^ orig) & RTC_SET )
+ check_update_timer(s);
+ if ( (data ^ orig) & (RTC_24H | RTC_DM_BINARY | RTC_SET) )
+ alarm_timer_update(s);
break;
case RTC_REG_C:
case RTC_REG_D:
@@ -456,7 +466,7 @@ static int rtc_ioport_write(void *opaque
static inline int to_bcd(RTCState *s, int a)
{
- if ( s->hw.cmos_data[RTC_REG_B] & 0x04 )
+ if ( s->hw.cmos_data[RTC_REG_B] & RTC_DM_BINARY )
return a;
else
return ((a / 10) << 4) | (a % 10);
@@ -464,7 +474,7 @@ static inline int to_bcd(RTCState *s, in
static inline int from_bcd(RTCState *s, int a)
{
- if ( s->hw.cmos_data[RTC_REG_B] & 0x04 )
+ if ( s->hw.cmos_data[RTC_REG_B] & RTC_DM_BINARY )
return a;
else
return ((a >> 4) * 10) + (a & 0x0f);
@@ -472,12 +482,14 @@ static inline int from_bcd(RTCState *s,
/* 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 hour)
+static inline int convert_hour(RTCState *s, int raw)
{
+ int hour = from_bcd(s, raw & 0x7f);
+
if (!(s->hw.cmos_data[RTC_REG_B] & RTC_24H))
{
hour %= 12;
- if (s->hw.cmos_data[RTC_HOURS] & 0x80)
+ if (raw & 0x80)
hour += 12;
}
return hour;
@@ -496,8 +508,7 @@ 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 = from_bcd(s, s->hw.cmos_data[RTC_HOURS] & 0x7f);
- tm->tm_hour = convert_hour(s, tm->tm_hour);
+ tm->tm_hour = convert_hour(s, s->hw.cmos_data[RTC_HOURS]);
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;
--- a/xen/arch/x86/hvm/vpt.c
+++ b/xen/arch/x86/hvm/vpt.c
@@ -22,6 +22,7 @@
#include <asm/hvm/vpt.h>
#include <asm/event.h>
#include <asm/apic.h>
+#include <asm/mc146818rtc.h>
#define mode_is(d, name) \
((d)->arch.hvm_domain.params[HVM_PARAM_TIMER_MODE] == HVMPTM_##name)
@@ -218,6 +219,7 @@ void pt_update_irq(struct vcpu *v)
struct periodic_time *pt, *temp, *earliest_pt = NULL;
uint64_t max_lag = -1ULL;
int irq, is_lapic;
+ void *pt_priv;
spin_lock(&v->arch.hvm_vcpu.tm_lock);
@@ -251,13 +253,14 @@ void pt_update_irq(struct vcpu *v)
earliest_pt->irq_issued = 1;
irq = earliest_pt->irq;
is_lapic = (earliest_pt->source == PTSRC_lapic);
+ pt_priv = earliest_pt->priv;
spin_unlock(&v->arch.hvm_vcpu.tm_lock);
if ( is_lapic )
- {
vlapic_set_irq(vcpu_vlapic(v), irq, 0);
- }
+ else if ( irq == RTC_IRQ && pt_priv )
+ rtc_periodic_interrupt(pt_priv);
else
{
hvm_isa_irq_deassert(v->domain, irq);
--- a/xen/include/asm-x86/hvm/vpt.h
+++ b/xen/include/asm-x86/hvm/vpt.h
@@ -181,6 +181,7 @@ void rtc_migrate_timers(struct vcpu *v);
void rtc_deinit(struct domain *d);
void rtc_reset(struct domain *d);
void rtc_update_clock(struct domain *d);
+void rtc_periodic_interrupt(void *);
void pmtimer_init(struct vcpu *v);
void pmtimer_deinit(struct domain *d);
[-- Attachment #2: x86-hvm-rtc.patch --]
[-- Type: text/plain, Size: 11521 bytes --]
x86/HVM: assorted RTC emulation adjustments
- don't look at RTC_PIE in rtc_timer_update(), and hence don't call the
function on REG_B writes at all
- only call alarm_timer_update() on REG_B writes when relevant bits
change
- only call check_update_timer() on REG_B writes when SET changes
- instead properly handle AF and PF when the guest is not also setting
AIE/PIE respectively (for UF this was already the case, only a
comment was slightly inaccurate)
- raise the RTC IRQ not only when UIE gets set while UF was already
set, but generalize this to cover AIE and PIE as well
- properly mask off bit 7 when retrieving the hour values in
alarm_timer_update(), and properly use RTC_HOURS_ALARM's bit 7 when
converting from 12- to 24-hour value
- also handle the two other possible clock bases
- use RTC_* names in a couple of places where literal numbers were used
so far
Signed-off-by: Jan Beulich <jbeulich@suse.com>
--- a/xen/arch/x86/hvm/rtc.c
+++ b/xen/arch/x86/hvm/rtc.c
@@ -50,11 +50,24 @@ 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_periodic_cb(struct vcpu *v, void *opaque)
+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)
{
RTCState *s = opaque;
+
spin_lock(&s->lock);
- s->hw.cmos_data[RTC_REG_C] |= 0xc0;
+ s->hw.cmos_data[RTC_REG_C] |= RTC_PF;
+ if ( s->hw.cmos_data[RTC_REG_B] & RTC_PIE )
+ rtc_toggle_irq(s);
spin_unlock(&s->lock);
}
@@ -68,19 +81,25 @@ 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;
- if ( (period_code != 0) && (s->hw.cmos_data[RTC_REG_B] & RTC_PIE) )
+ switch ( s->hw.cmos_data[RTC_REG_A] & RTC_DIV_CTL )
{
- if ( period_code <= 2 )
+ case RTC_REF_CLCK_32KHZ:
+ if ( (period_code != 0) && (period_code <= 2) )
period_code += 7;
-
- 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
- {
+ /* 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:
destroy_periodic_time(&s->pt);
+ break;
}
}
@@ -102,7 +121,7 @@ static void check_update_timer(RTCState
guest_usec = get_localtime_us(d) % USEC_PER_SEC;
if (guest_usec >= (USEC_PER_SEC - 244))
{
- /* RTC is in update cycle when enabling UIE */
+ /* RTC is in update cycle */
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;
@@ -144,7 +163,6 @@ static void rtc_update_timer(void *opaqu
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))
@@ -152,11 +170,7 @@ static void rtc_update_timer2(void *opaq
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))
- {
- s->hw.cmos_data[RTC_REG_C] |= RTC_IRQF;
- hvm_isa_irq_deassert(d, RTC_IRQ);
- hvm_isa_irq_assert(d, RTC_IRQ);
- }
+ rtc_toggle_irq(s);
check_update_timer(s);
}
spin_unlock(&s->lock);
@@ -175,21 +189,18 @@ static void alarm_timer_update(RTCState
stop_timer(&s->alarm_timer);
- if ((s->hw.cmos_data[RTC_REG_B] & RTC_AIE) &&
- !(s->hw.cmos_data[RTC_REG_B] & RTC_SET))
+ if ( !(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 = from_bcd(s, s->hw.cmos_data[RTC_HOURS_ALARM]);
- alarm_hour = convert_hour(s, alarm_hour);
+ alarm_hour = convert_hour(s, s->hw.cmos_data[RTC_HOURS_ALARM]);
cur_sec = from_bcd(s, s->hw.cmos_data[RTC_SECONDS]);
cur_min = from_bcd(s, s->hw.cmos_data[RTC_MINUTES]);
- cur_hour = from_bcd(s, s->hw.cmos_data[RTC_HOURS]);
- cur_hour = convert_hour(s, cur_hour);
+ cur_hour = convert_hour(s, s->hw.cmos_data[RTC_HOURS]);
next_update_time = USEC_PER_SEC - (get_localtime_us(d) % USEC_PER_SEC);
next_update_time = next_update_time * NS_PER_USEC + NOW();
@@ -343,7 +354,6 @@ static void alarm_timer_update(RTCState
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))
@@ -351,11 +361,7 @@ 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)
- {
- s->hw.cmos_data[RTC_REG_C] |= RTC_IRQF;
- hvm_isa_irq_deassert(d, RTC_IRQ);
- hvm_isa_irq_assert(d, RTC_IRQ);
- }
+ rtc_toggle_irq(s);
alarm_timer_update(s);
}
spin_unlock(&s->lock);
@@ -365,7 +371,7 @@ static int rtc_ioport_write(void *opaque
{
RTCState *s = opaque;
struct domain *d = vrtc_domain(s);
- uint32_t orig;
+ uint32_t orig, mask;
spin_lock(&s->lock);
@@ -417,7 +423,7 @@ static int rtc_ioport_write(void *opaque
/* set mode: reset UIP mode */
s->hw.cmos_data[RTC_REG_A] &= ~RTC_UIP;
/* adjust cmos before stopping */
- if (!(s->hw.cmos_data[RTC_REG_B] & RTC_SET))
+ if (!(orig & RTC_SET))
{
s->current_tm = gmtime(get_localtime(d));
rtc_copy_date(s);
@@ -426,22 +432,26 @@ static int rtc_ioport_write(void *opaque
else
{
/* if disabling set mode, update the time */
- if ( s->hw.cmos_data[RTC_REG_B] & RTC_SET )
+ if ( orig & RTC_SET )
rtc_set_time(s);
}
- /* 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)
+ /*
+ * 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) )
{
- hvm_isa_irq_deassert(d, RTC_IRQ);
- hvm_isa_irq_assert(d, RTC_IRQ);
+ rtc_toggle_irq(s);
+ break;
}
s->hw.cmos_data[RTC_REG_B] = data;
- if ( (data ^ orig) & RTC_PIE )
- rtc_timer_update(s);
- check_update_timer(s);
- alarm_timer_update(s);
+ if ( (data ^ orig) & RTC_SET )
+ check_update_timer(s);
+ if ( (data ^ orig) & (RTC_24H | RTC_DM_BINARY | RTC_SET) )
+ alarm_timer_update(s);
break;
case RTC_REG_C:
case RTC_REG_D:
@@ -456,7 +466,7 @@ static int rtc_ioport_write(void *opaque
static inline int to_bcd(RTCState *s, int a)
{
- if ( s->hw.cmos_data[RTC_REG_B] & 0x04 )
+ if ( s->hw.cmos_data[RTC_REG_B] & RTC_DM_BINARY )
return a;
else
return ((a / 10) << 4) | (a % 10);
@@ -464,7 +474,7 @@ static inline int to_bcd(RTCState *s, in
static inline int from_bcd(RTCState *s, int a)
{
- if ( s->hw.cmos_data[RTC_REG_B] & 0x04 )
+ if ( s->hw.cmos_data[RTC_REG_B] & RTC_DM_BINARY )
return a;
else
return ((a >> 4) * 10) + (a & 0x0f);
@@ -472,12 +482,14 @@ static inline int from_bcd(RTCState *s,
/* 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 hour)
+static inline int convert_hour(RTCState *s, int raw)
{
+ int hour = from_bcd(s, raw & 0x7f);
+
if (!(s->hw.cmos_data[RTC_REG_B] & RTC_24H))
{
hour %= 12;
- if (s->hw.cmos_data[RTC_HOURS] & 0x80)
+ if (raw & 0x80)
hour += 12;
}
return hour;
@@ -496,8 +508,7 @@ 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 = from_bcd(s, s->hw.cmos_data[RTC_HOURS] & 0x7f);
- tm->tm_hour = convert_hour(s, tm->tm_hour);
+ tm->tm_hour = convert_hour(s, s->hw.cmos_data[RTC_HOURS]);
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;
--- a/xen/arch/x86/hvm/vpt.c
+++ b/xen/arch/x86/hvm/vpt.c
@@ -22,6 +22,7 @@
#include <asm/hvm/vpt.h>
#include <asm/event.h>
#include <asm/apic.h>
+#include <asm/mc146818rtc.h>
#define mode_is(d, name) \
((d)->arch.hvm_domain.params[HVM_PARAM_TIMER_MODE] == HVMPTM_##name)
@@ -218,6 +219,7 @@ void pt_update_irq(struct vcpu *v)
struct periodic_time *pt, *temp, *earliest_pt = NULL;
uint64_t max_lag = -1ULL;
int irq, is_lapic;
+ void *pt_priv;
spin_lock(&v->arch.hvm_vcpu.tm_lock);
@@ -251,13 +253,14 @@ void pt_update_irq(struct vcpu *v)
earliest_pt->irq_issued = 1;
irq = earliest_pt->irq;
is_lapic = (earliest_pt->source == PTSRC_lapic);
+ pt_priv = earliest_pt->priv;
spin_unlock(&v->arch.hvm_vcpu.tm_lock);
if ( is_lapic )
- {
vlapic_set_irq(vcpu_vlapic(v), irq, 0);
- }
+ else if ( irq == RTC_IRQ && pt_priv )
+ rtc_periodic_interrupt(pt_priv);
else
{
hvm_isa_irq_deassert(v->domain, irq);
--- a/xen/include/asm-x86/hvm/vpt.h
+++ b/xen/include/asm-x86/hvm/vpt.h
@@ -181,6 +181,7 @@ void rtc_migrate_timers(struct vcpu *v);
void rtc_deinit(struct domain *d);
void rtc_reset(struct domain *d);
void rtc_update_clock(struct domain *d);
+void rtc_periodic_interrupt(void *);
void pmtimer_init(struct vcpu *v);
void pmtimer_deinit(struct domain *d);
[-- Attachment #3: Type: text/plain, Size: 126 bytes --]
_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
http://lists.xen.org/xen-devel
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: [PATCH, v3] x86/HVM: assorted RTC emulation adjustments
2012-09-07 13:01 [PATCH, v3] x86/HVM: assorted RTC emulation adjustments Jan Beulich
@ 2012-09-11 7:23 ` Keir Fraser
0 siblings, 0 replies; 2+ messages in thread
From: Keir Fraser @ 2012-09-11 7:23 UTC (permalink / raw)
To: Jan Beulich, xen-devel
On 07/09/2012 14:01, "Jan Beulich" <JBeulich@suse.com> wrote:
> - don't look at RTC_PIE in rtc_timer_update(), and hence don't call the
> function on REG_B writes at all
> - only call alarm_timer_update() on REG_B writes when relevant bits
> change
> - only call check_update_timer() on REG_B writes when SET changes
> - instead properly handle AF and PF when the guest is not also setting
> AIE/PIE respectively (for UF this was already the case, only a
> comment was slightly inaccurate)
> - raise the RTC IRQ not only when UIE gets set while UF was already
> set, but generalize this to cover AIE and PIE as well
> - properly mask off bit 7 when retrieving the hour values in
> alarm_timer_update(), and properly use RTC_HOURS_ALARM's bit 7 when
> converting from 12- to 24-hour value
> - also handle the two other possible clock bases
> - use RTC_* names in a couple of places where literal numbers were used
> so far
>
> Signed-off-by: Jan Beulich <jbeulich@suse.com>
Acked-by: Keir Fraser <keir@xen.org>
> --- a/xen/arch/x86/hvm/rtc.c
> +++ b/xen/arch/x86/hvm/rtc.c
> @@ -50,11 +50,24 @@ 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_periodic_cb(struct vcpu *v, void *opaque)
> +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)
> {
> RTCState *s = opaque;
> +
> spin_lock(&s->lock);
> - s->hw.cmos_data[RTC_REG_C] |= 0xc0;
> + s->hw.cmos_data[RTC_REG_C] |= RTC_PF;
> + if ( s->hw.cmos_data[RTC_REG_B] & RTC_PIE )
> + rtc_toggle_irq(s);
> spin_unlock(&s->lock);
> }
>
> @@ -68,19 +81,25 @@ 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;
> - if ( (period_code != 0) && (s->hw.cmos_data[RTC_REG_B] & RTC_PIE) )
> + switch ( s->hw.cmos_data[RTC_REG_A] & RTC_DIV_CTL )
> {
> - if ( period_code <= 2 )
> + case RTC_REF_CLCK_32KHZ:
> + if ( (period_code != 0) && (period_code <= 2) )
> period_code += 7;
> -
> - 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
> - {
> + /* 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:
> destroy_periodic_time(&s->pt);
> + break;
> }
> }
>
> @@ -102,7 +121,7 @@ static void check_update_timer(RTCState
> guest_usec = get_localtime_us(d) % USEC_PER_SEC;
> if (guest_usec >= (USEC_PER_SEC - 244))
> {
> - /* RTC is in update cycle when enabling UIE */
> + /* RTC is in update cycle */
> 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;
> @@ -144,7 +163,6 @@ static void rtc_update_timer(void *opaqu
> 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))
> @@ -152,11 +170,7 @@ static void rtc_update_timer2(void *opaq
> 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))
> - {
> - s->hw.cmos_data[RTC_REG_C] |= RTC_IRQF;
> - hvm_isa_irq_deassert(d, RTC_IRQ);
> - hvm_isa_irq_assert(d, RTC_IRQ);
> - }
> + rtc_toggle_irq(s);
> check_update_timer(s);
> }
> spin_unlock(&s->lock);
> @@ -175,21 +189,18 @@ static void alarm_timer_update(RTCState
>
> stop_timer(&s->alarm_timer);
>
> - if ((s->hw.cmos_data[RTC_REG_B] & RTC_AIE) &&
> - !(s->hw.cmos_data[RTC_REG_B] & RTC_SET))
> + if ( !(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 = from_bcd(s, s->hw.cmos_data[RTC_HOURS_ALARM]);
> - alarm_hour = convert_hour(s, alarm_hour);
> + alarm_hour = convert_hour(s, s->hw.cmos_data[RTC_HOURS_ALARM]);
>
> cur_sec = from_bcd(s, s->hw.cmos_data[RTC_SECONDS]);
> cur_min = from_bcd(s, s->hw.cmos_data[RTC_MINUTES]);
> - cur_hour = from_bcd(s, s->hw.cmos_data[RTC_HOURS]);
> - cur_hour = convert_hour(s, cur_hour);
> + cur_hour = convert_hour(s, s->hw.cmos_data[RTC_HOURS]);
>
> next_update_time = USEC_PER_SEC - (get_localtime_us(d) %
> USEC_PER_SEC);
> next_update_time = next_update_time * NS_PER_USEC + NOW();
> @@ -343,7 +354,6 @@ static void alarm_timer_update(RTCState
> 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))
> @@ -351,11 +361,7 @@ 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)
> - {
> - s->hw.cmos_data[RTC_REG_C] |= RTC_IRQF;
> - hvm_isa_irq_deassert(d, RTC_IRQ);
> - hvm_isa_irq_assert(d, RTC_IRQ);
> - }
> + rtc_toggle_irq(s);
> alarm_timer_update(s);
> }
> spin_unlock(&s->lock);
> @@ -365,7 +371,7 @@ static int rtc_ioport_write(void *opaque
> {
> RTCState *s = opaque;
> struct domain *d = vrtc_domain(s);
> - uint32_t orig;
> + uint32_t orig, mask;
>
> spin_lock(&s->lock);
>
> @@ -417,7 +423,7 @@ static int rtc_ioport_write(void *opaque
> /* set mode: reset UIP mode */
> s->hw.cmos_data[RTC_REG_A] &= ~RTC_UIP;
> /* adjust cmos before stopping */
> - if (!(s->hw.cmos_data[RTC_REG_B] & RTC_SET))
> + if (!(orig & RTC_SET))
> {
> s->current_tm = gmtime(get_localtime(d));
> rtc_copy_date(s);
> @@ -426,22 +432,26 @@ static int rtc_ioport_write(void *opaque
> else
> {
> /* if disabling set mode, update the time */
> - if ( s->hw.cmos_data[RTC_REG_B] & RTC_SET )
> + if ( orig & RTC_SET )
> rtc_set_time(s);
> }
> - /* 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)
> + /*
> + * 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) )
> {
> - hvm_isa_irq_deassert(d, RTC_IRQ);
> - hvm_isa_irq_assert(d, RTC_IRQ);
> + rtc_toggle_irq(s);
> + break;
> }
> s->hw.cmos_data[RTC_REG_B] = data;
> - if ( (data ^ orig) & RTC_PIE )
> - rtc_timer_update(s);
> - check_update_timer(s);
> - alarm_timer_update(s);
> + if ( (data ^ orig) & RTC_SET )
> + check_update_timer(s);
> + if ( (data ^ orig) & (RTC_24H | RTC_DM_BINARY | RTC_SET) )
> + alarm_timer_update(s);
> break;
> case RTC_REG_C:
> case RTC_REG_D:
> @@ -456,7 +466,7 @@ static int rtc_ioport_write(void *opaque
>
> static inline int to_bcd(RTCState *s, int a)
> {
> - if ( s->hw.cmos_data[RTC_REG_B] & 0x04 )
> + if ( s->hw.cmos_data[RTC_REG_B] & RTC_DM_BINARY )
> return a;
> else
> return ((a / 10) << 4) | (a % 10);
> @@ -464,7 +474,7 @@ static inline int to_bcd(RTCState *s, in
>
> static inline int from_bcd(RTCState *s, int a)
> {
> - if ( s->hw.cmos_data[RTC_REG_B] & 0x04 )
> + if ( s->hw.cmos_data[RTC_REG_B] & RTC_DM_BINARY )
> return a;
> else
> return ((a >> 4) * 10) + (a & 0x0f);
> @@ -472,12 +482,14 @@ static inline int from_bcd(RTCState *s,
>
> /* 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 hour)
> +static inline int convert_hour(RTCState *s, int raw)
> {
> + int hour = from_bcd(s, raw & 0x7f);
> +
> if (!(s->hw.cmos_data[RTC_REG_B] & RTC_24H))
> {
> hour %= 12;
> - if (s->hw.cmos_data[RTC_HOURS] & 0x80)
> + if (raw & 0x80)
> hour += 12;
> }
> return hour;
> @@ -496,8 +508,7 @@ 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 = from_bcd(s, s->hw.cmos_data[RTC_HOURS] & 0x7f);
> - tm->tm_hour = convert_hour(s, tm->tm_hour);
> + tm->tm_hour = convert_hour(s, s->hw.cmos_data[RTC_HOURS]);
> 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;
> --- a/xen/arch/x86/hvm/vpt.c
> +++ b/xen/arch/x86/hvm/vpt.c
> @@ -22,6 +22,7 @@
> #include <asm/hvm/vpt.h>
> #include <asm/event.h>
> #include <asm/apic.h>
> +#include <asm/mc146818rtc.h>
>
> #define mode_is(d, name) \
> ((d)->arch.hvm_domain.params[HVM_PARAM_TIMER_MODE] == HVMPTM_##name)
> @@ -218,6 +219,7 @@ void pt_update_irq(struct vcpu *v)
> struct periodic_time *pt, *temp, *earliest_pt = NULL;
> uint64_t max_lag = -1ULL;
> int irq, is_lapic;
> + void *pt_priv;
>
> spin_lock(&v->arch.hvm_vcpu.tm_lock);
>
> @@ -251,13 +253,14 @@ void pt_update_irq(struct vcpu *v)
> earliest_pt->irq_issued = 1;
> irq = earliest_pt->irq;
> is_lapic = (earliest_pt->source == PTSRC_lapic);
> + pt_priv = earliest_pt->priv;
>
> spin_unlock(&v->arch.hvm_vcpu.tm_lock);
>
> if ( is_lapic )
> - {
> vlapic_set_irq(vcpu_vlapic(v), irq, 0);
> - }
> + else if ( irq == RTC_IRQ && pt_priv )
> + rtc_periodic_interrupt(pt_priv);
> else
> {
> hvm_isa_irq_deassert(v->domain, irq);
> --- a/xen/include/asm-x86/hvm/vpt.h
> +++ b/xen/include/asm-x86/hvm/vpt.h
> @@ -181,6 +181,7 @@ void rtc_migrate_timers(struct vcpu *v);
> void rtc_deinit(struct domain *d);
> void rtc_reset(struct domain *d);
> void rtc_update_clock(struct domain *d);
> +void rtc_periodic_interrupt(void *);
>
> void pmtimer_init(struct vcpu *v);
> void pmtimer_deinit(struct domain *d);
>
>
> _______________________________________________
> Xen-devel mailing list
> Xen-devel@lists.xen.org
> http://lists.xen.org/xen-devel
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2012-09-11 7:23 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-09-07 13:01 [PATCH, v3] x86/HVM: assorted RTC emulation adjustments Jan Beulich
2012-09-11 7:23 ` Keir Fraser
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).