[PATCH kvmtool 16/16] Add PCI Express 1.1 support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



PCI Express comes with an extended addressing scheme, which directly
translated into a bigger device configuration space (256->4096 bytes)
and bigger PCI configuration space (16->256 MB), as well as mandatory
capabilities (power management and PCI Express capabilities) [1].

Add support for PCI Express in preparation for running EDK2.

We take this opportunity to rewrite the initialization steps in
virtio_pci__init to make it more consistent and easy to follow.

[1] PCI Express Base Specification Revision 1.1

Signed-off-by: Alexandru Elisei <alexandru.elisei@xxxxxxx>
---
 arm/include/arm-common/kvm-arch.h |  2 +-
 arm/pci.c                         |  2 +-
 hw/vesa.c                         | 15 ++++++++
 include/kvm/pci.h                 | 57 +++++++++++++++++++++++-------
 pci.c                             |  5 +--
 virtio/pci.c                      | 58 +++++++++++++++++++++----------
 6 files changed, 104 insertions(+), 35 deletions(-)

diff --git a/arm/include/arm-common/kvm-arch.h b/arm/include/arm-common/kvm-arch.h
index b9d486d5eac2..aafb8bb797cb 100644
--- a/arm/include/arm-common/kvm-arch.h
+++ b/arm/include/arm-common/kvm-arch.h
@@ -23,7 +23,7 @@
 
 #define ARM_IOPORT_SIZE		(ARM_MMIO_AREA - ARM_IOPORT_AREA)
 #define ARM_VIRTIO_MMIO_SIZE	(ARM_AXI_AREA - (ARM_MMIO_AREA + ARM_GIC_SIZE))
-#define ARM_PCI_CFG_SIZE	(1ULL << 24)
+#define ARM_PCI_CFG_SIZE	(1ULL << 28)
 #define ARM_PCI_MMIO_SIZE	(ARM_MEMORY_AREA - \
 				(ARM_AXI_AREA + ARM_PCI_CFG_SIZE))
 
diff --git a/arm/pci.c b/arm/pci.c
index 4e6467357ce8..0c8e3aecc20b 100644
--- a/arm/pci.c
+++ b/arm/pci.c
@@ -80,7 +80,7 @@ void pci__generate_fdt_nodes(void *fdt)
 	_FDT(fdt_property_cell(fdt, "#address-cells", 0x3));
 	_FDT(fdt_property_cell(fdt, "#size-cells", 0x2));
 	_FDT(fdt_property_cell(fdt, "#interrupt-cells", 0x1));
-	_FDT(fdt_property_string(fdt, "compatible", "pci-host-cam-generic"));
+	_FDT(fdt_property_string(fdt, "compatible", "pci-host-ecam-generic"));
 	_FDT(fdt_property(fdt, "dma-coherent", NULL, 0));
 
 	_FDT(fdt_property(fdt, "bus-range", bus_range, sizeof(bus_range)));
diff --git a/hw/vesa.c b/hw/vesa.c
index 0191e9264666..d0caecf0b1b4 100644
--- a/hw/vesa.c
+++ b/hw/vesa.c
@@ -69,6 +69,21 @@ struct framebuffer *vesa__init(struct kvm *kvm)
 
 	vesa_base_addr			= (u16)r;
 	vesa_pci_device.bar[0]		= cpu_to_le32(vesa_base_addr | PCI_BASE_ADDRESS_SPACE_IO);
+	vesa_pci_device.capabilities	= (void *)&vesa_pci_device.pm - (void *)&vesa_pci_device;
+
+	vesa_pci_device.pm = (struct pm_cap) {
+		.cap	= PCI_CAP_ID_PM,
+		.next	= (void *)&vesa_pci_device.pcie - (void *)&vesa_pci_device,
+		.pmc	= PM_CAP_VER,
+		.pmcsr	= PCI_PM_CTRL_NO_SOFT_RESET,
+	};
+
+	vesa_pci_device.pcie = (struct pcie_cap) {
+		.cap		= PCI_CAP_ID_EXP,
+		.next		= 0,
+		.cap_reg	= PCIE_CAP_REG_DEV_LEGACY | PCIE_CAP_REG_VER,
+	};
+
 	device__register(&vesa_device);
 
 	mem = mmap(NULL, VESA_MEM_SIZE, PROT_RW, MAP_ANON_NORESERVE, -1, 0);
diff --git a/include/kvm/pci.h b/include/kvm/pci.h
index ccb155e3e8fe..1ac7809e80ed 100644
--- a/include/kvm/pci.h
+++ b/include/kvm/pci.h
@@ -20,7 +20,11 @@
 #define PCI_CONFIG_BUS_FORWARD	0xcfa
 #define PCI_IO_SIZE		0x100
 #define PCI_IOPORT_START	0x6200
-#define PCI_CFG_SIZE		(1ULL << 24)
+#define PCI_CFG_SIZE		(1ULL << 28)
+
+#define PCIE_CAP_REG_VER	0x1
+#define PCIE_CAP_REG_DEV_LEGACY	(1 << 4)
+#define PM_CAP_VER		0x3
 
 struct kvm;
 
@@ -28,19 +32,19 @@ union pci_config_address {
 	struct {
 #if __BYTE_ORDER == __LITTLE_ENDIAN
 		unsigned	reg_offset	: 2;		/* 1  .. 0  */
-		unsigned	register_number	: 6;		/* 7  .. 2  */
-		unsigned	function_number	: 3;		/* 10 .. 8  */
-		unsigned	device_number	: 5;		/* 15 .. 11 */
-		unsigned	bus_number	: 8;		/* 23 .. 16 */
-		unsigned	reserved	: 7;		/* 30 .. 24 */
+		unsigned	register_number	: 10;		/* 11 .. 2  */
+		unsigned	function_number	: 3;		/* 14 .. 12  */
+		unsigned	device_number	: 5;		/* 19 .. 15 */
+		unsigned	bus_number	: 8;		/* 27 .. 20 */
+		unsigned	reserved	: 3;		/* 30 .. 28 */
 		unsigned	enable_bit	: 1;		/* 31       */
 #else
 		unsigned	enable_bit	: 1;		/* 31       */
-		unsigned	reserved	: 7;		/* 30 .. 24 */
-		unsigned	bus_number	: 8;		/* 23 .. 16 */
-		unsigned	device_number	: 5;		/* 15 .. 11 */
-		unsigned	function_number	: 3;		/* 10 .. 8  */
-		unsigned	register_number	: 6;		/* 7  .. 2  */
+		unsigned	reserved	: 3;		/* 30 .. 28 */
+		unsigned	bus_number	: 8;		/* 27 .. 20 */
+		unsigned	device_number	: 5;		/* 19 .. 15 */
+		unsigned	function_number	: 3;		/* 14 .. 12  */
+		unsigned	register_number	: 10;		/* 11 .. 2  */
 		unsigned	reg_offset	: 2;		/* 1  .. 0  */
 #endif
 	};
@@ -88,8 +92,35 @@ struct pci_cap_hdr {
 	u8	next;
 };
 
+struct pcie_cap {
+	u8 cap;
+	u8 next;
+	u16 cap_reg;
+	u32 dev_cap;
+	u16 dev_ctrl;
+	u16 dev_status;
+	u32 link_cap;
+	u16 link_ctrl;
+	u16 link_status;
+	u32 slot_cap;
+	u16 slot_ctrl;
+	u16 slot_status;
+	u16 root_ctrl;
+	u16 root_cap;
+	u32 root_status;
+};
+
+struct pm_cap {
+	u8 cap;
+	u8 next;
+	u16 pmc;
+	u16 pmcsr;
+	u8 pmcsr_bse;
+	u8 data;
+};
+
 #define PCI_BAR_OFFSET(b)	(offsetof(struct pci_device_header, bar[b]))
-#define PCI_DEV_CFG_SIZE	256
+#define PCI_DEV_CFG_SIZE	4096
 #define PCI_DEV_CFG_MASK	(PCI_DEV_CFG_SIZE - 1)
 
 struct pci_device_header;
@@ -127,6 +158,8 @@ struct pci_device_header {
 			u8		irq_pin;
 			u8		min_gnt;
 			u8		max_lat;
+			struct pm_cap pm;
+			struct pcie_cap pcie;
 			struct msix_cap msix;
 		} __attribute__((packed));
 		/* Pad to PCI config space size */
diff --git a/pci.c b/pci.c
index b4677434c50c..f1bfa5a8d157 100644
--- a/pci.c
+++ b/pci.c
@@ -155,7 +155,8 @@ static struct ioport_operations pci_config_data_ops = {
 void pci__config_wr(struct kvm *kvm, union pci_config_address addr, void *data, int size)
 {
 	void *base;
-	u8 bar, offset;
+	u8 bar;
+	u16 offset;
 	struct pci_device_header *pci_hdr;
 	u8 dev_num = addr.device_number;
 	u32 value = 0;
@@ -222,7 +223,7 @@ void pci__config_wr(struct kvm *kvm, union pci_config_address addr, void *data,
 
 void pci__config_rd(struct kvm *kvm, union pci_config_address addr, void *data, int size)
 {
-	u8 offset;
+	u16 offset;
 	struct pci_device_header *pci_hdr;
 	u8 dev_num = addr.device_number;
 
diff --git a/virtio/pci.c b/virtio/pci.c
index dadb796e6d62..004a136af672 100644
--- a/virtio/pci.c
+++ b/virtio/pci.c
@@ -666,36 +666,56 @@ int virtio_pci__init(struct kvm *kvm, void *dev, struct virtio_device *vdev,
 		.bar[1]                 = vpci->mmio_addr | PCI_BASE_ADDRESS_SPACE_MEMORY,
 		.bar[2]                 = vpci->msix_io_block | PCI_BASE_ADDRESS_SPACE_MEMORY,
 		.status			= cpu_to_le16(PCI_STATUS_CAP_LIST),
-		.capabilities		= (void *)&vpci->pci_hdr.msix - (void *)&vpci->pci_hdr,
+		.capabilities		= (void *)&vpci->pci_hdr.pm - (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),
 		.cfg_ops.write		= virtio_pci__config_wr,
 	};
 
+	vpci->pci_hdr.pm = (struct pm_cap) {
+		.cap	= PCI_CAP_ID_PM,
+		.next	= (void *)&vpci->pci_hdr.pcie - (void *)&vpci->pci_hdr,
+		.pmc	= PM_CAP_VER,
+		/*
+		 * We don't do device state emulation, let the driver know that
+		 * going from D0->D3hot->D0 won't change the internal state of
+		 * the device.
+		 */
+		.pmcsr	= PCI_PM_CTRL_NO_SOFT_RESET,
+	};
+
+	vpci->pci_hdr.pcie = (struct pcie_cap) {
+		.cap		= PCI_CAP_ID_EXP,
+		.next		= (void *)&vpci->pci_hdr.msix - (void *)&vpci->pci_hdr,
+		.cap_reg	= PCIE_CAP_REG_DEV_LEGACY | PCIE_CAP_REG_VER,
+	};
+
+	vpci->pci_hdr.msix = (struct msix_cap) {
+		.cap		= PCI_CAP_ID_MSIX,
+		.next		= 0,
+		/*
+		 * We at most have VIRTIO_PCI_MAX_VQ entries for virt queue,
+		 * VIRTIO_PCI_MAX_CONFIG entries for config.
+		 *
+		 * To quote the PCI spec:
+		 *
+		 * System software reads this field to determine the
+		 * MSI-X Table Size N, which is encoded as N-1.
+		 * For example, a returned value of "00000000011"
+		 * indicates a table size of 4.
+		 */
+		.ctrl		= cpu_to_le16(VIRTIO_PCI_MAX_VQ + VIRTIO_PCI_MAX_CONFIG - 1),
+		/* Both table and PBA are mapped to the same BAR (2) */
+		.table_offset	= cpu_to_le32(2),
+		.pba_offset	= cpu_to_le32(2 | PCI_IO_SIZE),
+	};
+
 	vpci->dev_hdr = (struct device_header) {
 		.bus_type		= DEVICE_BUS_PCI,
 		.data			= &vpci->pci_hdr,
 	};
 
-	vpci->pci_hdr.msix.cap = PCI_CAP_ID_MSIX;
-	vpci->pci_hdr.msix.next = 0;
-	/*
-	 * We at most have VIRTIO_PCI_MAX_VQ entries for virt queue,
-	 * VIRTIO_PCI_MAX_CONFIG entries for config.
-	 *
-	 * To quote the PCI spec:
-	 *
-	 * System software reads this field to determine the
-	 * MSI-X Table Size N, which is encoded as N-1.
-	 * For example, a returned value of "00000000011"
-	 * indicates a table size of 4.
-	 */
-	vpci->pci_hdr.msix.ctrl = cpu_to_le16(VIRTIO_PCI_MAX_VQ + VIRTIO_PCI_MAX_CONFIG - 1);
-
-	/* Both table and PBA are mapped to the same BAR (2) */
-	vpci->pci_hdr.msix.table_offset = cpu_to_le32(2);
-	vpci->pci_hdr.msix.pba_offset = cpu_to_le32(2 | PCI_IO_SIZE);
 	vpci->config_vector = 0;
 
 	if (irq__can_signal_msi(kvm))
-- 
2.20.1




[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux