Commit 73822ec806bc8459047b6e9dea71d675c283a84c

Authored by aliguori
1 parent 5fc1503e

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
hw/apic.c
@@ -100,6 +100,8 @@ struct IOAPICState { @@ -100,6 +100,8 @@ struct IOAPICState {
100 static int apic_io_memory; 100 static int apic_io_memory;
101 static APICState *local_apics[MAX_APICS + 1]; 101 static APICState *local_apics[MAX_APICS + 1];
102 static int last_apic_id = 0; 102 static int last_apic_id = 0;
  103 +static int apic_irq_delivered;
  104 +
103 105
104 static void apic_init_ipi(APICState *s); 106 static void apic_init_ipi(APICState *s);
105 static void apic_set_irq(APICState *s, int vector_num, int trigger_mode); 107 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) @@ -133,6 +135,14 @@ static inline void reset_bit(uint32_t *tab, int index)
133 tab[i] &= ~mask; 135 tab[i] &= ~mask;
134 } 136 }
135 137
  138 +static inline int get_bit(uint32_t *tab, int index)
  139 +{
  140 + int i, mask;
  141 + i = index >> 5;
  142 + mask = 1 << (index & 0x1f);
  143 + return !!(tab[i] & mask);
  144 +}
  145 +
136 static void apic_local_deliver(CPUState *env, int vector) 146 static void apic_local_deliver(CPUState *env, int vector)
137 { 147 {
138 APICState *s = env->apic_state; 148 APICState *s = env->apic_state;
@@ -349,8 +359,20 @@ static void apic_update_irq(APICState *s) @@ -349,8 +359,20 @@ static void apic_update_irq(APICState *s)
349 cpu_interrupt(s->cpu_env, CPU_INTERRUPT_HARD); 359 cpu_interrupt(s->cpu_env, CPU_INTERRUPT_HARD);
350 } 360 }
351 361
  362 +void apic_reset_irq_delivered(void)
  363 +{
  364 + apic_irq_delivered = 0;
  365 +}
  366 +
  367 +int apic_get_irq_delivered(void)
  368 +{
  369 + return apic_irq_delivered;
  370 +}
  371 +
352 static void apic_set_irq(APICState *s, int vector_num, int trigger_mode) 372 static void apic_set_irq(APICState *s, int vector_num, int trigger_mode)
353 { 373 {
  374 + apic_irq_delivered += !get_bit(s->irr, vector_num);
  375 +
354 set_bit(s->irr, vector_num); 376 set_bit(s->irr, vector_num);
355 if (trigger_mode) 377 if (trigger_mode)
356 set_bit(s->tmr, vector_num); 378 set_bit(s->tmr, vector_num);
hw/mc146818rtc.c
@@ -67,6 +67,10 @@ struct RTCState { @@ -67,6 +67,10 @@ struct RTCState {
67 int64_t next_periodic_time; 67 int64_t next_periodic_time;
68 /* second update */ 68 /* second update */
69 int64_t next_second_time; 69 int64_t next_second_time;
  70 +#ifdef TARGET_I386
  71 + uint32_t irq_coalesced;
  72 + uint32_t period;
  73 +#endif
70 QEMUTimer *second_timer; 74 QEMUTimer *second_timer;
71 QEMUTimer *second_timer2; 75 QEMUTimer *second_timer2;
72 }; 76 };
@@ -104,12 +108,20 @@ static void rtc_timer_update(RTCState *s, int64_t current_time) @@ -104,12 +108,20 @@ static void rtc_timer_update(RTCState *s, int64_t current_time)
104 period_code += 7; 108 period_code += 7;
105 /* period in 32 Khz cycles */ 109 /* period in 32 Khz cycles */
106 period = 1 << (period_code - 1); 110 period = 1 << (period_code - 1);
  111 +#ifdef TARGET_I386
  112 + if(period != s->period)
  113 + s->irq_coalesced = (s->irq_coalesced * s->period) / period;
  114 + s->period = period;
  115 +#endif
107 /* compute 32 khz clock */ 116 /* compute 32 khz clock */
108 cur_clock = muldiv64(current_time, 32768, ticks_per_sec); 117 cur_clock = muldiv64(current_time, 32768, ticks_per_sec);
109 next_irq_clock = (cur_clock & ~(period - 1)) + period; 118 next_irq_clock = (cur_clock & ~(period - 1)) + period;
110 s->next_periodic_time = muldiv64(next_irq_clock, ticks_per_sec, 32768) + 1; 119 s->next_periodic_time = muldiv64(next_irq_clock, ticks_per_sec, 32768) + 1;
111 qemu_mod_timer(s->periodic_timer, s->next_periodic_time); 120 qemu_mod_timer(s->periodic_timer, s->next_periodic_time);
112 } else { 121 } else {
  122 +#ifdef TARGET_I386
  123 + s->irq_coalesced = 0;
  124 +#endif
113 qemu_del_timer(s->periodic_timer); 125 qemu_del_timer(s->periodic_timer);
114 } 126 }
115 } 127 }
@@ -119,6 +131,12 @@ static void rtc_periodic_timer(void *opaque) @@ -119,6 +131,12 @@ static void rtc_periodic_timer(void *opaque)
119 RTCState *s = opaque; 131 RTCState *s = opaque;
120 132
121 rtc_timer_update(s, s->next_periodic_time); 133 rtc_timer_update(s, s->next_periodic_time);
  134 +#ifdef TARGET_I386
  135 + if ((s->cmos_data[RTC_REG_C] & 0xc0) && rtc_td_hack) {
  136 + s->irq_coalesced++;
  137 + return;
  138 + }
  139 +#endif
122 s->cmos_data[RTC_REG_C] |= 0xc0; 140 s->cmos_data[RTC_REG_C] |= 0xc0;
123 rtc_irq_raise(s->irq); 141 rtc_irq_raise(s->irq);
124 } 142 }
@@ -379,6 +397,15 @@ static uint32_t cmos_ioport_read(void *opaque, uint32_t addr) @@ -379,6 +397,15 @@ static uint32_t cmos_ioport_read(void *opaque, uint32_t addr)
379 case RTC_REG_C: 397 case RTC_REG_C:
380 ret = s->cmos_data[s->cmos_index]; 398 ret = s->cmos_data[s->cmos_index];
381 qemu_irq_lower(s->irq); 399 qemu_irq_lower(s->irq);
  400 +#ifdef TARGET_I386
  401 + if(s->irq_coalesced) {
  402 + apic_reset_irq_delivered();
  403 + qemu_irq_raise(s->irq);
  404 + if (apic_get_irq_delivered())
  405 + s->irq_coalesced--;
  406 + break;
  407 + }
  408 +#endif
382 s->cmos_data[RTC_REG_C] = 0x00; 409 s->cmos_data[RTC_REG_C] = 0x00;
383 break; 410 break;
384 default: 411 default:
@@ -473,6 +500,28 @@ static int rtc_load(QEMUFile *f, void *opaque, int version_id) @@ -473,6 +500,28 @@ static int rtc_load(QEMUFile *f, void *opaque, int version_id)
473 return 0; 500 return 0;
474 } 501 }
475 502
  503 +#ifdef TARGET_I386
  504 +static void rtc_save_td(QEMUFile *f, void *opaque)
  505 +{
  506 + RTCState *s = opaque;
  507 +
  508 + qemu_put_be32(f, s->irq_coalesced);
  509 + qemu_put_be32(f, s->period);
  510 +}
  511 +
  512 +static int rtc_load_td(QEMUFile *f, void *opaque, int version_id)
  513 +{
  514 + RTCState *s = opaque;
  515 +
  516 + if (version_id != 1)
  517 + return -EINVAL;
  518 +
  519 + s->irq_coalesced = qemu_get_be32(f);
  520 + s->period = qemu_get_be32(f);
  521 + return 0;
  522 +}
  523 +#endif
  524 +
476 RTCState *rtc_init(int base, qemu_irq irq) 525 RTCState *rtc_init(int base, qemu_irq irq)
477 { 526 {
478 RTCState *s; 527 RTCState *s;
@@ -503,6 +552,10 @@ RTCState *rtc_init(int base, qemu_irq irq) @@ -503,6 +552,10 @@ RTCState *rtc_init(int base, qemu_irq irq)
503 register_ioport_read(base, 2, 1, cmos_ioport_read, s); 552 register_ioport_read(base, 2, 1, cmos_ioport_read, s);
504 553
505 register_savevm("mc146818rtc", base, 1, rtc_save, rtc_load, s); 554 register_savevm("mc146818rtc", base, 1, rtc_save, rtc_load, s);
  555 +#ifdef TARGET_I386
  556 + if (rtc_td_hack)
  557 + register_savevm("mc146818rtc-td", base, 1, rtc_save_td, rtc_load_td, s);
  558 +#endif
506 return s; 559 return s;
507 } 560 }
508 561
@@ -609,5 +662,9 @@ RTCState *rtc_mm_init(target_phys_addr_t base, int it_shift, qemu_irq irq) @@ -609,5 +662,9 @@ RTCState *rtc_mm_init(target_phys_addr_t base, int it_shift, qemu_irq irq)
609 cpu_register_physical_memory(base, 2 << it_shift, io_memory); 662 cpu_register_physical_memory(base, 2 << it_shift, io_memory);
610 663
611 register_savevm("mc146818rtc", base, 1, rtc_save, rtc_load, s); 664 register_savevm("mc146818rtc", base, 1, rtc_save, rtc_load, s);
  665 +#ifdef TARGET_I386
  666 + if (rtc_td_hack)
  667 + register_savevm("mc146818rtc-td", base, 1, rtc_save_td, rtc_load_td, s);
  668 +#endif
612 return s; 669 return s;
613 } 670 }
@@ -46,6 +46,8 @@ void apic_deliver_pic_intr(CPUState *env, int level); @@ -46,6 +46,8 @@ void apic_deliver_pic_intr(CPUState *env, int level);
46 int apic_get_interrupt(CPUState *env); 46 int apic_get_interrupt(CPUState *env);
47 IOAPICState *ioapic_init(void); 47 IOAPICState *ioapic_init(void);
48 void ioapic_set_irq(void *opaque, int vector, int level); 48 void ioapic_set_irq(void *opaque, int vector, int level);
  49 +void apic_reset_irq_delivered(void);
  50 +int apic_get_irq_delivered(void);
49 51
50 /* i8254.c */ 52 /* i8254.c */
51 53
qemu-doc.texi
@@ -420,6 +420,11 @@ Use it when installing Windows 2000 to avoid a disk full bug. After @@ -420,6 +420,11 @@ Use it when installing Windows 2000 to avoid a disk full bug. After
420 Windows 2000 is installed, you no longer need this option (this option 420 Windows 2000 is installed, you no longer need this option (this option
421 slows down the IDE transfers). 421 slows down the IDE transfers).
422 422
  423 +@item -rtc-td-hack
  424 +Use it if you experience time drift problem in Windows with ACPI HAL.
  425 +This option will try to figure out how many timer interrupts were not
  426 +processed by the Windows guest and will re-inject them.
  427 +
423 @item -option-rom @var{file} 428 @item -option-rom @var{file}
424 Load the contents of @var{file} as an option ROM. 429 Load the contents of @var{file} as an option ROM.
425 This option is useful to load things like EtherBoot. 430 This option is useful to load things like EtherBoot.
sysemu.h
@@ -90,6 +90,7 @@ extern int graphic_depth; @@ -90,6 +90,7 @@ extern int graphic_depth;
90 extern int nographic; 90 extern int nographic;
91 extern const char *keyboard_layout; 91 extern const char *keyboard_layout;
92 extern int win2k_install_hack; 92 extern int win2k_install_hack;
  93 +extern int rtc_td_hack;
93 extern int alt_grab; 94 extern int alt_grab;
94 extern int usb_enabled; 95 extern int usb_enabled;
95 extern int smp_cpus; 96 extern int smp_cpus;
@@ -212,6 +212,7 @@ CharDriverState *parallel_hds[MAX_PARALLEL_PORTS]; @@ -212,6 +212,7 @@ CharDriverState *parallel_hds[MAX_PARALLEL_PORTS];
212 CharDriverState *virtcon_hds[MAX_VIRTIO_CONSOLES]; 212 CharDriverState *virtcon_hds[MAX_VIRTIO_CONSOLES];
213 #ifdef TARGET_I386 213 #ifdef TARGET_I386
214 int win2k_install_hack = 0; 214 int win2k_install_hack = 0;
  215 +int rtc_td_hack = 0;
215 #endif 216 #endif
216 int usb_enabled = 0; 217 int usb_enabled = 0;
217 int smp_cpus = 1; 218 int smp_cpus = 1;
@@ -3878,6 +3879,7 @@ static void help(int exitcode) @@ -3878,6 +3879,7 @@ static void help(int exitcode)
3878 "-full-screen start in full screen\n" 3879 "-full-screen start in full screen\n"
3879 #ifdef TARGET_I386 3880 #ifdef TARGET_I386
3880 "-win2k-hack use it when installing Windows 2000 to avoid a disk full bug\n" 3881 "-win2k-hack use it when installing Windows 2000 to avoid a disk full bug\n"
  3882 + "-rtc-td-hack use it to fix time drift in Windows ACPI HAL\n"
3881 #endif 3883 #endif
3882 "-usb enable the USB driver (will be the default soon)\n" 3884 "-usb enable the USB driver (will be the default soon)\n"
3883 "-usbdevice name add the host or guest USB device 'name'\n" 3885 "-usbdevice name add the host or guest USB device 'name'\n"
@@ -4074,6 +4076,7 @@ enum { @@ -4074,6 +4076,7 @@ enum {
4074 QEMU_OPTION_kernel_kqemu, 4076 QEMU_OPTION_kernel_kqemu,
4075 QEMU_OPTION_enable_kvm, 4077 QEMU_OPTION_enable_kvm,
4076 QEMU_OPTION_win2k_hack, 4078 QEMU_OPTION_win2k_hack,
  4079 + QEMU_OPTION_rtc_td_hack,
4077 QEMU_OPTION_usb, 4080 QEMU_OPTION_usb,
4078 QEMU_OPTION_usbdevice, 4081 QEMU_OPTION_usbdevice,
4079 QEMU_OPTION_smp, 4082 QEMU_OPTION_smp,
@@ -4183,6 +4186,7 @@ static const QEMUOption qemu_options[] = { @@ -4183,6 +4186,7 @@ static const QEMUOption qemu_options[] = {
4183 #endif 4186 #endif
4184 { "pidfile", HAS_ARG, QEMU_OPTION_pidfile }, 4187 { "pidfile", HAS_ARG, QEMU_OPTION_pidfile },
4185 { "win2k-hack", 0, QEMU_OPTION_win2k_hack }, 4188 { "win2k-hack", 0, QEMU_OPTION_win2k_hack },
  4189 + { "rtc-td-hack", 0, QEMU_OPTION_rtc_td_hack },
4186 { "usbdevice", HAS_ARG, QEMU_OPTION_usbdevice }, 4190 { "usbdevice", HAS_ARG, QEMU_OPTION_usbdevice },
4187 { "smp", HAS_ARG, QEMU_OPTION_smp }, 4191 { "smp", HAS_ARG, QEMU_OPTION_smp },
4188 { "vnc", HAS_ARG, QEMU_OPTION_vnc }, 4192 { "vnc", HAS_ARG, QEMU_OPTION_vnc },
@@ -5011,6 +5015,9 @@ int main(int argc, char **argv, char **envp) @@ -5011,6 +5015,9 @@ int main(int argc, char **argv, char **envp)
5011 case QEMU_OPTION_win2k_hack: 5015 case QEMU_OPTION_win2k_hack:
5012 win2k_install_hack = 1; 5016 win2k_install_hack = 1;
5013 break; 5017 break;
  5018 + case QEMU_OPTION_rtc_td_hack:
  5019 + rtc_td_hack = 1;
  5020 + break;
5014 #endif 5021 #endif
5015 #ifdef USE_KQEMU 5022 #ifdef USE_KQEMU
5016 case QEMU_OPTION_no_kqemu: 5023 case QEMU_OPTION_no_kqemu: