From: "Xin Li (Intel)" <[email protected]>

Introduce support for handling FRED MSR access requests, enabling both
host and guest to read and write FRED MSRs, which is essential for VM
save/restore and live migration, and allows userspace tools such as QEMU
to access the relevant MSRs.

Specially, intercept accesses to the FRED SSP0 MSR (IA32_PL0_SSP), which
remains accessible when FRED is enumerated even if CET is not.  This
ensures the guest value is fully virtual and does not alter the hardware
FRED SSP0 MSR.

Signed-off-by: Xin Li (Intel) <[email protected]>
Signed-off-by: Sohil Mehta <[email protected]>
Reviewed-by: Chao Gao <[email protected]>
---
v10:
 - Fix a corner case identified by Chao Gao.
---
 arch/x86/include/asm/kvm_host.h  |  5 ++
 arch/x86/include/asm/msr-index.h |  2 +
 arch/x86/kvm/msrs.c              | 89 ++++++++++++++++++++++++++++++--
 arch/x86/kvm/vmx/vmx.c           | 45 ++++++++++++++++
 4 files changed, 138 insertions(+), 3 deletions(-)

diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 8d3999772ac2..0fd5d4edeebb 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -1032,6 +1032,11 @@ struct kvm_vcpu_arch {
 #if IS_ENABLED(CONFIG_HYPERV)
        hpa_t hv_root_tdp;
 #endif
+       /*
+        * Stores the FRED SSP0 MSR when CET is not supported, prompting KVM
+        * to intercept its accesses.
+        */
+       u64 fred_ssp0_fallback;
 };
 
 struct kvm_lpage_info {
diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h
index ad899d9070e1..b386709515c4 100644
--- a/arch/x86/include/asm/msr-index.h
+++ b/arch/x86/include/asm/msr-index.h
@@ -64,6 +64,8 @@
 #define MSR_IA32_FRED_SSP3     0x1d3                   /* Level 3 shadow stack 
pointer */
 #define MSR_IA32_FRED_CONFIG   0x1d4                   /* Entrypoint and 
interrupt stack level */
 
+#define FRED_CONFIG_RESERVED   (BIT_ULL(2) | GENMASK_ULL(5, 4) | BIT_ULL(11))
+
 /* Intel MSRs. Some also available on other CPUs */
 #define MSR_TEST_CTRL                          0x00000033
 #define MSR_TEST_CTRL_SPLIT_LOCK_DETECT_BIT    29
diff --git a/arch/x86/kvm/msrs.c b/arch/x86/kvm/msrs.c
index 66fa7140d65d..9eabb7b02cb5 100644
--- a/arch/x86/kvm/msrs.c
+++ b/arch/x86/kvm/msrs.c
@@ -193,6 +193,9 @@ static const u32 msrs_to_save_base[] = {
        MSR_STAR,
 #ifdef CONFIG_X86_64
        MSR_CSTAR, MSR_KERNEL_GS_BASE, MSR_SYSCALL_MASK, MSR_LSTAR,
+       MSR_IA32_FRED_RSP0, MSR_IA32_FRED_RSP1, MSR_IA32_FRED_RSP2,
+       MSR_IA32_FRED_RSP3, MSR_IA32_FRED_STKLVLS, MSR_IA32_FRED_SSP1,
+       MSR_IA32_FRED_SSP2, MSR_IA32_FRED_SSP3, MSR_IA32_FRED_CONFIG,
 #endif
        MSR_IA32_TSC, MSR_IA32_CR_PAT, MSR_VM_HSAVE_PA,
        MSR_IA32_FEAT_CTL, MSR_IA32_BNDCFGS, MSR_TSC_AUX,
@@ -772,7 +775,7 @@ static int __kvm_set_msr(struct kvm_vcpu *vcpu, u32 index, 
u64 data,
                 * architecture. Intercepting XRSTORS/XSAVES for this
                 * special case isn't deemed worthwhile.
                 */
-       case MSR_IA32_PL0_SSP ... MSR_IA32_INT_SSP_TAB:
+       case MSR_IA32_PL1_SSP ... MSR_IA32_INT_SSP_TAB:
                if (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK))
                        return KVM_MSR_RET_UNSUPPORTED;
                /*
@@ -787,6 +790,52 @@ static int __kvm_set_msr(struct kvm_vcpu *vcpu, u32 index, 
u64 data,
                if (index != MSR_IA32_INT_SSP_TAB && !IS_ALIGNED(data, 4))
                        return 1;
                break;
+       case MSR_IA32_FRED_STKLVLS:
+               if (!guest_cpu_cap_has(vcpu, X86_FEATURE_FRED))
+                       return KVM_MSR_RET_UNSUPPORTED;
+               break;
+       case MSR_IA32_FRED_RSP0 ... MSR_IA32_FRED_RSP3:
+       case MSR_IA32_FRED_SSP1 ... MSR_IA32_FRED_CONFIG: {
+               u64 reserved_bits = 0;
+
+               if (!guest_cpu_cap_has(vcpu, X86_FEATURE_FRED))
+                       return KVM_MSR_RET_UNSUPPORTED;
+
+               if (is_noncanonical_msr_address(data, vcpu))
+                       return 1;
+
+               switch (index) {
+               case MSR_IA32_FRED_CONFIG:
+                       reserved_bits = FRED_CONFIG_RESERVED;
+                       break;
+               case MSR_IA32_FRED_RSP0 ... MSR_IA32_FRED_RSP3:
+                       reserved_bits = GENMASK_ULL(5, 0);
+                       break;
+               case MSR_IA32_FRED_SSP1 ... MSR_IA32_FRED_SSP3:
+                       reserved_bits = GENMASK_ULL(2, 0);
+                       break;
+               default:
+                       WARN_ON_ONCE(1);
+                       return 1;
+               }
+
+               if (data & reserved_bits)
+                       return 1;
+
+               break;
+       }
+       case MSR_IA32_PL0_SSP: /* I.e., MSR_IA32_FRED_SSP0 */
+               if (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK) &&
+                   !guest_cpu_cap_has(vcpu, X86_FEATURE_FRED))
+                       return KVM_MSR_RET_UNSUPPORTED;
+
+               if (is_noncanonical_msr_address(data, vcpu))
+                       return 1;
+
+               if (!IS_ALIGNED(data, 4))
+                       return 1;
+
+               break;
        }
 
        msr.data = data;
@@ -841,10 +890,19 @@ static int __kvm_get_msr(struct kvm_vcpu *vcpu, u32 
index, u64 *data,
                if (!host_initiated)
                        return 1;
                fallthrough;
-       case MSR_IA32_PL0_SSP ... MSR_IA32_INT_SSP_TAB:
+       case MSR_IA32_PL1_SSP ... MSR_IA32_INT_SSP_TAB:
                if (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK))
                        return KVM_MSR_RET_UNSUPPORTED;
                break;
+       case MSR_IA32_FRED_RSP0 ... MSR_IA32_FRED_CONFIG:
+               if (!guest_cpu_cap_has(vcpu, X86_FEATURE_FRED))
+                       return KVM_MSR_RET_UNSUPPORTED;
+               break;
+       case MSR_IA32_PL0_SSP: /* I.e., MSR_IA32_FRED_SSP0 */
+               if (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK) &&
+                   !guest_cpu_cap_has(vcpu, X86_FEATURE_FRED))
+                       return KVM_MSR_RET_UNSUPPORTED;
+               break;
        }
 
        msr.index = index;
@@ -1833,7 +1891,15 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct 
msr_data *msr_info)
                break;
 #endif
        case MSR_IA32_U_CET:
+               kvm_set_xstate_msr(vcpu, msr_info);
+               break;
        case MSR_IA32_PL0_SSP ... MSR_IA32_PL3_SSP:
+               if (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK)) {
+                       WARN_ON_ONCE(msr != MSR_IA32_FRED_SSP0);
+                       vcpu->arch.fred_ssp0_fallback = data;
+                       break;
+               }
+
                kvm_set_xstate_msr(vcpu, msr_info);
                break;
        default:
@@ -2186,7 +2252,15 @@ int kvm_get_msr_common(struct kvm_vcpu *vcpu, struct 
msr_data *msr_info)
                break;
 #endif
        case MSR_IA32_U_CET:
+               kvm_get_xstate_msr(vcpu, msr_info);
+               break;
        case MSR_IA32_PL0_SSP ... MSR_IA32_PL3_SSP:
+               if (!guest_cpu_cap_has(vcpu, X86_FEATURE_SHSTK)) {
+                       WARN_ON_ONCE(msr_info->index != MSR_IA32_FRED_SSP0);
+                       msr_info->data = vcpu->arch.fred_ssp0_fallback;
+                       break;
+               }
+
                kvm_get_xstate_msr(vcpu, msr_info);
                break;
        default:
@@ -2656,10 +2730,19 @@ static void kvm_probe_msr_to_save(u32 msr_index)
                if (!kvm_cpu_cap_has(X86_FEATURE_LM))
                        return;
                fallthrough;
-       case MSR_IA32_PL0_SSP ... MSR_IA32_PL3_SSP:
+       case MSR_IA32_PL1_SSP ... MSR_IA32_PL3_SSP:
                if (!kvm_cpu_cap_has(X86_FEATURE_SHSTK))
                        return;
                break;
+       case MSR_IA32_FRED_RSP0 ... MSR_IA32_FRED_CONFIG:
+               if (!kvm_cpu_cap_has(X86_FEATURE_FRED))
+                       return;
+               break;
+       case MSR_IA32_PL0_SSP: /* I.e., MSR_IA32_FRED_SSP0 */
+               if (!kvm_cpu_cap_has(X86_FEATURE_SHSTK) &&
+                   !kvm_cpu_cap_has(X86_FEATURE_FRED))
+                       return;
+               break;
        default:
                break;
        }
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index b6b73ab9cfb7..362bd4dda298 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -1489,6 +1489,18 @@ static void vmx_write_guest_kernel_gs_base(struct 
vcpu_vmx *vmx, u64 data)
        vmx_write_guest_host_msr(vmx, MSR_KERNEL_GS_BASE, data,
                                 &vmx->msr_guest_kernel_gs_base);
 }
+
+static u64 vmx_read_guest_fred_rsp0(struct vcpu_vmx *vmx)
+{
+       return vmx_read_guest_host_msr(vmx, MSR_IA32_FRED_RSP0,
+                                      &vmx->msr_guest_fred_rsp0);
+}
+
+static void vmx_write_guest_fred_rsp0(struct vcpu_vmx *vmx, u64 data)
+{
+       vmx_write_guest_host_msr(vmx, MSR_IA32_FRED_RSP0, data,
+                                &vmx->msr_guest_fred_rsp0);
+}
 #endif
 
 static void grow_ple_window(struct kvm_vcpu *vcpu)
@@ -2140,6 +2152,27 @@ int vmx_get_feature_msr(u32 msr, u64 *data)
        }
 }
 
+#ifdef CONFIG_X86_64
+static const u32 fred_msr_vmcs_fields[] = {
+       GUEST_IA32_FRED_RSP1,
+       GUEST_IA32_FRED_RSP2,
+       GUEST_IA32_FRED_RSP3,
+       GUEST_IA32_FRED_STKLVLS,
+       GUEST_IA32_FRED_SSP1,
+       GUEST_IA32_FRED_SSP2,
+       GUEST_IA32_FRED_SSP3,
+       GUEST_IA32_FRED_CONFIG,
+};
+
+static_assert(MSR_IA32_FRED_CONFIG - MSR_IA32_FRED_RSP1 ==
+             ARRAY_SIZE(fred_msr_vmcs_fields) - 1);
+
+static u32 fred_msr_to_vmcs(u32 msr)
+{
+       return fred_msr_vmcs_fields[msr - MSR_IA32_FRED_RSP1];
+}
+#endif
+
 /*
  * Reads an msr value (of 'msr_info->index') into 'msr_info->data'.
  * Returns 0 on success, non-0 otherwise.
@@ -2162,6 +2195,12 @@ int vmx_get_msr(struct kvm_vcpu *vcpu, struct msr_data 
*msr_info)
        case MSR_KERNEL_GS_BASE:
                msr_info->data = vmx_read_guest_kernel_gs_base(vmx);
                break;
+       case MSR_IA32_FRED_RSP0:
+               msr_info->data = vmx_read_guest_fred_rsp0(vmx);
+               break;
+       case MSR_IA32_FRED_RSP1 ... MSR_IA32_FRED_CONFIG:
+               msr_info->data = vmcs_read64(fred_msr_to_vmcs(msr_info->index));
+               break;
 #endif
        case MSR_EFER:
                return kvm_get_msr_common(vcpu, msr_info);
@@ -2394,6 +2433,12 @@ int vmx_set_msr(struct kvm_vcpu *vcpu, struct msr_data 
*msr_info)
                        vmx_update_exception_bitmap(vcpu);
                }
                break;
+       case MSR_IA32_FRED_RSP0:
+               vmx_write_guest_fred_rsp0(vmx, data);
+               break;
+       case MSR_IA32_FRED_RSP1 ... MSR_IA32_FRED_CONFIG:
+               vmcs_write64(fred_msr_to_vmcs(msr_index), data);
+               break;
 #endif
        case MSR_IA32_SYSENTER_CS:
                if (is_guest_mode(vcpu))
-- 
2.43.0


Reply via email to