static int vfio_setup_pcie_cap(VFIOPCIDevice *vdev, int pos, uint8_t size,

                               Error **errp)

{

    uint16_t flags;

    uint8_t type;



    flags = pci_get_word(vdev->pdev.config + pos + PCI_CAP_FLAGS);

    type = (flags & PCI_EXP_FLAGS_TYPE) >> 4;



    if (type != PCI_EXP_TYPE_ENDPOINT &&

        type != PCI_EXP_TYPE_LEG_END &&

        type != PCI_EXP_TYPE_RC_END) {



        error_setg(errp, "assignment of PCIe type 0x%x "

                   "devices is not currently supported", type);

        return -EINVAL;

    }



    if (!pci_bus_is_express(vdev->pdev.bus)) {

        PCIBus *bus = vdev->pdev.bus;

        PCIDevice *bridge;



        /*

         * Traditionally PCI device assignment exposes the PCIe capability

         * as-is on non-express buses.  The reason being that some drivers

         * simply assume that it's there, for example tg3.  However when

         * we're running on a native PCIe machine type, like Q35, we need

         * to hide the PCIe capability.  The reason for this is twofold;

         * first Windows guests get a Code 10 error when the PCIe capability

         * is exposed in this configuration.  Therefore express devices won't

         * work at all unless they're attached to express buses in the VM.

         * Second, a native PCIe machine introduces the possibility of fine

         * granularity IOMMUs supporting both translation and isolation.

         * Guest code to discover the IOMMU visibility of a device, such as

         * IOMMU grouping code on Linux, is very aware of device types and

         * valid transitions between bus types.  An express device on a non-

         * express bus is not a valid combination on bare metal systems.

         *

         * Drivers that require a PCIe capability to make the device

         * functional are simply going to need to have their devices placed

         * on a PCIe bus in the VM.

         */

        while (!pci_bus_is_root(bus)) {

            bridge = pci_bridge_get_device(bus);

            bus = bridge->bus;

        }



        if (pci_bus_is_express(bus)) {

            return 0;

        }



    } else if (pci_bus_is_root(vdev->pdev.bus)) {

        /*

         * On a Root Complex bus Endpoints become Root Complex Integrated

         * Endpoints, which changes the type and clears the LNK & LNK2 fields.

         */

        if (type == PCI_EXP_TYPE_ENDPOINT) {

            vfio_add_emulated_word(vdev, pos + PCI_CAP_FLAGS,

                                   PCI_EXP_TYPE_RC_END << 4,

                                   PCI_EXP_FLAGS_TYPE);



            /* Link Capabilities, Status, and Control goes away */

            if (size > PCI_EXP_LNKCTL) {

                vfio_add_emulated_long(vdev, pos + PCI_EXP_LNKCAP, 0, ~0);

                vfio_add_emulated_word(vdev, pos + PCI_EXP_LNKCTL, 0, ~0);

                vfio_add_emulated_word(vdev, pos + PCI_EXP_LNKSTA, 0, ~0);



#ifndef PCI_EXP_LNKCAP2

#define PCI_EXP_LNKCAP2 44

#endif

#ifndef PCI_EXP_LNKSTA2

#define PCI_EXP_LNKSTA2 50

#endif

                /* Link 2 Capabilities, Status, and Control goes away */

                if (size > PCI_EXP_LNKCAP2) {

                    vfio_add_emulated_long(vdev, pos + PCI_EXP_LNKCAP2, 0, ~0);

                    vfio_add_emulated_word(vdev, pos + PCI_EXP_LNKCTL2, 0, ~0);

                    vfio_add_emulated_word(vdev, pos + PCI_EXP_LNKSTA2, 0, ~0);

                }

            }



        } else if (type == PCI_EXP_TYPE_LEG_END) {

            /*

             * Legacy endpoints don't belong on the root complex.  Windows

             * seems to be happier with devices if we skip the capability.

             */

            return 0;

        }



    } else {

        /*

         * Convert Root Complex Integrated Endpoints to regular endpoints.

         * These devices don't support LNK/LNK2 capabilities, so make them up.

         */

        if (type == PCI_EXP_TYPE_RC_END) {

            vfio_add_emulated_word(vdev, pos + PCI_CAP_FLAGS,

                                   PCI_EXP_TYPE_ENDPOINT << 4,

                                   PCI_EXP_FLAGS_TYPE);

            vfio_add_emulated_long(vdev, pos + PCI_EXP_LNKCAP,

                                   PCI_EXP_LNK_MLW_1 | PCI_EXP_LNK_LS_25, ~0);

            vfio_add_emulated_word(vdev, pos + PCI_EXP_LNKCTL, 0, ~0);

        }



        /* Mark the Link Status bits as emulated to allow virtual negotiation */

        vfio_add_emulated_word(vdev, pos + PCI_EXP_LNKSTA,

                               pci_get_word(vdev->pdev.config + pos +

                                            PCI_EXP_LNKSTA),

                               PCI_EXP_LNKCAP_MLW | PCI_EXP_LNKCAP_SLS);

    }



    /*

     * Intel 82599 SR-IOV VFs report an invalid PCIe capability version 0

     * (Niantic errate #35) causing Windows to error with a Code 10 for the

     * device on Q35.  Fixup any such devices to report version 1.  If we

     * were to remove the capability entirely the guest would lose extended

     * config space.

     */

    if ((flags & PCI_EXP_FLAGS_VERS) == 0) {

        vfio_add_emulated_word(vdev, pos + PCI_CAP_FLAGS,

                               1, PCI_EXP_FLAGS_VERS);

    }



    pos = pci_add_capability(&vdev->pdev, PCI_CAP_ID_EXP, pos, size,

                             errp);

    if (pos < 0) {

        return pos;

    }



    vdev->pdev.exp.exp_cap = pos;



    return pos;

}
