]>
Commit | Line | Data |
---|---|---|
5d17c0d2 JK |
1 | /* |
2 | * KVM in-kernel PIT (i8254) support | |
3 | * | |
4 | * Copyright (c) 2003-2004 Fabrice Bellard | |
5 | * Copyright (c) 2012 Jan Kiszka, Siemens AG | |
6 | * | |
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
8 | * of this software and associated documentation files (the "Software"), to deal | |
9 | * in the Software without restriction, including without limitation the rights | |
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
11 | * copies of the Software, and to permit persons to whom the Software is | |
12 | * furnished to do so, subject to the following conditions: | |
13 | * | |
14 | * The above copyright notice and this permission notice shall be included in | |
15 | * all copies or substantial portions of the Software. | |
16 | * | |
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
20 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
23 | * THE SOFTWARE. | |
24 | */ | |
1de7afc9 | 25 | #include "qemu/timer.h" |
0cdd3d14 | 26 | #include "sysemu.h" |
5d17c0d2 JK |
27 | #include "hw/i8254.h" |
28 | #include "hw/i8254_internal.h" | |
29 | #include "kvm.h" | |
30 | ||
31 | #define KVM_PIT_REINJECT_BIT 0 | |
32 | ||
0cdd3d14 JK |
33 | #define CALIBRATION_ROUNDS 3 |
34 | ||
5d17c0d2 JK |
35 | typedef struct KVMPITState { |
36 | PITCommonState pit; | |
37 | LostTickPolicy lost_tick_policy; | |
205df4d1 JK |
38 | bool vm_stopped; |
39 | int64_t kernel_clock_offset; | |
5d17c0d2 JK |
40 | } KVMPITState; |
41 | ||
0cdd3d14 | 42 | static int64_t abs64(int64_t v) |
5d17c0d2 | 43 | { |
0cdd3d14 JK |
44 | return v < 0 ? -v : v; |
45 | } | |
46 | ||
205df4d1 | 47 | static void kvm_pit_update_clock_offset(KVMPITState *s) |
0cdd3d14 | 48 | { |
0cdd3d14 JK |
49 | int64_t offset, clock_offset; |
50 | struct timespec ts; | |
205df4d1 | 51 | int i; |
0cdd3d14 JK |
52 | |
53 | /* | |
54 | * Measure the delta between CLOCK_MONOTONIC, the base used for | |
55 | * kvm_pit_channel_state::count_load_time, and vm_clock. Take the | |
56 | * minimum of several samples to filter out scheduling noise. | |
57 | */ | |
58 | clock_offset = INT64_MAX; | |
59 | for (i = 0; i < CALIBRATION_ROUNDS; i++) { | |
60 | offset = qemu_get_clock_ns(vm_clock); | |
61 | clock_gettime(CLOCK_MONOTONIC, &ts); | |
62 | offset -= ts.tv_nsec; | |
63 | offset -= (int64_t)ts.tv_sec * 1000000000; | |
64 | if (abs64(offset) < abs64(clock_offset)) { | |
65 | clock_offset = offset; | |
66 | } | |
67 | } | |
205df4d1 JK |
68 | s->kernel_clock_offset = clock_offset; |
69 | } | |
70 | ||
71 | static void kvm_pit_get(PITCommonState *pit) | |
72 | { | |
73 | KVMPITState *s = DO_UPCAST(KVMPITState, pit, pit); | |
74 | struct kvm_pit_state2 kpit; | |
75 | struct kvm_pit_channel_state *kchan; | |
76 | struct PITChannelState *sc; | |
77 | int i, ret; | |
78 | ||
79 | /* No need to re-read the state if VM is stopped. */ | |
80 | if (s->vm_stopped) { | |
81 | return; | |
82 | } | |
0cdd3d14 | 83 | |
5d17c0d2 JK |
84 | if (kvm_has_pit_state2()) { |
85 | ret = kvm_vm_ioctl(kvm_state, KVM_GET_PIT2, &kpit); | |
86 | if (ret < 0) { | |
87 | fprintf(stderr, "KVM_GET_PIT2 failed: %s\n", strerror(ret)); | |
88 | abort(); | |
89 | } | |
0cdd3d14 | 90 | pit->channels[0].irq_disabled = kpit.flags & KVM_PIT_FLAGS_HPET_LEGACY; |
5d17c0d2 JK |
91 | } else { |
92 | /* | |
93 | * kvm_pit_state2 is superset of kvm_pit_state struct, | |
94 | * so we can use it for KVM_GET_PIT as well. | |
95 | */ | |
96 | ret = kvm_vm_ioctl(kvm_state, KVM_GET_PIT, &kpit); | |
97 | if (ret < 0) { | |
98 | fprintf(stderr, "KVM_GET_PIT failed: %s\n", strerror(ret)); | |
99 | abort(); | |
100 | } | |
101 | } | |
102 | for (i = 0; i < 3; i++) { | |
103 | kchan = &kpit.channels[i]; | |
0cdd3d14 | 104 | sc = &pit->channels[i]; |
5d17c0d2 JK |
105 | sc->count = kchan->count; |
106 | sc->latched_count = kchan->latched_count; | |
107 | sc->count_latched = kchan->count_latched; | |
108 | sc->status_latched = kchan->status_latched; | |
109 | sc->status = kchan->status; | |
110 | sc->read_state = kchan->read_state; | |
111 | sc->write_state = kchan->write_state; | |
112 | sc->write_latch = kchan->write_latch; | |
113 | sc->rw_mode = kchan->rw_mode; | |
114 | sc->mode = kchan->mode; | |
115 | sc->bcd = kchan->bcd; | |
116 | sc->gate = kchan->gate; | |
205df4d1 | 117 | sc->count_load_time = kchan->count_load_time + s->kernel_clock_offset; |
5d17c0d2 JK |
118 | } |
119 | ||
0cdd3d14 | 120 | sc = &pit->channels[0]; |
5d17c0d2 JK |
121 | sc->next_transition_time = |
122 | pit_get_next_transition_time(sc, sc->count_load_time); | |
123 | } | |
124 | ||
050a4606 | 125 | static void kvm_pit_put(PITCommonState *pit) |
5d17c0d2 | 126 | { |
050a4606 | 127 | KVMPITState *s = DO_UPCAST(KVMPITState, pit, pit); |
5d17c0d2 JK |
128 | struct kvm_pit_state2 kpit; |
129 | struct kvm_pit_channel_state *kchan; | |
130 | struct PITChannelState *sc; | |
131 | int i, ret; | |
132 | ||
050a4606 JK |
133 | /* The offset keeps changing as long as the VM is stopped. */ |
134 | if (s->vm_stopped) { | |
135 | kvm_pit_update_clock_offset(s); | |
136 | } | |
137 | ||
138 | kpit.flags = pit->channels[0].irq_disabled ? KVM_PIT_FLAGS_HPET_LEGACY : 0; | |
5d17c0d2 JK |
139 | for (i = 0; i < 3; i++) { |
140 | kchan = &kpit.channels[i]; | |
050a4606 | 141 | sc = &pit->channels[i]; |
5d17c0d2 JK |
142 | kchan->count = sc->count; |
143 | kchan->latched_count = sc->latched_count; | |
144 | kchan->count_latched = sc->count_latched; | |
145 | kchan->status_latched = sc->status_latched; | |
146 | kchan->status = sc->status; | |
147 | kchan->read_state = sc->read_state; | |
148 | kchan->write_state = sc->write_state; | |
149 | kchan->write_latch = sc->write_latch; | |
150 | kchan->rw_mode = sc->rw_mode; | |
151 | kchan->mode = sc->mode; | |
152 | kchan->bcd = sc->bcd; | |
153 | kchan->gate = sc->gate; | |
050a4606 | 154 | kchan->count_load_time = sc->count_load_time - s->kernel_clock_offset; |
5d17c0d2 JK |
155 | } |
156 | ||
157 | ret = kvm_vm_ioctl(kvm_state, | |
158 | kvm_has_pit_state2() ? KVM_SET_PIT2 : KVM_SET_PIT, | |
159 | &kpit); | |
160 | if (ret < 0) { | |
161 | fprintf(stderr, "%s failed: %s\n", | |
162 | kvm_has_pit_state2() ? "KVM_SET_PIT2" : "KVM_SET_PIT", | |
163 | strerror(ret)); | |
164 | abort(); | |
165 | } | |
166 | } | |
167 | ||
168 | static void kvm_pit_set_gate(PITCommonState *s, PITChannelState *sc, int val) | |
169 | { | |
170 | kvm_pit_get(s); | |
171 | ||
172 | switch (sc->mode) { | |
173 | default: | |
174 | case 0: | |
175 | case 4: | |
176 | /* XXX: just disable/enable counting */ | |
177 | break; | |
178 | case 1: | |
179 | case 2: | |
180 | case 3: | |
181 | case 5: | |
182 | if (sc->gate < val) { | |
183 | /* restart counting on rising edge */ | |
184 | sc->count_load_time = qemu_get_clock_ns(vm_clock); | |
185 | } | |
186 | break; | |
187 | } | |
188 | sc->gate = val; | |
189 | ||
190 | kvm_pit_put(s); | |
191 | } | |
192 | ||
193 | static void kvm_pit_get_channel_info(PITCommonState *s, PITChannelState *sc, | |
194 | PITChannelInfo *info) | |
195 | { | |
196 | kvm_pit_get(s); | |
197 | ||
198 | pit_get_channel_info_common(s, sc, info); | |
199 | } | |
200 | ||
201 | static void kvm_pit_reset(DeviceState *dev) | |
202 | { | |
203 | PITCommonState *s = DO_UPCAST(PITCommonState, dev.qdev, dev); | |
204 | ||
205 | pit_reset_common(s); | |
206 | ||
207 | kvm_pit_put(s); | |
208 | } | |
209 | ||
210 | static void kvm_pit_irq_control(void *opaque, int n, int enable) | |
211 | { | |
212 | PITCommonState *pit = opaque; | |
213 | PITChannelState *s = &pit->channels[0]; | |
214 | ||
215 | kvm_pit_get(pit); | |
216 | ||
217 | s->irq_disabled = !enable; | |
218 | ||
219 | kvm_pit_put(pit); | |
220 | } | |
221 | ||
0cdd3d14 JK |
222 | static void kvm_pit_vm_state_change(void *opaque, int running, |
223 | RunState state) | |
224 | { | |
225 | KVMPITState *s = opaque; | |
226 | ||
227 | if (running) { | |
205df4d1 JK |
228 | kvm_pit_update_clock_offset(s); |
229 | s->vm_stopped = false; | |
0cdd3d14 | 230 | } else { |
205df4d1 | 231 | kvm_pit_update_clock_offset(s); |
0cdd3d14 | 232 | kvm_pit_get(&s->pit); |
205df4d1 | 233 | s->vm_stopped = true; |
0cdd3d14 JK |
234 | } |
235 | } | |
236 | ||
5d17c0d2 JK |
237 | static int kvm_pit_initfn(PITCommonState *pit) |
238 | { | |
239 | KVMPITState *s = DO_UPCAST(KVMPITState, pit, pit); | |
240 | struct kvm_pit_config config = { | |
241 | .flags = 0, | |
242 | }; | |
243 | int ret; | |
244 | ||
245 | if (kvm_check_extension(kvm_state, KVM_CAP_PIT2)) { | |
246 | ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_PIT2, &config); | |
247 | } else { | |
248 | ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_PIT); | |
249 | } | |
250 | if (ret < 0) { | |
251 | fprintf(stderr, "Create kernel PIC irqchip failed: %s\n", | |
252 | strerror(ret)); | |
253 | return ret; | |
254 | } | |
255 | switch (s->lost_tick_policy) { | |
256 | case LOST_TICK_DELAY: | |
257 | break; /* enabled by default */ | |
258 | case LOST_TICK_DISCARD: | |
259 | if (kvm_check_extension(kvm_state, KVM_CAP_REINJECT_CONTROL)) { | |
260 | struct kvm_reinject_control control = { .pit_reinject = 0 }; | |
261 | ||
262 | ret = kvm_vm_ioctl(kvm_state, KVM_REINJECT_CONTROL, &control); | |
263 | if (ret < 0) { | |
264 | fprintf(stderr, | |
265 | "Can't disable in-kernel PIT reinjection: %s\n", | |
266 | strerror(ret)); | |
267 | return ret; | |
268 | } | |
269 | } | |
270 | break; | |
271 | default: | |
272 | return -EINVAL; | |
273 | } | |
274 | ||
275 | memory_region_init_reservation(&pit->ioports, "kvm-pit", 4); | |
276 | ||
277 | qdev_init_gpio_in(&pit->dev.qdev, kvm_pit_irq_control, 1); | |
278 | ||
0cdd3d14 JK |
279 | qemu_add_vm_change_state_handler(kvm_pit_vm_state_change, s); |
280 | ||
5d17c0d2 JK |
281 | return 0; |
282 | } | |
283 | ||
284 | static Property kvm_pit_properties[] = { | |
285 | DEFINE_PROP_HEX32("iobase", KVMPITState, pit.iobase, -1), | |
286 | DEFINE_PROP_LOSTTICKPOLICY("lost_tick_policy", KVMPITState, | |
287 | lost_tick_policy, LOST_TICK_DELAY), | |
288 | DEFINE_PROP_END_OF_LIST(), | |
289 | }; | |
290 | ||
291 | static void kvm_pit_class_init(ObjectClass *klass, void *data) | |
292 | { | |
293 | PITCommonClass *k = PIT_COMMON_CLASS(klass); | |
294 | DeviceClass *dc = DEVICE_CLASS(klass); | |
295 | ||
296 | k->init = kvm_pit_initfn; | |
297 | k->set_channel_gate = kvm_pit_set_gate; | |
298 | k->get_channel_info = kvm_pit_get_channel_info; | |
299 | k->pre_save = kvm_pit_get; | |
300 | k->post_load = kvm_pit_put; | |
301 | dc->reset = kvm_pit_reset; | |
302 | dc->props = kvm_pit_properties; | |
303 | } | |
304 | ||
305 | static TypeInfo kvm_pit_info = { | |
306 | .name = "kvm-pit", | |
307 | .parent = TYPE_PIT_COMMON, | |
308 | .instance_size = sizeof(KVMPITState), | |
309 | .class_init = kvm_pit_class_init, | |
310 | }; | |
311 | ||
312 | static void kvm_pit_register(void) | |
313 | { | |
314 | type_register_static(&kvm_pit_info); | |
315 | } | |
316 | ||
317 | type_init(kvm_pit_register) |