Annotation of qemu/hw/kvm/i8254.c, revision 1.1.1.1

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)

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.