KVM: x86: Deliver exception payload on KVM_GET_VCPU_EVENTS
authorOliver Upton <oupton@google.com>
Fri, 7 Feb 2020 10:36:06 +0000 (02:36 -0800)
committerPaolo Bonzini <pbonzini@redhat.com>
Wed, 12 Feb 2020 11:34:10 +0000 (12:34 +0100)
KVM allows the deferral of exception payloads when a vCPU is in guest
mode to allow the L1 hypervisor to intercept certain events (#PF, #DB)
before register state has been modified. However, this behavior is
incompatible with the KVM_{GET,SET}_VCPU_EVENTS ABI, as userspace
expects register state to have been immediately modified. Userspace may
opt-in for the payload deferral behavior with the
KVM_CAP_EXCEPTION_PAYLOAD per-VM capability. As such,
kvm_multiple_exception() will immediately manipulate guest registers if
the capability hasn't been requested.

Since the deferral is only necessary if a userspace ioctl were to be
serviced at the same as a payload bearing exception is recognized, this
behavior can be relaxed. Instead, opportunistically defer the payload
from kvm_multiple_exception() and deliver the payload before completing
a KVM_GET_VCPU_EVENTS ioctl.

Signed-off-by: Oliver Upton <oupton@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
arch/x86/kvm/x86.c

index 95b753dab2079cbf860f3008392fef88f4347352..4d3310df1758c027c232b750988a2b5370d0d6a3 100644 (file)
@@ -498,19 +498,7 @@ static void kvm_multiple_exception(struct kvm_vcpu *vcpu,
                vcpu->arch.exception.error_code = error_code;
                vcpu->arch.exception.has_payload = has_payload;
                vcpu->arch.exception.payload = payload;
-               /*
-                * In guest mode, payload delivery should be deferred,
-                * so that the L1 hypervisor can intercept #PF before
-                * CR2 is modified (or intercept #DB before DR6 is
-                * modified under nVMX).  However, for ABI
-                * compatibility with KVM_GET_VCPU_EVENTS and
-                * KVM_SET_VCPU_EVENTS, we can't delay payload
-                * delivery unless userspace has enabled this
-                * functionality via the per-VM capability,
-                * KVM_CAP_EXCEPTION_PAYLOAD.
-                */
-               if (!vcpu->kvm->arch.exception_payload_enabled ||
-                   !is_guest_mode(vcpu))
+               if (!is_guest_mode(vcpu))
                        kvm_deliver_exception_payload(vcpu);
                return;
        }
@@ -3803,6 +3791,21 @@ static void kvm_vcpu_ioctl_x86_get_vcpu_events(struct kvm_vcpu *vcpu,
 {
        process_nmi(vcpu);
 
+       /*
+        * In guest mode, payload delivery should be deferred,
+        * so that the L1 hypervisor can intercept #PF before
+        * CR2 is modified (or intercept #DB before DR6 is
+        * modified under nVMX). Unless the per-VM capability,
+        * KVM_CAP_EXCEPTION_PAYLOAD, is set, we may not defer the delivery of
+        * an exception payload and handle after a KVM_GET_VCPU_EVENTS. Since we
+        * opportunistically defer the exception payload, deliver it if the
+        * capability hasn't been requested before processing a
+        * KVM_GET_VCPU_EVENTS.
+        */
+       if (!vcpu->kvm->arch.exception_payload_enabled &&
+           vcpu->arch.exception.pending && vcpu->arch.exception.has_payload)
+               kvm_deliver_exception_payload(vcpu);
+
        /*
         * The API doesn't provide the instruction length for software
         * exceptions, so don't report them. As long as the guest RIP