Patchwork [v2,7/9] provide apic-kvm

login
register
mail settings
Submitter Glauber Costa
Date Nov. 16, 2009, 5:12 p.m.
Message ID <1258391527-18840-8-git-send-email-glommer@redhat.com>
Download mbox | patch
Permalink /patch/38529/
State New
Headers show

Comments

Glauber Costa - Nov. 16, 2009, 5:12 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             |    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
Alexander Graf - Dec. 1, 2009, 4:44 p.m.
Glauber Costa wrote:
> 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 --git a/Makefile.target b/Makefile.target
> index 86cf0a5..761e905 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
>   

On S390X:

cc1: warnings being treated as errors
In file included from /suse/agraf/work/kvm-s390/qemu.works/vl.c:156:
/suse/agraf/work/kvm-s390/qemu.works/kvm.h:104: error: ‘struct
kvm_lapic_state’ declared inside parameter list
/suse/agraf/work/kvm-s390/qemu.works/kvm.h:104: error: its scope is only
this definition or declaration, which is probably not what you want
/suse/agraf/work/kvm-s390/qemu.works/kvm.h:105: error: ‘struct
kvm_lapic_state’ declared inside parameter list


Alex

Patch

diff --git a/Makefile.target b/Makefile.target
index 86cf0a5..761e905 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 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 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 5f62e96..c5a0915 100644
--- a/kvm.h
+++ b/kvm.h
@@ -96,6 +96,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 c671894..6eb0f21 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 6ba0b7e..0224818 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)
@@ -990,3 +993,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