[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250923050942.206116-11-Neeraj.Upadhyay@amd.com>
Date: Tue, 23 Sep 2025 10:39:17 +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 10/35] KVM: selftests: Add MSR access support for SEV-ES guests
For SEV-ES guests, RDMSR/WRMSR of the intercepted MSRs trap and
generate a #VC (VMM Communication) exception. The guest must then
handle this exception and communicate the desired MSR operation to the
hypervisor using the GHCB protocol.
Add the necessary selftest infrastructure to support this flow, enabling
tests to correctly perform MSR operations from within an SEV-ES guest.
Two mechanisms are introduced:
1. #VC Exception Handling: Provide a #VC handler, which inspects the
trapping instruction and the register state and does a GHCB
request to forward the MSR operation to the host .
2. Paravirtual Interface: Provide a direct, "paravirtual" way to test
the MSR GHCB protocol. This allows test code to request an MSR read
or write without needing to execute a trapping instruction,
simplifying certain test scenarios.
Signed-off-by: Neeraj Upadhyay <Neeraj.Upadhyay@....com>
---
tools/testing/selftests/kvm/include/x86/sev.h | 2 +
tools/testing/selftests/kvm/lib/x86/sev.c | 83 ++++++++++++++++++-
2 files changed, 84 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/kvm/include/x86/sev.h b/tools/testing/selftests/kvm/include/x86/sev.h
index a4fbea0d3562..9c1fe6be5e68 100644
--- a/tools/testing/selftests/kvm/include/x86/sev.h
+++ b/tools/testing/selftests/kvm/include/x86/sev.h
@@ -165,4 +165,6 @@ static inline void snp_launch_update_data(struct kvm_vm *vm, vm_paddr_t gpa,
void sev_es_ucall_port_write(uint32_t port, uint64_t data);
+void sev_es_vc_handler(struct ex_regs *regs);
+void sev_es_pv_msr_rw(uint64_t msr, uint64_t *data, bool write);
#endif /* SELFTEST_KVM_SEV_H */
diff --git a/tools/testing/selftests/kvm/lib/x86/sev.c b/tools/testing/selftests/kvm/lib/x86/sev.c
index edefba7f49ce..57ae5a388b8c 100644
--- a/tools/testing/selftests/kvm/lib/x86/sev.c
+++ b/tools/testing/selftests/kvm/lib/x86/sev.c
@@ -12,7 +12,8 @@
#define IOIO_DATA_8 (1 << 4)
#define IOIO_REP (1 << 3)
-#define SW_EXIT_CODE_IOIO 0x7b
+#define SW_EXIT_CODE_IOIO 0x7b
+#define SW_EXIT_CODE_MSR 0x7c
struct ghcb_entry {
struct ghcb ghcb;
@@ -356,3 +357,83 @@ void sev_es_ucall_port_write(uint32_t port, uint64_t data)
ghcb_free(entry);
}
+
+static void __sev_es_msr_rw(struct ghcb_entry *entry, uint64_t msr,
+ uint32_t *low, uint32_t *high, bool write)
+{
+ uint64_t exitinfo1 = write ? 1 : 0;
+ struct ghcb *ghcb = &entry->ghcb;
+ uint32_t ret;
+
+ ghcb_set_sw_exit_code(ghcb, SW_EXIT_CODE_MSR);
+ ghcb_set_sw_exit_info_1(ghcb, exitinfo1);
+ ghcb_set_sw_exit_info_2(ghcb, 0);
+
+ ghcb_set_rcx(ghcb, msr);
+ if (write) {
+ ghcb_set_rax(ghcb, *low);
+ ghcb_set_rdx(ghcb, *high);
+ }
+
+ do_vmg_exit(entry->gpa);
+
+ ret = ghcb->save.sw_exit_info_1 & 0xffffffff;
+ __GUEST_ASSERT(!ret, "%smsr failed, ret: %u", write ? "wr" : "rd", ret);
+
+ if (!write) {
+ *low = ghcb->save.rax;
+ *high = ghcb->save.rdx;
+ }
+}
+
+void sev_es_pv_msr_rw(uint64_t msr, uint64_t *data, bool write)
+{
+ struct ghcb_entry *entry;
+ uint32_t low, high;
+
+ entry = ghcb_alloc();
+ register_ghcb_page(entry->gpa);
+
+ if (write) {
+ low = *data & ((1ULL << 32) - 1);
+ high = *data >> 32;
+ }
+ __sev_es_msr_rw(entry, msr, &low, &high, write);
+
+ if (!write)
+ *data = low | (uint64_t)high << 32;
+
+ ghcb_free(entry);
+}
+
+static void sev_es_vc_msr_handler(struct ex_regs *regs)
+{
+ struct ghcb_entry *entry;
+ bool write;
+
+ /* wrmsr encoding has second byte = 0x30 */
+ write = (*((char *)regs->rip + 1) == 0x30);
+
+ entry = ghcb_alloc();
+ register_ghcb_page(entry->gpa);
+
+ __sev_es_msr_rw(entry, regs->rcx, (uint32_t *)®s->rax,
+ (uint32_t *)®s->rdx, write);
+
+ ghcb_free(entry);
+}
+
+void sev_es_vc_handler(struct ex_regs *regs)
+{
+ uint64_t exit_code = regs->error_code;
+
+ switch (exit_code) {
+ case SVM_EXIT_MSR:
+ sev_es_vc_msr_handler(regs);
+ /* rdmsr/wrmsr instruction size = 2 */
+ regs->rip += 2;
+ break;
+ default:
+ __GUEST_ASSERT(0, "No VC handler\n");
+ }
+}
--
2.34.1
Powered by blists - more mailing lists