From: Joerg Roedel <[email protected]>

Exercise the GET_AP_APIC_IDS NAE event from an SNP guest using a test-local
GHCB encoder.  Verify the exact count and APIC IDs, missing RAX validity,
zero-sized requests, invalid GPAs, and the 1024-vCPU descriptor boundary
when supported.

Assisted-by: LLM
Signed-off-by: Joerg Roedel <[email protected]>
---
 tools/testing/selftests/kvm/Makefile.kvm      |   1 +
 .../selftests/kvm/x86/sev_snp_apic_id_test.c  | 180 ++++++++++++++++++
 2 files changed, 181 insertions(+)
 create mode 100644 tools/testing/selftests/kvm/x86/sev_snp_apic_id_test.c

diff --git a/tools/testing/selftests/kvm/Makefile.kvm 
b/tools/testing/selftests/kvm/Makefile.kvm
index 96bab7002d39..92eb3dff13ff 100644
--- a/tools/testing/selftests/kvm/Makefile.kvm
+++ b/tools/testing/selftests/kvm/Makefile.kvm
@@ -149,6 +149,7 @@ TEST_GEN_PROGS_x86 += x86/xen_vmcall_test
 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_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/x86/sev_snp_apic_id_test.c 
b/tools/testing/selftests/kvm/x86/sev_snp_apic_id_test.c
new file mode 100644
index 000000000000..8ad9c15091c2
--- /dev/null
+++ b/tools/testing/selftests/kvm/x86/sev_snp_apic_id_test.c
@@ -0,0 +1,180 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <stddef.h>
+#include <stdint.h>
+
+#include "kvm_util.h"
+#include "processor.h"
+#include "sev.h"
+#include "svm_util.h"
+
+#define GHCB_SAVE_RAX_OFFSET           0x1f8
+#define GHCB_SAVE_SW_EXIT_CODE_OFFSET  0x390
+#define GHCB_SAVE_SW_EXIT_INFO_1_OFFSET        0x398
+#define GHCB_SAVE_SW_EXIT_INFO_2_OFFSET        0x3a0
+#define GHCB_SAVE_VALID_BITMAP_OFFSET  0x3f0
+
+#define GHCB_MSR_REG_GPA_REQ           0x012
+#define GHCB_MSR_REG_GPA_RESP          0x013
+#define GHCB_MSR_INFO_MASK             GENMASK_ULL(11, 0)
+
+#define GHCB_HV_RESP_MALFORMED_INPUT   2
+#define GHCB_ERR_MISSING_INPUT         4
+#define GHCB_ERR_INVALID_INPUT         5
+
+struct apic_id_desc {
+       u32 nr_entries;
+       u32 apic_ids[];
+};
+
+struct apic_id_results {
+       u64 missing_info1;
+       u64 missing_info2;
+       u64 zero_info1;
+       u64 zero_info2;
+       u64 zero_rax;
+       u64 invalid_info1;
+       u64 invalid_info2;
+       u64 valid_info1;
+       u64 valid_info2;
+       u32 nr_entries;
+       u32 first_apic_id;
+       u32 last_apic_id;
+};
+
+static void ghcb_set_field(void *ghcb, size_t offset, u64 value, bool valid)
+{
+       u8 *valid_bitmap = ghcb + GHCB_SAVE_VALID_BITMAP_OFFSET;
+
+       *(u64 *)(ghcb + offset) = value;
+       if (valid)
+               valid_bitmap[(offset / sizeof(u64)) / 8] |=
+                       BIT((offset / sizeof(u64)) % 8);
+}
+
+static u64 ghcb_get_field(void *ghcb, size_t offset)
+{
+       return *(u64 *)(ghcb + offset);
+}
+
+static void do_get_apic_ids(void *ghcb, gpa_t buffer_gpa, u64 pages,
+                           bool rax_valid)
+{
+       memset(ghcb, 0, PAGE_SIZE);
+       ghcb_set_field(ghcb, GHCB_SAVE_SW_EXIT_CODE_OFFSET,
+                      SVM_VMGEXIT_GET_APIC_IDS, true);
+       ghcb_set_field(ghcb, GHCB_SAVE_SW_EXIT_INFO_1_OFFSET, buffer_gpa, true);
+       ghcb_set_field(ghcb, GHCB_SAVE_SW_EXIT_INFO_2_OFFSET, 0, true);
+       ghcb_set_field(ghcb, GHCB_SAVE_RAX_OFFSET, pages, rax_valid);
+       vmgexit();
+}
+
+static void guest_code(void *ghcb, gpa_t ghcb_gpa, void *list,
+                      gpa_t list_gpa, struct apic_id_results *results,
+                      u64 expected_vcpus)
+{
+       struct apic_id_desc *desc = list;
+       u64 msr;
+
+       wrmsr(MSR_AMD64_SEV_ES_GHCB,
+             (ghcb_gpa >> PAGE_SHIFT) << PAGE_SHIFT | GHCB_MSR_REG_GPA_REQ);
+       vmgexit();
+       msr = rdmsr(MSR_AMD64_SEV_ES_GHCB);
+       if ((msr & GHCB_MSR_INFO_MASK) != GHCB_MSR_REG_GPA_RESP)
+               goto terminate;
+
+       wrmsr(MSR_AMD64_SEV_ES_GHCB, ghcb_gpa);
+
+       do_get_apic_ids(ghcb, list_gpa, 1, false);
+       results->missing_info1 = ghcb_get_field(ghcb, 
GHCB_SAVE_SW_EXIT_INFO_1_OFFSET);
+       results->missing_info2 = ghcb_get_field(ghcb, 
GHCB_SAVE_SW_EXIT_INFO_2_OFFSET);
+
+       do_get_apic_ids(ghcb, list_gpa, 0, true);
+       results->zero_info1 = ghcb_get_field(ghcb, 
GHCB_SAVE_SW_EXIT_INFO_1_OFFSET);
+       results->zero_info2 = ghcb_get_field(ghcb, 
GHCB_SAVE_SW_EXIT_INFO_2_OFFSET);
+       results->zero_rax = ghcb_get_field(ghcb, GHCB_SAVE_RAX_OFFSET);
+
+       do_get_apic_ids(ghcb, BIT_ULL(52), 2, true);
+       results->invalid_info1 = ghcb_get_field(ghcb, 
GHCB_SAVE_SW_EXIT_INFO_1_OFFSET);
+       results->invalid_info2 = ghcb_get_field(ghcb, 
GHCB_SAVE_SW_EXIT_INFO_2_OFFSET);
+
+       do_get_apic_ids(ghcb, list_gpa, 2, true);
+       results->valid_info1 = ghcb_get_field(ghcb, 
GHCB_SAVE_SW_EXIT_INFO_1_OFFSET);
+       results->valid_info2 = ghcb_get_field(ghcb, 
GHCB_SAVE_SW_EXIT_INFO_2_OFFSET);
+       results->nr_entries = desc->nr_entries;
+       results->first_apic_id = desc->apic_ids[0];
+       results->last_apic_id = desc->apic_ids[expected_vcpus - 1];
+
+terminate:
+       wrmsr(MSR_AMD64_SEV_ES_GHCB, GHCB_MSR_TERM_REQ);
+       vmgexit();
+}
+
+static void run_apic_id_test(unsigned int nr_vcpus)
+{
+       struct apic_id_results *results;
+       struct kvm_vcpu *vcpu;
+       struct kvm_vm *vm;
+       gva_t ghcb_gva, list_gva, results_gva;
+       gpa_t ghcb_gpa, list_gpa;
+       unsigned int i;
+
+       kvm_set_files_rlimit(nr_vcpus);
+       vm = vm_sev_create_with_one_vcpu(KVM_X86_SNP_VM, guest_code, &vcpu);
+       for (i = 1; i < nr_vcpus; i++)
+               __vm_vcpu_add(vm, i);
+
+       ghcb_gva = vm_alloc_shared(vm, PAGE_SIZE, KVM_UTIL_MIN_VADDR,
+                                  MEM_REGION_TEST_DATA);
+       list_gva = vm_alloc_shared(vm, 2 * PAGE_SIZE, KVM_UTIL_MIN_VADDR,
+                                  MEM_REGION_TEST_DATA);
+       results_gva = vm_alloc_shared(vm, PAGE_SIZE, KVM_UTIL_MIN_VADDR,
+                                     MEM_REGION_TEST_DATA);
+       ghcb_gpa = addr_gva2gpa(vm, ghcb_gva);
+       list_gpa = addr_gva2gpa(vm, list_gva);
+       results = addr_gva2hva(vm, results_gva);
+
+       vcpu_args_set(vcpu, 6, ghcb_gva, ghcb_gpa, list_gva, list_gpa,
+                     results_gva, nr_vcpus);
+       memset(addr_gva2hva(vm, ghcb_gva), 0, PAGE_SIZE);
+       memset(addr_gva2hva(vm, list_gva), 0, 2 * PAGE_SIZE);
+       memset(results, 0, PAGE_SIZE);
+       vm_sev_launch(vm, snp_default_policy(), NULL);
+
+       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);
+
+       TEST_ASSERT_EQ(results->missing_info1, GHCB_HV_RESP_MALFORMED_INPUT);
+       TEST_ASSERT_EQ(results->missing_info2, GHCB_ERR_MISSING_INPUT);
+       TEST_ASSERT_EQ(results->zero_info1, 0);
+       TEST_ASSERT_EQ(results->zero_info2, 0);
+       TEST_ASSERT_EQ(results->zero_rax,
+                      DIV_ROUND_UP(sizeof(struct apic_id_desc) + nr_vcpus * 
sizeof(u32),
+                                   PAGE_SIZE));
+       TEST_ASSERT_EQ(results->invalid_info1, GHCB_HV_RESP_MALFORMED_INPUT);
+       TEST_ASSERT_EQ(results->invalid_info2, GHCB_ERR_INVALID_INPUT);
+       TEST_ASSERT_EQ(results->valid_info1, 0);
+       TEST_ASSERT_EQ(results->valid_info2, 0);
+       TEST_ASSERT_EQ(results->nr_entries, nr_vcpus);
+       TEST_ASSERT_EQ(results->first_apic_id, 0);
+       TEST_ASSERT_EQ(results->last_apic_id, nr_vcpus - 1);
+
+       kvm_vm_free(vm);
+}
+
+int main(int argc, char *argv[])
+{
+       unsigned int max_vcpus;
+
+       TEST_REQUIRE(kvm_check_cap(KVM_CAP_VM_TYPES) & BIT(KVM_X86_SNP_VM));
+       run_apic_id_test(2);
+
+       /* 1024 IDs cross the one-page descriptor boundary. */
+       max_vcpus = kvm_check_cap(KVM_CAP_MAX_VCPUS);
+       if (max_vcpus >= 1024)
+               run_apic_id_test(1024);
+       else
+               pr_info("Skipping vCPU-count boundary test (max vCPUs: %u)\n", 
max_vcpus);
+
+       return 0;
+}
-- 
2.53.0


Reply via email to