]>
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 | */ | |
a9c94277 | 25 | |
b6a0aa05 | 26 | #include "qemu/osdep.h" |
a9c94277 | 27 | #include <linux/kvm.h> |
2ae16a6a | 28 | #include "qapi/qapi-types-misc.h" |
da34e65c | 29 | #include "qapi/error.h" |
0b8fa32f | 30 | #include "qemu/module.h" |
1de7afc9 | 31 | #include "qemu/timer.h" |
54d31236 | 32 | #include "sysemu/runstate.h" |
0d09e41a PB |
33 | #include "hw/timer/i8254.h" |
34 | #include "hw/timer/i8254_internal.h" | |
9c17d615 | 35 | #include "sysemu/kvm.h" |
db1015e9 | 36 | #include "qom/object.h" |
5d17c0d2 JK |
37 | |
38 | #define KVM_PIT_REINJECT_BIT 0 | |
39 | ||
0cdd3d14 JK |
40 | #define CALIBRATION_ROUNDS 3 |
41 | ||
db1015e9 EH |
42 | typedef struct KVMPITClass KVMPITClass; |
43 | typedef struct KVMPITState KVMPITState; | |
8110fa1d EH |
44 | DECLARE_OBJ_CHECKERS(KVMPITState, KVMPITClass, |
45 | KVM_PIT, TYPE_KVM_I8254) | |
58cd9864 | 46 | |
db1015e9 | 47 | struct KVMPITState { |
58cd9864 AF |
48 | PITCommonState parent_obj; |
49 | ||
5d17c0d2 | 50 | LostTickPolicy lost_tick_policy; |
205df4d1 JK |
51 | bool vm_stopped; |
52 | int64_t kernel_clock_offset; | |
db1015e9 | 53 | }; |
5d17c0d2 | 54 | |
db1015e9 | 55 | struct KVMPITClass { |
a15d0912 AF |
56 | PITCommonClass parent_class; |
57 | ||
58 | DeviceRealize parent_realize; | |
db1015e9 | 59 | }; |
a15d0912 | 60 | |
0cdd3d14 | 61 | static int64_t abs64(int64_t v) |
5d17c0d2 | 62 | { |
0cdd3d14 JK |
63 | return v < 0 ? -v : v; |
64 | } | |
65 | ||
205df4d1 | 66 | static void kvm_pit_update_clock_offset(KVMPITState *s) |
0cdd3d14 | 67 | { |
0cdd3d14 JK |
68 | int64_t offset, clock_offset; |
69 | struct timespec ts; | |
205df4d1 | 70 | int i; |
0cdd3d14 JK |
71 | |
72 | /* | |
73 | * Measure the delta between CLOCK_MONOTONIC, the base used for | |
bc72ad67 | 74 | * kvm_pit_channel_state::count_load_time, and QEMU_CLOCK_VIRTUAL. Take the |
0cdd3d14 JK |
75 | * minimum of several samples to filter out scheduling noise. |
76 | */ | |
77 | clock_offset = INT64_MAX; | |
78 | for (i = 0; i < CALIBRATION_ROUNDS; i++) { | |
bc72ad67 | 79 | offset = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); |
0cdd3d14 JK |
80 | clock_gettime(CLOCK_MONOTONIC, &ts); |
81 | offset -= ts.tv_nsec; | |
82 | offset -= (int64_t)ts.tv_sec * 1000000000; | |
83 | if (abs64(offset) < abs64(clock_offset)) { | |
84 | clock_offset = offset; | |
85 | } | |
86 | } | |
205df4d1 JK |
87 | s->kernel_clock_offset = clock_offset; |
88 | } | |
89 | ||
90 | static void kvm_pit_get(PITCommonState *pit) | |
91 | { | |
58cd9864 | 92 | KVMPITState *s = KVM_PIT(pit); |
205df4d1 JK |
93 | struct kvm_pit_state2 kpit; |
94 | struct kvm_pit_channel_state *kchan; | |
95 | struct PITChannelState *sc; | |
96 | int i, ret; | |
97 | ||
98 | /* No need to re-read the state if VM is stopped. */ | |
99 | if (s->vm_stopped) { | |
100 | return; | |
101 | } | |
0cdd3d14 | 102 | |
5d17c0d2 JK |
103 | if (kvm_has_pit_state2()) { |
104 | ret = kvm_vm_ioctl(kvm_state, KVM_GET_PIT2, &kpit); | |
105 | if (ret < 0) { | |
106 | fprintf(stderr, "KVM_GET_PIT2 failed: %s\n", strerror(ret)); | |
107 | abort(); | |
108 | } | |
0cdd3d14 | 109 | pit->channels[0].irq_disabled = kpit.flags & KVM_PIT_FLAGS_HPET_LEGACY; |
5d17c0d2 JK |
110 | } else { |
111 | /* | |
112 | * kvm_pit_state2 is superset of kvm_pit_state struct, | |
113 | * so we can use it for KVM_GET_PIT as well. | |
114 | */ | |
115 | ret = kvm_vm_ioctl(kvm_state, KVM_GET_PIT, &kpit); | |
116 | if (ret < 0) { | |
117 | fprintf(stderr, "KVM_GET_PIT failed: %s\n", strerror(ret)); | |
118 | abort(); | |
119 | } | |
120 | } | |
121 | for (i = 0; i < 3; i++) { | |
122 | kchan = &kpit.channels[i]; | |
0cdd3d14 | 123 | sc = &pit->channels[i]; |
5d17c0d2 JK |
124 | sc->count = kchan->count; |
125 | sc->latched_count = kchan->latched_count; | |
126 | sc->count_latched = kchan->count_latched; | |
127 | sc->status_latched = kchan->status_latched; | |
128 | sc->status = kchan->status; | |
129 | sc->read_state = kchan->read_state; | |
130 | sc->write_state = kchan->write_state; | |
131 | sc->write_latch = kchan->write_latch; | |
132 | sc->rw_mode = kchan->rw_mode; | |
133 | sc->mode = kchan->mode; | |
134 | sc->bcd = kchan->bcd; | |
135 | sc->gate = kchan->gate; | |
205df4d1 | 136 | sc->count_load_time = kchan->count_load_time + s->kernel_clock_offset; |
5d17c0d2 JK |
137 | } |
138 | ||
0cdd3d14 | 139 | sc = &pit->channels[0]; |
5d17c0d2 JK |
140 | sc->next_transition_time = |
141 | pit_get_next_transition_time(sc, sc->count_load_time); | |
142 | } | |
143 | ||
050a4606 | 144 | static void kvm_pit_put(PITCommonState *pit) |
5d17c0d2 | 145 | { |
58cd9864 | 146 | KVMPITState *s = KVM_PIT(pit); |
b0a05512 | 147 | struct kvm_pit_state2 kpit = {}; |
5d17c0d2 JK |
148 | struct kvm_pit_channel_state *kchan; |
149 | struct PITChannelState *sc; | |
150 | int i, ret; | |
151 | ||
050a4606 JK |
152 | /* The offset keeps changing as long as the VM is stopped. */ |
153 | if (s->vm_stopped) { | |
154 | kvm_pit_update_clock_offset(s); | |
155 | } | |
156 | ||
157 | kpit.flags = pit->channels[0].irq_disabled ? KVM_PIT_FLAGS_HPET_LEGACY : 0; | |
5d17c0d2 JK |
158 | for (i = 0; i < 3; i++) { |
159 | kchan = &kpit.channels[i]; | |
050a4606 | 160 | sc = &pit->channels[i]; |
5d17c0d2 JK |
161 | kchan->count = sc->count; |
162 | kchan->latched_count = sc->latched_count; | |
163 | kchan->count_latched = sc->count_latched; | |
164 | kchan->status_latched = sc->status_latched; | |
165 | kchan->status = sc->status; | |
166 | kchan->read_state = sc->read_state; | |
167 | kchan->write_state = sc->write_state; | |
168 | kchan->write_latch = sc->write_latch; | |
169 | kchan->rw_mode = sc->rw_mode; | |
170 | kchan->mode = sc->mode; | |
171 | kchan->bcd = sc->bcd; | |
172 | kchan->gate = sc->gate; | |
050a4606 | 173 | kchan->count_load_time = sc->count_load_time - s->kernel_clock_offset; |
5d17c0d2 JK |
174 | } |
175 | ||
176 | ret = kvm_vm_ioctl(kvm_state, | |
177 | kvm_has_pit_state2() ? KVM_SET_PIT2 : KVM_SET_PIT, | |
178 | &kpit); | |
179 | if (ret < 0) { | |
180 | fprintf(stderr, "%s failed: %s\n", | |
181 | kvm_has_pit_state2() ? "KVM_SET_PIT2" : "KVM_SET_PIT", | |
182 | strerror(ret)); | |
183 | abort(); | |
184 | } | |
185 | } | |
186 | ||
187 | static void kvm_pit_set_gate(PITCommonState *s, PITChannelState *sc, int val) | |
188 | { | |
189 | kvm_pit_get(s); | |
190 | ||
191 | switch (sc->mode) { | |
192 | default: | |
193 | case 0: | |
194 | case 4: | |
195 | /* XXX: just disable/enable counting */ | |
196 | break; | |
197 | case 1: | |
198 | case 2: | |
199 | case 3: | |
200 | case 5: | |
201 | if (sc->gate < val) { | |
202 | /* restart counting on rising edge */ | |
bc72ad67 | 203 | sc->count_load_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); |
5d17c0d2 JK |
204 | } |
205 | break; | |
206 | } | |
207 | sc->gate = val; | |
208 | ||
209 | kvm_pit_put(s); | |
210 | } | |
211 | ||
212 | static void kvm_pit_get_channel_info(PITCommonState *s, PITChannelState *sc, | |
213 | PITChannelInfo *info) | |
214 | { | |
215 | kvm_pit_get(s); | |
216 | ||
217 | pit_get_channel_info_common(s, sc, info); | |
218 | } | |
219 | ||
220 | static void kvm_pit_reset(DeviceState *dev) | |
221 | { | |
58cd9864 | 222 | PITCommonState *s = PIT_COMMON(dev); |
5d17c0d2 JK |
223 | |
224 | pit_reset_common(s); | |
225 | ||
226 | kvm_pit_put(s); | |
227 | } | |
228 | ||
229 | static void kvm_pit_irq_control(void *opaque, int n, int enable) | |
230 | { | |
231 | PITCommonState *pit = opaque; | |
232 | PITChannelState *s = &pit->channels[0]; | |
233 | ||
234 | kvm_pit_get(pit); | |
235 | ||
236 | s->irq_disabled = !enable; | |
237 | ||
238 | kvm_pit_put(pit); | |
239 | } | |
240 | ||
0cdd3d14 JK |
241 | static void kvm_pit_vm_state_change(void *opaque, int running, |
242 | RunState state) | |
243 | { | |
244 | KVMPITState *s = opaque; | |
245 | ||
246 | if (running) { | |
205df4d1 | 247 | kvm_pit_update_clock_offset(s); |
be894f51 | 248 | kvm_pit_put(PIT_COMMON(s)); |
205df4d1 | 249 | s->vm_stopped = false; |
0cdd3d14 | 250 | } else { |
205df4d1 | 251 | kvm_pit_update_clock_offset(s); |
58cd9864 | 252 | kvm_pit_get(PIT_COMMON(s)); |
205df4d1 | 253 | s->vm_stopped = true; |
0cdd3d14 JK |
254 | } |
255 | } | |
256 | ||
a15d0912 | 257 | static void kvm_pit_realizefn(DeviceState *dev, Error **errp) |
5d17c0d2 | 258 | { |
a15d0912 AF |
259 | PITCommonState *pit = PIT_COMMON(dev); |
260 | KVMPITClass *kpc = KVM_PIT_GET_CLASS(dev); | |
58cd9864 | 261 | KVMPITState *s = KVM_PIT(pit); |
5d17c0d2 JK |
262 | struct kvm_pit_config config = { |
263 | .flags = 0, | |
264 | }; | |
265 | int ret; | |
266 | ||
267 | if (kvm_check_extension(kvm_state, KVM_CAP_PIT2)) { | |
268 | ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_PIT2, &config); | |
269 | } else { | |
270 | ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_PIT); | |
271 | } | |
272 | if (ret < 0) { | |
a15d0912 AF |
273 | error_setg(errp, "Create kernel PIC irqchip failed: %s", |
274 | strerror(ret)); | |
275 | return; | |
5d17c0d2 JK |
276 | } |
277 | switch (s->lost_tick_policy) { | |
104059da | 278 | case LOST_TICK_POLICY_DELAY: |
5d17c0d2 | 279 | break; /* enabled by default */ |
104059da | 280 | case LOST_TICK_POLICY_DISCARD: |
5d17c0d2 JK |
281 | if (kvm_check_extension(kvm_state, KVM_CAP_REINJECT_CONTROL)) { |
282 | struct kvm_reinject_control control = { .pit_reinject = 0 }; | |
283 | ||
284 | ret = kvm_vm_ioctl(kvm_state, KVM_REINJECT_CONTROL, &control); | |
285 | if (ret < 0) { | |
a15d0912 AF |
286 | error_setg(errp, |
287 | "Can't disable in-kernel PIT reinjection: %s", | |
288 | strerror(ret)); | |
289 | return; | |
5d17c0d2 JK |
290 | } |
291 | } | |
292 | break; | |
293 | default: | |
a15d0912 AF |
294 | error_setg(errp, "Lost tick policy not supported."); |
295 | return; | |
5d17c0d2 JK |
296 | } |
297 | ||
257a7430 | 298 | memory_region_init_io(&pit->ioports, OBJECT(dev), NULL, NULL, "kvm-pit", 4); |
5d17c0d2 | 299 | |
a15d0912 | 300 | qdev_init_gpio_in(dev, kvm_pit_irq_control, 1); |
5d17c0d2 | 301 | |
0cdd3d14 JK |
302 | qemu_add_vm_change_state_handler(kvm_pit_vm_state_change, s); |
303 | ||
a15d0912 | 304 | kpc->parent_realize(dev, errp); |
5d17c0d2 JK |
305 | } |
306 | ||
307 | static Property kvm_pit_properties[] = { | |
c7bcc85d | 308 | DEFINE_PROP_UINT32("iobase", PITCommonState, iobase, -1), |
5d17c0d2 | 309 | DEFINE_PROP_LOSTTICKPOLICY("lost_tick_policy", KVMPITState, |
104059da | 310 | lost_tick_policy, LOST_TICK_POLICY_DELAY), |
5d17c0d2 JK |
311 | DEFINE_PROP_END_OF_LIST(), |
312 | }; | |
313 | ||
314 | static void kvm_pit_class_init(ObjectClass *klass, void *data) | |
315 | { | |
a15d0912 | 316 | KVMPITClass *kpc = KVM_PIT_CLASS(klass); |
5d17c0d2 JK |
317 | PITCommonClass *k = PIT_COMMON_CLASS(klass); |
318 | DeviceClass *dc = DEVICE_CLASS(klass); | |
319 | ||
bf853881 PMD |
320 | device_class_set_parent_realize(dc, kvm_pit_realizefn, |
321 | &kpc->parent_realize); | |
5d17c0d2 JK |
322 | k->set_channel_gate = kvm_pit_set_gate; |
323 | k->get_channel_info = kvm_pit_get_channel_info; | |
5d17c0d2 | 324 | dc->reset = kvm_pit_reset; |
4f67d30b | 325 | device_class_set_props(dc, kvm_pit_properties); |
5d17c0d2 JK |
326 | } |
327 | ||
8c43a6f0 | 328 | static const TypeInfo kvm_pit_info = { |
58cd9864 | 329 | .name = TYPE_KVM_I8254, |
5d17c0d2 JK |
330 | .parent = TYPE_PIT_COMMON, |
331 | .instance_size = sizeof(KVMPITState), | |
332 | .class_init = kvm_pit_class_init, | |
a15d0912 | 333 | .class_size = sizeof(KVMPITClass), |
5d17c0d2 JK |
334 | }; |
335 | ||
336 | static void kvm_pit_register(void) | |
337 | { | |
338 | type_register_static(&kvm_pit_info); | |
339 | } | |
340 | ||
341 | type_init(kvm_pit_register) |