Patchwork [7/9] provide apic-kvm

login
register
mail settings
Submitter Glauber Costa
Date Dec. 2, 2009, 1:46 p.m.
Message ID <1259761575-3953-8-git-send-email-glommer@redhat.com>
Download mbox | patch
Permalink /patch/40065/
State New
Headers show

Comments

Glauber Costa - Dec. 2, 2009, 1:46 p.m.
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             |    5 ++
 target-i386/cpu.h |    4 ++
 target-i386/kvm.c |   19 ++++++-
 7 files changed, 192 insertions(+), 3 deletions(-)
 create mode 100644 hw/apic-kvm.c

Patch

diff --git a/Makefile.target b/Makefile.target
index 8fb68a4..c23aeb3 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -199,7 +199,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 a58562a..ea3f93f 100644
--- a/hw/pc.c
+++ b/hw/pc.c
@@ -962,7 +962,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 3d79b9d..6b3817a 100644
--- a/hw/pc.h
+++ b/hw/pc.h
@@ -42,6 +42,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 3b358d0..cb896e3 100644
--- a/kvm.h
+++ b/kvm.h
@@ -98,6 +98,11 @@  int kvm_arch_init_vcpu(CPUState *env);
 
 void kvm_arch_reset_vcpu(CPUState *env);
 
+#ifdef TARGET_I386
+int kvm_set_lapic(CPUState *env, struct kvm_lapic_state *s);
+int kvm_get_lapic(CPUState *env, struct kvm_lapic_state *s);
+#endif
+
 int kvm_arch_set_irq(KVMState *s, int irq, int level, int *status);
 
 struct kvm_guest_debug;
diff --git a/target-i386/cpu.h b/target-i386/cpu.h
index 1ebf80e..747e4b2 100644
--- a/target-i386/cpu.h
+++ b/target-i386/cpu.h
@@ -854,6 +854,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 4eb61f1..a908819 100644
--- a/target-i386/kvm.c
+++ b/target-i386/kvm.c
@@ -348,13 +348,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)
@@ -1030,4 +1033,18 @@  int kvm_arch_set_irq(KVMState *s, int irq, int level, int *status)
     return 1;
 }
 
+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);
+}