]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - arch/x86/kvm/vmx.c
x86/cpufeature: Replace cpu_has_xsaves with boot_cpu_has() usage
[mirror_ubuntu-artful-kernel.git] / arch / x86 / kvm / vmx.c
index 5e45c2731a5d60ba6a9adfc58841ca075cd90834..d5908bde93429d5d5759478d4c61380935e4b706 100644 (file)
@@ -598,6 +598,10 @@ struct vcpu_vmx {
        struct page *pml_pg;
 
        u64 current_tsc_ratio;
+
+       bool guest_pkru_valid;
+       u32 guest_pkru;
+       u32 host_pkru;
 };
 
 enum segment_cache_field {
@@ -2107,6 +2111,7 @@ static void vmx_vcpu_pi_load(struct kvm_vcpu *vcpu, int cpu)
        } while (cmpxchg(&pi_desc->control, old.control,
                        new.control) != old.control);
 }
+
 /*
  * Switches to specified vcpu, until a matching vcpu_put(), but assumes
  * vcpu mutex is already taken.
@@ -2167,6 +2172,7 @@ static void vmx_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
        }
 
        vmx_vcpu_pi_load(vcpu, cpu);
+       vmx->host_pkru = read_pkru();
 }
 
 static void vmx_vcpu_pi_put(struct kvm_vcpu *vcpu)
@@ -2286,6 +2292,11 @@ static void vmx_set_rflags(struct kvm_vcpu *vcpu, unsigned long rflags)
        vmcs_writel(GUEST_RFLAGS, rflags);
 }
 
+static u32 vmx_get_pkru(struct kvm_vcpu *vcpu)
+{
+       return to_vmx(vcpu)->guest_pkru;
+}
+
 static u32 vmx_get_interrupt_shadow(struct kvm_vcpu *vcpu)
 {
        u32 interruptibility = vmcs_read32(GUEST_INTERRUPTIBILITY_INFO);
@@ -2712,8 +2723,15 @@ static void nested_vmx_setup_ctls_msrs(struct vcpu_vmx *vmx)
        } else
                vmx->nested.nested_vmx_ept_caps = 0;
 
+       /*
+        * Old versions of KVM use the single-context version without
+        * checking for support, so declare that it is supported even
+        * though it is treated as global context.  The alternative is
+        * not failing the single-context invvpid, and it is worse.
+        */
        if (enable_vpid)
                vmx->nested.nested_vmx_vpid_caps = VMX_VPID_INVVPID_BIT |
+                               VMX_VPID_EXTENT_SINGLE_CONTEXT_BIT |
                                VMX_VPID_EXTENT_GLOBAL_CONTEXT_BIT;
        else
                vmx->nested.nested_vmx_vpid_caps = 0;
@@ -3368,7 +3386,7 @@ static __init int setup_vmcs_config(struct vmcs_config *vmcs_conf)
                }
        }
 
-       if (cpu_has_xsaves)
+       if (boot_cpu_has(X86_FEATURE_XSAVES))
                rdmsrl(MSR_IA32_XSS, host_xss);
 
        return 0;
@@ -3886,13 +3904,17 @@ static int vmx_set_cr4(struct kvm_vcpu *vcpu, unsigned long cr4)
 
        if (!enable_unrestricted_guest && !is_paging(vcpu))
                /*
-                * SMEP/SMAP is disabled if CPU is in non-paging mode in
-                * hardware.  However KVM always uses paging mode without
-                * unrestricted guest.
-                * To emulate this behavior, SMEP/SMAP needs to be manually
-                * disabled when guest switches to non-paging mode.
+                * SMEP/SMAP/PKU is disabled if CPU is in non-paging mode in
+                * hardware.  To emulate this behavior, SMEP/SMAP/PKU needs
+                * to be manually disabled when guest switches to non-paging
+                * mode.
+                *
+                * If !enable_unrestricted_guest, the CPU is always running
+                * with CR0.PG=1 and CR4 needs to be modified.
+                * If enable_unrestricted_guest, the CPU automatically
+                * disables SMEP/SMAP/PKU when the guest sets CR0.PG=0.
                 */
-               hw_cr4 &= ~(X86_CR4_SMEP | X86_CR4_SMAP);
+               hw_cr4 &= ~(X86_CR4_SMEP | X86_CR4_SMAP | X86_CR4_PKE);
 
        vmcs_writel(CR4_READ_SHADOW, cr4);
        vmcs_writel(GUEST_CR4, hw_cr4);
@@ -5506,7 +5528,7 @@ static int handle_set_cr4(struct kvm_vcpu *vcpu, unsigned long val)
                return kvm_set_cr4(vcpu, val);
 }
 
-/* called to set cr0 as approriate for clts instruction exit. */
+/* called to set cr0 as appropriate for clts instruction exit. */
 static void handle_clts(struct kvm_vcpu *vcpu)
 {
        if (is_guest_mode(vcpu)) {
@@ -7245,7 +7267,7 @@ static int handle_vmwrite(struct kvm_vcpu *vcpu)
        /* The value to write might be 32 or 64 bits, depending on L1's long
         * mode, and eventually we need to write that into a field of several
         * possible lengths. The code below first zero-extends the value to 64
-        * bit (field_value), and then copies only the approriate number of
+        * bit (field_value), and then copies only the appropriate number of
         * bits into the vmcs12 field.
         */
        u64 field_value = 0;
@@ -7399,6 +7421,7 @@ static int handle_invept(struct kvm_vcpu *vcpu)
        if (!(types & (1UL << type))) {
                nested_vmx_failValid(vcpu,
                                VMXERR_INVALID_OPERAND_TO_INVEPT_INVVPID);
+               skip_emulated_instruction(vcpu);
                return 1;
        }
 
@@ -7457,6 +7480,7 @@ static int handle_invvpid(struct kvm_vcpu *vcpu)
        if (!(types & (1UL << type))) {
                nested_vmx_failValid(vcpu,
                        VMXERR_INVALID_OPERAND_TO_INVEPT_INVVPID);
+               skip_emulated_instruction(vcpu);
                return 1;
        }
 
@@ -7473,12 +7497,17 @@ static int handle_invvpid(struct kvm_vcpu *vcpu)
        }
 
        switch (type) {
+       case VMX_VPID_EXTENT_SINGLE_CONTEXT:
+               /*
+                * Old versions of KVM use the single-context version so we
+                * have to support it; just treat it the same as all-context.
+                */
        case VMX_VPID_EXTENT_ALL_CONTEXT:
                __vmx_flush_tlb(vcpu, to_vmx(vcpu)->nested.vpid02);
                nested_vmx_succeed(vcpu);
                break;
        default:
-               /* Trap single context invalidation invvpid calls */
+               /* Trap individual address invalidation invvpid calls */
                BUG_ON(1);
                break;
        }
@@ -8385,6 +8414,7 @@ static void vmx_complete_atomic_exit(struct vcpu_vmx *vmx)
 static void vmx_handle_external_intr(struct kvm_vcpu *vcpu)
 {
        u32 exit_intr_info = vmcs_read32(VM_EXIT_INTR_INFO);
+       register void *__sp asm(_ASM_SP);
 
        /*
         * If external interrupt exists, IF bit is set in rflags/eflags on the
@@ -8417,8 +8447,9 @@ static void vmx_handle_external_intr(struct kvm_vcpu *vcpu)
                        "call *%[entry]\n\t"
                        :
 #ifdef CONFIG_X86_64
-                       [sp]"=&r"(tmp)
+                       [sp]"=&r"(tmp),
 #endif
+                       "+r"(__sp)
                        :
                        [entry]"r"(entry),
                        [ss]"i"(__KERNEL_DS),
@@ -8619,6 +8650,9 @@ static void __noclone vmx_vcpu_run(struct kvm_vcpu *vcpu)
        if (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP)
                vmx_set_interrupt_shadow(vcpu, 0);
 
+       if (vmx->guest_pkru_valid)
+               __write_pkru(vmx->guest_pkru);
+
        atomic_switch_perf_msrs(vmx);
        debugctlmsr = get_debugctlmsr();
 
@@ -8758,6 +8792,20 @@ static void __noclone vmx_vcpu_run(struct kvm_vcpu *vcpu)
 
        vmx->exit_reason = vmcs_read32(VM_EXIT_REASON);
 
+       /*
+        * eager fpu is enabled if PKEY is supported and CR4 is switched
+        * back on host, so it is safe to read guest PKRU from current
+        * XSAVE.
+        */
+       if (boot_cpu_has(X86_FEATURE_OSPKE)) {
+               vmx->guest_pkru = __read_pkru();
+               if (vmx->guest_pkru != vmx->host_pkru) {
+                       vmx->guest_pkru_valid = true;
+                       __write_pkru(vmx->host_pkru);
+               } else
+                       vmx->guest_pkru_valid = false;
+       }
+
        /*
         * the KVM_REQ_EVENT optimization bit is only on for one entry, and if
         * we did not inject a still-pending event to L1 now because of
@@ -10882,6 +10930,9 @@ static struct kvm_x86_ops vmx_x86_ops = {
        .cache_reg = vmx_cache_reg,
        .get_rflags = vmx_get_rflags,
        .set_rflags = vmx_set_rflags,
+
+       .get_pkru = vmx_get_pkru,
+
        .fpu_activate = vmx_fpu_activate,
        .fpu_deactivate = vmx_fpu_deactivate,