[PATCH RESEND 29/30] KVM: VMX: implement force_intercept_exceptions_mask

From: Maxim Levitsky
Date: Mon Feb 07 2022 - 11:16:32 EST


All exceptions are supported. Some bugs might remain in regard to KVM own
interception of #PF but since this is strictly
debug feature this should be OK.

Signed-off-by: Maxim Levitsky <mlevitsk@xxxxxxxxxx>
---
arch/x86/kvm/vmx/nested.c | 8 +++++++
arch/x86/kvm/vmx/vmcs.h | 6 +++++
arch/x86/kvm/vmx/vmx.c | 47 +++++++++++++++++++++++++++++++++------
3 files changed, 54 insertions(+), 7 deletions(-)

diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c
index c73e4d938ddc3..e89b32b1d9efb 100644
--- a/arch/x86/kvm/vmx/nested.c
+++ b/arch/x86/kvm/vmx/nested.c
@@ -5902,6 +5902,14 @@ static bool nested_vmx_l0_wants_exit(struct kvm_vcpu *vcpu,
switch ((u16)exit_reason.basic) {
case EXIT_REASON_EXCEPTION_NMI:
intr_info = vmx_get_intr_info(vcpu);
+
+ if (is_exception(intr_info)) {
+ int ex_no = intr_info & INTR_INFO_VECTOR_MASK;
+
+ if (kvm_is_exception_force_intercepted(vcpu->kvm, ex_no))
+ return true;
+ }
+
if (is_nmi(intr_info))
return true;
else if (is_page_fault(intr_info))
diff --git a/arch/x86/kvm/vmx/vmcs.h b/arch/x86/kvm/vmx/vmcs.h
index e325c290a8162..d5aac5abe5cdd 100644
--- a/arch/x86/kvm/vmx/vmcs.h
+++ b/arch/x86/kvm/vmx/vmcs.h
@@ -94,6 +94,12 @@ static inline bool is_exception_n(u32 intr_info, u8 vector)
return is_intr_type_n(intr_info, INTR_TYPE_HARD_EXCEPTION, vector);
}

+static inline bool is_exception(u32 intr_info)
+{
+ return is_intr_type(intr_info, INTR_TYPE_HARD_EXCEPTION) ||
+ is_intr_type(intr_info, INTR_TYPE_SOFT_EXCEPTION);
+}
+
static inline bool is_debug(u32 intr_info)
{
return is_exception_n(intr_info, DB_VECTOR);
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index fc9c4eca90a78..aec2b962707a0 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -719,6 +719,7 @@ static u32 vmx_read_guest_seg_ar(struct vcpu_vmx *vmx, unsigned seg)
void vmx_update_exception_bitmap(struct kvm_vcpu *vcpu)
{
u32 eb;
+ int exc;

eb = (1u << PF_VECTOR) | (1u << UD_VECTOR) | (1u << MC_VECTOR) |
(1u << DB_VECTOR) | (1u << AC_VECTOR);
@@ -749,7 +750,8 @@ void vmx_update_exception_bitmap(struct kvm_vcpu *vcpu)
else {
int mask = 0, match = 0;

- if (enable_ept && (eb & (1u << PF_VECTOR))) {
+ if (enable_ept && (eb & (1u << PF_VECTOR)) &&
+ !kvm_is_exception_force_intercepted(vcpu->kvm, PF_VECTOR)) {
/*
* If EPT is enabled, #PF is currently only intercepted
* if MAXPHYADDR is smaller on the guest than on the
@@ -772,6 +774,10 @@ void vmx_update_exception_bitmap(struct kvm_vcpu *vcpu)
if (vcpu->arch.xfd_no_write_intercept)
eb |= (1u << NM_VECTOR);

+ for (exc = 0 ; exc < 32 ; ++exc)
+ if (kvm_is_exception_force_intercepted(vcpu->kvm, exc) && exc != NMI_VECTOR)
+ eb |= (1u << exc);
+
vmcs_write32(EXCEPTION_BITMAP, eb);
}

@@ -4867,18 +4873,23 @@ static int handle_exception_nmi(struct kvm_vcpu *vcpu)
error_code = vmcs_read32(VM_EXIT_INTR_ERROR_CODE);

if (!vmx->rmode.vm86_active && is_gp_fault(intr_info)) {
- WARN_ON_ONCE(!enable_vmware_backdoor);
-
/*
* VMware backdoor emulation on #GP interception only handles
* IN{S}, OUT{S}, and RDPMC, none of which generate a non-zero
* error code on #GP.
*/
- if (error_code) {
+
+ if (enable_vmware_backdoor && !error_code)
+ return kvm_emulate_instruction(vcpu, EMULTYPE_VMWARE_GP);
+
+ if (!kvm_is_exception_force_intercepted(vcpu->kvm, GP_VECTOR))
+ WARN_ON_ONCE(!enable_vmware_backdoor);
+
+ if (intr_info & INTR_INFO_DELIVER_CODE_MASK)
kvm_queue_exception_e(vcpu, GP_VECTOR, error_code);
- return 1;
- }
- return kvm_emulate_instruction(vcpu, EMULTYPE_VMWARE_GP);
+ else
+ kvm_queue_exception(vcpu, GP_VECTOR);
+ return 1;
}

/*
@@ -4887,6 +4898,7 @@ static int handle_exception_nmi(struct kvm_vcpu *vcpu)
* See the comments in vmx_handle_exit.
*/
if ((vect_info & VECTORING_INFO_VALID_MASK) &&
+ !kvm_is_exception_force_intercepted(vcpu->kvm, PF_VECTOR) &&
!(is_page_fault(intr_info) && !(error_code & PFERR_RSVD_MASK))) {
vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
vcpu->run->internal.suberror = KVM_INTERNAL_ERROR_SIMUL_EX;
@@ -4901,10 +4913,23 @@ static int handle_exception_nmi(struct kvm_vcpu *vcpu)
if (is_page_fault(intr_info)) {
cr2 = vmx_get_exit_qual(vcpu);
if (enable_ept && !vcpu->arch.apf.host_apf_flags) {
+ /*
+ * If we force intercept #PF and the page fault
+ * is due to the reason which we don't intercept,
+ * reflect it to the guest.
+ */
+ if (kvm_is_exception_force_intercepted(vcpu->kvm, PF_VECTOR) &&
+ (!allow_smaller_maxphyaddr ||
+ !(error_code & PFERR_PRESENT_MASK) ||
+ (error_code & PFERR_RSVD_MASK))) {
+ kvm_queue_exception_e_p(vcpu, PF_VECTOR, error_code, cr2);
+ return 1;
+ }
/*
* EPT will cause page fault only if we need to
* detect illegal GPAs.
*/
+
WARN_ON_ONCE(!allow_smaller_maxphyaddr);
kvm_fixup_and_inject_pf_error(vcpu, cr2, error_code);
return 1;
@@ -4983,6 +5008,14 @@ static int handle_exception_nmi(struct kvm_vcpu *vcpu)
return 1;
fallthrough;
default:
+ if (kvm_is_exception_force_intercepted(vcpu->kvm, ex_no)) {
+ if (intr_info & INTR_INFO_DELIVER_CODE_MASK)
+ kvm_queue_exception_e(vcpu, ex_no, error_code);
+ else
+ kvm_queue_exception(vcpu, ex_no);
+ break;
+ }
+
kvm_run->exit_reason = KVM_EXIT_EXCEPTION;
kvm_run->ex.exception = ex_no;
kvm_run->ex.error_code = error_code;
--
2.26.3