/* * node_device_conf.c: config handling for node devices * * Copyright (C) 2009-2015 Red Hat, Inc. * Copyright (C) 2008 Virtual Iron Software, Inc. * Copyright (C) 2008 David F. Lively * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . */ #include #include #include "virerror.h" #include "viralloc.h" #include "virstring.h" #include "node_device_conf.h" #include "device_conf.h" #include "virxml.h" #include "virbuffer.h" #include "viruuid.h" #include "virrandom.h" #include "virlog.h" #include "virfcp.h" #include "virpcivpd.h" #define VIR_FROM_THIS VIR_FROM_NODEDEV VIR_LOG_INIT("conf.node_device_conf"); VIR_ENUM_IMPL(virNodeDevDevnode, VIR_NODE_DEV_DEVNODE_LAST, "dev", "link", ); VIR_ENUM_IMPL(virNodeDevCap, VIR_NODE_DEV_CAP_LAST, "system", "pci", "usb_device", "usb", "net", "scsi_host", "scsi_target", "scsi", "storage", "fc_host", "vports", "scsi_generic", "drm", "mdev_types", "mdev", "ccw", "css", "vdpa", "ap_card", "ap_queue", "ap_matrix", "vpd", ); VIR_ENUM_IMPL(virNodeDevNetCap, VIR_NODE_DEV_CAP_NET_LAST, "80203", "80211", ); VIR_ENUM_IMPL(virNodeDevDRM, VIR_NODE_DEV_DRM_LAST, "primary", "control", "render", ); static int virNodeDevCapsDefParseString(const char *xpath, xmlXPathContextPtr ctxt, char **string) { char *s; if (!(s = virXPathString(xpath, ctxt))) return -1; *string = s; return 0; } void virNodeDeviceDefFree(virNodeDeviceDef *def) { virNodeDevCapsDef *caps; if (!def) return; g_free(def->name); g_free(def->parent); g_free(def->parent_wwnn); g_free(def->parent_wwpn); g_free(def->parent_fabric_wwn); g_free(def->driver); g_free(def->sysfs_path); g_free(def->parent_sysfs_path); g_free(def->devnode); g_strfreev(def->devlinks); caps = def->caps; while (caps) { virNodeDevCapsDef *next = caps->next; virNodeDevCapsDefFree(caps); caps = next; } g_free(def); } static void virPCIELinkFormat(virBuffer *buf, virPCIELink *lnk, const char *attrib) { if (!lnk) return; virBufferAsprintf(buf, "port >= 0) virBufferAsprintf(buf, " port='%d'", lnk->port); if (lnk->speed) virBufferAsprintf(buf, " speed='%s'", virPCIELinkSpeedTypeToString(lnk->speed)); virBufferAsprintf(buf, " width='%d'", lnk->width); virBufferAddLit(buf, "/>\n"); } static void virPCIEDeviceInfoFormat(virBuffer *buf, virPCIEDeviceInfo *info) { if (!info->link_cap && !info->link_sta) { virBufferAddLit(buf, "\n"); return; } virBufferAddLit(buf, "\n"); virBufferAdjustIndent(buf, 2); virPCIELinkFormat(buf, info->link_cap, "cap"); virPCIELinkFormat(buf, info->link_sta, "sta"); virBufferAdjustIndent(buf, -2); virBufferAddLit(buf, "\n"); } static void virNodeDeviceCapSystemDefFormat(virBuffer *buf, const virNodeDevCapData *data) { char uuidstr[VIR_UUID_STRING_BUFLEN]; if (data->system.product_name) virBufferEscapeString(buf, "%s\n", data->system.product_name); virBufferAddLit(buf, "\n"); virBufferAdjustIndent(buf, 2); if (data->system.hardware.vendor_name) virBufferEscapeString(buf, "%s\n", data->system.hardware.vendor_name); if (data->system.hardware.version) virBufferEscapeString(buf, "%s\n", data->system.hardware.version); if (data->system.hardware.serial) virBufferEscapeString(buf, "%s\n", data->system.hardware.serial); virUUIDFormat(data->system.hardware.uuid, uuidstr); virBufferAsprintf(buf, "%s\n", uuidstr); virBufferAdjustIndent(buf, -2); virBufferAddLit(buf, "\n"); virBufferAddLit(buf, "\n"); virBufferAdjustIndent(buf, 2); if (data->system.firmware.vendor_name) virBufferEscapeString(buf, "%s\n", data->system.firmware.vendor_name); if (data->system.firmware.version) virBufferEscapeString(buf, "%s\n", data->system.firmware.version); if (data->system.firmware.release_date) virBufferEscapeString(buf, "%s\n", data->system.firmware.release_date); virBufferAdjustIndent(buf, -2); virBufferAddLit(buf, "\n"); } static void virNodeDeviceCapMdevTypesFormat(virBuffer *buf, virMediatedDeviceType **mdev_types, const size_t nmdev_types) { size_t i; if (nmdev_types > 0) { virBufferAddLit(buf, "\n"); virBufferAdjustIndent(buf, 2); for (i = 0; i < nmdev_types; i++) { virMediatedDeviceType *type = mdev_types[i]; virBufferEscapeString(buf, "\n", type->id); virBufferAdjustIndent(buf, 2); if (type->name) virBufferEscapeString(buf, "%s\n", type->name); virBufferEscapeString(buf, "%s\n", type->device_api); virBufferAsprintf(buf, "%u\n", type->available_instances); virBufferAdjustIndent(buf, -2); virBufferAddLit(buf, "\n"); } virBufferAdjustIndent(buf, -2); virBufferAddLit(buf, "\n"); } } static void virNodeDeviceCapVPDFormatCustomVendorField(virPCIVPDResourceCustom *field, virBuffer *buf) { if (field == NULL || field->value == NULL) return; virBufferAsprintf(buf, "%s\n", field->idx, field->value); } static void virNodeDeviceCapVPDFormatCustomSystemField(virPCIVPDResourceCustom *field, virBuffer *buf) { if (field == NULL || field->value == NULL) return; virBufferAsprintf(buf, "%s\n", field->idx, field->value); } static inline void virNodeDeviceCapVPDFormatRegularField(virBuffer *buf, const char *keyword, const char *value) { if (keyword == NULL || value == NULL) return; virBufferAsprintf(buf, "<%s>%s\n", keyword, value, keyword); } static void virNodeDeviceCapVPDFormat(virBuffer *buf, virPCIVPDResource *res) { if (res == NULL) return; virBufferAddLit(buf, "\n"); virBufferAdjustIndent(buf, 2); virBufferEscapeString(buf, "%s\n", res->name); if (res->ro != NULL) { virBufferEscapeString(buf, "\n", "readonly"); virBufferAdjustIndent(buf, 2); virNodeDeviceCapVPDFormatRegularField(buf, "change_level", res->ro->change_level); virNodeDeviceCapVPDFormatRegularField(buf, "manufacture_id", res->ro->manufacture_id); virNodeDeviceCapVPDFormatRegularField(buf, "part_number", res->ro->part_number); virNodeDeviceCapVPDFormatRegularField(buf, "serial_number", res->ro->serial_number); g_ptr_array_foreach(res->ro->vendor_specific, (GFunc)virNodeDeviceCapVPDFormatCustomVendorField, buf); virBufferAdjustIndent(buf, -2); virBufferAddLit(buf, "\n"); } if (res->rw != NULL) { virBufferEscapeString(buf, "\n", "readwrite"); virBufferAdjustIndent(buf, 2); virNodeDeviceCapVPDFormatRegularField(buf, "asset_tag", res->rw->asset_tag); g_ptr_array_foreach(res->rw->vendor_specific, (GFunc)virNodeDeviceCapVPDFormatCustomVendorField, buf); g_ptr_array_foreach(res->rw->system_specific, (GFunc)virNodeDeviceCapVPDFormatCustomSystemField, buf); virBufferAdjustIndent(buf, -2); virBufferAddLit(buf, "\n"); } virBufferAdjustIndent(buf, -2); virBufferAddLit(buf, "\n"); } static void virNodeDeviceCapPCIDefFormat(virBuffer *buf, const virNodeDevCapData *data) { size_t i; if (data->pci_dev.klass >= 0) virBufferAsprintf(buf, "0x%.6x\n", data->pci_dev.klass); virBufferAsprintf(buf, "%d\n", data->pci_dev.domain); virBufferAsprintf(buf, "%u\n", data->pci_dev.bus); virBufferAsprintf(buf, "%u\n", data->pci_dev.slot); virBufferAsprintf(buf, "%u\n", data->pci_dev.function); virBufferAsprintf(buf, "pci_dev.product); if (data->pci_dev.product_name) virBufferEscapeString(buf, ">%s\n", data->pci_dev.product_name); else virBufferAddLit(buf, "/>\n"); virBufferAsprintf(buf, "pci_dev.vendor); if (data->pci_dev.vendor_name) virBufferEscapeString(buf, ">%s\n", data->pci_dev.vendor_name); else virBufferAddLit(buf, "/>\n"); if (data->pci_dev.flags & VIR_NODE_DEV_CAP_FLAG_PCI_PHYSICAL_FUNCTION) { virBufferAddLit(buf, "\n"); virBufferAdjustIndent(buf, 2); virBufferAsprintf(buf, "
\n", data->pci_dev.physical_function->domain, data->pci_dev.physical_function->bus, data->pci_dev.physical_function->slot, data->pci_dev.physical_function->function); virBufferAdjustIndent(buf, -2); virBufferAddLit(buf, "\n"); } if (data->pci_dev.flags & VIR_NODE_DEV_CAP_FLAG_PCI_VIRTUAL_FUNCTION) { virBufferAddLit(buf, "pci_dev.max_virtual_functions) virBufferAsprintf(buf, " maxCount='%u'", data->pci_dev.max_virtual_functions); if (data->pci_dev.num_virtual_functions == 0) { virBufferAddLit(buf, "/>\n"); } else { virBufferAddLit(buf, ">\n"); virBufferAdjustIndent(buf, 2); for (i = 0; i < data->pci_dev.num_virtual_functions; i++) { virBufferAsprintf(buf, "
\n", data->pci_dev.virtual_functions[i]->domain, data->pci_dev.virtual_functions[i]->bus, data->pci_dev.virtual_functions[i]->slot, data->pci_dev.virtual_functions[i]->function); } virBufferAdjustIndent(buf, -2); virBufferAddLit(buf, "\n"); } } if (data->pci_dev.hdrType) { virBufferAsprintf(buf, "\n", virPCIHeaderTypeToString(data->pci_dev.hdrType)); } if (data->pci_dev.flags & VIR_NODE_DEV_CAP_FLAG_PCI_MDEV) { virNodeDeviceCapMdevTypesFormat(buf, data->pci_dev.mdev_types, data->pci_dev.nmdev_types); } if (data->pci_dev.flags & VIR_NODE_DEV_CAP_FLAG_PCI_VPD) { virNodeDeviceCapVPDFormat(buf, data->pci_dev.vpd); } if (data->pci_dev.nIommuGroupDevices) { virBufferAsprintf(buf, "\n", data->pci_dev.iommuGroupNumber); virBufferAdjustIndent(buf, 2); for (i = 0; i < data->pci_dev.nIommuGroupDevices; i++) { virBufferAsprintf(buf, "
\n", data->pci_dev.iommuGroupDevices[i]->domain, data->pci_dev.iommuGroupDevices[i]->bus, data->pci_dev.iommuGroupDevices[i]->slot, data->pci_dev.iommuGroupDevices[i]->function); } virBufferAdjustIndent(buf, -2); virBufferAddLit(buf, "\n"); } if (data->pci_dev.numa_node >= 0) virBufferAsprintf(buf, "\n", data->pci_dev.numa_node); if (data->pci_dev.flags & VIR_NODE_DEV_CAP_FLAG_PCIE) virPCIEDeviceInfoFormat(buf, data->pci_dev.pci_express); } static void virNodeDeviceCapUSBDevDefFormat(virBuffer *buf, const virNodeDevCapData *data) { virBufferAsprintf(buf, "%d\n", data->usb_dev.bus); virBufferAsprintf(buf, "%d\n", data->usb_dev.device); virBufferAsprintf(buf, "usb_dev.product); if (data->usb_dev.product_name) virBufferEscapeString(buf, ">%s\n", data->usb_dev.product_name); else virBufferAddLit(buf, " />\n"); virBufferAsprintf(buf, "usb_dev.vendor); if (data->usb_dev.vendor_name) virBufferEscapeString(buf, ">%s\n", data->usb_dev.vendor_name); else virBufferAddLit(buf, " />\n"); } static void virNodeDeviceCapUSBInterfaceDefFormat(virBuffer *buf, const virNodeDevCapData *data) { virBufferAsprintf(buf, "%d\n", data->usb_if.number); virBufferAsprintf(buf, "%d\n", data->usb_if.klass); virBufferAsprintf(buf, "%d\n", data->usb_if.subclass); virBufferAsprintf(buf, "%d\n", data->usb_if.protocol); if (data->usb_if.description) virBufferEscapeString(buf, "%s\n", data->usb_if.description); } static void virNodeDeviceCapNetDefFormat(virBuffer *buf, const virNodeDevCapData *data) { size_t i; virBufferEscapeString(buf, "%s\n", data->net.ifname); if (data->net.address) virBufferEscapeString(buf, "
%s
\n", data->net.address); virInterfaceLinkFormat(buf, &data->net.lnk); if (data->net.features) { for (i = 0; i < VIR_NET_DEV_FEAT_LAST; i++) { if (virBitmapIsBitSet(data->net.features, i)) { virBufferAsprintf(buf, "\n", virNetDevFeatureTypeToString(i)); } } } if (data->net.subtype != VIR_NODE_DEV_CAP_NET_LAST) { const char *subtyp = virNodeDevNetCapTypeToString(data->net.subtype); virBufferEscapeString(buf, "\n", subtyp); } } static void virNodeDeviceCapSCSIHostDefFormat(virBuffer *buf, const virNodeDevCapData *data) { virBufferAsprintf(buf, "%d\n", data->scsi_host.host); if (data->scsi_host.unique_id != -1) virBufferAsprintf(buf, "%d\n", data->scsi_host.unique_id); if (data->scsi_host.flags & VIR_NODE_DEV_CAP_FLAG_HBA_FC_HOST) { virBufferAddLit(buf, "\n"); virBufferAdjustIndent(buf, 2); virBufferEscapeString(buf, "%s\n", data->scsi_host.wwnn); virBufferEscapeString(buf, "%s\n", data->scsi_host.wwpn); virBufferEscapeString(buf, "%s\n", data->scsi_host.fabric_wwn); virBufferAdjustIndent(buf, -2); virBufferAddLit(buf, "\n"); } if (data->scsi_host.flags & VIR_NODE_DEV_CAP_FLAG_HBA_VPORT_OPS) { virBufferAddLit(buf, "\n"); virBufferAdjustIndent(buf, 2); virBufferAsprintf(buf, "%d\n", data->scsi_host.max_vports); virBufferAsprintf(buf, "%d\n", data->scsi_host.vports); virBufferAdjustIndent(buf, -2); virBufferAddLit(buf, "\n"); } } static void virNodeDeviceCapSCSIDefFormat(virBuffer *buf, const virNodeDevCapData *data) { virBufferAsprintf(buf, "%d\n", data->scsi.host); virBufferAsprintf(buf, "%d\n", data->scsi.bus); virBufferAsprintf(buf, "%d\n", data->scsi.target); virBufferAsprintf(buf, "%d\n", data->scsi.lun); if (data->scsi.type) virBufferEscapeString(buf, "%s\n", data->scsi.type); } static void virNodeDeviceCapStorageDefFormat(virBuffer *buf, const virNodeDevCapData *data) { virBufferEscapeString(buf, "%s\n", data->storage.block); if (data->storage.bus) virBufferEscapeString(buf, "%s\n", data->storage.bus); if (data->storage.drive_type) virBufferEscapeString(buf, "%s\n", data->storage.drive_type); if (data->storage.model) virBufferEscapeString(buf, "%s\n", data->storage.model); if (data->storage.vendor) virBufferEscapeString(buf, "%s\n", data->storage.vendor); if (data->storage.serial) virBufferEscapeString(buf, "%s\n", data->storage.serial); if (data->storage.flags & VIR_NODE_DEV_CAP_STORAGE_REMOVABLE) { int avl = data->storage.flags & VIR_NODE_DEV_CAP_STORAGE_REMOVABLE_MEDIA_AVAILABLE; virBufferAddLit(buf, "\n"); virBufferAdjustIndent(buf, 2); virBufferAsprintf(buf, "%d" "\n", avl ? 1 : 0); virBufferAsprintf(buf, "%llu\n", data->storage.removable_media_size); if (data->storage.media_label) virBufferEscapeString(buf, "%s\n", data->storage.media_label); if (data->storage.logical_block_size > 0) virBufferAsprintf(buf, "%llu" "\n", data->storage.logical_block_size); if (data->storage.num_blocks > 0) virBufferAsprintf(buf, "%llu\n", data->storage.num_blocks); virBufferAdjustIndent(buf, -2); virBufferAddLit(buf, "\n"); } else { virBufferAsprintf(buf, "%llu\n", data->storage.size); if (data->storage.logical_block_size > 0) virBufferAsprintf(buf, "%llu" "\n", data->storage.logical_block_size); if (data->storage.num_blocks > 0) virBufferAsprintf(buf, "%llu\n", data->storage.num_blocks); } if (data->storage.flags & VIR_NODE_DEV_CAP_STORAGE_HOTPLUGGABLE) virBufferAddLit(buf, "\n"); } static void virNodeDeviceCapMdevDefFormat(virBuffer *buf, const virNodeDevCapData *data) { size_t i; virBufferEscapeString(buf, "\n", data->mdev.type); virBufferEscapeString(buf, "%s\n", data->mdev.uuid); virBufferEscapeString(buf, "%s\n", data->mdev.parent_addr); virBufferAsprintf(buf, "\n", data->mdev.iommuGroupNumber); for (i = 0; i < data->mdev.nattributes; i++) { virMediatedDeviceAttr *attr = data->mdev.attributes[i]; virBufferAsprintf(buf, "\n", attr->name, attr->value); } } static void virNodeDeviceCapVDPADefFormat(virBuffer *buf, const virNodeDevCapData *data) { virBufferEscapeString(buf, "%s\n", data->vdpa.chardev); } static void virNodeDeviceCapCCWDefFormat(virBuffer *buf, const virNodeDevCapData *data) { virBufferAsprintf(buf, "0x%x\n", data->ccw_dev.cssid); virBufferAsprintf(buf, "0x%x\n", data->ccw_dev.ssid); virBufferAsprintf(buf, "0x%04x\n", data->ccw_dev.devno); } static void virNodeDeviceCapCSSDefFormat(virBuffer *buf, const virNodeDevCapData *data) { virNodeDevCapCCW ccw_dev = data->ccw_dev; virNodeDeviceCapCCWDefFormat(buf, data); if (ccw_dev.channel_dev_addr) { virCCWDeviceAddress *ccw = ccw_dev.channel_dev_addr; virBufferAddLit(buf, "\n"); virBufferAdjustIndent(buf, 2); virBufferAsprintf(buf, "0x%x\n", ccw->cssid); virBufferAsprintf(buf, "0x%x\n", ccw->ssid); virBufferAsprintf(buf, "0x%04x\n", ccw->devno); virBufferAdjustIndent(buf, -2); virBufferAddLit(buf, "\n"); } if (ccw_dev.flags & VIR_NODE_DEV_CAP_FLAG_CSS_MDEV) virNodeDeviceCapMdevTypesFormat(buf, ccw_dev.mdev_types, ccw_dev.nmdev_types); } char * virNodeDeviceDefFormat(const virNodeDeviceDef *def) { g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; virNodeDevCapsDef *caps; size_t i = 0; virBufferAddLit(&buf, "\n"); virBufferAdjustIndent(&buf, 2); virBufferEscapeString(&buf, "%s\n", def->name); virBufferEscapeString(&buf, "%s\n", def->sysfs_path); if (def->devnode) virBufferEscapeString(&buf, "%s\n", def->devnode); if (def->devlinks) { for (i = 0; def->devlinks[i]; i++) virBufferEscapeString(&buf, "%s\n", def->devlinks[i]); } if (def->parent) virBufferEscapeString(&buf, "%s\n", def->parent); if (def->driver) { virBufferAddLit(&buf, "\n"); virBufferAdjustIndent(&buf, 2); virBufferEscapeString(&buf, "%s\n", def->driver); virBufferAdjustIndent(&buf, -2); virBufferAddLit(&buf, "\n"); } for (caps = def->caps; caps; caps = caps->next) { virNodeDevCapData *data = &caps->data; virBufferAsprintf(&buf, "\n", virNodeDevCapTypeToString(caps->data.type)); virBufferAdjustIndent(&buf, 2); switch (caps->data.type) { case VIR_NODE_DEV_CAP_SYSTEM: virNodeDeviceCapSystemDefFormat(&buf, data); break; case VIR_NODE_DEV_CAP_PCI_DEV: virNodeDeviceCapPCIDefFormat(&buf, data); break; case VIR_NODE_DEV_CAP_USB_DEV: virNodeDeviceCapUSBDevDefFormat(&buf, data); break; case VIR_NODE_DEV_CAP_USB_INTERFACE: virNodeDeviceCapUSBInterfaceDefFormat(&buf, data); break; case VIR_NODE_DEV_CAP_NET: virNodeDeviceCapNetDefFormat(&buf, data); break; case VIR_NODE_DEV_CAP_SCSI_HOST: virNodeDeviceCapSCSIHostDefFormat(&buf, data); break; case VIR_NODE_DEV_CAP_SCSI_TARGET: virBufferEscapeString(&buf, "%s\n", data->scsi_target.name); if (data->scsi_target.flags & VIR_NODE_DEV_CAP_FLAG_FC_RPORT) { virBufferAddLit(&buf, "\n"); virBufferAdjustIndent(&buf, 2); virBufferAsprintf(&buf, "%s\n", data->scsi_target.rport); virBufferAsprintf(&buf, "%s\n", data->scsi_target.wwpn); virBufferAdjustIndent(&buf, -2); virBufferAddLit(&buf, "\n"); } break; case VIR_NODE_DEV_CAP_SCSI: virNodeDeviceCapSCSIDefFormat(&buf, data); break; case VIR_NODE_DEV_CAP_STORAGE: virNodeDeviceCapStorageDefFormat(&buf, data); break; case VIR_NODE_DEV_CAP_SCSI_GENERIC: virBufferEscapeString(&buf, "%s\n", data->sg.path); break; case VIR_NODE_DEV_CAP_DRM: virBufferEscapeString(&buf, "%s\n", virNodeDevDRMTypeToString(data->drm.type)); break; case VIR_NODE_DEV_CAP_MDEV: virNodeDeviceCapMdevDefFormat(&buf, data); break; case VIR_NODE_DEV_CAP_CCW_DEV: virNodeDeviceCapCCWDefFormat(&buf, data); break; case VIR_NODE_DEV_CAP_CSS_DEV: virNodeDeviceCapCSSDefFormat(&buf, data); break; case VIR_NODE_DEV_CAP_VDPA: virNodeDeviceCapVDPADefFormat(&buf, data); break; case VIR_NODE_DEV_CAP_AP_CARD: virBufferAsprintf(&buf, "0x%02x\n", data->ap_card.ap_adapter); break; case VIR_NODE_DEV_CAP_AP_QUEUE: virBufferAsprintf(&buf, "0x%02x\n", data->ap_queue.ap_adapter); virBufferAsprintf(&buf, "0x%04x\n", data->ap_queue.ap_domain); break; case VIR_NODE_DEV_CAP_AP_MATRIX: if (data->ap_matrix.flags & VIR_NODE_DEV_CAP_FLAG_AP_MATRIX_MDEV) virNodeDeviceCapMdevTypesFormat(&buf, data->ap_matrix.mdev_types, data->ap_matrix.nmdev_types); break; case VIR_NODE_DEV_CAP_MDEV_TYPES: virNodeDeviceCapMdevTypesFormat(&buf, data->mdev_parent.mdev_types, data->mdev_parent.nmdev_types); break; case VIR_NODE_DEV_CAP_FC_HOST: case VIR_NODE_DEV_CAP_VPORTS: case VIR_NODE_DEV_CAP_VPD: case VIR_NODE_DEV_CAP_LAST: break; } virBufferAdjustIndent(&buf, -2); virBufferAddLit(&buf, "\n"); } virBufferAdjustIndent(&buf, -2); virBufferAddLit(&buf, "\n"); return virBufferContentAndReset(&buf); } /** * virNodeDevCapsDefParseIntOptional: * @xpath: XPath to evaluate * @ctxt: Context * @value: Where to store parsed value * @def: Node device which is parsed * @invalid_error_fmt: error message to print on invalid format * * Returns: -1 on error (invalid int format under @xpath) * 0 if @xpath was not found (@value is untouched) * 1 on success */ static int virNodeDevCapsDefParseIntOptional(const char *xpath, xmlXPathContextPtr ctxt, int *value, virNodeDeviceDef *def, const char *invalid_error_fmt) { int ret; int val; ret = virXPathInt(xpath, ctxt, &val); if (ret < -1) { virReportError(VIR_ERR_INTERNAL_ERROR, invalid_error_fmt, def->name); return -1; } else if (ret == -1) { return 0; } *value = val; return 1; } static int virNodeDevCapsDefParseUInt(const char *xpath, xmlXPathContextPtr ctxt, unsigned int *value, virNodeDeviceDef *def, const char *missing_error_fmt, const char *invalid_error_fmt) { int ret; ret = virXPathUInt(xpath, ctxt, value); if (ret < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, ret == -1 ? missing_error_fmt : invalid_error_fmt, def->name); return -1; } return 0; } static int virNodeDevCapsDefParseULongLong(const char *xpath, xmlXPathContextPtr ctxt, unsigned long long *value, virNodeDeviceDef *def, const char *missing_error_fmt, const char *invalid_error_fmt) { int ret; unsigned long long val; ret = virXPathULongLong(xpath, ctxt, &val); if (ret < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, ret == -1 ? missing_error_fmt : invalid_error_fmt, def->name); return -1; } *value = val; return 0; } static int virNodeDevCapDRMParseXML(xmlXPathContextPtr ctxt, virNodeDeviceDef *def, xmlNodePtr node, virNodeDevCapDRM *drm) { VIR_XPATH_NODE_AUTORESTORE(ctxt) int val; g_autofree char *type = NULL; ctxt->node = node; type = virXPathString("string(./type[1])", ctxt); if ((val = virNodeDevDRMTypeFromString(type)) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("unknown drm type '%s' for '%s'"), type, def->name); return -1; } drm->type = val; return 0; } static int virNodeDevCapMdevTypesParseXML(xmlXPathContextPtr ctxt, virMediatedDeviceType ***mdev_types, size_t *nmdev_types) { int ret = -1; xmlNodePtr orignode = NULL; g_autofree xmlNodePtr *nodes = NULL; int ntypes = -1; virMediatedDeviceType *type = NULL; size_t i; if ((ntypes = virXPathNodeSet("./type", ctxt, &nodes)) < 0) goto cleanup; if (nmdev_types == 0) { virReportError(VIR_ERR_XML_ERROR, "%s", _("missing element in element")); goto cleanup; } orignode = ctxt->node; for (i = 0; i < ntypes; i++) { ctxt->node = nodes[i]; type = g_new0(virMediatedDeviceType, 1); if (!(type->id = virXPathString("string(./@id[1])", ctxt))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("missing 'id' attribute for mediated device's " " element")); goto cleanup; } if (!(type->device_api = virXPathString("string(./deviceAPI[1])", ctxt))) { virReportError(VIR_ERR_XML_ERROR, _("missing device API for mediated device type '%s'"), type->id); goto cleanup; } if (virXPathUInt("string(./availableInstances)", ctxt, &type->available_instances) < 0) { virReportError(VIR_ERR_XML_ERROR, _("missing number of available instances for " "mediated device type '%s'"), type->id); goto cleanup; } type->name = virXPathString("string(./name)", ctxt); VIR_APPEND_ELEMENT(*mdev_types, *nmdev_types, type); } ret = 0; cleanup: virMediatedDeviceTypeFree(type); ctxt->node = orignode; return ret; } static int virNodeDeviceCapVPDParseCustomFields(xmlXPathContextPtr ctxt, virPCIVPDResource *res, bool readOnly) { int nfields = -1; g_autofree xmlNodePtr *nodes = NULL; size_t i = 0; if ((nfields = virXPathNodeSet("./vendor_field[@index]", ctxt, &nodes)) < 0) { virReportError(VIR_ERR_XML_ERROR, "%s", _("failed to evaluate elements")); return -1; } for (i = 0; i < nfields; i++) { g_autofree char *value = NULL; g_autofree char *index = NULL; VIR_XPATH_NODE_AUTORESTORE(ctxt) g_autofree char *keyword = NULL; ctxt->node = nodes[i]; if (!(index = virXPathString("string(./@index[1])", ctxt)) || strlen(index) > 1) { virReportError(VIR_ERR_XML_ERROR, "%s", _(" evaluation has failed")); continue; } if (!(value = virXPathString("string(./text())", ctxt))) { virReportError(VIR_ERR_XML_ERROR, "%s", _(" value evaluation has failed")); continue; } keyword = g_strdup_printf("V%c", index[0]); virPCIVPDResourceUpdateKeyword(res, readOnly, keyword, value); } VIR_FREE(nodes); if (!readOnly) { if ((nfields = virXPathNodeSet("./system_field[@index]", ctxt, &nodes)) < 0) { virReportError(VIR_ERR_XML_ERROR, "%s", _("failed to evaluate elements")); return -1; } for (i = 0; i < nfields; i++) { g_autofree char *value = NULL; g_autofree char *index = NULL; g_autofree char *keyword = NULL; VIR_XPATH_NODE_AUTORESTORE(ctxt); ctxt->node = nodes[i]; if (!(index = virXPathString("string(./@index[1])", ctxt)) || strlen(index) > 1) { virReportError(VIR_ERR_XML_ERROR, "%s", _(" evaluation has failed")); continue; } if (!(value = virXPathString("string(./text())", ctxt))) { virReportError(VIR_ERR_XML_ERROR, "%s", _(" value evaluation has failed")); continue; } keyword = g_strdup_printf("Y%c", index[0]); virPCIVPDResourceUpdateKeyword(res, readOnly, keyword, value); } } return 0; } static int virNodeDeviceCapVPDParseReadOnlyFields(xmlXPathContextPtr ctxt, virPCIVPDResource *res) { const char *keywords[] = {"change_level", "manufacture_id", "serial_number", "part_number", NULL}; size_t i = 0; if (res == NULL) return -1; res->ro = virPCIVPDResourceRONew(); while (keywords[i]) { g_autofree char *expression = g_strdup_printf("string(./%s)", keywords[i]); g_autofree char *result = virXPathString(expression, ctxt); virPCIVPDResourceUpdateKeyword(res, true, keywords[i], result); ++i; } if (virNodeDeviceCapVPDParseCustomFields(ctxt, res, true) < 0) return -1; return 0; } static int virNodeDeviceCapVPDParseReadWriteFields(xmlXPathContextPtr ctxt, virPCIVPDResource *res) { g_autofree char *assetTag = virXPathString("string(./asset_tag)", ctxt); res->rw = virPCIVPDResourceRWNew(); virPCIVPDResourceUpdateKeyword(res, false, "asset_tag", assetTag); if (virNodeDeviceCapVPDParseCustomFields(ctxt, res, false) < 0) return -1; return 0; } static int virNodeDeviceCapVPDParseXML(xmlXPathContextPtr ctxt, virPCIVPDResource **res) { g_autofree xmlNodePtr *nodes = NULL; int nfields = -1; size_t i = 0; g_autoptr(virPCIVPDResource) newres = g_new0(virPCIVPDResource, 1); if (res == NULL) return -1; if (!(newres->name = virXPathString("string(./name)", ctxt))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Could not read a device name from the element")); return -1; } if ((nfields = virXPathNodeSet("./fields[@access]", ctxt, &nodes)) < 0) { virReportError(VIR_ERR_XML_ERROR, "%s", _("no VPD elements with an access type attribute found")); return -1; } for (i = 0; i < nfields; i++) { g_autofree char *access = NULL; VIR_XPATH_NODE_AUTORESTORE(ctxt); ctxt->node = nodes[i]; if (!(access = virXPathString("string(./@access[1])", ctxt))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("VPD fields access type parsing has failed")); return -1; } if (STREQ(access, "readonly")) { if (virNodeDeviceCapVPDParseReadOnlyFields(ctxt, newres) < 0) { virReportError(VIR_ERR_XML_ERROR, _("Could not parse %s VPD resource fields"), access); return -1; } } else if (STREQ(access, "readwrite")) { if (virNodeDeviceCapVPDParseReadWriteFields(ctxt, newres) < 0) { virReportError(VIR_ERR_XML_ERROR, _("Could not parse %s VPD resource fields"), access); return -1; } } else { virReportError(VIR_ERR_XML_ERROR, _("Unsupported VPD field access type specified %s"), access); return -1; } } /* Replace the existing VPD representation if there is one already. */ if (*res != NULL) virPCIVPDResourceFree(*res); *res = g_steal_pointer(&newres); return 0; } static int virNodeDevAPMatrixCapabilityParseXML(xmlXPathContextPtr ctxt, xmlNodePtr node, virNodeDevCapAPMatrix *apm_dev) { g_autofree char *type = virXMLPropString(node, "type"); VIR_XPATH_NODE_AUTORESTORE(ctxt) ctxt->node = node; if (!type) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing capability type")); return -1; } if (STREQ(type, "mdev_types")) { if (virNodeDevCapMdevTypesParseXML(ctxt, &apm_dev->mdev_types, &apm_dev->nmdev_types) < 0) return -1; apm_dev->flags |= VIR_NODE_DEV_CAP_FLAG_AP_MATRIX_MDEV; } return 0; } static int virNodeDevCCWDeviceAddressParseXML(xmlXPathContextPtr ctxt, xmlNodePtr node, const char *dev_name, virCCWDeviceAddress *ccw_addr) { VIR_XPATH_NODE_AUTORESTORE(ctxt) g_autofree char *cssid = NULL; g_autofree char *ssid = NULL; g_autofree char *devno = NULL; ctxt->node = node; if (!(cssid = virXPathString("string(./cssid[1])", ctxt))) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("missing cssid value for '%s'"), dev_name); return -1; } if (virStrToLong_uip(cssid, NULL, 0, &ccw_addr->cssid) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("invalid cssid value '%s' for '%s'"), cssid, dev_name); return -1; } if (!(ssid = virXPathString("string(./ssid[1])", ctxt))) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("missing ssid value for '%s'"), dev_name); return -1; } if (virStrToLong_uip(ssid, NULL, 0, &ccw_addr->ssid) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("invalid ssid value '%s' for '%s'"), ssid, dev_name); return -1; } if (!(devno = virXPathString("string(./devno[1])", ctxt))) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("missing devno value for '%s'"), dev_name); return -1; } if (virStrToLong_uip(devno, NULL, 16, &ccw_addr->devno) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("invalid devno value '%s' for '%s'"), devno, dev_name); return -1; } return 0; } static int virNodeDevCapCCWParseXML(xmlXPathContextPtr ctxt, virNodeDeviceDef *def, xmlNodePtr node, virNodeDevCapCCW *ccw_dev) { VIR_XPATH_NODE_AUTORESTORE(ctxt) g_autofree virCCWDeviceAddress *ccw_addr = NULL; ctxt->node = node; ccw_addr = g_new0(virCCWDeviceAddress, 1); if (virNodeDevCCWDeviceAddressParseXML(ctxt, node, def->name, ccw_addr) < 0) return -1; ccw_dev->cssid = ccw_addr->cssid; ccw_dev->ssid = ccw_addr->ssid; ccw_dev->devno = ccw_addr->devno; return 0; } static int virNodeDevCSSCapabilityParseXML(xmlXPathContextPtr ctxt, xmlNodePtr node, virNodeDevCapCCW *ccw_dev) { g_autofree char *type = virXMLPropString(node, "type"); VIR_XPATH_NODE_AUTORESTORE(ctxt) ctxt->node = node; if (!type) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing capability type")); return -1; } if (STREQ(type, "mdev_types")) { if (virNodeDevCapMdevTypesParseXML(ctxt, &ccw_dev->mdev_types, &ccw_dev->nmdev_types) < 0) return -1; ccw_dev->flags |= VIR_NODE_DEV_CAP_FLAG_CSS_MDEV; } return 0; } static int virNodeDevCapCSSParseXML(xmlXPathContextPtr ctxt, virNodeDeviceDef *def, xmlNodePtr node, virNodeDevCapCCW *ccw_dev) { VIR_XPATH_NODE_AUTORESTORE(ctxt) g_autofree xmlNodePtr *nodes = NULL; int n = 0; size_t i = 0; xmlNodePtr channel_ddno = NULL; ctxt->node = node; if (virNodeDevCapCCWParseXML(ctxt, def, node, ccw_dev) < 0) return -1; if ((n = virXPathNodeSet("./capability", ctxt, &nodes)) < 0) return -1; for (i = 0; i < n; i++) { if (virNodeDevCSSCapabilityParseXML(ctxt, nodes[i], ccw_dev) < 0) return -1; } /* channel_dev_addr is optional */ if ((channel_ddno = virXPathNode("./channel_dev_addr[1]", ctxt))) { g_autofree virCCWDeviceAddress *channel_dev = NULL; channel_dev = g_new0(virCCWDeviceAddress, 1); if (virNodeDevCCWDeviceAddressParseXML(ctxt, channel_ddno, def->name, channel_dev) < 0) return -1; ccw_dev->channel_dev_addr = g_steal_pointer(&channel_dev); } return 0; } static int virNodeDevCapAPAdapterParseXML(xmlXPathContextPtr ctxt, virNodeDeviceDef *def, unsigned int *ap_adapter) { g_autofree char *adapter = NULL; if (!(adapter = virXPathString("string(./ap-adapter[1])", ctxt))) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("missing ap-adapter value for '%s'"), def->name); return -1; } if (virStrToLong_uip(adapter, NULL, 0, ap_adapter) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("invalid ap-adapter value '%s' for '%s'"), adapter, def->name); return -1; } return 0; } static int virNodeDevCapAPCardParseXML(xmlXPathContextPtr ctxt, virNodeDeviceDef *def, xmlNodePtr node, virNodeDevCapAPCard *ap_card) { VIR_XPATH_NODE_AUTORESTORE(ctxt) ctxt->node = node; return virNodeDevCapAPAdapterParseXML(ctxt, def, &ap_card->ap_adapter); } static int virNodeDevCapAPQueueParseXML(xmlXPathContextPtr ctxt, virNodeDeviceDef *def, xmlNodePtr node, virNodeDevCapAPQueue *ap_queue) { int ret = -1; VIR_XPATH_NODE_AUTORESTORE(ctxt) g_autofree char *dom = NULL; ctxt->node = node; ret = virNodeDevCapAPAdapterParseXML(ctxt, def, &ap_queue->ap_adapter); if (ret < 0) return ret; if (!(dom = virXPathString("string(./ap-domain[1])", ctxt))) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("missing ap-domain value for '%s'"), def->name); return -1; } if (virStrToLong_uip(dom, NULL, 0, &ap_queue->ap_domain) < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("invalid ap-domain value '%s' for '%s'"), dom, def->name); return -1; } if (ap_queue->ap_domain > 255) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("ap-domain value '%s' is out of range for '%s'"), dom, def->name); return -1; } return 0; } static int virNodeDevCapAPMatrixParseXML(xmlXPathContextPtr ctxt, virNodeDeviceDef *def G_GNUC_UNUSED, xmlNodePtr node, virNodeDevCapAPMatrix *ap_matrix) { VIR_XPATH_NODE_AUTORESTORE(ctxt) g_autofree xmlNodePtr *nodes = NULL; int n = 0; size_t i = 0; ctxt->node = node; if ((n = virXPathNodeSet("./capability", ctxt, &nodes)) < 0) return -1; for (i = 0; i < n; i++) { if (virNodeDevAPMatrixCapabilityParseXML(ctxt, nodes[i], ap_matrix) < 0) return -1; } return 0; } static int virNodeDevCapStorageParseXML(xmlXPathContextPtr ctxt, virNodeDeviceDef *def, xmlNodePtr node, virNodeDevCapStorage *storage) { VIR_XPATH_NODE_AUTORESTORE(ctxt) g_autofree xmlNodePtr *nodes = NULL; size_t i; int n; unsigned long long val; ctxt->node = node; storage->block = virXPathString("string(./block[1])", ctxt); if (!storage->block) { virReportError(VIR_ERR_INTERNAL_ERROR, _("no block device path supplied for '%s'"), def->name); return -1; } storage->bus = virXPathString("string(./bus[1])", ctxt); storage->drive_type = virXPathString("string(./drive_type[1])", ctxt); storage->model = virXPathString("string(./model[1])", ctxt); storage->vendor = virXPathString("string(./vendor[1])", ctxt); storage->serial = virXPathString("string(./serial[1])", ctxt); if ((n = virXPathNodeSet("./capability", ctxt, &nodes)) < 0) return -1; for (i = 0; i < n; i++) { g_autofree char *type = virXMLPropString(nodes[i], "type"); if (!type) { virReportError(VIR_ERR_INTERNAL_ERROR, _("missing storage capability type for '%s'"), def->name); return -1; } if (STREQ(type, "hotpluggable")) { storage->flags |= VIR_NODE_DEV_CAP_STORAGE_HOTPLUGGABLE; } else if (STREQ(type, "removable")) { xmlNodePtr orignode2; storage->flags |= VIR_NODE_DEV_CAP_STORAGE_REMOVABLE; orignode2 = ctxt->node; ctxt->node = nodes[i]; if (virXPathBoolean("count(./media_available[. = '1']) > 0", ctxt)) storage->flags |= VIR_NODE_DEV_CAP_STORAGE_REMOVABLE_MEDIA_AVAILABLE; storage->media_label = virXPathString("string(./media_label[1])", ctxt); val = 0; if (virNodeDevCapsDefParseULongLong("string(./media_size[1])", ctxt, &val, def, _("no removable media size supplied for '%s'"), _("invalid removable media size supplied for '%s'")) < 0) { return -1; } storage->removable_media_size = val; ctxt->node = orignode2; } else { virReportError(VIR_ERR_INTERNAL_ERROR, _("unknown storage capability type '%s' for '%s'"), type, def->name); return -1; } } if (!(storage->flags & VIR_NODE_DEV_CAP_STORAGE_REMOVABLE)) { val = 0; if (virNodeDevCapsDefParseULongLong("string(./size[1])", ctxt, &val, def, _("no size supplied for '%s'"), _("invalid size supplied for '%s'")) < 0) return -1; storage->size = val; } return 0; } static int virNodeDevCapSCSIParseXML(xmlXPathContextPtr ctxt, virNodeDeviceDef *def, xmlNodePtr node, virNodeDevCapSCSI *scsi) { VIR_XPATH_NODE_AUTORESTORE(ctxt) ctxt->node = node; if (virNodeDevCapsDefParseUInt("string(./host[1])", ctxt, &scsi->host, def, _("no SCSI host ID supplied for '%s'"), _("invalid SCSI host ID supplied for '%s'")) < 0) return -1; if (virNodeDevCapsDefParseUInt("string(./bus[1])", ctxt, &scsi->bus, def, _("no SCSI bus ID supplied for '%s'"), _("invalid SCSI bus ID supplied for '%s'")) < 0) return -1; if (virNodeDevCapsDefParseUInt("string(./target[1])", ctxt, &scsi->target, def, _("no SCSI target ID supplied for '%s'"), _("invalid SCSI target ID supplied for '%s'")) < 0) return -1; if (virNodeDevCapsDefParseUInt("string(./lun[1])", ctxt, &scsi->lun, def, _("no SCSI LUN ID supplied for '%s'"), _("invalid SCSI LUN ID supplied for '%s'")) < 0) return -1; scsi->type = virXPathString("string(./type[1])", ctxt); return 0; } static int virNodeDevCapSCSITargetParseXML(xmlXPathContextPtr ctxt, virNodeDeviceDef *def, xmlNodePtr node, virNodeDevCapSCSITarget *scsi_target) { VIR_XPATH_NODE_AUTORESTORE(ctxt) g_autofree xmlNodePtr *nodes = NULL; int n = 0; size_t i; ctxt->node = node; scsi_target->name = virXPathString("string(./target[1])", ctxt); if (!scsi_target->name) { virReportError(VIR_ERR_INTERNAL_ERROR, _("no target name supplied for '%s'"), def->name); return -1; } if ((n = virXPathNodeSet("./capability", ctxt, &nodes)) < 0) return -1; for (i = 0; i < n; ++i) { g_autofree char *type = NULL; type = virXMLPropString(nodes[i], "type"); if (!type) { virReportError(VIR_ERR_INTERNAL_ERROR, _("missing type for SCSI target capability for '%s'"), def->name); return -1; } if (STREQ(type, "fc_remote_port")) { scsi_target->flags |= VIR_NODE_DEV_CAP_FLAG_FC_RPORT; ctxt->node = nodes[i]; if (virNodeDevCapsDefParseString("string(./rport[1])", ctxt, &scsi_target->rport) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("missing rport name for '%s'"), def->name); return -1; } if (virNodeDevCapsDefParseString("string(./wwpn[1])", ctxt, &scsi_target->wwpn) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("missing wwpn identifier for '%s'"), def->name); return -1; } } else { virReportError(VIR_ERR_INTERNAL_ERROR, _("unknown SCSI target capability type '%s' for '%s'"), type, def->name); return -1; } } return 0; } static int virNodeDevCapSCSIHostParseXML(xmlXPathContextPtr ctxt, virNodeDeviceDef *def, xmlNodePtr node, virNodeDevCapSCSIHost *scsi_host, int create, const char *virt_type) { VIR_XPATH_NODE_AUTORESTORE(ctxt) g_autofree xmlNodePtr *nodes = NULL; int n = 0; size_t i; ctxt->node = node; if (create == EXISTING_DEVICE) { if (virNodeDevCapsDefParseUInt("string(./host[1])", ctxt, &scsi_host->host, def, _("no SCSI host ID supplied for '%s'"), _("invalid SCSI host ID supplied for '%s'")) < 0) { return -1; } /* Optional unique_id value */ scsi_host->unique_id = -1; if (virNodeDevCapsDefParseIntOptional("number(./unique_id[1])", ctxt, &scsi_host->unique_id, def, _("invalid unique_id supplied for '%s'")) < 0) { return -1; } } if ((n = virXPathNodeSet("./capability", ctxt, &nodes)) < 0) return -1; for (i = 0; i < n; i++) { g_autofree char *type = NULL; type = virXMLPropString(nodes[i], "type"); if (!type) { virReportError(VIR_ERR_INTERNAL_ERROR, _("missing SCSI host capability type for '%s'"), def->name); return -1; } if (STREQ(type, "vport_ops")) { scsi_host->flags |= VIR_NODE_DEV_CAP_FLAG_HBA_VPORT_OPS; } else if (STREQ(type, "fc_host")) { scsi_host->flags |= VIR_NODE_DEV_CAP_FLAG_HBA_FC_HOST; ctxt->node = nodes[i]; if (virNodeDevCapsDefParseString("string(./wwnn[1])", ctxt, &scsi_host->wwnn) < 0) { if (virRandomGenerateWWN(&scsi_host->wwnn, virt_type) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("no WWNN supplied for '%s', and " "auto-generation failed"), def->name); return -1; } } if (virNodeDevCapsDefParseString("string(./wwpn[1])", ctxt, &scsi_host->wwpn) < 0) { if (virRandomGenerateWWN(&scsi_host->wwpn, virt_type) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("no WWPN supplied for '%s', and " "auto-generation failed"), def->name); return -1; } } if (virNodeDevCapsDefParseString("string(./fabric_wwn[1])", ctxt, &scsi_host->fabric_wwn) < 0) VIR_DEBUG("No fabric_wwn defined for '%s'", def->name); } else { virReportError(VIR_ERR_INTERNAL_ERROR, _("unknown SCSI host capability type '%s' for '%s'"), type, def->name); return -1; } } return 0; } static int virNodeDevCapNetParseXML(xmlXPathContextPtr ctxt, virNodeDeviceDef *def, xmlNodePtr node, virNodeDevCapNet *net) { VIR_XPATH_NODE_AUTORESTORE(ctxt) xmlNodePtr lnk; size_t i = -1; int n = -1; g_autofree char *type = NULL; g_autofree xmlNodePtr *nodes = NULL; ctxt->node = node; net->ifname = virXPathString("string(./interface[1])", ctxt); if (!net->ifname) { virReportError(VIR_ERR_INTERNAL_ERROR, _("no network interface supplied for '%s'"), def->name); return -1; } net->address = virXPathString("string(./address[1])", ctxt); if ((n = virXPathNodeSet("./feature", ctxt, &nodes)) < 0) return -1; if (n > 0) net->features = virBitmapNew(VIR_NET_DEV_FEAT_LAST); for (i = 0; i < n; i++) { g_autofree char *tmp = NULL; int val; if (!(tmp = virXMLPropString(nodes[i], "name"))) { virReportError(VIR_ERR_XML_ERROR, "%s", _("missing network device feature name")); return -1; } if ((val = virNetDevFeatureTypeFromString(tmp)) < 0) { virReportError(VIR_ERR_XML_ERROR, _("unknown network device feature '%s'"), tmp); return -1; } ignore_value(virBitmapSetBit(net->features, val)); } net->subtype = VIR_NODE_DEV_CAP_NET_LAST; type = virXPathString("string(./capability/@type)", ctxt); if (type) { int val = virNodeDevNetCapTypeFromString(type); if (val < 0) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("invalid network type supplied for '%s'"), def->name); return -1; } net->subtype = val; } lnk = virXPathNode("./link", ctxt); if (lnk && virInterfaceLinkParseXML(lnk, &net->lnk) < 0) return -1; return 0; } static int virNodeDevCapUSBInterfaceParseXML(xmlXPathContextPtr ctxt, virNodeDeviceDef *def, xmlNodePtr node, virNodeDevCapUSBIf *usb_if) { VIR_XPATH_NODE_AUTORESTORE(ctxt) ctxt->node = node; if (virNodeDevCapsDefParseUInt("string(./number[1])", ctxt, &usb_if->number, def, _("no USB interface number supplied for '%s'"), _("invalid USB interface number supplied for '%s'")) < 0) return -1; if (virNodeDevCapsDefParseUInt("string(./class[1])", ctxt, &usb_if->klass, def, _("no USB interface class supplied for '%s'"), _("invalid USB interface class supplied for '%s'")) < 0) return -1; if (virNodeDevCapsDefParseUInt("string(./subclass[1])", ctxt, &usb_if->subclass, def, _("no USB interface subclass supplied for '%s'"), _("invalid USB interface subclass supplied for '%s'")) < 0) return -1; if (virNodeDevCapsDefParseUInt("string(./protocol[1])", ctxt, &usb_if->protocol, def, _("no USB interface protocol supplied for '%s'"), _("invalid USB interface protocol supplied for '%s'")) < 0) return -1; usb_if->description = virXPathString("string(./description[1])", ctxt); return 0; } static int virNodeDevCapsDefParseHexId(const char *xpath, xmlXPathContextPtr ctxt, unsigned *value, virNodeDeviceDef *def, const char *missing_error_fmt, const char *invalid_error_fmt) { int ret; unsigned long val; ret = virXPathULongHex(xpath, ctxt, &val); if (ret < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, ret == -1 ? missing_error_fmt : invalid_error_fmt, def->name); return -1; } *value = val; return 0; } static int virNodeDevCapUSBDevParseXML(xmlXPathContextPtr ctxt, virNodeDeviceDef *def, xmlNodePtr node, virNodeDevCapUSBDev *usb_dev) { VIR_XPATH_NODE_AUTORESTORE(ctxt) ctxt->node = node; if (virNodeDevCapsDefParseUInt("string(./bus[1])", ctxt, &usb_dev->bus, def, _("no USB bus number supplied for '%s'"), _("invalid USB bus number supplied for '%s'")) < 0) return -1; if (virNodeDevCapsDefParseUInt("string(./device[1])", ctxt, &usb_dev->device, def, _("no USB device number supplied for '%s'"), _("invalid USB device number supplied for '%s'")) < 0) return -1; if (virNodeDevCapsDefParseHexId("string(./vendor[1]/@id)", ctxt, &usb_dev->vendor, def, _("no USB vendor ID supplied for '%s'"), _("invalid USB vendor ID supplied for '%s'")) < 0) return -1; if (virNodeDevCapsDefParseHexId("string(./product[1]/@id)", ctxt, &usb_dev->product, def, _("no USB product ID supplied for '%s'"), _("invalid USB product ID supplied for '%s'")) < 0) return -1; usb_dev->vendor_name = virXPathString("string(./vendor[1])", ctxt); usb_dev->product_name = virXPathString("string(./product[1])", ctxt); return 0; } static int virNodeDevCapPCIDevIommuGroupParseXML(xmlXPathContextPtr ctxt, xmlNodePtr iommuGroupNode, virNodeDevCapPCIDev *pci_dev) { VIR_XPATH_NODE_AUTORESTORE(ctxt) g_autofree xmlNodePtr *addrNodes = NULL; int nAddrNodes; size_t i; ctxt->node = iommuGroupNode; if (virXMLPropUInt(iommuGroupNode, "number", 10, VIR_XML_PROP_REQUIRED, &pci_dev->iommuGroupNumber) < 0) return -1; if ((nAddrNodes = virXPathNodeSet("./address", ctxt, &addrNodes)) < 0) return -1; for (i = 0; i < nAddrNodes; i++) { g_autoptr(virPCIDeviceAddress) pciAddr = g_new0(virPCIDeviceAddress, 1); if (virPCIDeviceAddressParseXML(addrNodes[i], pciAddr) < 0) return -1; VIR_APPEND_ELEMENT(pci_dev->iommuGroupDevices, pci_dev->nIommuGroupDevices, pciAddr); } return 0; } static int virPCIEDeviceInfoLinkParseXML(xmlNodePtr linkNode, virPCIELink *lnk) { if (virXMLPropUInt(linkNode, "width", 0, VIR_XML_PROP_REQUIRED, &lnk->width) < 0) return -1; if (virXMLPropEnum(linkNode, "speed", virPCIELinkSpeedTypeFromString, VIR_XML_PROP_NONE, &lnk->speed) < 0) return -1; if (virXMLPropInt(linkNode, "port", 10, VIR_XML_PROP_NONE, &lnk->port, -1) < 0) return -1; return 0; } static int virPCIEDeviceInfoParseXML(xmlXPathContextPtr ctxt, xmlNodePtr pciExpressNode, virPCIEDeviceInfo *pci_express) { VIR_XPATH_NODE_AUTORESTORE(ctxt) xmlNodePtr lnk; ctxt->node = pciExpressNode; if ((lnk = virXPathNode("./link[@validity='cap']", ctxt))) { pci_express->link_cap = g_new0(virPCIELink, 1); if (virPCIEDeviceInfoLinkParseXML(lnk, pci_express->link_cap) < 0) return -1; } if ((lnk = virXPathNode("./link[@validity='sta']", ctxt))) { pci_express->link_sta = g_new0(virPCIELink, 1); if (virPCIEDeviceInfoLinkParseXML(lnk, pci_express->link_sta) < 0) return -1; } return 0; } static int virNodeDevPCICapSRIOVPhysicalParseXML(xmlXPathContextPtr ctxt, virNodeDevCapPCIDev *pci_dev) { xmlNodePtr address = virXPathNode("./address[1]", ctxt); pci_dev->physical_function = g_new0(virPCIDeviceAddress, 1); if (!address) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing address in 'phys_function' capability")); return -1; } if (virPCIDeviceAddressParseXML(address, pci_dev->physical_function) < 0) return -1; pci_dev->flags |= VIR_NODE_DEV_CAP_FLAG_PCI_PHYSICAL_FUNCTION; return 0; } static int virNodeDevPCICapSRIOVVirtualParseXML(xmlXPathContextPtr ctxt, virNodeDevCapPCIDev *pci_dev) { g_autofree xmlNodePtr *addresses = NULL; int naddresses = virXPathNodeSet("./address", ctxt, &addresses); g_autofree char *maxFuncsStr = virXPathString("string(./@maxCount)", ctxt); size_t i; if (naddresses < 0) return -1; if (maxFuncsStr && virStrToLong_uip(maxFuncsStr, NULL, 10, &pci_dev->max_virtual_functions) < 0) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Malformed 'maxCount' parameter")); return -1; } pci_dev->virtual_functions = g_new0(virPCIDeviceAddress *, naddresses); for (i = 0; i < naddresses; i++) { g_autoptr(virPCIDeviceAddress) addr = NULL; addr = g_new0(virPCIDeviceAddress, 1); if (virPCIDeviceAddressParseXML(addresses[i], addr) < 0) return -1; VIR_APPEND_ELEMENT(pci_dev->virtual_functions, pci_dev->num_virtual_functions, addr); } pci_dev->flags |= VIR_NODE_DEV_CAP_FLAG_PCI_VIRTUAL_FUNCTION; return 0; } static int virNodeDevPCICapabilityParseXML(xmlXPathContextPtr ctxt, xmlNodePtr node, virNodeDevCapPCIDev *pci_dev) { g_autofree char *type = virXMLPropString(node, "type"); VIR_XPATH_NODE_AUTORESTORE(ctxt) ctxt->node = node; if (!type) { virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing capability type")); return -1; } if (STREQ(type, "phys_function") && virNodeDevPCICapSRIOVPhysicalParseXML(ctxt, pci_dev) < 0) { return -1; } else if (STREQ(type, "virt_functions") && virNodeDevPCICapSRIOVVirtualParseXML(ctxt, pci_dev) < 0) { return -1; } else if (STREQ(type, "mdev_types")) { if (virNodeDevCapMdevTypesParseXML(ctxt, &pci_dev->mdev_types, &pci_dev->nmdev_types) < 0) return -1; pci_dev->flags |= VIR_NODE_DEV_CAP_FLAG_PCI_MDEV; } else if (STREQ(type, "vpd")) { if (virNodeDeviceCapVPDParseXML(ctxt, &pci_dev->vpd) < 0) return -1; pci_dev->flags |= VIR_NODE_DEV_CAP_FLAG_PCI_VPD; } else { int hdrType = virPCIHeaderTypeFromString(type); if (hdrType > 0 && !pci_dev->hdrType) pci_dev->hdrType = hdrType; } return 0; } static int virNodeDevCapPCIDevParseXML(xmlXPathContextPtr ctxt, virNodeDeviceDef *def, xmlNodePtr node, virNodeDevCapPCIDev *pci_dev) { VIR_XPATH_NODE_AUTORESTORE(ctxt) xmlNodePtr iommuGroupNode; xmlNodePtr pciExpress; g_autofree xmlNodePtr *nodes = NULL; int n = 0; int ret = -1; virPCIEDeviceInfo *pci_express = NULL; g_autofree 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; } } else { pci_dev->klass = -1; } if (virNodeDevCapsDefParseUInt("string(./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 (virNodeDevCapsDefParseUInt("string(./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 (virNodeDevCapsDefParseUInt("string(./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 (virNodeDevCapsDefParseUInt("string(./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; } 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 = g_steal_pointer(&pci_express); pci_dev->flags |= VIR_NODE_DEV_CAP_FLAG_PCIE; } ret = 0; out: virPCIEDeviceInfoFree(pci_express); return ret; } static int virNodeDevCapSystemParseXML(xmlXPathContextPtr ctxt, virNodeDeviceDef *def, xmlNodePtr node, virNodeDevCapSystem *syscap) { virNodeDevCapSystemHardware *hardware = &syscap->hardware; virNodeDevCapSystemFirmware *firmware = &syscap->firmware; VIR_XPATH_NODE_AUTORESTORE(ctxt) g_autofree char *tmp = NULL; ctxt->node = node; syscap->product_name = virXPathString("string(./product[1])", ctxt); hardware->vendor_name = virXPathString("string(./hardware/vendor[1])", ctxt); hardware->version = virXPathString("string(./hardware/version[1])", ctxt); hardware->serial = virXPathString("string(./hardware/serial[1])", ctxt); tmp = virXPathString("string(./hardware/uuid[1])", ctxt); if (!tmp) { virReportError(VIR_ERR_INTERNAL_ERROR, _("no system UUID supplied for '%s'"), def->name); return -1; } if (virUUIDParse(tmp, hardware->uuid) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("malformed uuid element for '%s'"), def->name); return -1; } firmware->vendor_name = virXPathString("string(./firmware/vendor[1])", ctxt); firmware->version = virXPathString("string(./firmware/version[1])", ctxt); firmware->release_date = virXPathString("string(./firmware/release_date[1])", ctxt); return 0; } static int virNodeDevCapMdevAttributeParseXML(xmlXPathContextPtr ctxt, xmlNodePtr node, virNodeDevCapMdev *mdev) { VIR_XPATH_NODE_AUTORESTORE(ctxt) g_autoptr(virMediatedDeviceAttr) attr = virMediatedDeviceAttrNew(); ctxt->node = node; attr->name = virXPathString("string(./@name)", ctxt); attr->value = virXPathString("string(./@value)", ctxt); if (!attr->name || !attr->value) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("mdev attribute missing name or value")); return -1; } VIR_APPEND_ELEMENT(mdev->attributes, mdev->nattributes, attr); return 0; } static int virNodeDevCapMdevParseXML(xmlXPathContextPtr ctxt, virNodeDeviceDef *def, xmlNodePtr node, virNodeDevCapMdev *mdev) { VIR_XPATH_NODE_AUTORESTORE(ctxt) int nattrs = 0; g_autofree xmlNodePtr *attrs = NULL; size_t i; g_autofree char *uuidstr = NULL; ctxt->node = node; if (!(mdev->type = virXPathString("string(./type[1]/@id)", ctxt))) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("missing type id attribute for '%s'"), def->name); return -1; } if ((uuidstr = virXPathString("string(./uuid[1])", ctxt))) { unsigned char uuidbuf[VIR_UUID_BUFLEN]; /* make sure that the provided uuid is valid */ if (virUUIDParse(uuidstr, uuidbuf) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Invalid uuid '%s' for new mdev device"), uuidstr); return -1; } mdev->uuid = g_new0(char, VIR_UUID_STRING_BUFLEN); virUUIDFormat(uuidbuf, mdev->uuid); } /* 'iommuGroup' is optional, only report an error if the supplied value is * invalid (-2), not if it's missing (-1) */ if (virXPathUInt("string(./iommuGroup[1]/@number)", ctxt, &mdev->iommuGroupNumber) < -1) { virReportError(VIR_ERR_INTERNAL_ERROR, _("invalid iommuGroup number attribute for '%s'"), def->name); return -1; } if ((nattrs = virXPathNodeSet("./attr", ctxt, &attrs)) < 0) return -1; for (i = 0; i < nattrs; i++) virNodeDevCapMdevAttributeParseXML(ctxt, attrs[i], mdev); return 0; } static virNodeDevCapsDef * virNodeDevCapsDefParseXML(xmlXPathContextPtr ctxt, virNodeDeviceDef *def, xmlNodePtr node, int create, const char *virt_type) { g_autoptr(virNodeDevCapsDef) caps = g_new0(virNodeDevCapsDef, 1); int ret = -1; if (virXMLPropEnum(node, "type", virNodeDevCapTypeFromString, VIR_XML_PROP_REQUIRED, &caps->data.type) < 0) return NULL; switch (caps->data.type) { case VIR_NODE_DEV_CAP_SYSTEM: ret = virNodeDevCapSystemParseXML(ctxt, def, node, &caps->data.system); break; case VIR_NODE_DEV_CAP_PCI_DEV: ret = virNodeDevCapPCIDevParseXML(ctxt, def, node, &caps->data.pci_dev); break; case VIR_NODE_DEV_CAP_USB_DEV: ret = virNodeDevCapUSBDevParseXML(ctxt, def, node, &caps->data.usb_dev); break; case VIR_NODE_DEV_CAP_USB_INTERFACE: ret = virNodeDevCapUSBInterfaceParseXML(ctxt, def, node, &caps->data.usb_if); break; case VIR_NODE_DEV_CAP_NET: ret = virNodeDevCapNetParseXML(ctxt, def, node, &caps->data.net); break; case VIR_NODE_DEV_CAP_SCSI_HOST: ret = virNodeDevCapSCSIHostParseXML(ctxt, def, node, &caps->data.scsi_host, create, virt_type); break; case VIR_NODE_DEV_CAP_SCSI_TARGET: ret = virNodeDevCapSCSITargetParseXML(ctxt, def, node, &caps->data.scsi_target); break; case VIR_NODE_DEV_CAP_SCSI: ret = virNodeDevCapSCSIParseXML(ctxt, def, node, &caps->data.scsi); break; case VIR_NODE_DEV_CAP_STORAGE: ret = virNodeDevCapStorageParseXML(ctxt, def, node, &caps->data.storage); break; case VIR_NODE_DEV_CAP_DRM: ret = virNodeDevCapDRMParseXML(ctxt, def, node, &caps->data.drm); break; case VIR_NODE_DEV_CAP_MDEV: ret = virNodeDevCapMdevParseXML(ctxt, def, node, &caps->data.mdev); break; case VIR_NODE_DEV_CAP_CCW_DEV: ret = virNodeDevCapCCWParseXML(ctxt, def, node, &caps->data.ccw_dev); break; case VIR_NODE_DEV_CAP_CSS_DEV: ret = virNodeDevCapCSSParseXML(ctxt, def, node, &caps->data.ccw_dev); break; case VIR_NODE_DEV_CAP_AP_CARD: ret = virNodeDevCapAPCardParseXML(ctxt, def, node, &caps->data.ap_card); break; case VIR_NODE_DEV_CAP_AP_QUEUE: ret = virNodeDevCapAPQueueParseXML(ctxt, def, node, &caps->data.ap_queue); break; case VIR_NODE_DEV_CAP_AP_MATRIX: ret = virNodeDevCapAPMatrixParseXML(ctxt, def, node, &caps->data.ap_matrix); break; case VIR_NODE_DEV_CAP_MDEV_TYPES: case VIR_NODE_DEV_CAP_FC_HOST: case VIR_NODE_DEV_CAP_VPORTS: case VIR_NODE_DEV_CAP_SCSI_GENERIC: case VIR_NODE_DEV_CAP_VDPA: case VIR_NODE_DEV_CAP_VPD: case VIR_NODE_DEV_CAP_LAST: virReportError(VIR_ERR_INTERNAL_ERROR, _("unknown capability type '%d' for '%s'"), caps->data.type, def->name); ret = -1; break; } if (ret < 0) return NULL; return g_steal_pointer(&caps); } virNodeDeviceDef * virNodeDeviceDefParseXML(xmlXPathContextPtr ctxt, int create, const char *virt_type) { g_autoptr(virNodeDeviceDef) def = g_new0(virNodeDeviceDef, 1); virNodeDevCapsDef **next_cap; g_autofree xmlNodePtr *devnode = NULL; g_autofree xmlNodePtr *capability = NULL; int n, m; size_t i; /* Extract device name */ if (create == EXISTING_DEVICE) { def->name = virXPathString("string(./name[1])", ctxt); if (!def->name) { virReportError(VIR_ERR_NO_NAME, NULL); return NULL; } } else { def->name = g_strdup("new device"); } def->sysfs_path = virXPathString("string(./path[1])", ctxt); /* Parse devnodes */ if ((n = virXPathNodeSet("./devnode", ctxt, &devnode)) < 0) return NULL; def->devlinks = g_new0(char *, n + 1); for (i = 0, m = 0; i < n; i++) { xmlNodePtr node = devnode[i]; virNodeDevDevnodeType val; if (virXMLPropEnum(node, "type", virNodeDevDevnodeTypeFromString, VIR_XML_PROP_REQUIRED, &val) < 0) return NULL; switch (val) { case VIR_NODE_DEV_DEVNODE_DEV: if (!(def->devnode = virXMLNodeContentString(node))) return NULL; break; case VIR_NODE_DEV_DEVNODE_LINK: if (!(def->devlinks[m++] = virXMLNodeContentString(node))) return NULL; break; case VIR_NODE_DEV_DEVNODE_LAST: break; } } /* Extract device parent, if any */ def->parent = virXPathString("string(./parent[1])", ctxt); def->parent_wwnn = virXPathString("string(./parent[1]/@wwnn)", ctxt); def->parent_wwpn = virXPathString("string(./parent[1]/@wwpn)", ctxt); if (def->parent_wwnn && !def->parent_wwpn) { virReportError(VIR_ERR_XML_ERROR, _("when providing parent wwnn='%s', the " "wwpn must also be provided"), def->parent_wwnn); return NULL; } if (!def->parent_wwnn && def->parent_wwpn) { virReportError(VIR_ERR_XML_ERROR, _("when providing parent wwpn='%s', the " "wwnn must also be provided"), def->parent_wwpn); return NULL; } def->parent_fabric_wwn = virXPathString("string(./parent[1]/@fabric_wwn)", ctxt); /* Parse device capabilities */ if ((n = virXPathNodeSet("./capability", ctxt, &capability)) < 0) return NULL; if (n == 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("no device capabilities for '%s'"), def->name); return NULL; } next_cap = &def->caps; for (i = 0; i < n; i++) { *next_cap = virNodeDevCapsDefParseXML(ctxt, def, capability[i], create, virt_type); if (!*next_cap) return NULL; next_cap = &(*next_cap)->next; } return g_steal_pointer(&def); } virNodeDeviceDef * virNodeDeviceDefParse(const char *str, const char *filename, int create, const char *virt_type, virNodeDeviceDefParserCallbacks *parserCallbacks, void *opaque, bool validate) { g_autoptr(xmlDoc) xml = NULL; g_autoptr(xmlXPathContext) ctxt = NULL; g_autoptr(virNodeDeviceDef) def = NULL; if (!(xml = virXMLParse(filename, str, _("(node_device_definition)"), "device", &ctxt, "nodedev.rng", validate))) return NULL; if (!(def = virNodeDeviceDefParseXML(ctxt, create, virt_type))) return NULL; if (parserCallbacks) { int ret = 0; /* fill in backend-specific aspects */ if (parserCallbacks->postParse) { ret = parserCallbacks->postParse(def, opaque); if (ret < 0) return NULL; } /* validate definition */ if (parserCallbacks->validate) { ret = parserCallbacks->validate(def, opaque); if (ret < 0) return NULL; } } return g_steal_pointer(&def); } /* * Return fc_host dev's WWNN and WWPN */ int virNodeDeviceGetWWNs(virNodeDeviceDef *def, char **wwnn, char **wwpn) { virNodeDevCapsDef *cap = NULL; cap = def->caps; while (cap != NULL) { if (cap->data.type == VIR_NODE_DEV_CAP_SCSI_HOST && cap->data.scsi_host.flags & VIR_NODE_DEV_CAP_FLAG_HBA_FC_HOST) { *wwnn = g_strdup(cap->data.scsi_host.wwnn); *wwpn = g_strdup(cap->data.scsi_host.wwpn); break; } cap = cap->next; } if (cap == NULL) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Device is not a fibre channel HBA")); return -1; } return 0; } void virNodeDevCapsDefFree(virNodeDevCapsDef *caps) { size_t i = 0; virNodeDevCapData *data = &caps->data; switch (caps->data.type) { case VIR_NODE_DEV_CAP_SYSTEM: g_free(data->system.product_name); g_free(data->system.hardware.vendor_name); g_free(data->system.hardware.version); g_free(data->system.hardware.serial); g_free(data->system.firmware.vendor_name); g_free(data->system.firmware.version); g_free(data->system.firmware.release_date); break; case VIR_NODE_DEV_CAP_PCI_DEV: g_free(data->pci_dev.product_name); g_free(data->pci_dev.vendor_name); g_free(data->pci_dev.physical_function); for (i = 0; i < data->pci_dev.num_virtual_functions; i++) g_free(data->pci_dev.virtual_functions[i]); g_free(data->pci_dev.virtual_functions); for (i = 0; i < data->pci_dev.nIommuGroupDevices; i++) g_free(data->pci_dev.iommuGroupDevices[i]); g_free(data->pci_dev.iommuGroupDevices); virPCIEDeviceInfoFree(data->pci_dev.pci_express); for (i = 0; i < data->pci_dev.nmdev_types; i++) virMediatedDeviceTypeFree(data->pci_dev.mdev_types[i]); g_free(data->pci_dev.mdev_types); virPCIVPDResourceFree(g_steal_pointer(&data->pci_dev.vpd)); break; case VIR_NODE_DEV_CAP_USB_DEV: g_free(data->usb_dev.product_name); g_free(data->usb_dev.vendor_name); break; case VIR_NODE_DEV_CAP_USB_INTERFACE: g_free(data->usb_if.description); break; case VIR_NODE_DEV_CAP_NET: g_free(data->net.ifname); g_free(data->net.address); virBitmapFree(data->net.features); break; case VIR_NODE_DEV_CAP_SCSI_HOST: g_free(data->scsi_host.wwnn); g_free(data->scsi_host.wwpn); g_free(data->scsi_host.fabric_wwn); break; case VIR_NODE_DEV_CAP_SCSI_TARGET: g_free(data->scsi_target.name); g_free(data->scsi_target.rport); g_free(data->scsi_target.wwpn); break; case VIR_NODE_DEV_CAP_SCSI: g_free(data->scsi.type); break; case VIR_NODE_DEV_CAP_STORAGE: g_free(data->storage.block); g_free(data->storage.bus); g_free(data->storage.drive_type); g_free(data->storage.model); g_free(data->storage.vendor); g_free(data->storage.serial); g_free(data->storage.media_label); break; case VIR_NODE_DEV_CAP_SCSI_GENERIC: g_free(data->sg.path); break; case VIR_NODE_DEV_CAP_MDEV: g_free(data->mdev.type); g_free(data->mdev.uuid); for (i = 0; i < data->mdev.nattributes; i++) virMediatedDeviceAttrFree(data->mdev.attributes[i]); g_free(data->mdev.attributes); g_free(data->mdev.parent_addr); break; case VIR_NODE_DEV_CAP_CSS_DEV: for (i = 0; i < data->ccw_dev.nmdev_types; i++) virMediatedDeviceTypeFree(data->ccw_dev.mdev_types[i]); g_free(data->ccw_dev.mdev_types); g_free(data->ccw_dev.channel_dev_addr); break; case VIR_NODE_DEV_CAP_AP_MATRIX: g_free(data->ap_matrix.addr); for (i = 0; i < data->ap_matrix.nmdev_types; i++) virMediatedDeviceTypeFree(data->ap_matrix.mdev_types[i]); g_free(data->ap_matrix.mdev_types); break; case VIR_NODE_DEV_CAP_MDEV_TYPES: for (i = 0; i < data->mdev_parent.nmdev_types; i++) virMediatedDeviceTypeFree(data->mdev_parent.mdev_types[i]); g_free(data->mdev_parent.mdev_types); g_free(data->mdev_parent.address); break; case VIR_NODE_DEV_CAP_DRM: case VIR_NODE_DEV_CAP_FC_HOST: case VIR_NODE_DEV_CAP_VPORTS: case VIR_NODE_DEV_CAP_CCW_DEV: case VIR_NODE_DEV_CAP_VDPA: case VIR_NODE_DEV_CAP_AP_CARD: case VIR_NODE_DEV_CAP_AP_QUEUE: case VIR_NODE_DEV_CAP_VPD: case VIR_NODE_DEV_CAP_LAST: /* This case is here to shutup the compiler */ break; } g_free(caps); } int virNodeDeviceUpdateCaps(virNodeDeviceDef *def) { virNodeDevCapsDef *cap = def->caps; while (cap) { switch (cap->data.type) { case VIR_NODE_DEV_CAP_SCSI_HOST: virNodeDeviceGetSCSIHostCaps(&cap->data.scsi_host); break; case VIR_NODE_DEV_CAP_SCSI_TARGET: virNodeDeviceGetSCSITargetCaps(def->sysfs_path, &cap->data.scsi_target); break; case VIR_NODE_DEV_CAP_NET: if (virNetDevGetLinkInfo(cap->data.net.ifname, &cap->data.net.lnk) < 0) return -1; virBitmapFree(cap->data.net.features); if (virNetDevGetFeatures(cap->data.net.ifname, &cap->data.net.features) < 0) return -1; break; case VIR_NODE_DEV_CAP_PCI_DEV: if (virNodeDeviceGetPCIDynamicCaps(def->sysfs_path, &cap->data.pci_dev) < 0) return -1; break; case VIR_NODE_DEV_CAP_CSS_DEV: if (virNodeDeviceGetCSSDynamicCaps(def->sysfs_path, &cap->data.ccw_dev) < 0) return -1; break; case VIR_NODE_DEV_CAP_AP_MATRIX: if (virNodeDeviceGetAPMatrixDynamicCaps(def->sysfs_path, &cap->data.ap_matrix) < 0) return -1; break; case VIR_NODE_DEV_CAP_MDEV_TYPES: if (virNodeDeviceGetMdevParentDynamicCaps(def->sysfs_path, &cap->data.mdev_parent) < 0) return -1; break; /* all types that (supposedly) don't require any updates * relative to what's in the cache. */ case VIR_NODE_DEV_CAP_DRM: case VIR_NODE_DEV_CAP_SYSTEM: case VIR_NODE_DEV_CAP_USB_DEV: case VIR_NODE_DEV_CAP_USB_INTERFACE: case VIR_NODE_DEV_CAP_SCSI: case VIR_NODE_DEV_CAP_STORAGE: case VIR_NODE_DEV_CAP_FC_HOST: case VIR_NODE_DEV_CAP_VPORTS: case VIR_NODE_DEV_CAP_SCSI_GENERIC: case VIR_NODE_DEV_CAP_MDEV: case VIR_NODE_DEV_CAP_CCW_DEV: case VIR_NODE_DEV_CAP_VDPA: case VIR_NODE_DEV_CAP_AP_CARD: case VIR_NODE_DEV_CAP_AP_QUEUE: case VIR_NODE_DEV_CAP_VPD: case VIR_NODE_DEV_CAP_LAST: break; } cap = cap->next; } return 0; } /** * virNodeDeviceCapsListExport: * @def: node device definition * @list: pointer to an array to store all supported capabilities by a device * * Takes the definition, scans through all the capabilities that the device * supports (including the nested caps) and populates a newly allocated list * with them. Caller is responsible for freeing the list. * If NULL is passed to @list, only the number of caps will be returned. * * Returns the number of capabilities the device supports, -1 on error. */ int virNodeDeviceCapsListExport(virNodeDeviceDef *def, virNodeDevCapType **list) { virNodeDevCapsDef *caps = NULL; g_autofree virNodeDevCapType *tmp = NULL; bool want_list = !!list; int ncaps = 0; #define MAYBE_ADD_CAP(cap) \ do { \ if (want_list) \ tmp[ncaps] = cap; \ } while (0) if (virNodeDeviceUpdateCaps(def) < 0) return -1; if (want_list) tmp = g_new0(virNodeDevCapType, VIR_NODE_DEV_CAP_LAST - 1); for (caps = def->caps; caps; caps = caps->next) { unsigned int flags; MAYBE_ADD_CAP(caps->data.type); ncaps++; /* check nested caps for a given type as well */ if (caps->data.type == VIR_NODE_DEV_CAP_SCSI_HOST) { flags = caps->data.scsi_host.flags; if (flags & VIR_NODE_DEV_CAP_FLAG_HBA_FC_HOST) { MAYBE_ADD_CAP(VIR_NODE_DEV_CAP_FC_HOST); ncaps++; } if (flags & VIR_NODE_DEV_CAP_FLAG_HBA_VPORT_OPS) { MAYBE_ADD_CAP(VIR_NODE_DEV_CAP_VPORTS); ncaps++; } } if (caps->data.type == VIR_NODE_DEV_CAP_PCI_DEV) { flags = caps->data.pci_dev.flags; if (flags & VIR_NODE_DEV_CAP_FLAG_PCI_MDEV) { MAYBE_ADD_CAP(VIR_NODE_DEV_CAP_MDEV_TYPES); ncaps++; } if (flags & VIR_NODE_DEV_CAP_FLAG_PCI_VPD) { MAYBE_ADD_CAP(VIR_NODE_DEV_CAP_VPD); ncaps++; } } if (caps->data.type == VIR_NODE_DEV_CAP_CSS_DEV) { flags = caps->data.ccw_dev.flags; if (flags & VIR_NODE_DEV_CAP_FLAG_CSS_MDEV) { MAYBE_ADD_CAP(VIR_NODE_DEV_CAP_MDEV_TYPES); ncaps++; } } if (caps->data.type == VIR_NODE_DEV_CAP_AP_MATRIX) { flags = caps->data.ap_matrix.flags; if (flags & VIR_NODE_DEV_CAP_FLAG_AP_MATRIX_MDEV) { MAYBE_ADD_CAP(VIR_NODE_DEV_CAP_MDEV_TYPES); ncaps++; } } } #undef MAYBE_ADD_CAP if (want_list) *list = g_steal_pointer(&tmp); return ncaps; } #ifdef __linux__ int virNodeDeviceGetSCSIHostCaps(virNodeDevCapSCSIHost *scsi_host) { g_autofree char *tmp = NULL; int ret = -1; if ((scsi_host->unique_id = virSCSIHostGetUniqueId(NULL, scsi_host->host)) < 0) { VIR_DEBUG("Failed to read unique_id for host%d", scsi_host->host); scsi_host->unique_id = -1; } VIR_DEBUG("Checking if host%d is an FC HBA", scsi_host->host); if (virVHBAPathExists(NULL, scsi_host->host)) { scsi_host->flags |= VIR_NODE_DEV_CAP_FLAG_HBA_FC_HOST; if (!(tmp = virVHBAGetConfig(NULL, scsi_host->host, "port_name"))) { VIR_WARN("Failed to read WWPN for host%d", scsi_host->host); goto cleanup; } VIR_FREE(scsi_host->wwpn); scsi_host->wwpn = g_steal_pointer(&tmp); if (!(tmp = virVHBAGetConfig(NULL, scsi_host->host, "node_name"))) { VIR_WARN("Failed to read WWNN for host%d", scsi_host->host); goto cleanup; } VIR_FREE(scsi_host->wwnn); scsi_host->wwnn = g_steal_pointer(&tmp); if ((tmp = virVHBAGetConfig(NULL, scsi_host->host, "fabric_name"))) { VIR_FREE(scsi_host->fabric_wwn); scsi_host->fabric_wwn = g_steal_pointer(&tmp); } } if (virVHBAIsVportCapable(NULL, scsi_host->host)) { scsi_host->flags |= VIR_NODE_DEV_CAP_FLAG_HBA_VPORT_OPS; if (!(tmp = virVHBAGetConfig(NULL, scsi_host->host, "max_npiv_vports"))) { VIR_WARN("Failed to read max_npiv_vports for host%d", scsi_host->host); goto cleanup; } if (virStrToLong_i(tmp, NULL, 10, &scsi_host->max_vports) < 0) { VIR_WARN("Failed to parse value of max_npiv_vports '%s'", tmp); goto cleanup; } if (!(tmp = virVHBAGetConfig(NULL, scsi_host->host, "npiv_vports_inuse"))) { VIR_WARN("Failed to read npiv_vports_inuse for host%d", scsi_host->host); goto cleanup; } if (virStrToLong_i(tmp, NULL, 10, &scsi_host->vports) < 0) { VIR_WARN("Failed to parse value of npiv_vports_inuse '%s'", tmp); goto cleanup; } } ret = 0; cleanup: if (ret < 0) { /* Clear the two flags in case of producing confusing XML output */ scsi_host->flags &= ~(VIR_NODE_DEV_CAP_FLAG_HBA_FC_HOST | VIR_NODE_DEV_CAP_FLAG_HBA_VPORT_OPS); VIR_FREE(scsi_host->wwnn); VIR_FREE(scsi_host->wwpn); VIR_FREE(scsi_host->fabric_wwn); } return ret; } int virNodeDeviceGetSCSITargetCaps(const char *sysfsPath, virNodeDevCapSCSITarget *scsi_target) { int ret = -1; g_autofree char *dir = NULL; g_autofree char *rport = NULL; VIR_DEBUG("Checking if '%s' is an FC remote port", scsi_target->name); /* /sys/devices/[...]/host0/rport-0:0-0/target0:0:0 -> rport-0:0-0 */ dir = g_path_get_dirname(sysfsPath); rport = g_path_get_basename(dir); if (!virFCIsCapableRport(rport)) goto cleanup; VIR_FREE(scsi_target->rport); scsi_target->rport = g_steal_pointer(&rport); if (virFCReadRportValue(scsi_target->rport, "port_name", &scsi_target->wwpn) < 0) { VIR_WARN("Failed to read port_name for '%s'", scsi_target->rport); goto cleanup; } scsi_target->flags |= VIR_NODE_DEV_CAP_FLAG_FC_RPORT; ret = 0; cleanup: if (ret < 0) { VIR_FREE(scsi_target->rport); VIR_FREE(scsi_target->wwpn); scsi_target->flags &= ~VIR_NODE_DEV_CAP_FLAG_FC_RPORT; } return ret; } static int virNodeDeviceGetPCISRIOVCaps(const char *sysfsPath, virNodeDevCapPCIDev *pci_dev) { g_autoptr(virPCIVirtualFunctionList) vfs = NULL; size_t i; int ret; /* this could be a refresh, so clear out the old data */ for (i = 0; i < pci_dev->num_virtual_functions; i++) VIR_FREE(pci_dev->virtual_functions[i]); VIR_FREE(pci_dev->virtual_functions); VIR_FREE(pci_dev->physical_function); pci_dev->num_virtual_functions = 0; pci_dev->max_virtual_functions = 0; pci_dev->flags &= ~VIR_NODE_DEV_CAP_FLAG_PCI_VIRTUAL_FUNCTION; pci_dev->flags &= ~VIR_NODE_DEV_CAP_FLAG_PCI_PHYSICAL_FUNCTION; ret = virPCIGetPhysicalFunction(sysfsPath, &pci_dev->physical_function); if (ret < 0) return ret; if (pci_dev->physical_function) pci_dev->flags |= VIR_NODE_DEV_CAP_FLAG_PCI_PHYSICAL_FUNCTION; if (virPCIGetVirtualFunctions(sysfsPath, &vfs) < 0) return -1; pci_dev->virtual_functions = g_new0(virPCIDeviceAddress *, vfs->nfunctions); for (i = 0; i < vfs->nfunctions; i++) pci_dev->virtual_functions[i] = g_steal_pointer(&vfs->functions[i].addr); pci_dev->num_virtual_functions = vfs->nfunctions; pci_dev->max_virtual_functions = vfs->maxfunctions; if (pci_dev->num_virtual_functions > 0 || pci_dev->max_virtual_functions > 0) pci_dev->flags |= VIR_NODE_DEV_CAP_FLAG_PCI_VIRTUAL_FUNCTION; return ret; } static int virNodeDeviceGetPCIIOMMUGroupCaps(virNodeDevCapPCIDev *pci_dev) { size_t i; int tmpGroup; virPCIDeviceAddress addr = { 0 }; /* this could be a refresh, so clear out the old data */ for (i = 0; i < pci_dev->nIommuGroupDevices; i++) VIR_FREE(pci_dev->iommuGroupDevices[i]); VIR_FREE(pci_dev->iommuGroupDevices); pci_dev->nIommuGroupDevices = 0; pci_dev->iommuGroupNumber = 0; addr.domain = pci_dev->domain; addr.bus = pci_dev->bus; addr.slot = pci_dev->slot; addr.function = pci_dev->function; tmpGroup = virPCIDeviceAddressGetIOMMUGroupNum(&addr); if (tmpGroup == -1) { /* error was already reported */ return -1; } if (tmpGroup == -2) /* -2 return means there is no iommu_group data */ return 0; if (tmpGroup >= 0) { if (virPCIDeviceAddressGetIOMMUGroupAddresses(&addr, &pci_dev->iommuGroupDevices, &pci_dev->nIommuGroupDevices) < 0) return -1; pci_dev->iommuGroupNumber = tmpGroup; } return 0; } static int virNodeDeviceGetMdevTypesCaps(const char *sysfspath, virMediatedDeviceType ***mdev_types, size_t *nmdev_types) { virMediatedDeviceType **types = NULL; size_t ntypes = 0; size_t i; /* this could be a refresh, so clear out the old data */ for (i = 0; i < *nmdev_types; i++) virMediatedDeviceTypeFree((*mdev_types)[i]); VIR_FREE(*mdev_types); *nmdev_types = 0; if (virMediatedDeviceGetMdevTypes(sysfspath, &types, &ntypes) < 0) return -1; *mdev_types = g_steal_pointer(&types); *nmdev_types = ntypes; return 0; } /** * virNodeDeviceGetPCIVPDDynamicCap: * @devCapPCIDev: a virNodeDevCapPCIDev for which to add VPD resources. * * While VPD has a read-only portion, there may be a read-write portion per * the specs which may change dynamically. * * Returns: 0 if the operation was successful (even if VPD is not present for * that device since it is optional in the specs, -1 otherwise. */ static int virNodeDeviceGetPCIVPDDynamicCap(virNodeDevCapPCIDev *devCapPCIDev) { g_autoptr(virPCIDevice) pciDev = NULL; virPCIDeviceAddress devAddr = { 0 }; g_autoptr(virPCIVPDResource) res = NULL; devAddr.domain = devCapPCIDev->domain; devAddr.bus = devCapPCIDev->bus; devAddr.slot = devCapPCIDev->slot; devAddr.function = devCapPCIDev->function; if (!(pciDev = virPCIDeviceNew(&devAddr))) return -1; if (virPCIDeviceHasVPD(pciDev)) { /* VPD is optional in PCI(e) specs. If it is there, attempt to add it. */ if ((res = virPCIDeviceGetVPD(pciDev))) { devCapPCIDev->flags |= VIR_NODE_DEV_CAP_FLAG_PCI_VPD; devCapPCIDev->vpd = g_steal_pointer(&res); } else { virPCIVPDResourceFree(g_steal_pointer(&devCapPCIDev->vpd)); } } return 0; } /* virNodeDeviceGetPCIDynamicCaps() get info that is stored in sysfs * about devices related to this device, i.e. things that can change * without this device itself changing. These must be refreshed * anytime full XML of the device is requested, because they can * change with no corresponding notification from the kernel/udev. */ int virNodeDeviceGetPCIDynamicCaps(const char *sysfsPath, virNodeDevCapPCIDev *pci_dev) { if (virNodeDeviceGetPCISRIOVCaps(sysfsPath, pci_dev) < 0 || virNodeDeviceGetPCIIOMMUGroupCaps(pci_dev) < 0) return -1; pci_dev->flags &= ~VIR_NODE_DEV_CAP_FLAG_PCI_MDEV; if (virNodeDeviceGetMdevTypesCaps(sysfsPath, &pci_dev->mdev_types, &pci_dev->nmdev_types) < 0) return -1; if (pci_dev->nmdev_types > 0) pci_dev->flags |= VIR_NODE_DEV_CAP_FLAG_PCI_MDEV; if (virNodeDeviceGetPCIVPDDynamicCap(pci_dev) < 0) return -1; return 0; } /* virNodeDeviceGetCSSDynamicCaps() get info that is stored in sysfs * about devices related to this device, i.e. things that can change * without this device itself changing. These must be refreshed * anytime full XML of the device is requested, because they can * change with no corresponding notification from the kernel/udev. */ int virNodeDeviceGetCSSDynamicCaps(const char *sysfsPath, virNodeDevCapCCW *ccw_dev) { ccw_dev->flags &= ~VIR_NODE_DEV_CAP_FLAG_CSS_MDEV; if (virNodeDeviceGetMdevTypesCaps(sysfsPath, &ccw_dev->mdev_types, &ccw_dev->nmdev_types) < 0) return -1; if (ccw_dev->nmdev_types > 0) ccw_dev->flags |= VIR_NODE_DEV_CAP_FLAG_CSS_MDEV; return 0; } /* virNodeDeviceGetAPMatrixDynamicCaps() get info that is stored in sysfs * about devices related to this device, i.e. things that can change * without this device itself changing. These must be refreshed * anytime full XML of the device is requested, because they can * change with no corresponding notification from the kernel/udev. */ int virNodeDeviceGetAPMatrixDynamicCaps(const char *sysfsPath, virNodeDevCapAPMatrix *ap_matrix) { ap_matrix->flags &= ~VIR_NODE_DEV_CAP_FLAG_AP_MATRIX_MDEV; if (virNodeDeviceGetMdevTypesCaps(sysfsPath, &ap_matrix->mdev_types, &ap_matrix->nmdev_types) < 0) return -1; if (ap_matrix->nmdev_types > 0) ap_matrix->flags |= VIR_NODE_DEV_CAP_FLAG_AP_MATRIX_MDEV; return 0; } /* virNodeDeviceGetMdevParentDynamicCaps() get info that is stored in sysfs * about devices related to this device, i.e. things that can change * without this device itself changing. These must be refreshed * anytime full XML of the device is requested, because they can * change with no corresponding notification from the kernel/udev. */ int virNodeDeviceGetMdevParentDynamicCaps(const char *sysfsPath, virNodeDevCapMdevParent *mdev_parent) { if (virNodeDeviceGetMdevTypesCaps(sysfsPath, &mdev_parent->mdev_types, &mdev_parent->nmdev_types) < 0) return -1; return 0; } #else int virNodeDeviceGetSCSIHostCaps(virNodeDevCapSCSIHost *scsi_host G_GNUC_UNUSED) { return -1; } int virNodeDeviceGetPCIDynamicCaps(const char *sysfsPath G_GNUC_UNUSED, virNodeDevCapPCIDev *pci_dev G_GNUC_UNUSED) { return -1; } int virNodeDeviceGetSCSITargetCaps(const char *sysfsPath G_GNUC_UNUSED, virNodeDevCapSCSITarget *scsi_target G_GNUC_UNUSED) { return -1; } int virNodeDeviceGetCSSDynamicCaps(const char *sysfsPath G_GNUC_UNUSED, virNodeDevCapCCW *ccw_dev G_GNUC_UNUSED) { return -1; } int virNodeDeviceGetAPMatrixDynamicCaps(const char *sysfsPath G_GNUC_UNUSED, virNodeDevCapAPMatrix *ap_matrix G_GNUC_UNUSED) { return -1; } int virNodeDeviceGetMdevParentDynamicCaps(const char *sysfsPath G_GNUC_UNUSED, virNodeDevCapMdevParent *mdev_parent G_GNUC_UNUSED) { return -1; } #endif /* __linux__ */