Revision 0cdd3d14
b/hw/kvm/i8254.c | ||
---|---|---|
23 | 23 |
* THE SOFTWARE. |
24 | 24 |
*/ |
25 | 25 |
#include "qemu-timer.h" |
26 |
#include "sysemu.h" |
|
26 | 27 |
#include "hw/i8254.h" |
27 | 28 |
#include "hw/i8254_internal.h" |
28 | 29 |
#include "kvm.h" |
29 | 30 |
|
30 | 31 |
#define KVM_PIT_REINJECT_BIT 0 |
31 | 32 |
|
33 |
#define CALIBRATION_ROUNDS 3 |
|
34 |
|
|
32 | 35 |
typedef struct KVMPITState { |
33 | 36 |
PITCommonState pit; |
34 | 37 |
LostTickPolicy lost_tick_policy; |
38 |
bool state_valid; |
|
35 | 39 |
} KVMPITState; |
36 | 40 |
|
37 |
static void kvm_pit_get(PITCommonState *s)
|
|
41 |
static int64_t abs64(int64_t v)
|
|
38 | 42 |
{ |
43 |
return v < 0 ? -v : v; |
|
44 |
} |
|
45 |
|
|
46 |
static void kvm_pit_get(PITCommonState *pit) |
|
47 |
{ |
|
48 |
KVMPITState *s = DO_UPCAST(KVMPITState, pit, pit); |
|
39 | 49 |
struct kvm_pit_state2 kpit; |
40 | 50 |
struct kvm_pit_channel_state *kchan; |
41 | 51 |
struct PITChannelState *sc; |
52 |
int64_t offset, clock_offset; |
|
53 |
struct timespec ts; |
|
42 | 54 |
int i, ret; |
43 | 55 |
|
56 |
if (s->state_valid) { |
|
57 |
return; |
|
58 |
} |
|
59 |
|
|
60 |
/* |
|
61 |
* Measure the delta between CLOCK_MONOTONIC, the base used for |
|
62 |
* kvm_pit_channel_state::count_load_time, and vm_clock. Take the |
|
63 |
* minimum of several samples to filter out scheduling noise. |
|
64 |
*/ |
|
65 |
clock_offset = INT64_MAX; |
|
66 |
for (i = 0; i < CALIBRATION_ROUNDS; i++) { |
|
67 |
offset = qemu_get_clock_ns(vm_clock); |
|
68 |
clock_gettime(CLOCK_MONOTONIC, &ts); |
|
69 |
offset -= ts.tv_nsec; |
|
70 |
offset -= (int64_t)ts.tv_sec * 1000000000; |
|
71 |
if (abs64(offset) < abs64(clock_offset)) { |
|
72 |
clock_offset = offset; |
|
73 |
} |
|
74 |
} |
|
75 |
|
|
44 | 76 |
if (kvm_has_pit_state2()) { |
45 | 77 |
ret = kvm_vm_ioctl(kvm_state, KVM_GET_PIT2, &kpit); |
46 | 78 |
if (ret < 0) { |
47 | 79 |
fprintf(stderr, "KVM_GET_PIT2 failed: %s\n", strerror(ret)); |
48 | 80 |
abort(); |
49 | 81 |
} |
50 |
s->channels[0].irq_disabled = kpit.flags & KVM_PIT_FLAGS_HPET_LEGACY;
|
|
82 |
pit->channels[0].irq_disabled = kpit.flags & KVM_PIT_FLAGS_HPET_LEGACY;
|
|
51 | 83 |
} else { |
52 | 84 |
/* |
53 | 85 |
* kvm_pit_state2 is superset of kvm_pit_state struct, |
... | ... | |
61 | 93 |
} |
62 | 94 |
for (i = 0; i < 3; i++) { |
63 | 95 |
kchan = &kpit.channels[i]; |
64 |
sc = &s->channels[i];
|
|
96 |
sc = &pit->channels[i];
|
|
65 | 97 |
sc->count = kchan->count; |
66 | 98 |
sc->latched_count = kchan->latched_count; |
67 | 99 |
sc->count_latched = kchan->count_latched; |
... | ... | |
74 | 106 |
sc->mode = kchan->mode; |
75 | 107 |
sc->bcd = kchan->bcd; |
76 | 108 |
sc->gate = kchan->gate; |
77 |
sc->count_load_time = kchan->count_load_time; |
|
109 |
sc->count_load_time = kchan->count_load_time + clock_offset;
|
|
78 | 110 |
} |
79 | 111 |
|
80 |
sc = &s->channels[0];
|
|
112 |
sc = &pit->channels[0];
|
|
81 | 113 |
sc->next_transition_time = |
82 | 114 |
pit_get_next_transition_time(sc, sc->count_load_time); |
83 | 115 |
} |
... | ... | |
173 | 205 |
kvm_pit_put(pit); |
174 | 206 |
} |
175 | 207 |
|
208 |
static void kvm_pit_vm_state_change(void *opaque, int running, |
|
209 |
RunState state) |
|
210 |
{ |
|
211 |
KVMPITState *s = opaque; |
|
212 |
|
|
213 |
if (running) { |
|
214 |
s->state_valid = false; |
|
215 |
} else { |
|
216 |
kvm_pit_get(&s->pit); |
|
217 |
s->state_valid = true; |
|
218 |
} |
|
219 |
} |
|
220 |
|
|
176 | 221 |
static int kvm_pit_initfn(PITCommonState *pit) |
177 | 222 |
{ |
178 | 223 |
KVMPITState *s = DO_UPCAST(KVMPITState, pit, pit); |
... | ... | |
215 | 260 |
|
216 | 261 |
qdev_init_gpio_in(&pit->dev.qdev, kvm_pit_irq_control, 1); |
217 | 262 |
|
263 |
qemu_add_vm_change_state_handler(kvm_pit_vm_state_change, s); |
|
264 |
|
|
218 | 265 |
return 0; |
219 | 266 |
} |
220 | 267 |
|
Also available in: Unified diff