mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: David Woodhouse <dwmw2@infradead.org>
To: Sean Christopherson <seanjc@google.com>,
	Paolo Bonzini <pbonzini@redhat.com>,
	kvm@vger.kernel.org
Cc: linux-kernel@vger.kernel.org, iommu@lists.linux.dev,
	linux-kselftest@vger.kernel.org, linux-media@vger.kernel.org,
	dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org,
	"Thomas Gleixner" <tglx@kernel.org>,
	"Ingo Molnar" <mingo@redhat.com>,
	"Borislav Petkov" <bp@alien8.de>,
	"Dave Hansen" <dave.hansen@linux.intel.com>,
	"H . Peter Anvin" <hpa@zytor.com>,
	x86@kernel.org, "Jason Gunthorpe" <jgg@ziepe.ca>,
	"Kevin Tian" <kevin.tian@intel.com>,
	"Joerg Roedel" <joro@8bytes.org>, "Will Deacon" <will@kernel.org>,
	"Robin Murphy" <robin.murphy@arm.com>,
	"Shuah Khan" <shuah@kernel.org>,
	"Sumit Semwal" <sumit.semwal@linaro.org>,
	"Christian König" <christian.koenig@amd.com>,
	"Tom Lendacky" <thomas.lendacky@amd.com>,
	"Tycho Andersen" <tycho@kernel.org>,
	"Kees Cook" <kees@kernel.org>,
	"Ashish Kalra" <ashish.kalra@amd.com>,
	"Connor Williamson" <connordw@amazon.com>,
	"Andrew Morton" <akpm@linux-foundation.org>,
	"Zhou Yuhang" <zhouyuhang@kylinos.cn>, "Zi Li" <zi.li@linux.dev>,
	"Dan Williams" <djbw@kernel.org>,
	"Eric Biggers" <ebiggers@kernel.org>,
	"Colin Ian King" <colin.i.king@gmail.com>,
	connordw@amazon.co.uk, graf@amazon.de, fgriffo@amazon.co.uk,
	"David Woodhouse" <dwmw@amazon.co.uk>
Subject: [RFC PATCH v2 09/11] selftests/kvm: gmem_provider KVM-only tests
Date: Mon, 20 Jul 2026 12:03:10 +0100	[thread overview]
Message-ID: <45bcdb0af20bcb929ea818b59b4539daf1610f9d.1784545391.git.dwmw@amazon.co.uk> (raw)
In-Reply-To: <2b5e7a83d277134294199a455469bb436196b902.1784545391.git.dwmw@amazon.co.uk>

From: David Woodhouse <dwmw@amazon.co.uk>

Three selftests that exercise the guest_memfd provider ABI purely from
the KVM side (no iommufd, no assigned devices):

  gmem_provider_test
    SEV-SNP launch + re-bind against a page-less provider region.
    Loads a 16 MiB guest at physical addr= from the module parameters,
    runs SNP_LAUNCH_START -> SNP_LAUNCH_UPDATE -> SNP_LAUNCH_FINISH ->
    KVM_CREATE_VCPU, tears the VM down, and re-binds the same provider
    fd to a fresh SNP VM.  Proves the LU-survivable path (fd persists,
    KVM refs transfer, RMP transitions clean).

  gmem_provider_hugepage_test
    Non-CoCo (SW_PROTECTED_VM) guest whose slot is backed by a
    mmap-capable provider fd.  Verifies via KVM_GET_STATS that the NPT
    for a 1 GiB, 1G-aligned slot contains at least one 1 GiB and one
    2 MiB mapping.  Exercises kvm_gmem_max_mapping_level and the
    mmap-capable = gmem-only path.

  gmem_provider_revoke_test
    GMEM_PROVIDER_SET_PRESENT round trip.  Reads a page from the guest
    (populates NPT), revokes the range, re-enters the guest to observe
    the fault (memory attribute goes absent, vcpu_run fails EFAULT),
    restores and re-enters, confirms the guest reads the original
    value.  Exercises the provider-driven kvm_gmem_provider_invalidate
    helper end-to-end.

All three assume the sample provider is loaded with an addr=/len=
page-less region; hugepage and revoke additionally require the fd to
be set up with GMEM_PROVIDER_FLAG_MMAP_CAPABLE.

Signed-off-by: David Woodhouse (Kiro) <dwmw@amazon.co.uk>
---
 tools/testing/selftests/kvm/Makefile.kvm      |   3 +
 .../kvm/x86/gmem_provider_hugepage_test.c     | 130 ++++++++++++
 .../kvm/x86/gmem_provider_revoke_test.c       | 135 ++++++++++++
 .../selftests/kvm/x86/gmem_provider_test.c    | 195 ++++++++++++++++++
 4 files changed, 463 insertions(+)
 create mode 100644 tools/testing/selftests/kvm/x86/gmem_provider_hugepage_test.c
 create mode 100644 tools/testing/selftests/kvm/x86/gmem_provider_revoke_test.c
 create mode 100644 tools/testing/selftests/kvm/x86/gmem_provider_test.c

diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm
index d28a057fa6c2..e6e9ac9da45a 100644
--- a/tools/testing/selftests/kvm/Makefile.kvm
+++ b/tools/testing/selftests/kvm/Makefile.kvm
@@ -161,6 +161,9 @@ TEST_GEN_PROGS_x86 += rseq_test
 TEST_GEN_PROGS_x86 += steal_time
 TEST_GEN_PROGS_x86 += system_counter_offset_test
 TEST_GEN_PROGS_x86 += pre_fault_memory_test
+TEST_GEN_PROGS_x86 += x86/gmem_provider_test
+TEST_GEN_PROGS_x86 += x86/gmem_provider_hugepage_test
+TEST_GEN_PROGS_x86 += x86/gmem_provider_revoke_test
 
 # Compiled outputs used by test targets
 TEST_GEN_PROGS_EXTENDED_x86 += x86/nx_huge_pages_test
diff --git a/tools/testing/selftests/kvm/x86/gmem_provider_hugepage_test.c b/tools/testing/selftests/kvm/x86/gmem_provider_hugepage_test.c
new file mode 100644
index 000000000000..fbbef9761e64
--- /dev/null
+++ b/tools/testing/selftests/kvm/x86/gmem_provider_hugepage_test.c
@@ -0,0 +1,130 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * gmem_provider_hugepage_test - verify KVM installs 2M and 1G NPT/EPT mappings
+ * for a non-confidential guest backed entirely by the samples/kvm gmem_provider
+ * in its gmem-only (mmap) mode.
+ *
+ * The test opens the provider with GMEM_PROVIDER_FLAG_MMAP_CAPABLE at SETUP time.
+ * Load the module with a >=1G, 1G-aligned page-less range:
+ *   insmod gmem_provider.ko addr=0x5D40000000 len=0x42000000
+ *
+ * The provider fd is mmap-capable, so its slot is gmem-only: all guest faults
+ * route to the provider (no private attributes needed) and the mapping level
+ * comes from the provider's max_order. The slot's userspace_addr is a
+ * 1G-aligned mmap of the provider, so the lpage HVA-alignment check passes and
+ * 1G is permitted. Skips if the module or SW_PROTECTED VM support is absent.
+ */
+#include <fcntl.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+
+#include <linux/sizes.h>
+
+#include "test_util.h"
+#include "kvm_util.h"
+#include "processor.h"
+
+/* Mirrors samples/kvm/gmem_provider.h */
+struct gmem_provider_setup {
+	__s32 kvm_fd;
+	__u32 flags;
+	__u64 size;
+};
+#define GMEM_PROVIDER_SETUP _IOW('G', 1, struct gmem_provider_setup)
+#define GMEM_PROVIDER_FLAG_MMAP_CAPABLE (1u << 0)
+
+#define DATA_SLOT	10
+#define DATA_GPA	(1ULL << 32)		/* 4G: 1G-aligned */
+#define DATA_SIZE	((uint64_t)SZ_1G + SZ_2M)
+
+static void guest_code(void)
+{
+	*(volatile uint64_t *)DATA_GPA = 0xdead;		/* -> 1G mapping */
+	*(volatile uint64_t *)(DATA_GPA + SZ_1G) = 0xbeef;	/* -> 2M mapping */
+	GUEST_DONE();
+}
+
+int main(void)
+{
+	struct vm_shape shape = {
+		.mode = VM_MODE_DEFAULT,
+		.type = KVM_X86_SW_PROTECTED_VM,
+	};
+	struct gmem_provider_setup setup = { .flags = GMEM_PROVIDER_FLAG_MMAP_CAPABLE };
+	struct kvm_vcpu *vcpu;
+	struct kvm_vm *vm;
+	struct ucall uc;
+	int gmem_ctl, gmem_fd, r;
+	void *resv, *hva;
+	uint64_t p4k, p2m, p1g;
+
+	TEST_REQUIRE(kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SW_PROTECTED_VM));
+
+	gmem_ctl = open("/dev/gmem_provider", O_RDWR);
+	__TEST_REQUIRE(gmem_ctl >= 0,
+		       "gmem_provider module not loaded (/dev/gmem_provider absent)");
+
+	vm = vm_create_shape_with_one_vcpu(shape, &vcpu, guest_code);
+
+	setup.kvm_fd = vm->fd;
+	setup.size = DATA_SIZE;
+	gmem_fd = ioctl(gmem_ctl, GMEM_PROVIDER_SETUP, &setup);
+	TEST_ASSERT(gmem_fd >= 0, "GMEM_PROVIDER_SETUP failed, errno %d", errno);
+
+	/*
+	 * Map the provider at a 1G-aligned host VA so the slot's userspace_addr
+	 * is 1G-congruent with the (1G-aligned) GPA and KVM permits 1G lpages.
+	 * A non-mmap-capable provider (the coco path) would fail this mmap.
+	 */
+	resv = mmap(NULL, DATA_SIZE + SZ_1G, PROT_NONE,
+		    MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
+	TEST_ASSERT(resv != MAP_FAILED, "reserve VA failed, errno %d", errno);
+	hva = (void *)(((uintptr_t)resv + SZ_1G - 1) & ~((uintptr_t)SZ_1G - 1));
+	hva = mmap(hva, DATA_SIZE, PROT_READ | PROT_WRITE,
+		   MAP_SHARED | MAP_FIXED, gmem_fd, 0);
+	TEST_ASSERT(hva != MAP_FAILED,
+		    "mmap(provider) failed, errno %d", errno);
+
+	/* gmem-only slot: the provider is the whole backing; host uses @hva. */
+	r = __vm_set_user_memory_region2(vm, DATA_SLOT, KVM_MEM_GUEST_MEMFD,
+					 DATA_GPA, DATA_SIZE, hva, gmem_fd, 0);
+	TEST_ASSERT(!r, "KVM_SET_USER_MEMORY_REGION2 failed: %d errno %d", r, errno);
+
+	/* Map only the two pages the guest touches into its page tables. */
+	virt_map(vm, DATA_GPA, DATA_GPA, 1);
+	virt_map(vm, DATA_GPA + SZ_1G, DATA_GPA + SZ_1G, 1);
+
+	vcpu_run(vcpu);
+	switch (get_ucall(vcpu, &uc)) {
+	case UCALL_DONE:
+		break;
+	case UCALL_ABORT:
+		REPORT_GUEST_ASSERT(uc);
+	default:
+		TEST_FAIL("Unexpected exit: %s",
+			  exit_reason_str(vcpu->run->exit_reason));
+	}
+
+	p4k = vm_get_stat(vm, pages_4k);
+	p2m = vm_get_stat(vm, pages_2m);
+	p1g = vm_get_stat(vm, pages_1g);
+	pr_info("gmem-only provider NPT mappings: 4K=%lu 2M=%lu 1G=%lu\n",
+		p4k, p2m, p1g);
+	/* Informational: the host mapping and guest share the same memory. */
+	pr_info("host reads at DATA_GPA via mmap: 0x%llx\n",
+		(unsigned long long)*(volatile uint64_t *)hva);
+
+	TEST_ASSERT(p1g > 0, "expected at least one 1G NPT mapping, got 0");
+	TEST_ASSERT(p2m > 0, "expected at least one 2M NPT mapping, got 0");
+
+	kvm_vm_free(vm);
+	munmap(hva, DATA_SIZE);
+	close(gmem_fd);
+	close(gmem_ctl);
+	return 0;
+}
diff --git a/tools/testing/selftests/kvm/x86/gmem_provider_revoke_test.c b/tools/testing/selftests/kvm/x86/gmem_provider_revoke_test.c
new file mode 100644
index 000000000000..415972aa8a7e
--- /dev/null
+++ b/tools/testing/selftests/kvm/x86/gmem_provider_revoke_test.c
@@ -0,0 +1,135 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * gmem_provider_revoke_test - exercise provider-driven page revocation.
+ *
+ * Flips a provider-backed page between present and absent via an ioctl on the
+ * provider fd and checks the provider->KVM path: revoking zaps the guest's NPT
+ * (so the guest re-faults into an absent get_pfn and cannot read the page),
+ * restoring lets the next fault re-map it. This is the mechanism a memory
+ * overcommit manager would use to reclaim a page from a running guest.
+ *
+ * The test opens the provider with GMEM_PROVIDER_FLAG_MMAP_CAPABLE at SETUP time (gmem-only). Load the module with a
+ * backing region of at least DATA_SIZE, e.g.:
+ *   insmod gmem_provider.ko addr=0x5D40000000 len=0x1000000
+ */
+#include <fcntl.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+
+#include "test_util.h"
+#include "kvm_util.h"
+#include "processor.h"
+
+/* Mirrors samples/kvm/gmem_provider.h */
+struct gmem_provider_setup {
+	__s32 kvm_fd;
+	__u32 flags;
+	__u64 size;
+};
+struct gmem_provider_present {
+	__u64 offset;
+	__u64 len;
+	__u32 present;
+	__u32 pad;
+};
+#define GMEM_PROVIDER_SETUP		_IOW('G', 1, struct gmem_provider_setup)
+#define GMEM_PROVIDER_FLAG_MMAP_CAPABLE	(1u << 0)
+#define GMEM_PROVIDER_SET_PRESENT	_IOW('G', 2, struct gmem_provider_present)
+
+#define DATA_SLOT	10
+#define DATA_GPA	(1ULL << 32)
+#define DATA_SIZE	0x200000ULL		/* 2 MiB region */
+#define MAGIC		0x1234abcdULL
+
+static void guest_code(void)
+{
+	*(volatile uint64_t *)DATA_GPA = MAGIC;
+	for (;;)
+		GUEST_SYNC(*(volatile uint64_t *)DATA_GPA);
+}
+
+int main(void)
+{
+	struct vm_shape shape = {
+		.mode = VM_MODE_DEFAULT,
+		.type = KVM_X86_SW_PROTECTED_VM,
+	};
+	struct gmem_provider_setup setup = { .flags = GMEM_PROVIDER_FLAG_MMAP_CAPABLE };
+	struct gmem_provider_present req;
+	struct kvm_vcpu *vcpu;
+	struct kvm_vm *vm;
+	struct ucall uc;
+	int gmem_ctl, gmem_fd, r;
+	void *hva;
+
+	TEST_REQUIRE(kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SW_PROTECTED_VM));
+
+	gmem_ctl = open("/dev/gmem_provider", O_RDWR);
+	__TEST_REQUIRE(gmem_ctl >= 0,
+		       "gmem_provider module not loaded (/dev/gmem_provider absent)");
+
+	vm = vm_create_shape_with_one_vcpu(shape, &vcpu, guest_code);
+
+	setup.kvm_fd = vm->fd;
+	setup.size = DATA_SIZE;
+	gmem_fd = ioctl(gmem_ctl, GMEM_PROVIDER_SETUP, &setup);
+	TEST_ASSERT(gmem_fd >= 0, "GMEM_PROVIDER_SETUP failed, errno %d", errno);
+
+	hva = mmap(NULL, DATA_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, gmem_fd, 0);
+	TEST_ASSERT(hva != MAP_FAILED,
+		    "mmap(provider) failed, errno %d", errno);
+
+	r = __vm_set_user_memory_region2(vm, DATA_SLOT, KVM_MEM_GUEST_MEMFD,
+					 DATA_GPA, DATA_SIZE, hva, gmem_fd, 0);
+	TEST_ASSERT(!r, "KVM_SET_USER_MEMORY_REGION2 failed: %d errno %d", r, errno);
+	virt_map(vm, DATA_GPA, DATA_GPA, 1);
+
+	/* 1) Present: guest writes MAGIC and reads it back. */
+	vcpu_run(vcpu);
+	TEST_ASSERT(get_ucall(vcpu, &uc) == UCALL_SYNC, "expected UCALL_SYNC");
+	TEST_ASSERT(uc.args[1] == MAGIC, "guest read 0x%lx, want MAGIC",
+		    (unsigned long)uc.args[1]);
+	pr_info("present: guest read 0x%llx\n", MAGIC);
+
+	/* 2) Revoke: mark absent and zap the guest NPT (provider->KVM). */
+	req = (struct gmem_provider_present){ .offset = 0, .len = 4096, .present = 0 };
+	r = ioctl(gmem_fd, GMEM_PROVIDER_SET_PRESENT, &req);
+	TEST_ASSERT(!r, "revoke ioctl failed, errno %d", errno);
+
+	/* 3) Guest re-reads -> re-fault into absent get_pfn -> must NOT see MAGIC. */
+	r = _vcpu_run(vcpu);
+	/*
+	 * KVM_EXIT_MEMORY_FAULT returns -1 from KVM_RUN with errno == EFAULT.
+	 * Assert unconditionally that we observed the expected fault:
+	 * -1 + EFAULT + exit_reason == KVM_EXIT_MEMORY_FAULT.
+	 */
+	TEST_ASSERT(r == -1 && errno == EFAULT &&
+		    vcpu->run->exit_reason == KVM_EXIT_MEMORY_FAULT,
+		    "revoked: expected KVM_EXIT_MEMORY_FAULT (r=%d errno=%d exit_reason=%u %s)",
+		    r, errno, vcpu->run->exit_reason,
+		    exit_reason_str(vcpu->run->exit_reason));
+	pr_info("revoked: KVM_EXIT_MEMORY_FAULT as expected\n");
+
+	/* 4) Restore: mark present again. */
+	req.present = 1;
+	r = ioctl(gmem_fd, GMEM_PROVIDER_SET_PRESENT, &req);
+	TEST_ASSERT(!r, "restore ioctl failed, errno %d", errno);
+
+	/* 5) Re-enter: the fault re-maps via get_pfn, guest reads MAGIC again. */
+	vcpu_run(vcpu);
+	TEST_ASSERT(get_ucall(vcpu, &uc) == UCALL_SYNC, "expected UCALL_SYNC after restore");
+	TEST_ASSERT(uc.args[1] == MAGIC, "after restore guest read 0x%lx, want MAGIC",
+		    (unsigned long)uc.args[1]);
+	pr_info("restored: guest read 0x%llx again -- revoke/restore path works\n", MAGIC);
+
+	kvm_vm_free(vm);
+	munmap(hva, DATA_SIZE);
+	close(gmem_fd);
+	close(gmem_ctl);
+	return 0;
+}
diff --git a/tools/testing/selftests/kvm/x86/gmem_provider_test.c b/tools/testing/selftests/kvm/x86/gmem_provider_test.c
new file mode 100644
index 000000000000..d7caa11616df
--- /dev/null
+++ b/tools/testing/selftests/kvm/x86/gmem_provider_test.c
@@ -0,0 +1,195 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * gmem_provider_test - exercise the samples/kvm gmem_provider module through a
+ * full SEV-SNP guest launch, then re-bind the same provider fd to a second VM
+ * (the live-update path).
+ *
+ * The provider module must be loaded first, in either mode:
+ *   insmod gmem_provider.ko addr=0x5D40000000 len=0x1000000   # page-less
+ *   insmod gmem_provider.ko                                   # CMA fallback
+ *
+ * The test skips (KSFT_SKIP) if /dev/gmem_provider or SNP support is absent.
+ *
+ * Note: with the current kvm_gmem_populate() ABI the launch source is an
+ * ordinary anonymous buffer (pinned via get_user_pages_fast); the provider's
+ * populate() copies it into the backing.  No /dev/mem mapping is needed.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <stdint.h>
+#include <linux/kvm.h>
+#include <asm/kvm.h>
+
+/* Mirrors samples/kvm/gmem_provider.h */
+struct gmem_provider_setup {
+	int32_t kvm_fd;
+	uint32_t pad;
+	uint64_t size;
+};
+#define GMEM_PROVIDER_SETUP _IOW('G', 1, struct gmem_provider_setup)
+
+#define KSFT_PASS 0
+#define KSFT_FAIL 1
+#define KSFT_SKIP 4
+
+#define GUEST_MEM_SIZE	(16UL * 1024 * 1024)
+#define PAGE_SIZE_4K	4096UL
+
+static int sev_ioctl(int vm_fd, int sev_fd, int cmd, void *data)
+{
+	struct kvm_sev_cmd sev_cmd = {
+		.id = cmd,
+		.data = (uint64_t)(unsigned long)data,
+		.sev_fd = sev_fd,
+	};
+	return ioctl(vm_fd, KVM_MEMORY_ENCRYPT_OP, &sev_cmd);
+}
+
+static int launch_snp_vm(int kvm_fd, int gmem_fd, void *src, int vm_num)
+{
+	int vm_fd, vcpu_fd, sev_fd, ret;
+
+	printf("[VM%d] KVM_CREATE_VM (SNP)\n", vm_num);
+	vm_fd = ioctl(kvm_fd, KVM_CREATE_VM, KVM_X86_SNP_VM);
+	if (vm_fd < 0) { perror("KVM_CREATE_VM"); return -1; }
+
+	sev_fd = open("/dev/sev", O_RDWR);
+	if (sev_fd < 0) { perror("open /dev/sev"); close(vm_fd); return -1; }
+
+	struct kvm_sev_init init = { 0 };
+	ret = sev_ioctl(vm_fd, sev_fd, KVM_SEV_INIT2, &init);
+	if (ret) { perror("KVM_SEV_INIT2"); goto out; }
+
+	struct kvm_userspace_memory_region2 region = {
+		.slot = 0,
+		.flags = KVM_MEM_GUEST_MEMFD,
+		.guest_phys_addr = 0,
+		.memory_size = GUEST_MEM_SIZE,
+		.userspace_addr = (uint64_t)(unsigned long)src,
+		.guest_memfd = gmem_fd,
+		.guest_memfd_offset = 0,
+	};
+	ret = ioctl(vm_fd, KVM_SET_USER_MEMORY_REGION2, &region);
+	if (ret) { perror("KVM_SET_USER_MEMORY_REGION2"); goto out; }
+
+	struct kvm_memory_attributes attrs = {
+		.address = 0,
+		.size = GUEST_MEM_SIZE,
+		.attributes = KVM_MEMORY_ATTRIBUTE_PRIVATE,
+	};
+	ret = ioctl(vm_fd, KVM_SET_MEMORY_ATTRIBUTES, &attrs);
+	if (ret) { perror("KVM_SET_MEMORY_ATTRIBUTES"); goto out; }
+
+	struct kvm_sev_snp_launch_start start = { .policy = 0x30000 };
+	ret = sev_ioctl(vm_fd, sev_fd, KVM_SEV_SNP_LAUNCH_START, &start);
+	if (ret) { perror("SNP_LAUNCH_START"); goto out; }
+
+	printf("[VM%d] SNP_LAUNCH_UPDATE (code + zero, %luMB)\n",
+	       vm_num, GUEST_MEM_SIZE >> 20);
+	struct kvm_sev_snp_launch_update update = {
+		.gfn_start = 0,
+		.uaddr = (uint64_t)(unsigned long)src,
+		.len = PAGE_SIZE_4K,
+		.type = KVM_SEV_SNP_PAGE_TYPE_NORMAL,
+	};
+	ret = sev_ioctl(vm_fd, sev_fd, KVM_SEV_SNP_LAUNCH_UPDATE, &update);
+	if (ret) { perror("SNP_LAUNCH_UPDATE code"); goto out; }
+
+	struct kvm_sev_snp_launch_update update_zero = {
+		.gfn_start = 1,
+		.uaddr = (uint64_t)(unsigned long)(src + PAGE_SIZE_4K),
+		.len = GUEST_MEM_SIZE - PAGE_SIZE_4K,
+		.type = KVM_SEV_SNP_PAGE_TYPE_ZERO,
+	};
+	ret = sev_ioctl(vm_fd, sev_fd, KVM_SEV_SNP_LAUNCH_UPDATE, &update_zero);
+	if (ret) { perror("SNP_LAUNCH_UPDATE zero"); goto out; }
+
+	struct kvm_sev_snp_launch_finish finish = { 0 };
+	ret = sev_ioctl(vm_fd, sev_fd, KVM_SEV_SNP_LAUNCH_FINISH, &finish);
+	if (ret) { perror("SNP_LAUNCH_FINISH"); goto out; }
+
+	vcpu_fd = ioctl(vm_fd, KVM_CREATE_VCPU, 0);
+	if (vcpu_fd < 0) { perror("KVM_CREATE_VCPU"); ret = -1; goto out; }
+
+	printf("[VM%d] *** launch succeeded ***\n", vm_num);
+	close(vcpu_fd);
+	ret = 0;
+out:
+	close(sev_fd);
+	close(vm_fd);
+	return ret;
+}
+
+int main(void)
+{
+	int kvm_fd, gmem_ctl_fd, gmem_fd, tmp_vm;
+	void *src;
+
+	setbuf(stdout, NULL);
+
+	kvm_fd = open("/dev/kvm", O_RDWR);
+	if (kvm_fd < 0) {
+		printf("SKIP: cannot open /dev/kvm (%s)\n", strerror(errno));
+		return KSFT_SKIP;
+	}
+
+	gmem_ctl_fd = open("/dev/gmem_provider", O_RDWR);
+	if (gmem_ctl_fd < 0) {
+		printf("SKIP: /dev/gmem_provider not present -- load gmem_provider.ko (%s)\n",
+		       strerror(errno));
+		return KSFT_SKIP;
+	}
+
+	/* Provider setup needs a VM fd; ownership transfers to each VM on bind. */
+	tmp_vm = ioctl(kvm_fd, KVM_CREATE_VM, KVM_X86_SNP_VM);
+	if (tmp_vm < 0) {
+		printf("SKIP: cannot create SNP VM -- host not SNP-capable? (%s)\n",
+		       strerror(errno));
+		return KSFT_SKIP;
+	}
+
+	struct gmem_provider_setup setup = {
+		.kvm_fd = tmp_vm,
+		.size = GUEST_MEM_SIZE,
+	};
+	gmem_fd = ioctl(gmem_ctl_fd, GMEM_PROVIDER_SETUP, &setup);
+	if (gmem_fd < 0) {
+		perror("GMEM_PROVIDER_SETUP");
+		return KSFT_FAIL;
+	}
+	close(tmp_vm);
+	printf("provider gmem_fd = %d (persists across VMs)\n", gmem_fd);
+
+	/* Launch source: ordinary anonymous memory holding a HLT at gfn 0. */
+	src = mmap(NULL, GUEST_MEM_SIZE, PROT_READ | PROT_WRITE,
+		   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+	if (src == MAP_FAILED) { perror("mmap src"); return KSFT_FAIL; }
+	memset(src, 0, GUEST_MEM_SIZE);
+	((uint8_t *)src)[0] = 0xf4; /* HLT */
+
+	if (launch_snp_vm(kvm_fd, gmem_fd, src, 1) != 0) {
+		printf("FAIL: VM1 launch failed\n");
+		return KSFT_FAIL;
+	}
+
+	usleep(100000);
+
+	/* Re-bind the SAME provider fd to a fresh VM (live-update path). */
+	if (launch_snp_vm(kvm_fd, gmem_fd, src, 2) != 0) {
+		printf("FAIL: VM2 re-bind launch failed\n");
+		return KSFT_FAIL;
+	}
+
+	printf("PASS: page-less/provider-backed SNP launch + re-bind succeeded\n");
+	munmap(src, GUEST_MEM_SIZE);
+	close(gmem_fd);
+	close(gmem_ctl_fd);
+	close(kvm_fd);
+	return KSFT_PASS;
+}
-- 
2.55.0


  parent reply	other threads:[~2026-07-20 11:13 UTC|newest]

Thread overview: 28+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-20 11:03 [RFC PATCH v2 00/11] KVM: Allow alternative providers of guest_memfd backed by PFNMAP memory David Woodhouse
2026-07-20 11:03 ` [RFC PATCH v2 01/11] KVM: selftests: sev_smoke_test: Only run VM types the host offers David Woodhouse
2026-07-20 11:03   ` [RFC PATCH v2 02/11] KVM: selftests: sev_init2_tests: Derive SEV availability from KVM David Woodhouse
2026-07-20 11:03   ` [RFC PATCH v2 03/11] KVM: SEV: Remove struct page dependency from SNP gmem paths David Woodhouse
2026-07-20 11:03   ` [RFC PATCH v2 04/11] KVM: guest_memfd: Introduce guest memory ops and route native gmem through them David Woodhouse
2026-07-20 11:03   ` [RFC PATCH v2 05/11] iommufd: Look up private-interconnect phys via exporter symbols David Woodhouse
2026-07-20 11:03   ` [RFC PATCH v2 06/11] iommufd: Plumb dma-buf memory-type (RAM vs MMIO) through the phys map David Woodhouse
2026-07-20 11:03   ` [RFC PATCH v2 07/11] KVM: guest_memfd: Add ops-driven page revocation David Woodhouse
2026-07-20 11:03   ` [RFC PATCH v2 08/11] samples/kvm: Add guest_memfd backing sample David Woodhouse
2026-07-20 11:03   ` David Woodhouse [this message]
2026-07-20 11:03   ` [RFC PATCH v2 10/11] selftests/kvm: gmem_provider iommufd tests David Woodhouse
2026-07-20 11:03   ` [RFC PATCH v2 11/11] samples/kvm, selftests/kvm: Allow the gmem_provider NVMe DMA test on arm64 David Woodhouse
2026-07-20 15:11 ` [RFC PATCH v2 00/11] KVM: Allow alternative providers of guest_memfd backed by PFNMAP memory Paolo Bonzini
2026-07-20 16:39   ` David Woodhouse
2026-07-23  0:24 ` Ackerley Tng
2026-07-23  9:40   ` David Woodhouse
2026-07-23 16:01     ` Ackerley Tng
2026-10-05  9:55 ` [RFC PATCH 0/6] KVM: guest_memfd: back guest_memfd with an imported dma-buf Fred Griffoul
2026-10-05  9:55   ` [RFC PATCH 1/6] KVM: guest_memfd: Add a writable result to get_pfn() Fred Griffoul
2026-10-05  9:55   ` [RFC PATCH 2/6] dma-buf: Add get_phys() to describe a physical run Fred Griffoul
2026-10-05 10:07     ` Christian König
2026-10-05 13:20       ` Fred Griffoul
2026-10-05 14:53         ` Christian König
2026-10-05  9:55   ` [RFC PATCH 3/6] dma-buf: Add ranged mapping invalidation Fred Griffoul
2026-10-05 10:08     ` Christian König
2026-10-05  9:55   ` [RFC PATCH 4/6] dma-buf: Allow dynamic attach without a device Fred Griffoul
2026-10-05  9:55   ` [RFC PATCH 5/6] KVM: guest_memfd: Add dma-buf backing Fred Griffoul
2026-10-05  9:55   ` [RFC PATCH 6/6] samples/kvm, selftests/kvm: Exercise " Fred Griffoul

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=45bcdb0af20bcb929ea818b59b4539daf1610f9d.1784545391.git.dwmw@amazon.co.uk \
    --to=dwmw2@infradead.org \
    --cc=akpm@linux-foundation.org \
    --cc=ashish.kalra@amd.com \
    --cc=bp@alien8.de \
    --cc=christian.koenig@amd.com \
    --cc=colin.i.king@gmail.com \
    --cc=connordw@amazon.co.uk \
    --cc=connordw@amazon.com \
    --cc=dave.hansen@linux.intel.com \
    --cc=djbw@kernel.org \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=dwmw@amazon.co.uk \
    --cc=ebiggers@kernel.org \
    --cc=fgriffo@amazon.co.uk \
    --cc=graf@amazon.de \
    --cc=hpa@zytor.com \
    --cc=iommu@lists.linux.dev \
    --cc=jgg@ziepe.ca \
    --cc=joro@8bytes.org \
    --cc=kees@kernel.org \
    --cc=kevin.tian@intel.com \
    --cc=kvm@vger.kernel.org \
    --cc=linaro-mm-sig@lists.linaro.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-kselftest@vger.kernel.org \
    --cc=linux-media@vger.kernel.org \
    --cc=mingo@redhat.com \
    --cc=pbonzini@redhat.com \
    --cc=robin.murphy@arm.com \
    --cc=seanjc@google.com \
    --cc=shuah@kernel.org \
    --cc=sumit.semwal@linaro.org \
    --cc=tglx@kernel.org \
    --cc=thomas.lendacky@amd.com \
    --cc=tycho@kernel.org \
    --cc=will@kernel.org \
    --cc=x86@kernel.org \
    --cc=zhouyuhang@kylinos.cn \
    --cc=zi.li@linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®