virtio/pci: Size the MSI-X bar according to the number of MSI-X

Since 45d3b59e8c45 ("kvm tools: Increase amount of possible interrupts
per PCI device"), the number of MSI-S has gone from 4 to 33.

However, the corresponding storage hasn't been upgraded, and writing
to the MSI-X table is a pretty risky business. Now that the Linux
kernel writes to *all* MSI-X entries before doing anything else
with the device, kvmtool dies a horrible death.

Fix it by properly defining the size of the MSI-X bar, and make
Linux great again.

Signed-off-by: Marc Zyngier <maz@kernel.org>
diff --git a/virtio/pci.c b/virtio/pci.c
index eb91f51..726146f 100644
--- a/virtio/pci.c
+++ b/virtio/pci.c
@@ -7,6 +7,7 @@
 #include "kvm/irq.h"
 #include "kvm/virtio.h"
 #include "kvm/ioeventfd.h"
+#include "kvm/util.h"
 
 #include <sys/ioctl.h>
 #include <linux/virtio_pci.h>
@@ -14,6 +15,13 @@
 #include <assert.h>
 #include <string.h>
 
+#define ALIGN_UP(x, s)		ALIGN((x) + (s) - 1, (s))
+#define VIRTIO_NR_MSIX		(VIRTIO_PCI_MAX_VQ + VIRTIO_PCI_MAX_CONFIG)
+#define VIRTIO_MSIX_TABLE_SIZE	(VIRTIO_NR_MSIX * 16)
+#define VIRTIO_MSIX_PBA_SIZE	(ALIGN_UP(VIRTIO_MSIX_TABLE_SIZE, 64) / 8)
+#define VIRTIO_MSIX_BAR_SIZE	(1UL << fls_long(VIRTIO_MSIX_TABLE_SIZE + \
+						 VIRTIO_MSIX_PBA_SIZE))
+
 static u16 virtio_pci__port_addr(struct virtio_pci *vpci)
 {
 	return pci__bar_address(&vpci->pci_hdr, 0);
@@ -336,15 +344,20 @@
 	int vecnum;
 	size_t offset;
 
-	if (addr > msix_io_addr + PCI_IO_SIZE) {
+	if (addr > msix_io_addr + VIRTIO_MSIX_TABLE_SIZE) {
+		/* Read access to PBA */
 		if (is_write)
 			return;
-		table  = (struct msix_table *)&vpci->msix_pba;
-		offset = addr - (msix_io_addr + PCI_IO_SIZE);
-	} else {
-		table  = vpci->msix_table;
-		offset = addr - msix_io_addr;
+		offset = addr - (msix_io_addr + VIRTIO_MSIX_TABLE_SIZE);
+		if ((offset + len) > sizeof (vpci->msix_pba))
+			return;
+		memcpy(data, (void *)&vpci->msix_pba + offset, len);
+		return;
 	}
+
+	table  = vpci->msix_table;
+	offset = addr - msix_io_addr;
+
 	vecnum = offset / sizeof(struct msix_table);
 	offset = offset % sizeof(struct msix_table);
 
@@ -520,7 +533,7 @@
 
 	port_addr = pci_get_io_port_block(PCI_IO_SIZE);
 	mmio_addr = pci_get_mmio_block(PCI_IO_SIZE);
-	msix_io_block = pci_get_mmio_block(PCI_IO_SIZE * 2);
+	msix_io_block = pci_get_mmio_block(VIRTIO_MSIX_BAR_SIZE);
 
 	vpci->pci_hdr = (struct pci_device_header) {
 		.vendor_id		= cpu_to_le16(PCI_VENDOR_ID_REDHAT_QUMRANET),
@@ -543,7 +556,7 @@
 		.capabilities		= (void *)&vpci->pci_hdr.msix - (void *)&vpci->pci_hdr,
 		.bar_size[0]		= cpu_to_le32(PCI_IO_SIZE),
 		.bar_size[1]		= cpu_to_le32(PCI_IO_SIZE),
-		.bar_size[2]		= cpu_to_le32(PCI_IO_SIZE*2),
+		.bar_size[2]		= cpu_to_le32(VIRTIO_MSIX_BAR_SIZE),
 	};
 
 	r = pci__register_bar_regions(kvm, &vpci->pci_hdr,