[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250228093024.114983-27-Neeraj.Upadhyay@amd.com>
Date: Fri, 28 Feb 2025 15:00:19 +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>
Subject: [RFC PATCH 26/31] KVM: selftests: Add test to verify APIC MSR accesses for SAVIC guest
Extend SAVIC test to verify APIC MSR accesses in SAVIC enabled
mode. Verify the behavior of reads and writes using rdmsr/wrmsr
for various APIC registers. In addition, test whether wrmsr
based writes are propagated to guest backing page.
Signed-off-by: Neeraj Upadhyay <Neeraj.Upadhyay@....com>
---
.../testing/selftests/kvm/include/x86/apic.h | 1 +
.../testing/selftests/kvm/include/x86/savic.h | 3 +
tools/testing/selftests/kvm/lib/x86/savic.c | 7 +-
tools/testing/selftests/kvm/x86/savic_test.c | 192 +++++++++++++++++-
4 files changed, 198 insertions(+), 5 deletions(-)
diff --git a/tools/testing/selftests/kvm/include/x86/apic.h b/tools/testing/selftests/kvm/include/x86/apic.h
index aa3a5d54c404..af555638086f 100644
--- a/tools/testing/selftests/kvm/include/x86/apic.h
+++ b/tools/testing/selftests/kvm/include/x86/apic.h
@@ -33,6 +33,7 @@
#define APIC_SPIV 0xF0
#define APIC_SPIV_FOCUS_DISABLED (1 << 9)
#define APIC_SPIV_APIC_ENABLED (1 << 8)
+#define APIC_ISR 0x100
#define APIC_TMR 0x180
#define APIC_IRR 0x200
#define APIC_ICR 0x300
diff --git a/tools/testing/selftests/kvm/include/x86/savic.h b/tools/testing/selftests/kvm/include/x86/savic.h
index cb432eb527b3..33f19f5e39b3 100644
--- a/tools/testing/selftests/kvm/include/x86/savic.h
+++ b/tools/testing/selftests/kvm/include/x86/savic.h
@@ -6,6 +6,9 @@
#ifndef SELFTEST_KVM_SAVIC_H
#define SELFTEST_KVM_SAVIC_H
+#define APIC_REG_OFF(VEC) (VEC / 32 * 16)
+#define APIC_VEC_POS(VEC) (VEC % 32)
+
struct guest_apic_page;
void guest_apic_pages_init(struct kvm_vm *vm);
diff --git a/tools/testing/selftests/kvm/lib/x86/savic.c b/tools/testing/selftests/kvm/lib/x86/savic.c
index d4c9fcf835ad..c637e486b6e8 100644
--- a/tools/testing/selftests/kvm/lib/x86/savic.c
+++ b/tools/testing/selftests/kvm/lib/x86/savic.c
@@ -9,6 +9,7 @@
#include "kvm_util.h"
#include "sev.h"
#include "ex_regs.h"
+#include "savic.h"
struct apic_page {
u8 apic_regs[PAGE_SIZE];
@@ -45,9 +46,6 @@ enum lapic_lvt_entry {
#define SVM_EXIT_AVIC_UNACCELERATED_ACCESS 0x402
#define SVM_EXIT_AVIC_INCOMPLETE_IPI 0x401
-#define REG_OFF(VEC) (VEC / 32 * 16)
-#define VEC_POS(VEC) (VEC % 32)
-
#define SAVIC_NMI_REQ_OFFSET 0x278
/*
@@ -363,7 +361,8 @@ static void send_ipi(int cpu, int vector, bool nmi)
if (nmi)
savic_write_reg(apic_page, SAVIC_NMI_REQ_OFFSET, 1);
else
- savic_write_reg(apic_page, APIC_IRR + REG_OFF(vector), BIT(VEC_POS(vector)));
+ savic_write_reg(apic_page, APIC_IRR + APIC_REG_OFF(vector),
+ BIT(APIC_VEC_POS(vector)));
}
static bool is_cpu_present(int cpu)
diff --git a/tools/testing/selftests/kvm/x86/savic_test.c b/tools/testing/selftests/kvm/x86/savic_test.c
index ca1d7352bc3e..8cba7a81bce2 100644
--- a/tools/testing/selftests/kvm/x86/savic_test.c
+++ b/tools/testing/selftests/kvm/x86/savic_test.c
@@ -29,6 +29,7 @@ enum savic_test_state {
SAVIC_TEST_STATE(X2APIC_ENABLE),
/* APIC regs state on Secure AVIC enablement */
SAVIC_TEST_STATE(SAVIC_EN),
+ SAVIC_TEST_STATE(SAVIC_APIC_MSR_ACCESSES),
};
/* APIC reg values written by host. */
@@ -288,6 +289,193 @@ static void guest_savic_enabled(int id)
savic_write_apic_regs(apage);
}
+static int savic_wrmsr(uint32_t reg, uint64_t val)
+{
+ switch (reg) {
+ case APIC_LVR:
+ case APIC_LDR:
+ case APIC_ISR:
+ case APIC_TMR:
+ case APIC_IRR:
+ case APIC_TMCCT:
+ x2apic_write_reg_fault(reg, val);
+ return -1;
+ default:
+ x2apic_write_reg(reg, val);
+ break;
+ }
+
+ return 0;
+}
+
+static uint64_t savic_rdmsr(uint32_t reg)
+{
+ uint64_t val;
+ uint32_t msr = APIC_BASE_MSR + (reg >> 4);
+
+ switch (reg) {
+ case APIC_EOI:
+ uint8_t fault = rdmsr_safe(msr, &val);
+
+ __GUEST_ASSERT(fault == GP_VECTOR,
+ "Wanted #GP on RDMSR(%x) = %x, got 0x%x\n",
+ msr, GP_VECTOR, fault);
+ return val;
+ default:
+ return x2apic_read_reg(reg);
+ }
+}
+
+static void guest_verify_host_guest_reg(struct guest_apic_page *apage, uint32_t reg,
+ uint64_t val, char *regname)
+{
+ uint64_t hval, gval, gval2;
+
+ if (savic_wrmsr(reg, val) == -1) {
+ savic_write_reg(apage, reg, val);
+ /*
+ * Write using PV interface if wrmsr fails. Skip for
+ * regs which trigger GP
+ */
+ if (reg != APIC_LVR && reg != APIC_TMR && reg != APIC_IRR)
+ savic_hv_write_reg(reg, val);
+ }
+
+ gval = savic_read_reg(apage, reg);
+ gval2 = savic_rdmsr(reg);
+ hval = savic_hv_read_reg(reg);
+ __GUEST_ASSERT(gval == val, "Unexpected Guest %s 0x%lx, expected val:0x%lx\n",
+ regname, gval, val);
+ __GUEST_ASSERT(gval == gval2, "Unexpected Guest %s backing page value : 0x%lx, msr read val:0x%lx\n",
+ regname, gval, gval2);
+
+ switch (reg) {
+ case APIC_LVR:
+ case APIC_LDR:
+ case APIC_ISR:
+ case APIC_TMICT:
+ case APIC_TDCR:
+ case APIC_LVTT:
+ case APIC_LVTTHMR:
+ case APIC_LVTPC:
+ case APIC_LVT0:
+ case APIC_LVT1:
+ case APIC_LVTERR:
+ __GUEST_ASSERT(hval == gval, "Guest 0x%lx host 0x%lx %s mismatch\n",
+ gval, hval, regname);
+ break;
+ case APIC_TASKPRI:
+ case APIC_SPIV:
+ case APIC_ICR:
+ case APIC_TMR:
+ case APIC_IRR:
+ __GUEST_ASSERT(hval != gval, "Guest 0x%lx host 0x%lx reg: %x %s must not match\n",
+ gval, hval, reg, regname);
+ break;
+ default:
+ break;
+ }
+}
+
+static inline uint32_t x2apic_ldr(uint32_t id)
+{
+ return ((id >> 4) << 16) | (1 << (id & 0xf));
+}
+
+static void guest_savic_apic_msr_accesses(int id)
+{
+ struct guest_apic_page *apage = get_guest_apic_page();
+ uint64_t val, hval;
+ uint32_t reg;
+ int vec;
+ int i;
+ uint32_t lvt_regs[] = {
+ APIC_LVTT, APIC_LVTTHMR, APIC_LVTPC,
+ APIC_LVT0, APIC_LVT1, APIC_LVTERR
+ };
+
+ reg = APIC_LVR;
+ val = savic_hv_read_reg(reg);
+ /* APIC_LVR state is in sync between host and guest. */
+ guest_verify_host_guest_reg(apage, reg, val, "APIC_LVR");
+
+ reg = APIC_TASKPRI;
+ val = 0x30;
+ /* Write new TASKPRI to host using PV interface. */
+ savic_hv_write_reg(reg, val);
+ val = 0x40;
+ /* TASKPRI is accelerated and state is not up-to-date in host. */
+ guest_verify_host_guest_reg(apage, reg, val, "APIC_TASKPRI");
+
+ reg = APIC_PROCPRI;
+ val = x2apic_read_reg(reg);
+ /* APIC_PROCPRI is updated with the APIC_TASKPRI update above. */
+ GUEST_ASSERT((val & 0xf0) == (x2apic_read_reg(APIC_TASKPRI) & 0xf0));
+ GUEST_ASSERT((val & 0xf0) == 0x40);
+ vec = 0x20;
+ x2apic_write_reg(APIC_ICR, APIC_DEST_SELF | APIC_INT_ASSERT | vec);
+ /* Interrupt remains pending in APIC_IRR. */
+ val = savic_read_reg(apage, APIC_IRR + APIC_REG_OFF(vec));
+ GUEST_ASSERT((val & BIT_ULL(APIC_VEC_POS(vec))) == BIT_ULL(APIC_VEC_POS(vec)));
+ savic_wrmsr(APIC_TASKPRI, 0x0);
+
+ /* Triggers GP fault */
+ savic_rdmsr(APIC_EOI);
+
+ reg = APIC_LDR;
+ val = x2apic_ldr(savic_rdmsr(APIC_ID));
+ hval = savic_hv_read_reg(APIC_LDR);
+ __GUEST_ASSERT(val == hval, "APIC_LDR mismatch between host %lx and guest %lx",
+ hval, val);
+
+ /* APIC_SPIV state is not visible to host. */
+ reg = APIC_SPIV;
+ val = savic_rdmsr(APIC_SPIV) & ~APIC_SPIV_APIC_ENABLED;
+ savic_hv_write_reg(reg, val);
+ val = savic_rdmsr(APIC_SPIV) | APIC_SPIV_APIC_ENABLED;
+ guest_verify_host_guest_reg(apage, reg, val, "APIC_SPIV");
+
+ reg = APIC_ISR;
+ (void) savic_rdmsr(reg);
+ /* Triggers GP fault */
+ savic_wrmsr(reg, 0x10);
+
+ /* APIC_TMR is not synced to host. */
+ reg = APIC_TMR;
+ val = 0x10000;
+ guest_verify_host_guest_reg(apage, reg, val, "APIC_TMR");
+ vec = 0x20;
+ savic_write_reg(apage, reg + APIC_REG_OFF(vec), BIT_ULL(APIC_VEC_POS(vec)));
+ GUEST_ASSERT(x2apic_read_reg(reg + APIC_REG_OFF(vec)) & BIT_ULL(APIC_VEC_POS(vec)));
+
+ reg = APIC_IRR;
+ val = 0x10000;
+ guest_verify_host_guest_reg(apage, reg, val, "APIC_IRR");
+ savic_write_reg(apage, reg, 0x0);
+
+ reg = APIC_TMICT;
+ val = 0x555;
+ guest_verify_host_guest_reg(apage, reg, val, "APIC_TMICT");
+
+ reg = APIC_TMCCT;
+ savic_rdmsr(reg);
+ savic_wrmsr(reg, 0xf);
+
+ reg = APIC_TDCR;
+ val = 0x1;
+ savic_hv_write_reg(reg, val);
+ val = 0x3;
+ guest_verify_host_guest_reg(apage, reg, val, "APIC_TDCR");
+
+ for (i = 0; i < ARRAY_SIZE(lvt_regs); i++) {
+ reg = lvt_regs[i];
+ val = 0x41;
+ savic_hv_write_reg(reg, val);
+ val = 0x42;
+ guest_verify_host_guest_reg(apage, reg, val, "APIC_LVTx");
+ }
+}
+
static void guest_code(int id)
{
GUEST_ASSERT(rdmsr(MSR_AMD64_SEV) & MSR_AMD64_SNP_SECURE_AVIC);
@@ -302,6 +490,8 @@ static void guest_code(int id)
SAVIC_GUEST_SYNC(SAVIC_EN, guest_savic_enabled);
+ SAVIC_GUEST_SYNC(SAVIC_APIC_MSR_ACCESSES, guest_savic_apic_msr_accesses);
+
GUEST_DONE();
}
@@ -448,7 +638,7 @@ int main(int argc, char *argv[])
vcpu_args_set(vcpus[0], 1, vcpus[0]->id);
- vm_install_exception_handler(vm, 29, sev_es_vc_handler);
+ vm_install_exception_handler(vm, 29, savic_vc_handler);
vm_sev_launch(vm, snp_default_policy(), NULL);
r = pthread_create(&threads[0], NULL, vcpu_thread, vcpus[0]);
--
2.34.1
Powered by blists - more mailing lists