From mboxrd@z Thu Jan 1 00:00:00 1970 From: Glauber Costa Subject: [PATCH 07/10] provide apic-kvm Date: Fri, 26 Feb 2010 17:12:18 -0300 Message-ID: <1267215141-13629-8-git-send-email-glommer@redhat.com> References: <1267215141-13629-1-git-send-email-glommer@redhat.com> <1267215141-13629-2-git-send-email-glommer@redhat.com> <1267215141-13629-3-git-send-email-glommer@redhat.com> <1267215141-13629-4-git-send-email-glommer@redhat.com> <1267215141-13629-5-git-send-email-glommer@redhat.com> <1267215141-13629-6-git-send-email-glommer@redhat.com> <1267215141-13629-7-git-send-email-glommer@redhat.com> Cc: mtosatti@redhat.com To: kvm@vger.kernel.org Return-path: Received: from mx1.redhat.com ([209.132.183.28]:41862 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S965998Ab0BZUMe (ORCPT ); Fri, 26 Feb 2010 15:12:34 -0500 Received: from int-mx03.intmail.prod.int.phx2.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.16]) by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id o1QKCY06023791 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Fri, 26 Feb 2010 15:12:34 -0500 In-Reply-To: <1267215141-13629-7-git-send-email-glommer@redhat.com> Sender: kvm-owner@vger.kernel.org List-ID: This patch provides the file apic-kvm.c, which implements a schim over the kvm in-kernel APIC. Signed-off-by: Glauber Costa --- 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 | 25 ++++++++- 7 files changed, 197 insertions(+), 4 deletions(-) create mode 100644 hw/apic-kvm.c diff --git a/Makefile.target b/Makefile.target index bc5263e..f00af07 100644 --- a/Makefile.target +++ b/Makefile.target @@ -213,7 +213,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 debugcon.o multiboot.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 fb40b86..66a54c0 100644 --- a/hw/pc.c +++ b/hw/pc.c @@ -776,7 +776,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 f727e30..c76831d 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 f5be35c..7278874 100644 --- a/kvm.h +++ b/kvm.h @@ -95,6 +95,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 78399a1..6678ea1 100644 --- a/target-i386/cpu.h +++ b/target-i386/cpu.h @@ -865,6 +865,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 825decd..e9a7585 100644 --- a/target-i386/kvm.c +++ b/target-i386/kvm.c @@ -423,26 +423,31 @@ 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_set_apic_tpr(CPUState *env, uint8_t val) { if (!kvm_irqchip_in_kernel()) cpu_set_apic_tpr(env, val); + else + env->cr8 = val; } static uint8_t kvm_get_apic_tpr(CPUState *env) { if (!kvm_irqchip_in_kernel()) return cpu_get_apic_tpr(env); - return 0; + return env->cr8; } @@ -1208,3 +1213,19 @@ 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); +} -- 1.6.6