|
|
1.1 ! root 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)
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.