x86: xsave: Add testcase for emulation of AVX instructions

Extend the XSAVE test to validate KVM's recently added emulation of AVX
VMOVDQA instructions.  Test mem=>reg, reg=>reg, and reg=>mem moves, and
when forced emulation is supported, verify that mixing forced emulation
and native accesses works as expected, e.g. that KVM writes internal state
into hardware.

Link: https://patch.msgid.link/20251114003228.60592-1-pbonzini@redhat.com
Co-developed-by: Sean Christopherson <seanjc@google.com>
Signed-off-by: Sean Christopherson <seanjc@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Message-ID: <20251121180901.271486-9-seanjc@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
diff --git a/x86/xsave.c b/x86/xsave.c
index 0113073..72e9c67 100644
--- a/x86/xsave.c
+++ b/x86/xsave.c
@@ -2,6 +2,69 @@
 #include "desc.h"
 #include "processor.h"
 
+char __attribute__((aligned(32))) v32_1[32];
+char __attribute__((aligned(32))) v32_2[32];
+char __attribute__((aligned(32))) v32_3[32];
+
+static void initialize_avx_buffers(void)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(v32_1); i++)
+		v32_1[i] = (char)rdtsc();
+
+	memset(v32_2, 0, sizeof(v32_2));
+	memset(v32_3, 0, sizeof(v32_3));
+}
+
+#define __TEST_VMOVDQA(reg1, reg2, FEP)					\
+do {									\
+	asm volatile(FEP "vmovdqa v32_1(%%rip), %%" #reg1 "\n"		\
+		     FEP "vmovdqa %%" #reg1 ", %%" #reg2 "\n"		\
+		     FEP "vmovdqa %%" #reg2 ", v32_2(%%rip)\n"		\
+		     "vmovdqa %%" #reg2 ", v32_3(%%rip)\n"		\
+		     ::: "memory", #reg1, #reg2);			\
+									\
+	report(!memcmp(v32_1, v32_2, sizeof(v32_1)),			\
+	       "%s VMOVDQA using " #reg1 " and " #reg2,			\
+	       strlen(FEP) ? "Emulated" : "Native");			\
+	report(!memcmp(v32_1, v32_3, sizeof(v32_1)),			\
+	       "%s VMOVDQA using " #reg1 " and " #reg2,			\
+	       strlen(FEP) ? "Emulated+Native" : "Native");		\
+} while (0)
+
+#define TEST_VMOVDQA(r1, r2)						\
+do {									\
+	initialize_avx_buffers();					\
+									\
+	__TEST_VMOVDQA(ymm##r1, ymm##r2, "");				\
+									\
+	if (is_fep_available)						\
+		__TEST_VMOVDQA(ymm##r1, ymm##r2, KVM_FEP);		\
+} while (0)
+
+static __attribute__((target("avx"))) void test_avx_vmovdqa(void)
+{
+	write_xcr0(XFEATURE_MASK_FP_SSE | XFEATURE_MASK_YMM);
+
+	TEST_VMOVDQA(0, 15);
+	TEST_VMOVDQA(1, 14);
+	TEST_VMOVDQA(2, 13);
+	TEST_VMOVDQA(3, 12);
+	TEST_VMOVDQA(4, 11);
+	TEST_VMOVDQA(5, 10);
+	TEST_VMOVDQA(6, 9);
+	TEST_VMOVDQA(7, 8);
+	TEST_VMOVDQA(8, 7);
+	TEST_VMOVDQA(9, 6);
+	TEST_VMOVDQA(10, 5);
+	TEST_VMOVDQA(11, 4);
+	TEST_VMOVDQA(12, 3);
+	TEST_VMOVDQA(13, 2);
+	TEST_VMOVDQA(14, 2);
+	TEST_VMOVDQA(15, 1);
+}
+
 static void test_unsupported_xcrs(void)
 {
 	u64 ign;
@@ -61,6 +124,9 @@
 	write_xcr0(XFEATURE_MASK_FP_SSE);
 	(void)read_xcr0();
 
+	if (supported_xcr0 & XFEATURE_MASK_YMM)
+		test_avx_vmovdqa();
+
 	printf("\tIllegal tests\n");
 	report(write_xcr0_safe(0) == GP_VECTOR,
 	       "\t\tWrite XCR0 = 0 - expect #GP");