mirror of
https://github.com/torvalds/linux.git
synced 2024-12-06 19:11:31 +00:00
KVM: MIPS/Emulate: Update CP0_Compare emulation for VZ
Update emulation of guest writes to CP0_Compare for VZ. There are two main differences compared to trap & emulate: - Writing to CP0_Compare in the VZ hardware guest context acks any pending timer, clearing CP0_Cause.TI. If we don't want an ack to take place we must carefully restore the TI bit if it was previously set. - Even with guest timer access disabled in CP0_GuestCtl0.GT, if the guest CP0_Count reaches the guest CP0_Compare the timer interrupt will assert. To prevent this we must set CP0_GTOffset to move the guest CP0_Count out of the way of the new guest CP0_Compare, either before or after depending on whether it is a forwards or backwards change. Signed-off-by: James Hogan <james.hogan@imgtec.com> Cc: Paolo Bonzini <pbonzini@redhat.com> Cc: "Radim Krčmář" <rkrcmar@redhat.com> Cc: Ralf Baechle <ralf@linux-mips.org> Cc: linux-mips@linux-mips.org Cc: kvm@vger.kernel.org
This commit is contained in:
parent
372582a6c6
commit
5dee99b24e
@ -621,7 +621,9 @@ void kvm_mips_write_compare(struct kvm_vcpu *vcpu, u32 compare, bool ack)
|
||||
struct mips_coproc *cop0 = vcpu->arch.cop0;
|
||||
int dc;
|
||||
u32 old_compare = kvm_read_c0_guest_compare(cop0);
|
||||
ktime_t now;
|
||||
s32 delta = compare - old_compare;
|
||||
u32 cause;
|
||||
ktime_t now = ktime_set(0, 0); /* silence bogus GCC warning */
|
||||
u32 count;
|
||||
|
||||
/* if unchanged, must just be an ack */
|
||||
@ -633,6 +635,21 @@ void kvm_mips_write_compare(struct kvm_vcpu *vcpu, u32 compare, bool ack)
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* If guest CP0_Compare moves forward, CP0_GTOffset should be adjusted
|
||||
* too to prevent guest CP0_Count hitting guest CP0_Compare.
|
||||
*
|
||||
* The new GTOffset corresponds to the new value of CP0_Compare, and is
|
||||
* set prior to it being written into the guest context. We disable
|
||||
* preemption until the new value is written to prevent restore of a
|
||||
* GTOffset corresponding to the old CP0_Compare value.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_KVM_MIPS_VZ) && delta > 0) {
|
||||
preempt_disable();
|
||||
write_c0_gtoffset(compare - read_c0_count());
|
||||
back_to_back_c0_hazard();
|
||||
}
|
||||
|
||||
/* freeze_hrtimer() takes care of timer interrupts <= count */
|
||||
dc = kvm_mips_count_disabled(vcpu);
|
||||
if (!dc)
|
||||
@ -640,12 +657,36 @@ void kvm_mips_write_compare(struct kvm_vcpu *vcpu, u32 compare, bool ack)
|
||||
|
||||
if (ack)
|
||||
kvm_mips_callbacks->dequeue_timer_int(vcpu);
|
||||
else if (IS_ENABLED(CONFIG_KVM_MIPS_VZ))
|
||||
/*
|
||||
* With VZ, writing CP0_Compare acks (clears) CP0_Cause.TI, so
|
||||
* preserve guest CP0_Cause.TI if we don't want to ack it.
|
||||
*/
|
||||
cause = kvm_read_c0_guest_cause(cop0);
|
||||
|
||||
kvm_write_c0_guest_compare(cop0, compare);
|
||||
|
||||
if (IS_ENABLED(CONFIG_KVM_MIPS_VZ)) {
|
||||
if (delta > 0)
|
||||
preempt_enable();
|
||||
|
||||
back_to_back_c0_hazard();
|
||||
|
||||
if (!ack && cause & CAUSEF_TI)
|
||||
kvm_write_c0_guest_cause(cop0, cause);
|
||||
}
|
||||
|
||||
/* resume_hrtimer() takes care of timer interrupts > count */
|
||||
if (!dc)
|
||||
kvm_mips_resume_hrtimer(vcpu, now, count);
|
||||
|
||||
/*
|
||||
* If guest CP0_Compare is moving backward, we delay CP0_GTOffset change
|
||||
* until after the new CP0_Compare is written, otherwise new guest
|
||||
* CP0_Count could hit new guest CP0_Compare.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_KVM_MIPS_VZ) && delta <= 0)
|
||||
write_c0_gtoffset(compare - read_c0_count());
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user