From: "Xin Li (Intel)" <[email protected]>

Set the VMX nested exception bit in VM-entry interruption information
field when injecting a nested exception using FRED event delivery to
ensure:
  1) A nested exception is injected on a correct stack level.
  2) The nested bit defined in FRED stack frame is set.

The event stack level used by FRED event delivery depends on whether
the event was a nested exception encountered during delivery of an
earlier event, because a nested exception is "regarded" as happening
on ring 0.  E.g., when #PF is configured to use stack level 1 in
IA32_FRED_STKLVLS MSR:
  - nested #PF will be delivered on the stack pointed by IA32_FRED_RSP1
    MSR when encountered in ring 3 and ring 0.
  - normal #PF will be delivered on the stack pointed by IA32_FRED_RSP0
    MSR when encountered in ring 3.

The VMX nested-exception support ensures a correct event stack level is
chosen when a VM entry injects a nested exception.

In addition, save/restore the nested flag of an exception during VM
save/restore and live migration to ensure a correct event stack level
is chosen when a nested exception is injected through FRED event
delivery across VM save/restore and live migration.

Note, like the other fields of struct kvm_queued_exception, the nested
flag is meaningful only while the exception is pending or injected. It
is not consulted otherwise, and so doesn't need to be cleared when an
exception is dropped.

Signed-off-by: Xin Li (Intel) <[email protected]>
[ Sean: reworked kvm_requeue_exception() to simply the code changes ]
Signed-off-by: Sean Christopherson <[email protected]>
Signed-off-by: Sohil Mehta <[email protected]>
Reviewed-by: Chao Gao <[email protected]>
---
v10:
 - Rename exception status member variable nested to is_nested (Sean).
 - Rename KVM_CAP_EXCEPTION_NESTED_FLAG to KVM_CAP_X86_FRED_EVENT, to
   include both FRED event nested flag and data.
 - Track the nested flag of an exception being queued in a local
   variable (so that a flag left over from a no-longer-live exception
   can't be folded into a new one).
---
 arch/x86/include/asm/kvm_host.h |  1 +
 arch/x86/include/asm/vmx.h      |  5 ++++-
 arch/x86/include/uapi/asm/kvm.h |  3 ++-
 arch/x86/kvm/svm/svm.c          |  2 +-
 arch/x86/kvm/vmx/vmx.c          | 10 ++++++++--
 arch/x86/kvm/x86.c              | 18 +++++++++++++++++-
 arch/x86/kvm/x86.h              |  4 +++-
 7 files changed, 36 insertions(+), 7 deletions(-)

diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index dd68db17fdae..994c354dc47b 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -695,6 +695,7 @@ struct kvm_queued_exception {
        u32 error_code;
        unsigned long payload;
        bool has_payload;
+       bool is_nested;
 };
 
 /*
diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h
index 4720619f09ac..7eb08f84a7e5 100644
--- a/arch/x86/include/asm/vmx.h
+++ b/arch/x86/include/asm/vmx.h
@@ -152,6 +152,7 @@ struct vmcs {
 #define VMX_BASIC_INOUT                                BIT_ULL(54)
 #define VMX_BASIC_TRUE_CTLS                    BIT_ULL(55)
 #define VMX_BASIC_NO_HW_ERROR_CODE_CC          BIT_ULL(56)
+#define VMX_BASIC_NESTED_EXCEPTION             BIT_ULL(58)
 #define VMX_BASIC_NO_SEAMRET_INVD_VMCS         BIT_ULL(60)
 
 static inline u32 vmx_basic_vmcs_revision_id(u64 vmx_basic)
@@ -451,13 +452,15 @@ enum vmcs_field {
 #define INTR_INFO_INTR_TYPE_MASK        0x700           /* 10:8 */
 #define INTR_INFO_DELIVER_CODE_MASK     0x800           /* 11 */
 #define INTR_INFO_UNBLOCK_NMI          0x1000          /* 12 */
+#define INTR_INFO_NESTED_EXCEPTION_MASK        0x2000          /* 13 */
 #define INTR_INFO_VALID_MASK            0x80000000      /* 31 */
-#define INTR_INFO_RESVD_BITS_MASK       0x7ffff000
+#define INTR_INFO_RESVD_BITS_MASK       0x7fffd000
 
 #define VECTORING_INFO_VECTOR_MASK             INTR_INFO_VECTOR_MASK
 #define VECTORING_INFO_TYPE_MASK               INTR_INFO_INTR_TYPE_MASK
 #define VECTORING_INFO_DELIVER_CODE_MASK       INTR_INFO_DELIVER_CODE_MASK
 #define VECTORING_INFO_VALID_MASK              INTR_INFO_VALID_MASK
+#define VECTORING_INFO_NESTED_EXCEPTION_MASK   INTR_INFO_NESTED_EXCEPTION_MASK
 
 #define INTR_TYPE_EXT_INTR             (EVENT_TYPE_EXTINT << 8)        /* 
external interrupt */
 #define INTR_TYPE_RESERVED             (EVENT_TYPE_RESERVED << 8)      /* 
reserved */
diff --git a/arch/x86/include/uapi/asm/kvm.h b/arch/x86/include/uapi/asm/kvm.h
index 6a67832c59bb..659c8391d40e 100644
--- a/arch/x86/include/uapi/asm/kvm.h
+++ b/arch/x86/include/uapi/asm/kvm.h
@@ -369,7 +369,8 @@ struct kvm_vcpu_events {
        struct {
                __u8 pending;
        } triple_fault;
-       __u8 reserved[26];
+       __u8 reserved[25];
+       __u8 exception_is_nested;
        __u8 exception_has_payload;
        __u64 exception_payload;
 };
diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c
index ea647938a2a6..7762db655b28 100644
--- a/arch/x86/kvm/svm/svm.c
+++ b/arch/x86/kvm/svm/svm.c
@@ -4392,7 +4392,7 @@ static void svm_complete_interrupts(struct kvm_vcpu *vcpu)
 
                kvm_requeue_exception(vcpu, vector,
                                      exitintinfo & SVM_EXITINTINFO_VALID_ERR,
-                                     error_code);
+                                     error_code, false);
                break;
        }
        case SVM_EXITINTINFO_TYPE_INTR:
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index 362bd4dda298..25c3301e137b 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -2008,8 +2008,13 @@ void vmx_inject_exception(struct kvm_vcpu *vcpu)
                vmcs_write32(VM_ENTRY_INSTRUCTION_LEN,
                             vmx->vcpu.arch.event_exit_inst_len);
                intr_info |= INTR_TYPE_SOFT_EXCEPTION;
-       } else
+       } else {
                intr_info |= INTR_TYPE_HARD_EXCEPTION;
+               if (is_fred_enabled(vcpu)) {
+                       if (ex->is_nested)
+                               intr_info |= INTR_INFO_NESTED_EXCEPTION_MASK;
+               }
+       }
 
        vmcs_write32(VM_ENTRY_INTR_INFO_FIELD, intr_info);
 
@@ -7501,7 +7506,8 @@ static void __vmx_complete_interrupts(struct kvm_vcpu 
*vcpu,
 
                kvm_requeue_exception(vcpu, vector,
                                      idt_vectoring_info & 
VECTORING_INFO_DELIVER_CODE_MASK,
-                                     error_code);
+                                     error_code,
+                                     idt_vectoring_info & 
VECTORING_INFO_NESTED_EXCEPTION_MASK);
                break;
        }
        case INTR_TYPE_SOFT_INTR:
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 01198aba27cf..94d684210cf6 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -461,6 +461,7 @@ static void kvm_multiple_exception(struct kvm_vcpu *vcpu, 
unsigned int nr,
                                   bool has_error, u32 error_code,
                                   bool has_payload, unsigned long payload)
 {
+       bool is_nested = false;
        u32 prev_nr;
        int class1, class2;
 
@@ -487,6 +488,11 @@ static void kvm_multiple_exception(struct kvm_vcpu *vcpu, 
unsigned int nr,
                vcpu->arch.exception.error_code = error_code;
                vcpu->arch.exception.has_payload = has_payload;
                vcpu->arch.exception.payload = payload;
+               /* #DF is NOT a nested event, per its definition. */
+               vcpu->arch.exception.is_nested = (nr != DF_VECTOR) &&
+                                                (is_nested ||
+                                                 vcpu->arch.nmi_injected ||
+                                                 
vcpu->arch.interrupt.injected);
                return;
        }
 
@@ -510,6 +516,9 @@ static void kvm_multiple_exception(struct kvm_vcpu *vcpu, 
unsigned int nr,
 
                kvm_queue_exception_e(vcpu, DF_VECTOR, 0);
        } else {
+               /* The new exception arrived while delivering the previous one. 
*/
+               is_nested = true;
+
                /* replace previous exception with a new one in a hope
                   that instruction re-execution will regenerate lost
                   exception */
@@ -538,7 +547,8 @@ static void kvm_queue_exception_e_p(struct kvm_vcpu *vcpu, 
unsigned nr,
 }
 
 void kvm_requeue_exception(struct kvm_vcpu *vcpu, unsigned int nr,
-                          bool has_error_code, u32 error_code)
+                          bool has_error_code, u32 error_code,
+                          bool is_nested)
 {
 
        /*
@@ -563,6 +573,7 @@ void kvm_requeue_exception(struct kvm_vcpu *vcpu, unsigned 
int nr,
        vcpu->arch.exception.error_code = error_code;
        vcpu->arch.exception.has_payload = false;
        vcpu->arch.exception.payload = 0;
+       vcpu->arch.exception.is_nested = is_nested;
 }
 EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_requeue_exception);
 
@@ -3000,6 +3011,7 @@ static void kvm_vcpu_ioctl_x86_get_vcpu_events(struct 
kvm_vcpu *vcpu,
        events->exception.error_code = ex->error_code;
        events->exception_has_payload = ex->has_payload;
        events->exception_payload = ex->payload;
+       events->exception_is_nested = ex->is_nested;
 
        events->interrupt.injected =
                vcpu->arch.interrupt.injected && !vcpu->arch.interrupt.soft;
@@ -3060,6 +3072,8 @@ static int kvm_vcpu_ioctl_x86_set_vcpu_events(struct 
kvm_vcpu *vcpu,
        if (events->flags & KVM_VCPUEVENT_VALID_FRED_STATE) {
                if (!vcpu->kvm->arch.exception_fred_state_enabled)
                        return -EINVAL;
+       } else {
+               events->exception_is_nested = 0;
        }
 
        if ((events->exception.injected || events->exception.pending) &&
@@ -3087,6 +3101,7 @@ static int kvm_vcpu_ioctl_x86_set_vcpu_events(struct 
kvm_vcpu *vcpu,
        vcpu->arch.exception.error_code = events->exception.error_code;
        vcpu->arch.exception.has_payload = events->exception_has_payload;
        vcpu->arch.exception.payload = events->exception_payload;
+       vcpu->arch.exception.is_nested = events->exception_is_nested;
 
        vcpu->arch.interrupt.injected = events->interrupt.injected;
        vcpu->arch.interrupt.nr = events->interrupt.nr;
@@ -8978,6 +8993,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
                                           ex->has_payload, ex->payload);
                ex->injected = false;
                ex->pending = false;
+               ex->is_nested = false;
        }
        vcpu->arch.exception_from_userspace = false;
 
diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h
index 0f5919b092e4..2d6bae66cfae 100644
--- a/arch/x86/kvm/x86.h
+++ b/arch/x86/kvm/x86.h
@@ -158,6 +158,7 @@ static inline void kvm_clear_exception_queue(struct 
kvm_vcpu *vcpu)
 {
        vcpu->arch.exception.pending = false;
        vcpu->arch.exception.injected = false;
+       vcpu->arch.exception.is_nested = false;
        vcpu->arch.exception_vmexit.pending = false;
 }
 
@@ -502,7 +503,8 @@ void kvm_queue_exception(struct kvm_vcpu *vcpu, unsigned 
nr);
 void kvm_queue_exception_e(struct kvm_vcpu *vcpu, unsigned nr, u32 error_code);
 void kvm_queue_exception_p(struct kvm_vcpu *vcpu, unsigned nr, unsigned long 
payload);
 void kvm_requeue_exception(struct kvm_vcpu *vcpu, unsigned int nr,
-                          bool has_error_code, u32 error_code);
+                          bool has_error_code, u32 error_code,
+                          bool is_nested);
 void kvm_inject_page_fault(struct kvm_vcpu *vcpu, struct x86_exception *fault,
                           bool from_hardware);
 void __kvm_inject_emulated_page_fault(struct kvm_vcpu *vcpu,
-- 
2.43.0


Reply via email to