Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

KVM: arm/arm64: Fix bug when registering redist iodevs

If userspace creates the VCPUs after initializing the VGIC, then we end
up in a situation where we trigger a bug in kvm_vcpu_get_idx(), because
it is called prior to adding the VCPU into the vcpus array on the VM.

There is no tight coupling between the VCPU index and the area of the
redistributor region used for the VCPU, so we can simply ensure that all
creations of redistributors are serialized per VM, and increment an
offset when we successfully add a redistributor.

The vgic_register_redist_iodev() function can be called from two paths:
vgic_redister_all_redist_iodev() which is called via the kvm_vgic_addr()
device attribute handler. This patch already holds the kvm->lock mutex.

The other path is via kvm_vgic_vcpu_init, which is called through a
longer chain from kvm_vm_ioctl_create_vcpu(), which releases the
kvm->lock mutex just before calling kvm_arch_vcpu_create(), so we can
simply take this mutex again later for our purposes.

Fixes: ab6f468c10 ("KVM: arm/arm64: Register iodevs when setting redist base and creating VCPUs")
Signed-off-by: Christoffer Dall <cdall@linaro.org>
Tested-by: Jean-Philippe Brucker <jean-philippe.brucker@arm.com>
Reviewed-by: Eric Auger <eric.auger@redhat.com>

+14 -5
+4 -1
include/kvm/arm_vgic.h
··· 195 195 /* either a GICv2 CPU interface */ 196 196 gpa_t vgic_cpu_base; 197 197 /* or a number of GICv3 redistributor regions */ 198 - gpa_t vgic_redist_base; 198 + struct { 199 + gpa_t vgic_redist_base; 200 + gpa_t vgic_redist_free_offset; 201 + }; 199 202 }; 200 203 201 204 /* distributor enabled */
+4 -1
virt/kvm/arm/vgic/vgic-init.c
··· 242 242 * If we are creating a VCPU with a GICv3 we must also register the 243 243 * KVM io device for the redistributor that belongs to this VCPU. 244 244 */ 245 - if (dist->vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3) 245 + if (dist->vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3) { 246 + mutex_lock(&vcpu->kvm->lock); 246 247 ret = vgic_register_redist_iodev(vcpu); 248 + mutex_unlock(&vcpu->kvm->lock); 249 + } 247 250 return ret; 248 251 } 249 252
+6 -3
virt/kvm/arm/vgic/vgic-mmio-v3.c
··· 586 586 if (!vgic_v3_check_base(kvm)) 587 587 return -EINVAL; 588 588 589 - rd_base = vgic->vgic_redist_base + kvm_vcpu_get_idx(vcpu) * SZ_64K * 2; 589 + rd_base = vgic->vgic_redist_base + vgic->vgic_redist_free_offset; 590 590 sgi_base = rd_base + SZ_64K; 591 591 592 592 kvm_iodevice_init(&rd_dev->dev, &kvm_io_gic_ops); ··· 615 615 ret = kvm_io_bus_register_dev(kvm, KVM_MMIO_BUS, sgi_base, 616 616 SZ_64K, &sgi_dev->dev); 617 617 mutex_unlock(&kvm->slots_lock); 618 - if (ret) 618 + if (ret) { 619 619 kvm_io_bus_unregister_dev(kvm, KVM_MMIO_BUS, 620 620 &rd_dev->dev); 621 + return ret; 622 + } 621 623 622 - return ret; 624 + vgic->vgic_redist_free_offset += 2 * SZ_64K; 625 + return 0; 623 626 } 624 627 625 628 static void vgic_unregister_redist_iodev(struct kvm_vcpu *vcpu)