qemu/hw/i386/kvm/apic.c
<<
>>
Prefs
   1/*
   2 * KVM in-kernel APIC support
   3 *
   4 * Copyright (c) 2011 Siemens AG
   5 *
   6 * Authors:
   7 *  Jan Kiszka          <jan.kiszka@siemens.com>
   8 *
   9 * This work is licensed under the terms of the GNU GPL version 2.
  10 * See the COPYING file in the top-level directory.
  11 */
  12#include "qemu/osdep.h"
  13#include "hw/i386/apic_internal.h"
  14#include "hw/pci/msi.h"
  15#include "sysemu/kvm.h"
  16
  17static inline void kvm_apic_set_reg(struct kvm_lapic_state *kapic,
  18                                    int reg_id, uint32_t val)
  19{
  20    *((uint32_t *)(kapic->regs + (reg_id << 4))) = val;
  21}
  22
  23static inline uint32_t kvm_apic_get_reg(struct kvm_lapic_state *kapic,
  24                                        int reg_id)
  25{
  26    return *((uint32_t *)(kapic->regs + (reg_id << 4)));
  27}
  28
  29void kvm_put_apic_state(DeviceState *dev, struct kvm_lapic_state *kapic)
  30{
  31    APICCommonState *s = APIC_COMMON(dev);
  32    int i;
  33
  34    memset(kapic, 0, sizeof(*kapic));
  35    kvm_apic_set_reg(kapic, 0x2, s->id << 24);
  36    kvm_apic_set_reg(kapic, 0x8, s->tpr);
  37    kvm_apic_set_reg(kapic, 0xd, s->log_dest << 24);
  38    kvm_apic_set_reg(kapic, 0xe, s->dest_mode << 28 | 0x0fffffff);
  39    kvm_apic_set_reg(kapic, 0xf, s->spurious_vec);
  40    for (i = 0; i < 8; i++) {
  41        kvm_apic_set_reg(kapic, 0x10 + i, s->isr[i]);
  42        kvm_apic_set_reg(kapic, 0x18 + i, s->tmr[i]);
  43        kvm_apic_set_reg(kapic, 0x20 + i, s->irr[i]);
  44    }
  45    kvm_apic_set_reg(kapic, 0x28, s->esr);
  46    kvm_apic_set_reg(kapic, 0x30, s->icr[0]);
  47    kvm_apic_set_reg(kapic, 0x31, s->icr[1]);
  48    for (i = 0; i < APIC_LVT_NB; i++) {
  49        kvm_apic_set_reg(kapic, 0x32 + i, s->lvt[i]);
  50    }
  51    kvm_apic_set_reg(kapic, 0x38, s->initial_count);
  52    kvm_apic_set_reg(kapic, 0x3e, s->divide_conf);
  53}
  54
  55void kvm_get_apic_state(DeviceState *dev, struct kvm_lapic_state *kapic)
  56{
  57    APICCommonState *s = APIC_COMMON(dev);
  58    int i, v;
  59
  60    s->id = kvm_apic_get_reg(kapic, 0x2) >> 24;
  61    s->tpr = kvm_apic_get_reg(kapic, 0x8);
  62    s->arb_id = kvm_apic_get_reg(kapic, 0x9);
  63    s->log_dest = kvm_apic_get_reg(kapic, 0xd) >> 24;
  64    s->dest_mode = kvm_apic_get_reg(kapic, 0xe) >> 28;
  65    s->spurious_vec = kvm_apic_get_reg(kapic, 0xf);
  66    for (i = 0; i < 8; i++) {
  67        s->isr[i] = kvm_apic_get_reg(kapic, 0x10 + i);
  68        s->tmr[i] = kvm_apic_get_reg(kapic, 0x18 + i);
  69        s->irr[i] = kvm_apic_get_reg(kapic, 0x20 + i);
  70    }
  71    s->esr = kvm_apic_get_reg(kapic, 0x28);
  72    s->icr[0] = kvm_apic_get_reg(kapic, 0x30);
  73    s->icr[1] = kvm_apic_get_reg(kapic, 0x31);
  74    for (i = 0; i < APIC_LVT_NB; i++) {
  75        s->lvt[i] = kvm_apic_get_reg(kapic, 0x32 + i);
  76    }
  77    s->initial_count = kvm_apic_get_reg(kapic, 0x38);
  78    s->divide_conf = kvm_apic_get_reg(kapic, 0x3e);
  79
  80    v = (s->divide_conf & 3) | ((s->divide_conf >> 1) & 4);
  81    s->count_shift = (v + 1) & 7;
  82
  83    s->initial_count_load_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
  84    apic_next_timer(s, s->initial_count_load_time);
  85}
  86
  87static void kvm_apic_set_base(APICCommonState *s, uint64_t val)
  88{
  89    s->apicbase = val;
  90}
  91
  92static void kvm_apic_set_tpr(APICCommonState *s, uint8_t val)
  93{
  94    s->tpr = (val & 0x0f) << 4;
  95}
  96
  97static uint8_t kvm_apic_get_tpr(APICCommonState *s)
  98{
  99    return s->tpr >> 4;
 100}
 101
 102static void kvm_apic_enable_tpr_reporting(APICCommonState *s, bool enable)
 103{
 104    struct kvm_tpr_access_ctl ctl = {
 105        .enabled = enable
 106    };
 107
 108    kvm_vcpu_ioctl(CPU(s->cpu), KVM_TPR_ACCESS_REPORTING, &ctl);
 109}
 110
 111static void kvm_apic_vapic_base_update(APICCommonState *s)
 112{
 113    struct kvm_vapic_addr vapid_addr = {
 114        .vapic_addr = s->vapic_paddr,
 115    };
 116    int ret;
 117
 118    ret = kvm_vcpu_ioctl(CPU(s->cpu), KVM_SET_VAPIC_ADDR, &vapid_addr);
 119    if (ret < 0) {
 120        fprintf(stderr, "KVM: setting VAPIC address failed (%s)\n",
 121                strerror(-ret));
 122        abort();
 123    }
 124}
 125
 126static void do_inject_external_nmi(void *data)
 127{
 128    APICCommonState *s = data;
 129    CPUState *cpu = CPU(s->cpu);
 130    uint32_t lvt;
 131    int ret;
 132
 133    cpu_synchronize_state(cpu);
 134
 135    lvt = s->lvt[APIC_LVT_LINT1];
 136    if (!(lvt & APIC_LVT_MASKED) && ((lvt >> 8) & 7) == APIC_DM_NMI) {
 137        ret = kvm_vcpu_ioctl(cpu, KVM_NMI);
 138        if (ret < 0) {
 139            fprintf(stderr, "KVM: injection failed, NMI lost (%s)\n",
 140                    strerror(-ret));
 141        }
 142    }
 143}
 144
 145static void kvm_apic_external_nmi(APICCommonState *s)
 146{
 147    run_on_cpu(CPU(s->cpu), do_inject_external_nmi, s);
 148}
 149
 150static uint64_t kvm_apic_mem_read(void *opaque, hwaddr addr,
 151                                  unsigned size)
 152{
 153    return ~(uint64_t)0;
 154}
 155
 156static void kvm_apic_mem_write(void *opaque, hwaddr addr,
 157                               uint64_t data, unsigned size)
 158{
 159    MSIMessage msg = { .address = addr, .data = data };
 160    int ret;
 161
 162    ret = kvm_irqchip_send_msi(kvm_state, msg);
 163    if (ret < 0) {
 164        fprintf(stderr, "KVM: injection failed, MSI lost (%s)\n",
 165                strerror(-ret));
 166    }
 167}
 168
 169static const MemoryRegionOps kvm_apic_io_ops = {
 170    .read = kvm_apic_mem_read,
 171    .write = kvm_apic_mem_write,
 172    .endianness = DEVICE_NATIVE_ENDIAN,
 173};
 174
 175static void kvm_apic_reset(APICCommonState *s)
 176{
 177    /* Not used by KVM, which uses the CPU mp_state instead.  */
 178    s->wait_for_sipi = 0;
 179}
 180
 181static void kvm_apic_realize(DeviceState *dev, Error **errp)
 182{
 183    APICCommonState *s = APIC_COMMON(dev);
 184
 185    memory_region_init_io(&s->io_memory, NULL, &kvm_apic_io_ops, s, "kvm-apic-msi",
 186                          APIC_SPACE_SIZE);
 187
 188    if (kvm_has_gsi_routing()) {
 189        msi_nonbroken = true;
 190    }
 191}
 192
 193static void kvm_apic_class_init(ObjectClass *klass, void *data)
 194{
 195    APICCommonClass *k = APIC_COMMON_CLASS(klass);
 196
 197    k->realize = kvm_apic_realize;
 198    k->reset = kvm_apic_reset;
 199    k->set_base = kvm_apic_set_base;
 200    k->set_tpr = kvm_apic_set_tpr;
 201    k->get_tpr = kvm_apic_get_tpr;
 202    k->enable_tpr_reporting = kvm_apic_enable_tpr_reporting;
 203    k->vapic_base_update = kvm_apic_vapic_base_update;
 204    k->external_nmi = kvm_apic_external_nmi;
 205}
 206
 207static const TypeInfo kvm_apic_info = {
 208    .name = "kvm-apic",
 209    .parent = TYPE_APIC_COMMON,
 210    .instance_size = sizeof(APICCommonState),
 211    .class_init = kvm_apic_class_init,
 212};
 213
 214static void kvm_apic_register_types(void)
 215{
 216    type_register_static(&kvm_apic_info);
 217}
 218
 219type_init(kvm_apic_register_types)
 220