@@ -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
new file mode 100644
@@ -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;
+}
@@ -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);
}
@@ -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);
@@ -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;
@@ -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);
@@ -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
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