/*
* 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 "datatypes.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"
#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",
);
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(virNodeDeviceDefPtr def)
{
virNodeDevCapsDefPtr caps;
if (!def)
return;
VIR_FREE(def->name);
VIR_FREE(def->parent);
VIR_FREE(def->parent_wwnn);
VIR_FREE(def->parent_wwpn);
VIR_FREE(def->parent_fabric_wwn);
VIR_FREE(def->driver);
VIR_FREE(def->sysfs_path);
VIR_FREE(def->parent_sysfs_path);
VIR_FREE(def->devnode);
g_strfreev(def->devlinks);
caps = def->caps;
while (caps) {
virNodeDevCapsDefPtr next = caps->next;
virNodeDevCapsDefFree(caps);
caps = next;
}
VIR_FREE(def);
}
static void
virPCIELinkFormat(virBufferPtr buf,
virPCIELinkPtr 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(virBufferPtr buf,
virPCIEDeviceInfoPtr 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(virBufferPtr 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(virBufferPtr buf,
virMediatedDeviceTypePtr *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++) {
virMediatedDeviceTypePtr 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
virNodeDeviceCapPCIDefFormat(virBufferPtr 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, "%d\n", data->pci_dev.bus);
virBufferAsprintf(buf, "%d\n",
data->pci_dev.slot);
virBufferAsprintf(buf, "%d\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.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(virBufferPtr 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(virBufferPtr 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(virBufferPtr 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(virBufferPtr 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(virBufferPtr 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(virBufferPtr 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(virBufferPtr buf,
const virNodeDevCapData *data)
{
size_t i;
virBufferEscapeString(buf, "\n", data->mdev.type);
virBufferAsprintf(buf, "\n",
data->mdev.iommuGroupNumber);
for (i = 0; i < data->mdev.nattributes; i++) {
virMediatedDeviceAttrPtr attr = data->mdev.attributes[i];
virBufferAsprintf(buf, "\n",
attr->name, attr->value);
}
}
static void
virNodeDeviceCapVDPADefFormat(virBufferPtr buf,
const virNodeDevCapData *data)
{
virBufferEscapeString(buf, "%s\n", data->vdpa.chardev);
}
static void
virNodeDeviceCapCCWDefFormat(virBufferPtr 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);
if (data->ccw_dev.flags & VIR_NODE_DEV_CAP_FLAG_CSS_MDEV)
virNodeDeviceCapMdevTypesFormat(buf,
data->ccw_dev.mdev_types,
data->ccw_dev.nmdev_types);
}
char *
virNodeDeviceDefFormat(const virNodeDeviceDef *def)
{
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
virNodeDevCapsDefPtr 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) {
virNodeDevCapDataPtr 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:
case VIR_NODE_DEV_CAP_CSS_DEV:
virNodeDeviceCapCCWDefFormat(&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);
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_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,
virNodeDeviceDefPtr 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
virNodeDevCapsDefParseULong(const char *xpath,
xmlXPathContextPtr ctxt,
unsigned *value,
virNodeDeviceDefPtr def,
const char *missing_error_fmt,
const char *invalid_error_fmt)
{
int ret;
unsigned long val;
ret = virXPathULong(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
virNodeDevCapsDefParseULongLong(const char *xpath,
xmlXPathContextPtr ctxt,
unsigned long long *value,
virNodeDeviceDefPtr 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,
virNodeDeviceDefPtr def,
xmlNodePtr node,
virNodeDevCapDRMPtr drm)
{
VIR_XPATH_NODE_AUTORESTORE(ctxt)
int ret = -1, val;
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);
goto out;
}
drm->type = val;
ret = 0;
out:
VIR_FREE(type);
return ret;
}
static int
virNodeDevCapMdevTypesParseXML(xmlXPathContextPtr ctxt,
virMediatedDeviceTypePtr **mdev_types,
size_t *nmdev_types)
{
int ret = -1;
xmlNodePtr orignode = NULL;
xmlNodePtr *nodes = NULL;
int ntypes = -1;
virMediatedDeviceTypePtr 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("number(./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);
if (VIR_APPEND_ELEMENT(*mdev_types,
*nmdev_types, type) < 0)
goto cleanup;
}
ret = 0;
cleanup:
VIR_FREE(nodes);
virMediatedDeviceTypeFree(type);
ctxt->node = orignode;
return ret;
}
static int
virNodeDevAPMatrixCapabilityParseXML(xmlXPathContextPtr ctxt,
xmlNodePtr node,
virNodeDevCapAPMatrixPtr 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
virNodeDevCSSCapabilityParseXML(xmlXPathContextPtr ctxt,
xmlNodePtr node,
virNodeDevCapCCWPtr 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
virNodeDevCapCCWParseXML(xmlXPathContextPtr ctxt,
virNodeDeviceDefPtr def,
xmlNodePtr node,
virNodeDevCapCCWPtr ccw_dev)
{
VIR_XPATH_NODE_AUTORESTORE(ctxt)
g_autofree xmlNodePtr *nodes = NULL;
int n = 0;
size_t i = 0;
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'"), def->name);
return -1;
}
if (virStrToLong_uip(cssid, NULL, 0, &ccw_dev->cssid) < 0) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("invalid cssid value '%s' for '%s'"),
cssid, def->name);
return -1;
}
if (!(ssid = virXPathString("string(./ssid[1])", ctxt))) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("missing ssid value for '%s'"), def->name);
return -1;
}
if (virStrToLong_uip(ssid, NULL, 0, &ccw_dev->ssid) < 0) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("invalid ssid value '%s' for '%s'"),
cssid, def->name);
return -1;
}
if (!(devno = virXPathString("string(./devno[1])", ctxt))) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("missing devno value for '%s'"), def->name);
return -1;
}
if (virStrToLong_uip(devno, NULL, 16, &ccw_dev->devno) < 0) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("invalid devno value '%s' for '%s'"),
devno, def->name);
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;
}
return 0;
}
static int
virNodeDevCapAPAdapterParseXML(xmlXPathContextPtr ctxt,
virNodeDeviceDefPtr 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,
virNodeDeviceDefPtr def,
xmlNodePtr node,
virNodeDevCapAPCardPtr ap_card)
{
VIR_XPATH_NODE_AUTORESTORE(ctxt)
ctxt->node = node;
return virNodeDevCapAPAdapterParseXML(ctxt, def, &ap_card->ap_adapter);
}
static int
virNodeDevCapAPQueueParseXML(xmlXPathContextPtr ctxt,
virNodeDeviceDefPtr def,
xmlNodePtr node,
virNodeDevCapAPQueuePtr 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,
virNodeDeviceDefPtr def G_GNUC_UNUSED,
xmlNodePtr node,
virNodeDevCapAPMatrixPtr 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,
virNodeDeviceDefPtr def,
xmlNodePtr node,
virNodeDevCapStoragePtr storage)
{
VIR_XPATH_NODE_AUTORESTORE(ctxt)
xmlNodePtr *nodes = NULL;
size_t i;
int n, ret = -1;
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);
goto out;
}
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)
goto out;
for (i = 0; i < n; i++) {
char *type = virXMLPropString(nodes[i], "type");
if (!type) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("missing storage capability type for '%s'"),
def->name);
goto out;
}
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("number(./media_size[1])", ctxt, &val, def,
_("no removable media size supplied for '%s'"),
_("invalid removable media size supplied for '%s'")) < 0) {
VIR_FREE(type);
goto out;
}
storage->removable_media_size = val;
ctxt->node = orignode2;
} else {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unknown storage capability type '%s' for '%s'"),
type, def->name);
VIR_FREE(type);
goto out;
}
VIR_FREE(type);
}
if (!(storage->flags & VIR_NODE_DEV_CAP_STORAGE_REMOVABLE)) {
val = 0;
if (virNodeDevCapsDefParseULongLong("number(./size[1])", ctxt, &val, def,
_("no size supplied for '%s'"),
_("invalid size supplied for '%s'")) < 0)
goto out;
storage->size = val;
}
ret = 0;
out:
VIR_FREE(nodes);
return ret;
}
static int
virNodeDevCapSCSIParseXML(xmlXPathContextPtr ctxt,
virNodeDeviceDefPtr def,
xmlNodePtr node,
virNodeDevCapSCSIPtr scsi)
{
VIR_XPATH_NODE_AUTORESTORE(ctxt)
int ret = -1;
ctxt->node = node;
if (virNodeDevCapsDefParseULong("number(./host[1])", ctxt,
&scsi->host, def,
_("no SCSI host ID supplied for '%s'"),
_("invalid SCSI host ID supplied for '%s'")) < 0)
goto out;
if (virNodeDevCapsDefParseULong("number(./bus[1])", ctxt,
&scsi->bus, def,
_("no SCSI bus ID supplied for '%s'"),
_("invalid SCSI bus ID supplied for '%s'")) < 0)
goto out;
if (virNodeDevCapsDefParseULong("number(./target[1])", ctxt,
&scsi->target, def,
_("no SCSI target ID supplied for '%s'"),
_("invalid SCSI target ID supplied for '%s'")) < 0)
goto out;
if (virNodeDevCapsDefParseULong("number(./lun[1])", ctxt,
&scsi->lun, def,
_("no SCSI LUN ID supplied for '%s'"),
_("invalid SCSI LUN ID supplied for '%s'")) < 0)
goto out;
scsi->type = virXPathString("string(./type[1])", ctxt);
ret = 0;
out:
return ret;
}
static int
virNodeDevCapSCSITargetParseXML(xmlXPathContextPtr ctxt,
virNodeDeviceDefPtr def,
xmlNodePtr node,
virNodeDevCapSCSITargetPtr scsi_target)
{
VIR_XPATH_NODE_AUTORESTORE(ctxt)
xmlNodePtr *nodes = NULL;
int ret = -1, n = 0;
size_t i;
char *type = NULL;
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);
goto out;
}
if ((n = virXPathNodeSet("./capability", ctxt, &nodes)) < 0)
goto out;
for (i = 0; i < n; ++i) {
type = virXMLPropString(nodes[i], "type");
if (!type) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("missing type for SCSI target capability for '%s'"),
def->name);
goto out;
}
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);
goto out;
}
if (virNodeDevCapsDefParseString("string(./wwpn[1])",
ctxt, &scsi_target->wwpn) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("missing wwpn identifier for '%s'"),
def->name);
goto out;
}
} else {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unknown SCSI target capability type '%s' for '%s'"),
type, def->name);
goto out;
}
VIR_FREE(type);
}
ret = 0;
out:
VIR_FREE(type);
VIR_FREE(nodes);
return ret;
}
static int
virNodeDevCapSCSIHostParseXML(xmlXPathContextPtr ctxt,
virNodeDeviceDefPtr def,
xmlNodePtr node,
virNodeDevCapSCSIHostPtr scsi_host,
int create,
const char *virt_type)
{
VIR_XPATH_NODE_AUTORESTORE(ctxt)
xmlNodePtr *nodes = NULL;
int ret = -1, n = 0;
size_t i;
char *type = NULL;
ctxt->node = node;
if (create == EXISTING_DEVICE) {
if (virNodeDevCapsDefParseULong("number(./host[1])", ctxt,
&scsi_host->host, def,
_("no SCSI host ID supplied for '%s'"),
_("invalid SCSI host ID supplied for '%s'")) < 0) {
goto out;
}
/* 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) {
goto out;
}
}
if ((n = virXPathNodeSet("./capability", ctxt, &nodes)) < 0)
goto out;
for (i = 0; i < n; i++) {
type = virXMLPropString(nodes[i], "type");
if (!type) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("missing SCSI host capability type for '%s'"),
def->name);
goto out;
}
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);
goto out;
}
}
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);
goto out;
}
}
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);
goto out;
}
VIR_FREE(type);
}
ret = 0;
out:
VIR_FREE(type);
VIR_FREE(nodes);
return ret;
}
static int
virNodeDevCapNetParseXML(xmlXPathContextPtr ctxt,
virNodeDeviceDefPtr def,
xmlNodePtr node,
virNodeDevCapNetPtr net)
{
VIR_XPATH_NODE_AUTORESTORE(ctxt)
xmlNodePtr lnk;
size_t i = -1;
int ret = -1, n = -1;
char *tmp = NULL;
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);
goto out;
}
net->address = virXPathString("string(./address[1])", ctxt);
if ((n = virXPathNodeSet("./feature", ctxt, &nodes)) < 0)
goto out;
if (n > 0)
net->features = virBitmapNew(VIR_NET_DEV_FEAT_LAST);
for (i = 0; i < n; i++) {
int val;
if (!(tmp = virXMLPropString(nodes[i], "name"))) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("missing network device feature name"));
goto out;
}
if ((val = virNetDevFeatureTypeFromString(tmp)) < 0) {
virReportError(VIR_ERR_XML_ERROR,
_("unknown network device feature '%s'"),
tmp);
goto out;
}
ignore_value(virBitmapSetBit(net->features, val));
VIR_FREE(tmp);
}
net->subtype = VIR_NODE_DEV_CAP_NET_LAST;
tmp = virXPathString("string(./capability/@type)", ctxt);
if (tmp) {
int val = virNodeDevNetCapTypeFromString(tmp);
VIR_FREE(tmp);
if (val < 0) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("invalid network type supplied for '%s'"),
def->name);
goto out;
}
net->subtype = val;
}
lnk = virXPathNode("./link", ctxt);
if (lnk && virInterfaceLinkParseXML(lnk, &net->lnk) < 0)
goto out;
ret = 0;
out:
VIR_FREE(nodes);
VIR_FREE(tmp);
return ret;
}
static int
virNodeDevCapUSBInterfaceParseXML(xmlXPathContextPtr ctxt,
virNodeDeviceDefPtr def,
xmlNodePtr node,
virNodeDevCapUSBIfPtr usb_if)
{
VIR_XPATH_NODE_AUTORESTORE(ctxt)
int ret = -1;
ctxt->node = node;
if (virNodeDevCapsDefParseULong("number(./number[1])", ctxt,
&usb_if->number, def,
_("no USB interface number supplied for '%s'"),
_("invalid USB interface number supplied for '%s'")) < 0)
goto out;
if (virNodeDevCapsDefParseULong("number(./class[1])", ctxt,
&usb_if->klass, def,
_("no USB interface class supplied for '%s'"),
_("invalid USB interface class supplied for '%s'")) < 0)
goto out;
if (virNodeDevCapsDefParseULong("number(./subclass[1])", ctxt,
&usb_if->subclass, def,
_("no USB interface subclass supplied for '%s'"),
_("invalid USB interface subclass supplied for '%s'")) < 0)
goto out;
if (virNodeDevCapsDefParseULong("number(./protocol[1])", ctxt,
&usb_if->protocol, def,
_("no USB interface protocol supplied for '%s'"),
_("invalid USB interface protocol supplied for '%s'")) < 0)
goto out;
usb_if->description = virXPathString("string(./description[1])", ctxt);
ret = 0;
out:
return ret;
}
static int
virNodeDevCapsDefParseHexId(const char *xpath,
xmlXPathContextPtr ctxt,
unsigned *value,
virNodeDeviceDefPtr 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,
virNodeDeviceDefPtr def,
xmlNodePtr node,
virNodeDevCapUSBDevPtr usb_dev)
{
VIR_XPATH_NODE_AUTORESTORE(ctxt)
int ret = -1;
ctxt->node = node;
if (virNodeDevCapsDefParseULong("number(./bus[1])", ctxt,
&usb_dev->bus, def,
_("no USB bus number supplied for '%s'"),
_("invalid USB bus number supplied for '%s'")) < 0)
goto out;
if (virNodeDevCapsDefParseULong("number(./device[1])", ctxt,
&usb_dev->device, def,
_("no USB device number supplied for '%s'"),
_("invalid USB device number supplied for '%s'")) < 0)
goto out;
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)
goto out;
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)
goto out;
usb_dev->vendor_name = virXPathString("string(./vendor[1])", ctxt);
usb_dev->product_name = virXPathString("string(./product[1])", ctxt);
ret = 0;
out:
return ret;
}
static int
virNodeDevCapPCIDevIommuGroupParseXML(xmlXPathContextPtr ctxt,
xmlNodePtr iommuGroupNode,
virNodeDevCapPCIDevPtr pci_dev)
{
VIR_XPATH_NODE_AUTORESTORE(ctxt)
xmlNodePtr *addrNodes = NULL;
char *numberStr = NULL;
int nAddrNodes, ret = -1;
size_t i;
virPCIDeviceAddressPtr pciAddr = NULL;
ctxt->node = iommuGroupNode;
numberStr = virXMLPropString(iommuGroupNode, "number");
if (!numberStr) {
virReportError(VIR_ERR_XML_ERROR,
"%s", _("missing iommuGroup number attribute"));
goto cleanup;
}
if (virStrToLong_ui(numberStr, NULL, 10,
&pci_dev->iommuGroupNumber) < 0) {
virReportError(VIR_ERR_XML_ERROR,
_("invalid iommuGroup number attribute '%s'"),
numberStr);
goto cleanup;
}
if ((nAddrNodes = virXPathNodeSet("./address", ctxt, &addrNodes)) < 0)
goto cleanup;
for (i = 0; i < nAddrNodes; i++) {
virPCIDeviceAddress addr = {0};
if (virPCIDeviceAddressParseXML(addrNodes[i], &addr) < 0)
goto cleanup;
pciAddr = g_new0(virPCIDeviceAddress, 1);
pciAddr->domain = addr.domain;
pciAddr->bus = addr.bus;
pciAddr->slot = addr.slot;
pciAddr->function = addr.function;
if (VIR_APPEND_ELEMENT(pci_dev->iommuGroupDevices,
pci_dev->nIommuGroupDevices,
pciAddr) < 0)
goto cleanup;
}
ret = 0;
cleanup:
VIR_FREE(numberStr);
VIR_FREE(addrNodes);
VIR_FREE(pciAddr);
return ret;
}
static int
virPCIEDeviceInfoLinkParseXML(xmlXPathContextPtr ctxt,
xmlNodePtr linkNode,
virPCIELinkPtr lnk)
{
VIR_XPATH_NODE_AUTORESTORE(ctxt)
int ret = -1, speed;
char *speedStr = NULL, *portStr = NULL;
ctxt->node = linkNode;
if (virXPathUInt("number(./@width)", ctxt, &lnk->width) < 0) {
virReportError(VIR_ERR_XML_DETAIL, "%s",
_("mandatory attribute 'width' is missing or malformed"));
goto cleanup;
}
if ((speedStr = virXPathString("string(./@speed)", ctxt))) {
if ((speed = virPCIELinkSpeedTypeFromString(speedStr)) < 0) {
virReportError(VIR_ERR_XML_DETAIL,
_("malformed 'speed' attribute: %s"),
speedStr);
goto cleanup;
}
lnk->speed = speed;
}
if ((portStr = virXPathString("string(./@port)", ctxt))) {
if (virStrToLong_i(portStr, NULL, 10, &lnk->port) < 0) {
virReportError(VIR_ERR_XML_DETAIL,
_("malformed 'port' attribute: %s"),
portStr);
goto cleanup;
}
} else {
lnk->port = -1;
}
ret = 0;
cleanup:
VIR_FREE(portStr);
VIR_FREE(speedStr);
return ret;
}
static int
virPCIEDeviceInfoParseXML(xmlXPathContextPtr ctxt,
xmlNodePtr pciExpressNode,
virPCIEDeviceInfoPtr pci_express)
{
VIR_XPATH_NODE_AUTORESTORE(ctxt)
xmlNodePtr lnk;
int ret = -1;
ctxt->node = pciExpressNode;
if ((lnk = virXPathNode("./link[@validity='cap']", ctxt))) {
pci_express->link_cap = g_new0(virPCIELink, 1);
if (virPCIEDeviceInfoLinkParseXML(ctxt, lnk,
pci_express->link_cap) < 0)
goto cleanup;
}
if ((lnk = virXPathNode("./link[@validity='sta']", ctxt))) {
pci_express->link_sta = g_new0(virPCIELink, 1);
if (virPCIEDeviceInfoLinkParseXML(ctxt, lnk,
pci_express->link_sta) < 0)
goto cleanup;
}
ret = 0;
cleanup:
return ret;
}
static int
virNodeDevPCICapSRIOVPhysicalParseXML(xmlXPathContextPtr ctxt,
virNodeDevCapPCIDevPtr 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,
virNodeDevCapPCIDevPtr pci_dev)
{
int ret = -1;
xmlNodePtr *addresses = NULL;
int naddresses = virXPathNodeSet("./address", ctxt, &addresses);
char *maxFuncsStr = virXPathString("string(./@maxCount)", ctxt);
size_t i;
if (naddresses < 0)
goto cleanup;
if (maxFuncsStr &&
virStrToLong_uip(maxFuncsStr, NULL, 10,
&pci_dev->max_virtual_functions) < 0) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("Malformed 'maxCount' parameter"));
goto cleanup;
}
pci_dev->virtual_functions = g_new0(virPCIDeviceAddressPtr, naddresses);
for (i = 0; i < naddresses; i++) {
g_autoptr(virPCIDeviceAddress) addr = NULL;
addr = g_new0(virPCIDeviceAddress, 1);
if (virPCIDeviceAddressParseXML(addresses[i], addr) < 0)
goto cleanup;
if (VIR_APPEND_ELEMENT(pci_dev->virtual_functions,
pci_dev->num_virtual_functions,
addr) < 0)
goto cleanup;
}
pci_dev->flags |= VIR_NODE_DEV_CAP_FLAG_PCI_VIRTUAL_FUNCTION;
ret = 0;
cleanup:
VIR_FREE(addresses);
VIR_FREE(maxFuncsStr);
return ret;
}
static int
virNodeDevPCICapabilityParseXML(xmlXPathContextPtr ctxt,
xmlNodePtr node,
virNodeDevCapPCIDevPtr pci_dev)
{
char *type = virXMLPropString(node, "type");
VIR_XPATH_NODE_AUTORESTORE(ctxt)
int ret = -1;
ctxt->node = node;
if (!type) {
virReportError(VIR_ERR_XML_ERROR, "%s", _("Missing capability type"));
goto cleanup;
}
if (STREQ(type, "phys_function") &&
virNodeDevPCICapSRIOVPhysicalParseXML(ctxt, pci_dev) < 0) {
goto cleanup;
} else if (STREQ(type, "virt_functions") &&
virNodeDevPCICapSRIOVVirtualParseXML(ctxt, pci_dev) < 0) {
goto cleanup;
} else if (STREQ(type, "mdev_types")) {
if (virNodeDevCapMdevTypesParseXML(ctxt,
&pci_dev->mdev_types,
&pci_dev->nmdev_types) < 0)
goto cleanup;
pci_dev->flags |= VIR_NODE_DEV_CAP_FLAG_PCI_MDEV;
} else {
int hdrType = virPCIHeaderTypeFromString(type);
if (hdrType > 0 && !pci_dev->hdrType)
pci_dev->hdrType = hdrType;
}
ret = 0;
cleanup:
VIR_FREE(type);
return ret;
}
static int
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;
}
static int
virNodeDevCapSystemParseXML(xmlXPathContextPtr ctxt,
virNodeDeviceDefPtr def,
xmlNodePtr node,
virNodeDevCapSystemPtr syscap)
{
virNodeDevCapSystemHardwarePtr hardware = &syscap->hardware;
virNodeDevCapSystemFirmwarePtr firmware = &syscap->firmware;
VIR_XPATH_NODE_AUTORESTORE(ctxt)
int ret = -1;
char *tmp;
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);
goto out;
}
if (virUUIDParse(tmp, hardware->uuid) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("malformed uuid element for '%s'"), def->name);
VIR_FREE(tmp);
goto out;
}
VIR_FREE(tmp);
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);
ret = 0;
out:
return ret;
}
static int
virNodeDevCapMdevAttributeParseXML(xmlXPathContextPtr ctxt,
xmlNodePtr node,
virNodeDevCapMdevPtr 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;
}
return VIR_APPEND_ELEMENT(mdev->attributes,
mdev->nattributes,
attr);
}
static int
virNodeDevCapMdevParseXML(xmlXPathContextPtr ctxt,
virNodeDeviceDefPtr def,
xmlNodePtr node,
virNodeDevCapMdevPtr mdev)
{
VIR_XPATH_NODE_AUTORESTORE(ctxt)
int ret = -1;
int nattrs = 0;
g_autofree xmlNodePtr *attrs = NULL;
size_t i;
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);
goto out;
}
/* 'iommuGroup' is optional, only report an error if the supplied value is
* invalid (-2), not if it's missing (-1) */
if (virXPathUInt("number(./iommuGroup[1]/@number)",
ctxt, &mdev->iommuGroupNumber) < -1) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("invalid iommuGroup number attribute for '%s'"),
def->name);
goto out;
}
if ((nattrs = virXPathNodeSet("./attr", ctxt, &attrs)) < 0)
goto out;
for (i = 0; i < nattrs; i++)
virNodeDevCapMdevAttributeParseXML(ctxt, attrs[i], mdev);
ret = 0;
out:
return ret;
}
static virNodeDevCapsDefPtr
virNodeDevCapsDefParseXML(xmlXPathContextPtr ctxt,
virNodeDeviceDefPtr def,
xmlNodePtr node,
int create,
const char *virt_type)
{
virNodeDevCapsDefPtr caps;
char *tmp;
int val, ret = -1;
caps = g_new0(virNodeDevCapsDef, 1);
tmp = virXMLPropString(node, "type");
if (!tmp) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("missing capability type"));
goto error;
}
if ((val = virNodeDevCapTypeFromString(tmp)) < 0) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("unknown capability type '%s'"), tmp);
VIR_FREE(tmp);
goto error;
}
caps->data.type = val;
VIR_FREE(tmp);
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:
case VIR_NODE_DEV_CAP_CSS_DEV:
ret = virNodeDevCapCCWParseXML(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_LAST:
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unknown capability type '%d' for '%s'"),
caps->data.type, def->name);
ret = -1;
break;
}
if (ret < 0)
goto error;
return caps;
error:
virNodeDevCapsDefFree(caps);
return NULL;
}
static virNodeDeviceDefPtr
virNodeDeviceDefParseXML(xmlXPathContextPtr ctxt,
int create,
const char *virt_type)
{
virNodeDeviceDefPtr def;
virNodeDevCapsDefPtr *next_cap;
xmlNodePtr *nodes = NULL;
int n, m;
size_t i;
def = g_new0(virNodeDeviceDef, 1);
/* Extract device name */
if (create == EXISTING_DEVICE) {
def->name = virXPathString("string(./name[1])", ctxt);
if (!def->name) {
virReportError(VIR_ERR_NO_NAME, NULL);
goto error;
}
} else {
def->name = g_strdup("new device");
}
def->sysfs_path = virXPathString("string(./path[1])", ctxt);
/* Parse devnodes */
if ((n = virXPathNodeSet("./devnode", ctxt, &nodes)) < 0)
goto error;
def->devlinks = g_new0(char *, n + 1);
for (i = 0, m = 0; i < n; i++) {
xmlNodePtr node = nodes[i];
char *tmp = virXMLPropString(node, "type");
int val;
if (!tmp) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("missing devnode type"));
goto error;
}
val = virNodeDevDevnodeTypeFromString(tmp);
if (val < 0) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("unknown devnode type '%s'"), tmp);
VIR_FREE(tmp);
goto error;
}
VIR_FREE(tmp);
switch ((virNodeDevDevnodeType)val) {
case VIR_NODE_DEV_DEVNODE_DEV:
if (!(def->devnode = virXMLNodeContentString(node)))
goto error;
break;
case VIR_NODE_DEV_DEVNODE_LINK:
if (!(def->devlinks[m++] = virXMLNodeContentString(node)))
goto error;
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);
goto error;
}
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);
goto error;
}
def->parent_fabric_wwn = virXPathString("string(./parent[1]/@fabric_wwn)",
ctxt);
/* Parse device capabilities */
VIR_FREE(nodes);
if ((n = virXPathNodeSet("./capability", ctxt, &nodes)) < 0)
goto error;
if (n == 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("no device capabilities for '%s'"),
def->name);
goto error;
}
next_cap = &def->caps;
for (i = 0; i < n; i++) {
*next_cap = virNodeDevCapsDefParseXML(ctxt, def,
nodes[i],
create,
virt_type);
if (!*next_cap)
goto error;
next_cap = &(*next_cap)->next;
}
VIR_FREE(nodes);
return def;
error:
virNodeDeviceDefFree(def);
VIR_FREE(nodes);
return NULL;
}
virNodeDeviceDefPtr
virNodeDeviceDefParseNode(xmlDocPtr xml,
xmlNodePtr root,
int create,
const char *virt_type)
{
g_autoptr(xmlXPathContext) ctxt = NULL;
if (!virXMLNodeNameEqual(root, "device")) {
virReportError(VIR_ERR_XML_ERROR,
_("unexpected root element <%s> "
"expecting "),
root->name);
return NULL;
}
if (!(ctxt = virXMLXPathContextNew(xml)))
return NULL;
ctxt->node = root;
return virNodeDeviceDefParseXML(ctxt, create, virt_type);
}
static virNodeDeviceDefPtr
virNodeDeviceDefParse(const char *str,
const char *filename,
int create,
const char *virt_type)
{
xmlDocPtr xml;
virNodeDeviceDefPtr def = NULL;
if ((xml = virXMLParse(filename, str, _("(node_device_definition)")))) {
def = virNodeDeviceDefParseNode(xml, xmlDocGetRootElement(xml),
create, virt_type);
xmlFreeDoc(xml);
}
return def;
}
virNodeDeviceDefPtr
virNodeDeviceDefParseString(const char *str,
int create,
const char *virt_type)
{
return virNodeDeviceDefParse(str, NULL, create, virt_type);
}
virNodeDeviceDefPtr
virNodeDeviceDefParseFile(const char *filename,
int create,
const char *virt_type)
{
return virNodeDeviceDefParse(NULL, filename, create, virt_type);
}
/*
* Return fc_host dev's WWNN and WWPN
*/
int
virNodeDeviceGetWWNs(virNodeDeviceDefPtr def,
char **wwnn,
char **wwpn)
{
virNodeDevCapsDefPtr 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(virNodeDevCapsDefPtr caps)
{
size_t i = 0;
virNodeDevCapDataPtr data = &caps->data;
switch (caps->data.type) {
case VIR_NODE_DEV_CAP_SYSTEM:
VIR_FREE(data->system.product_name);
VIR_FREE(data->system.hardware.vendor_name);
VIR_FREE(data->system.hardware.version);
VIR_FREE(data->system.hardware.serial);
VIR_FREE(data->system.firmware.vendor_name);
VIR_FREE(data->system.firmware.version);
VIR_FREE(data->system.firmware.release_date);
break;
case VIR_NODE_DEV_CAP_PCI_DEV:
VIR_FREE(data->pci_dev.product_name);
VIR_FREE(data->pci_dev.vendor_name);
VIR_FREE(data->pci_dev.physical_function);
for (i = 0; i < data->pci_dev.num_virtual_functions; i++)
VIR_FREE(data->pci_dev.virtual_functions[i]);
VIR_FREE(data->pci_dev.virtual_functions);
for (i = 0; i < data->pci_dev.nIommuGroupDevices; i++)
VIR_FREE(data->pci_dev.iommuGroupDevices[i]);
VIR_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]);
VIR_FREE(data->pci_dev.mdev_types);
break;
case VIR_NODE_DEV_CAP_USB_DEV:
VIR_FREE(data->usb_dev.product_name);
VIR_FREE(data->usb_dev.vendor_name);
break;
case VIR_NODE_DEV_CAP_USB_INTERFACE:
VIR_FREE(data->usb_if.description);
break;
case VIR_NODE_DEV_CAP_NET:
VIR_FREE(data->net.ifname);
VIR_FREE(data->net.address);
virBitmapFree(data->net.features);
data->net.features = NULL;
break;
case VIR_NODE_DEV_CAP_SCSI_HOST:
VIR_FREE(data->scsi_host.wwnn);
VIR_FREE(data->scsi_host.wwpn);
VIR_FREE(data->scsi_host.fabric_wwn);
break;
case VIR_NODE_DEV_CAP_SCSI_TARGET:
VIR_FREE(data->scsi_target.name);
VIR_FREE(data->scsi_target.rport);
VIR_FREE(data->scsi_target.wwpn);
break;
case VIR_NODE_DEV_CAP_SCSI:
VIR_FREE(data->scsi.type);
break;
case VIR_NODE_DEV_CAP_STORAGE:
VIR_FREE(data->storage.block);
VIR_FREE(data->storage.bus);
VIR_FREE(data->storage.drive_type);
VIR_FREE(data->storage.model);
VIR_FREE(data->storage.vendor);
VIR_FREE(data->storage.serial);
VIR_FREE(data->storage.media_label);
break;
case VIR_NODE_DEV_CAP_SCSI_GENERIC:
VIR_FREE(data->sg.path);
break;
case VIR_NODE_DEV_CAP_MDEV:
VIR_FREE(data->mdev.type);
VIR_FREE(data->mdev.uuid);
for (i = 0; i < data->mdev.nattributes; i++)
virMediatedDeviceAttrFree(data->mdev.attributes[i]);
VIR_FREE(data->mdev.attributes);
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]);
VIR_FREE(data->ccw_dev.mdev_types);
break;
case VIR_NODE_DEV_CAP_AP_MATRIX:
VIR_FREE(data->ap_matrix.addr);
for (i = 0; i < data->ap_matrix.nmdev_types; i++)
virMediatedDeviceTypeFree(data->ap_matrix.mdev_types[i]);
VIR_FREE(data->ap_matrix.mdev_types);
break;
case VIR_NODE_DEV_CAP_MDEV_TYPES:
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_LAST:
/* This case is here to shutup the compiler */
break;
}
VIR_FREE(caps);
}
int
virNodeDeviceUpdateCaps(virNodeDeviceDefPtr def)
{
virNodeDevCapsDefPtr 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;
/* 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_TYPES:
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_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(virNodeDeviceDefPtr def,
virNodeDevCapType **list)
{
virNodeDevCapsDefPtr caps = NULL;
virNodeDevCapType *tmp = NULL;
bool want_list = !!list;
int ncaps = 0;
int ret = -1;
#define MAYBE_ADD_CAP(cap) \
do { \
if (want_list) \
tmp[ncaps] = cap; \
} while (0)
if (virNodeDeviceUpdateCaps(def) < 0)
goto cleanup;
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 (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);
ret = ncaps;
cleanup:
VIR_FREE(tmp);
return ret;
}
#ifdef __linux__
int
virNodeDeviceGetSCSIHostCaps(virNodeDevCapSCSIHostPtr scsi_host)
{
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;
}
VIR_FREE(tmp);
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);
}
VIR_FREE(tmp);
return ret;
}
int
virNodeDeviceGetSCSITargetCaps(const char *sysfsPath,
virNodeDevCapSCSITargetPtr scsi_target)
{
int ret = -1;
char *dir = NULL, *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;
}
VIR_FREE(rport);
VIR_FREE(dir);
return ret;
}
static int
virNodeDeviceGetPCISRIOVCaps(const char *sysfsPath,
virNodeDevCapPCIDevPtr pci_dev)
{
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;
ret = virPCIGetVirtualFunctions(sysfsPath, &pci_dev->virtual_functions,
&pci_dev->num_virtual_functions,
&pci_dev->max_virtual_functions);
if (ret < 0)
return ret;
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(virNodeDevCapPCIDevPtr pci_dev)
{
size_t i;
int tmpGroup;
virPCIDeviceAddress addr;
/* 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,
virMediatedDeviceTypePtr **mdev_types,
size_t *nmdev_types)
{
virMediatedDeviceTypePtr *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;
}
/* 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,
virNodeDevCapPCIDevPtr 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;
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,
virNodeDevCapCCWPtr 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,
virNodeDevCapAPMatrixPtr 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;
}
#else
int
virNodeDeviceGetSCSIHostCaps(virNodeDevCapSCSIHostPtr scsi_host G_GNUC_UNUSED)
{
return -1;
}
int
virNodeDeviceGetPCIDynamicCaps(const char *sysfsPath G_GNUC_UNUSED,
virNodeDevCapPCIDevPtr pci_dev G_GNUC_UNUSED)
{
return -1;
}
int virNodeDeviceGetSCSITargetCaps(const char *sysfsPath G_GNUC_UNUSED,
virNodeDevCapSCSITargetPtr scsi_target G_GNUC_UNUSED)
{
return -1;
}
int
virNodeDeviceGetCSSDynamicCaps(const char *sysfsPath G_GNUC_UNUSED,
virNodeDevCapCCWPtr ccw_dev G_GNUC_UNUSED)
{
return -1;
}
int
virNodeDeviceGetAPMatrixDynamicCaps(const char *sysfsPath G_GNUC_UNUSED,
virNodeDevCapAPMatrixPtr ap_matrix G_GNUC_UNUSED)
{
return -1;
}
#endif /* __linux__ */