diff mbox

[6/8] provide apic-kvm

Message ID 1257531990-19437-7-git-send-email-glommer@redhat.com
State New
Headers show

Commit Message

Glauber Costa Nov. 6, 2009, 6:26 p.m. UTC
This patch provides the file apic-kvm.c, which implements a schim over
the kvm in-kernel APIC.

Signed-off-by: Glauber Costa <glommer@redhat.com>
---
 Makefile.target   |    2 +-
 hw/apic-kvm.c     |  157 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/pc.c           |    6 ++-
 hw/pc.h           |    2 +
 kvm.h             |    3 +
 target-i386/cpu.h |    4 ++
 target-i386/kvm.c |   23 ++++++++-
 7 files changed, 194 insertions(+), 3 deletions(-)
 create mode 100644 hw/apic-kvm.c
diff mbox

Patch

diff --git a/Makefile.target b/Makefile.target
index 37b692e..50004d5 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -197,7 +197,7 @@  obj-i386-y += usb-uhci.o vmmouse.o vmport.o vmware_vga.o hpet.o
 obj-i386-y += device-hotplug.o pci-hotplug.o smbios.o wdt_ib700.o
 obj-i386-y += ne2000-isa.o
 
-obj-i386-$(CONFIG_KVM) += ioapic-kvm.o i8259-kvm.o
+obj-i386-$(CONFIG_KVM) += ioapic-kvm.o i8259-kvm.o apic-kvm.o
 
 # shared objects
 obj-ppc-y = ppc.o ide/core.o ide/qdev.o ide/isa.o ide/pci.o ide/macio.o
diff --git a/hw/apic-kvm.c b/hw/apic-kvm.c
new file mode 100644
index 0000000..089fa45
--- /dev/null
+++ b/hw/apic-kvm.c
@@ -0,0 +1,157 @@ 
+#include "hw.h"
+#include "pc.h"
+#include "pci.h"
+#include "msix.h"
+#include "qemu-timer.h"
+#include "host-utils.h"
+#include "kvm.h"
+
+#define APIC_LVT_NB      6
+#define APIC_LVT_LINT0   3
+
+struct qemu_lapic_state {
+    uint32_t apicbase;
+    uint8_t id;
+    uint8_t arb_id;
+    uint8_t tpr;
+    uint32_t spurious_vec;
+    uint8_t log_dest;
+    uint8_t dest_mode;
+    uint32_t isr[8];  /* in service register */
+    uint32_t tmr[8];  /* trigger mode register */
+    uint32_t irr[8]; /* interrupt request register */
+    uint32_t lvt[APIC_LVT_NB];
+    uint32_t esr; /* error register */
+    uint32_t icr[2];
+
+    uint32_t divide_conf;
+    int count_shift;
+    uint32_t initial_count;
+    int64_t initial_count_load_time, next_time;
+    uint32_t idx;
+    int sipi_vector;
+    int wait_for_sipi;
+};
+
+typedef struct APICState {
+    CPUState *cpu_env;
+
+/* KVM lapic structure is just a big array of regs. But it is what kvm
+ * functions expect. So have both the fields separated, for easy access,
+ * and the kvm stucture, for ioctls communications */
+    union {
+        struct qemu_lapic_state dev;
+        struct kvm_lapic_state kvm_lapic_state;
+    };
+} APICState;
+
+void kvm_apic_set_base(CPUState *env, uint64_t val)
+{
+    APICState *s = env->apic_state;
+
+    if (!s)
+        return;
+
+    s->dev.apicbase = val;
+}
+
+uint64_t kvm_apic_get_base(CPUState *env)
+{
+    APICState *s = env->apic_state;
+
+    return s ? s->dev.apicbase : 0;
+}
+
+static void apic_pre_save(void *opaque)
+{
+    APICState *s = opaque;
+
+    kvm_get_lapic(s->cpu_env, &s->kvm_lapic_state);
+}
+
+static int apic_post_load(void *opaque, int version_id)
+{
+    APICState *s = opaque;
+
+    return kvm_set_lapic(s->cpu_env, &s->kvm_lapic_state);
+}
+
+static const VMStateDescription vmstate_apic = {
+    .name = "apic-kvm",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields      = (VMStateField []) {
+        VMSTATE_BUFFER_UNSAFE(kvm_lapic_state.regs, APICState, 1, KVM_APIC_REG_SIZE),
+        VMSTATE_END_OF_LIST()
+    },
+    .pre_save = apic_pre_save,
+    .post_load = apic_post_load,
+};
+
+static void kvm_apic_reset(void *opaque)
+{
+    APICState *s = opaque;
+    int bsp;
+    int i;
+
+    cpu_synchronize_state(s->cpu_env);
+
+    bsp = cpu_is_bsp(s->cpu_env);
+
+    s->dev.apicbase = 0xfee00000 |
+        (bsp ? MSR_IA32_APICBASE_BSP : 0) | MSR_IA32_APICBASE_ENABLE;
+
+    cpu_reset(s->cpu_env);
+
+    s->dev.tpr = 0;
+    s->dev.spurious_vec = 0xff;
+    s->dev.log_dest = 0;
+    s->dev.dest_mode = 0xf;
+    memset(s->dev.isr, 0, sizeof(s->dev.isr));
+    memset(s->dev.tmr, 0, sizeof(s->dev.tmr));
+    memset(s->dev.irr, 0, sizeof(s->dev.irr));
+    for(i = 0; i < APIC_LVT_NB; i++)
+        s->dev.lvt[i] = 1 << 16; /* mask LVT */
+    s->dev.esr = 0;
+    memset(s->dev.icr, 0, sizeof(s->dev.icr));
+    s->dev.divide_conf = 0;
+    s->dev.count_shift = 0;
+    s->dev.initial_count = 0;
+    s->dev.initial_count_load_time = 0;
+    s->dev.next_time = 0;
+    s->dev.wait_for_sipi = 1;
+
+    s->cpu_env->halted = !(s->dev.apicbase & MSR_IA32_APICBASE_BSP);
+
+    s->cpu_env->mp_state
+            = s->cpu_env->halted ? KVM_MP_STATE_UNINITIALIZED : KVM_MP_STATE_RUNNABLE;
+
+    kvm_put_mp_state(s->cpu_env);
+
+    if (bsp) {
+        /*
+         * LINT0 delivery mode on CPU #0 is set to ExtInt at initialization
+         * time typically by BIOS, so PIC interrupt can be delivered to the
+         * processor when local APIC is enabled.
+         */
+        s->dev.lvt[APIC_LVT_LINT0] = 0x700;
+    }
+    kvm_set_lapic(s->cpu_env, &s->kvm_lapic_state);
+}
+
+int kvm_apic_init(CPUState *env)
+{
+    APICState *s;
+
+    s = qemu_mallocz(sizeof(*s));
+    env->apic_state = s;
+    s->cpu_env = env;
+
+    msix_supported = 1;
+
+    vmstate_register(s->dev.id, &vmstate_apic, s);
+    qemu_register_reset(kvm_apic_reset, s);
+
+    return 0;
+}
diff --git a/hw/pc.c b/hw/pc.c
index 003ae11..ddc8d32 100644
--- a/hw/pc.c
+++ b/hw/pc.c
@@ -1018,7 +1018,11 @@  static CPUState *pc_new_cpu(const char *cpu_model)
     if ((env->cpuid_features & CPUID_APIC) || smp_cpus > 1) {
         env->cpuid_apic_id = env->cpu_index;
         /* APIC reset callback resets cpu */
-        apic_init(env);
+        if (kvm_enabled() && kvm_irqchip_in_kernel()) {
+            kvm_apic_init(env);
+        } else {
+            apic_init(env);
+        }
     } else {
         qemu_register_reset((QEMUResetHandler*)cpu_reset, env);
     }
diff --git a/hw/pc.h b/hw/pc.h
index 7493760..aed9955 100644
--- a/hw/pc.h
+++ b/hw/pc.h
@@ -41,6 +41,8 @@  void apic_deliver_irq(uint8_t dest, uint8_t dest_mode,
                              uint8_t vector_num, uint8_t polarity,
                              uint8_t trigger_mode);
 int apic_init(CPUState *env);
+int kvm_apic_init(CPUState *env);
+
 int apic_accept_pic_intr(CPUState *env);
 void apic_deliver_pic_intr(CPUState *env, int level);
 int apic_get_interrupt(CPUState *env);
diff --git a/kvm.h b/kvm.h
index 2765352..f0c9201 100644
--- a/kvm.h
+++ b/kvm.h
@@ -99,6 +99,9 @@  int kvm_arch_init(KVMState *s, int smp_cpus);
 
 int kvm_arch_init_vcpu(CPUState *env);
 
+int kvm_set_lapic(CPUState *env, struct kvm_lapic_state *s);
+int kvm_get_lapic(CPUState *env, struct kvm_lapic_state *s);
+
 struct kvm_guest_debug;
 struct kvm_debug_exit_arch;
 
diff --git a/target-i386/cpu.h b/target-i386/cpu.h
index e5470f7..991ae43 100644
--- a/target-i386/cpu.h
+++ b/target-i386/cpu.h
@@ -855,6 +855,10 @@  void cpu_set_apic_tpr(CPUX86State *env, uint8_t val);
 uint8_t cpu_get_apic_tpr(CPUX86State *env);
 #endif
 
+/* hw/apic-kvm.c */
+void kvm_apic_set_base(CPUX86State *env, uint64_t val);
+uint64_t kvm_apic_get_base(CPUX86State *env);
+
 /* hw/pc.c */
 void cpu_smm_update(CPUX86State *env);
 uint64_t cpu_get_tsc(CPUX86State *env);
diff --git a/target-i386/kvm.c b/target-i386/kvm.c
index 7c761f6..842cd00 100644
--- a/target-i386/kvm.c
+++ b/target-i386/kvm.c
@@ -342,13 +342,16 @@  static void kvm_set_apic_base(CPUState *env, uint64_t val)
 {
     if (!kvm_irqchip_in_kernel())
         cpu_set_apic_base(env, val);
+    else
+        kvm_apic_set_base(env, val);
 }
 
 static uint64_t kvm_get_apic_base(CPUState *env)
 {
     if (!kvm_irqchip_in_kernel())
         return cpu_get_apic_base(env);
-    return 0;
+    else
+        return kvm_apic_get_base(env);
 }
 
 static void kvm_getput_reg(__u64 *kvm_reg, target_ulong *qemu_reg, int set)
@@ -970,3 +973,21 @@  void kvm_arch_update_guest_debug(CPUState *env, struct kvm_guest_debug *dbg)
     }
 }
 #endif /* KVM_CAP_SET_GUEST_DEBUG */
+
+#ifdef KVM_CAP_IRQCHIP
+int kvm_set_lapic(CPUState *env, struct kvm_lapic_state *s)
+{
+    if (!kvm_irqchip_in_kernel())
+        return 0;
+
+    return kvm_vcpu_ioctl(env, KVM_SET_LAPIC, s);
+}
+
+int kvm_get_lapic(CPUState *env, struct kvm_lapic_state *s)
+{
+    if (!kvm_irqchip_in_kernel())
+        return 0;
+
+    return kvm_vcpu_ioctl(env, KVM_GET_LAPIC, s);
+}
+#endif