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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox