From: Joerg Roedel <[email protected]>

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 <[email protected]>
---
 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


Reply via email to