All of lore.kernel.org
 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: 12+ 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:51   ` sashiko-bot
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:53   ` sashiko-bot
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-08 10:49   ` sashiko-bot
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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.