Commit 0560b0e9 authored by Shmulik Ladkani's avatar Shmulik Ladkani Committed by Michael S. Tsirkin
Browse files

virtio-pci: Set the QEMU_PCI_CAP_EXPRESS capability early in its DeviceClass realize method



In 1811e64c 'hw/virtio: Add PCIe capability to virtio devices', the
QEMU_PCI_CAP_EXPRESS capability was added to virtio's pci_dev, within
'virtio_pci_realize' - the pci device object realization method.

This occurs to late, as 'pci_qdev_realize' (DeviceClass.realize of
TYPE_PCI_DEVICE) has already been called, without knowing that the
device instance is indeed an "express" instance, thus allocating
insufficient pci config space.

As a result, device may crash upon attempt to write to the PCIE config
space.

Fix, by arming the QEMU_PCI_CAP_EXPRESS capability early in virtio-pci's
own DeviceClass realize method.

This also makes code cleaner, as 'virtio_pci_realize' may now access the
'pci_is_express' predicate when needed.

Signed-off-by: default avatarShmulik Ladkani <shmulik.ladkani@ravellosystems.com>
Reviewed-by: default avatarMichael S. Tsirkin <mst@redhat.com>
Signed-off-by: default avatarMichael S. Tsirkin <mst@redhat.com>
Reviewed-by: default avatarMarcel Apfelbaum <marcel@redhat.com>
Tested-by: default avatarMarcel Apfelbaum <marcel@redhat.com>
parent 11380b36
Loading
Loading
Loading
Loading
+25 −5
Original line number Diff line number Diff line
@@ -1814,13 +1814,10 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp)

    address_space_init(&proxy->modern_as, &proxy->modern_cfg, "virtio-pci-cfg-as");

    if (!(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_PCIE)
        && !(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_MODERN)
        && pci_bus_is_express(pci_dev->bus)
        && !pci_bus_is_root(pci_dev->bus)) {
    if (pci_is_express(pci_dev) && pci_bus_is_express(pci_dev->bus) &&
        !pci_bus_is_root(pci_dev->bus)) {
        int pos;

        pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS;
        pos = pcie_endpoint_cap_init(pci_dev, 0);
        assert(pos > 0);

@@ -1832,6 +1829,12 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp)
         * PCI Power Management Interface Specification.
         */
        pci_set_word(pci_dev->config + pos + PCI_PM_PMC, 0x3);
    } else {
        /*
         * make future invocations of pci_is_express() return false
         * and pci_config_size() return PCI_CONFIG_SPACE_SIZE.
         */
        pci_dev->cap_present &= ~QEMU_PCI_CAP_EXPRESS;
    }

    virtio_pci_bus_new(&proxy->bus, sizeof(proxy->bus), proxy);
@@ -1879,10 +1882,25 @@ static Property virtio_pci_properties[] = {
    DEFINE_PROP_END_OF_LIST(),
};

static void virtio_pci_dc_realize(DeviceState *qdev, Error **errp)
{
    VirtioPCIClass *vpciklass = VIRTIO_PCI_GET_CLASS(qdev);
    VirtIOPCIProxy *proxy = VIRTIO_PCI(qdev);
    PCIDevice *pci_dev = &proxy->pci_dev;

    if (!(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_PCIE) &&
        !(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_MODERN)) {
        pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS;
    }

    vpciklass->parent_dc_realize(qdev, errp);
}

static void virtio_pci_class_init(ObjectClass *klass, void *data)
{
    DeviceClass *dc = DEVICE_CLASS(klass);
    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
    VirtioPCIClass *vpciklass = VIRTIO_PCI_CLASS(klass);

    dc->props = virtio_pci_properties;
    k->realize = virtio_pci_realize;
@@ -1890,6 +1908,8 @@ static void virtio_pci_class_init(ObjectClass *klass, void *data)
    k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
    k->revision = VIRTIO_PCI_ABI_VERSION;
    k->class_id = PCI_CLASS_OTHERS;
    vpciklass->parent_dc_realize = dc->realize;
    dc->realize = virtio_pci_dc_realize;
    dc->reset = virtio_pci_reset;
}

+1 −0
Original line number Diff line number Diff line
@@ -105,6 +105,7 @@ typedef struct {

typedef struct VirtioPCIClass {
    PCIDeviceClass parent_class;
    DeviceRealize parent_dc_realize;
    void (*realize)(VirtIOPCIProxy *vpci_dev, Error **errp);
} VirtioPCIClass;