]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
KVM: VMX: enable use of MBEC
authorPaolo Bonzini <pbonzini@redhat.com>
Wed, 8 Apr 2026 15:42:05 +0000 (11:42 -0400)
committerPaolo Bonzini <pbonzini@redhat.com>
Sun, 10 May 2026 12:55:00 +0000 (14:55 +0200)
If available, set SECONDARY_EXEC_MODE_BASED_EPT_EXEC in the secondary
execution controls.

The changes are limited because the MMU is designed to create the same
sPTEs independent of the MBEC setting.  On hosts lacking support for
MBEC, and in nested guests which cannot enable it as of this commit,
the XU bit is ignored by the processor.

Note that, as of this patch, MBEC is not available to L1 hypervisors
for their guests.

Tested-by: David Riley <d.riley@proxmox.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
arch/x86/include/asm/vmx.h
arch/x86/kvm/mmu.h
arch/x86/kvm/vmx/capabilities.h
arch/x86/kvm/vmx/common.h
arch/x86/kvm/vmx/main.c
arch/x86/kvm/vmx/nested.c
arch/x86/kvm/vmx/vmx.c
arch/x86/kvm/vmx/vmx.h
arch/x86/kvm/vmx/x86_ops.h

index 2b30b921b375b8aa1bae26a873109d2b8732f376..54aa5be50df945d2b52b344763b3e1c02a1dd21f 100644 (file)
@@ -619,9 +619,12 @@ enum vm_entry_failure_code {
 #define EPT_VIOLATION_GVA_TRANSLATED   BIT(8)
 
 #define EPT_VIOLATION_RWX_TO_PROT(__epte) (((__epte) & VMX_EPT_RWX_MASK) << 3)
+#define EPT_VIOLATION_USER_EXEC_TO_PROT(__epte) (((__epte) & VMX_EPT_USER_EXECUTABLE_MASK) >> 4)
 
 static_assert(EPT_VIOLATION_RWX_TO_PROT(VMX_EPT_RWX_MASK) ==
              (EPT_VIOLATION_PROT_READ | EPT_VIOLATION_PROT_WRITE | EPT_VIOLATION_PROT_EXEC));
+static_assert(EPT_VIOLATION_USER_EXEC_TO_PROT(VMX_EPT_USER_EXECUTABLE_MASK) ==
+             (EPT_VIOLATION_PROT_USER_EXEC));
 
 /*
  * Exit Qualifications for NOTIFY VM EXIT
index d8c13e43c2d7967811f9f995c5615f3edc7dc2ae..23bc5b18efd079df68cd750665e3153d4e438bf0 100644 (file)
@@ -83,6 +83,11 @@ static inline gfn_t kvm_mmu_max_gfn(void)
        return (1ULL << (max_gpa_bits - PAGE_SHIFT)) - 1;
 }
 
+static inline bool mmu_has_mbec(struct kvm_mmu *mmu)
+{
+       return mmu->root_role.cr4_smep;
+}
+
 u8 kvm_mmu_get_max_tdp_level(void);
 
 void kvm_mmu_set_mmio_spte_mask(u64 mmio_value, u64 mmio_mask, u64 access_mask);
index 7e59eb0f41bbea0c4b93ff952597fcd671179291..07469d1cfe7462cb78f75d973abbf0b855cfc2e5 100644 (file)
@@ -15,6 +15,7 @@ extern bool __read_mostly enable_ept;
 extern bool __read_mostly enable_unrestricted_guest;
 extern bool __read_mostly enable_ept_ad_bits;
 extern bool __read_mostly enable_pml;
+extern bool __read_mostly enable_mbec;
 extern int __read_mostly pt_mode;
 
 #define PT_MODE_SYSTEM         0
@@ -406,4 +407,10 @@ static inline bool cpu_has_notify_vmexit(void)
                SECONDARY_EXEC_NOTIFY_VM_EXITING;
 }
 
+static inline bool cpu_has_ept_mbec(void)
+{
+       return vmcs_config.cpu_based_2nd_exec_ctrl &
+               SECONDARY_EXEC_MODE_BASED_EPT_EXEC;
+}
+
 #endif /* __KVM_X86_VMX_CAPS_H */
index 1afbf272efae3a19afdc294b9096020263a1d504..40fa72f31fc7990735a1d142ee48ba25187ebe94 100644 (file)
@@ -91,15 +91,15 @@ static inline int __vmx_handle_ept_violation(struct kvm_vcpu *vcpu, gpa_t gpa,
        /* Is it a fetch fault? */
        error_code |= (exit_qualification & EPT_VIOLATION_ACC_INSTR)
                      ? PFERR_FETCH_MASK : 0;
-       /*
-        * ept page table entry is present?
-        * note: unconditionally clear USER_EXEC until mode-based
-        * execute control is implemented
-        */
+       /* ept page table entry is present?  */
        error_code |= (exit_qualification &
                       (EPT_VIOLATION_PROT_MASK & ~EPT_VIOLATION_PROT_USER_EXEC))
                      ? PFERR_PRESENT_MASK : 0;
 
+       if (mmu_has_mbec(vcpu->arch.mmu))
+               error_code |= (exit_qualification & EPT_VIOLATION_PROT_USER_EXEC)
+                             ? PFERR_PRESENT_MASK : 0;
+
        if (exit_qualification & EPT_VIOLATION_GVA_IS_VALID)
                error_code |= (exit_qualification & EPT_VIOLATION_GVA_TRANSLATED) ?
                              PFERR_GUEST_FINAL_MASK : PFERR_GUEST_PAGE_MASK;
index dbebddf648be738db9e77ff4ab0b79651e75f8a9..83d9921277eac2df42e51466eab79fd7cf3b0c9f 100644 (file)
@@ -755,6 +755,14 @@ static int vt_set_identity_map_addr(struct kvm *kvm, u64 ident_addr)
        return vmx_set_identity_map_addr(kvm, ident_addr);
 }
 
+static bool vt_tdp_has_smep(struct kvm *kvm)
+{
+       if (is_td(kvm))
+               return false;
+
+       return vmx_tdp_has_smep(kvm);
+}
+
 static u64 vt_get_l2_tsc_offset(struct kvm_vcpu *vcpu)
 {
        /* TDX doesn't support L2 guest at the moment. */
@@ -966,6 +974,7 @@ struct kvm_x86_ops vt_x86_ops __initdata = {
        .set_tss_addr = vt_op(set_tss_addr),
        .set_identity_map_addr = vt_op(set_identity_map_addr),
        .get_mt_mask = vmx_get_mt_mask,
+       .tdp_has_smep = vt_op(tdp_has_smep),
 
        .get_exit_info = vt_op(get_exit_info),
        .get_entry_info = vt_op(get_entry_info),
index cd1924c6e07584daf9df46c43db16de1af878b88..299d4ca60fb3aebe6137060c9cca280175f7dd68 100644 (file)
@@ -2440,6 +2440,7 @@ static void prepare_vmcs02_early(struct vcpu_vmx *vmx, struct loaded_vmcs *vmcs0
                                  SECONDARY_EXEC_VIRTUAL_INTR_DELIVERY |
                                  SECONDARY_EXEC_APIC_REGISTER_VIRT |
                                  SECONDARY_EXEC_ENABLE_VMFUNC |
+                                 SECONDARY_EXEC_MODE_BASED_EPT_EXEC |
                                  SECONDARY_EXEC_DESC);
 
                if (nested_cpu_has(vmcs12,
index 337bbfecc0210589c2960bf7b12727c3cfc61e30..d9ba9f14ffd1214ffc0dfb09b239c67c2822e70b 100644 (file)
@@ -114,6 +114,9 @@ module_param(emulate_invalid_guest_state, bool, 0444);
 static bool __read_mostly fasteoi = 1;
 module_param(fasteoi, bool, 0444);
 
+bool __read_mostly enable_mbec = 1;
+module_param_named(mbec, enable_mbec, bool, 0444);
+
 module_param(enable_apicv, bool, 0444);
 module_param(enable_ipiv, bool, 0444);
 
@@ -2773,6 +2776,7 @@ static int setup_vmcs_config(struct vmcs_config *vmcs_conf,
                        return -EIO;
 
                vmx_cap->ept = 0;
+               _cpu_based_2nd_exec_control &= ~SECONDARY_EXEC_MODE_BASED_EPT_EXEC;
                _cpu_based_2nd_exec_control &= ~SECONDARY_EXEC_EPT_VIOLATION_VE;
        }
        if (!(_cpu_based_2nd_exec_control & SECONDARY_EXEC_ENABLE_VPID) &&
@@ -4735,6 +4739,9 @@ static u32 vmx_secondary_exec_control(struct vcpu_vmx *vmx)
         */
        exec_control &= ~SECONDARY_EXEC_ENABLE_VMFUNC;
 
+       if (!enable_mbec)
+               exec_control &= ~SECONDARY_EXEC_MODE_BASED_EPT_EXEC;
+
        /* SECONDARY_EXEC_DESC is enabled/disabled on writes to CR4.UMIP,
         * in vmx_set_cr4.  */
        exec_control &= ~SECONDARY_EXEC_DESC;
@@ -8622,6 +8629,8 @@ __init int vmx_hardware_setup(void)
 
        if (!cpu_has_vmx_ept_ad_bits() || !enable_ept)
                enable_ept_ad_bits = 0;
+       if (!cpu_has_ept_mbec() || !enable_ept)
+               enable_mbec = 0;
 
        if (!cpu_has_vmx_unrestricted_guest() || !enable_ept)
                enable_unrestricted_guest = 0;
index db84e8001da586b96da97a91893742ea83d54408..0a4e263c40950c7e386916d3acbcc72af9233d56 100644 (file)
@@ -567,6 +567,7 @@ static inline u8 vmx_get_rvi(void)
         SECONDARY_EXEC_ENABLE_VMFUNC |                                 \
         SECONDARY_EXEC_BUS_LOCK_DETECTION |                            \
         SECONDARY_EXEC_NOTIFY_VM_EXITING |                             \
+        SECONDARY_EXEC_MODE_BASED_EPT_EXEC |                           \
         SECONDARY_EXEC_ENCLS_EXITING |                                 \
         SECONDARY_EXEC_EPT_VIOLATION_VE)
 
index d09abeac2b56af65cf2f10e3d02e081be0ec4d2e..4098580742462ce41ce5779fa23ef3aecd3113da 100644 (file)
@@ -4,6 +4,7 @@
 
 #include <linux/kvm_host.h>
 
+#include "capabilities.h"
 #include "x86.h"
 
 __init int vmx_hardware_setup(void);
@@ -104,6 +105,11 @@ int vmx_set_tss_addr(struct kvm *kvm, unsigned int addr);
 int vmx_set_identity_map_addr(struct kvm *kvm, u64 ident_addr);
 u8 vmx_get_mt_mask(struct kvm_vcpu *vcpu, gfn_t gfn, bool is_mmio);
 
+static inline bool vmx_tdp_has_smep(struct kvm *kvm)
+{
+       return enable_mbec;
+}
+
 void vmx_get_exit_info(struct kvm_vcpu *vcpu, u32 *reason,
                       u64 *info1, u64 *info2, u32 *intr_info, u32 *error_code);
 void vmx_get_entry_info(struct kvm_vcpu *vcpu, u32 *intr_info, u32 *error_code);