@@ -24,6 +24,9 @@
#include "sysbus.h"
#include "trace.h"
#include "kvm.h"
+#ifdef CONFIG_HPET_DRIFTFIX
+#include "sysemu.h"
+#endif
/* APIC Local Vector Table */
#define APIC_LVT_TIMER 0
@@ -1143,6 +1146,10 @@ static SysBusDeviceInfo apic_info = {
static void apic_register_devices(void)
{
+#ifdef CONFIG_HPET_DRIFTFIX
+ qemu_get_irq_delivered = apic_get_irq_delivered;
+ qemu_reset_irq_delivered = apic_reset_irq_delivered;
+#endif
sysbus_register_withprop(&apic_info);
}
@@ -31,6 +31,9 @@
#include "hpet_emul.h"
#include "sysbus.h"
#include "mc146818rtc.h"
+#ifdef CONFIG_HPET_DRIFTFIX
+#include "sysemu.h"
+#endif
//#define HPET_DEBUG
#ifdef HPET_DEBUG
@@ -41,6 +44,13 @@
#define HPET_MSI_SUPPORT 0
+#ifdef CONFIG_HPET_DRIFTFIX
+#define MAX_IRQS_TO_INJECT (uint32_t)5000
+#define MAX_IRQ_RATE (uint32_t)10
+
+extern int hpet_driftfix;
+#endif
+
struct HPETState;
typedef struct HPETTimer { /* timers */
uint8_t tn; /*timer number*/
@@ -55,6 +65,12 @@ typedef struct HPETTimer { /* timers */
uint8_t wrap_flag; /* timer pop will indicate wrap for one-shot 32-bit
* mode. Next pop will be actual timer expiration.
*/
+#ifdef CONFIG_HPET_DRIFTFIX
+ uint64_t saved_period;
+ uint32_t irqs_to_inject;
+ uint32_t irq_rate;
+ uint32_t divisor;
+#endif
} HPETTimer;
typedef struct HPETState {
@@ -169,11 +185,12 @@ static inline uint64_t hpet_calculate_di
}
}
-static void update_irq(struct HPETTimer *timer, int set)
+static int update_irq(struct HPETTimer *timer, int set)
{
uint64_t mask;
HPETState *s;
int route;
+ int irq_delivered = 1;
if (timer->tn <= 1 && hpet_in_legacy_mode(timer->state)) {
/* if LegacyReplacementRoute bit is set, HPET specification requires
@@ -198,8 +215,19 @@ static void update_irq(struct HPETTimer
qemu_irq_raise(s->irqs[route]);
} else {
s->isr &= ~mask;
+#ifdef CONFIG_HPET_DRIFTFIX
+ if (hpet_driftfix && qemu_get_irq_delivered
+ && qemu_reset_irq_delivered) {
+ qemu_reset_irq_delivered();
+ qemu_irq_raise(s->irqs[route]);
+ irq_delivered = qemu_get_irq_delivered();
+ qemu_irq_lower(s->irqs[route]);
+ }
+ else
+#endif
qemu_irq_pulse(s->irqs[route]);
}
+ return irq_delivered;
}
static void hpet_pre_save(void *opaque)
@@ -246,7 +274,11 @@ static int hpet_post_load(void *opaque,
static const VMStateDescription vmstate_hpet_timer = {
.name = "hpet_timer",
+#ifdef CONFIG_HPET_DRIFTFIX
+ .version_id = 3,
+#else
.version_id = 1,
+#endif
.minimum_version_id = 1,
.minimum_version_id_old = 1,
.fields = (VMStateField []) {
@@ -256,6 +288,12 @@ static const VMStateDescription vmstate_
VMSTATE_UINT64(fsb, HPETTimer),
VMSTATE_UINT64(period, HPETTimer),
VMSTATE_UINT8(wrap_flag, HPETTimer),
+#ifdef CONFIG_HPET_DRIFTFIX
+ VMSTATE_UINT64_V(saved_period, HPETTimer, 3),
+ VMSTATE_UINT32_V(irqs_to_inject, HPETTimer, 3),
+ VMSTATE_UINT32_V(irq_rate, HPETTimer, 3),
+ VMSTATE_UINT32_V(divisor, HPETTimer, 3),
+#endif
VMSTATE_TIMER(qemu_timer, HPETTimer),
VMSTATE_END_OF_LIST()
}
@@ -263,7 +301,11 @@ static const VMStateDescription vmstate_
static const VMStateDescription vmstate_hpet = {
.name = "hpet",
+#ifdef CONFIG_HPET_DRIFTFIX
+ .version_id = 3,
+#else
.version_id = 2,
+#endif
.minimum_version_id = 1,
.minimum_version_id_old = 1,
.pre_save = hpet_pre_save,
@@ -287,7 +329,10 @@ static void hpet_timer(void *opaque)
{
HPETTimer *t = opaque;
uint64_t diff;
-
+#ifdef CONFIG_HPET_DRIFTFIX
+ uint32_t irq_count = 0;
+ int irq_delivered = 0;
+#endif
uint64_t period = t->period;
uint64_t cur_tick = hpet_get_ticks(t->state);
@@ -295,13 +340,39 @@ static void hpet_timer(void *opaque)
if (t->config & HPET_TN_32BIT) {
while (hpet_time_after(cur_tick, t->cmp)) {
t->cmp = (uint32_t)(t->cmp + t->period);
+#ifdef CONFIG_HPET_DRIFTFIX
+ irq_count++;
+#endif
}
} else {
while (hpet_time_after64(cur_tick, t->cmp)) {
t->cmp += period;
+#ifdef CONFIG_HPET_DRIFTFIX
+ irq_count++;
+#endif
}
}
diff = hpet_calculate_diff(t, cur_tick);
+#ifdef CONFIG_HPET_DRIFTFIX
+ if (hpet_driftfix) {
+ if (t->saved_period != t->period) {
+ t->irqs_to_inject = (t->irqs_to_inject * t->saved_period)
+ / t->period;
+ t->saved_period = t->period;
+ }
+ t->irqs_to_inject += irq_count;
+ t->irqs_to_inject = MIN(t->irqs_to_inject, MAX_IRQS_TO_INJECT);
+ if (t->irqs_to_inject > 1) {
+ if (irq_count > 1) {
+ t->irq_rate++;
+ t->irq_rate = MIN(t->irq_rate, MAX_IRQ_RATE);
+ }
+ if (irq_count || t->divisor == 0)
+ t->divisor = t->irq_rate;
+ diff /= t->divisor--;
+ }
+ }
+#endif
qemu_mod_timer(t->qemu_timer,
qemu_get_clock(vm_clock) + (int64_t)ticks_to_ns(diff));
} else if (t->config & HPET_TN_32BIT && !timer_is_periodic(t)) {
@@ -312,6 +383,23 @@ static void hpet_timer(void *opaque)
t->wrap_flag = 0;
}
}
+#ifdef CONFIG_HPET_DRIFTFIX
+ if (hpet_driftfix && timer_is_periodic(t) && period != 0) {
+ if (t->irqs_to_inject) {
+ irq_delivered = update_irq(t, 1);
+ if (irq_delivered) {
+ t->irq_rate = MIN(t->irq_rate, t->irqs_to_inject);
+ t->irqs_to_inject--;
+ } else {
+ if (irq_count) {
+ t->irq_rate++;
+ t->irq_rate = MIN(t->irq_rate, MAX_IRQ_RATE);
+ }
+ }
+ }
+ }
+ else
+#endif
update_irq(t, 1);
}
@@ -651,6 +739,12 @@ static void hpet_reset(DeviceState *d)
timer->config |= 0x00000004ULL << 32;
timer->period = 0ULL;
timer->wrap_flag = 0;
+#ifdef CONFIG_HPET_DRIFTFIX
+ timer->saved_period = 0;
+ timer->irqs_to_inject = 0;
+ timer->irq_rate = 1;
+ timer->divisor = 1;
+#endif
}
s->hpet_counter = 0ULL;
@@ -711,6 +805,12 @@ static int hpet_init(SysBusDevice *dev)
timer->qemu_timer = qemu_new_timer(vm_clock, hpet_timer, timer);
timer->tn = i;
timer->state = s;
+#ifdef CONFIG_HPET_DRIFTFIX
+ timer->saved_period = 0;
+ timer->irqs_to_inject = 0;
+ timer->irq_rate = 1;
+ timer->divisor = 1;
+#endif
}
/* 64-bit main counter; LegacyReplacementRoute. */
@@ -96,6 +96,11 @@ int qemu_savevm_state_complete(Monitor *
void qemu_savevm_state_cancel(Monitor *mon, QEMUFile *f);
int qemu_loadvm_state(QEMUFile *f);
+#ifdef CONFIG_HPET_DRIFTFIX
+extern int (*qemu_get_irq_delivered)(void);
+extern void (*qemu_reset_irq_delivered)(void);
+#endif
+
/* SLIRP */
void do_info_slirp(Monitor *mon);
@@ -205,6 +205,8 @@ int win2k_install_hack = 0;
int rtc_td_hack = 0;
#ifdef CONFIG_HPET_DRIFTFIX
int hpet_driftfix = 0;
+int (*qemu_get_irq_delivered)(void) = 0;
+void (*qemu_reset_irq_delivered)(void) = 0;
#endif
int usb_enabled = 0;
int singlestep = 0;