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 100 static int apic_io_memory;
101 101 static APICState *local_apics[MAX_APICS + 1];
102 102 static int last_apic_id = 0;
  103 +static int apic_irq_delivered;
  104 +
103 105  
104 106 static void apic_init_ipi(APICState *s);
105 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 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 146 static void apic_local_deliver(CPUState *env, int vector)
137 147 {
138 148 APICState *s = env->apic_state;
... ... @@ -349,8 +359,20 @@ static void apic_update_irq(APICState *s)
349 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 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 376 set_bit(s->irr, vector_num);
355 377 if (trigger_mode)
356 378 set_bit(s->tmr, vector_num);
... ...
hw/mc146818rtc.c
... ... @@ -67,6 +67,10 @@ struct RTCState {
67 67 int64_t next_periodic_time;
68 68 /* second update */
69 69 int64_t next_second_time;
  70 +#ifdef TARGET_I386
  71 + uint32_t irq_coalesced;
  72 + uint32_t period;
  73 +#endif
70 74 QEMUTimer *second_timer;
71 75 QEMUTimer *second_timer2;
72 76 };
... ... @@ -104,12 +108,20 @@ static void rtc_timer_update(RTCState *s, int64_t current_time)
104 108 period_code += 7;
105 109 /* period in 32 Khz cycles */
106 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 116 /* compute 32 khz clock */
108 117 cur_clock = muldiv64(current_time, 32768, ticks_per_sec);
109 118 next_irq_clock = (cur_clock & ~(period - 1)) + period;
110 119 s->next_periodic_time = muldiv64(next_irq_clock, ticks_per_sec, 32768) + 1;
111 120 qemu_mod_timer(s->periodic_timer, s->next_periodic_time);
112 121 } else {
  122 +#ifdef TARGET_I386
  123 + s->irq_coalesced = 0;
  124 +#endif
113 125 qemu_del_timer(s->periodic_timer);
114 126 }
115 127 }
... ... @@ -119,6 +131,12 @@ static void rtc_periodic_timer(void *opaque)
119 131 RTCState *s = opaque;
120 132  
121 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 140 s->cmos_data[RTC_REG_C] |= 0xc0;
123 141 rtc_irq_raise(s->irq);
124 142 }
... ... @@ -379,6 +397,15 @@ static uint32_t cmos_ioport_read(void *opaque, uint32_t addr)
379 397 case RTC_REG_C:
380 398 ret = s->cmos_data[s->cmos_index];
381 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 409 s->cmos_data[RTC_REG_C] = 0x00;
383 410 break;
384 411 default:
... ... @@ -473,6 +500,28 @@ static int rtc_load(QEMUFile *f, void *opaque, int version_id)
473 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 525 RTCState *rtc_init(int base, qemu_irq irq)
477 526 {
478 527 RTCState *s;
... ... @@ -503,6 +552,10 @@ RTCState *rtc_init(int base, qemu_irq irq)
503 552 register_ioport_read(base, 2, 1, cmos_ioport_read, s);
504 553  
505 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 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 662 cpu_register_physical_memory(base, 2 << it_shift, io_memory);
610 663  
611 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 669 return s;
613 670 }
... ...
... ... @@ -46,6 +46,8 @@ void apic_deliver_pic_intr(CPUState *env, int level);
46 46 int apic_get_interrupt(CPUState *env);
47 47 IOAPICState *ioapic_init(void);
48 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 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 420 Windows 2000 is installed, you no longer need this option (this option
421 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 428 @item -option-rom @var{file}
424 429 Load the contents of @var{file} as an option ROM.
425 430 This option is useful to load things like EtherBoot.
... ...
sysemu.h
... ... @@ -90,6 +90,7 @@ extern int graphic_depth;
90 90 extern int nographic;
91 91 extern const char *keyboard_layout;
92 92 extern int win2k_install_hack;
  93 +extern int rtc_td_hack;
93 94 extern int alt_grab;
94 95 extern int usb_enabled;
95 96 extern int smp_cpus;
... ...
... ... @@ -212,6 +212,7 @@ CharDriverState *parallel_hds[MAX_PARALLEL_PORTS];
212 212 CharDriverState *virtcon_hds[MAX_VIRTIO_CONSOLES];
213 213 #ifdef TARGET_I386
214 214 int win2k_install_hack = 0;
  215 +int rtc_td_hack = 0;
215 216 #endif
216 217 int usb_enabled = 0;
217 218 int smp_cpus = 1;
... ... @@ -3878,6 +3879,7 @@ static void help(int exitcode)
3878 3879 "-full-screen start in full screen\n"
3879 3880 #ifdef TARGET_I386
3880 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 3883 #endif
3882 3884 "-usb enable the USB driver (will be the default soon)\n"
3883 3885 "-usbdevice name add the host or guest USB device 'name'\n"
... ... @@ -4074,6 +4076,7 @@ enum {
4074 4076 QEMU_OPTION_kernel_kqemu,
4075 4077 QEMU_OPTION_enable_kvm,
4076 4078 QEMU_OPTION_win2k_hack,
  4079 + QEMU_OPTION_rtc_td_hack,
4077 4080 QEMU_OPTION_usb,
4078 4081 QEMU_OPTION_usbdevice,
4079 4082 QEMU_OPTION_smp,
... ... @@ -4183,6 +4186,7 @@ static const QEMUOption qemu_options[] = {
4183 4186 #endif
4184 4187 { "pidfile", HAS_ARG, QEMU_OPTION_pidfile },
4185 4188 { "win2k-hack", 0, QEMU_OPTION_win2k_hack },
  4189 + { "rtc-td-hack", 0, QEMU_OPTION_rtc_td_hack },
4186 4190 { "usbdevice", HAS_ARG, QEMU_OPTION_usbdevice },
4187 4191 { "smp", HAS_ARG, QEMU_OPTION_smp },
4188 4192 { "vnc", HAS_ARG, QEMU_OPTION_vnc },
... ... @@ -5011,6 +5015,9 @@ int main(int argc, char **argv, char **envp)
5011 5015 case QEMU_OPTION_win2k_hack:
5012 5016 win2k_install_hack = 1;
5013 5017 break;
  5018 + case QEMU_OPTION_rtc_td_hack:
  5019 + rtc_td_hack = 1;
  5020 + break;
5014 5021 #endif
5015 5022 #ifdef USE_KQEMU
5016 5023 case QEMU_OPTION_no_kqemu:
... ...