Commit cb3f0ad8 authored by Christoffer Dall's avatar Christoffer Dall

KVM: arm/arm64: Disallow userspace control of in-kernel IRQ lines

When injecting an IRQ to the VGIC, you now have to present an owner
token for that IRQ line to show that you are the owner of that line.

IRQ lines driven from userspace or via an irqfd do not have an owner and
will simply pass a NULL pointer.

Also get rid of the unused kvm_vgic_inject_mapped_irq prototype.
Signed-off-by: default avatarChristoffer Dall <cdall@linaro.org>
Acked-by: default avatarMarc Zyngier <marc.zyngier@arm.com>
parent abcb851d
...@@ -300,9 +300,7 @@ int kvm_vgic_hyp_init(void); ...@@ -300,9 +300,7 @@ int kvm_vgic_hyp_init(void);
void kvm_vgic_init_cpu_hardware(void); void kvm_vgic_init_cpu_hardware(void);
int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int intid, int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int intid,
bool level); bool level, void *owner);
int kvm_vgic_inject_mapped_irq(struct kvm *kvm, int cpuid, unsigned int intid,
bool level);
int kvm_vgic_map_phys_irq(struct kvm_vcpu *vcpu, u32 virt_irq, u32 phys_irq); int kvm_vgic_map_phys_irq(struct kvm_vcpu *vcpu, u32 virt_irq, u32 phys_irq);
int kvm_vgic_unmap_phys_irq(struct kvm_vcpu *vcpu, unsigned int virt_irq); int kvm_vgic_unmap_phys_irq(struct kvm_vcpu *vcpu, unsigned int virt_irq);
bool kvm_vgic_map_is_active(struct kvm_vcpu *vcpu, unsigned int virt_irq); bool kvm_vgic_map_is_active(struct kvm_vcpu *vcpu, unsigned int virt_irq);
......
...@@ -226,7 +226,8 @@ static void kvm_timer_update_irq(struct kvm_vcpu *vcpu, bool new_level, ...@@ -226,7 +226,8 @@ static void kvm_timer_update_irq(struct kvm_vcpu *vcpu, bool new_level,
if (likely(irqchip_in_kernel(vcpu->kvm))) { if (likely(irqchip_in_kernel(vcpu->kvm))) {
ret = kvm_vgic_inject_irq(vcpu->kvm, vcpu->vcpu_id, ret = kvm_vgic_inject_irq(vcpu->kvm, vcpu->vcpu_id,
timer_ctx->irq.irq, timer_ctx->irq.irq,
timer_ctx->irq.level); timer_ctx->irq.level,
timer_ctx);
WARN_ON(ret); WARN_ON(ret);
} }
} }
......
...@@ -832,7 +832,7 @@ int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level, ...@@ -832,7 +832,7 @@ int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level,
if (irq_num < VGIC_NR_SGIS || irq_num >= VGIC_NR_PRIVATE_IRQS) if (irq_num < VGIC_NR_SGIS || irq_num >= VGIC_NR_PRIVATE_IRQS)
return -EINVAL; return -EINVAL;
return kvm_vgic_inject_irq(kvm, vcpu->vcpu_id, irq_num, level); return kvm_vgic_inject_irq(kvm, vcpu->vcpu_id, irq_num, level, NULL);
case KVM_ARM_IRQ_TYPE_SPI: case KVM_ARM_IRQ_TYPE_SPI:
if (!irqchip_in_kernel(kvm)) if (!irqchip_in_kernel(kvm))
return -ENXIO; return -ENXIO;
...@@ -840,7 +840,7 @@ int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level, ...@@ -840,7 +840,7 @@ int kvm_vm_ioctl_irq_line(struct kvm *kvm, struct kvm_irq_level *irq_level,
if (irq_num < VGIC_NR_PRIVATE_IRQS) if (irq_num < VGIC_NR_PRIVATE_IRQS)
return -EINVAL; return -EINVAL;
return kvm_vgic_inject_irq(kvm, 0, irq_num, level); return kvm_vgic_inject_irq(kvm, 0, irq_num, level, NULL);
} }
return -EINVAL; return -EINVAL;
......
...@@ -215,7 +215,8 @@ static void kvm_pmu_check_overflow(struct kvm_vcpu *vcpu) ...@@ -215,7 +215,8 @@ static void kvm_pmu_check_overflow(struct kvm_vcpu *vcpu)
if (likely(irqchip_in_kernel(vcpu->kvm))) { if (likely(irqchip_in_kernel(vcpu->kvm))) {
int ret = kvm_vgic_inject_irq(vcpu->kvm, vcpu->vcpu_id, int ret = kvm_vgic_inject_irq(vcpu->kvm, vcpu->vcpu_id,
pmu->irq_num, overflow); pmu->irq_num, overflow,
&vcpu->arch.pmu);
WARN_ON(ret); WARN_ON(ret);
} }
} }
......
...@@ -34,7 +34,7 @@ static int vgic_irqfd_set_irq(struct kvm_kernel_irq_routing_entry *e, ...@@ -34,7 +34,7 @@ static int vgic_irqfd_set_irq(struct kvm_kernel_irq_routing_entry *e,
if (!vgic_valid_spi(kvm, spi_id)) if (!vgic_valid_spi(kvm, spi_id))
return -EINVAL; return -EINVAL;
return kvm_vgic_inject_irq(kvm, 0, spi_id, level); return kvm_vgic_inject_irq(kvm, 0, spi_id, level, NULL);
} }
/** /**
......
...@@ -235,10 +235,14 @@ static void vgic_sort_ap_list(struct kvm_vcpu *vcpu) ...@@ -235,10 +235,14 @@ static void vgic_sort_ap_list(struct kvm_vcpu *vcpu)
/* /*
* Only valid injection if changing level for level-triggered IRQs or for a * Only valid injection if changing level for level-triggered IRQs or for a
* rising edge. * rising edge, and in-kernel connected IRQ lines can only be controlled by
* their owner.
*/ */
static bool vgic_validate_injection(struct vgic_irq *irq, bool level) static bool vgic_validate_injection(struct vgic_irq *irq, bool level, void *owner)
{ {
if (irq->owner != owner)
return false;
switch (irq->config) { switch (irq->config) {
case VGIC_CONFIG_LEVEL: case VGIC_CONFIG_LEVEL:
return irq->line_level != level; return irq->line_level != level;
...@@ -350,13 +354,16 @@ bool vgic_queue_irq_unlock(struct kvm *kvm, struct vgic_irq *irq) ...@@ -350,13 +354,16 @@ bool vgic_queue_irq_unlock(struct kvm *kvm, struct vgic_irq *irq)
* false: to ignore the call * false: to ignore the call
* Level-sensitive true: raise the input signal * Level-sensitive true: raise the input signal
* false: lower the input signal * false: lower the input signal
* @owner: The opaque pointer to the owner of the IRQ being raised to verify
* that the caller is allowed to inject this IRQ. Userspace
* injections will have owner == NULL.
* *
* The VGIC is not concerned with devices being active-LOW or active-HIGH for * The VGIC is not concerned with devices being active-LOW or active-HIGH for
* level-sensitive interrupts. You can think of the level parameter as 1 * level-sensitive interrupts. You can think of the level parameter as 1
* being HIGH and 0 being LOW and all devices being active-HIGH. * being HIGH and 0 being LOW and all devices being active-HIGH.
*/ */
int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int intid, int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int intid,
bool level) bool level, void *owner)
{ {
struct kvm_vcpu *vcpu; struct kvm_vcpu *vcpu;
struct vgic_irq *irq; struct vgic_irq *irq;
...@@ -378,7 +385,7 @@ int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int intid, ...@@ -378,7 +385,7 @@ int kvm_vgic_inject_irq(struct kvm *kvm, int cpuid, unsigned int intid,
spin_lock(&irq->irq_lock); spin_lock(&irq->irq_lock);
if (!vgic_validate_injection(irq, level)) { if (!vgic_validate_injection(irq, level, owner)) {
/* Nothing to see here, move along... */ /* Nothing to see here, move along... */
spin_unlock(&irq->irq_lock); spin_unlock(&irq->irq_lock);
vgic_put_irq(kvm, irq); vgic_put_irq(kvm, irq);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment