Statistics
| Branch: | Revision:

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)