From: Joerg Roedel 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 --- 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