|
@@ -311,6 +311,54 @@ int kvm_apic_set_irq(struct kvm_vcpu *vcpu, struct kvm_lapic_irq *irq)
|
|
|
irq->level, irq->trig_mode);
|
|
|
}
|
|
|
|
|
|
+static int pv_eoi_put_user(struct kvm_vcpu *vcpu, u8 val)
|
|
|
+{
|
|
|
+
|
|
|
+ return kvm_write_guest_cached(vcpu->kvm, &vcpu->arch.pv_eoi.data, &val,
|
|
|
+ sizeof(val));
|
|
|
+}
|
|
|
+
|
|
|
+static int pv_eoi_get_user(struct kvm_vcpu *vcpu, u8 *val)
|
|
|
+{
|
|
|
+
|
|
|
+ return kvm_read_guest_cached(vcpu->kvm, &vcpu->arch.pv_eoi.data, val,
|
|
|
+ sizeof(*val));
|
|
|
+}
|
|
|
+
|
|
|
+static inline bool pv_eoi_enabled(struct kvm_vcpu *vcpu)
|
|
|
+{
|
|
|
+ return vcpu->arch.pv_eoi.msr_val & KVM_MSR_ENABLED;
|
|
|
+}
|
|
|
+
|
|
|
+static bool pv_eoi_get_pending(struct kvm_vcpu *vcpu)
|
|
|
+{
|
|
|
+ u8 val;
|
|
|
+ if (pv_eoi_get_user(vcpu, &val) < 0)
|
|
|
+ apic_debug("Can't read EOI MSR value: 0x%llx\n",
|
|
|
+ (unsigned long long)vcpi->arch.pv_eoi.msr_val);
|
|
|
+ return val & 0x1;
|
|
|
+}
|
|
|
+
|
|
|
+static void pv_eoi_set_pending(struct kvm_vcpu *vcpu)
|
|
|
+{
|
|
|
+ if (pv_eoi_put_user(vcpu, KVM_PV_EOI_ENABLED) < 0) {
|
|
|
+ apic_debug("Can't set EOI MSR value: 0x%llx\n",
|
|
|
+ (unsigned long long)vcpi->arch.pv_eoi.msr_val);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ __set_bit(KVM_APIC_PV_EOI_PENDING, &vcpu->arch.apic_attention);
|
|
|
+}
|
|
|
+
|
|
|
+static void pv_eoi_clr_pending(struct kvm_vcpu *vcpu)
|
|
|
+{
|
|
|
+ if (pv_eoi_put_user(vcpu, KVM_PV_EOI_DISABLED) < 0) {
|
|
|
+ apic_debug("Can't clear EOI MSR value: 0x%llx\n",
|
|
|
+ (unsigned long long)vcpi->arch.pv_eoi.msr_val);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ __clear_bit(KVM_APIC_PV_EOI_PENDING, &vcpu->arch.apic_attention);
|
|
|
+}
|
|
|
+
|
|
|
static inline int apic_find_highest_isr(struct kvm_lapic *apic)
|
|
|
{
|
|
|
int result;
|
|
@@ -527,15 +575,18 @@ int kvm_apic_compare_prio(struct kvm_vcpu *vcpu1, struct kvm_vcpu *vcpu2)
|
|
|
return vcpu1->arch.apic_arb_prio - vcpu2->arch.apic_arb_prio;
|
|
|
}
|
|
|
|
|
|
-static void apic_set_eoi(struct kvm_lapic *apic)
|
|
|
+static int apic_set_eoi(struct kvm_lapic *apic)
|
|
|
{
|
|
|
int vector = apic_find_highest_isr(apic);
|
|
|
+
|
|
|
+ trace_kvm_eoi(apic, vector);
|
|
|
+
|
|
|
/*
|
|
|
* Not every write EOI will has corresponding ISR,
|
|
|
* one example is when Kernel check timer on setup_IO_APIC
|
|
|
*/
|
|
|
if (vector == -1)
|
|
|
- return;
|
|
|
+ return vector;
|
|
|
|
|
|
apic_clear_isr(vector, apic);
|
|
|
apic_update_ppr(apic);
|
|
@@ -550,6 +601,7 @@ static void apic_set_eoi(struct kvm_lapic *apic)
|
|
|
kvm_ioapic_update_eoi(apic->vcpu->kvm, vector, trigger_mode);
|
|
|
}
|
|
|
kvm_make_request(KVM_REQ_EVENT, apic->vcpu);
|
|
|
+ return vector;
|
|
|
}
|
|
|
|
|
|
static void apic_send_ipi(struct kvm_lapic *apic)
|
|
@@ -1132,6 +1184,7 @@ void kvm_lapic_reset(struct kvm_vcpu *vcpu)
|
|
|
atomic_set(&apic->lapic_timer.pending, 0);
|
|
|
if (kvm_vcpu_is_bsp(vcpu))
|
|
|
vcpu->arch.apic_base |= MSR_IA32_APICBASE_BSP;
|
|
|
+ vcpu->arch.pv_eoi.msr_val = 0;
|
|
|
apic_update_ppr(apic);
|
|
|
|
|
|
vcpu->arch.apic_arb_prio = 0;
|
|
@@ -1332,11 +1385,51 @@ void __kvm_migrate_apic_timer(struct kvm_vcpu *vcpu)
|
|
|
hrtimer_start_expires(timer, HRTIMER_MODE_ABS);
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * apic_sync_pv_eoi_from_guest - called on vmexit or cancel interrupt
|
|
|
+ *
|
|
|
+ * Detect whether guest triggered PV EOI since the
|
|
|
+ * last entry. If yes, set EOI on guests's behalf.
|
|
|
+ * Clear PV EOI in guest memory in any case.
|
|
|
+ */
|
|
|
+static void apic_sync_pv_eoi_from_guest(struct kvm_vcpu *vcpu,
|
|
|
+ struct kvm_lapic *apic)
|
|
|
+{
|
|
|
+ bool pending;
|
|
|
+ int vector;
|
|
|
+ /*
|
|
|
+ * PV EOI state is derived from KVM_APIC_PV_EOI_PENDING in host
|
|
|
+ * and KVM_PV_EOI_ENABLED in guest memory as follows:
|
|
|
+ *
|
|
|
+ * KVM_APIC_PV_EOI_PENDING is unset:
|
|
|
+ * -> host disabled PV EOI.
|
|
|
+ * KVM_APIC_PV_EOI_PENDING is set, KVM_PV_EOI_ENABLED is set:
|
|
|
+ * -> host enabled PV EOI, guest did not execute EOI yet.
|
|
|
+ * KVM_APIC_PV_EOI_PENDING is set, KVM_PV_EOI_ENABLED is unset:
|
|
|
+ * -> host enabled PV EOI, guest executed EOI.
|
|
|
+ */
|
|
|
+ BUG_ON(!pv_eoi_enabled(vcpu));
|
|
|
+ pending = pv_eoi_get_pending(vcpu);
|
|
|
+ /*
|
|
|
+ * Clear pending bit in any case: it will be set again on vmentry.
|
|
|
+ * While this might not be ideal from performance point of view,
|
|
|
+ * this makes sure pv eoi is only enabled when we know it's safe.
|
|
|
+ */
|
|
|
+ pv_eoi_clr_pending(vcpu);
|
|
|
+ if (pending)
|
|
|
+ return;
|
|
|
+ vector = apic_set_eoi(apic);
|
|
|
+ trace_kvm_pv_eoi(apic, vector);
|
|
|
+}
|
|
|
+
|
|
|
void kvm_lapic_sync_from_vapic(struct kvm_vcpu *vcpu)
|
|
|
{
|
|
|
u32 data;
|
|
|
void *vapic;
|
|
|
|
|
|
+ if (test_bit(KVM_APIC_PV_EOI_PENDING, &vcpu->arch.apic_attention))
|
|
|
+ apic_sync_pv_eoi_from_guest(vcpu, vcpu->arch.apic);
|
|
|
+
|
|
|
if (!test_bit(KVM_APIC_CHECK_VAPIC, &vcpu->arch.apic_attention))
|
|
|
return;
|
|
|
|
|
@@ -1347,17 +1440,44 @@ void kvm_lapic_sync_from_vapic(struct kvm_vcpu *vcpu)
|
|
|
apic_set_tpr(vcpu->arch.apic, data & 0xff);
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * apic_sync_pv_eoi_to_guest - called before vmentry
|
|
|
+ *
|
|
|
+ * Detect whether it's safe to enable PV EOI and
|
|
|
+ * if yes do so.
|
|
|
+ */
|
|
|
+static void apic_sync_pv_eoi_to_guest(struct kvm_vcpu *vcpu,
|
|
|
+ struct kvm_lapic *apic)
|
|
|
+{
|
|
|
+ if (!pv_eoi_enabled(vcpu) ||
|
|
|
+ /* IRR set or many bits in ISR: could be nested. */
|
|
|
+ apic->irr_pending ||
|
|
|
+ /* Cache not set: could be safe but we don't bother. */
|
|
|
+ apic->highest_isr_cache == -1 ||
|
|
|
+ /* Need EOI to update ioapic. */
|
|
|
+ kvm_ioapic_handles_vector(vcpu->kvm, apic->highest_isr_cache)) {
|
|
|
+ /*
|
|
|
+ * PV EOI was disabled by apic_sync_pv_eoi_from_guest
|
|
|
+ * so we need not do anything here.
|
|
|
+ */
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ pv_eoi_set_pending(apic->vcpu);
|
|
|
+}
|
|
|
+
|
|
|
void kvm_lapic_sync_to_vapic(struct kvm_vcpu *vcpu)
|
|
|
{
|
|
|
u32 data, tpr;
|
|
|
int max_irr, max_isr;
|
|
|
- struct kvm_lapic *apic;
|
|
|
+ struct kvm_lapic *apic = vcpu->arch.apic;
|
|
|
void *vapic;
|
|
|
|
|
|
+ apic_sync_pv_eoi_to_guest(vcpu, apic);
|
|
|
+
|
|
|
if (!test_bit(KVM_APIC_CHECK_VAPIC, &vcpu->arch.apic_attention))
|
|
|
return;
|
|
|
|
|
|
- apic = vcpu->arch.apic;
|
|
|
tpr = apic_get_reg(apic, APIC_TASKPRI) & 0xff;
|
|
|
max_irr = apic_find_highest_irr(apic);
|
|
|
if (max_irr < 0)
|
|
@@ -1443,3 +1563,16 @@ int kvm_hv_vapic_msr_read(struct kvm_vcpu *vcpu, u32 reg, u64 *data)
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
+
|
|
|
+int kvm_lapic_enable_pv_eoi(struct kvm_vcpu *vcpu, u64 data)
|
|
|
+{
|
|
|
+ u64 addr = data & ~KVM_MSR_ENABLED;
|
|
|
+ if (!IS_ALIGNED(addr, 4))
|
|
|
+ return 1;
|
|
|
+
|
|
|
+ vcpu->arch.pv_eoi.msr_val = data;
|
|
|
+ if (!pv_eoi_enabled(vcpu))
|
|
|
+ return 0;
|
|
|
+ return kvm_gfn_to_hva_cache_init(vcpu->kvm, &vcpu->arch.pv_eoi.data,
|
|
|
+ addr);
|
|
|
+}
|