#define KVM_HOST_DATA_FLAG_HAS_TRBE                    1
 #define KVM_HOST_DATA_FLAG_TRBE_ENABLED                        4
 #define KVM_HOST_DATA_FLAG_EL1_TRACING_CONFIGURED      5
+#define KVM_HOST_DATA_FLAG_VCPU_IN_HYP_CONTEXT         6
        unsigned long flags;
 
        struct kvm_cpu_context host_ctxt;
 
        if (!vcpu_has_nv(vcpu))
                return hcr;
 
+       /*
+        * We rely on the invariant that a vcpu entered from HYP
+        * context must also exit in the same context, as only an ERET
+        * instruction can kick us out of it, and we obviously trap
+        * that sucker. PSTATE.M will get fixed-up on exit.
+        */
        if (is_hyp_ctxt(vcpu)) {
+               host_data_set_flag(VCPU_IN_HYP_CONTEXT);
+
                hcr |= HCR_NV | HCR_NV2 | HCR_AT | HCR_TTLB;
 
                if (!vcpu_el2_e2h_is_set(vcpu))
                        hcr |= HCR_NV1;
 
                write_sysreg_s(vcpu->arch.ctxt.vncr_array, SYS_VNCR_EL2);
+       } else {
+               host_data_clear_flag(VCPU_IN_HYP_CONTEXT);
        }
 
        return hcr | (__vcpu_sys_reg(vcpu, HCR_EL2) & ~NV_HCR_GUEST_EXCLUDE);
 
        /*
         * If we were in HYP context on entry, adjust the PSTATE view
-        * so that the usual helpers work correctly.
+        * so that the usual helpers work correctly. This enforces our
+        * invariant that the guest's HYP context status is preserved
+        * across a run.
         */
-       if (vcpu_has_nv(vcpu) && (read_sysreg(hcr_el2) & HCR_NV)) {
+       if (vcpu_has_nv(vcpu) &&
+           unlikely(host_data_test_flag(VCPU_IN_HYP_CONTEXT))) {
                u64 mode = *vcpu_cpsr(vcpu) & (PSR_MODE_MASK | PSR_MODE32_BIT);
 
                switch (mode) {
                *vcpu_cpsr(vcpu) |= mode;
        }
 
+       /* Apply extreme paranoia! */
+       BUG_ON(vcpu_has_nv(vcpu) &&
+              !!host_data_test_flag(VCPU_IN_HYP_CONTEXT) != is_hyp_ctxt(vcpu));
+
        return __fixup_guest_exit(vcpu, exit_code, hyp_exit_handlers);
 }