Tizen 2.1 base
[sdk/emulator/qemu.git] / hw / kvm / i8254.c
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.h"
27 #include "hw/i8254.h"
28 #include "hw/i8254_internal.h"
29 #include "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 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)