The existing redistributor-region test already adds valid partial
regions, rejects a later region that collides with the distributor, and
retries with a valid region. Its guest code does not access a
redistributor, so it misses redistributor MMIO bus entries lost by the
failed rollback.

Run a guest that reads GICR_TYPER from the second redistributor in the
original region and verifies its processor number. Put the test layout
outside the default guest memory slot so an absent iodev results in a
KVM_EXIT_MMIO instead of an ordinary RAM access.

Allow the common VGIC setup helper to select the guest entry point so the
retry case can reuse the normal test setup.

Assisted-by: Codex:gpt-5.6-sol
Signed-off-by: Karl Mehltretter <[email protected]>
---
 tools/testing/selftests/kvm/arm64/vgic_init.c | 115 +++++++++++++-----
 1 file changed, 86 insertions(+), 29 deletions(-)

diff --git a/tools/testing/selftests/kvm/arm64/vgic_init.c 
b/tools/testing/selftests/kvm/arm64/vgic_init.c
index 47e34b43afb29..8b321a1409b34 100644
--- a/tools/testing/selftests/kvm/arm64/vgic_init.c
+++ b/tools/testing/selftests/kvm/arm64/vgic_init.c
@@ -5,6 +5,7 @@
  * Copyright (C) 2020, Red Hat, Inc.
  */
 #include <linux/kernel.h>
+#include <linux/sizes.h>
 #include <sys/syscall.h>
 #include <asm/kvm.h>
 #include <asm/kvm_para.h>
@@ -13,12 +14,23 @@
 
 #include "test_util.h"
 #include "kvm_util.h"
+#include "gic.h"
 #include "processor.h"
+#include "ucall_common.h"
 #include "vgic.h"
 #include "gic_v3.h"
 
 #define NR_VCPUS               4
 
+/* Keep the redistributor range outside the default guest memory slot. */
+#define REDIST_TEST_REGION0_BASE       GICR_BASE_GPA
+#define REDIST_TEST_REGION1_BASE       \
+       (REDIST_TEST_REGION0_BASE + 2 * KVM_VGIC_V3_REDIST_SIZE)
+#define REDIST_TEST_DIST_BASE  \
+       (REDIST_TEST_REGION1_BASE + KVM_VGIC_V3_REDIST_SIZE)
+#define REDIST_TEST_REGION2_BASE       \
+       (REDIST_TEST_DIST_BASE + KVM_VGIC_V3_DIST_SIZE)
+
 #define REG_OFFSET(vcpu, offset) (((u64)vcpu << 32) | offset)
 
 #define VGIC_DEV_IS_V2(_d) ((_d) == KVM_DEV_TYPE_ARM_VGIC_V2)
@@ -65,6 +77,16 @@ static void guest_code(void)
        GUEST_DONE();
 }
 
+static void guest_read_second_redist(void)
+{
+       u64 typer = readq((void *)(unsigned long)(REDIST_TEST_REGION0_BASE +
+                                                  KVM_VGIC_V3_REDIST_SIZE +
+                                                  GICR_TYPER));
+
+       GUEST_ASSERT_EQ(GICR_TYPER_CPU_NUMBER(typer), 1);
+       GUEST_DONE();
+}
+
 /* we don't want to assert on run execution, hence that helper */
 static int run_vcpu(struct kvm_vcpu *vcpu)
 {
@@ -73,12 +95,13 @@ static int run_vcpu(struct kvm_vcpu *vcpu)
 
 static struct vm_gic vm_gic_create_with_vcpus(u32 gic_dev_type,
                                              u32 nr_vcpus,
+                                             void (*guest_fn)(void),
                                              struct kvm_vcpu *vcpus[])
 {
        struct vm_gic v;
 
        v.gic_dev_type = gic_dev_type;
-       v.vm = vm_create_with_vcpus(nr_vcpus, guest_code, vcpus);
+       v.vm = vm_create_with_vcpus(nr_vcpus, guest_fn, vcpus);
        v.gic_fd = kvm_create_device(v.vm, gic_dev_type);
 
        return v;
@@ -240,38 +263,40 @@ static void subtest_v3_redist_regions(struct vm_gic *v)
                                    KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr);
        TEST_ASSERT(ret && errno == EINVAL, "redist region attr value with 
count== 0");
 
-       addr = REDIST_REGION_ATTR_ADDR(2, 0x200000, 0, 1);
+       addr = REDIST_REGION_ATTR_ADDR(2, REDIST_TEST_REGION0_BASE, 0, 1);
        ret = __kvm_device_attr_set(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
                                    KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr);
        TEST_ASSERT(ret && errno == EINVAL,
                    "attempt to register the first rdist region with index != 
0");
 
-       addr = REDIST_REGION_ATTR_ADDR(2, 0x201000, 0, 1);
+       addr = REDIST_REGION_ATTR_ADDR(2, REDIST_TEST_REGION0_BASE + 0x1000,
+                                      0, 1);
        ret = __kvm_device_attr_set(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
                                    KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr);
        TEST_ASSERT(ret && errno == EINVAL, "rdist region with misaligned 
address");
 
-       addr = REDIST_REGION_ATTR_ADDR(2, 0x200000, 0, 0);
+       addr = REDIST_REGION_ATTR_ADDR(2, REDIST_TEST_REGION0_BASE, 0, 0);
        kvm_device_attr_set(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
                            KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr);
 
-       addr = REDIST_REGION_ATTR_ADDR(2, 0x200000, 0, 1);
+       addr = REDIST_REGION_ATTR_ADDR(2, REDIST_TEST_REGION0_BASE, 0, 1);
        ret = __kvm_device_attr_set(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
                                    KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr);
        TEST_ASSERT(ret && errno == EINVAL, "register an rdist region with 
already used index");
 
-       addr = REDIST_REGION_ATTR_ADDR(1, 0x210000, 0, 2);
+       addr = REDIST_REGION_ATTR_ADDR(1, REDIST_TEST_REGION0_BASE + 0x10000,
+                                      0, 2);
        ret = __kvm_device_attr_set(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
                                    KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr);
        TEST_ASSERT(ret && errno == EINVAL,
                    "register an rdist region overlapping with another one");
 
-       addr = REDIST_REGION_ATTR_ADDR(1, 0x240000, 0, 2);
+       addr = REDIST_REGION_ATTR_ADDR(1, REDIST_TEST_REGION1_BASE, 0, 2);
        ret = __kvm_device_attr_set(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
                                    KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr);
        TEST_ASSERT(ret && errno == EINVAL, "register redist region with index 
not +1");
 
-       addr = REDIST_REGION_ATTR_ADDR(1, 0x240000, 0, 1);
+       addr = REDIST_REGION_ATTR_ADDR(1, REDIST_TEST_REGION1_BASE, 0, 1);
        kvm_device_attr_set(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
                            KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr);
 
@@ -288,7 +313,7 @@ static void subtest_v3_redist_regions(struct vm_gic *v)
        TEST_ASSERT(ret && errno == E2BIG,
                    "register redist region with top address beyond IPA range");
 
-       addr = 0x260000;
+       addr = REDIST_TEST_DIST_BASE;
        ret = __kvm_device_attr_set(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
                                    KVM_VGIC_V3_ADDR_TYPE_REDIST, &addr);
        TEST_ASSERT(ret && errno == EINVAL,
@@ -296,19 +321,19 @@ static void subtest_v3_redist_regions(struct vm_gic *v)
 
        /*
         * Now there are 2 redist regions:
-        * region 0 @ 0x200000 2 redists
-        * region 1 @ 0x240000 1 redist
+        * region 0 has 2 redistributors
+        * region 1 has 1 redistributor
         * Attempt to read their characteristics
         */
 
        addr = REDIST_REGION_ATTR_ADDR(0, 0, 0, 0);
-       expected_addr = REDIST_REGION_ATTR_ADDR(2, 0x200000, 0, 0);
+       expected_addr = REDIST_REGION_ATTR_ADDR(2, REDIST_TEST_REGION0_BASE, 0, 
0);
        ret = __kvm_device_attr_get(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
                                    KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr);
        TEST_ASSERT(!ret && addr == expected_addr, "read characteristics of 
region #0");
 
        addr = REDIST_REGION_ATTR_ADDR(0, 0, 0, 1);
-       expected_addr = REDIST_REGION_ATTR_ADDR(1, 0x240000, 0, 1);
+       expected_addr = REDIST_REGION_ATTR_ADDR(1, REDIST_TEST_REGION1_BASE, 0, 
1);
        ret = __kvm_device_attr_get(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
                                    KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr);
        TEST_ASSERT(!ret && addr == expected_addr, "read characteristics of 
region #1");
@@ -318,11 +343,11 @@ static void subtest_v3_redist_regions(struct vm_gic *v)
                                    KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr);
        TEST_ASSERT(ret && errno == ENOENT, "read characteristics of non 
existing region");
 
-       addr = 0x260000;
+       addr = REDIST_TEST_DIST_BASE;
        kvm_device_attr_set(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
                            KVM_VGIC_V3_ADDR_TYPE_DIST, &addr);
 
-       addr = REDIST_REGION_ATTR_ADDR(1, 0x260000, 0, 2);
+       addr = REDIST_REGION_ATTR_ADDR(1, REDIST_TEST_DIST_BASE, 0, 2);
        ret = __kvm_device_attr_set(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
                                    KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr);
        TEST_ASSERT(ret && errno == EINVAL, "register redist region colliding 
with dist");
@@ -338,7 +363,7 @@ static void test_vgic_then_vcpus(u32 gic_dev_type)
        struct vm_gic v;
        int ret, i;
 
-       v = vm_gic_create_with_vcpus(gic_dev_type, 1, vcpus);
+       v = vm_gic_create_with_vcpus(gic_dev_type, 1, guest_code, vcpus);
 
        subtest_dist_rdist(&v);
 
@@ -359,7 +384,8 @@ static void test_vcpus_then_vgic(u32 gic_dev_type)
        struct vm_gic v;
        int ret;
 
-       v = vm_gic_create_with_vcpus(gic_dev_type, NR_VCPUS, vcpus);
+       v = vm_gic_create_with_vcpus(gic_dev_type, NR_VCPUS, guest_code,
+                                    vcpus);
 
        subtest_dist_rdist(&v);
 
@@ -403,6 +429,29 @@ static void test_v2_uaccess_cpuif_no_vcpus(void)
        vm_gic_destroy(&v);
 }
 
+static void run_vcpu_expect_done(struct kvm_vcpu *vcpu)
+{
+       struct ucall uc;
+
+       vcpu_run(vcpu);
+
+       switch (get_ucall(vcpu, &uc)) {
+       case UCALL_DONE:
+               return;
+       case UCALL_ABORT:
+               REPORT_GUEST_ASSERT(uc);
+               break;
+       case UCALL_NONE:
+               if (vcpu->run->exit_reason == KVM_EXIT_MMIO)
+                       TEST_FAIL("Unexpected MMIO exit at 0x%llx",
+                                 vcpu->run->mmio.phys_addr);
+               fallthrough;
+       default:
+               TEST_FAIL("Unexpected ucall %lu, exit_reason %u",
+                         uc.cmd, vcpu->run->exit_reason);
+       }
+}
+
 static void test_v3_new_redist_regions(void)
 {
        struct kvm_vcpu *vcpus[NR_VCPUS];
@@ -411,7 +460,8 @@ static void test_v3_new_redist_regions(void)
        u64 addr;
        int ret;
 
-       v = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, NR_VCPUS, vcpus);
+       v = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, NR_VCPUS,
+                                    guest_code, vcpus);
        subtest_v3_redist_regions(&v);
        kvm_device_attr_set(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
                            KVM_DEV_ARM_VGIC_CTRL_INIT, NULL);
@@ -420,12 +470,13 @@ static void test_v3_new_redist_regions(void)
        TEST_ASSERT(ret == -ENXIO, "running without sufficient number of 
rdists");
        vm_gic_destroy(&v);
 
-       /* step2 */
+       /* Step 2: adding enough redistributors after KVM_RUN is rejected. */
 
-       v = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, NR_VCPUS, vcpus);
+       v = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, NR_VCPUS,
+                                    guest_code, vcpus);
        subtest_v3_redist_regions(&v);
 
-       addr = REDIST_REGION_ATTR_ADDR(1, 0x280000, 0, 2);
+       addr = REDIST_REGION_ATTR_ADDR(1, REDIST_TEST_REGION2_BASE, 0, 2);
        kvm_device_attr_set(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
                            KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr);
 
@@ -434,9 +485,10 @@ static void test_v3_new_redist_regions(void)
 
        vm_gic_destroy(&v);
 
-       /* step 3 */
+       /* Step 3: retry the failed region setup and exercise an existing 
rdist. */
 
-       v = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, NR_VCPUS, vcpus);
+       v = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, NR_VCPUS,
+                                    guest_read_second_redist, vcpus);
        subtest_v3_redist_regions(&v);
 
        ret = __kvm_device_attr_set(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
@@ -444,15 +496,17 @@ static void test_v3_new_redist_regions(void)
        TEST_ASSERT(ret && errno == EFAULT,
                    "register a third region allowing to cover the 4 vcpus");
 
-       addr = REDIST_REGION_ATTR_ADDR(1, 0x280000, 0, 2);
+       addr = REDIST_REGION_ATTR_ADDR(1, REDIST_TEST_REGION2_BASE, 0, 2);
        kvm_device_attr_set(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
                            KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr);
 
+       virt_map(v.vm, REDIST_TEST_REGION0_BASE, REDIST_TEST_REGION0_BASE,
+                vm_calc_num_guest_pages(v.vm->mode,
+                                        2 * KVM_VGIC_V3_REDIST_SIZE));
        kvm_device_attr_set(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
                            KVM_DEV_ARM_VGIC_CTRL_INIT, NULL);
 
-       ret = run_vcpu(vcpus[3]);
-       TEST_ASSERT(!ret, "vcpu run");
+       run_vcpu_expect_done(vcpus[0]);
 
        vm_gic_destroy(&v);
 }
@@ -608,7 +662,8 @@ static void test_v3_redist_ipa_range_check_at_vcpu_run(void)
        int ret, i;
        u64 addr;
 
-       v = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, 1, vcpus);
+       v = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, 1,
+                                    guest_code, vcpus);
 
        /* Set space for 3 redists, we have 1 vcpu, so this succeeds. */
        addr = max_phys_size - (3 * 2 * 0x10000);
@@ -641,7 +696,8 @@ static void test_v3_its_region(void)
        u64 addr;
        int its_fd, ret;
 
-       v = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, NR_VCPUS, vcpus);
+       v = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, NR_VCPUS,
+                                    guest_code, vcpus);
        its_fd = kvm_create_device(v.vm, KVM_DEV_TYPE_ARM_VGIC_ITS);
 
        addr = 0x401000;
@@ -684,7 +740,8 @@ static void test_v3_nassgicap(void)
        u32 typer2;
        int ret;
 
-       vm = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, NR_VCPUS, 
vcpus);
+       vm = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, NR_VCPUS,
+                                     guest_code, vcpus);
        kvm_device_attr_get(vm.gic_fd, KVM_DEV_ARM_VGIC_GRP_DIST_REGS,
                            GICD_TYPER2, &typer2);
        has_nassgicap = typer2 & GICD_TYPER2_nASSGIcap;
-- 
2.53.0


Reply via email to