mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-11-09 23:10:08 +00:00
03f9dfbb51
Commit3210c8b04
introduced a separate field 'diskbus' for internal use by the commandline formatter. Adjust the check for address equality to compare the relevant fields separately instead of the whole struct. This means that the changes of the 'diskbus' field added in commit03ed14d2d4
no longer affect the outcome of the address equality check. https://bugzilla.redhat.com/show_bug.cgi?id=2023605 Fixes:3210c8b047
Fixes:03ed14d2d4
Signed-off-by: Ján Tomko <jtomko@redhat.com> Reviewed-by: Peter Krempa <pkrempa@redhat.com>
497 lines
14 KiB
C
497 lines
14 KiB
C
/*
|
|
* device_conf.c: device XML handling
|
|
*
|
|
* Copyright (C) 2006-2016 Red Hat, Inc.
|
|
*
|
|
* 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
|
|
* <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include "virerror.h"
|
|
#include "datatypes.h"
|
|
#include "viralloc.h"
|
|
#include "virxml.h"
|
|
#include "viruuid.h"
|
|
#include "virbuffer.h"
|
|
#include "device_conf.h"
|
|
#include "domain_addr.h"
|
|
#include "virstring.h"
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_DEVICE
|
|
|
|
VIR_ENUM_IMPL(virDomainDeviceAddress,
|
|
VIR_DOMAIN_DEVICE_ADDRESS_TYPE_LAST,
|
|
"none",
|
|
"pci",
|
|
"drive",
|
|
"virtio-serial",
|
|
"ccid",
|
|
"usb",
|
|
"spapr-vio",
|
|
"virtio-s390",
|
|
"ccw",
|
|
"virtio-mmio",
|
|
"isa",
|
|
"dimm",
|
|
"unassigned",
|
|
);
|
|
|
|
static int
|
|
virZPCIDeviceAddressParseXML(xmlNodePtr node,
|
|
virPCIDeviceAddress *addr)
|
|
{
|
|
int retUid;
|
|
int retFid;
|
|
|
|
if ((retUid = virXMLPropUInt(node, "uid", 0, VIR_XML_PROP_NONE,
|
|
&addr->zpci.uid.value)) < 0)
|
|
return -1;
|
|
|
|
if (retUid > 0)
|
|
addr->zpci.uid.isSet = true;
|
|
|
|
if ((retFid = virXMLPropUInt(node, "fid", 0, VIR_XML_PROP_NONE,
|
|
&addr->zpci.fid.value)) < 0)
|
|
return -1;
|
|
|
|
if (retFid > 0)
|
|
addr->zpci.fid.isSet = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
virDomainDeviceInfoClear(virDomainDeviceInfo *info)
|
|
{
|
|
VIR_FREE(info->alias);
|
|
memset(&info->addr, 0, sizeof(info->addr));
|
|
info->type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE;
|
|
VIR_FREE(info->romfile);
|
|
VIR_FREE(info->loadparm);
|
|
info->isolationGroup = 0;
|
|
info->isolationGroupLocked = false;
|
|
}
|
|
|
|
void
|
|
virDomainDeviceInfoFree(virDomainDeviceInfo *info)
|
|
{
|
|
if (info) {
|
|
virDomainDeviceInfoClear(info);
|
|
g_free(info);
|
|
}
|
|
}
|
|
|
|
bool
|
|
virDomainDeviceInfoAddressIsEqual(const virDomainDeviceInfo *a,
|
|
const virDomainDeviceInfo *b)
|
|
{
|
|
if (a->type != b->type)
|
|
return false;
|
|
|
|
switch ((virDomainDeviceAddressType) a->type) {
|
|
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE:
|
|
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_LAST:
|
|
/* address types below don't have any specific data */
|
|
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_MMIO:
|
|
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_S390:
|
|
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_UNASSIGNED:
|
|
break;
|
|
|
|
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI:
|
|
/* the 'multi' field shouldn't be checked */
|
|
if (a->addr.pci.domain != b->addr.pci.domain ||
|
|
a->addr.pci.bus != b->addr.pci.bus ||
|
|
a->addr.pci.slot != b->addr.pci.slot ||
|
|
a->addr.pci.function != b->addr.pci.function)
|
|
return false;
|
|
break;
|
|
|
|
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE:
|
|
if (a->addr.drive.controller != b->addr.drive.controller ||
|
|
a->addr.drive.unit != b->addr.drive.unit ||
|
|
a->addr.drive.bus != b->addr.drive.bus ||
|
|
a->addr.drive.target != b->addr.drive.target)
|
|
return false;
|
|
break;
|
|
|
|
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_SERIAL:
|
|
if (memcmp(&a->addr.vioserial, &b->addr.vioserial, sizeof(a->addr.vioserial)))
|
|
return false;
|
|
break;
|
|
|
|
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCID:
|
|
if (memcmp(&a->addr.ccid, &b->addr.ccid, sizeof(a->addr.ccid)))
|
|
return false;
|
|
break;
|
|
|
|
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB:
|
|
if (memcmp(&a->addr.usb, &b->addr.usb, sizeof(a->addr.usb)))
|
|
return false;
|
|
break;
|
|
|
|
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_SPAPRVIO:
|
|
if (memcmp(&a->addr.spaprvio, &b->addr.spaprvio, sizeof(a->addr.spaprvio)))
|
|
return false;
|
|
break;
|
|
|
|
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW:
|
|
/* the 'assigned' field denotes that the address was generated */
|
|
if (a->addr.ccw.cssid != b->addr.ccw.cssid ||
|
|
a->addr.ccw.ssid != b->addr.ccw.ssid ||
|
|
a->addr.ccw.devno != b->addr.ccw.devno)
|
|
return false;
|
|
break;
|
|
|
|
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_ISA:
|
|
if (memcmp(&a->addr.isa, &b->addr.isa, sizeof(a->addr.isa)))
|
|
return false;
|
|
break;
|
|
|
|
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DIMM:
|
|
if (memcmp(&a->addr.dimm, &b->addr.dimm, sizeof(a->addr.dimm)))
|
|
return false;
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
virDeviceInfoPCIAddressIsWanted(const virDomainDeviceInfo *info)
|
|
{
|
|
return info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE ||
|
|
(info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI &&
|
|
virPCIDeviceAddressIsEmpty(&info->addr.pci));
|
|
}
|
|
|
|
bool
|
|
virDeviceInfoPCIAddressIsPresent(const virDomainDeviceInfo *info)
|
|
{
|
|
return info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI &&
|
|
!virPCIDeviceAddressIsEmpty(&info->addr.pci);
|
|
}
|
|
|
|
bool
|
|
virDeviceInfoPCIAddressExtensionIsWanted(const virDomainDeviceInfo *info)
|
|
{
|
|
return (info->addr.pci.extFlags & VIR_PCI_ADDRESS_EXTENSION_ZPCI) &&
|
|
virZPCIDeviceAddressIsIncomplete(&info->addr.pci.zpci);
|
|
}
|
|
|
|
bool
|
|
virDeviceInfoPCIAddressExtensionIsPresent(const virDomainDeviceInfo *info)
|
|
{
|
|
return (info->addr.pci.extFlags & VIR_PCI_ADDRESS_EXTENSION_ZPCI) &&
|
|
virZPCIDeviceAddressIsPresent(&info->addr.pci.zpci);
|
|
}
|
|
|
|
int
|
|
virPCIDeviceAddressParseXML(xmlNodePtr node,
|
|
virPCIDeviceAddress *addr)
|
|
{
|
|
xmlNodePtr cur;
|
|
xmlNodePtr zpci = NULL;
|
|
|
|
memset(addr, 0, sizeof(*addr));
|
|
|
|
if (virXMLPropUInt(node, "domain", 0, VIR_XML_PROP_NONE,
|
|
&addr->domain) < 0)
|
|
return -1;
|
|
|
|
if (virXMLPropUInt(node, "bus", 0, VIR_XML_PROP_NONE,
|
|
&addr->bus) < 0)
|
|
return -1;
|
|
|
|
if (virXMLPropUInt(node, "slot", 0, VIR_XML_PROP_NONE,
|
|
&addr->slot) < 0)
|
|
return -1;
|
|
|
|
if (virXMLPropUInt(node, "function", 0, VIR_XML_PROP_NONE,
|
|
&addr->function) < 0)
|
|
return -1;
|
|
|
|
if (virXMLPropTristateSwitch(node, "multifunction", VIR_XML_PROP_NONE,
|
|
&addr->multi) < 0)
|
|
return -1;
|
|
|
|
if (!virPCIDeviceAddressIsEmpty(addr) && !virPCIDeviceAddressIsValid(addr, true))
|
|
return -1;
|
|
|
|
cur = node->children;
|
|
while (cur) {
|
|
if (cur->type == XML_ELEMENT_NODE &&
|
|
virXMLNodeNameEqual(cur, "zpci")) {
|
|
zpci = cur;
|
|
}
|
|
cur = cur->next;
|
|
}
|
|
|
|
if (zpci && virZPCIDeviceAddressParseXML(zpci, addr) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
virPCIDeviceAddressFormat(virBuffer *buf,
|
|
virPCIDeviceAddress addr,
|
|
bool includeTypeInAddr)
|
|
{
|
|
virBufferAsprintf(buf, "<address %sdomain='0x%04x' bus='0x%02x' "
|
|
"slot='0x%02x' function='0x%d'/>\n",
|
|
includeTypeInAddr ? "type='pci' " : "",
|
|
addr.domain,
|
|
addr.bus,
|
|
addr.slot,
|
|
addr.function);
|
|
}
|
|
|
|
bool
|
|
virDomainDeviceCCWAddressIsValid(virDomainDeviceCCWAddress *addr)
|
|
{
|
|
return addr->cssid <= VIR_DOMAIN_DEVICE_CCW_MAX_CSSID &&
|
|
addr->ssid <= VIR_DOMAIN_DEVICE_CCW_MAX_SSID &&
|
|
addr->devno <= VIR_DOMAIN_DEVICE_CCW_MAX_DEVNO;
|
|
}
|
|
|
|
int
|
|
virDomainDeviceCCWAddressParseXML(xmlNodePtr node,
|
|
virDomainDeviceCCWAddress *addr)
|
|
{
|
|
int cssid;
|
|
int ssid;
|
|
int devno;
|
|
|
|
memset(addr, 0, sizeof(*addr));
|
|
|
|
if ((cssid = virXMLPropUInt(node, "cssid", 0, VIR_XML_PROP_NONE,
|
|
&addr->cssid)) < 0)
|
|
return -1;
|
|
|
|
if ((ssid = virXMLPropUInt(node, "ssid", 0, VIR_XML_PROP_NONE,
|
|
&addr->ssid)) < 0)
|
|
return -1;
|
|
|
|
if ((devno = virXMLPropUInt(node, "devno", 0, VIR_XML_PROP_NONE,
|
|
&addr->devno)) < 0)
|
|
return -1;
|
|
|
|
if (!virDomainDeviceCCWAddressIsValid(addr)) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Invalid specification for virtio ccw address: cssid='0x%x' ssid='0x%x' devno='0x%04x'"),
|
|
addr->cssid, addr->ssid, addr->devno);
|
|
return -1;
|
|
}
|
|
|
|
if (cssid && ssid && devno) {
|
|
addr->assigned = true;
|
|
} else if (cssid || ssid || devno) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Invalid partial specification for virtio ccw address"));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool
|
|
virDomainDeviceCCWAddressEqual(virDomainDeviceCCWAddress *addr1,
|
|
virDomainDeviceCCWAddress *addr2)
|
|
{
|
|
if (addr1->cssid == addr2->cssid &&
|
|
addr1->ssid == addr2->ssid &&
|
|
addr1->devno == addr2->devno) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int
|
|
virDomainDeviceDriveAddressParseXML(xmlNodePtr node,
|
|
virDomainDeviceDriveAddress *addr)
|
|
{
|
|
memset(addr, 0, sizeof(*addr));
|
|
|
|
if (virXMLPropUInt(node, "controller", 10, VIR_XML_PROP_NONE,
|
|
&addr->controller) < 0)
|
|
return -1;
|
|
|
|
if (virXMLPropUInt(node, "bus", 10, VIR_XML_PROP_NONE, &addr->bus) < 0)
|
|
return -1;
|
|
|
|
if (virXMLPropUInt(node, "target", 10, VIR_XML_PROP_NONE,
|
|
&addr->target) < 0)
|
|
return -1;
|
|
|
|
if (virXMLPropUInt(node, "unit", 10, VIR_XML_PROP_NONE, &addr->unit) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
virDomainDeviceVirtioSerialAddressParseXML(xmlNodePtr node,
|
|
virDomainDeviceVirtioSerialAddress *addr)
|
|
{
|
|
memset(addr, 0, sizeof(*addr));
|
|
|
|
if (virXMLPropUInt(node, "controller", 10, VIR_XML_PROP_NONE,
|
|
&addr->controller) < 0)
|
|
return -1;
|
|
|
|
if (virXMLPropUInt(node, "bus", 10, VIR_XML_PROP_NONE, &addr->bus) < 0)
|
|
return -1;
|
|
|
|
if (virXMLPropUInt(node, "port", 10, VIR_XML_PROP_NONE, &addr->port) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
virDomainDeviceCcidAddressParseXML(xmlNodePtr node,
|
|
virDomainDeviceCcidAddress *addr)
|
|
{
|
|
memset(addr, 0, sizeof(*addr));
|
|
|
|
if (virXMLPropUInt(node, "controller", 10, VIR_XML_PROP_NONE,
|
|
&addr->controller) < 0)
|
|
return -1;
|
|
|
|
if (virXMLPropUInt(node, "slot", 10, VIR_XML_PROP_NONE, &addr->slot) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
virDomainDeviceUSBAddressParsePort(virDomainDeviceUSBAddress *addr,
|
|
char *port)
|
|
{
|
|
char *tmp = port;
|
|
size_t i;
|
|
|
|
for (i = 0; i < VIR_DOMAIN_DEVICE_USB_MAX_PORT_DEPTH; i++) {
|
|
if (virStrToLong_uip(tmp, &tmp, 10, &addr->port[i]) < 0)
|
|
break;
|
|
|
|
if (*tmp == '\0')
|
|
return 0;
|
|
|
|
if (*tmp == '.')
|
|
tmp++;
|
|
}
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Cannot parse <address> 'port' attribute"));
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
virDomainDeviceUSBAddressParseXML(xmlNodePtr node,
|
|
virDomainDeviceUSBAddress *addr)
|
|
{
|
|
g_autofree char *port = virXMLPropString(node, "port");
|
|
|
|
memset(addr, 0, sizeof(*addr));
|
|
|
|
if (port && virDomainDeviceUSBAddressParsePort(addr, port) < 0)
|
|
return -1;
|
|
|
|
if (virXMLPropUInt(node, "bus", 10, VIR_XML_PROP_NONE, &addr->bus) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
virDomainDeviceSpaprVioAddressParseXML(xmlNodePtr node,
|
|
virDomainDeviceSpaprVioAddress *addr)
|
|
{
|
|
int reg;
|
|
|
|
memset(addr, 0, sizeof(*addr));
|
|
|
|
if ((reg = virXMLPropULongLong(node, "reg", 16, VIR_XML_PROP_NONE,
|
|
&addr->reg)) < 0)
|
|
return -1;
|
|
|
|
if (reg != 0)
|
|
addr->has_reg = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool
|
|
virDomainDeviceAddressIsValid(virDomainDeviceInfo *info,
|
|
int type)
|
|
{
|
|
if (info->type != type)
|
|
return false;
|
|
|
|
switch (info->type) {
|
|
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI:
|
|
return virPCIDeviceAddressIsValid(&info->addr.pci, false);
|
|
|
|
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE:
|
|
return true;
|
|
|
|
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_S390:
|
|
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_MMIO:
|
|
return true;
|
|
|
|
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW:
|
|
return virDomainDeviceCCWAddressIsValid(&info->addr.ccw);
|
|
|
|
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB:
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
int
|
|
virInterfaceLinkParseXML(xmlNodePtr node,
|
|
virNetDevIfLink *lnk)
|
|
{
|
|
if (virXMLPropEnum(node, "state", virNetDevIfStateTypeFromString,
|
|
VIR_XML_PROP_NONE, &lnk->state) < 0)
|
|
return -1;
|
|
|
|
if (virXMLPropUInt(node, "speed", 10, VIR_XML_PROP_NONE, &lnk->speed) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
virInterfaceLinkFormat(virBuffer *buf,
|
|
const virNetDevIfLink *lnk)
|
|
{
|
|
if (!lnk->speed && !lnk->state) {
|
|
/* If there's nothing to format, return early. */
|
|
return 0;
|
|
}
|
|
|
|
virBufferAddLit(buf, "<link");
|
|
if (lnk->speed)
|
|
virBufferAsprintf(buf, " speed='%u'", lnk->speed);
|
|
if (lnk->state)
|
|
virBufferAsprintf(buf, " state='%s'",
|
|
virNetDevIfStateTypeToString(lnk->state));
|
|
virBufferAddLit(buf, "/>\n");
|
|
return 0;
|
|
}
|