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.upadh...@amd.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


Reply via email to