]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
KVM: arm64: selftests: vgic_irq: Add asymmetric SPI deaectivation test
authorMarc Zyngier <maz@kernel.org>
Thu, 20 Nov 2025 17:25:37 +0000 (17:25 +0000)
committerOliver Upton <oupton@kernel.org>
Mon, 24 Nov 2025 22:29:15 +0000 (14:29 -0800)
Add a new test case that makes an interrupt pending on a vcpu,
activates it, do the priority drop, and then get *another* vcpu
to do the deactivation.

Special care is taken not to trigger an exit in the process, so
that we are sure that the active interrupt is in an LR. Joy.

Tested-by: Fuad Tabba <tabba@google.com>
Signed-off-by: Marc Zyngier <maz@kernel.org>
Tested-by: Mark Brown <broonie@kernel.org>
Link: https://msgid.link/20251120172540.2267180-48-maz@kernel.org
Signed-off-by: Oliver Upton <oupton@kernel.org>
tools/testing/selftests/kvm/arm64/vgic_irq.c

index 72f7bb0d201e5d3bc591a5a0eec4507eb05ff8ab..a53ab809fe8ae14ed647b8a63e16aa9fe4ecbc21 100644 (file)
@@ -29,6 +29,7 @@ struct test_args {
        bool level_sensitive; /* 1 is level, 0 is edge */
        int kvm_max_routes; /* output of KVM_CAP_IRQ_ROUTING */
        bool kvm_supports_irqfd; /* output of KVM_CAP_IRQFD */
+       uint32_t shared_data;
 };
 
 /*
@@ -801,6 +802,109 @@ done:
        kvm_vm_free(vm);
 }
 
+static void guest_code_asym_dir(struct test_args *args, int cpuid)
+{
+       gic_init(GIC_V3, 2);
+
+       gic_set_eoi_split(1);
+       gic_set_priority_mask(CPU_PRIO_MASK);
+
+       if (cpuid == 0) {
+               uint32_t intid;
+
+               local_irq_disable();
+
+               gic_set_priority(MIN_PPI, IRQ_DEFAULT_PRIO);
+               gic_irq_enable(MIN_SPI);
+               gic_irq_set_pending(MIN_SPI);
+
+               intid = wait_for_and_activate_irq();
+               GUEST_ASSERT_EQ(intid, MIN_SPI);
+
+               gic_set_eoi(intid);
+               isb();
+
+               WRITE_ONCE(args->shared_data, MIN_SPI);
+               dsb(ishst);
+
+               do {
+                       dsb(ishld);
+               } while (READ_ONCE(args->shared_data) == MIN_SPI);
+               GUEST_ASSERT(!gic_irq_get_active(MIN_SPI));
+       } else {
+               do {
+                       dsb(ishld);
+               } while (READ_ONCE(args->shared_data) != MIN_SPI);
+
+               gic_set_dir(MIN_SPI);
+               isb();
+
+               WRITE_ONCE(args->shared_data, 0);
+               dsb(ishst);
+       }
+
+       GUEST_DONE();
+}
+
+static void *test_vcpu_run(void *arg)
+{
+       struct kvm_vcpu *vcpu = arg;
+       struct ucall uc;
+
+       while (1) {
+               vcpu_run(vcpu);
+
+               switch (get_ucall(vcpu, &uc)) {
+               case UCALL_ABORT:
+                       REPORT_GUEST_ASSERT(uc);
+                       break;
+               case UCALL_DONE:
+                       return NULL;
+               default:
+                       TEST_FAIL("Unknown ucall %lu", uc.cmd);
+               }
+       }
+
+       return NULL;
+}
+
+static void test_vgic_two_cpus(void *gcode)
+{
+       pthread_t thr[2];
+       struct kvm_vcpu *vcpus[2];
+       struct test_args args = {};
+       struct kvm_vm *vm;
+       vm_vaddr_t args_gva;
+       int gic_fd, ret;
+
+       vm = vm_create_with_vcpus(2, gcode, vcpus);
+
+       vm_init_descriptor_tables(vm);
+       vcpu_init_descriptor_tables(vcpus[0]);
+       vcpu_init_descriptor_tables(vcpus[1]);
+
+       /* Setup the guest args page (so it gets the args). */
+       args_gva = vm_vaddr_alloc_page(vm);
+       memcpy(addr_gva2hva(vm, args_gva), &args, sizeof(args));
+       vcpu_args_set(vcpus[0], 2, args_gva, 0);
+       vcpu_args_set(vcpus[1], 2, args_gva, 1);
+
+       gic_fd = vgic_v3_setup(vm, 2, 64);
+
+       ret = pthread_create(&thr[0], NULL, test_vcpu_run, vcpus[0]);
+       if (ret)
+               TEST_FAIL("Can't create thread for vcpu 0 (%d)\n", ret);
+       ret = pthread_create(&thr[1], NULL, test_vcpu_run, vcpus[1]);
+       if (ret)
+               TEST_FAIL("Can't create thread for vcpu 1 (%d)\n", ret);
+
+       pthread_join(thr[0], NULL);
+       pthread_join(thr[1], NULL);
+
+       close(gic_fd);
+       kvm_vm_free(vm);
+}
+
 static void help(const char *name)
 {
        printf(
@@ -857,6 +961,7 @@ int main(int argc, char **argv)
                test_vgic(nr_irqs, false /* level */, true /* eoi_split */);
                test_vgic(nr_irqs, true /* level */, false /* eoi_split */);
                test_vgic(nr_irqs, true /* level */, true /* eoi_split */);
+               test_vgic_two_cpus(guest_code_asym_dir);
        } else {
                test_vgic(nr_irqs, level_sensitive, eoi_split);
        }