[PATCH 3/4] provide a kvm-free implementation of apic

Glauber Costa
2009-06-03T21:19:31Z

Also, provide a kvm_apic that does not depend highly on common code.

Signed-off-by: Glauber Costa <glommer@redhat.com>
---
hw/apic.c | 241 ++++++++++++++++++++++++++++++++++----------------------
hw/pc.c | 7 ++-
hw/pc.h | 1 +
qemu-kvm-x86.c | 5 +-
4 files changed, 157 insertions(+), 97 deletions(-)

diff --git a/hw/apic.c b/hw/apic.c
index 39e1675..e3b606b 100644
--- a/hw/apic.c
+++ b/hw/apic.c
@@ -857,103 +857,11 @@ static void apic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
}
}

-#ifdef KVM_CAP_IRQCHIP
-
-static inline uint32_t kapic_reg(struct kvm_lapic_state *kapic, int reg_id)
-{
- return *((uint32_t *) (kapic->regs + (reg_id << 4)));
-}
-
-static inline void kapic_set_reg(struct kvm_lapic_state *kapic,
- int reg_id, uint32_t val)
-{
- *((uint32_t *) (kapic->regs + (reg_id << 4))) = val;
-}
-
-static void kvm_kernel_lapic_save_to_user(APICState *s)
-{
- struct kvm_lapic_state apic;
- struct kvm_lapic_state *kapic = &apic;
- int i, v;
-
- kvm_get_lapic(kvm_context, s->cpu_env->cpu_index, kapic);
-
- s->id = kapic_reg(kapic, 0x2) >> 24;
- s->tpr = kapic_reg(kapic, 0x8);
- s->arb_id = kapic_reg(kapic, 0x9);
- s->log_dest = kapic_reg(kapic, 0xd) >> 24;
- s->dest_mode = kapic_reg(kapic, 0xe) >> 28;
- s->spurious_vec = kapic_reg(kapic, 0xf);
- for (i = 0; i < 8; i++) {
- s->isr[i] = kapic_reg(kapic, 0x10 + i);
- s->tmr[i] = kapic_reg(kapic, 0x18 + i);
- s->irr[i] = kapic_reg(kapic, 0x20 + i);
- }
- s->esr = kapic_reg(kapic, 0x28);
- s->icr[0] = kapic_reg(kapic, 0x30);
- s->icr[1] = kapic_reg(kapic, 0x31);
- for (i = 0; i < APIC_LVT_NB; i++)
- s->lvt[i] = kapic_reg(kapic, 0x32 + i);
- s->initial_count = kapic_reg(kapic, 0x38);
- s->divide_conf = kapic_reg(kapic, 0x3e);
-
- v = (s->divide_conf & 3) | ((s->divide_conf >> 1) & 4);
- s->count_shift = (v + 1) & 7;
-
- s->initial_count_load_time = qemu_get_clock(vm_clock);
- apic_timer_update(s, s->initial_count_load_time);
-}
-
-static void kvm_kernel_lapic_load_from_user(APICState *s)
-{
- struct kvm_lapic_state apic;
- struct kvm_lapic_state *klapic = &apic;
- int i;
-
- memset(klapic, 0, sizeof apic);
- kapic_set_reg(klapic, 0x2, s->id << 24);
- kapic_set_reg(klapic, 0x8, s->tpr);
- kapic_set_reg(klapic, 0xd, s->log_dest << 24);
- kapic_set_reg(klapic, 0xe, s->dest_mode << 28 | 0x0fffffff);
- kapic_set_reg(klapic, 0xf, s->spurious_vec);
- for (i = 0; i < 8; i++) {
- kapic_set_reg(klapic, 0x10 + i, s->isr[i]);
- kapic_set_reg(klapic, 0x18 + i, s->tmr[i]);
- kapic_set_reg(klapic, 0x20 + i, s->irr[i]);
- }
- kapic_set_reg(klapic, 0x28, s->esr);
- kapic_set_reg(klapic, 0x30, s->icr[0]);
- kapic_set_reg(klapic, 0x31, s->icr[1]);
- for (i = 0; i < APIC_LVT_NB; i++)
- kapic_set_reg(klapic, 0x32 + i, s->lvt[i]);
- kapic_set_reg(klapic, 0x38, s->initial_count);
- kapic_set_reg(klapic, 0x3e, s->divide_conf);
-
- kvm_set_lapic(kvm_context, s->cpu_env->cpu_index, klapic);
-}
-
-#endif
-
-void qemu_kvm_load_lapic(CPUState *env)
-{
-#ifdef KVM_CAP_IRQCHIP
- if (kvm_enabled() && kvm_vcpu_inited(env) && qemu_kvm_irqchip_in_kernel()) {
- kvm_kernel_lapic_load_from_user(env->apic_state);
- }
-#endif
-}
-
static void apic_save(QEMUFile *f, void *opaque)
{
APICState *s = opaque;
int i;

-#ifdef KVM_CAP_IRQCHIP
- if (kvm_enabled() && qemu_kvm_irqchip_in_kernel()) {
- kvm_kernel_lapic_save_to_user(s);
- }
-#endif
-
qemu_put_be32s(f, &s->apicbase);
qemu_put_8s(f, &s->id);
qemu_put_8s(f, &s->arb_id);
@@ -1017,8 +925,6 @@ static int apic_load(QEMUFile *f, void *opaque, int version_id)
if (version_id >= 2)
qemu_get_timer(f, s->timer);

- qemu_kvm_load_lapic(s->cpu_env);
-
return 0;
}

@@ -1039,7 +945,6 @@ static void apic_reset(void *opaque)
*/
s->lvt[APIC_LVT_LINT0] = 0x700;
}
- qemu_kvm_load_lapic(s->cpu_env);
}

static CPUReadMemoryFunc *apic_mem_read[3] = {
@@ -1086,3 +991,149 @@ int apic_init(CPUState *env)
return 0;
}

+#ifdef KVM_CAP_IRQCHIP
+
+static inline uint32_t kapic_reg(struct kvm_lapic_state *kapic, int reg_id)
+{
+ return *((uint32_t *) (kapic->regs + (reg_id << 4)));
+}
+
+static inline void kapic_set_reg(struct kvm_lapic_state *kapic,
+ int reg_id, uint32_t val)
+{
+ *((uint32_t *) (kapic->regs + (reg_id << 4))) = val;
+}
+
+static void kvm_kernel_lapic_save_to_user(APICState *s)
+{
+ struct kvm_lapic_state apic;
+ struct kvm_lapic_state *kapic = &apic;
+ int i, v;
+
+ kvm_get_lapic(kvm_context, s->cpu_env->cpu_index, kapic);
+
+ s->id = kapic_reg(kapic, 0x2) >> 24;
+ s->tpr = kapic_reg(kapic, 0x8);
+ s->arb_id = kapic_reg(kapic, 0x9);
+ s->log_dest = kapic_reg(kapic, 0xd) >> 24;
+ s->dest_mode = kapic_reg(kapic, 0xe) >> 28;
+ s->spurious_vec = kapic_reg(kapic, 0xf);
+ for (i = 0; i < 8; i++) {
+ s->isr[i] = kapic_reg(kapic, 0x10 + i);
+ s->tmr[i] = kapic_reg(kapic, 0x18 + i);
+ s->irr[i] = kapic_reg(kapic, 0x20 + i);
+ }
+ s->esr = kapic_reg(kapic, 0x28);
+ s->icr[0] = kapic_reg(kapic, 0x30);
+ s->icr[1] = kapic_reg(kapic, 0x31);
+ for (i = 0; i < APIC_LVT_NB; i++)
+ s->lvt[i] = kapic_reg(kapic, 0x32 + i);
+ s->initial_count = kapic_reg(kapic, 0x38);
+ s->divide_conf = kapic_reg(kapic, 0x3e);
+
+ v = (s->divide_conf & 3) | ((s->divide_conf >> 1) & 4);
+ s->count_shift = (v + 1) & 7;
+
+ s->initial_count_load_time = qemu_get_clock(vm_clock);
+ apic_timer_update(s, s->initial_count_load_time);
+}
+
+static void kvm_kernel_lapic_load_from_user(APICState *s)
+{
+ struct kvm_lapic_state apic;
+ struct kvm_lapic_state *klapic = &apic;
+ int i;
+
+ memset(klapic, 0, sizeof apic);
+ kapic_set_reg(klapic, 0x2, s->id << 24);
+ kapic_set_reg(klapic, 0x8, s->tpr);
+ kapic_set_reg(klapic, 0xd, s->log_dest << 24);
+ kapic_set_reg(klapic, 0xe, s->dest_mode << 28 | 0x0fffffff);
+ kapic_set_reg(klapic, 0xf, s->spurious_vec);
+ for (i = 0; i < 8; i++) {
+ kapic_set_reg(klapic, 0x10 + i, s->isr[i]);
+ kapic_set_reg(klapic, 0x18 + i, s->tmr[i]);
+ kapic_set_reg(klapic, 0x20 + i, s->irr[i]);
+ }
+ kapic_set_reg(klapic, 0x28, s->esr);
+ kapic_set_reg(klapic, 0x30, s->icr[0]);
+ kapic_set_reg(klapic, 0x31, s->icr[1]);
+ for (i = 0; i < APIC_LVT_NB; i++)
+ kapic_set_reg(klapic, 0x32 + i, s->lvt[i]);
+ kapic_set_reg(klapic, 0x38, s->initial_count);
+ kapic_set_reg(klapic, 0x3e, s->divide_conf);
+
+ kvm_set_lapic(kvm_context, s->cpu_env->cpu_index, klapic);
+}
+
+#endif
+
+static void kvm_apic_save(QEMUFile *f, void *opaque)
+{
+ APICState *s = opaque;
+
+ kvm_kernel_lapic_save_to_user(s);
+ apic_save(f, opaque);
+}
+
+static int kvm_apic_load(QEMUFile *f, void *opaque, int version_id)
+{
+ APICState *s = opaque;
+ int r = apic_load(f, opaque, version_id);
+
+ if (r == 0)
+ qemu_kvm_load_lapic(s->cpu_env);
+ return r;
+}
+
+
+void qemu_kvm_load_lapic(CPUState *env)
+{
+ if (kvm_vcpu_inited(env)) {
+ kvm_kernel_lapic_load_from_user(env->apic_state);
+ }
+}
+
+static void kvm_apic_reset(void *opaque)
+{
+ APICState *s = opaque;
+
+ s->apicbase = 0xfee00000 |
+ (s->id ? 0 : MSR_IA32_APICBASE_BSP) | MSR_IA32_APICBASE_ENABLE;
+
+ apic_init_ipi_state(s);
+
+ if (s->id == 0) {
+ /*
+ * 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->lvt[APIC_LVT_LINT0] = 0x700;
+ }
+
+ qemu_kvm_load_lapic(s->cpu_env);
+}
+
+int kvm_apic_init(CPUState *env)
+{
+ APICState *s;
+
+ if (last_apic_id >= MAX_APICS)
+ return -1;
+ s = qemu_mallocz(sizeof(APICState));
+ env->apic_state = s;
+ s->id = last_apic_id++;
+ env->cpuid_apic_id = s->id;
+ s->cpu_env = env;
+
+ kvm_apic_reset(s);
+
+ s->timer = qemu_new_timer(vm_clock, apic_timer, s);
+
+ register_savevm("apic", s->id, 2, kvm_apic_save, kvm_apic_load, s);
+ qemu_register_reset(kvm_apic_reset, 0, s);
+
+ local_apics[s->id] = s;
+ return 0;
+}
diff --git a/hw/pc.c b/hw/pc.c
index 66f4635..a99ab07 100644
--- a/hw/pc.c
+++ b/hw/pc.c
@@ -842,7 +842,12 @@ CPUState *pc_new_cpu(int cpu, const char *cpu_model, int pci_enabled)
}
qemu_register_reset(main_cpu_reset, 0, env);
if (pci_enabled) {
- apic_init(env);
+#ifdef KVM_CAP_IRQCHIP
+ if (kvm_enabled() && qemu_kvm_irqchip_in_kernel()) {
+ kvm_apic_init(env);
+ } else
+#endif
+ apic_init(env);
}

/* kvm needs this to run after the apic is initialized. Otherwise,
diff --git a/hw/pc.h b/hw/pc.h
index 3af22f2..ab847b9 100644
--- a/hw/pc.h
+++ b/hw/pc.h
@@ -48,6 +48,7 @@ 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/qemu-kvm-x86.c b/qemu-kvm-x86.c
index 98aa530..925d70e 100644
--- a/qemu-kvm-x86.c
+++ b/qemu-kvm-x86.c
@@ -527,7 +527,10 @@ int kvm_arch_qemu_init_env(CPUState *cenv)
CPUState copy;
uint32_t i, j, limit;

- qemu_kvm_load_lapic(cenv);
+#ifdef KVM_CAP_IRQCHIP
+ if (qemu_kvm_irqchip_in_kernel())
+ qemu_kvm_load_lapic(cenv);
+#endif

copy = *cenv;

--
1.5.6.6