@@ -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))
@@ -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)));
@@ -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);
@@ -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 */
@@ -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;
@@ -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))
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@arm.com> --- 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(-)