Kernel KVM virtualization development
 help / color / mirror / Atom feed
* [kvm-unit-tests PATCH] x86/emulator64: Add CMPXCHG8B/CMPXCHG16B emulation tests
@ 2026-07-06  6:21 Sairaj Kodilkar
  0 siblings, 0 replies; only message in thread
From: Sairaj Kodilkar @ 2026-07-06  6:21 UTC (permalink / raw)
  To: kvm; +Cc: pbonzini, vasant.hegde, suravee.suthikulpanit, Sairaj Kodilkar

Test KVM instruction emulation for cmpxchg8b/16b (0x0FC7)
with and without the LOCK prefix, and with the KVM fast-emulate
prefix (FEP) when available.  Cover both the compare-succeed (ZF=1) and
compare-fail (ZF=0) outcomes.

Signed-off-by: Sairaj Kodilkar <sarunkod@amd.com>
---
This patch is intended to validate the cmpxchg16b kernel patch[1], which is
required to support the AMD hardware accelerated vIOMMU.

The cmpxchg16b tests hangs (with emulation error) on the kernel which does
not have patch[1]. I am not sure what is the right approach to resolve this
issue, any suggestions on how to resolve this are welcomed.

[1] https://lore.kernel.org/all/20260306102047.29760-1-sarunkod@amd.com/

Base: 1da1819e49fc4938985edca67df669099b4c87a7
---
 x86/emulator64.c | 161 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 161 insertions(+)

diff --git a/x86/emulator64.c b/x86/emulator64.c
index c4df5178147c..3d92053a06d8 100644
--- a/x86/emulator64.c
+++ b/x86/emulator64.c
@@ -245,6 +245,164 @@ static void test_xchg(void *mem)
 	       "xchg reg, r/m (4)");
 }
 
+static void test_cmpxchg8b_success(void *mem)
+{
+	u64 *memq = mem;
+	u64 rax, rbx, rcx, rdx;
+	u32 eax, ebx, ecx, edx;
+	u8 zf;
+
+#define LOCK_PREFIX "lock;"
+#define EMPTY ""
+
+#define TEST_CMPXCHG8B(fep, lock, report_str) do { \
+	asm volatile("mov $0x0123456789abcdef, %%rax\n\t" \
+		     "mov %%rax, (%[memq])\n\t" \
+		     "mov $0x01234567, %%edx\n\t" \
+		     "mov $0x89abcdef, %%eax\n\t" \
+		     "mov $0xfedcba98, %%ecx\n\t" \
+		     "mov $0x76543210, %%ebx\n\t" \
+		     fep lock "cmpxchg8b (%[memq])\n\t" \
+		     "mov %%eax, %[eax]\n\t" \
+		     "mov %%ebx, %[ebx]\n\t" \
+		     "mov %%ecx, %[ecx]\n\t" \
+		     "mov %%edx, %[edx]\n\t" \
+		     "sete %[zf]\n\t" \
+		     : [zf]"=qm"(zf), [eax]"=r"(eax), [ebx]"=r"(ebx), \
+		       [ecx]"=r"(ecx), [edx]"=r"(edx) \
+		     : [memq]"r"(memq) \
+		     : "memory", "eax", "ebx", "ecx", "edx", "cc"); \
+	report(memq[0] == 0xfedcba9876543210 && zf \
+	       && edx == 0x01234567 && eax == 0x89abcdef \
+	       && ecx == 0xfedcba98 && ebx == 0x76543210, \
+	       "cmpxchg8b  mem == edx:eax (" report_str ")"); \
+} while (0)
+
+#define TEST_CMPXCHG16B(fep, lock, report_str) do { \
+	asm volatile("mov $0x0123456789abcdef, %%rax\n\t" \
+		     "mov %%rax, (%[memq])\n\t" \
+		     "mov $0xfedcba9876543210, %%rax\n\t" \
+		     "mov %%rax, 8(%[memq])\n\t" \
+		     "mov $0xfedcba9876543210, %%rdx\n\t" \
+		     "mov $0x0123456789abcdef, %%rax\n\t" \
+		     "mov $0x0123456789abcdef, %%rcx\n\t" \
+		     "mov $0xfedcba9876543210, %%rbx\n\t" \
+		     fep lock "cmpxchg16b (%[memq])\n\t" \
+		     "mov %%rax, %[rax]\n\t" \
+		     "mov %%rbx, %[rbx]\n\t" \
+		     "mov %%rcx, %[rcx]\n\t" \
+		     "mov %%rdx, %[rdx]\n\t" \
+		     "sete %[zf]\n\t" \
+		     : [zf]"=qm"(zf), [rax]"=r"(rax), [rbx]"=r"(rbx), \
+		       [rcx]"=r"(rcx), [rdx]"=r"(rdx) \
+		     : [memq]"r"(memq) \
+		     : "memory", "rax", "rbx", "rcx", "rdx", "cc"); \
+	report(memq[0] == 0xfedcba9876543210 && memq[1] == 0x0123456789abcdef && zf \
+	       && rdx == 0xfedcba9876543210 && rax == 0x0123456789abcdef \
+	       && rcx == 0x0123456789abcdef && rbx == 0xfedcba9876543210, \
+	       "cmpxchg16b mem == rdx:rax (" report_str ")");  \
+} while (0)
+
+	TEST_CMPXCHG8B(EMPTY, EMPTY, "nofep, nolock");
+	TEST_CMPXCHG16B(EMPTY, EMPTY, "nofep, nolock");
+	TEST_CMPXCHG8B(EMPTY, LOCK_PREFIX, "nofep, lock");
+	TEST_CMPXCHG16B(EMPTY, LOCK_PREFIX, "nofep, lock");
+
+	if (!is_fep_available)
+		return;
+
+	memq = vmap(virt_to_phys(alloc_page()), PAGE_SIZE);
+
+	TEST_CMPXCHG8B(KVM_FEP, EMPTY, "fep, nolock");
+	TEST_CMPXCHG16B(KVM_FEP, EMPTY, "fep, nolock");
+	TEST_CMPXCHG8B(KVM_FEP, LOCK_PREFIX, "fep, lock");
+	TEST_CMPXCHG16B(KVM_FEP, LOCK_PREFIX, "fep, lock");
+
+#undef TEST_CMPXCHG8B
+#undef TEST_CMPXCHG16B
+#undef LOCK_PREFIX
+#undef EMPTY
+}
+
+static void test_cmpxchg8b_fail(void *mem)
+{
+	u64 *memq = mem;
+	u64 rax, rbx, rcx, rdx;
+	u32 eax, ebx, ecx, edx;
+	u8 zf;
+
+#define LOCK_PREFIX "lock;"
+#define EMPTY ""
+
+#define TEST_CMPXCHG8B(fep, lock, report_str) do { \
+	asm volatile("mov $0x0123456789abcdef, %%rax\n\t" \
+		     "mov %%rax, (%[memq])\n\t" \
+		     "mov $0xfedcba98, %%edx\n\t" \
+		     "mov $0x76543210, %%eax\n\t" \
+		     "mov $0x01234567, %%ecx\n\t" \
+		     "mov $0x89abcdef, %%ebx\n\t" \
+		     fep lock "cmpxchg8b (%[memq])\n\t" \
+		     "mov %%eax, %[eax]\n\t" \
+		     "mov %%ebx, %[ebx]\n\t" \
+		     "mov %%ecx, %[ecx]\n\t" \
+		     "mov %%edx, %[edx]\n\t" \
+		     "sete %[zf]\n\t" \
+		     : [zf]"=qm"(zf), [eax]"=r"(eax), [ebx]"=r"(ebx), \
+		       [ecx]"=r"(ecx), [edx]"=r"(edx) \
+		     : [memq]"r"(memq) \
+		     : "memory", "eax", "ebx", "ecx", "edx", "cc"); \
+	report(memq[0] == 0x0123456789abcdef && !zf \
+	       && edx == 0x01234567 && eax == 0x89abcdef \
+	       && ecx == 0x01234567 && ebx == 0x89abcdef, \
+	       "cmpxchg8b  mem != edx:eax (" report_str ")"); \
+} while (0)
+
+#define TEST_CMPXCHG16B(fep, lock, report_str) do { \
+	asm volatile("mov $0x0123456789abcdef, %%rax\n\t" \
+		     "mov %%rax, (%[memq])\n\t" \
+		     "mov $0xfedcba9876543210, %%rax\n\t" \
+		     "mov %%rax, 8(%[memq])\n\t" \
+		     "mov $0x0123456789abcdef, %%rdx\n\t" \
+		     "mov $0xfedcba9876543210, %%rax\n\t" \
+		     "mov $0x0123456789abcdef, %%rcx\n\t" \
+		     "mov $0xfedcba9876543210, %%rbx\n\t" \
+		     fep lock "cmpxchg16b (%[memq])\n\t" \
+		     "mov %%rax, %[rax]\n\t" \
+		     "mov %%rbx, %[rbx]\n\t" \
+		     "mov %%rcx, %[rcx]\n\t" \
+		     "mov %%rdx, %[rdx]\n\t" \
+		     "sete %[zf]\n\t" \
+		     : [zf]"=qm"(zf), [rax]"=r"(rax), [rbx]"=r"(rbx), \
+		       [rcx]"=r"(rcx), [rdx]"=r"(rdx) \
+		     : [memq]"r"(memq) \
+		     : "memory", "rax", "rbx", "rcx", "rdx", "cc"); \
+	report(memq[0] == 0x0123456789abcdef && memq[1] == 0xfedcba9876543210 && !zf \
+	       && rdx == 0xfedcba9876543210 && rax == 0x123456789abcdef \
+	       && rcx == 0x0123456789abcdef && rbx == 0xfedcba9876543210, \
+	       "cmpxchg16b mem != rdx:rax (" report_str ")"); \
+} while (0)
+
+	TEST_CMPXCHG8B(EMPTY, EMPTY, "nofep, nolock");
+	TEST_CMPXCHG16B(EMPTY, EMPTY, "nofep, nolock");
+	TEST_CMPXCHG8B(EMPTY, LOCK_PREFIX, "nofep, lock");
+	TEST_CMPXCHG16B(EMPTY, LOCK_PREFIX, "nofep, lock");
+
+	if (!is_fep_available)
+		return;
+
+	memq = vmap(virt_to_phys(alloc_page()), PAGE_SIZE);
+
+	TEST_CMPXCHG8B(KVM_FEP, EMPTY, "fep, nolock");
+	TEST_CMPXCHG16B(KVM_FEP, EMPTY, "fep, nolock");
+	TEST_CMPXCHG8B(KVM_FEP, LOCK_PREFIX, "fep, lock");
+	TEST_CMPXCHG16B(KVM_FEP, LOCK_PREFIX, "fep, lock");
+
+#undef TEST_CMPXCHG8B
+#undef TEST_CMPXCHG16B
+#undef LOCK_PREFIX
+#undef EMPTY
+}
+
 static void test_xadd(void *mem)
 {
 	unsigned long *memq = mem;
@@ -542,6 +700,9 @@ static void test_emulator_64(void *mem)
 	test_pop(mem);
 
 	test_xchg(mem);
+	test_cmpxchg8b_success(mem);
+	test_cmpxchg8b_fail(mem);
+
 	test_xadd(mem);
 
 	test_cr8();
-- 
2.34.1


^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2026-07-06  6:22 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-06  6:21 [kvm-unit-tests PATCH] x86/emulator64: Add CMPXCHG8B/CMPXCHG16B emulation tests Sairaj Kodilkar

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox