# Commit 10b2b21a241795394637167bd4b076f2de17741f
# Date 2013-06-04 17:25:41 +0200
# Author Jan Beulich <jbeulich@suse.com>
# Committer Jan Beulich <jbeulich@suse.com>
x86: fix XCR0 handling

- both VMX and SVM ignored the ECX input to XSETBV
- both SVM and VMX used the full 64-bit RAX when calculating the input
  mask to XSETBV
- faults on XSETBV did not get recovered from

Also consolidate the handling for PV and HVM into a single function,
and make the per-CPU variable "xcr0" static to xstate.c.

Signed-off-by: Jan Beulich <jbeulich@suse.com>
Acked-by: Keir Fraser <keir@xen.org>
Acked-by: George Dunlap <george.dunlap@eu.citrix.com>

Index: xen-4.1.6-testing/xen/arch/x86/domain.c
===================================================================
--- xen-4.1.6-testing.orig/xen/arch/x86/domain.c
+++ xen-4.1.6-testing/xen/arch/x86/domain.c
@@ -1498,8 +1498,9 @@ static void __context_switch(void)
         memcpy(stack_regs,
                &n->arch.guest_context.user_regs,
                CTXT_SWITCH_STACK_BYTES);
-        if ( xsave_enabled(n) && n->arch.xcr0 != get_xcr0() )
-            set_xcr0(n->arch.xcr0);
+        if ( xsave_enabled(n) && n->arch.xcr0 != get_xcr0() &&
+             !set_xcr0(n->arch.xcr0) )
+            BUG();
         n->arch.ctxt_switch_to(n);
     }
 
Index: xen-4.1.6-testing/xen/arch/x86/hvm/hvm.c
===================================================================
--- xen-4.1.6-testing.orig/xen/arch/x86/hvm/hvm.c
+++ xen-4.1.6-testing/xen/arch/x86/hvm/hvm.c
@@ -1242,25 +1242,17 @@ bool_t hvm_hap_nested_page_fault(paddr_t
     return 0;
 }
 
-int hvm_handle_xsetbv(u64 new_bv)
+int hvm_handle_xsetbv(u32 index, u64 new_bv)
 {
-    struct vcpu *v = current;
     struct segment_register sreg;
 
-    hvm_get_segment_register(v, x86_seg_ss, &sreg);
+    hvm_get_segment_register(current, x86_seg_ss, &sreg);
     if ( sreg.attr.fields.dpl != 0 )
         goto err;
 
-    if ( ((new_bv ^ xfeature_mask) & ~xfeature_mask) || !(new_bv & 1) )
+    if ( handle_xsetbv(index, new_bv) )
         goto err;
 
-    if ( (xfeature_mask & XSTATE_YMM & new_bv) && !(new_bv & XSTATE_SSE) )
-        goto err;
-
-    v->arch.xcr0 = new_bv;
-    v->arch.xcr0_accum |= new_bv;
-    set_xcr0(new_bv);
-
     return 0;
 err:
     hvm_inject_exception(TRAP_gp_fault, 0, 0);
Index: xen-4.1.6-testing/xen/arch/x86/hvm/svm/svm.c
===================================================================
--- xen-4.1.6-testing.orig/xen/arch/x86/hvm/svm/svm.c
+++ xen-4.1.6-testing/xen/arch/x86/hvm/svm/svm.c
@@ -1832,7 +1832,8 @@ void svm_vmexit_handler(struct cpu_user_
     case VMEXIT_XSETBV:
         if ( (inst_len = __get_instruction_length(current, INSTR_XSETBV))==0 )
             break;
-        if ( hvm_handle_xsetbv((((u64)regs->edx) << 32) | regs->eax) == 0 )
+        if ( hvm_handle_xsetbv(regs->ecx,
+                               ((u64)regs->edx << 32) | (u32)regs->eax) == 0 )
             __update_guest_eip(regs, inst_len);
         break;
 
Index: xen-4.1.6-testing/xen/arch/x86/hvm/vmx/vmx.c
===================================================================
--- xen-4.1.6-testing.orig/xen/arch/x86/hvm/vmx/vmx.c
+++ xen-4.1.6-testing/xen/arch/x86/hvm/vmx/vmx.c
@@ -2541,12 +2541,10 @@ void vmx_vmexit_handler(struct cpu_user_
         break;
 
     case EXIT_REASON_XSETBV:
-    {
-        u64 new_bv = (((u64)regs->edx) << 32) | regs->eax;
-        if ( hvm_handle_xsetbv(new_bv) == 0 )
+        if ( hvm_handle_xsetbv(regs->ecx,
+                               ((u64)regs->edx << 32) | (u32)regs->eax) == 0 )
             update_guest_eip(); /* Safe: XSETBV */
         break;
-    }
 
     case EXIT_REASON_ACCESS_GDTR_OR_IDTR:
     case EXIT_REASON_ACCESS_LDTR_OR_TR:
Index: xen-4.1.6-testing/xen/arch/x86/i387.c
===================================================================
--- xen-4.1.6-testing.orig/xen/arch/x86/i387.c
+++ xen-4.1.6-testing/xen/arch/x86/i387.c
@@ -103,13 +103,17 @@ void setup_fpu(struct vcpu *v)
 
     if ( xsave_enabled(v) )
     {
+        bool_t ok;
+
         /*
          * XCR0 normally represents what guest OS set. In case of Xen itself, 
          * we set all supported feature mask before doing save/restore.
          */
-        set_xcr0(v->arch.xcr0_accum);
+        ok = set_xcr0(v->arch.xcr0_accum);
+        ASSERT(ok);
         xrstor(v);
-        set_xcr0(v->arch.xcr0);
+        ok = set_xcr0(v->arch.xcr0);
+        ASSERT(ok);
     }
     else if ( v->fpu_initialised )
     {
@@ -152,11 +156,13 @@ void save_init_fpu(struct vcpu *v)
     if ( xsave_enabled(v) )
     {
         struct xsave_struct *ptr = v->arch.xsave_area;
+        bool_t ok;
 
         /* XCR0 normally represents what guest OS set. In case of Xen itself,
          * we set all accumulated feature mask before doing save/restore.
          */
-        set_xcr0(v->arch.xcr0_accum);
+        ok = set_xcr0(v->arch.xcr0_accum);
+        ASSERT(ok);
 
 #ifdef CONFIG_X86_64
         if ( word_size <= 0 || !is_pv_32bit_vcpu(v) )
@@ -205,7 +211,8 @@ void save_init_fpu(struct vcpu *v)
         if ( word_size >= 0 )
             ptr->fpu_sse.x[FPU_WORD_SIZE_OFFSET] = word_size;
 
-        set_xcr0(v->arch.xcr0);
+        ok = set_xcr0(v->arch.xcr0);
+        ASSERT(ok);
     }
     else if ( cpu_has_fxsr )
     {
@@ -384,7 +391,8 @@ void xsave_init(void)
      * Set CR4_OSXSAVE and run "cpuid" to get xsave_cntxt_size.
      */
     set_in_cr4(X86_CR4_OSXSAVE);
-    set_xcr0((((u64)edx << 32) | eax) & XCNTXT_MASK);
+    if ( !set_xcr0((((u64)edx << 32) | eax) & XCNTXT_MASK) )
+        BUG();
     cpuid_count(XSTATE_CPUID, 0, &eax, &ebx, &ecx, &edx);
 
     if ( cpu == 0 )
@@ -453,6 +461,28 @@ bool_t xsave_enabled(const struct vcpu *
     return cpu_has_xsave;	
 }
 
+int handle_xsetbv(u32 index, u64 new_bv)
+{
+    struct vcpu *curr = current;
+
+    if ( index != XCR_XFEATURE_ENABLED_MASK )
+        return -EOPNOTSUPP;
+
+    if ( (new_bv & ~xfeature_mask) || !(new_bv & XSTATE_FP) )
+        return -EINVAL;
+
+    if ( (new_bv & XSTATE_YMM) && !(new_bv & XSTATE_SSE) )
+        return -EINVAL;
+
+    if ( !set_xcr0(new_bv) )
+        return -EFAULT;
+
+    curr->arch.xcr0 = new_bv;
+    curr->arch.xcr0_accum |= new_bv;
+
+    return 0;
+}
+
 /*
  * Local variables:
  * mode: C
Index: xen-4.1.6-testing/xen/arch/x86/traps.c
===================================================================
--- xen-4.1.6-testing.orig/xen/arch/x86/traps.c
+++ xen-4.1.6-testing/xen/arch/x86/traps.c
@@ -2219,25 +2219,9 @@ static int emulate_privileged_op(struct
             if ( !guest_kernel_mode(v, regs) )
                 goto fail;
 
-            switch ( (u32)regs->ecx )
-            {
-                case XCR_XFEATURE_ENABLED_MASK:
-                    /* bit 0 of XCR0 must be set and reserved bit must not be set */
-                    if ( !(new_xfeature & XSTATE_FP) || (new_xfeature & ~xfeature_mask) )
-                        goto fail;
-
-                    /* YMM state takes SSE state as prerequisite. */
-                    if ( (xfeature_mask & new_xfeature & XSTATE_YMM) &&
-                         !(new_xfeature & XSTATE_SSE) )
-                        goto fail;
+            if ( handle_xsetbv(regs->ecx, new_xfeature) )
+                goto fail;
 
-                    v->arch.xcr0 = new_xfeature;
-                    v->arch.xcr0_accum |= new_xfeature;
-                    set_xcr0(new_xfeature);
-                    break;
-                default:
-                    goto fail;
-            }
             break;
         }
         default:
Index: xen-4.1.6-testing/xen/include/asm-x86/hvm/support.h
===================================================================
--- xen-4.1.6-testing.orig/xen/include/asm-x86/hvm/support.h
+++ xen-4.1.6-testing/xen/include/asm-x86/hvm/support.h
@@ -128,7 +128,7 @@ void hvm_triple_fault(void);
 
 void hvm_rdtsc_intercept(struct cpu_user_regs *regs);
 
-int hvm_handle_xsetbv(u64 new_bv);
+int __must_check hvm_handle_xsetbv(u32 index, u64 new_bv);
 
 /* These functions all return X86EMUL return codes. */
 int hvm_set_efer(uint64_t value);
Index: xen-4.1.6-testing/xen/include/asm-x86/i387.h
===================================================================
--- xen-4.1.6-testing.orig/xen/include/asm-x86/i387.h
+++ xen-4.1.6-testing/xen/include/asm-x86/i387.h
@@ -13,6 +13,7 @@
 
 #include <xen/types.h>
 #include <xen/percpu.h>
+#include <asm/asm_defns.h>
 
 struct vcpu;
 
@@ -98,19 +99,29 @@ struct xsave_struct
 
 DECLARE_PER_CPU(uint64_t, xcr0);
 
-static inline void xsetbv(u32 index, u64 xfeatures)
+static inline bool_t xsetbv(u32 index, u64 xfeatures)
 {
     u32 hi = xfeatures >> 32;
     u32 lo = (u32)xfeatures;
 
-    asm volatile (".byte 0x0f,0x01,0xd1" :: "c" (index),
-            "a" (lo), "d" (hi));
+    asm volatile ( "1: .byte 0x0f,0x01,0xd1\n"
+                   "3:                     \n"
+                   ".section .fixup,\"ax\" \n"
+                   "2: xor %0,%0           \n"
+                   "   jmp 3b              \n"
+                   ".previous              \n"
+                   _ASM_EXTABLE(1b, 2b)
+                   : "+a" (lo)
+                   : "c" (index), "d" (hi));
+    return lo != 0;
 }
 
-static inline void set_xcr0(u64 xfeatures)
+static inline bool_t __must_check set_xcr0(u64 xfeatures)
 {
+    if ( !xsetbv(XCR_XFEATURE_ENABLED_MASK, xfeatures) )
+        return 0;
     this_cpu(xcr0) = xfeatures;
-    xsetbv(XCR_XFEATURE_ENABLED_MASK, xfeatures);
+    return 1;
 }
 
 static inline uint64_t get_xcr0(void)
@@ -118,6 +129,8 @@ static inline uint64_t get_xcr0(void)
     return this_cpu(xcr0);
 }
 
+int __must_check handle_xsetbv(u32 index, u64 new_bv);
+
 extern void setup_fpu(struct vcpu *v);
 extern void save_init_fpu(struct vcpu *v);
 
