* [RFC PATCH 0/2] KVM: arm64: fix VGICv3 redistributor rollback @ 2026-08-10 6:52 Karl Mehltretter 2026-08-10 6:52 ` [RFC PATCH 1/2] KVM: arm64: vgic-v3: Roll back failed redistributor region setup Karl Mehltretter 2026-08-10 6:52 ` [RFC PATCH 2/2] KVM: arm64: selftests: Test VGICv3 redistributor region retry Karl Mehltretter 0 siblings, 2 replies; 5+ messages in thread From: Karl Mehltretter @ 2026-08-10 6:52 UTC (permalink / raw) To: Marc Zyngier, Oliver Upton, kvmarm Cc: Karl Mehltretter, Fuad Tabba, Joey Gouly, Steffen Eiden, Suzuki K Poulose, Zenghui Yu, Catalin Marinas, Will Deacon, Paolo Bonzini, Shuah Khan, Eric Auger, kvm, linux-arm-kernel, linux-kernel, linux-kselftest A failed REDIST_REGION update can unregister redistributor iodevs belonging to earlier regions without clearing their cached base addresses. A corrected retry then succeeds without restoring the missing iodevs. Limit rollback to vCPUs assigned to the newly added region. Extend vgic_init to access an earlier redistributor after a failed update and retry. Commits 8542a8f95a67, 59112e9c390b, and 0d92e4a7ffd5 progressively fixed cleanup after a failed region update. Preserving earlier regions follows that model. Built with GCC 15.2.0 and tested under QEMU 10.2.1 TCG with -machine virt,virtualization=on,gic-version=3 and -cpu max. Both runs reached the VGICv3 tests. The patched kernel passed; the same selftest against the unpatched base failed with: Unexpected MMIO exit at 0x8050008 Karl Mehltretter (2): KVM: arm64: vgic-v3: Roll back failed redistributor region setup KVM: arm64: selftests: Test VGICv3 redistributor region retry arch/arm64/kvm/vgic/vgic-mmio-v3.c | 42 ++++--- tools/testing/selftests/kvm/arm64/vgic_init.c | 115 +++++++++++++----- 2 files changed, 112 insertions(+), 45 deletions(-) base-commit: b9b3e33b70b71e516930117e21de3ad2a7723747 -- 2.53.0 ^ permalink raw reply [flat|nested] 5+ messages in thread
* [RFC PATCH 1/2] KVM: arm64: vgic-v3: Roll back failed redistributor region setup 2026-08-10 6:52 [RFC PATCH 0/2] KVM: arm64: fix VGICv3 redistributor rollback Karl Mehltretter @ 2026-08-10 6:52 ` Karl Mehltretter 2026-08-10 14:03 ` Marc Zyngier 2026-08-10 6:52 ` [RFC PATCH 2/2] KVM: arm64: selftests: Test VGICv3 redistributor region retry Karl Mehltretter 1 sibling, 1 reply; 5+ messages in thread From: Karl Mehltretter @ 2026-08-10 6:52 UTC (permalink / raw) To: Marc Zyngier, Oliver Upton, kvmarm Cc: Karl Mehltretter, Fuad Tabba, Joey Gouly, Steffen Eiden, Suzuki K Poulose, Zenghui Yu, Catalin Marinas, Will Deacon, Paolo Bonzini, Shuah Khan, Eric Auger, kvm, linux-arm-kernel, linux-kernel, linux-kselftest A later REDIST_REGION attribute can be inserted successfully and then fail while registering redistributor iodevs. For example, a region that overlaps the distributor is caught by vgic_v3_check_base() when the first unassigned vCPU is processed. The existing rollback unregisters all previously registered redistributor iodevs. It leaves their rd_iodev.base_addr values set, so a corrected retry skips re-registering those stale vCPUs and succeeds with holes in the MMIO bus. A failure from kvm_io_bus_register_dev() can leave another hole. The failing vCPU already has its base address and region assigned, but the old i < c rollback does not include it. Preserve devices assigned by earlier successful setters. On failure, unregister only vCPUs associated with the newly inserted region, clear their cached base addresses, and free that region. This also includes the current vCPU when iodev registration itself fails. Fixes: c011f4ea106b ("KVM: arm/arm64: Check vcpu redist base before registering an iodev") Assisted-by: Codex:gpt-5.6-sol Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com> --- arch/arm64/kvm/vgic/vgic-mmio-v3.c | 42 ++++++++++++++++++------------ 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/arch/arm64/kvm/vgic/vgic-mmio-v3.c b/arch/arm64/kvm/vgic/vgic-mmio-v3.c index 5913a20d83019..832a7fe65984c 100644 --- a/arch/arm64/kvm/vgic/vgic-mmio-v3.c +++ b/arch/arm64/kvm/vgic/vgic-mmio-v3.c @@ -841,6 +841,31 @@ void vgic_unregister_redist_iodev(struct kvm_vcpu *vcpu) kvm_io_bus_unregister_dev(vcpu->kvm, KVM_MMIO_BUS, &rd_dev->dev); } +static void vgic_v3_rollback_redist_region(struct kvm *kvm, u32 index) +{ + struct vgic_redist_region *rdreg; + struct kvm_vcpu *vcpu; + unsigned long c; + + lockdep_assert_held(&kvm->slots_lock); + + rdreg = vgic_v3_rdist_region_from_index(kvm, index); + + kvm_for_each_vcpu(c, vcpu, kvm) { + if (vcpu->arch.vgic_cpu.rdreg == rdreg) + vgic_unregister_redist_iodev(vcpu); + } + + guard(mutex)(&kvm->arch.config_lock); + + kvm_for_each_vcpu(c, vcpu, kvm) { + if (vcpu->arch.vgic_cpu.rdreg == rdreg) + vcpu->arch.vgic_cpu.rd_iodev.base_addr = VGIC_ADDR_UNDEF; + } + + vgic_v3_free_redist_region(kvm, rdreg); +} + static int vgic_register_all_redist_iodevs(struct kvm *kvm) { struct kvm_vcpu *vcpu; @@ -855,16 +880,6 @@ static int vgic_register_all_redist_iodevs(struct kvm *kvm) break; } - if (ret) { - /* The current c failed, so iterate over the previous ones. */ - int i; - - for (i = 0; i < c; i++) { - vcpu = kvm_get_vcpu(kvm, i); - vgic_unregister_redist_iodev(vcpu); - } - } - return ret; } @@ -984,12 +999,7 @@ int vgic_v3_set_redist_base(struct kvm *kvm, u32 index, u64 addr, u32 count) */ ret = vgic_register_all_redist_iodevs(kvm); if (ret) { - struct vgic_redist_region *rdreg; - - mutex_lock(&kvm->arch.config_lock); - rdreg = vgic_v3_rdist_region_from_index(kvm, index); - vgic_v3_free_redist_region(kvm, rdreg); - mutex_unlock(&kvm->arch.config_lock); + vgic_v3_rollback_redist_region(kvm, index); return ret; } -- 2.53.0 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [RFC PATCH 1/2] KVM: arm64: vgic-v3: Roll back failed redistributor region setup 2026-08-10 6:52 ` [RFC PATCH 1/2] KVM: arm64: vgic-v3: Roll back failed redistributor region setup Karl Mehltretter @ 2026-08-10 14:03 ` Marc Zyngier 2026-08-10 21:22 ` Karl Mehltretter 0 siblings, 1 reply; 5+ messages in thread From: Marc Zyngier @ 2026-08-10 14:03 UTC (permalink / raw) To: Karl Mehltretter Cc: Oliver Upton, kvmarm, Fuad Tabba, Joey Gouly, Steffen Eiden, Suzuki K Poulose, Zenghui Yu, Catalin Marinas, Will Deacon, Paolo Bonzini, Shuah Khan, Eric Auger, kvm, linux-arm-kernel, linux-kernel, linux-kselftest On Mon, 10 Aug 2026 07:52:16 +0100, Karl Mehltretter <kmehltretter@gmail.com> wrote: I'm sorry, but I find it difficult understand what you are trying to explain here: > A later REDIST_REGION attribute can be inserted successfully and then Later than what? > fail while registering redistributor iodevs. For example, a region that > overlaps the distributor is caught by vgic_v3_check_base() when the first > unassigned vCPU is processed. > > The existing rollback unregisters all previously registered > redistributor iodevs. It leaves their rd_iodev.base_addr values set, so a > corrected retry skips re-registering those stale vCPUs and succeeds with > holes in the MMIO bus. Holes in the MMIO space are the norm. The IPA space can multi-TB large, and there is no reason why it'd cover everything (where would you place the RAM otherwise?). Is the problem here that you are left with vcpus that seem to have been matched to an RD (base_addr being set), but that really are left unconnected? > > A failure from kvm_io_bus_register_dev() can leave another hole. The MMIO hole? Coverage hole? > failing vCPU already has its base address and region assigned, but the > old i < c rollback does not include it. What is "it"? > > Preserve devices assigned by earlier successful setters. On failure, > unregister only vCPUs associated with the newly inserted region, clear > their cached base addresses, and free that region. This also includes the > current vCPU when iodev registration itself fails. What I don't see here is an argument explaining that doing this doesn't change the guest-visible assignment of RDs, which would be a regression. > > Fixes: c011f4ea106b ("KVM: arm/arm64: Check vcpu redist base before registering an iodev") > Assisted-by: Codex:gpt-5.6-sol > Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com> > --- > arch/arm64/kvm/vgic/vgic-mmio-v3.c | 42 ++++++++++++++++++------------ > 1 file changed, 26 insertions(+), 16 deletions(-) > > diff --git a/arch/arm64/kvm/vgic/vgic-mmio-v3.c b/arch/arm64/kvm/vgic/vgic-mmio-v3.c > index 5913a20d83019..832a7fe65984c 100644 > --- a/arch/arm64/kvm/vgic/vgic-mmio-v3.c > +++ b/arch/arm64/kvm/vgic/vgic-mmio-v3.c > @@ -841,6 +841,31 @@ void vgic_unregister_redist_iodev(struct kvm_vcpu *vcpu) > kvm_io_bus_unregister_dev(vcpu->kvm, KVM_MMIO_BUS, &rd_dev->dev); > } > > +static void vgic_v3_rollback_redist_region(struct kvm *kvm, u32 index) > +{ > + struct vgic_redist_region *rdreg; > + struct kvm_vcpu *vcpu; > + unsigned long c; > + > + lockdep_assert_held(&kvm->slots_lock); > + > + rdreg = vgic_v3_rdist_region_from_index(kvm, index); > + > + kvm_for_each_vcpu(c, vcpu, kvm) { > + if (vcpu->arch.vgic_cpu.rdreg == rdreg) > + vgic_unregister_redist_iodev(vcpu); > + } > + > + guard(mutex)(&kvm->arch.config_lock); > + > + kvm_for_each_vcpu(c, vcpu, kvm) { > + if (vcpu->arch.vgic_cpu.rdreg == rdreg) > + vcpu->arch.vgic_cpu.rd_iodev.base_addr = VGIC_ADDR_UNDEF; > + } > + > + vgic_v3_free_redist_region(kvm, rdreg); > +} > + > static int vgic_register_all_redist_iodevs(struct kvm *kvm) > { > struct kvm_vcpu *vcpu; > @@ -855,16 +880,6 @@ static int vgic_register_all_redist_iodevs(struct kvm *kvm) > break; > } > > - if (ret) { > - /* The current c failed, so iterate over the previous ones. */ > - int i; > - > - for (i = 0; i < c; i++) { > - vcpu = kvm_get_vcpu(kvm, i); > - vgic_unregister_redist_iodev(vcpu); > - } > - } > - > return ret; > } > > @@ -984,12 +999,7 @@ int vgic_v3_set_redist_base(struct kvm *kvm, u32 index, u64 addr, u32 count) > */ > ret = vgic_register_all_redist_iodevs(kvm); > if (ret) { > - struct vgic_redist_region *rdreg; > - > - mutex_lock(&kvm->arch.config_lock); > - rdreg = vgic_v3_rdist_region_from_index(kvm, index); > - vgic_v3_free_redist_region(kvm, rdreg); > - mutex_unlock(&kvm->arch.config_lock); > + vgic_v3_rollback_redist_region(kvm, index); > return ret; > } > Based on what I understand of your earlier description, why isn't this as simple as this untested hack: diff --git a/arch/arm64/kvm/vgic/vgic-mmio-v3.c b/arch/arm64/kvm/vgic/vgic-mmio-v3.c index 5913a20d83019..804fcc69cbd48 100644 --- a/arch/arm64/kvm/vgic/vgic-mmio-v3.c +++ b/arch/arm64/kvm/vgic/vgic-mmio-v3.c @@ -859,7 +859,7 @@ static int vgic_register_all_redist_iodevs(struct kvm *kvm) /* The current c failed, so iterate over the previous ones. */ int i; - for (i = 0; i < c; i++) { + for (i = 0; i <= c; i++) { vcpu = kvm_get_vcpu(kvm, i); vgic_unregister_redist_iodev(vcpu); } @@ -960,8 +960,10 @@ void vgic_v3_free_redist_region(struct kvm *kvm, struct vgic_redist_region *rdre /* Garbage collect the region */ kvm_for_each_vcpu(c, vcpu, kvm) { - if (vcpu->arch.vgic_cpu.rdreg == rdreg) + if (vcpu->arch.vgic_cpu.rdreg == rdreg) { vcpu->arch.vgic_cpu.rdreg = NULL; + vcpu->arch.vgic_cpu.rd_iodev.base_addr = VGIC_ADDR_UNDEF; + } } list_del(&rdreg->list); I don't mind the cleaning up, but not as part of fixing the issue, which has to be as small as possible (think of the backports). Thanks, M. -- Without deviation from the norm, progress is not possible. ^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [RFC PATCH 1/2] KVM: arm64: vgic-v3: Roll back failed redistributor region setup 2026-08-10 14:03 ` Marc Zyngier @ 2026-08-10 21:22 ` Karl Mehltretter 0 siblings, 0 replies; 5+ messages in thread From: Karl Mehltretter @ 2026-08-10 21:22 UTC (permalink / raw) To: Marc Zyngier Cc: Oliver Upton, kvmarm, Fuad Tabba, Joey Gouly, Steffen Eiden, Suzuki K Poulose, Zenghui Yu, Catalin Marinas, Will Deacon, Paolo Bonzini, Shuah Khan, Eric Auger, kvm, linux-arm-kernel, linux-kernel, linux-kselftest On Mon, Aug 10, 2026 at 03:03:44PM +0100, Marc Zyngier wrote: > > > A later REDIST_REGION attribute can be inserted successfully and then > > Later than what? I meant a REDIST_REGION write after earlier region writes have already assigned RDs to some vCPUs. In the selftest (patch 2) - region 0 contains RDs for vCPUs 0 and 1 - region 1 contains RD for vCPU 2 - the REDIST_REGION write for region 2 fails while KVM processes vCPU 3 > Holes in the MMIO space are the norm. The IPA space can multi-TB > large, and there is no reason why it'd cover everything (where would > you place the RAM otherwise?). > > Is the problem here that you are left with vcpus that seem to have > been matched to an RD (base_addr being set), but that really are left > unconnected? Yes I guess "hole" is the wrong term. The vCPU still has an RD and a base address, but its iodev has been removed from KVM_MMIO_BUS. An access to that RD address then causes KVM_RUN to return to userspace with KVM_EXIT_MMIO. > > > failing vCPU already has its base address and region assigned, but the > > old i < c rollback does not include it. > > What is "it"? I meant the current vCPU redistributor iodev. vgic_register_redist_iodev() sets the vCPU's region and base address before calling kvm_io_bus_register_dev(). If this call fails for vCPU c, the rollback in vgic_register_all_redist_iodevs() processes only vCPUs with indices below c. > > Preserve devices assigned by earlier successful setters. On failure, > > unregister only vCPUs associated with the newly inserted region, clear > > their cached base addresses, and free that region. This also includes the > > current vCPU when iodev registration itself fails. > > What I don't see here is an argument explaining that doing this > doesn't change the guest-visible assignment of RDs, which would be a > regression. vgic_register_redist_iodev() returns immediately if a vCPU's RD base address is already set, so its RD region and address don't change. Regions are filled in index order, a vCPU without an RD address can use the new region only after older regions are full. So freeing the new region and clearing the RD state of vCPUs associated with it restores the state before the failed write. > Based on what I understand of your earlier description, why isn't this > as simple as this untested hack: I ran the selftest (patch 2) with this, still failed with: Unexpected MMIO exit at 0x8050008 When redistributor registration for vCPU 3 fails, the for loop does vCPUs 0 to 3. The issue is that the iodevs for vCPUs 0 to 2 were registered by earlier successful region writes and should not be unregistered. On retry, vgic_register_redist_iodev() sees the set addresses and does not re-register those iodevs. > I don't mind the cleaning up, but not as part of fixing the issue, > which has to be as small as possible (think of the backports). I reworked the change without a new helper (see below). Is this closer to what you had in mind? diff --git a/arch/arm64/kvm/vgic/vgic-mmio-v3.c b/arch/arm64/kvm/vgic/vgic-mmio-v3.c index 5913a20d83019..d9a28b983ecca 100644 --- a/arch/arm64/kvm/vgic/vgic-mmio-v3.c +++ b/arch/arm64/kvm/vgic/vgic-mmio-v3.c @@ -841,7 +841,7 @@ void vgic_unregister_redist_iodev(struct kvm_vcpu *vcpu) kvm_io_bus_unregister_dev(vcpu->kvm, KVM_MMIO_BUS, &rd_dev->dev); } -static int vgic_register_all_redist_iodevs(struct kvm *kvm) +static int vgic_register_all_redist_iodevs(struct kvm *kvm, u32 index) { struct kvm_vcpu *vcpu; unsigned long c; @@ -856,12 +856,15 @@ static int vgic_register_all_redist_iodevs(struct kvm *kvm) } if (ret) { - /* The current c failed, so iterate over the previous ones. */ + struct vgic_redist_region *rdreg; int i; - for (i = 0; i < c; i++) { + rdreg = vgic_v3_rdist_region_from_index(kvm, index); + + for (i = 0; i <= c; i++) { vcpu = kvm_get_vcpu(kvm, i); - vgic_unregister_redist_iodev(vcpu); + if (vcpu->arch.vgic_cpu.rdreg == rdreg) + vgic_unregister_redist_iodev(vcpu); } } @@ -960,8 +963,10 @@ void vgic_v3_free_redist_region(struct kvm *kvm, struct vgic_redist_region *rdre /* Garbage collect the region */ kvm_for_each_vcpu(c, vcpu, kvm) { - if (vcpu->arch.vgic_cpu.rdreg == rdreg) + if (vcpu->arch.vgic_cpu.rdreg == rdreg) { vcpu->arch.vgic_cpu.rdreg = NULL; + vcpu->arch.vgic_cpu.rd_iodev.base_addr = VGIC_ADDR_UNDEF; + } } list_del(&rdreg->list); @@ -982,7 +987,7 @@ int vgic_v3_set_redist_base(struct kvm *kvm, u32 index, u64 addr, u32 count) * Register iodevs for each existing VCPU. Adding more VCPUs * afterwards will register the iodevs when needed. */ - ret = vgic_register_all_redist_iodevs(kvm); + ret = vgic_register_all_redist_iodevs(kvm, index); if (ret) { struct vgic_redist_region *rdreg; Thanks, Karl ^ permalink raw reply related [flat|nested] 5+ messages in thread
* [RFC PATCH 2/2] KVM: arm64: selftests: Test VGICv3 redistributor region retry 2026-08-10 6:52 [RFC PATCH 0/2] KVM: arm64: fix VGICv3 redistributor rollback Karl Mehltretter 2026-08-10 6:52 ` [RFC PATCH 1/2] KVM: arm64: vgic-v3: Roll back failed redistributor region setup Karl Mehltretter @ 2026-08-10 6:52 ` Karl Mehltretter 1 sibling, 0 replies; 5+ messages in thread From: Karl Mehltretter @ 2026-08-10 6:52 UTC (permalink / raw) To: Marc Zyngier, Oliver Upton, kvmarm Cc: Karl Mehltretter, Fuad Tabba, Joey Gouly, Steffen Eiden, Suzuki K Poulose, Zenghui Yu, Catalin Marinas, Will Deacon, Paolo Bonzini, Shuah Khan, Eric Auger, kvm, linux-arm-kernel, linux-kernel, linux-kselftest 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 <kmehltretter@gmail.com> --- 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 ^ permalink raw reply related [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-08-10 21:22 UTC | newest] Thread overview: 5+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-08-10 6:52 [RFC PATCH 0/2] KVM: arm64: fix VGICv3 redistributor rollback Karl Mehltretter 2026-08-10 6:52 ` [RFC PATCH 1/2] KVM: arm64: vgic-v3: Roll back failed redistributor region setup Karl Mehltretter 2026-08-10 14:03 ` Marc Zyngier 2026-08-10 21:22 ` Karl Mehltretter 2026-08-10 6:52 ` [RFC PATCH 2/2] KVM: arm64: selftests: Test VGICv3 redistributor region retry Karl Mehltretter
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox