[PATCH 58/60] kvm: svm: Invoke a specified VMPL level VMSA for the vCPU
From: Jörg Rödel
Date: Mon Jun 08 2026 - 11:23:31 EST
From: Tom Lendacky <thomas.lendacky@xxxxxxx>
Implement the SNP Run VMPL NAE event and MSR protocol to allow a guest to
request a different VMPL level VMSA be run for the vCPU. This allows the
guest to "call" an SVSM to process an SVSM request.
Signed-off-by: Tom Lendacky <thomas.lendacky@xxxxxxx>
Co-developed-by: Joerg Roedel <joerg.roedel@xxxxxxx>
Signed-off-by: Joerg Roedel <joerg.roedel@xxxxxxx>
---
arch/x86/include/asm/sev-common.h | 6 +++
arch/x86/kvm/svm/sev.c | 71 +++++++++++++++++++++++++++++++
2 files changed, 77 insertions(+)
diff --git a/arch/x86/include/asm/sev-common.h b/arch/x86/include/asm/sev-common.h
index cedb7ea91da5..a09cf5690aba 100644
--- a/arch/x86/include/asm/sev-common.h
+++ b/arch/x86/include/asm/sev-common.h
@@ -114,6 +114,8 @@ enum psc_op {
/* GHCB Run at VMPL Request/Response */
#define GHCB_MSR_VMPL_REQ 0x016
+#define GHCB_MSR_VMPL_LEVEL_POS 32
+#define GHCB_MSR_VMPL_LEVEL_MASK GENMASK_ULL(7, 0)
#define GHCB_MSR_VMPL_REQ_LEVEL(v) \
/* GHCBData[39:32] */ \
((((u64)(v) & GENMASK_ULL(7, 0)) << 32) | \
@@ -121,6 +123,10 @@ enum psc_op {
GHCB_MSR_VMPL_REQ)
#define GHCB_MSR_VMPL_RESP 0x017
+#define GHCB_MSR_VMPL_ERROR_POS 32
+#define GHCB_MSR_VMPL_ERROR_MASK GENMASK_ULL(31, 0)
+#define GHCB_MSR_VMPL_RSVD_POS 12
+#define GHCB_MSR_VMPL_RSVD_MASK GENMASK_ULL(19, 0)
#define GHCB_MSR_VMPL_RESP_VAL(v) \
/* GHCBData[63:32] */ \
(((u64)(v) & GENMASK_ULL(63, 32)) >> 32)
diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index 53cd3aba7368..b67566fcb69e 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -3556,6 +3556,10 @@ static int sev_es_validate_vmgexit(struct vcpu_svm *svm)
if (!sev_snp_guest(vcpu->kvm))
goto vmgexit_err;
break;
+ case SVM_VMGEXIT_SNP_RUN_VMPL:
+ if (!sev_snp_guest(vcpu->kvm))
+ goto vmgexit_err;
+ break;
default:
reason = GHCB_ERR_INVALID_EVENT;
goto vmgexit_err;
@@ -4593,6 +4597,45 @@ static void sev_get_apic_ids(struct vcpu_svm *svm)
kvfree(desc);
}
+static int __sev_snp_run_vmpl(struct vcpu_svm *svm, unsigned int vmpl)
+{
+ struct kvm_vcpu *vcpu = &svm->vcpu;
+ struct kvm_vcpu *target = vcpu->common->vcpus[vmpl];
+ struct vcpu_svm *target_svm = to_svm(target);
+
+ if (!target)
+ return -EINVAL;
+
+ /* Mark current plane as stopped so it is not selected */
+ kvm_set_mp_state(target, KVM_MP_STATE_RUNNABLE);
+ /* In case KVM_REQ_UPDATE_PROTECTED_GUEST_STATE is set - mark the new VMSA as runnable */
+ target_svm->sev_es.snp_ap_runnable = true;
+ kvm_vcpu_set_plane_runnable(target);
+ kvm_vcpu_set_plane_stopped(vcpu);
+
+ kvm_make_request(KVM_REQ_PLANE_RESCHED, vcpu);
+
+ return 1;
+}
+
+static int sev_snp_run_vmpl(struct vcpu_svm *svm)
+{
+ struct ghcb *ghcb = svm->sev_es.ghcb;
+ struct kvm_vcpu *vcpu = &svm->vcpu;
+ unsigned int vmpl;
+
+ vmpl = lower_32_bits(svm->vmcb->control.exit_info_1);
+ if (vmpl >= SVM_SEV_VMPL_MAX) {
+ vcpu_unimpl(vcpu, "vmgexit: invalid VMPL level [%u] from guest\n", vmpl);
+ return -EINVAL;
+ }
+
+ ghcb_set_sw_exit_info_1(ghcb, 0);
+ ghcb_set_sw_exit_info_2(ghcb, 0);
+
+ return __sev_snp_run_vmpl(svm, vmpl);
+}
+
static int sev_handle_vmgexit_msr_protocol(struct vcpu_svm *svm)
{
struct vmcb_control_area *control = &svm->vmcb->control;
@@ -4704,6 +4747,27 @@ static int sev_handle_vmgexit_msr_protocol(struct vcpu_svm *svm)
ret = snp_begin_psc_msr(svm, control->ghcb_gpa);
break;
+ case GHCB_MSR_VMPL_REQ: {
+ unsigned int vmpl;
+
+ if (!sev_snp_guest(vcpu->kvm))
+ goto out_terminate;
+
+ vmpl = get_ghcb_msr_bits(svm, GHCB_MSR_VMPL_LEVEL_MASK, GHCB_MSR_VMPL_LEVEL_POS);
+
+ set_ghcb_msr_bits(svm, 0, GHCB_MSR_VMPL_ERROR_MASK, GHCB_MSR_VMPL_ERROR_POS);
+ set_ghcb_msr_bits(svm, 0, GHCB_MSR_VMPL_RSVD_MASK, GHCB_MSR_VMPL_RSVD_POS);
+ set_ghcb_msr_bits(svm, GHCB_MSR_VMPL_RESP, GHCB_MSR_INFO_MASK, GHCB_MSR_INFO_POS);
+
+ if (vmpl >= SVM_SEV_VMPL_MAX) {
+ vcpu_unimpl(vcpu, "vmgexit: invalid VMPL level [%u] from guest\n", vmpl);
+ set_ghcb_msr_bits(svm, 1, GHCB_MSR_VMPL_ERROR_MASK, GHCB_MSR_VMPL_ERROR_POS);
+ break;
+ }
+
+ ret = __sev_snp_run_vmpl(svm, vmpl);
+ break;
+ }
case GHCB_MSR_TERM_REQ: {
u64 reason_set, reason_code;
@@ -4887,6 +4951,13 @@ int sev_handle_vmgexit(struct kvm_vcpu *vcpu)
sev_get_apic_ids(svm);
ret = 1;
break;
+ case SVM_VMGEXIT_SNP_RUN_VMPL:
+ ret = sev_snp_run_vmpl(svm);
+ if (ret < 0) {
+ svm_vmgexit_bad_input(svm, GHCB_ERR_INVALID_INPUT);
+ ret = 1;
+ }
+ break;
case SVM_VMGEXIT_UNSUPPORTED_EVENT:
vcpu_unimpl(vcpu,
"vmgexit: unsupported event - exit_info_1=%#llx, exit_info_2=%#llx\n",
--
2.53.0