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