* [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 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.