KVM: arm64: Opportunistically track HCR_EL2.E2H being flipped
Signed-off-by: Marc Zyngier <maz@kernel.org>
diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h
index 8f6aa7e..9070405 100644
--- a/arch/arm64/include/asm/kvm_host.h
+++ b/arch/arm64/include/asm/kvm_host.h
@@ -593,6 +593,9 @@
/* State flags for kernel bookkeeping, unused by the hypervisor code */
u8 sflags;
+ /* Output flags from the hypervisor to the kernel */
+ u8 oflags;
+
/*
* Don't run the guest (internal implementation need).
*
@@ -819,6 +822,8 @@
/* WFI instruction trapped */
#define IN_WFI __vcpu_single_flag(sflags, BIT(7))
+/* vcpu entered with HCR_EL2.E2H set */
+#define VCPU_HCR_E2H __vcpu_single_flag(oflags, BIT(0))
/* Pointer to the vcpu's SVE FFR for sve_{save,load}_state() */
#define vcpu_sve_pffr(vcpu) (kern_hyp_va((vcpu)->arch.sve_state) + \
diff --git a/arch/arm64/kvm/handle_exit.c b/arch/arm64/kvm/handle_exit.c
index e7bc520..78adc52 100644
--- a/arch/arm64/kvm/handle_exit.c
+++ b/arch/arm64/kvm/handle_exit.c
@@ -384,6 +384,13 @@
/* For exit types that need handling before we can be preempted */
void handle_exit_early(struct kvm_vcpu *vcpu, int exception_index)
{
+ /* Check whether HCR_EL2.E2H was flipped behind our back */
+ if (vcpu_has_nv2(vcpu) && is_hyp_ctxt(vcpu) &&
+ (!!vcpu_get_flag(vcpu, VCPU_HCR_E2H) != vcpu_el2_e2h_is_set(vcpu))) {
+ kvm_arch_vcpu_put(vcpu);
+ kvm_arch_vcpu_load(vcpu, smp_processor_id());
+ }
+
if (ARM_SERROR_PENDING(exception_index)) {
if (this_cpu_has_cap(ARM64_HAS_RAS_EXTN)) {
u64 disr = kvm_vcpu_get_disr(vcpu);
diff --git a/arch/arm64/kvm/hyp/vhe/switch.c b/arch/arm64/kvm/hyp/vhe/switch.c
index b30e515..b09e95f 100644
--- a/arch/arm64/kvm/hyp/vhe/switch.c
+++ b/arch/arm64/kvm/hyp/vhe/switch.c
@@ -434,10 +434,18 @@
sysreg_restore_guest_state_vhe(guest_ctxt);
__debug_switch_to_guest(vcpu);
- if (is_hyp_ctxt(vcpu))
+ if (is_hyp_ctxt(vcpu)) {
+ if (vcpu_has_nv2(vcpu)) {
+ if (vcpu_el2_e2h_is_set(vcpu))
+ vcpu_set_flag(vcpu, VCPU_HCR_E2H);
+ else
+ vcpu_clear_flag(vcpu, VCPU_HCR_E2H);
+ }
+
vcpu_set_flag(vcpu, VCPU_HYP_CONTEXT);
- else
+ } else {
vcpu_clear_flag(vcpu, VCPU_HYP_CONTEXT);
+ }
do {
/* Jump in the fire! */