KVM: arm/arm64: vgic-v2: Handle SGI bits in GICD_I{S,C}PENDR0 as WI [Linux 4.19.70]

This Linux kernel change "KVM: arm/arm64: vgic-v2: Handle SGI bits in GICD_I{S,C}PENDR0 as WI" is included in the Linux 4.19.70 release. This change is authored by Marc Zyngier <maz [at] kernel.org> on Wed Aug 28 11:10:16 2019 +0100. The commit for this change in Linux stable tree is 79f1b33 (patch) which is from upstream commit 82e40f5. The same Linux upstream change may have been applied to various maintained Linux releases and you can find all Linux releases containing changes from upstream 82e40f5.

KVM: arm/arm64: vgic-v2: Handle SGI bits in GICD_I{S,C}PENDR0 as WI

[ Upstream commit 82e40f558de566fdee214bec68096bbd5e64a6a4 ]

A guest is not allowed to inject a SGI (or clear its pending state)
by writing to GICD_ISPENDR0 (resp. GICD_ICPENDR0), as these bits are
defined as WI (as per ARM IHI 0048B 4.3.7 and 4.3.8).

Make sure we correctly emulate the architecture.

Fixes: 96b298000db4 ("KVM: arm/arm64: vgic-new: Add PENDING registers handlers")
Cc: stable@vger.kernel.org # 4.7+
Reported-by: Andre Przywara <andre.przywara@arm.com>
Signed-off-by: Marc Zyngier <maz@kernel.org>
Signed-off-by: Will Deacon <will@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>

There are 28 lines of Linux source code added/deleted in this change. Code changes to Linux kernel are as follows.

 virt/kvm/arm/vgic/vgic-mmio.c | 18 ++++++++++++++++++
 virt/kvm/arm/vgic/vgic-v2.c   |  5 ++++-
 virt/kvm/arm/vgic/vgic-v3.c   |  5 ++++-
 3 files changed, 26 insertions(+), 2 deletions(-)

diff --git a/virt/kvm/arm/vgic/vgic-mmio.c b/virt/kvm/arm/vgic/vgic-mmio.c
index ceeda7e..762f819 100644
--- a/virt/kvm/arm/vgic/vgic-mmio.c
+++ b/virt/kvm/arm/vgic/vgic-mmio.c
@@ -203,6 +203,12 @@ static void vgic_hw_irq_spending(struct kvm_vcpu *vcpu, struct vgic_irq *irq,
    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) &&
+       vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V2);
+}
+
 void vgic_mmio_write_spending(struct kvm_vcpu *vcpu,
                  gpa_t addr, unsigned int len,
                  unsigned long val)
@@ -215,6 +221,12 @@ void vgic_mmio_write_spending(struct kvm_vcpu *vcpu,
    for_each_set_bit(i, &val, len * 8) {
        struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);

+       /* GICD_ISPENDR0 SGI bits are WI */
+       if (is_vgic_v2_sgi(vcpu, irq)) {
+           vgic_put_irq(vcpu->kvm, irq);
+           continue;
+       }
+
        spin_lock_irqsave(&irq->irq_lock, flags);
        if (irq->hw)
            vgic_hw_irq_spending(vcpu, irq, is_uaccess);
@@ -262,6 +274,12 @@ void vgic_mmio_write_cpending(struct kvm_vcpu *vcpu,
    for_each_set_bit(i, &val, len * 8) {
        struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);

+       /* GICD_ICPENDR0 SGI bits are WI */
+       if (is_vgic_v2_sgi(vcpu, irq)) {
+           vgic_put_irq(vcpu->kvm, irq);
+           continue;
+       }
+
        spin_lock_irqsave(&irq->irq_lock, flags);

        if (irq->hw)
diff --git a/virt/kvm/arm/vgic/vgic-v2.c b/virt/kvm/arm/vgic/vgic-v2.c
index 57281c1..91b14df 100644
--- a/virt/kvm/arm/vgic/vgic-v2.c
+++ b/virt/kvm/arm/vgic/vgic-v2.c
@@ -195,7 +195,10 @@ void vgic_v2_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr)
        if (vgic_irq_is_sgi(irq->intid)) {
            u32 src = ffs(irq->source);

-           BUG_ON(!src);
+           if (WARN_RATELIMIT(!src, "No SGI source for INTID %d\n",
+                      irq->intid))
+               return;
+
            val |= (src - 1) << GICH_LR_PHYSID_CPUID_SHIFT;
            irq->source &= ~(1 << (src - 1));
            if (irq->source) {
diff --git a/virt/kvm/arm/vgic/vgic-v3.c b/virt/kvm/arm/vgic/vgic-v3.c
index 5c55995..8b958ed 100644
--- a/virt/kvm/arm/vgic/vgic-v3.c
+++ b/virt/kvm/arm/vgic/vgic-v3.c
@@ -179,7 +179,10 @@ void vgic_v3_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr)
            model == KVM_DEV_TYPE_ARM_VGIC_V2) {
            u32 src = ffs(irq->source);

-           BUG_ON(!src);
+           if (WARN_RATELIMIT(!src, "No SGI source for INTID %d\n",
+                      irq->intid))
+               return;
+
            val |= (src - 1) << GICH_LR_PHYSID_CPUID_SHIFT;
            irq->source &= ~(1 << (src - 1));
            if (irq->source) {

Leave a Reply

Your email address will not be published. Required fields are marked *