diff mbox series

[kvm-unit-tests,9/9] x86/apic: Add test to verify aliased xAPIC IDs both receive IPI

Message ID 20221001011301.2077437-10-seanjc@google.com (mailing list archive)
State New, archived
Headers show
Series x86/apic: Cleanups and new tests | expand

Commit Message

Sean Christopherson Oct. 1, 2022, 1:13 a.m. UTC
Verify that multiple vCPUs with the same physical xAPIC ID receive an
IPI sent to said ID.  Note, on_cpu() maintains its own CPU=>ID map and
is effectively unusuable after changing the xAPIC ID.  Update each vCPU's
xAPIC ID from within the IRQ handler so as to avoid having to send
yet another IPI from vCPU0 to tell vCPU1 to update its ID.

Signed-off-by: Sean Christopherson <seanjc@google.com>
---
 lib/x86/processor.h |  1 +
 x86/apic.c          | 67 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 68 insertions(+)
diff mbox series

Patch

diff --git a/lib/x86/processor.h b/lib/x86/processor.h
index 0324220..280821e 100644
--- a/lib/x86/processor.h
+++ b/lib/x86/processor.h
@@ -219,6 +219,7 @@  static inline bool is_intel(void)
 #define	X86_FEATURE_VMX			(CPUID(0x1, 0, ECX, 5))
 #define	X86_FEATURE_PDCM		(CPUID(0x1, 0, ECX, 15))
 #define	X86_FEATURE_PCID		(CPUID(0x1, 0, ECX, 17))
+#define X86_FEATURE_X2APIC		(CPUID(0x1, 0, ECX, 21))
 #define	X86_FEATURE_MOVBE		(CPUID(0x1, 0, ECX, 22))
 #define	X86_FEATURE_TSC_DEADLINE_TIMER	(CPUID(0x1, 0, ECX, 24))
 #define	X86_FEATURE_XSAVE		(CPUID(0x1, 0, ECX, 26))
diff --git a/x86/apic.c b/x86/apic.c
index 1cc61d3..20c3a1a 100644
--- a/x86/apic.c
+++ b/x86/apic.c
@@ -847,6 +847,72 @@  static void test_logical_ipi_xapic(void)
 	report(!f, "IPI to multiple targets using logical cluster mode");
 }
 
+static void set_xapic_physical_id(void *apic_id)
+{
+	apic_write(APIC_ID, (unsigned long)apic_id << 24);
+}
+
+static void handle_aliased_ipi(isr_regs_t *regs)
+{
+	u32 apic_id = apic_read(APIC_ID) >> 24;
+
+	if (apic_id == 0xff)
+		apic_id = smp_id();
+	else
+		apic_id++;
+	apic_write(APIC_ID, (unsigned long)apic_id << 24);
+
+	/*
+	 * Handle the IPI after updating the APIC ID, as the IPI count acts as
+	 * synchronization barrier before vCPU0 sends the next IPI.
+	 */
+	handle_ipi(regs);
+}
+
+static void test_aliased_xapic_physical_ipi(void)
+{
+	u8 vector = 0xf1;
+	int i, f;
+
+	if (cpu_count() < 2)
+		return;
+
+	/*
+	 * All vCPUs must be in xAPIC mode, i.e. simply resetting this vCPUs
+	 * APIC is not sufficient.
+	 */
+	if (is_x2apic_enabled())
+		return;
+
+	/*
+	 * By default, KVM doesn't follow the x86 APIC architecture for aliased
+	 * APIC IDs if userspace has enabled KVM_X2APIC_API_USE_32BIT_IDS.
+	 * If x2APIC is supported, assume the userspace VMM has enabled 32-bit
+	 * IDs and thus activated KVM's quirk.  Delete this code to run the
+	 * aliasing test on x2APIC CPUs, e.g. to run it on bare metal.
+	 */
+	if (this_cpu_has(X86_FEATURE_X2APIC))
+		return;
+
+	handle_irq(vector, handle_aliased_ipi);
+
+	/*
+	 * Set both vCPU0 and vCPU1's APIC IDs to 0, then start the chain
+	 * reaction of IPIs from APIC ID 0..255.  Each vCPU will increment its
+	 * APIC ID in the handler, and then "reset" to its original ID (using
+	 * smp_id()) after the last IPI.  Using on_cpu() to set vCPU1's ID
+	 * after this point won't work due to on_cpu() using physical mode.
+	 */
+	on_cpu(1, set_xapic_physical_id, (void *)0ul);
+	set_xapic_physical_id((void *)0ul);
+
+	f = 0;
+	for (i = 0; i < 0x100; i++)
+		f += test_fixed_ipi(APIC_DEST_PHYSICAL, i, vector, 2, "physical");
+
+	report(!f, "IPI to aliased xAPIC physical IDs");
+}
+
 typedef void (*apic_test_fn)(void);
 
 int main(void)
@@ -883,6 +949,7 @@  int main(void)
 		 */
 		test_apic_id,
 		test_apicbase,
+		test_aliased_xapic_physical_ipi,
 	};
 
 	assert_msg(is_apic_hw_enabled() && is_apic_sw_enabled(),