virtio: pci-legacy: Validate queue pfn
authorSuzuki K Poulose <suzuki.poulose@arm.com>
Wed, 18 Jul 2018 09:18:45 +0000 (10:18 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 15 Sep 2018 07:42:57 +0000 (09:42 +0200)
[ Upstream commit 69599206ea9a3f8f2e94d46580579cbf9d08ad6c ]

Legacy PCI over virtio uses a 32bit PFN for the queue. If the
queue pfn is too large to fit in 32bits, which we could hit on
arm64 systems with 52bit physical addresses (even with 64K page
size), we simply miss out a proper link to the other side of
the queue.

Add a check to validate the PFN, rather than silently breaking
the devices.

Cc: "Michael S. Tsirkin" <mst@redhat.com>
Cc: Jason Wang <jasowang@redhat.com>
Cc: Marc Zyngier <marc.zyngier@arm.com>
Cc: Christoffer Dall <cdall@kernel.org>
Cc: Peter Maydel <peter.maydell@linaro.org>
Cc: Jean-Philippe Brucker <jean-philippe.brucker@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Sasha Levin <alexander.levin@microsoft.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/virtio/virtio_pci_legacy.c

index 6d9e5173d5fa6b7f4da58b48268dd48c7e8c1e1f..fbc4761987e855cff1f5d9e8cb1b17e70cb55753 100644 (file)
@@ -121,6 +121,7 @@ static struct virtqueue *setup_vq(struct virtio_pci_device *vp_dev,
        struct virtqueue *vq;
        u16 num;
        int err;
+       u64 q_pfn;
 
        /* Select the queue we're interested in */
        iowrite16(index, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_SEL);
@@ -139,9 +140,17 @@ static struct virtqueue *setup_vq(struct virtio_pci_device *vp_dev,
        if (!vq)
                return ERR_PTR(-ENOMEM);
 
+       q_pfn = virtqueue_get_desc_addr(vq) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT;
+       if (q_pfn >> 32) {
+               dev_err(&vp_dev->pci_dev->dev,
+                       "platform bug: legacy virtio-mmio must not be used with RAM above 0x%llxGB\n",
+                       0x1ULL << (32 + PAGE_SHIFT - 30));
+               err = -E2BIG;
+               goto out_del_vq;
+       }
+
        /* activate the queue */
-       iowrite32(virtqueue_get_desc_addr(vq) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT,
-                 vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN);
+       iowrite32(q_pfn, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN);
 
        vq->priv = (void __force *)vp_dev->ioaddr + VIRTIO_PCI_QUEUE_NOTIFY;
 
@@ -158,6 +167,7 @@ static struct virtqueue *setup_vq(struct virtio_pci_device *vp_dev,
 
 out_deactivate:
        iowrite32(0, vp_dev->ioaddr + VIRTIO_PCI_QUEUE_PFN);
+out_del_vq:
        vring_del_virtqueue(vq);
        return ERR_PTR(err);
 }