root / hw / kvm / i8254.c @ 0cdd3d14
History | View | Annotate | Download (8.6 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.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 state_valid;
|
39 |
} KVMPITState; |
40 |
|
41 |
static int64_t abs64(int64_t v)
|
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); |
49 |
struct kvm_pit_state2 kpit;
|
50 |
struct kvm_pit_channel_state *kchan;
|
51 |
struct PITChannelState *sc;
|
52 |
int64_t offset, clock_offset; |
53 |
struct timespec ts;
|
54 |
int i, ret;
|
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 |
|
76 |
if (kvm_has_pit_state2()) {
|
77 |
ret = kvm_vm_ioctl(kvm_state, KVM_GET_PIT2, &kpit); |
78 |
if (ret < 0) { |
79 |
fprintf(stderr, "KVM_GET_PIT2 failed: %s\n", strerror(ret));
|
80 |
abort(); |
81 |
} |
82 |
pit->channels[0].irq_disabled = kpit.flags & KVM_PIT_FLAGS_HPET_LEGACY;
|
83 |
} else {
|
84 |
/*
|
85 |
* kvm_pit_state2 is superset of kvm_pit_state struct,
|
86 |
* so we can use it for KVM_GET_PIT as well.
|
87 |
*/
|
88 |
ret = kvm_vm_ioctl(kvm_state, KVM_GET_PIT, &kpit); |
89 |
if (ret < 0) { |
90 |
fprintf(stderr, "KVM_GET_PIT failed: %s\n", strerror(ret));
|
91 |
abort(); |
92 |
} |
93 |
} |
94 |
for (i = 0; i < 3; i++) { |
95 |
kchan = &kpit.channels[i]; |
96 |
sc = &pit->channels[i]; |
97 |
sc->count = kchan->count; |
98 |
sc->latched_count = kchan->latched_count; |
99 |
sc->count_latched = kchan->count_latched; |
100 |
sc->status_latched = kchan->status_latched; |
101 |
sc->status = kchan->status; |
102 |
sc->read_state = kchan->read_state; |
103 |
sc->write_state = kchan->write_state; |
104 |
sc->write_latch = kchan->write_latch; |
105 |
sc->rw_mode = kchan->rw_mode; |
106 |
sc->mode = kchan->mode; |
107 |
sc->bcd = kchan->bcd; |
108 |
sc->gate = kchan->gate; |
109 |
sc->count_load_time = kchan->count_load_time + clock_offset; |
110 |
} |
111 |
|
112 |
sc = &pit->channels[0];
|
113 |
sc->next_transition_time = |
114 |
pit_get_next_transition_time(sc, sc->count_load_time); |
115 |
} |
116 |
|
117 |
static void kvm_pit_put(PITCommonState *s) |
118 |
{ |
119 |
struct kvm_pit_state2 kpit;
|
120 |
struct kvm_pit_channel_state *kchan;
|
121 |
struct PITChannelState *sc;
|
122 |
int i, ret;
|
123 |
|
124 |
kpit.flags = s->channels[0].irq_disabled ? KVM_PIT_FLAGS_HPET_LEGACY : 0; |
125 |
for (i = 0; i < 3; i++) { |
126 |
kchan = &kpit.channels[i]; |
127 |
sc = &s->channels[i]; |
128 |
kchan->count = sc->count; |
129 |
kchan->latched_count = sc->latched_count; |
130 |
kchan->count_latched = sc->count_latched; |
131 |
kchan->status_latched = sc->status_latched; |
132 |
kchan->status = sc->status; |
133 |
kchan->read_state = sc->read_state; |
134 |
kchan->write_state = sc->write_state; |
135 |
kchan->write_latch = sc->write_latch; |
136 |
kchan->rw_mode = sc->rw_mode; |
137 |
kchan->mode = sc->mode; |
138 |
kchan->bcd = sc->bcd; |
139 |
kchan->gate = sc->gate; |
140 |
kchan->count_load_time = sc->count_load_time; |
141 |
} |
142 |
|
143 |
ret = kvm_vm_ioctl(kvm_state, |
144 |
kvm_has_pit_state2() ? KVM_SET_PIT2 : KVM_SET_PIT, |
145 |
&kpit); |
146 |
if (ret < 0) { |
147 |
fprintf(stderr, "%s failed: %s\n",
|
148 |
kvm_has_pit_state2() ? "KVM_SET_PIT2" : "KVM_SET_PIT", |
149 |
strerror(ret)); |
150 |
abort(); |
151 |
} |
152 |
} |
153 |
|
154 |
static void kvm_pit_set_gate(PITCommonState *s, PITChannelState *sc, int val) |
155 |
{ |
156 |
kvm_pit_get(s); |
157 |
|
158 |
switch (sc->mode) {
|
159 |
default:
|
160 |
case 0: |
161 |
case 4: |
162 |
/* XXX: just disable/enable counting */
|
163 |
break;
|
164 |
case 1: |
165 |
case 2: |
166 |
case 3: |
167 |
case 5: |
168 |
if (sc->gate < val) {
|
169 |
/* restart counting on rising edge */
|
170 |
sc->count_load_time = qemu_get_clock_ns(vm_clock); |
171 |
} |
172 |
break;
|
173 |
} |
174 |
sc->gate = val; |
175 |
|
176 |
kvm_pit_put(s); |
177 |
} |
178 |
|
179 |
static void kvm_pit_get_channel_info(PITCommonState *s, PITChannelState *sc, |
180 |
PITChannelInfo *info) |
181 |
{ |
182 |
kvm_pit_get(s); |
183 |
|
184 |
pit_get_channel_info_common(s, sc, info); |
185 |
} |
186 |
|
187 |
static void kvm_pit_reset(DeviceState *dev) |
188 |
{ |
189 |
PITCommonState *s = DO_UPCAST(PITCommonState, dev.qdev, dev); |
190 |
|
191 |
pit_reset_common(s); |
192 |
|
193 |
kvm_pit_put(s); |
194 |
} |
195 |
|
196 |
static void kvm_pit_irq_control(void *opaque, int n, int enable) |
197 |
{ |
198 |
PITCommonState *pit = opaque; |
199 |
PITChannelState *s = &pit->channels[0];
|
200 |
|
201 |
kvm_pit_get(pit); |
202 |
|
203 |
s->irq_disabled = !enable; |
204 |
|
205 |
kvm_pit_put(pit); |
206 |
} |
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 |
|
221 |
static int kvm_pit_initfn(PITCommonState *pit) |
222 |
{ |
223 |
KVMPITState *s = DO_UPCAST(KVMPITState, pit, pit); |
224 |
struct kvm_pit_config config = {
|
225 |
.flags = 0,
|
226 |
}; |
227 |
int ret;
|
228 |
|
229 |
if (kvm_check_extension(kvm_state, KVM_CAP_PIT2)) {
|
230 |
ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_PIT2, &config); |
231 |
} else {
|
232 |
ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_PIT); |
233 |
} |
234 |
if (ret < 0) { |
235 |
fprintf(stderr, "Create kernel PIC irqchip failed: %s\n",
|
236 |
strerror(ret)); |
237 |
return ret;
|
238 |
} |
239 |
switch (s->lost_tick_policy) {
|
240 |
case LOST_TICK_DELAY:
|
241 |
break; /* enabled by default */ |
242 |
case LOST_TICK_DISCARD:
|
243 |
if (kvm_check_extension(kvm_state, KVM_CAP_REINJECT_CONTROL)) {
|
244 |
struct kvm_reinject_control control = { .pit_reinject = 0 }; |
245 |
|
246 |
ret = kvm_vm_ioctl(kvm_state, KVM_REINJECT_CONTROL, &control); |
247 |
if (ret < 0) { |
248 |
fprintf(stderr, |
249 |
"Can't disable in-kernel PIT reinjection: %s\n",
|
250 |
strerror(ret)); |
251 |
return ret;
|
252 |
} |
253 |
} |
254 |
break;
|
255 |
default:
|
256 |
return -EINVAL;
|
257 |
} |
258 |
|
259 |
memory_region_init_reservation(&pit->ioports, "kvm-pit", 4); |
260 |
|
261 |
qdev_init_gpio_in(&pit->dev.qdev, kvm_pit_irq_control, 1);
|
262 |
|
263 |
qemu_add_vm_change_state_handler(kvm_pit_vm_state_change, s); |
264 |
|
265 |
return 0; |
266 |
} |
267 |
|
268 |
static Property kvm_pit_properties[] = {
|
269 |
DEFINE_PROP_HEX32("iobase", KVMPITState, pit.iobase, -1), |
270 |
DEFINE_PROP_LOSTTICKPOLICY("lost_tick_policy", KVMPITState,
|
271 |
lost_tick_policy, LOST_TICK_DELAY), |
272 |
DEFINE_PROP_END_OF_LIST(), |
273 |
}; |
274 |
|
275 |
static void kvm_pit_class_init(ObjectClass *klass, void *data) |
276 |
{ |
277 |
PITCommonClass *k = PIT_COMMON_CLASS(klass); |
278 |
DeviceClass *dc = DEVICE_CLASS(klass); |
279 |
|
280 |
k->init = kvm_pit_initfn; |
281 |
k->set_channel_gate = kvm_pit_set_gate; |
282 |
k->get_channel_info = kvm_pit_get_channel_info; |
283 |
k->pre_save = kvm_pit_get; |
284 |
k->post_load = kvm_pit_put; |
285 |
dc->reset = kvm_pit_reset; |
286 |
dc->props = kvm_pit_properties; |
287 |
} |
288 |
|
289 |
static TypeInfo kvm_pit_info = {
|
290 |
.name = "kvm-pit",
|
291 |
.parent = TYPE_PIT_COMMON, |
292 |
.instance_size = sizeof(KVMPITState),
|
293 |
.class_init = kvm_pit_class_init, |
294 |
}; |
295 |
|
296 |
static void kvm_pit_register(void) |
297 |
{ |
298 |
type_register_static(&kvm_pit_info); |
299 |
} |
300 |
|
301 |
type_init(kvm_pit_register) |