mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: "Jörg Rödel" <joro@8bytes.org>
To: Paolo Bonzini <pbonzini@redhat.com>,
	Sean Christopherson <seanjc@google.com>
Cc: Michael Roth <michael.roth@amd.com>,
	Liam Merwick <liam.merwick@oracle.com>,
	Vishal Annapurve <vannapurve@google.com>,
	Ninad Naik <ninadnaik07@gmail.com>,
	Joerg Roedel <joerg.roedel@amd.com>,
	Tom Lendacky <thomas.lendacky@amd.com>,
	James Bottomley <James.Bottomley@HansenPartnership.com>,
	kvm@vger.kernel.org, linux-doc@vger.kernel.org,
	linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org,
	coconut-svsm@lists.linux.dev
Subject: [PATCH v2 8/8] KVM: selftests: Test SNP vCPU state and direct VMSA launch
Date: Tue,  8 Sep 2026 12:33:38 +0200	[thread overview]
Message-ID: <20260908103338.427254-9-joro@8bytes.org> (raw)
In-Reply-To: <20260908103338.427254-1-joro@8bytes.org>

From: Joerg Roedel <joerg.roedel@amd.com>

Add vCPU-scoped SEV ioctl helpers and exercise the SNP vCPU state get and
set commands.  Enable KVM_CAP_SNP_DIRECT_VMSA before creating vCPUs and
verify that invalid capability arguments and late enable attempts fail.

Boot through a handcrafted BSP VMSA, verify VMSA and GHCB address round
trips and validity bits, rebind to the selected GPA, and verify an extra
unassociated AP remains uninitialized.

Reject multi-page launch updates and VMSAs with nonzero VMPL or mismatched
SEV features.  Also verify the legacy launch path and reject non-SNP use,
bad or reserved arguments, unsafe alignment, absent pages, wrong ordering,
and post-launch SET calls.  Verify that SET accepts both shared and private
guestmemfd-backed VMSA GPAs without consulting memory attributes or the
RMP.

Assisted-by: LLM
Signed-off-by: Joerg Roedel <joerg.roedel@amd.com>
---
 tools/testing/selftests/kvm/Makefile.kvm      |   1 +
 tools/testing/selftests/kvm/include/x86/sev.h |  49 ++
 .../kvm/x86/sev_snp_direct_vmsa_test.c        | 421 ++++++++++++++++++
 3 files changed, 471 insertions(+)
 create mode 100644 tools/testing/selftests/kvm/x86/sev_snp_direct_vmsa_test.c

diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm
index 92eb3dff13ff..4cf0a8297b04 100644
--- a/tools/testing/selftests/kvm/Makefile.kvm
+++ b/tools/testing/selftests/kvm/Makefile.kvm
@@ -150,6 +150,7 @@ TEST_GEN_PROGS_x86 += x86/sev_dbg_test
 TEST_GEN_PROGS_x86 += x86/sev_init2_tests
 TEST_GEN_PROGS_x86 += x86/sev_migrate_tests
 TEST_GEN_PROGS_x86 += x86/sev_snp_apic_id_test
+TEST_GEN_PROGS_x86 += x86/sev_snp_direct_vmsa_test
 TEST_GEN_PROGS_x86 += x86/sev_smoke_test
 TEST_GEN_PROGS_x86 += x86/amx_test
 TEST_GEN_PROGS_x86 += x86/max_vcpuid_cap_test
diff --git a/tools/testing/selftests/kvm/include/x86/sev.h b/tools/testing/selftests/kvm/include/x86/sev.h
index dec383e59a47..0f6c92b802ee 100644
--- a/tools/testing/selftests/kvm/include/x86/sev.h
+++ b/tools/testing/selftests/kvm/include/x86/sev.h
@@ -100,6 +100,30 @@ static inline u64 snp_default_policy(void)
 	__TEST_ASSERT_VM_VCPU_IOCTL(!ret, #cmd,	ret, vm);		\
 })
 
+static inline int __vcpu_sev_ioctl(struct kvm_vcpu *vcpu, u32 cmd, void *arg)
+{
+	union {
+		struct kvm_sev_cmd c;
+		unsigned long raw;
+	} sev_cmd = { .c = {
+		.id = cmd,
+		.data = (u64)arg,
+		.sev_fd = vcpu->vm->arch.sev_fd,
+	} };
+	int ret;
+
+	ret = __vcpu_ioctl(vcpu, KVM_MEMORY_ENCRYPT_OP, &sev_cmd.raw);
+	return ret ?: sev_cmd.c.error;
+}
+
+#define vcpu_sev_ioctl(vcpu, cmd, arg)				\
+({								\
+	struct kvm_vcpu *__vcpu = (vcpu);				\
+	int ret = __vcpu_sev_ioctl(__vcpu, cmd, arg);		\
+								\
+	__TEST_ASSERT_VM_VCPU_IOCTL(!ret, #cmd, ret, __vcpu->vm);	\
+})
+
 void sev_vm_init(struct kvm_vm *vm);
 void sev_es_vm_init(struct kvm_vm *vm);
 void snp_vm_init(struct kvm_vm *vm);
@@ -144,6 +168,31 @@ static inline void snp_launch_update_data(struct kvm_vm *vm, gpa_t gpa,
 	vm_sev_ioctl(vm, KVM_SEV_SNP_LAUNCH_UPDATE, &update_data);
 }
 
+static inline void snp_launch_update_vmsa(struct kvm_vm *vm, gpa_t gpa,
+					  void *vmsa)
+{
+	vm_mem_set_private(vm, gpa, PAGE_SIZE);
+	snp_launch_update_data(vm, gpa, (u64)vmsa, PAGE_SIZE,
+			       KVM_SEV_SNP_PAGE_TYPE_VMSA);
+}
+
+static inline void snp_get_vcpu_state(struct kvm_vcpu *vcpu,
+				      struct kvm_sev_snp_vcpu_state *state)
+{
+	vcpu_sev_ioctl(vcpu, KVM_SEV_SNP_GET_VCPU_STATE, state);
+}
+
+static inline void snp_set_vcpu_state(struct kvm_vcpu *vcpu, gpa_t vmsa_gpa)
+{
+	struct kvm_sev_snp_vcpu_state state = {
+		.vmsa_gpa = vmsa_gpa,
+		.valid_fields = KVM_SEV_SNP_VCPU_STATE_VMSA_VALID |
+				KVM_SEV_SNP_VCPU_STATE_GHCB_VALID,
+	};
+
+	vcpu_sev_ioctl(vcpu, KVM_SEV_SNP_SET_VCPU_STATE, &state);
+}
+
 static inline void sev_dbg_crypt_memory(struct kvm_vm *vm, unsigned int cmd,
 					void *dst, void *src, unsigned int len)
 {
diff --git a/tools/testing/selftests/kvm/x86/sev_snp_direct_vmsa_test.c b/tools/testing/selftests/kvm/x86/sev_snp_direct_vmsa_test.c
new file mode 100644
index 000000000000..72eecf3853a3
--- /dev/null
+++ b/tools/testing/selftests/kvm/x86/sev_snp_direct_vmsa_test.c
@@ -0,0 +1,421 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <errno.h>
+#include <stdlib.h>
+
+#include "kvm_util.h"
+#include "processor.h"
+#include "sev.h"
+#include "svm_util.h"
+
+#define DIRECT_MARKER	0x444952454354564dULL
+#define LEGACY_MARKER	0x4c4547414359564dULL
+#define SNP_ACTIVE_SEV_FEATURE	BIT_ULL(0)
+#define VMSA_PMD_SIZE	BIT_ULL(21)
+#define VMSA_MIN_GPA	(VMSA_PMD_SIZE + PAGE_SIZE)
+
+struct test_vmsa {
+	struct vmcb_seg es, cs, ss, ds, fs, gs;
+	struct vmcb_seg gdtr, ldtr, idtr, tr;
+	u64 pl0_ssp, pl1_ssp, pl2_ssp, pl3_ssp;
+	u64 u_cet;
+	u8 reserved_0xc8[2];
+	u8 vmpl;
+	u8 cpl;
+	u8 reserved_0xcc[4];
+	u64 efer;
+	u8 reserved_0xd8[104];
+	u64 xss;
+	u64 cr4, cr3, cr0, dr7, dr6, rflags, rip;
+	u64 dr0, dr1, dr2, dr3;
+	u64 dr0_addr_mask, dr1_addr_mask, dr2_addr_mask, dr3_addr_mask;
+	u8 reserved_0x1c0[24];
+	u64 rsp, s_cet, ssp, isst_addr, rax;
+	u64 star, lstar, cstar, sfmask, kernel_gs_base;
+	u64 sysenter_cs, sysenter_esp, sysenter_eip, cr2;
+	u8 reserved_0x248[32];
+	u64 g_pat, dbgctl, br_from, br_to, last_excp_from, last_excp_to;
+	u8 reserved_0x298[80];
+	u32 pkru, tsc_aux;
+	u64 tsc_scale, tsc_offset;
+	u8 reserved_0x300[8];
+	u64 rcx, rdx, rbx, reserved_0x320, rbp, rsi, rdi;
+	u64 r8, r9, r10, r11, r12, r13, r14, r15;
+	u8 reserved_0x380[16];
+	u64 guest_exit_info_1, guest_exit_info_2, guest_exit_int_info, guest_nrip;
+	u64 sev_features, vintr_ctrl, guest_exit_code, virtual_tom, tlb_id, pcpu_id;
+	u64 event_inj, xcr0;
+	u8 reserved_0x3f0[16];
+	u64 x87_dp;
+	u32 mxcsr;
+	u16 x87_ftw, x87_fsw, x87_fcw, x87_fop, x87_ds, x87_cs;
+	u64 x87_rip;
+	u8 fpreg_x87[80];
+	u8 fpreg_xmm[256];
+	u8 fpreg_ymm[256];
+} __packed;
+
+static_assert(offsetof(struct test_vmsa, vmpl) == 0xca);
+static_assert(offsetof(struct test_vmsa, rip) == 0x178);
+static_assert(offsetof(struct test_vmsa, sev_features) == 0x3b0);
+static_assert(offsetof(struct test_vmsa, xcr0) == 0x3e8);
+
+static void guest_direct_entry(u64 *marker)
+{
+	*marker = DIRECT_MARKER;
+	wrmsr(MSR_AMD64_SEV_ES_GHCB, GHCB_MSR_TERM_REQ);
+	vmgexit();
+}
+
+static void guest_legacy_entry(u64 *marker)
+{
+	*marker = LEGACY_MARKER;
+	wrmsr(MSR_AMD64_SEV_ES_GHCB, GHCB_MSR_TERM_REQ);
+	vmgexit();
+}
+
+static void copy_segment(struct vmcb_seg *dst, const struct kvm_segment *src)
+{
+	dst->selector = src->selector;
+	dst->base = src->base;
+	dst->limit = src->limit;
+	dst->attrib = src->type |
+		(src->s << SVM_SELECTOR_S_SHIFT) |
+		(src->dpl << SVM_SELECTOR_DPL_SHIFT) |
+		((src->present && !src->unusable) << SVM_SELECTOR_P_SHIFT) |
+		(src->avl << SVM_SELECTOR_AVL_SHIFT) |
+		(src->l << SVM_SELECTOR_L_SHIFT) |
+		(src->db << SVM_SELECTOR_DB_SHIFT) |
+		(src->g << SVM_SELECTOR_G_SHIFT);
+}
+
+static void copy_dtable(struct vmcb_seg *dst, const struct kvm_dtable *src)
+{
+	dst->base = src->base;
+	dst->limit = src->limit;
+}
+
+static void prepare_vmsa(struct kvm_vcpu *vcpu, struct test_vmsa *vmsa,
+			 void *entry)
+{
+	struct kvm_sregs sregs;
+	struct kvm_regs regs;
+
+	memset(vmsa, 0, PAGE_SIZE);
+	vcpu_sregs_get(vcpu, &sregs);
+	vcpu_regs_get(vcpu, &regs);
+
+	copy_segment(&vmsa->es, &sregs.es);
+	copy_segment(&vmsa->cs, &sregs.cs);
+	copy_segment(&vmsa->ss, &sregs.ss);
+	copy_segment(&vmsa->ds, &sregs.ds);
+	copy_segment(&vmsa->fs, &sregs.fs);
+	copy_segment(&vmsa->gs, &sregs.gs);
+	copy_dtable(&vmsa->gdtr, &sregs.gdt);
+	copy_segment(&vmsa->ldtr, &sregs.ldt);
+	copy_dtable(&vmsa->idtr, &sregs.idt);
+	copy_segment(&vmsa->tr, &sregs.tr);
+
+	vmsa->cpl = sregs.cs.dpl;
+	/*
+	 * KVM_GET_SREGS exposes the guest-visible EFER and therefore omits
+	 * SVME, which KVM normally adds to the hardware VMSA itself.
+	 */
+	vmsa->efer = sregs.efer | EFER_SVME;
+	vmsa->cr4 = sregs.cr4;
+	vmsa->cr3 = sregs.cr3;
+	vmsa->cr0 = sregs.cr0;
+	vmsa->dr7 = 0x400;
+	vmsa->dr6 = 0xffff0ff0;
+	vmsa->rflags = regs.rflags;
+	vmsa->rip = (u64)entry;
+	vmsa->rsp = regs.rsp;
+	vmsa->rax = regs.rax;
+	vmsa->rcx = regs.rcx;
+	vmsa->rdx = regs.rdx;
+	vmsa->rbx = regs.rbx;
+	vmsa->rbp = regs.rbp;
+	vmsa->rsi = regs.rsi;
+	vmsa->rdi = regs.rdi;
+	vmsa->r8 = regs.r8;
+	vmsa->r9 = regs.r9;
+	vmsa->r10 = regs.r10;
+	vmsa->r11 = regs.r11;
+	vmsa->r12 = regs.r12;
+	vmsa->r13 = regs.r13;
+	vmsa->r14 = regs.r14;
+	vmsa->r15 = regs.r15;
+	vmsa->g_pat = 0x0007040600070406ULL;
+	vmsa->sev_features = SNP_ACTIVE_SEV_FEATURE;
+	vmsa->xcr0 = 1;
+	vmsa->mxcsr = 0x1f80;
+	vmsa->x87_fcw = 0x37f;
+}
+
+static void expect_launch_update_vmsa_error(struct kvm_vm *vm, gpa_t gpa,
+					    void *vmsa, u64 size)
+{
+	struct kvm_sev_snp_launch_update update = {
+		.gfn_start = gpa >> PAGE_SHIFT,
+		.uaddr = (u64)vmsa,
+		.len = size,
+		.type = KVM_SEV_SNP_PAGE_TYPE_VMSA,
+	};
+
+	errno = 0;
+	TEST_ASSERT_EQ(__vm_sev_ioctl(vm, KVM_SEV_SNP_LAUNCH_UPDATE, &update), -1);
+	TEST_ASSERT_EQ(errno, EINVAL);
+}
+
+static void exclude_from_normal_launch(struct kvm_vm *vm, gpa_t gpa,
+				       unsigned int npages)
+{
+	struct userspace_mem_region *region;
+
+	region = memslot2region(vm, vm->memslots[MEM_REGION_TEST_DATA]);
+	sparsebit_clear_num(region->protected_phy_pages, gpa >> PAGE_SHIFT, npages);
+}
+
+static void assert_vcpu_terminated(struct kvm_vcpu *vcpu)
+{
+	vcpu_run(vcpu);
+	TEST_ASSERT_EQ(vcpu->run->exit_reason, KVM_EXIT_SYSTEM_EVENT);
+	TEST_ASSERT_EQ(vcpu->run->system_event.type, KVM_SYSTEM_EVENT_SEV_TERM);
+}
+
+static struct kvm_vm *create_direct_vmsa_vm(unsigned int nr_vcpus,
+					    void *guest_code,
+					    struct kvm_vcpu **vcpus)
+{
+	struct vm_shape shape = {
+		.mode = VM_MODE_DEFAULT,
+		.type = KVM_X86_SNP_VM,
+	};
+	struct kvm_vm *vm;
+	unsigned int i;
+
+	vm = __vm_create(shape, nr_vcpus, 0);
+	vm_enable_cap(vm, KVM_CAP_SNP_DIRECT_VMSA, 0);
+	for (i = 0; i < nr_vcpus; i++)
+		vcpus[i] = vm_vcpu_add(vm, i, guest_code);
+	kvm_arch_vm_finalize_vcpus(vm);
+
+	return vm;
+}
+
+static void test_direct_vmsa(void)
+{
+	struct test_vmsa *vmsas, *selected_vmsa;
+	struct kvm_sev_snp_vcpu_state state = {};
+	struct kvm_mp_state mp_state = {
+		.mp_state = KVM_MP_STATE_UNINITIALIZED,
+	};
+	struct kvm_vcpu *vcpus[2];
+	struct kvm_vcpu *vcpu, *ap;
+	struct kvm_vm *vm;
+	gva_t marker_gva;
+	gpa_t vmsa_gpa;
+	u64 *marker;
+
+	vm = create_direct_vmsa_vm(ARRAY_SIZE(vcpus), guest_legacy_entry, vcpus);
+	vcpu = vcpus[0];
+	ap = vcpus[1];
+	/* Restore normal AP state after vm_vcpu_add() makes it runnable. */
+	vcpu_mp_state_set(ap, &mp_state);
+	marker_gva = vm_alloc_shared(vm, PAGE_SIZE, KVM_UTIL_MIN_VADDR,
+				     MEM_REGION_TEST_DATA);
+	marker = addr_gva2hva(vm, marker_gva);
+	vcpu_args_set(vcpu, 1, marker_gva);
+
+	vmsa_gpa = vm_phy_pages_alloc(vm, 2, VMSA_MIN_GPA,
+				      vm->memslots[MEM_REGION_TEST_DATA]);
+	TEST_ASSERT(vmsa_gpa & (VMSA_PMD_SIZE - 1), "unsafe VMSA GPA");
+	vmsas = aligned_alloc(PAGE_SIZE, 2 * PAGE_SIZE);
+	TEST_ASSERT(vmsas, "Failed to allocate VMSA source pages");
+	selected_vmsa = (void *)vmsas + PAGE_SIZE;
+	prepare_vmsa(vcpu, &vmsas[0], guest_legacy_entry);
+	prepare_vmsa(vcpu, selected_vmsa, guest_direct_entry);
+
+	snp_vm_launch_start(vm, snp_default_policy());
+	vm_mem_set_private(vm, vmsa_gpa, 2 * PAGE_SIZE);
+	expect_launch_update_vmsa_error(vm, vmsa_gpa, vmsas, 2 * PAGE_SIZE);
+
+	vmsas[0].vmpl = 1;
+	expect_launch_update_vmsa_error(vm, vmsa_gpa, vmsas, PAGE_SIZE);
+	vmsas[0].vmpl = 0;
+	vmsas[0].sev_features = 0;
+	expect_launch_update_vmsa_error(vm, vmsa_gpa, vmsas, PAGE_SIZE);
+	vmsas[0].sev_features = SNP_ACTIVE_SEV_FEATURE;
+
+	snp_launch_update_vmsa(vm, vmsa_gpa, vmsas);
+	snp_launch_update_vmsa(vm, vmsa_gpa + PAGE_SIZE, selected_vmsa);
+	exclude_from_normal_launch(vm, vmsa_gpa, 2);
+	snp_vm_launch_update(vm);
+
+	/* Rebinding is allowed; the second, selected VMSA must win. */
+	snp_set_vcpu_state(vcpu, vmsa_gpa);
+	snp_set_vcpu_state(vcpu, vmsa_gpa + PAGE_SIZE);
+	snp_get_vcpu_state(vcpu, &state);
+	TEST_ASSERT_EQ(state.valid_fields,
+		       KVM_SEV_SNP_VCPU_STATE_VMSA_VALID |
+		       KVM_SEV_SNP_VCPU_STATE_GHCB_VALID);
+	TEST_ASSERT_EQ(state.vmsa_gpa, vmsa_gpa + PAGE_SIZE);
+	TEST_ASSERT_EQ(state.ghcb_gpa, 0);
+	snp_vm_launch_finish(vm);
+
+	vcpu_mp_state_get(ap, &mp_state);
+	TEST_ASSERT_EQ(mp_state.mp_state, KVM_MP_STATE_UNINITIALIZED);
+	*marker = 0;
+	assert_vcpu_terminated(vcpu);
+	TEST_ASSERT_EQ(*marker, DIRECT_MARKER);
+
+	state = (struct kvm_sev_snp_vcpu_state) {
+		.vmsa_gpa = vmsa_gpa,
+		.valid_fields = KVM_SEV_SNP_VCPU_STATE_VMSA_VALID,
+	};
+	errno = 0;
+	TEST_ASSERT_EQ(__vcpu_sev_ioctl(vcpu, KVM_SEV_SNP_SET_VCPU_STATE,
+					&state), -1);
+	TEST_ASSERT_EQ(errno, EINVAL);
+
+	free(vmsas);
+	kvm_vm_free(vm);
+}
+
+static void expect_set_vcpu_state_error(struct kvm_vcpu *vcpu,
+					struct kvm_sev_snp_vcpu_state *state,
+					int expected_errno)
+{
+	errno = 0;
+	TEST_ASSERT_EQ(__vcpu_sev_ioctl(vcpu, KVM_SEV_SNP_SET_VCPU_STATE, state), -1);
+	TEST_ASSERT_EQ(errno, expected_errno);
+}
+
+static void test_invalid_requests(void)
+{
+	struct kvm_sev_snp_vcpu_state state = {
+		.valid_fields = KVM_SEV_SNP_VCPU_STATE_VMSA_VALID,
+	};
+	struct kvm_vcpu *vcpu;
+	struct kvm_vm *vm;
+	gva_t shared_gva;
+	gpa_t private_gpa;
+
+	vm = vm_create_with_one_vcpu(&vcpu, guest_legacy_entry);
+	expect_set_vcpu_state_error(vcpu, &state, ENOTTY);
+	kvm_vm_free(vm);
+
+	vm = vm_sev_create_with_one_vcpu(KVM_X86_SNP_VM, guest_legacy_entry, &vcpu);
+	expect_set_vcpu_state_error(vcpu, &state, EINVAL);
+	TEST_ASSERT_EQ(__vm_enable_cap(vm, KVM_CAP_SNP_DIRECT_VMSA, 0), -1);
+	TEST_ASSERT_EQ(errno, EINVAL);
+	snp_vm_launch_start(vm, snp_default_policy());
+	expect_set_vcpu_state_error(vcpu, &state, EINVAL);
+	kvm_vm_free(vm);
+
+	vm = create_direct_vmsa_vm(1, guest_legacy_entry, &vcpu);
+	expect_set_vcpu_state_error(vcpu, &state, EINVAL);
+	snp_vm_launch_start(vm, snp_default_policy());
+
+	state.pad[4] = 1;
+	expect_set_vcpu_state_error(vcpu, &state, EINVAL);
+	state.pad[4] = 0;
+	state.valid_fields |= BIT_ULL(2);
+	expect_set_vcpu_state_error(vcpu, &state, EINVAL);
+	state.valid_fields &= ~BIT_ULL(2);
+	state.vmsa_gpa = PAGE_SIZE + 1;
+	expect_set_vcpu_state_error(vcpu, &state, EINVAL);
+	state.vmsa_gpa = VMSA_PMD_SIZE;
+	expect_set_vcpu_state_error(vcpu, &state, EINVAL);
+	state.vmsa_gpa = BIT_ULL(40) + PAGE_SIZE;
+	expect_set_vcpu_state_error(vcpu, &state, EINVAL);
+
+	shared_gva = vm_alloc_shared(vm, 2 * PAGE_SIZE, KVM_UTIL_MIN_VADDR,
+				     MEM_REGION_TEST_DATA);
+	state.vmsa_gpa = addr_gva2gpa(vm, shared_gva);
+	if (!(state.vmsa_gpa & (VMSA_PMD_SIZE - 1)))
+		state.vmsa_gpa += PAGE_SIZE;
+	state.ghcb_gpa = BIT_ULL(40);
+	state.valid_fields |= KVM_SEV_SNP_VCPU_STATE_GHCB_VALID;
+	vcpu_sev_ioctl(vcpu, KVM_SEV_SNP_SET_VCPU_STATE, &state);
+
+	private_gpa = vm_phy_page_alloc(vm, VMSA_MIN_GPA,
+					vm->memslots[MEM_REGION_TEST_DATA]);
+	vm_mem_set_private(vm, private_gpa, PAGE_SIZE);
+	state.vmsa_gpa = private_gpa;
+	vcpu_sev_ioctl(vcpu, KVM_SEV_SNP_SET_VCPU_STATE, &state);
+
+	memset(&state, 0, sizeof(state));
+	snp_get_vcpu_state(vcpu, &state);
+	TEST_ASSERT_EQ(state.valid_fields,
+		       KVM_SEV_SNP_VCPU_STATE_VMSA_VALID |
+		       KVM_SEV_SNP_VCPU_STATE_GHCB_VALID);
+	TEST_ASSERT_EQ(state.vmsa_gpa, private_gpa);
+	TEST_ASSERT_EQ(state.ghcb_gpa, BIT_ULL(40));
+
+	kvm_vm_free(vm);
+}
+
+static void test_direct_vmsa_capability(void)
+{
+	struct kvm_enable_cap cap = {
+		.cap = KVM_CAP_SNP_DIRECT_VMSA,
+	};
+	struct vm_shape shape = {
+		.mode = VM_MODE_DEFAULT,
+		.type = KVM_X86_SNP_VM,
+	};
+	struct kvm_vm *vm;
+
+	vm = vm_create_barebones();
+	TEST_ASSERT_EQ(__vm_enable_cap(vm, KVM_CAP_SNP_DIRECT_VMSA, 0), -1);
+	TEST_ASSERT_EQ(errno, EINVAL);
+	kvm_vm_free(vm);
+
+	vm = __vm_create(shape, 1, 0);
+	TEST_ASSERT_EQ(__vm_enable_cap(vm, KVM_CAP_SNP_DIRECT_VMSA, 1), -1);
+	TEST_ASSERT_EQ(errno, EINVAL);
+	cap.args[3] = 1;
+	TEST_ASSERT_EQ(__vm_ioctl(vm, KVM_ENABLE_CAP, &cap), -1);
+	TEST_ASSERT_EQ(errno, EINVAL);
+	cap.args[3] = 0;
+	cap.flags = 1;
+	TEST_ASSERT_EQ(__vm_ioctl(vm, KVM_ENABLE_CAP, &cap), -1);
+	TEST_ASSERT_EQ(errno, EINVAL);
+	vm_enable_cap(vm, KVM_CAP_SNP_DIRECT_VMSA, 0);
+	vm_vcpu_add(vm, 0, guest_legacy_entry);
+	TEST_ASSERT_EQ(__vm_enable_cap(vm, KVM_CAP_SNP_DIRECT_VMSA, 0), -1);
+	TEST_ASSERT_EQ(errno, EINVAL);
+	kvm_vm_free(vm);
+}
+
+static void test_legacy_launch(void)
+{
+	struct kvm_vcpu *vcpu;
+	struct kvm_vm *vm;
+	gva_t marker_gva;
+	u64 *marker;
+
+	vm = vm_sev_create_with_one_vcpu(KVM_X86_SNP_VM, guest_legacy_entry, &vcpu);
+	marker_gva = vm_alloc_shared(vm, PAGE_SIZE, KVM_UTIL_MIN_VADDR,
+				     MEM_REGION_TEST_DATA);
+	marker = addr_gva2hva(vm, marker_gva);
+	vcpu_args_set(vcpu, 1, marker_gva);
+	vm_sev_launch(vm, snp_default_policy(), NULL);
+	*marker = 0;
+	assert_vcpu_terminated(vcpu);
+	TEST_ASSERT_EQ(*marker, LEGACY_MARKER);
+	kvm_vm_free(vm);
+}
+
+int main(int argc, char *argv[])
+{
+	TEST_REQUIRE(kvm_check_cap(KVM_CAP_SNP_VCPU_STATE));
+	TEST_REQUIRE(kvm_check_cap(KVM_CAP_SNP_DIRECT_VMSA));
+	TEST_ASSERT(sizeof(struct test_vmsa) <= PAGE_SIZE, "VMSA structure is too large");
+
+	test_direct_vmsa_capability();
+	test_invalid_requests();
+	test_direct_vmsa();
+	test_legacy_launch();
+	return 0;
+}
-- 
2.53.0


      parent reply	other threads:[~2026-09-08 10:33 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-08 10:33 [PATCH v2 0/8] KVM: SVM: Support direct setting of VMSA for SEV-SNP guests Jörg Rödel
2026-09-08 10:33 ` [PATCH v2 1/8] KVM: SEV: Document SNP direct VMSA userspace ABI Jörg Rödel
2026-09-08 10:33 ` [PATCH v2 2/8] KVM: SVM: Implement GET_AP_APIC_IDS NAE event Jörg Rödel
2026-09-08 10:33 ` [PATCH v2 3/8] KVM: SVM: Hold SRCU while reloading guest-owned VMSAs Jörg Rödel
2026-09-08 10:33 ` [PATCH v2 4/8] KVM: SEV: Add direct VMSA capability Jörg Rödel
2026-09-08 10:33 ` [PATCH v2 5/8] KVM: SEV: Allow VMSA pages in SNP launch updates Jörg Rödel
2026-09-08 10:33 ` [PATCH v2 6/8] KVM: SEV: Add SNP vCPU state get and set commands Jörg Rödel
2026-09-15 11:30   ` Tony Lindgren
2026-09-08 10:33 ` [PATCH v2 7/8] KVM: selftests: Test the SNP APIC-ID-list GHCB request Jörg Rödel
2026-09-08 10:33 ` Jörg Rödel [this message]

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=20260908103338.427254-9-joro@8bytes.org \
    --to=joro@8bytes.org \
    --cc=James.Bottomley@HansenPartnership.com \
    --cc=coconut-svsm@lists.linux.dev \
    --cc=joerg.roedel@amd.com \
    --cc=kvm@vger.kernel.org \
    --cc=liam.merwick@oracle.com \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-kselftest@vger.kernel.org \
    --cc=michael.roth@amd.com \
    --cc=ninadnaik07@gmail.com \
    --cc=pbonzini@redhat.com \
    --cc=seanjc@google.com \
    --cc=thomas.lendacky@amd.com \
    --cc=vannapurve@google.com \
    /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®