x86/svm: Fix handling of EFLAGS.RF on task switch
authorAndrew Cooper <andrew.cooper3@citrix.com>
Tue, 3 Dec 2019 16:59:09 +0000 (16:59 +0000)
committerAndrew Cooper <andrew.cooper3@citrix.com>
Thu, 5 Dec 2019 13:19:28 +0000 (13:19 +0000)
VT-x updates RF before vmexit, so eflags written into the outgoing TSS happens
to be correct.  SVM does not update RF before vmexit, and instead provides it
via a bit in exitinfo2.

In practice, needing RF set in the outgoing state occurs when a task gate is
used to handle faults.

Extend hvm_task_switch() with an extra_eflags parameter which gets fed into
the outgoing TSS, and fill it in suitably from the SVM vmexit information.

Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
Reviewed-by: Jan Beulich <jbeulich@suse.com>
xen/arch/x86/hvm/hvm.c
xen/arch/x86/hvm/svm/svm.c
xen/arch/x86/hvm/vmx/vmx.c
xen/include/asm-x86/hvm/hvm.h

index 7f556171bd6018d68202d50489aef987d6c354ad..47573f71b830f9125f032865d96273076f2843b7 100644 (file)
@@ -2913,7 +2913,7 @@ void hvm_prepare_vm86_tss(struct vcpu *v, uint32_t base, uint32_t limit)
 
 void hvm_task_switch(
     uint16_t tss_sel, enum hvm_task_switch_reason taskswitch_reason,
-    int32_t errcode, unsigned int insn_len)
+    int32_t errcode, unsigned int insn_len, unsigned int extra_eflags)
 {
     struct vcpu *v = current;
     struct cpu_user_regs *regs = guest_cpu_user_regs();
@@ -2988,7 +2988,7 @@ void hvm_task_switch(
         eflags &= ~X86_EFLAGS_NT;
 
     tss.eip    = regs->eip + insn_len;
-    tss.eflags = eflags;
+    tss.eflags = eflags | extra_eflags;
     tss.eax    = regs->eax;
     tss.ecx    = regs->ecx;
     tss.edx    = regs->edx;
index 290bd4c882c126b9273e978afd692d94ac147ca5..7cb235a667955b2c6cdd53b1e0d2d21b95b2d231 100644 (file)
@@ -2812,7 +2812,8 @@ void svm_vmexit_handler(struct cpu_user_regs *regs)
         if ( (vmcb->exitinfo2 >> 44) & 1 )
             errcode = (uint32_t)vmcb->exitinfo2;
 
-        hvm_task_switch(vmcb->exitinfo1, reason, errcode, insn_len);
+        hvm_task_switch(vmcb->exitinfo1, reason, errcode, insn_len,
+                        (vmcb->exitinfo2 & (1ul << 48)) ? X86_EFLAGS_RF : 0);
         break;
     }
 
index 7450cbe40dcc2902d44392c57e8e14e6d27fef80..bafc3b30c57a8c401639ce03c05ae19b59a15997 100644 (file)
@@ -3963,7 +3963,8 @@ void vmx_vmexit_handler(struct cpu_user_regs *regs)
         else
              ecode = -1;
 
-        hvm_task_switch(exit_qualification, reasons[source], ecode, inst_len);
+        hvm_task_switch(exit_qualification, reasons[source], ecode, inst_len,
+                        0 /* EFLAGS.RF already updated. */);
         break;
     }
     case EXIT_REASON_CPUID:
index 17fb7efa6eb74971914ff2d94531696fffb7a8cb..1d7b66f92722cf4a4f65c24d810430bab03b60ac 100644 (file)
@@ -296,7 +296,7 @@ void hvm_set_rdtsc_exiting(struct domain *d, bool_t enable);
 enum hvm_task_switch_reason { TSW_jmp, TSW_iret, TSW_call_or_int };
 void hvm_task_switch(
     uint16_t tss_sel, enum hvm_task_switch_reason taskswitch_reason,
-    int32_t errcode, unsigned int insn_len);
+    int32_t errcode, unsigned int insn_len, unsigned int extra_eflags);
 
 enum hvm_access_type {
     hvm_access_insn_fetch,