From: Tom Lendacky <[email protected]>

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 <[email protected]>
Co-developed-by: Joerg Roedel <[email protected]>
Signed-off-by: Joerg Roedel <[email protected]>
---
 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


Reply via email to