| From f3c23a79d60434424a9352137f5993da3139503b Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Thu, 9 Apr 2020 13:05:26 +0100 |
| Subject: KVM: arm: vgic-v2: Only use the virtual state when userspace accesses |
| pending bits |
| |
| From: Marc Zyngier <maz@kernel.org> |
| |
| [ Upstream commit ba1ed9e17b581c9a204ec1d72d40472dd8557edd ] |
| |
| There is no point in accessing the HW when writing to any of the |
| ISPENDR/ICPENDR registers from userspace, as only the guest should |
| be allowed to change the HW state. |
| |
| Introduce new userspace-specific accessors that deal solely with |
| the virtual state. Note that the API differs from that of GICv3, |
| where userspace exclusively uses ISPENDR to set the state. Too |
| bad we can't reuse it. |
| |
| Fixes: 82e40f558de56 ("KVM: arm/arm64: vgic-v2: Handle SGI bits in GICD_I{S,C}PENDR0 as WI") |
| Reviewed-by: James Morse <james.morse@arm.com> |
| Signed-off-by: Marc Zyngier <maz@kernel.org> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| virt/kvm/arm/vgic/vgic-mmio-v2.c | 6 ++- |
| virt/kvm/arm/vgic/vgic-mmio.c | 87 ++++++++++++++++++++++++-------- |
| virt/kvm/arm/vgic/vgic-mmio.h | 8 +++ |
| 3 files changed, 77 insertions(+), 24 deletions(-) |
| |
| diff --git a/virt/kvm/arm/vgic/vgic-mmio-v2.c b/virt/kvm/arm/vgic/vgic-mmio-v2.c |
| index d63881f60e1a5..7b288eb391b84 100644 |
| --- a/virt/kvm/arm/vgic/vgic-mmio-v2.c |
| +++ b/virt/kvm/arm/vgic/vgic-mmio-v2.c |
| @@ -415,10 +415,12 @@ static const struct vgic_register_region vgic_v2_dist_registers[] = { |
| vgic_mmio_read_enable, vgic_mmio_write_cenable, NULL, NULL, 1, |
| VGIC_ACCESS_32bit), |
| REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_PENDING_SET, |
| - vgic_mmio_read_pending, vgic_mmio_write_spending, NULL, NULL, 1, |
| + vgic_mmio_read_pending, vgic_mmio_write_spending, |
| + NULL, vgic_uaccess_write_spending, 1, |
| VGIC_ACCESS_32bit), |
| REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_PENDING_CLEAR, |
| - vgic_mmio_read_pending, vgic_mmio_write_cpending, NULL, NULL, 1, |
| + vgic_mmio_read_pending, vgic_mmio_write_cpending, |
| + NULL, vgic_uaccess_write_cpending, 1, |
| VGIC_ACCESS_32bit), |
| REGISTER_DESC_WITH_BITS_PER_IRQ(GIC_DIST_ACTIVE_SET, |
| vgic_mmio_read_active, vgic_mmio_write_sactive, |
| diff --git a/virt/kvm/arm/vgic/vgic-mmio.c b/virt/kvm/arm/vgic/vgic-mmio.c |
| index f659654b09a83..b6824bba8248b 100644 |
| --- a/virt/kvm/arm/vgic/vgic-mmio.c |
| +++ b/virt/kvm/arm/vgic/vgic-mmio.c |
| @@ -179,17 +179,6 @@ unsigned long vgic_mmio_read_pending(struct kvm_vcpu *vcpu, |
| return value; |
| } |
| |
| -/* Must be called with irq->irq_lock held */ |
| -static void vgic_hw_irq_spending(struct kvm_vcpu *vcpu, struct vgic_irq *irq, |
| - bool is_uaccess) |
| -{ |
| - if (is_uaccess) |
| - return; |
| - |
| - irq->pending_latch = true; |
| - vgic_irq_set_phys_active(irq, true); |
| -} |
| - |
| static bool is_vgic_v2_sgi(struct kvm_vcpu *vcpu, struct vgic_irq *irq) |
| { |
| return (vgic_irq_is_sgi(irq->intid) && |
| @@ -200,7 +189,6 @@ void vgic_mmio_write_spending(struct kvm_vcpu *vcpu, |
| gpa_t addr, unsigned int len, |
| unsigned long val) |
| { |
| - bool is_uaccess = !kvm_get_running_vcpu(); |
| u32 intid = VGIC_ADDR_TO_INTID(addr, 1); |
| int i; |
| unsigned long flags; |
| @@ -215,22 +203,49 @@ void vgic_mmio_write_spending(struct kvm_vcpu *vcpu, |
| } |
| |
| raw_spin_lock_irqsave(&irq->irq_lock, flags); |
| + |
| + irq->pending_latch = true; |
| if (irq->hw) |
| - vgic_hw_irq_spending(vcpu, irq, is_uaccess); |
| - else |
| - irq->pending_latch = true; |
| + vgic_irq_set_phys_active(irq, true); |
| + |
| vgic_queue_irq_unlock(vcpu->kvm, irq, flags); |
| vgic_put_irq(vcpu->kvm, irq); |
| } |
| } |
| |
| -/* Must be called with irq->irq_lock held */ |
| -static void vgic_hw_irq_cpending(struct kvm_vcpu *vcpu, struct vgic_irq *irq, |
| - bool is_uaccess) |
| +int vgic_uaccess_write_spending(struct kvm_vcpu *vcpu, |
| + gpa_t addr, unsigned int len, |
| + unsigned long val) |
| { |
| - if (is_uaccess) |
| - return; |
| + u32 intid = VGIC_ADDR_TO_INTID(addr, 1); |
| + int i; |
| + unsigned long flags; |
| + |
| + for_each_set_bit(i, &val, len * 8) { |
| + struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); |
| + |
| + raw_spin_lock_irqsave(&irq->irq_lock, flags); |
| + irq->pending_latch = true; |
| + |
| + /* |
| + * GICv2 SGIs are terribly broken. We can't restore |
| + * the source of the interrupt, so just pick the vcpu |
| + * itself as the source... |
| + */ |
| + if (is_vgic_v2_sgi(vcpu, irq)) |
| + irq->source |= BIT(vcpu->vcpu_id); |
| + |
| + vgic_queue_irq_unlock(vcpu->kvm, irq, flags); |
| + |
| + vgic_put_irq(vcpu->kvm, irq); |
| + } |
| |
| + return 0; |
| +} |
| + |
| +/* Must be called with irq->irq_lock held */ |
| +static void vgic_hw_irq_cpending(struct kvm_vcpu *vcpu, struct vgic_irq *irq) |
| +{ |
| irq->pending_latch = false; |
| |
| /* |
| @@ -253,7 +268,6 @@ void vgic_mmio_write_cpending(struct kvm_vcpu *vcpu, |
| gpa_t addr, unsigned int len, |
| unsigned long val) |
| { |
| - bool is_uaccess = !kvm_get_running_vcpu(); |
| u32 intid = VGIC_ADDR_TO_INTID(addr, 1); |
| int i; |
| unsigned long flags; |
| @@ -270,7 +284,7 @@ void vgic_mmio_write_cpending(struct kvm_vcpu *vcpu, |
| raw_spin_lock_irqsave(&irq->irq_lock, flags); |
| |
| if (irq->hw) |
| - vgic_hw_irq_cpending(vcpu, irq, is_uaccess); |
| + vgic_hw_irq_cpending(vcpu, irq); |
| else |
| irq->pending_latch = false; |
| |
| @@ -279,6 +293,35 @@ void vgic_mmio_write_cpending(struct kvm_vcpu *vcpu, |
| } |
| } |
| |
| +int vgic_uaccess_write_cpending(struct kvm_vcpu *vcpu, |
| + gpa_t addr, unsigned int len, |
| + unsigned long val) |
| +{ |
| + u32 intid = VGIC_ADDR_TO_INTID(addr, 1); |
| + int i; |
| + unsigned long flags; |
| + |
| + for_each_set_bit(i, &val, len * 8) { |
| + struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i); |
| + |
| + raw_spin_lock_irqsave(&irq->irq_lock, flags); |
| + /* |
| + * More fun with GICv2 SGIs! If we're clearing one of them |
| + * from userspace, which source vcpu to clear? Let's not |
| + * even think of it, and blow the whole set. |
| + */ |
| + if (is_vgic_v2_sgi(vcpu, irq)) |
| + irq->source = 0; |
| + |
| + irq->pending_latch = false; |
| + |
| + raw_spin_unlock_irqrestore(&irq->irq_lock, flags); |
| + |
| + vgic_put_irq(vcpu->kvm, irq); |
| + } |
| + |
| + return 0; |
| +} |
| |
| /* |
| * If we are fiddling with an IRQ's active state, we have to make sure the IRQ |
| diff --git a/virt/kvm/arm/vgic/vgic-mmio.h b/virt/kvm/arm/vgic/vgic-mmio.h |
| index 30713a44e3faa..b127f889113ed 100644 |
| --- a/virt/kvm/arm/vgic/vgic-mmio.h |
| +++ b/virt/kvm/arm/vgic/vgic-mmio.h |
| @@ -149,6 +149,14 @@ void vgic_mmio_write_cpending(struct kvm_vcpu *vcpu, |
| gpa_t addr, unsigned int len, |
| unsigned long val); |
| |
| +int vgic_uaccess_write_spending(struct kvm_vcpu *vcpu, |
| + gpa_t addr, unsigned int len, |
| + unsigned long val); |
| + |
| +int vgic_uaccess_write_cpending(struct kvm_vcpu *vcpu, |
| + gpa_t addr, unsigned int len, |
| + unsigned long val); |
| + |
| unsigned long vgic_mmio_read_active(struct kvm_vcpu *vcpu, |
| gpa_t addr, unsigned int len); |
| |
| -- |
| 2.20.1 |
| |