From: Joerg Roedel <[email protected]>

Pass the firmware VMSA page type through KVM_SEV_SNP_LAUNCH_UPDATE.
Only accept VMSA pages when userspace has enabled
KVM_CAP_SNP_DIRECT_VMSA.
Require each request to describe exactly one 4-KiB VMSA page.  Allow
repeated requests and keep VMSA creation independent of association with a
vCPU.

The VMSA's VMPL and SEV features define its execution context.  They must
agree with KVM's VM-wide configuration.  Before passing a VMSA to firmware,
require VMPL 0.  Require sev_features to exactly match the VM's configured
VMSA features.  Treat the remaining contents as guest-owned data.

Assisted-by: LLM
Signed-off-by: Joerg Roedel <[email protected]>
---
 arch/x86/include/uapi/asm/kvm.h |  1 +
 arch/x86/kvm/svm/sev.c          | 16 ++++++++++++++++
 2 files changed, 17 insertions(+)

diff --git a/arch/x86/include/uapi/asm/kvm.h b/arch/x86/include/uapi/asm/kvm.h
index 1585ec804066..69dcd044583f 100644
--- a/arch/x86/include/uapi/asm/kvm.h
+++ b/arch/x86/include/uapi/asm/kvm.h
@@ -887,6 +887,7 @@ struct kvm_sev_snp_launch_start {
 /* Kept in sync with firmware values for simplicity. */
 #define KVM_SEV_PAGE_TYPE_INVALID              0x0
 #define KVM_SEV_SNP_PAGE_TYPE_NORMAL           0x1
+#define KVM_SEV_SNP_PAGE_TYPE_VMSA             0x2
 #define KVM_SEV_SNP_PAGE_TYPE_ZERO             0x3
 #define KVM_SEV_SNP_PAGE_TYPE_UNMEASURED       0x4
 #define KVM_SEV_SNP_PAGE_TYPE_SECRETS          0x5
diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index 5a282aff04a8..7a9ef1bc54e9 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -2346,6 +2346,7 @@ struct sev_gmem_populate_args {
        __u8 type;
        int sev_fd;
        int fw_error;
+       bool vmsa_invalid;
 };
 
 static int sev_gmem_post_populate(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn,
@@ -2369,11 +2370,20 @@ static int sev_gmem_post_populate(struct kvm *kvm, 
gfn_t gfn, kvm_pfn_t pfn,
        if (src_page) {
                void *src_vaddr = kmap_local_page(src_page);
                void *dst_vaddr = kmap_local_pfn(pfn);
+               struct sev_es_save_area *vmsa = dst_vaddr;
 
                memcpy(dst_vaddr, src_vaddr, PAGE_SIZE);
+               if (sev_populate_args->type == KVM_SEV_SNP_PAGE_TYPE_VMSA &&
+                   (vmsa->vmpl || vmsa->sev_features != sev->vmsa_features)) {
+                       sev_populate_args->vmsa_invalid = true;
+                       ret = -EINVAL;
+               }
 
                kunmap_local(dst_vaddr);
                kunmap_local(src_vaddr);
+
+               if (ret)
+                       goto out;
        }
 
        ret = rmp_make_private(pfn, gfn << PAGE_SHIFT, PG_LEVEL_4K,
@@ -2439,7 +2449,10 @@ static int snp_launch_update(struct kvm *kvm, struct 
kvm_sev_cmd *argp)
                 params.gfn_start, params.len, params.type, params.flags);
 
        if (!params.len || !PAGE_ALIGNED(params.len) || params.flags ||
+           (params.type == KVM_SEV_SNP_PAGE_TYPE_VMSA &&
+            (!sev->snp_direct_vmsa || params.len != PAGE_SIZE)) ||
            (params.type != KVM_SEV_SNP_PAGE_TYPE_NORMAL &&
+            params.type != KVM_SEV_SNP_PAGE_TYPE_VMSA &&
             params.type != KVM_SEV_SNP_PAGE_TYPE_ZERO &&
             params.type != KVM_SEV_SNP_PAGE_TYPE_UNMEASURED &&
             params.type != KVM_SEV_SNP_PAGE_TYPE_SECRETS &&
@@ -2487,6 +2500,9 @@ static int snp_launch_update(struct kvm *kvm, struct 
kvm_sev_cmd *argp)
                                  params.type == KVM_SEV_SNP_PAGE_TYPE_CPUID,
                                  sev_gmem_post_populate, &sev_populate_args);
        if (count < 0) {
+               if (sev_populate_args.vmsa_invalid)
+                       return -EINVAL;
+
                argp->error = sev_populate_args.fw_error;
                pr_debug("%s: kvm_gmem_populate failed, ret %ld (fw_error 
%d)\n",
                         __func__, count, argp->error);
-- 
2.53.0


Reply via email to