aboutsummaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
authoraliguori <aliguori@c046a42c-6fe2-441c-8c8c-71466251a162>2009-01-15 20:11:34 +0000
committeraliguori <aliguori@c046a42c-6fe2-441c-8c8c-71466251a162>2009-01-15 20:11:34 +0000
commit73822ec806bc8459047b6e9dea71d675c283a84c (patch)
tree4a7a2c610ba22ddb4a46158f155a798fb74481c8 /hw
parent5fc1503efc552e0ae0d9fec070d5784f51fce6b9 (diff)
Add -rtc-td-hack option to fix time drift with RTC on Windows (Gleb Natapov)
After my last patch to fix interrupt coalescing was rejected on the basis that it is too intrusive we decided to make the fix much more localized and only fix the problem for RTC time source. Unfortunately it is impossible to fix the problem entirely inside RTC code like Andrzej proposed since Windows reads RTC register C more then once on each time interrupt so it is impossible to count reliably how many interrupt windows actually handled. Proposed solution is localized to I386 target and is disabled by default. To enable it "-rtc-td-hack" flag should be used. Signed-off-by: Gleb Natapov <gleb@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com> git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6320 c046a42c-6fe2-441c-8c8c-71466251a162
Diffstat (limited to 'hw')
-rw-r--r--hw/apic.c22
-rw-r--r--hw/mc146818rtc.c57
-rw-r--r--hw/pc.h2
3 files changed, 81 insertions, 0 deletions
diff --git a/hw/apic.c b/hw/apic.c
index bad137f826..3052843c87 100644
--- a/hw/apic.c
+++ b/hw/apic.c
@@ -100,6 +100,8 @@ struct IOAPICState {
static int apic_io_memory;
static APICState *local_apics[MAX_APICS + 1];
static int last_apic_id = 0;
+static int apic_irq_delivered;
+
static void apic_init_ipi(APICState *s);
static void apic_set_irq(APICState *s, int vector_num, int trigger_mode);
@@ -133,6 +135,14 @@ static inline void reset_bit(uint32_t *tab, int index)
tab[i] &= ~mask;
}
+static inline int get_bit(uint32_t *tab, int index)
+{
+ int i, mask;
+ i = index >> 5;
+ mask = 1 << (index & 0x1f);
+ return !!(tab[i] & mask);
+}
+
static void apic_local_deliver(CPUState *env, int vector)
{
APICState *s = env->apic_state;
@@ -349,8 +359,20 @@ static void apic_update_irq(APICState *s)
cpu_interrupt(s->cpu_env, CPU_INTERRUPT_HARD);
}
+void apic_reset_irq_delivered(void)
+{
+ apic_irq_delivered = 0;
+}
+
+int apic_get_irq_delivered(void)
+{
+ return apic_irq_delivered;
+}
+
static void apic_set_irq(APICState *s, int vector_num, int trigger_mode)
{
+ apic_irq_delivered += !get_bit(s->irr, vector_num);
+
set_bit(s->irr, vector_num);
if (trigger_mode)
set_bit(s->tmr, vector_num);
diff --git a/hw/mc146818rtc.c b/hw/mc146818rtc.c
index f57a9a6783..cd57bf3760 100644
--- a/hw/mc146818rtc.c
+++ b/hw/mc146818rtc.c
@@ -67,6 +67,10 @@ struct RTCState {
int64_t next_periodic_time;
/* second update */
int64_t next_second_time;
+#ifdef TARGET_I386
+ uint32_t irq_coalesced;
+ uint32_t period;
+#endif
QEMUTimer *second_timer;
QEMUTimer *second_timer2;
};
@@ -104,12 +108,20 @@ static void rtc_timer_update(RTCState *s, int64_t current_time)
period_code += 7;
/* period in 32 Khz cycles */
period = 1 << (period_code - 1);
+#ifdef TARGET_I386
+ if(period != s->period)
+ s->irq_coalesced = (s->irq_coalesced * s->period) / period;
+ s->period = period;
+#endif
/* compute 32 khz clock */
cur_clock = muldiv64(current_time, 32768, ticks_per_sec);
next_irq_clock = (cur_clock & ~(period - 1)) + period;
s->next_periodic_time = muldiv64(next_irq_clock, ticks_per_sec, 32768) + 1;
qemu_mod_timer(s->periodic_timer, s->next_periodic_time);
} else {
+#ifdef TARGET_I386
+ s->irq_coalesced = 0;
+#endif
qemu_del_timer(s->periodic_timer);
}
}
@@ -119,6 +131,12 @@ static void rtc_periodic_timer(void *opaque)
RTCState *s = opaque;
rtc_timer_update(s, s->next_periodic_time);
+#ifdef TARGET_I386
+ if ((s->cmos_data[RTC_REG_C] & 0xc0) && rtc_td_hack) {
+ s->irq_coalesced++;
+ return;
+ }
+#endif
s->cmos_data[RTC_REG_C] |= 0xc0;
rtc_irq_raise(s->irq);
}
@@ -379,6 +397,15 @@ static uint32_t cmos_ioport_read(void *opaque, uint32_t addr)
case RTC_REG_C:
ret = s->cmos_data[s->cmos_index];
qemu_irq_lower(s->irq);
+#ifdef TARGET_I386
+ if(s->irq_coalesced) {
+ apic_reset_irq_delivered();
+ qemu_irq_raise(s->irq);
+ if (apic_get_irq_delivered())
+ s->irq_coalesced--;
+ break;
+ }
+#endif
s->cmos_data[RTC_REG_C] = 0x00;
break;
default:
@@ -473,6 +500,28 @@ static int rtc_load(QEMUFile *f, void *opaque, int version_id)
return 0;
}
+#ifdef TARGET_I386
+static void rtc_save_td(QEMUFile *f, void *opaque)
+{
+ RTCState *s = opaque;
+
+ qemu_put_be32(f, s->irq_coalesced);
+ qemu_put_be32(f, s->period);
+}
+
+static int rtc_load_td(QEMUFile *f, void *opaque, int version_id)
+{
+ RTCState *s = opaque;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ s->irq_coalesced = qemu_get_be32(f);
+ s->period = qemu_get_be32(f);
+ return 0;
+}
+#endif
+
RTCState *rtc_init(int base, qemu_irq irq)
{
RTCState *s;
@@ -503,6 +552,10 @@ RTCState *rtc_init(int base, qemu_irq irq)
register_ioport_read(base, 2, 1, cmos_ioport_read, s);
register_savevm("mc146818rtc", base, 1, rtc_save, rtc_load, s);
+#ifdef TARGET_I386
+ if (rtc_td_hack)
+ register_savevm("mc146818rtc-td", base, 1, rtc_save_td, rtc_load_td, s);
+#endif
return s;
}
@@ -609,5 +662,9 @@ RTCState *rtc_mm_init(target_phys_addr_t base, int it_shift, qemu_irq irq)
cpu_register_physical_memory(base, 2 << it_shift, io_memory);
register_savevm("mc146818rtc", base, 1, rtc_save, rtc_load, s);
+#ifdef TARGET_I386
+ if (rtc_td_hack)
+ register_savevm("mc146818rtc-td", base, 1, rtc_save_td, rtc_load_td, s);
+#endif
return s;
}
diff --git a/hw/pc.h b/hw/pc.h
index a60bddf0f9..5dc770985e 100644
--- a/hw/pc.h
+++ b/hw/pc.h
@@ -46,6 +46,8 @@ void apic_deliver_pic_intr(CPUState *env, int level);
int apic_get_interrupt(CPUState *env);
IOAPICState *ioapic_init(void);
void ioapic_set_irq(void *opaque, int vector, int level);
+void apic_reset_irq_delivered(void);
+int apic_get_irq_delivered(void);
/* i8254.c */