lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250923050942.206116-21-Neeraj.Upadhyay@amd.com>
Date: Tue, 23 Sep 2025 10:39:27 +0530
From: Neeraj Upadhyay <Neeraj.Upadhyay@....com>
To: <kvm@...r.kernel.org>, <seanjc@...gle.com>, <pbonzini@...hat.com>
CC: <linux-kernel@...r.kernel.org>, <Thomas.Lendacky@....com>,
	<nikunj@....com>, <Santosh.Shukla@....com>, <Vasant.Hegde@....com>,
	<Suravee.Suthikulpanit@....com>, <bp@...en8.de>, <David.Kaplan@....com>,
	<huibo.wang@....com>, <naveen.rao@....com>, <pgonda@...gle.com>,
	<linux-kselftest@...r.kernel.org>, <shuah@...nel.org>, <tiala@...rosoft.com>
Subject: [RFC PATCH v2 20/35] KVM: selftests: Add instruction decoding for movabs instructions

Compilers can generate movabs instructions for MMIO accesses,
when accessing fixed addresses like the local APIC base. The movabs
instruction is unique in that it moves data between the accumulator (RAX)
and a memory location specified by an absolute 64-bit address encoded
directly in the instruction stream.

The existing MMIO #VC handler in the SEV-ES selftest was unable to
process these instructions because its decoding logic relied on ModR/M
and SIB bytes, which movabs does not use. This would cause MMIO
emulation to fail for such instructions.

Extend insn_decode_mmio() to recognize the movabs opcodes (0xa1,
0xa3). This allows the SEV-ES #VC handler to correctly emulate movabs
instructions, improving the robustness of MMIO testing for
features like the xAPIC.

Signed-off-by: Neeraj Upadhyay <Neeraj.Upadhyay@....com>
---
 .../selftests/kvm/include/x86/insn-eval.h     |  2 ++
 .../testing/selftests/kvm/lib/x86/insn-eval.c |  8 +++++
 tools/testing/selftests/kvm/lib/x86/sev.c     | 35 ++++++++++++++-----
 3 files changed, 36 insertions(+), 9 deletions(-)

diff --git a/tools/testing/selftests/kvm/include/x86/insn-eval.h b/tools/testing/selftests/kvm/include/x86/insn-eval.h
index 0547b622295a..911b7f4e5473 100644
--- a/tools/testing/selftests/kvm/include/x86/insn-eval.h
+++ b/tools/testing/selftests/kvm/include/x86/insn-eval.h
@@ -35,10 +35,12 @@ enum insn_mmio_type {
 	INSN_MMIO_DECODE_FAILED,
 	INSN_MMIO_WRITE,
 	INSN_MMIO_WRITE_IMM,
+	INSN_MMIO_WRITE_MOV_ABS,
 	INSN_MMIO_READ,
 	INSN_MMIO_READ_ZERO_EXTEND,
 	INSN_MMIO_READ_SIGN_EXTEND,
 	INSN_MMIO_MOVS,
+	INSN_MMIO_READ_MOV_ABS,
 };
 
 enum insn_mmio_type insn_decode_mmio(struct insn *insn, int *bytes);
diff --git a/tools/testing/selftests/kvm/lib/x86/insn-eval.c b/tools/testing/selftests/kvm/lib/x86/insn-eval.c
index 369530badba9..9d370b1cef84 100644
--- a/tools/testing/selftests/kvm/lib/x86/insn-eval.c
+++ b/tools/testing/selftests/kvm/lib/x86/insn-eval.c
@@ -1224,6 +1224,14 @@ enum insn_mmio_type insn_decode_mmio(struct insn *insn, int *bytes)
 			break;
 		}
 		break;
+	case 0xa1:
+		type = INSN_MMIO_READ_MOV_ABS;
+		*bytes = insn->opnd_bytes;
+		break;
+	case 0xa3:
+		type = INSN_MMIO_WRITE_MOV_ABS;
+		*bytes = insn->opnd_bytes;
+		break;
 	}
 
 	return type;
diff --git a/tools/testing/selftests/kvm/lib/x86/sev.c b/tools/testing/selftests/kvm/lib/x86/sev.c
index 610342b8e479..1e0719dfd6b0 100644
--- a/tools/testing/selftests/kvm/lib/x86/sev.c
+++ b/tools/testing/selftests/kvm/lib/x86/sev.c
@@ -469,9 +469,11 @@ void sev_es_pv_mmio_rw(uint32_t *reg_gpa, uint32_t *data, bool write)
 }
 
 static void do_mmio(struct ghcb_entry *entry, struct ex_regs *regs,
-		struct insn *insn, unsigned int bytes, bool read)
+		struct insn *insn, unsigned int bytes, bool read,
+		void *ref)
 {
-	void *ref = insn_get_addr_ref(insn, regs);
+	if (!ref)
+		ref = insn_get_addr_ref(insn, regs);
 
 	register_ghcb_page(entry->gpa);
 	__sev_es_hv_mmio_rw(entry, ref, bytes, !read);
@@ -600,11 +602,12 @@ static void sev_es_vc_mmio_handler(struct ex_regs *regs)
 	char buffer[MAX_INSN_SIZE];
 	struct ghcb_entry *entry;
 	enum insn_mmio_type mmio;
-	unsigned long *reg_data;
+	unsigned long *reg_data = NULL;
 	unsigned int bytes;
 	struct ghcb *ghcb;
 	uint8_t sign_byte;
 	struct insn insn;
+	void *abs_ref;
 	int ret;
 
 	memcpy(buffer, (uint8_t *)regs->rip, MAX_INSN_SIZE);
@@ -616,7 +619,9 @@ static void sev_es_vc_mmio_handler(struct ex_regs *regs)
 	mmio = insn_decode_mmio(&insn, (int *)&bytes);
 	__GUEST_ASSERT(!(mmio == INSN_MMIO_DECODE_FAILED), " MMIO decode failed\n");
 
-	if (mmio != INSN_MMIO_WRITE_IMM && mmio != INSN_MMIO_MOVS) {
+	if (mmio == INSN_MMIO_WRITE_MOV_ABS || mmio == INSN_MMIO_READ_MOV_ABS) {
+		reg_data = &regs->rax;
+	} else if (mmio != INSN_MMIO_WRITE_IMM && mmio != INSN_MMIO_MOVS) {
 		reg_data = insn_get_modrm_reg_ptr(&insn, regs);
 		__GUEST_ASSERT(reg_data, "insn_get_modrm_reg_ptr failed\n");
 	}
@@ -627,25 +632,37 @@ static void sev_es_vc_mmio_handler(struct ex_regs *regs)
 	switch (mmio) {
 	case INSN_MMIO_WRITE:
 		memcpy(ghcb->shared_buffer, reg_data, bytes);
-		do_mmio(entry, regs, &insn, bytes, false);
+		do_mmio(entry, regs, &insn, bytes, false, NULL);
 		break;
 	case INSN_MMIO_WRITE_IMM:
 		memcpy(ghcb->shared_buffer, insn.immediate1.bytes, bytes);
-		do_mmio(entry, regs, &insn, bytes, false);
+		do_mmio(entry, regs, &insn, bytes, false, NULL);
+		break;
+	case INSN_MMIO_WRITE_MOV_ABS:
+		abs_ref = (void *)*(uint64_t *)((uint8_t *)regs->rip + 1);
+		memcpy(ghcb->shared_buffer, reg_data, bytes);
+		do_mmio(entry, regs, &insn, bytes, false, abs_ref);
 		break;
 	case INSN_MMIO_READ:
-		do_mmio(entry, regs, &insn, bytes, true);
+		do_mmio(entry, regs, &insn, bytes, true, NULL);
+		if (bytes == 4)
+			*reg_data = 0;
+		memcpy(reg_data, ghcb->shared_buffer, bytes);
+		break;
+	case INSN_MMIO_READ_MOV_ABS:
+		abs_ref = (void *)*(uint64_t *)((char *)regs->rip + 1);
+		do_mmio(entry, regs, &insn, bytes, true, abs_ref);
 		if (bytes == 4)
 			*reg_data = 0;
 		memcpy(reg_data, ghcb->shared_buffer, bytes);
 		break;
 	case INSN_MMIO_READ_ZERO_EXTEND:
-		do_mmio(entry, regs, &insn, bytes, true);
+		do_mmio(entry, regs, &insn, bytes, true, NULL);
 		memset(reg_data, 0, insn.opnd_bytes);
 		memcpy(reg_data, ghcb->shared_buffer, bytes);
 		break;
 	case INSN_MMIO_READ_SIGN_EXTEND:
-		do_mmio(entry, regs, &insn, bytes, true);
+		do_mmio(entry, regs, &insn, bytes, true, NULL);
 		if (bytes == 1) {
 			uint8_t *val = (uint8_t *)ghcb->shared_buffer;
 
-- 
2.34.1


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ