Merge tag 'kvm-arm-for-v4.9-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/kvmarm/kvmarm into HEAD

KVM/ARM updates for v4.9-rc4

- Kick the vcpu when a pending interrupt becomes pending again
- Prevent access to invalid interrupt registers
- Invalid TLBs when two vcpus from the same VM share a CPU
This commit is contained in:
Paolo Bonzini
2016-11-11 11:13:36 +01:00
12 changed files with 112 additions and 23 deletions

View File

@@ -453,17 +453,33 @@ struct vgic_io_device *kvm_to_vgic_iodev(const struct kvm_io_device *dev)
return container_of(dev, struct vgic_io_device, dev);
}
static bool check_region(const struct vgic_register_region *region,
static bool check_region(const struct kvm *kvm,
const struct vgic_register_region *region,
gpa_t addr, int len)
{
if ((region->access_flags & VGIC_ACCESS_8bit) && len == 1)
return true;
if ((region->access_flags & VGIC_ACCESS_32bit) &&
len == sizeof(u32) && !(addr & 3))
return true;
if ((region->access_flags & VGIC_ACCESS_64bit) &&
len == sizeof(u64) && !(addr & 7))
return true;
int flags, nr_irqs = kvm->arch.vgic.nr_spis + VGIC_NR_PRIVATE_IRQS;
switch (len) {
case sizeof(u8):
flags = VGIC_ACCESS_8bit;
break;
case sizeof(u32):
flags = VGIC_ACCESS_32bit;
break;
case sizeof(u64):
flags = VGIC_ACCESS_64bit;
break;
default:
return false;
}
if ((region->access_flags & flags) && IS_ALIGNED(addr, len)) {
if (!region->bits_per_irq)
return true;
/* Do we access a non-allocated IRQ? */
return VGIC_ADDR_TO_INTID(addr, region->bits_per_irq) < nr_irqs;
}
return false;
}
@@ -477,7 +493,7 @@ static int dispatch_mmio_read(struct kvm_vcpu *vcpu, struct kvm_io_device *dev,
region = vgic_find_mmio_region(iodev->regions, iodev->nr_regions,
addr - iodev->base_addr);
if (!region || !check_region(region, addr, len)) {
if (!region || !check_region(vcpu->kvm, region, addr, len)) {
memset(val, 0, len);
return 0;
}
@@ -510,10 +526,7 @@ static int dispatch_mmio_write(struct kvm_vcpu *vcpu, struct kvm_io_device *dev,
region = vgic_find_mmio_region(iodev->regions, iodev->nr_regions,
addr - iodev->base_addr);
if (!region)
return 0;
if (!check_region(region, addr, len))
if (!region || !check_region(vcpu->kvm, region, addr, len))
return 0;
switch (iodev->iodev_type) {

View File

@@ -50,15 +50,15 @@ extern struct kvm_io_device_ops kvm_io_gic_ops;
#define VGIC_ADDR_IRQ_MASK(bits) (((bits) * 1024 / 8) - 1)
/*
* (addr & mask) gives us the byte offset for the INT ID, so we want to
* divide this with 'bytes per irq' to get the INT ID, which is given
* by '(bits) / 8'. But we do this with fixed-point-arithmetic and
* take advantage of the fact that division by a fraction equals
* multiplication with the inverted fraction, and scale up both the
* numerator and denominator with 8 to support at most 64 bits per IRQ:
* (addr & mask) gives us the _byte_ offset for the INT ID.
* We multiply this by 8 the get the _bit_ offset, then divide this by
* the number of bits to learn the actual INT ID.
* But instead of a division (which requires a "long long div" implementation),
* we shift by the binary logarithm of <bits>.
* This assumes that <bits> is a power of two.
*/
#define VGIC_ADDR_TO_INTID(addr, bits) (((addr) & VGIC_ADDR_IRQ_MASK(bits)) * \
64 / (bits) / 8)
8 >> ilog2(bits))
/*
* Some VGIC registers store per-IRQ information, with a different number

View File

@@ -273,6 +273,18 @@ retry:
* no more work for us to do.
*/
spin_unlock(&irq->irq_lock);
/*
* We have to kick the VCPU here, because we could be
* queueing an edge-triggered interrupt for which we
* get no EOI maintenance interrupt. In that case,
* while the IRQ is already on the VCPU's AP list, the
* VCPU could have EOI'ed the original interrupt and
* won't see this one until it exits for some other
* reason.
*/
if (vcpu)
kvm_vcpu_kick(vcpu);
return false;
}