summaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
authorPaolo Bonzini <pbonzini@redhat.com>2026-02-09 21:08:17 +0300
committerPaolo Bonzini <pbonzini@redhat.com>2026-02-11 20:45:12 +0300
commit9123c5f956b1fbedd63821eb528ece55ddd0e49c (patch)
treeefdc67b3e97c7df743b18d5d80182ebd8d101378 /arch
parent54f15ebfc61ee8499a97f2dbfc18b1b13fdcb524 (diff)
parent2a62345b30529e488beb6a1220577b3495933724 (diff)
downloadlinux-9123c5f956b1fbedd63821eb528ece55ddd0e49c.tar.xz
Merge tag 'kvm-x86-gmem-6.20' of https://github.com/kvm-x86/linux into HEAD
KVM guest_memfd changes for 6.20 - Remove kvm_gmem_populate()'s preparation tracking and half-baked hugepage handling, and instead rely on SNP (the only user of the tracking) to do its own tracking via the RMP. - Retroactively document and enforce (for SNP) that KVM_SEV_SNP_LAUNCH_UPDATE and KVM_TDX_INIT_MEM_REGION require the source page to be 4KiB aligned, to avoid non-trivial complexity for a non-existent usecase (and because in-place conversion simply can't support unaligned sources). - When populating guest_memfd memory, GUP the source page in common code and pass the refcounted page to the vendor callback, instead of letting vendor code do the heavy lifting. Doing so avoids a looming deadlock bug with in-place due an AB-BA conflict betwee mmap_lock and guest_memfd's filemap invalidate lock.
Diffstat (limited to 'arch')
-rw-r--r--arch/x86/kvm/svm/sev.c108
-rw-r--r--arch/x86/kvm/vmx/tdx.c16
2 files changed, 50 insertions, 74 deletions
diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c
index f9aad5c1447e..ea515cf41168 100644
--- a/arch/x86/kvm/svm/sev.c
+++ b/arch/x86/kvm/svm/sev.c
@@ -2277,66 +2277,52 @@ struct sev_gmem_populate_args {
int fw_error;
};
-static int sev_gmem_post_populate(struct kvm *kvm, gfn_t gfn_start, kvm_pfn_t pfn,
- void __user *src, int order, void *opaque)
+static int sev_gmem_post_populate(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn,
+ struct page *src_page, void *opaque)
{
struct sev_gmem_populate_args *sev_populate_args = opaque;
+ struct sev_data_snp_launch_update fw_args = {0};
struct kvm_sev_info *sev = to_kvm_sev_info(kvm);
- int n_private = 0, ret, i;
- int npages = (1 << order);
- gfn_t gfn;
+ bool assigned = false;
+ int level;
+ int ret;
- if (WARN_ON_ONCE(sev_populate_args->type != KVM_SEV_SNP_PAGE_TYPE_ZERO && !src))
+ if (WARN_ON_ONCE(sev_populate_args->type != KVM_SEV_SNP_PAGE_TYPE_ZERO && !src_page))
return -EINVAL;
- for (gfn = gfn_start, i = 0; gfn < gfn_start + npages; gfn++, i++) {
- struct sev_data_snp_launch_update fw_args = {0};
- bool assigned = false;
- int level;
-
- ret = snp_lookup_rmpentry((u64)pfn + i, &assigned, &level);
- if (ret || assigned) {
- pr_debug("%s: Failed to ensure GFN 0x%llx RMP entry is initial shared state, ret: %d assigned: %d\n",
- __func__, gfn, ret, assigned);
- ret = ret ? -EINVAL : -EEXIST;
- goto err;
- }
-
- if (src) {
- void *vaddr = kmap_local_pfn(pfn + i);
-
- if (copy_from_user(vaddr, src + i * PAGE_SIZE, PAGE_SIZE)) {
- ret = -EFAULT;
- goto err;
- }
- kunmap_local(vaddr);
- }
-
- ret = rmp_make_private(pfn + i, gfn << PAGE_SHIFT, PG_LEVEL_4K,
- sev_get_asid(kvm), true);
- if (ret)
- goto err;
+ ret = snp_lookup_rmpentry((u64)pfn, &assigned, &level);
+ if (ret || assigned) {
+ pr_debug("%s: Failed to ensure GFN 0x%llx RMP entry is initial shared state, ret: %d assigned: %d\n",
+ __func__, gfn, ret, assigned);
+ ret = ret ? -EINVAL : -EEXIST;
+ goto out;
+ }
- n_private++;
+ if (src_page) {
+ void *src_vaddr = kmap_local_page(src_page);
+ void *dst_vaddr = kmap_local_pfn(pfn);
- fw_args.gctx_paddr = __psp_pa(sev->snp_context);
- fw_args.address = __sme_set(pfn_to_hpa(pfn + i));
- fw_args.page_size = PG_LEVEL_TO_RMP(PG_LEVEL_4K);
- fw_args.page_type = sev_populate_args->type;
+ memcpy(dst_vaddr, src_vaddr, PAGE_SIZE);
- ret = __sev_issue_cmd(sev_populate_args->sev_fd, SEV_CMD_SNP_LAUNCH_UPDATE,
- &fw_args, &sev_populate_args->fw_error);
- if (ret)
- goto fw_err;
+ kunmap_local(src_vaddr);
+ kunmap_local(dst_vaddr);
}
- return 0;
+ ret = rmp_make_private(pfn, gfn << PAGE_SHIFT, PG_LEVEL_4K,
+ sev_get_asid(kvm), true);
+ if (ret)
+ goto out;
-fw_err:
+ fw_args.gctx_paddr = __psp_pa(sev->snp_context);
+ fw_args.address = __sme_set(pfn_to_hpa(pfn));
+ fw_args.page_size = PG_LEVEL_TO_RMP(PG_LEVEL_4K);
+ fw_args.page_type = sev_populate_args->type;
+
+ ret = __sev_issue_cmd(sev_populate_args->sev_fd, SEV_CMD_SNP_LAUNCH_UPDATE,
+ &fw_args, &sev_populate_args->fw_error);
/*
* If the firmware command failed handle the reclaim and cleanup of that
- * PFN specially vs. prior pages which can be cleaned up below without
- * needing to reclaim in advance.
+ * PFN before reporting an error.
*
* Additionally, when invalid CPUID function entries are detected,
* firmware writes the expected values into the page and leaves it
@@ -2346,26 +2332,22 @@ fw_err:
* information to provide information on which CPUID leaves/fields
* failed CPUID validation.
*/
- if (!snp_page_reclaim(kvm, pfn + i) &&
+ if (ret && !snp_page_reclaim(kvm, pfn) &&
sev_populate_args->type == KVM_SEV_SNP_PAGE_TYPE_CPUID &&
sev_populate_args->fw_error == SEV_RET_INVALID_PARAM) {
- void *vaddr = kmap_local_pfn(pfn + i);
+ void *src_vaddr = kmap_local_page(src_page);
+ void *dst_vaddr = kmap_local_pfn(pfn);
- if (copy_to_user(src + i * PAGE_SIZE, vaddr, PAGE_SIZE))
- pr_debug("Failed to write CPUID page back to userspace\n");
+ memcpy(src_vaddr, dst_vaddr, PAGE_SIZE);
- kunmap_local(vaddr);
+ kunmap_local(src_vaddr);
+ kunmap_local(dst_vaddr);
}
- /* pfn + i is hypervisor-owned now, so skip below cleanup for it. */
- n_private--;
-
-err:
- pr_debug("%s: exiting with error ret %d (fw_error %d), restoring %d gmem PFNs to shared.\n",
- __func__, ret, sev_populate_args->fw_error, n_private);
- for (i = 0; i < n_private; i++)
- kvm_rmp_make_shared(kvm, pfn + i, PG_LEVEL_4K);
-
+out:
+ if (ret)
+ pr_debug("%s: error updating GFN %llx, return code %d (fw_error %d)\n",
+ __func__, gfn, ret, sev_populate_args->fw_error);
return ret;
}
@@ -2396,6 +2378,11 @@ static int snp_launch_update(struct kvm *kvm, struct kvm_sev_cmd *argp)
params.type != KVM_SEV_SNP_PAGE_TYPE_CPUID))
return -EINVAL;
+ src = params.type == KVM_SEV_SNP_PAGE_TYPE_ZERO ? NULL : u64_to_user_ptr(params.uaddr);
+
+ if (!PAGE_ALIGNED(src))
+ return -EINVAL;
+
npages = params.len / PAGE_SIZE;
/*
@@ -2427,7 +2414,6 @@ static int snp_launch_update(struct kvm *kvm, struct kvm_sev_cmd *argp)
sev_populate_args.sev_fd = argp->sev_fd;
sev_populate_args.type = params.type;
- src = params.type == KVM_SEV_SNP_PAGE_TYPE_ZERO ? NULL : u64_to_user_ptr(params.uaddr);
count = kvm_gmem_populate(kvm, params.gfn_start, src, npages,
sev_gmem_post_populate, &sev_populate_args);
diff --git a/arch/x86/kvm/vmx/tdx.c b/arch/x86/kvm/vmx/tdx.c
index 2d7a4d52ccfb..5df9d32d2058 100644
--- a/arch/x86/kvm/vmx/tdx.c
+++ b/arch/x86/kvm/vmx/tdx.c
@@ -3118,34 +3118,24 @@ struct tdx_gmem_post_populate_arg {
};
static int tdx_gmem_post_populate(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn,
- void __user *src, int order, void *_arg)
+ struct page *src_page, void *_arg)
{
struct tdx_gmem_post_populate_arg *arg = _arg;
struct kvm_tdx *kvm_tdx = to_kvm_tdx(kvm);
u64 err, entry, level_state;
gpa_t gpa = gfn_to_gpa(gfn);
- struct page *src_page;
int ret, i;
if (KVM_BUG_ON(kvm_tdx->page_add_src, kvm))
return -EIO;
- /*
- * Get the source page if it has been faulted in. Return failure if the
- * source page has been swapped out or unmapped in primary memory.
- */
- ret = get_user_pages_fast((unsigned long)src, 1, 0, &src_page);
- if (ret < 0)
- return ret;
- if (ret != 1)
- return -ENOMEM;
+ if (!src_page)
+ return -EOPNOTSUPP;
kvm_tdx->page_add_src = src_page;
ret = kvm_tdp_mmu_map_private_pfn(arg->vcpu, gfn, pfn);
kvm_tdx->page_add_src = NULL;
- put_page(src_page);
-
if (ret || !(arg->flags & KVM_TDX_MEASURE_MEMORY_REGION))
return ret;