Add a new test case, test_pv_ipi_sparse(), to verify that PV IPIs are correctly delivered to vCPUs with APIC ID 255. APIC ID 255 represents a critical boundary condition for PV IPIs: 1. It is the transition point where 0xff ceases to be a broadcast ID (as in xAPIC) and becomes a valid unicast ID (in x2APIC). 2. KVM's PV IPI mechanism groups vCPUs into 64-bit clusters. APIC ID 255 maps to the 64th bit (bit 63) of the cluster bitmap. This tests potential off-by-one or fencepost errors at the MSB boundary of the bitmap processing loop. Additionally, this introduces a sparse topology test configuration (smp=1, maxcpus=256, and a hotplugged CPU at APIC ID 255) to ensure KVM's sparse APIC ID map handling is robust and does not suffer from out-of-bounds accesses when resolving target vCPUs. Assisted-by: Gemini:gemini-3.1-pro Signed-off-by: Jim Mattson --- x86/apic.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++ x86/unittests.cfg | 8 +++++++ 2 files changed, 68 insertions(+) diff --git a/x86/apic.c b/x86/apic.c index eea0c8d8e0f9..2b220c9e3909 100644 --- a/x86/apic.c +++ b/x86/apic.c @@ -915,6 +915,65 @@ static void test_aliased_xapic_physical_ipi(void) report(!f, "IPI to aliased xAPIC physical IDs"); } +static atomic_t pv_ipi_received[256]; + +static void handle_pv_ipi(isr_regs_t *regs) +{ + atomic_inc(&pv_ipi_received[smp_id()]); + apic_write(APIC_EOI, 0); +} + +/* + * Verify that PV IPIs are correctly delivered to a vCPU with APIC ID 255. + */ +static void test_pv_ipi_sparse(void) +{ + const u8 vector = 0xf2; + int ret; + + if (!this_cpu_has_kvm() || !this_cpu_has(KVM_FEATURE_PV_SEND_IPI)) { + report_skip("PV IPIs are not supported"); + return; + } + + if (!test_device_enabled()) { + report_skip("Test device not enabled"); + return; + } + + if (cpu_count() != 2) { + report_skip("Requires 2 CPUs"); + return; + } + + /* 255 is a broadcast ID in xAPIC, so we must use x2APIC */ + if (!is_x2apic_enabled()) { + report_skip("x2APIC is not enabled"); + return; + } + + if (id_map[1] != 255) { + report_skip("vCPU 1 does not have APIC ID 255"); + return; + } + + handle_irq(vector, handle_pv_ipi); + + /* Send PV IPI to 255 (vCPU 1) */ + ret = pv_ipi(1, 0, 255, APIC_DM_FIXED | vector); + + /* Wait for IPI to be received on vCPU 1 */ + u64 start = rdtsc(); + while (rdtsc() - start < 1000000000 && + !atomic_read(&pv_ipi_received[255])) { + pause(); + } + + report(ret == 1 && atomic_read(&pv_ipi_received[255]) == 1 && + atomic_read(&pv_ipi_received[0]) == 0, + "PV IPI to sparse APIC ID 255"); +} + struct apic_test { const char *name; void (*fn)(void); @@ -937,6 +996,7 @@ int main(int argc, char **argv) { "physical_broadcast", test_physical_broadcast }, { "logical_ipi_xapic", test_logical_ipi_xapic }, { "pv_ipi", test_pv_ipi }, + { "pv_ipi_sparse", test_pv_ipi_sparse }, { "sti_nmi", test_sti_nmi }, { "multiple_nmi", test_multiple_nmi }, { "pending_nmi", test_pending_nmi }, diff --git a/x86/unittests.cfg b/x86/unittests.cfg index 0c433ac29e5f..0bdeb4b1826f 100644 --- a/x86/unittests.cfg +++ b/x86/unittests.cfg @@ -535,3 +535,11 @@ qemu_params = -enable-kvm -m 2048 -cpu host file = lam.flat arch = x86_64 qemu_params = -enable-kvm -cpu max + +[apic_sparse] +file = apic.flat +smp = 1,maxcpus=256 +qemu_params = -M q35 -cpu qemu64,+x2apic,+tsc-deadline,+kvm-pv-ipi -device qemu64-x86_64-cpu,apic-id=255 -append 'pv_ipi_sparse' +arch = x86_64 +timeout = 30 +groups = apic -- 2.55.0.rc0.799.gd6f94ed593-goog