]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
KVM: VMX: Emulate read and write to CET MSRs
authorYang Weijiang <weijiang.yang@intel.com>
Fri, 19 Sep 2025 22:32:21 +0000 (15:32 -0700)
committerSean Christopherson <seanjc@google.com>
Tue, 23 Sep 2025 16:10:47 +0000 (09:10 -0700)
Add emulation interface for CET MSR access. The emulation code is split
into common part and vendor specific part. The former does common checks
for MSRs, e.g., accessibility, data validity etc., then passes operation
to either XSAVE-managed MSRs via the helpers or CET VMCS fields.

SSP can only be read via RDSSP. Writing even requires destructive and
potentially faulting operations such as SAVEPREVSSP/RSTORSSP or
SETSSBSY/CLRSSBSY. Let the host use a pseudo-MSR that is just a wrapper
for the GUEST_SSP field of the VMCS.

Suggested-by: Sean Christopherson <seanjc@google.com>
Signed-off-by: Yang Weijiang <weijiang.yang@intel.com>
Tested-by: Mathias Krause <minipli@grsecurity.net>
Tested-by: John Allen <john.allen@amd.com>
Tested-by: Rick Edgecombe <rick.p.edgecombe@intel.com>
Signed-off-by: Chao Gao <chao.gao@intel.com>
[sean: drop call to kvm_set_xstate_msr() for S_CET, consolidate code]
Reviewed-by: Binbin Wu <binbin.wu@linux.intel.com>
Reviewed-by: Xiaoyao Li <xiaoyao.li@intel.com>
Link: https://lore.kernel.org/r/20250919223258.1604852-15-seanjc@google.com
Signed-off-by: Sean Christopherson <seanjc@google.com>
arch/x86/kvm/vmx/vmx.c
arch/x86/kvm/x86.c
arch/x86/kvm/x86.h

index d2fa64bddb578d88b8123ac8dddcce17c8e3095c..62bf93dc08254c4b939c7bb4ae18cf340c6442bb 100644 (file)
@@ -2093,6 +2093,15 @@ int vmx_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
                else
                        msr_info->data = vmx->pt_desc.guest.addr_a[index / 2];
                break;
+       case MSR_IA32_S_CET:
+               msr_info->data = vmcs_readl(GUEST_S_CET);
+               break;
+       case MSR_KVM_INTERNAL_GUEST_SSP:
+               msr_info->data = vmcs_readl(GUEST_SSP);
+               break;
+       case MSR_IA32_INT_SSP_TAB:
+               msr_info->data = vmcs_readl(GUEST_INTR_SSP_TABLE);
+               break;
        case MSR_IA32_DEBUGCTLMSR:
                msr_info->data = vmx_guest_debugctl_read();
                break;
@@ -2411,6 +2420,15 @@ int vmx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
                else
                        vmx->pt_desc.guest.addr_a[index / 2] = data;
                break;
+       case MSR_IA32_S_CET:
+               vmcs_writel(GUEST_S_CET, data);
+               break;
+       case MSR_KVM_INTERNAL_GUEST_SSP:
+               vmcs_writel(GUEST_SSP, data);
+               break;
+       case MSR_IA32_INT_SSP_TAB:
+               vmcs_writel(GUEST_INTR_SSP_TABLE, data);
+               break;
        case MSR_IA32_PERF_CAPABILITIES:
                if (data & PERF_CAP_LBR_FMT) {
                        if ((data & PERF_CAP_LBR_FMT) !=
index d85bb723f25af71417374a75351d968e4dda3703..54d280fe9a4b04651ed22493e64e4a9ccc6c69f6 100644 (file)
@@ -1890,6 +1890,44 @@ static int __kvm_set_msr(struct kvm_vcpu *vcpu, u32 index, u64 data,
 
                data = (u32)data;
                break;
+       case MSR_IA32_U_CET:
+       case MSR_IA32_S_CET:
+               if (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK) &&
+                   !guest_cpu_cap_has(vcpu, X86_FEATURE_IBT))
+                       return KVM_MSR_RET_UNSUPPORTED;
+               if (!kvm_is_valid_u_s_cet(vcpu, data))
+                       return 1;
+               break;
+       case MSR_KVM_INTERNAL_GUEST_SSP:
+               if (!host_initiated)
+                       return 1;
+               fallthrough;
+               /*
+                * Note that the MSR emulation here is flawed when a vCPU
+                * doesn't support the Intel 64 architecture. The expected
+                * architectural behavior in this case is that the upper 32
+                * bits do not exist and should always read '0'. However,
+                * because the actual hardware on which the virtual CPU is
+                * running does support Intel 64, XRSTORS/XSAVES in the
+                * guest could observe behavior that violates the
+                * architecture. Intercepting XRSTORS/XSAVES for this
+                * special case isn't deemed worthwhile.
+                */
+       case MSR_IA32_PL0_SSP ... MSR_IA32_INT_SSP_TAB:
+               if (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK))
+                       return KVM_MSR_RET_UNSUPPORTED;
+               /*
+                * MSR_IA32_INT_SSP_TAB is not present on processors that do
+                * not support Intel 64 architecture.
+                */
+               if (index == MSR_IA32_INT_SSP_TAB && !guest_cpu_cap_has(vcpu, X86_FEATURE_LM))
+                       return KVM_MSR_RET_UNSUPPORTED;
+               if (is_noncanonical_msr_address(data, vcpu))
+                       return 1;
+               /* All SSP MSRs except MSR_IA32_INT_SSP_TAB must be 4-byte aligned */
+               if (index != MSR_IA32_INT_SSP_TAB && !IS_ALIGNED(data, 4))
+                       return 1;
+               break;
        }
 
        msr.data = data;
@@ -1934,6 +1972,20 @@ static int __kvm_get_msr(struct kvm_vcpu *vcpu, u32 index, u64 *data,
                    !guest_cpu_cap_has(vcpu, X86_FEATURE_RDPID))
                        return 1;
                break;
+       case MSR_IA32_U_CET:
+       case MSR_IA32_S_CET:
+               if (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK) &&
+                   !guest_cpu_cap_has(vcpu, X86_FEATURE_IBT))
+                       return KVM_MSR_RET_UNSUPPORTED;
+               break;
+       case MSR_KVM_INTERNAL_GUEST_SSP:
+               if (!host_initiated)
+                       return 1;
+               fallthrough;
+       case MSR_IA32_PL0_SSP ... MSR_IA32_INT_SSP_TAB:
+               if (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK))
+                       return KVM_MSR_RET_UNSUPPORTED;
+               break;
        }
 
        msr.index = index;
@@ -3865,12 +3917,12 @@ static __always_inline void kvm_access_xstate_msr(struct kvm_vcpu *vcpu,
        kvm_fpu_put();
 }
 
-static __maybe_unused void kvm_set_xstate_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
+static void kvm_set_xstate_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
 {
        kvm_access_xstate_msr(vcpu, msr_info, MSR_TYPE_W);
 }
 
-static __maybe_unused void kvm_get_xstate_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
+static void kvm_get_xstate_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
 {
        kvm_access_xstate_msr(vcpu, msr_info, MSR_TYPE_R);
 }
@@ -4256,6 +4308,10 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
                vcpu->arch.guest_fpu.xfd_err = data;
                break;
 #endif
+       case MSR_IA32_U_CET:
+       case MSR_IA32_PL0_SSP ... MSR_IA32_PL3_SSP:
+               kvm_set_xstate_msr(vcpu, msr_info);
+               break;
        default:
                if (kvm_pmu_is_valid_msr(vcpu, msr))
                        return kvm_pmu_set_msr(vcpu, msr_info);
@@ -4605,6 +4661,10 @@ int kvm_get_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
                msr_info->data = vcpu->arch.guest_fpu.xfd_err;
                break;
 #endif
+       case MSR_IA32_U_CET:
+       case MSR_IA32_PL0_SSP ... MSR_IA32_PL3_SSP:
+               kvm_get_xstate_msr(vcpu, msr_info);
+               break;
        default:
                if (kvm_pmu_is_valid_msr(vcpu, msr_info->index))
                        return kvm_pmu_get_msr(vcpu, msr_info);
index a7c9c72fca938ffacc4acd7fb635ad500df450e1..076eccba0f7e0955ff816877a4fe4b2072f4ed97 100644 (file)
@@ -710,4 +710,27 @@ int ____kvm_emulate_hypercall(struct kvm_vcpu *vcpu, int cpl,
 
 int kvm_emulate_hypercall(struct kvm_vcpu *vcpu);
 
+#define CET_US_RESERVED_BITS           GENMASK(9, 6)
+#define CET_US_SHSTK_MASK_BITS         GENMASK(1, 0)
+#define CET_US_IBT_MASK_BITS           (GENMASK_ULL(5, 2) | GENMASK_ULL(63, 10))
+#define CET_US_LEGACY_BITMAP_BASE(data)        ((data) >> 12)
+
+static inline bool kvm_is_valid_u_s_cet(struct kvm_vcpu *vcpu, u64 data)
+{
+       if (data & CET_US_RESERVED_BITS)
+               return false;
+       if (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK) &&
+           (data & CET_US_SHSTK_MASK_BITS))
+               return false;
+       if (!guest_cpu_cap_has(vcpu, X86_FEATURE_IBT) &&
+           (data & CET_US_IBT_MASK_BITS))
+               return false;
+       if (!IS_ALIGNED(CET_US_LEGACY_BITMAP_BASE(data), 4))
+               return false;
+       /* IBT can be suppressed iff the TRACKER isn't WAIT_ENDBR. */
+       if ((data & CET_SUPPRESS) && (data & CET_WAIT_ENDBR))
+               return false;
+
+       return true;
+}
 #endif