KVM: Add Directed EOI support to APIC emulation
Gleb Natapov [Sun, 5 Jul 2009 14:39:35 +0000 (17:39 +0300)]
Directed EOI is specified by x2APIC, but is available even when lapic is
in xAPIC mode.

Signed-off-by: Gleb Natapov <gleb@redhat.com>
Signed-off-by: Avi Kivity <avi@redhat.com>

arch/x86/include/asm/apicdef.h
arch/x86/kvm/lapic.c
arch/x86/kvm/lapic.h
arch/x86/kvm/x86.c
arch/x86/kvm/x86.h

index 7ddb36a..74ca38f 100644 (file)
@@ -14,6 +14,7 @@
 
 #define        APIC_LVR        0x30
 #define                APIC_LVR_MASK           0xFF00FF
+#define                APIC_LVR_DIRECTED_EOI   (1 << 24)
 #define                GET_APIC_VERSION(x)     ((x) & 0xFFu)
 #define                GET_APIC_MAXLVT(x)      (((x) >> 16) & 0xFFu)
 #ifdef CONFIG_X86_32
@@ -40,6 +41,7 @@
 #define                APIC_DFR_CLUSTER                0x0FFFFFFFul
 #define                APIC_DFR_FLAT                   0xFFFFFFFFul
 #define        APIC_SPIV       0xF0
+#define                APIC_SPIV_DIRECTED_EOI          (1 << 12)
 #define                APIC_SPIV_FOCUS_DISABLED        (1 << 9)
 #define                APIC_SPIV_APIC_ENABLED          (1 << 8)
 #define        APIC_ISR        0x100
index 265a765..62ea2ab 100644 (file)
@@ -35,6 +35,7 @@
 #include "kvm_cache_regs.h"
 #include "irq.h"
 #include "trace.h"
+#include "x86.h"
 
 #ifndef CONFIG_X86_64
 #define mod_64(x, y) ((x) - (y) * div64_u64(x, y))
@@ -142,6 +143,21 @@ static inline int apic_lvt_nmi_mode(u32 lvt_val)
        return (lvt_val & (APIC_MODE_MASK | APIC_LVT_MASKED)) == APIC_DM_NMI;
 }
 
+void kvm_apic_set_version(struct kvm_vcpu *vcpu)
+{
+       struct kvm_lapic *apic = vcpu->arch.apic;
+       struct kvm_cpuid_entry2 *feat;
+       u32 v = APIC_VERSION;
+
+       if (!irqchip_in_kernel(vcpu->kvm))
+               return;
+
+       feat = kvm_find_cpuid_entry(apic->vcpu, 0x1, 0);
+       if (feat && (feat->ecx & (1 << (X86_FEATURE_X2APIC & 31))))
+               v |= APIC_LVR_DIRECTED_EOI;
+       apic_set_reg(apic, APIC_LVR, v);
+}
+
 static unsigned int apic_lvt_mask[APIC_LVT_NUM] = {
        LVT_MASK | APIC_LVT_TIMER_PERIODIC,     /* LVTT */
        LVT_MASK | APIC_MODE_MASK,      /* LVTTHMR */
@@ -442,9 +458,11 @@ static void apic_set_eoi(struct kvm_lapic *apic)
                trigger_mode = IOAPIC_LEVEL_TRIG;
        else
                trigger_mode = IOAPIC_EDGE_TRIG;
-       mutex_lock(&apic->vcpu->kvm->irq_lock);
-       kvm_ioapic_update_eoi(apic->vcpu->kvm, vector, trigger_mode);
-       mutex_unlock(&apic->vcpu->kvm->irq_lock);
+       if (!(apic_get_reg(apic, APIC_SPIV) & APIC_SPIV_DIRECTED_EOI)) {
+               mutex_lock(&apic->vcpu->kvm->irq_lock);
+               kvm_ioapic_update_eoi(apic->vcpu->kvm, vector, trigger_mode);
+               mutex_unlock(&apic->vcpu->kvm->irq_lock);
+       }
 }
 
 static void apic_send_ipi(struct kvm_lapic *apic)
@@ -694,8 +712,11 @@ static int apic_mmio_write(struct kvm_io_device *this,
                apic_set_reg(apic, APIC_DFR, val | 0x0FFFFFFF);
                break;
 
-       case APIC_SPIV:
-               apic_set_reg(apic, APIC_SPIV, val & 0x3ff);
+       case APIC_SPIV: {
+               u32 mask = 0x3ff;
+               if (apic_get_reg(apic, APIC_LVR) & APIC_LVR_DIRECTED_EOI)
+                       mask |= APIC_SPIV_DIRECTED_EOI;
+               apic_set_reg(apic, APIC_SPIV, val & mask);
                if (!(val & APIC_SPIV_APIC_ENABLED)) {
                        int i;
                        u32 lvt_val;
@@ -710,7 +731,7 @@ static int apic_mmio_write(struct kvm_io_device *this,
 
                }
                break;
-
+       }
        case APIC_ICR:
                /* No delay here, so we always clear the pending bit */
                apic_set_reg(apic, APIC_ICR, val & ~(1 << 12));
@@ -837,7 +858,7 @@ void kvm_lapic_reset(struct kvm_vcpu *vcpu)
        hrtimer_cancel(&apic->lapic_timer.timer);
 
        apic_set_reg(apic, APIC_ID, vcpu->vcpu_id << 24);
-       apic_set_reg(apic, APIC_LVR, APIC_VERSION);
+       kvm_apic_set_version(apic->vcpu);
 
        for (i = 0; i < APIC_LVT_NUM; i++)
                apic_set_reg(apic, APIC_LVTT + 0x10 * i, APIC_LVT_MASKED);
@@ -1041,7 +1062,8 @@ void kvm_apic_post_state_restore(struct kvm_vcpu *vcpu)
 
        apic->base_address = vcpu->arch.apic_base &
                             MSR_IA32_APICBASE_BASE;
-       apic_set_reg(apic, APIC_LVR, APIC_VERSION);
+       kvm_apic_set_version(vcpu);
+
        apic_update_ppr(apic);
        hrtimer_cancel(&apic->lapic_timer.timer);
        update_divide_count(apic);
index 3f3ecc6..bc1c524 100644 (file)
@@ -29,6 +29,7 @@ u64 kvm_lapic_get_cr8(struct kvm_vcpu *vcpu);
 void kvm_lapic_set_tpr(struct kvm_vcpu *vcpu, unsigned long cr8);
 void kvm_lapic_set_base(struct kvm_vcpu *vcpu, u64 value);
 u64 kvm_lapic_get_base(struct kvm_vcpu *vcpu);
+void kvm_apic_set_version(struct kvm_vcpu *vcpu);
 
 int kvm_apic_match_physical_addr(struct kvm_lapic *apic, u16 dest);
 int kvm_apic_match_logical_addr(struct kvm_lapic *apic, u8 mda);
index d32e3c6..086f931 100644 (file)
@@ -79,8 +79,6 @@ static u64 __read_mostly efer_reserved_bits = 0xfffffffffffffffeULL;
 
 static int kvm_dev_ioctl_get_supported_cpuid(struct kvm_cpuid2 *cpuid,
                                    struct kvm_cpuid_entry2 __user *entries);
-struct kvm_cpuid_entry2 *kvm_find_cpuid_entry(struct kvm_vcpu *vcpu,
-                                             u32 function, u32 index);
 
 struct kvm_x86_ops *kvm_x86_ops;
 EXPORT_SYMBOL_GPL(kvm_x86_ops);
@@ -1373,6 +1371,7 @@ static int kvm_vcpu_ioctl_set_cpuid(struct kvm_vcpu *vcpu,
        vcpu->arch.cpuid_nent = cpuid->nent;
        cpuid_fix_nx_cap(vcpu);
        r = 0;
+       kvm_apic_set_version(vcpu);
 
 out_free:
        vfree(cpuid_entries);
@@ -1394,6 +1393,7 @@ static int kvm_vcpu_ioctl_set_cpuid2(struct kvm_vcpu *vcpu,
                           cpuid->nent * sizeof(struct kvm_cpuid_entry2)))
                goto out;
        vcpu->arch.cpuid_nent = cpuid->nent;
+       kvm_apic_set_version(vcpu);
        return 0;
 
 out:
index 4c8e10a..5eadea5 100644 (file)
@@ -31,4 +31,8 @@ static inline bool kvm_exception_is_soft(unsigned int nr)
 {
        return (nr == BP_VECTOR) || (nr == OF_VECTOR);
 }
+
+struct kvm_cpuid_entry2 *kvm_find_cpuid_entry(struct kvm_vcpu *vcpu,
+                                             u32 function, u32 index);
+
 #endif