Commit fc61b800 authored by Gleb Natapov's avatar Gleb Natapov Committed by Avi Kivity

KVM: Add Directed EOI support to APIC emulation

Directed EOI is specified by x2APIC, but is available even when lapic is
in xAPIC mode.
Signed-off-by: default avatarGleb Natapov <gleb@redhat.com>
Signed-off-by: default avatarAvi Kivity <avi@redhat.com>
parent cb247721
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#define APIC_LVR 0x30 #define APIC_LVR 0x30
#define APIC_LVR_MASK 0xFF00FF #define APIC_LVR_MASK 0xFF00FF
#define APIC_LVR_DIRECTED_EOI (1 << 24)
#define GET_APIC_VERSION(x) ((x) & 0xFFu) #define GET_APIC_VERSION(x) ((x) & 0xFFu)
#define GET_APIC_MAXLVT(x) (((x) >> 16) & 0xFFu) #define GET_APIC_MAXLVT(x) (((x) >> 16) & 0xFFu)
#ifdef CONFIG_X86_32 #ifdef CONFIG_X86_32
...@@ -40,6 +41,7 @@ ...@@ -40,6 +41,7 @@
#define APIC_DFR_CLUSTER 0x0FFFFFFFul #define APIC_DFR_CLUSTER 0x0FFFFFFFul
#define APIC_DFR_FLAT 0xFFFFFFFFul #define APIC_DFR_FLAT 0xFFFFFFFFul
#define APIC_SPIV 0xF0 #define APIC_SPIV 0xF0
#define APIC_SPIV_DIRECTED_EOI (1 << 12)
#define APIC_SPIV_FOCUS_DISABLED (1 << 9) #define APIC_SPIV_FOCUS_DISABLED (1 << 9)
#define APIC_SPIV_APIC_ENABLED (1 << 8) #define APIC_SPIV_APIC_ENABLED (1 << 8)
#define APIC_ISR 0x100 #define APIC_ISR 0x100
......
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include "kvm_cache_regs.h" #include "kvm_cache_regs.h"
#include "irq.h" #include "irq.h"
#include "trace.h" #include "trace.h"
#include "x86.h"
#ifndef CONFIG_X86_64 #ifndef CONFIG_X86_64
#define mod_64(x, y) ((x) - (y) * div64_u64(x, y)) #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) ...@@ -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; 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] = { static unsigned int apic_lvt_mask[APIC_LVT_NUM] = {
LVT_MASK | APIC_LVT_TIMER_PERIODIC, /* LVTT */ LVT_MASK | APIC_LVT_TIMER_PERIODIC, /* LVTT */
LVT_MASK | APIC_MODE_MASK, /* LVTTHMR */ LVT_MASK | APIC_MODE_MASK, /* LVTTHMR */
...@@ -442,9 +458,11 @@ static void apic_set_eoi(struct kvm_lapic *apic) ...@@ -442,9 +458,11 @@ static void apic_set_eoi(struct kvm_lapic *apic)
trigger_mode = IOAPIC_LEVEL_TRIG; trigger_mode = IOAPIC_LEVEL_TRIG;
else else
trigger_mode = IOAPIC_EDGE_TRIG; trigger_mode = IOAPIC_EDGE_TRIG;
if (!(apic_get_reg(apic, APIC_SPIV) & APIC_SPIV_DIRECTED_EOI)) {
mutex_lock(&apic->vcpu->kvm->irq_lock); mutex_lock(&apic->vcpu->kvm->irq_lock);
kvm_ioapic_update_eoi(apic->vcpu->kvm, vector, trigger_mode); kvm_ioapic_update_eoi(apic->vcpu->kvm, vector, trigger_mode);
mutex_unlock(&apic->vcpu->kvm->irq_lock); mutex_unlock(&apic->vcpu->kvm->irq_lock);
}
} }
static void apic_send_ipi(struct kvm_lapic *apic) static void apic_send_ipi(struct kvm_lapic *apic)
...@@ -694,8 +712,11 @@ static int apic_mmio_write(struct kvm_io_device *this, ...@@ -694,8 +712,11 @@ static int apic_mmio_write(struct kvm_io_device *this,
apic_set_reg(apic, APIC_DFR, val | 0x0FFFFFFF); apic_set_reg(apic, APIC_DFR, val | 0x0FFFFFFF);
break; break;
case APIC_SPIV: case APIC_SPIV: {
apic_set_reg(apic, APIC_SPIV, val & 0x3ff); 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)) { if (!(val & APIC_SPIV_APIC_ENABLED)) {
int i; int i;
u32 lvt_val; u32 lvt_val;
...@@ -710,7 +731,7 @@ static int apic_mmio_write(struct kvm_io_device *this, ...@@ -710,7 +731,7 @@ static int apic_mmio_write(struct kvm_io_device *this,
} }
break; break;
}
case APIC_ICR: case APIC_ICR:
/* No delay here, so we always clear the pending bit */ /* No delay here, so we always clear the pending bit */
apic_set_reg(apic, APIC_ICR, val & ~(1 << 12)); apic_set_reg(apic, APIC_ICR, val & ~(1 << 12));
...@@ -837,7 +858,7 @@ void kvm_lapic_reset(struct kvm_vcpu *vcpu) ...@@ -837,7 +858,7 @@ void kvm_lapic_reset(struct kvm_vcpu *vcpu)
hrtimer_cancel(&apic->lapic_timer.timer); hrtimer_cancel(&apic->lapic_timer.timer);
apic_set_reg(apic, APIC_ID, vcpu->vcpu_id << 24); 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++) for (i = 0; i < APIC_LVT_NUM; i++)
apic_set_reg(apic, APIC_LVTT + 0x10 * i, APIC_LVT_MASKED); 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) ...@@ -1041,7 +1062,8 @@ void kvm_apic_post_state_restore(struct kvm_vcpu *vcpu)
apic->base_address = vcpu->arch.apic_base & apic->base_address = vcpu->arch.apic_base &
MSR_IA32_APICBASE_BASE; MSR_IA32_APICBASE_BASE;
apic_set_reg(apic, APIC_LVR, APIC_VERSION); kvm_apic_set_version(vcpu);
apic_update_ppr(apic); apic_update_ppr(apic);
hrtimer_cancel(&apic->lapic_timer.timer); hrtimer_cancel(&apic->lapic_timer.timer);
update_divide_count(apic); update_divide_count(apic);
......
...@@ -29,6 +29,7 @@ u64 kvm_lapic_get_cr8(struct kvm_vcpu *vcpu); ...@@ -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_tpr(struct kvm_vcpu *vcpu, unsigned long cr8);
void kvm_lapic_set_base(struct kvm_vcpu *vcpu, u64 value); void kvm_lapic_set_base(struct kvm_vcpu *vcpu, u64 value);
u64 kvm_lapic_get_base(struct kvm_vcpu *vcpu); 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_physical_addr(struct kvm_lapic *apic, u16 dest);
int kvm_apic_match_logical_addr(struct kvm_lapic *apic, u8 mda); int kvm_apic_match_logical_addr(struct kvm_lapic *apic, u8 mda);
......
...@@ -79,8 +79,6 @@ static u64 __read_mostly efer_reserved_bits = 0xfffffffffffffffeULL; ...@@ -79,8 +79,6 @@ static u64 __read_mostly efer_reserved_bits = 0xfffffffffffffffeULL;
static int kvm_dev_ioctl_get_supported_cpuid(struct kvm_cpuid2 *cpuid, static int kvm_dev_ioctl_get_supported_cpuid(struct kvm_cpuid2 *cpuid,
struct kvm_cpuid_entry2 __user *entries); 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; struct kvm_x86_ops *kvm_x86_ops;
EXPORT_SYMBOL_GPL(kvm_x86_ops); EXPORT_SYMBOL_GPL(kvm_x86_ops);
...@@ -1373,6 +1371,7 @@ static int kvm_vcpu_ioctl_set_cpuid(struct kvm_vcpu *vcpu, ...@@ -1373,6 +1371,7 @@ static int kvm_vcpu_ioctl_set_cpuid(struct kvm_vcpu *vcpu,
vcpu->arch.cpuid_nent = cpuid->nent; vcpu->arch.cpuid_nent = cpuid->nent;
cpuid_fix_nx_cap(vcpu); cpuid_fix_nx_cap(vcpu);
r = 0; r = 0;
kvm_apic_set_version(vcpu);
out_free: out_free:
vfree(cpuid_entries); vfree(cpuid_entries);
...@@ -1394,6 +1393,7 @@ static int kvm_vcpu_ioctl_set_cpuid2(struct kvm_vcpu *vcpu, ...@@ -1394,6 +1393,7 @@ static int kvm_vcpu_ioctl_set_cpuid2(struct kvm_vcpu *vcpu,
cpuid->nent * sizeof(struct kvm_cpuid_entry2))) cpuid->nent * sizeof(struct kvm_cpuid_entry2)))
goto out; goto out;
vcpu->arch.cpuid_nent = cpuid->nent; vcpu->arch.cpuid_nent = cpuid->nent;
kvm_apic_set_version(vcpu);
return 0; return 0;
out: out:
......
...@@ -31,4 +31,8 @@ static inline bool kvm_exception_is_soft(unsigned int nr) ...@@ -31,4 +31,8 @@ static inline bool kvm_exception_is_soft(unsigned int nr)
{ {
return (nr == BP_VECTOR) || (nr == OF_VECTOR); return (nr == BP_VECTOR) || (nr == OF_VECTOR);
} }
struct kvm_cpuid_entry2 *kvm_find_cpuid_entry(struct kvm_vcpu *vcpu,
u32 function, u32 index);
#endif #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment