From: Sean Christopherson <seanjc@google.com>
To: Sean Christopherson <seanjc@google.com>,
Paolo Bonzini <pbonzini@redhat.com>
Cc: kvm@vger.kernel.org, linux-kernel@vger.kernel.org,
Naveen N Rao <naveen@kernel.org>
Subject: [PATCH v2 5/5] *** DO NOT MERGE *** KVM: selftests: Add hacky test to verify x2APIC MSR interception
Date: Wed, 6 May 2026 11:47:46 -0700 [thread overview]
Message-ID: <20260506184746.2719880-6-seanjc@google.com> (raw)
In-Reply-To: <20260506184746.2719880-1-seanjc@google.com>
Not-signed-off-by: Sean Christopherson <seanjc@google.com>
---
.../testing/selftests/kvm/include/x86/apic.h | 84 ++++++-
.../selftests/kvm/x86/fix_hypercall_test.c | 2 +-
.../selftests/kvm/x86/xapic_ipi_test.c | 4 +-
.../selftests/kvm/x86/xapic_state_test.c | 217 ++++++++++++++++++
4 files changed, 296 insertions(+), 11 deletions(-)
diff --git a/tools/testing/selftests/kvm/include/x86/apic.h b/tools/testing/selftests/kvm/include/x86/apic.h
index 31887bdc3d6c..3c3e362f2b98 100644
--- a/tools/testing/selftests/kvm/include/x86/apic.h
+++ b/tools/testing/selftests/kvm/include/x86/apic.h
@@ -23,21 +23,64 @@
#define APIC_BASE_MSR 0x800
#define X2APIC_ENABLE (1UL << 10)
+
+#define APIC_DELIVERY_MODE_FIXED 0
+#define APIC_DELIVERY_MODE_LOWESTPRIO 1
+#define APIC_DELIVERY_MODE_SMI 2
+#define APIC_DELIVERY_MODE_NMI 4
+#define APIC_DELIVERY_MODE_INIT 5
+#define APIC_DELIVERY_MODE_EXTINT 7
+
#define APIC_ID 0x20
+
#define APIC_LVR 0x30
-#define GET_APIC_ID_FIELD(x) (((x) >> 24) & 0xFF)
+#define APIC_LVR_MASK 0xFF00FF
+#define APIC_LVR_DIRECTED_EOI (1 << 24)
+#define GET_APIC_VERSION(x) ((x) & 0xFFu)
+#define GET_APIC_MAXLVT(x) (((x) >> 16) & 0xFFu)
+#ifdef CONFIG_X86_32
+# define APIC_INTEGRATED(x) ((x) & 0xF0u)
+#else
+# define APIC_INTEGRATED(x) (1)
+#endif
+#define APIC_XAPIC(x) ((x) >= 0x14)
+#define APIC_EXT_SPACE(x) ((x) & 0x80000000)
#define APIC_TASKPRI 0x80
+#define APIC_TPRI_MASK 0xFFu
+#define APIC_ARBPRI 0x90
+#define APIC_ARBPRI_MASK 0xFFu
#define APIC_PROCPRI 0xA0
-#define GET_APIC_PRI(x) (((x) & GENMASK(7, 4)) >> 4)
-#define SET_APIC_PRI(x, y) (((x) & ~GENMASK(7, 4)) | (y << 4))
+#define GET_APIC_PRI(x) (((x) & GENMASK(7, 4)) >> 4)
+#define SET_APIC_PRI(x, y) (((x) & ~GENMASK(7, 4)) | (y << 4))
#define APIC_EOI 0xB0
+#define APIC_EOI_ACK 0x0 /* Docs say 0 for future compat. */
+#define APIC_RRR 0xC0
+#define APIC_LDR 0xD0
+#define APIC_LDR_MASK (0xFFu << 24)
+#define GET_APIC_LOGICAL_ID(x) (((x) >> 24) & 0xFFu)
+#define SET_APIC_LOGICAL_ID(x) (((x) << 24))
+#define APIC_ALL_CPUS 0xFFu
+#define APIC_DFR 0xE0
+#define APIC_DFR_CLUSTER 0x0FFFFFFFul
+#define APIC_DFR_FLAT 0xFFFFFFFFul
#define APIC_SPIV 0xF0
+#define APIC_SPIV_DIRECTED_EOI (1 << 12)
#define APIC_SPIV_FOCUS_DISABLED (1 << 9)
#define APIC_SPIV_APIC_ENABLED (1 << 8)
#define APIC_ISR 0x100
-#define APIC_IRR 0x200
+#define APIC_ISR_NR 0x8 /* Number of 32 bit ISR registers. */
+#define APIC_TMR 0x180
+#define APIC_IRR 0x200
+#define APIC_ESR 0x280
+#define APIC_ESR_SEND_CS 0x00001
+#define APIC_ESR_RECV_CS 0x00002
+#define APIC_ESR_SEND_ACC 0x00004
+#define APIC_ESR_RECV_ACC 0x00008
+#define APIC_ESR_SENDILL 0x00020
+#define APIC_ESR_RECVILL 0x00040
+#define APIC_ESR_ILLREGA 0x00080
+#define APIC_LVTCMCI 0x2f0
#define APIC_ICR 0x300
-#define APIC_LVTCMCI 0x2f0
#define APIC_DEST_SELF 0x40000
#define APIC_DEST_ALLINC 0x80000
#define APIC_DEST_ALLBUT 0xC0000
@@ -61,16 +104,41 @@
#define APIC_DM_EXTINT 0x00700
#define APIC_VECTOR_MASK 0x000FF
#define APIC_ICR2 0x310
-#define SET_APIC_DEST_FIELD(x) ((x) << 24)
-#define APIC_LVTT 0x320
+#define GET_XAPIC_DEST_FIELD(x) (((x) >> 24) & 0xFF)
+#define SET_XAPIC_DEST_FIELD(x) ((x) << 24)
+#define APIC_LVTT 0x320
+#define APIC_LVTTHMR 0x330
+#define APIC_LVTPC 0x340
+#define APIC_LVT0 0x350
#define APIC_LVT_TIMER_ONESHOT (0 << 17)
#define APIC_LVT_TIMER_PERIODIC (1 << 17)
#define APIC_LVT_TIMER_TSCDEADLINE (2 << 17)
#define APIC_LVT_MASKED (1 << 16)
+#define APIC_LVT_LEVEL_TRIGGER (1 << 15)
+#define APIC_LVT_REMOTE_IRR (1 << 14)
+#define APIC_INPUT_POLARITY (1 << 13)
+#define APIC_SEND_PENDING (1 << 12)
+#define APIC_MODE_MASK 0x700
+#define GET_APIC_DELIVERY_MODE(x) (((x) >> 8) & 0x7)
+#define SET_APIC_DELIVERY_MODE(x, y) (((x) & ~0x700) | ((y) << 8))
+#define APIC_MODE_FIXED 0x0
+#define APIC_MODE_NMI 0x4
+#define APIC_MODE_EXTINT 0x7
+#define APIC_LVT1 0x360
+#define APIC_LVTERR 0x370
#define APIC_TMICT 0x380
#define APIC_TMCCT 0x390
#define APIC_TDCR 0x3E0
-#define APIC_SELF_IPI 0x3F0
+#define APIC_SELF_IPI 0x3F0
+#define APIC_TDR_DIV_TMBASE (1 << 2)
+#define APIC_TDR_DIV_1 0xB
+#define APIC_TDR_DIV_2 0x0
+#define APIC_TDR_DIV_4 0x1
+#define APIC_TDR_DIV_8 0x2
+#define APIC_TDR_DIV_16 0x3
+#define APIC_TDR_DIV_32 0x8
+#define APIC_TDR_DIV_64 0x9
+#define APIC_TDR_DIV_128 0xA
#define APIC_VECTOR_TO_BIT_NUMBER(v) ((unsigned int)(v) % 32)
#define APIC_VECTOR_TO_REG_OFFSET(v) ((unsigned int)(v) / 32 * 0x10)
diff --git a/tools/testing/selftests/kvm/x86/fix_hypercall_test.c b/tools/testing/selftests/kvm/x86/fix_hypercall_test.c
index 753a0e730ea8..ad61da99ee4c 100644
--- a/tools/testing/selftests/kvm/x86/fix_hypercall_test.c
+++ b/tools/testing/selftests/kvm/x86/fix_hypercall_test.c
@@ -63,7 +63,7 @@ static void guest_main(void)
memcpy(hypercall_insn, other_hypercall_insn, HYPERCALL_INSN_SIZE);
- ret = do_sched_yield(GET_APIC_ID_FIELD(xapic_read_reg(APIC_ID)));
+ ret = do_sched_yield(GET_XAPIC_DEST_FIELD(xapic_read_reg(APIC_ID)));
/*
* If the quirk is disabled, verify that guest_ud_handler() "returned"
diff --git a/tools/testing/selftests/kvm/x86/xapic_ipi_test.c b/tools/testing/selftests/kvm/x86/xapic_ipi_test.c
index 39ce9a9369f5..75b87f850abc 100644
--- a/tools/testing/selftests/kvm/x86/xapic_ipi_test.c
+++ b/tools/testing/selftests/kvm/x86/xapic_ipi_test.c
@@ -91,7 +91,7 @@ static void halter_guest_code(struct test_data_page *data)
verify_apic_base_addr();
xapic_enable();
- data->halter_apic_id = GET_APIC_ID_FIELD(xapic_read_reg(APIC_ID));
+ data->halter_apic_id = GET_XAPIC_DEST_FIELD(xapic_read_reg(APIC_ID));
data->halter_lvr = xapic_read_reg(APIC_LVR);
/*
@@ -147,7 +147,7 @@ static void sender_guest_code(struct test_data_page *data)
* set data->halter_apic_id.
*/
icr_val = (APIC_DEST_PHYSICAL | APIC_DM_FIXED | IPI_VECTOR);
- icr2_val = SET_APIC_DEST_FIELD(data->halter_apic_id);
+ icr2_val = SET_XAPIC_DEST_FIELD(data->halter_apic_id);
data->icr = icr_val;
data->icr2 = icr2_val;
diff --git a/tools/testing/selftests/kvm/x86/xapic_state_test.c b/tools/testing/selftests/kvm/x86/xapic_state_test.c
index 637bb90c1d93..3c7c6a5485e4 100644
--- a/tools/testing/selftests/kvm/x86/xapic_state_test.c
+++ b/tools/testing/selftests/kvm/x86/xapic_state_test.c
@@ -222,6 +222,221 @@ static void test_x2apic_id(void)
kvm_vm_free(vm);
}
+#define X2APIC_MSR(r) (0x800 + ((r) >> 4))
+
+static bool is_x2apic_mode = true;
+
+static bool is_ro_only_reg(int reg)
+{
+ switch (reg) {
+ case APIC_ID:
+ case APIC_LVR:
+ case APIC_PROCPRI:
+ case APIC_LDR:
+ case APIC_ARBPRI:
+ case APIC_ISR:
+ case APIC_TMR:
+ case APIC_IRR:
+ case APIC_TMCCT:
+ return true;
+ }
+ return false;
+}
+
+static bool is_xapic_only_reg(int reg)
+{
+ return reg == APIC_ARBPRI || reg == APIC_DFR || reg == APIC_ICR2;
+}
+
+static bool is_accelerated_reg(int reg, bool write)
+{
+ if (!write)
+ return reg != APIC_TMCCT;
+
+ switch (reg) {
+ case APIC_TASKPRI:
+ case APIC_EOI:
+ case APIC_SELF_IPI:
+ case APIC_ICR:
+ return true;
+ default:
+ break;
+ }
+ return false;
+}
+
+static void x2apic_msr_guest_code(void)
+{
+ const u32 xapic_regs[] = {
+ APIC_ID,
+ APIC_LVR,
+ APIC_TASKPRI,
+ APIC_PROCPRI,
+ APIC_LDR,
+ APIC_SPIV,
+ APIC_ISR,
+ APIC_TMR,
+ APIC_IRR,
+ APIC_ESR,
+ APIC_LVTT,
+ APIC_LVTTHMR,
+ APIC_LVTPC,
+ APIC_LVT0,
+ APIC_LVT1,
+ APIC_LVTERR,
+ APIC_TMICT,
+ APIC_TMCCT,
+ APIC_TDCR,
+
+ // APIC_EOI,
+ // APIC_SELF_IPI,
+ // APIC_ICR,
+
+ APIC_ARBPRI,
+ APIC_DFR,
+ APIC_ICR2,
+ };
+ int i, j;
+ u64 val;
+ u32 msr;
+ u8 vec;
+
+ cli();
+
+ if (is_x2apic_mode)
+ x2apic_enable();
+
+ GUEST_SYNC(0xbeef);
+
+ for (i = 0; i < ARRAY_SIZE(xapic_regs); i++) {
+ int nr_regs;
+ u8 rd, wr;
+
+ if (!is_x2apic_mode || is_xapic_only_reg(xapic_regs[i])) {
+ rd = wr = GP_VECTOR;
+ } else {
+ rd = 0;
+ wr = is_ro_only_reg(xapic_regs[i]) ? GP_VECTOR : 0;
+ }
+
+ if (xapic_regs[i] == APIC_IRR ||
+ xapic_regs[i] == APIC_ISR ||
+ xapic_regs[i] == APIC_TMR)
+ nr_regs = APIC_ISR_NR;
+ else
+ nr_regs = 1;
+
+ for (j = 0; j < nr_regs; j++) {
+ msr = X2APIC_MSR(xapic_regs[i] + j * 0x10);
+
+ vec = rdmsr_safe(msr, &val);
+ __GUEST_ASSERT(vec == rd,
+ "Wanted %s on RDMSR(%x), got %s",
+ ex_str(rd), msr, ex_str(vec));
+ GUEST_SYNC3(xapic_regs[i], false, vec);
+
+ vec = wrmsr_safe(msr, 0);
+ __GUEST_ASSERT(vec == wr,
+ "Wanted %s on WRMSR(%x), got %s",
+ ex_str(wr), msr, ex_str(vec));
+
+ GUEST_SYNC3(xapic_regs[i], true, vec);
+ }
+ }
+ GUEST_DONE();
+}
+
+static void test_x2apic_msr_intercepts(void)
+{
+ u64 last_guest, last_io, last_msr, guest_exits, io_exits, msr_exits;
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm;
+ struct ucall uc;
+
+ vm = vm_create_with_one_vcpu(&vcpu, x2apic_msr_guest_code);
+
+ TEST_ASSERT_EQ(vcpu_get_stat(vcpu, guest_induced_exits), 0);
+
+ vcpu_run(vcpu);
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
+
+ TEST_ASSERT_EQ(get_ucall(vcpu, NULL), UCALL_SYNC);
+
+ for ( ;; ) {
+ last_guest = vcpu_get_stat(vcpu, guest_induced_exits);
+ last_io = vcpu_get_stat(vcpu, io_exits);
+ last_msr = vcpu_get_stat(vcpu, msr_exits);
+
+ vcpu_run(vcpu);
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
+
+ guest_exits = vcpu_get_stat(vcpu, guest_induced_exits);
+ io_exits = vcpu_get_stat(vcpu, io_exits);
+ msr_exits = vcpu_get_stat(vcpu, msr_exits);
+
+ TEST_ASSERT_EQ(io_exits, last_io + 1);
+
+ switch (get_ucall(vcpu, &uc)) {
+ case UCALL_SYNC: {
+ int vector = uc.args[2];
+ bool write = uc.args[1];
+ u32 reg = uc.args[0];
+
+ printf("reg = %x, write = %u, fault = %u\n", reg, write, vector);
+ if (vector || !is_accelerated_reg(reg, write)) {
+ TEST_ASSERT_EQ(msr_exits, last_msr + 1);
+ TEST_ASSERT_EQ(guest_exits - last_guest,
+ io_exits - last_io + msr_exits - last_msr);
+ } else {
+ TEST_ASSERT_EQ(msr_exits, last_msr);
+ TEST_ASSERT_EQ(guest_exits - last_guest,
+ io_exits - last_io);
+ }
+ // printf("On msr = %x\n", msr);
+ break;
+ }
+ case UCALL_DONE:
+ goto test_xapic;
+ case UCALL_ABORT:
+ REPORT_GUEST_ASSERT(uc);
+ default:
+ TEST_FAIL("Unknown ucall %lu", uc.cmd);
+ }
+ }
+
+test_xapic:
+ kvm_vm_free(vm);
+
+ vm = vm_create_with_one_vcpu(&vcpu, x2apic_msr_guest_code);
+ vcpu_clear_cpuid_feature(vcpu, X86_FEATURE_X2APIC);
+ is_x2apic_mode = false;
+ sync_global_to_guest(vm, is_x2apic_mode);
+
+ for ( ;; ) {
+ vcpu_run(vcpu);
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
+
+ // vcpu_get_stat(vcpu, io_exits);
+ // vcpu_get_stat(vcpu, msr_exits);
+ // vcpu_get_stat(vcpu, guest_induced_exits);
+
+ switch (get_ucall(vcpu, &uc)) {
+ case UCALL_SYNC:
+ // u32 msr = uc.args[1];
+
+ // printf("On msr = %x\n", msr);
+ break;
+ case UCALL_DONE:
+ goto done;
+ case UCALL_ABORT:
+ REPORT_GUEST_ASSERT(uc);
+ default:
+ TEST_FAIL("Unknown ucall %lu", uc.cmd);
+ }
+ }
+done:
+}
+
int main(int argc, char *argv[])
{
struct xapic_vcpu x = {
@@ -230,6 +445,8 @@ int main(int argc, char *argv[])
};
struct kvm_vm *vm;
+ test_x2apic_msr_intercepts();
+
vm = vm_create_with_one_vcpu(&x.vcpu, x2apic_guest_code);
test_icr(&x);
kvm_vm_free(vm);
--
2.54.0.545.g6539524ca2-goog
next prev parent reply other threads:[~2026-05-06 18:47 UTC|newest]
Thread overview: 21+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-06 18:47 [PATCH v2 0/5] KVM: SVM: Fix x2AVIC MSR interception issues Sean Christopherson
2026-05-06 18:47 ` [PATCH v2 1/5] KVM: SVM: Disable x2AVIC RDMSR interception for MSRs KVM actually supports Sean Christopherson
2026-05-07 13:56 ` Naveen N Rao
2026-05-07 14:27 ` Sean Christopherson
2026-05-08 16:35 ` Naveen N Rao
2026-05-06 18:47 ` [PATCH v2 2/5] KVM: SVM: Always intercept RDMSR for TMCCT (current APIC timer count) Sean Christopherson
2026-05-07 14:19 ` Naveen N Rao
2026-05-07 15:44 ` Sean Christopherson
2026-05-07 18:26 ` Sean Christopherson
2026-05-08 16:41 ` Naveen N Rao
2026-05-08 16:56 ` Sean Christopherson
2026-05-06 18:47 ` [PATCH v2 3/5] KVM: SVM: Only disable x2AVIC WRMSR interception for MSRs that are accelerated Sean Christopherson
2026-05-08 16:59 ` Naveen N Rao
2026-05-13 6:29 ` Naveen N Rao
2026-05-13 13:33 ` Sean Christopherson
2026-05-06 18:47 ` [PATCH v2 4/5] *** DO NOT MERGE *** KVM: x86: Hack in a stat to track guest-induced exits (for testing) Sean Christopherson
2026-05-08 17:14 ` Naveen N Rao
2026-05-08 17:49 ` Sean Christopherson
2026-05-09 5:08 ` Naveen N Rao
2026-05-06 18:47 ` Sean Christopherson [this message]
2026-05-09 5:10 ` [PATCH v2 0/5] KVM: SVM: Fix x2AVIC MSR interception issues Naveen N Rao
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260506184746.2719880-6-seanjc@google.com \
--to=seanjc@google.com \
--cc=kvm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=naveen@kernel.org \
--cc=pbonzini@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.