virNodeDevCapPCIDevParseXML(xmlXPathContextPtr ctxt,
                            virNodeDeviceDefPtr def,
                            xmlNodePtr node,
                            virNodeDevCapPCIDevPtr pci_dev)
{
    VIR_XPATH_NODE_AUTORESTORE(ctxt)
    xmlNodePtr iommuGroupNode;
    xmlNodePtr pciExpress;
    xmlNodePtr *nodes = NULL;
    int n = 0;
    int ret = -1;
    virPCIEDeviceInfoPtr pci_express = NULL;
    char *tmp = NULL;
    size_t i = 0;

    ctxt->node = node;

    if ((tmp = virXPathString("string(./class[1])", ctxt))) {
        if (virStrToLong_i(tmp, NULL, 16, &pci_dev->klass) < 0 ||
            pci_dev->klass > 0xffffff) {
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("invalid PCI class supplied for '%s'"), def->name);
            goto out;
        }
        VIR_FREE(tmp);
    } else {
        pci_dev->klass = -1;
    }

    if (virNodeDevCapsDefParseULong("number(./domain[1])", ctxt,
                                    &pci_dev->domain, def,
                                    _("no PCI domain ID supplied for '%s'"),
                                    _("invalid PCI domain ID supplied for '%s'")) < 0)
        goto out;

    if (virNodeDevCapsDefParseULong("number(./bus[1])", ctxt,
                                    &pci_dev->bus, def,
                                    _("no PCI bus ID supplied for '%s'"),
                                    _("invalid PCI bus ID supplied for '%s'")) < 0)
        goto out;

    if (virNodeDevCapsDefParseULong("number(./slot[1])", ctxt,
                                    &pci_dev->slot, def,
                                    _("no PCI slot ID supplied for '%s'"),
                                    _("invalid PCI slot ID supplied for '%s'")) < 0)
        goto out;

    if (virNodeDevCapsDefParseULong("number(./function[1])", ctxt,
                                    &pci_dev->function, def,
                                    _("no PCI function ID supplied for '%s'"),
                                    _("invalid PCI function ID supplied for '%s'")) < 0)
        goto out;

    if (virNodeDevCapsDefParseHexId("string(./vendor[1]/@id)", ctxt,
                                    &pci_dev->vendor, def,
                                    _("no PCI vendor ID supplied for '%s'"),
                                    _("invalid PCI vendor ID supplied for '%s'")) < 0)
        goto out;

    if (virNodeDevCapsDefParseHexId("string(./product[1]/@id)", ctxt,
                                    &pci_dev->product, def,
                                    _("no PCI product ID supplied for '%s'"),
                                    _("invalid PCI product ID supplied for '%s'")) < 0)
        goto out;

    pci_dev->vendor_name  = virXPathString("string(./vendor[1])", ctxt);
    pci_dev->product_name = virXPathString("string(./product[1])", ctxt);

    if ((n = virXPathNodeSet("./capability", ctxt, &nodes)) < 0)
        goto out;

    for (i = 0; i < n; i++) {
        if (virNodeDevPCICapabilityParseXML(ctxt, nodes[i], pci_dev) < 0)
            goto out;
    }
    VIR_FREE(nodes);

    if ((iommuGroupNode = virXPathNode("./iommuGroup[1]", ctxt))) {
        if (virNodeDevCapPCIDevIommuGroupParseXML(ctxt, iommuGroupNode,
                                                  pci_dev) < 0) {
            goto out;
        }
    }

    /* The default value is -1 since zero is valid NUMA node number */
    pci_dev->numa_node = -1;
    if (virNodeDevCapsDefParseIntOptional("number(./numa[1]/@node)", ctxt,
                                          &pci_dev->numa_node, def,
                                          _("invalid NUMA node ID supplied for '%s'")) < 0)
        goto out;

    if ((pciExpress = virXPathNode("./pci-express[1]", ctxt))) {
        pci_express = g_new0(virPCIEDeviceInfo, 1);

        if (virPCIEDeviceInfoParseXML(ctxt, pciExpress, pci_express) < 0)
            goto out;

        pci_dev->pci_express = pci_express;
        pci_express = NULL;
        pci_dev->flags |= VIR_NODE_DEV_CAP_FLAG_PCIE;
    }

    ret = 0;
 out:
    VIR_FREE(nodes);
    VIR_FREE(tmp);
    virPCIEDeviceInfoFree(pci_express);
    return ret;
}