public inbox for kvm@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH] KVM: x86: Add support for cmpxchg16b emulation
@ 2026-03-06 10:20 Sairaj Kodilkar
  2026-03-06 15:38 ` Sean Christopherson
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Sairaj Kodilkar @ 2026-03-06 10:20 UTC (permalink / raw)
  To: H. Peter Anvin, Borislav Petkov, Dave Hansen, Ingo Molnar,
	Paolo Bonzini, Sean Christopherson, Thomas Gleixner, kvm,
	linux-kernel, x86
  Cc: suravee.suthikulpanit, vasant.hegde, nikunj.dadhania,
	Manali.Shukla, Sairaj Kodilkar

AMD and Intel both provides support for 128 bit cmpxchg operands using
cmpxchg8b/cmpxchg16b instructions (opcode 0FC7). However, kvm does not
support emulating cmpxchg16b (i.e when destination memory is 128 bit and
REX.W = 1) which causes emulation failure when QEMU guest performs a
cmpxchg16b on a memory region setup as a IO.

Hence extend cmpxchg8b to perform cmpxchg16b when the destination memory
is 128 bit.

Signed-off-by: Sairaj Kodilkar <sarunkod@amd.com>
---
Background:

The AMD IOMMU driver writes 256-bit device table entries with two
128-bit cmpxchg operations. For guests using hardware-accelerated
vIOMMU (still in progress), QEMU traps device table accesses to set up
nested page tables. Without 128-bit cmpxchg emulation, KVM cannot
handle these traps and DTE access emulation fails.

QEMU implementation that traps DTE accesses:
https://github.com/AMDESE/qemu-iommu/blob/wip/for_iommufd_hw_queue-v8_amd_viommu_20260106/hw/i386/amd_viommu.c#L517
---
 arch/x86/kvm/emulate.c     | 48 +++++++++++++++++++++++++++++++-------
 arch/x86/kvm/kvm_emulate.h |  1 +
 2 files changed, 41 insertions(+), 8 deletions(-)

diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
index c8e292e9a24d..e1a08cd3274b 100644
--- a/arch/x86/kvm/emulate.c
+++ b/arch/x86/kvm/emulate.c
@@ -2188,17 +2188,18 @@ static int em_call_near_abs(struct x86_emulate_ctxt *ctxt)
 	return rc;
 }
 
-static int em_cmpxchg8b(struct x86_emulate_ctxt *ctxt)
+static int __handle_cmpxchg8b(struct x86_emulate_ctxt *ctxt)
 {
-	u64 old = ctxt->dst.orig_val64;
+	u64 old64 = ctxt->dst.orig_val64;
 
-	if (ctxt->dst.bytes == 16)
+	/* Use of the REX.W prefix promotes operation to 128 bits */
+	if (ctxt->rex_bits & REX_W)
 		return X86EMUL_UNHANDLEABLE;
 
-	if (((u32) (old >> 0) != (u32) reg_read(ctxt, VCPU_REGS_RAX)) ||
-	    ((u32) (old >> 32) != (u32) reg_read(ctxt, VCPU_REGS_RDX))) {
-		*reg_write(ctxt, VCPU_REGS_RAX) = (u32) (old >> 0);
-		*reg_write(ctxt, VCPU_REGS_RDX) = (u32) (old >> 32);
+	if (((u32) (old64 >> 0) != (u32) reg_read(ctxt, VCPU_REGS_RAX)) ||
+	    ((u32) (old64 >> 32) != (u32) reg_read(ctxt, VCPU_REGS_RDX))) {
+		*reg_write(ctxt, VCPU_REGS_RAX) = (u32) (old64 >> 0);
+		*reg_write(ctxt, VCPU_REGS_RDX) = (u32) (old64 >> 32);
 		ctxt->eflags &= ~X86_EFLAGS_ZF;
 	} else {
 		ctxt->dst.val64 = ((u64)reg_read(ctxt, VCPU_REGS_RCX) << 32) |
@@ -2209,6 +2210,37 @@ static int em_cmpxchg8b(struct x86_emulate_ctxt *ctxt)
 	return X86EMUL_CONTINUE;
 }
 
+static int __handle_cmpxchg16b(struct x86_emulate_ctxt *ctxt)
+{
+	__uint128_t old128 = ctxt->dst.val128;
+
+	/* Use of the REX.W prefix promotes operation to 128 bits */
+	if (!(ctxt->rex_bits & REX_W))
+		return X86EMUL_UNHANDLEABLE;
+
+	if (((u64) (old128 >> 0) != (u64) reg_read(ctxt, VCPU_REGS_RAX)) ||
+	    ((u64) (old128 >> 64) != (u64) reg_read(ctxt, VCPU_REGS_RDX))) {
+		*reg_write(ctxt, VCPU_REGS_RAX) = (u64) (old128 >> 0);
+		*reg_write(ctxt, VCPU_REGS_RDX) = (u64) (old128 >> 64);
+		ctxt->eflags &= ~X86_EFLAGS_ZF;
+	} else {
+		ctxt->dst.val128 =
+			((__uint128_t) reg_read(ctxt, VCPU_REGS_RCX) << 64) |
+			(u64) reg_read(ctxt, VCPU_REGS_RBX);
+
+		ctxt->eflags |= X86_EFLAGS_ZF;
+	}
+	return X86EMUL_CONTINUE;
+}
+
+static int em_cmpxchgxb(struct x86_emulate_ctxt *ctxt)
+{
+	if (ctxt->dst.bytes == 16)
+		return __handle_cmpxchg16b(ctxt);
+
+	return __handle_cmpxchg8b(ctxt);
+}
+
 static int em_ret(struct x86_emulate_ctxt *ctxt)
 {
 	int rc;
@@ -4097,7 +4129,7 @@ static const struct gprefix pfx_0f_c7_7 = {
 
 
 static const struct group_dual group9 = { {
-	N, I(DstMem64 | Lock | PageTable, em_cmpxchg8b), N, N, N, N, N, N,
+	N, I(DstMem64 | Lock | PageTable, em_cmpxchgxb), N, N, N, N, N, N,
 }, {
 	N, N, N, N, N, N, N,
 	GP(0, &pfx_0f_c7_7),
diff --git a/arch/x86/kvm/kvm_emulate.h b/arch/x86/kvm/kvm_emulate.h
index fb3dab4b5a53..a1e95c57d40e 100644
--- a/arch/x86/kvm/kvm_emulate.h
+++ b/arch/x86/kvm/kvm_emulate.h
@@ -268,6 +268,7 @@ struct operand {
 	union {
 		unsigned long val;
 		u64 val64;
+		__uint128_t val128;
 		char valptr[sizeof(avx256_t)];
 		sse128_t vec_val;
 		avx256_t vec_val2;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-03-12 11:46 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-06 10:20 [PATCH] KVM: x86: Add support for cmpxchg16b emulation Sairaj Kodilkar
2026-03-06 15:38 ` Sean Christopherson
2026-03-11 11:18   ` Sairaj Kodilkar
2026-03-12 11:46   ` Paolo Bonzini
2026-03-07  2:39 ` kernel test robot
2026-03-07  5:40 ` kernel test robot

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