root / hw / i386 / kvm / i8254.c @ 54976b75
History | View | Annotate | Download (9.2 kB)
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 |
*/
|
25 |
#include "qemu/timer.h" |
26 |
#include "sysemu/sysemu.h" |
27 |
#include "hw/timer/i8254.h" |
28 |
#include "hw/timer/i8254_internal.h" |
29 |
#include "sysemu/kvm.h" |
30 |
|
31 |
#define KVM_PIT_REINJECT_BIT 0 |
32 |
|
33 |
#define CALIBRATION_ROUNDS 3 |
34 |
|
35 |
typedef struct KVMPITState { |
36 |
PITCommonState pit; |
37 |
LostTickPolicy lost_tick_policy; |
38 |
bool vm_stopped;
|
39 |
int64_t kernel_clock_offset; |
40 |
} KVMPITState; |
41 |
|
42 |
static int64_t abs64(int64_t v)
|
43 |
{ |
44 |
return v < 0 ? -v : v; |
45 |
} |
46 |
|
47 |
static void kvm_pit_update_clock_offset(KVMPITState *s) |
48 |
{ |
49 |
int64_t offset, clock_offset; |
50 |
struct timespec ts;
|
51 |
int i;
|
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 |
} |
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 |
} |
83 |
|
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 |
} |
90 |
pit->channels[0].irq_disabled = kpit.flags & KVM_PIT_FLAGS_HPET_LEGACY;
|
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]; |
104 |
sc = &pit->channels[i]; |
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; |
117 |
sc->count_load_time = kchan->count_load_time + s->kernel_clock_offset; |
118 |
} |
119 |
|
120 |
sc = &pit->channels[0];
|
121 |
sc->next_transition_time = |
122 |
pit_get_next_transition_time(sc, sc->count_load_time); |
123 |
} |
124 |
|
125 |
static void kvm_pit_put(PITCommonState *pit) |
126 |
{ |
127 |
KVMPITState *s = DO_UPCAST(KVMPITState, pit, pit); |
128 |
struct kvm_pit_state2 kpit;
|
129 |
struct kvm_pit_channel_state *kchan;
|
130 |
struct PITChannelState *sc;
|
131 |
int i, ret;
|
132 |
|
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; |
139 |
for (i = 0; i < 3; i++) { |
140 |
kchan = &kpit.channels[i]; |
141 |
sc = &pit->channels[i]; |
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; |
154 |
kchan->count_load_time = sc->count_load_time - s->kernel_clock_offset; |
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 |
|
222 |
static void kvm_pit_vm_state_change(void *opaque, int running, |
223 |
RunState state) |
224 |
{ |
225 |
KVMPITState *s = opaque; |
226 |
|
227 |
if (running) {
|
228 |
kvm_pit_update_clock_offset(s); |
229 |
s->vm_stopped = false;
|
230 |
} else {
|
231 |
kvm_pit_update_clock_offset(s); |
232 |
kvm_pit_get(&s->pit); |
233 |
s->vm_stopped = true;
|
234 |
} |
235 |
} |
236 |
|
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 |
|
279 |
qemu_add_vm_change_state_handler(kvm_pit_vm_state_change, s); |
280 |
|
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 const 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) |