2012-08-16 15:41:06 +00:00
|
|
|
/*
|
|
|
|
* device_conf.c: device XML handling
|
|
|
|
*
|
conf: allow type='pci' addresses with no address attributes specified
Prior to this, <address type='pci'/> wasn't allowed when parsing
(domain+bus+slot+function needed to be a "valid" PCI address, meaning
that at least one of domain/bus/slot had to be non-0), the RNG
required bus to be specified, and if type was set to PCI when
formatting, domain+bus+slot+function would always be output.
This makes all the address attributes optional during parse and RNG
validation, and suppresses domain+bus+slot+function if domain+bus+slot
are all 0 (NB: if d+b+s are all 0, any value for function is
nonsensical as that will never happen in the real world, and after
the next patch we will always assign a real working address to any
empty PCI address before it is ever output to anywhere).
Note that explicitly setting all attributes to 0 is equivalent to
setting none of them, which is okay, since 0000:00:00 is reserved in
any PCI bus setup, and can't be used anyway.
2016-05-17 18:03:00 +00:00
|
|
|
* Copyright (C) 2006-2016 Red Hat, Inc.
|
2012-08-16 15:41:06 +00:00
|
|
|
*
|
|
|
|
* 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
|
2012-09-20 22:30:55 +00:00
|
|
|
* License along with this library. If not, see
|
2012-08-16 15:41:06 +00:00
|
|
|
* <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
2012-12-13 18:21:53 +00:00
|
|
|
#include "virerror.h"
|
2012-08-16 15:41:06 +00:00
|
|
|
#include "datatypes.h"
|
2012-12-12 18:06:53 +00:00
|
|
|
#include "viralloc.h"
|
2012-12-13 18:13:21 +00:00
|
|
|
#include "virxml.h"
|
2012-12-13 18:01:25 +00:00
|
|
|
#include "viruuid.h"
|
2012-12-04 12:04:07 +00:00
|
|
|
#include "virbuffer.h"
|
2012-08-16 15:41:06 +00:00
|
|
|
#include "device_conf.h"
|
2018-11-08 11:00:28 +00:00
|
|
|
#include "domain_addr.h"
|
2013-04-03 10:36:23 +00:00
|
|
|
#include "virstring.h"
|
2012-08-16 15:41:06 +00:00
|
|
|
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_DEVICE
|
|
|
|
|
2019-03-16 18:20:32 +00:00
|
|
|
VIR_ENUM_IMPL(virDomainDeviceAddress,
|
|
|
|
VIR_DOMAIN_DEVICE_ADDRESS_TYPE_LAST,
|
2018-08-29 15:59:20 +00:00
|
|
|
"none",
|
|
|
|
"pci",
|
|
|
|
"drive",
|
|
|
|
"virtio-serial",
|
|
|
|
"ccid",
|
|
|
|
"usb",
|
|
|
|
"spapr-vio",
|
|
|
|
"virtio-s390",
|
|
|
|
"ccw",
|
|
|
|
"virtio-mmio",
|
|
|
|
"isa",
|
|
|
|
"dimm",
|
2019-12-17 20:35:02 +00:00
|
|
|
"unassigned",
|
2018-08-29 15:59:20 +00:00
|
|
|
);
|
|
|
|
|
2018-11-08 11:00:26 +00:00
|
|
|
static int
|
|
|
|
virZPCIDeviceAddressParseXML(xmlNodePtr node,
|
|
|
|
virPCIDeviceAddressPtr addr)
|
|
|
|
{
|
|
|
|
virZPCIDeviceAddress def = { 0 };
|
|
|
|
char *uid;
|
|
|
|
char *fid;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
uid = virXMLPropString(node, "uid");
|
|
|
|
fid = virXMLPropString(node, "fid");
|
|
|
|
|
|
|
|
if (uid &&
|
|
|
|
virStrToLong_uip(uid, NULL, 0, &def.uid) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Cannot parse <address> 'uid' attribute"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fid &&
|
|
|
|
virStrToLong_uip(fid, NULL, 0, &def.fid) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Cannot parse <address> 'fid' attribute"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!virZPCIDeviceAddressIsEmpty(&def) &&
|
|
|
|
!virZPCIDeviceAddressIsValid(&def))
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
addr->zpci = def;
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(uid);
|
|
|
|
VIR_FREE(fid);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-06-27 06:30:58 +00:00
|
|
|
void
|
|
|
|
virDomainDeviceInfoClear(virDomainDeviceInfoPtr 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(virDomainDeviceInfoPtr info)
|
|
|
|
{
|
|
|
|
if (info) {
|
|
|
|
virDomainDeviceInfoClear(info);
|
|
|
|
VIR_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:
|
2019-12-17 20:35:02 +00:00
|
|
|
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_UNASSIGNED:
|
2017-06-27 06:30:58 +00:00
|
|
|
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 (memcmp(&a->addr.drive, &b->addr.drive, sizeof(a->addr.drive)))
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-08-17 13:35:19 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-11-08 11:00:28 +00:00
|
|
|
bool
|
|
|
|
virDeviceInfoPCIAddressExtensionIsWanted(const virDomainDeviceInfo *info)
|
|
|
|
{
|
|
|
|
return (info->addr.pci.extFlags & VIR_PCI_ADDRESS_EXTENSION_ZPCI) &&
|
|
|
|
virZPCIDeviceAddressIsEmpty(&info->addr.pci.zpci);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
virDeviceInfoPCIAddressExtensionIsPresent(const virDomainDeviceInfo *info)
|
|
|
|
{
|
|
|
|
return (info->addr.pci.extFlags & VIR_PCI_ADDRESS_EXTENSION_ZPCI) &&
|
|
|
|
!virZPCIDeviceAddressIsEmpty(&info->addr.pci.zpci);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-08-16 15:41:06 +00:00
|
|
|
int
|
2016-04-03 18:16:51 +00:00
|
|
|
virPCIDeviceAddressParseXML(xmlNodePtr node,
|
|
|
|
virPCIDeviceAddressPtr addr)
|
2012-08-16 15:41:06 +00:00
|
|
|
{
|
|
|
|
char *domain, *slot, *bus, *function, *multi;
|
2018-11-08 11:00:26 +00:00
|
|
|
xmlNodePtr cur;
|
|
|
|
xmlNodePtr zpci = NULL;
|
2012-08-16 15:41:06 +00:00
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
memset(addr, 0, sizeof(*addr));
|
|
|
|
|
|
|
|
domain = virXMLPropString(node, "domain");
|
|
|
|
bus = virXMLPropString(node, "bus");
|
|
|
|
slot = virXMLPropString(node, "slot");
|
|
|
|
function = virXMLPropString(node, "function");
|
|
|
|
multi = virXMLPropString(node, "multifunction");
|
|
|
|
|
|
|
|
if (domain &&
|
2014-12-08 08:27:26 +00:00
|
|
|
virStrToLong_uip(domain, NULL, 0, &addr->domain) < 0) {
|
2012-08-16 15:41:06 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Cannot parse <address> 'domain' attribute"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bus &&
|
2014-12-08 08:27:26 +00:00
|
|
|
virStrToLong_uip(bus, NULL, 0, &addr->bus) < 0) {
|
2012-08-16 15:41:06 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Cannot parse <address> 'bus' attribute"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (slot &&
|
2014-12-08 08:27:26 +00:00
|
|
|
virStrToLong_uip(slot, NULL, 0, &addr->slot) < 0) {
|
2012-08-16 15:41:06 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Cannot parse <address> 'slot' attribute"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (function &&
|
2014-12-08 08:27:26 +00:00
|
|
|
virStrToLong_uip(function, NULL, 0, &addr->function) < 0) {
|
2012-08-16 15:41:06 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Cannot parse <address> 'function' attribute"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (multi &&
|
2014-06-27 15:18:53 +00:00
|
|
|
((addr->multi = virTristateSwitchTypeFromString(multi)) <= 0)) {
|
2012-08-16 15:41:06 +00:00
|
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
|
|
_("Unknown value '%s' for <address> 'multifunction' attribute"),
|
|
|
|
multi);
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
}
|
conf: allow type='pci' addresses with no address attributes specified
Prior to this, <address type='pci'/> wasn't allowed when parsing
(domain+bus+slot+function needed to be a "valid" PCI address, meaning
that at least one of domain/bus/slot had to be non-0), the RNG
required bus to be specified, and if type was set to PCI when
formatting, domain+bus+slot+function would always be output.
This makes all the address attributes optional during parse and RNG
validation, and suppresses domain+bus+slot+function if domain+bus+slot
are all 0 (NB: if d+b+s are all 0, any value for function is
nonsensical as that will never happen in the real world, and after
the next patch we will always assign a real working address to any
empty PCI address before it is ever output to anywhere).
Note that explicitly setting all attributes to 0 is equivalent to
setting none of them, which is okay, since 0000:00:00 is reserved in
any PCI bus setup, and can't be used anyway.
2016-05-17 18:03:00 +00:00
|
|
|
if (!virPCIDeviceAddressIsEmpty(addr) && !virPCIDeviceAddressIsValid(addr, true))
|
2012-08-16 15:41:06 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
2018-11-08 11:00:26 +00:00
|
|
|
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)
|
|
|
|
goto cleanup;
|
|
|
|
|
2012-08-16 15:41:06 +00:00
|
|
|
ret = 0;
|
|
|
|
|
2014-03-25 06:48:31 +00:00
|
|
|
cleanup:
|
2012-08-16 15:41:06 +00:00
|
|
|
VIR_FREE(domain);
|
|
|
|
VIR_FREE(bus);
|
|
|
|
VIR_FREE(slot);
|
|
|
|
VIR_FREE(function);
|
|
|
|
VIR_FREE(multi);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-12-19 11:58:42 +00:00
|
|
|
void
|
2016-04-03 18:16:51 +00:00
|
|
|
virPCIDeviceAddressFormat(virBufferPtr buf,
|
|
|
|
virPCIDeviceAddress addr,
|
2012-08-16 15:41:06 +00:00
|
|
|
bool includeTypeInAddr)
|
|
|
|
{
|
2019-07-30 14:24:39 +00:00
|
|
|
virBufferAsprintf(buf, "<address %sdomain='0x%04x' bus='0x%02x' "
|
|
|
|
"slot='0x%02x' function='0x%d'/>\n",
|
2012-08-16 15:41:06 +00:00
|
|
|
includeTypeInAddr ? "type='pci' " : "",
|
|
|
|
addr.domain,
|
|
|
|
addr.bus,
|
|
|
|
addr.slot,
|
|
|
|
addr.function);
|
|
|
|
}
|
2012-08-16 15:42:14 +00:00
|
|
|
|
2018-08-29 16:29:41 +00:00
|
|
|
bool
|
2018-08-29 16:19:15 +00:00
|
|
|
virDomainDeviceCCWAddressIsValid(virDomainDeviceCCWAddressPtr 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;
|
|
|
|
}
|
|
|
|
|
2018-08-29 17:14:24 +00:00
|
|
|
int
|
|
|
|
virDomainDeviceCCWAddressParseXML(xmlNodePtr node,
|
|
|
|
virDomainDeviceCCWAddressPtr addr)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
char *cssid;
|
|
|
|
char *ssid;
|
|
|
|
char *devno;
|
|
|
|
|
|
|
|
memset(addr, 0, sizeof(*addr));
|
|
|
|
|
|
|
|
cssid = virXMLPropString(node, "cssid");
|
|
|
|
ssid = virXMLPropString(node, "ssid");
|
|
|
|
devno = virXMLPropString(node, "devno");
|
|
|
|
|
|
|
|
if (cssid && ssid && devno) {
|
|
|
|
if (cssid &&
|
|
|
|
virStrToLong_uip(cssid, NULL, 0, &addr->cssid) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Cannot parse <address> 'cssid' attribute"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
if (ssid &&
|
|
|
|
virStrToLong_uip(ssid, NULL, 0, &addr->ssid) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Cannot parse <address> 'ssid' attribute"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
if (devno &&
|
|
|
|
virStrToLong_uip(devno, NULL, 0, &addr->devno) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Cannot parse <address> 'devno' attribute"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
if (!virDomainDeviceCCWAddressIsValid(addr)) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("Invalid specification for virtio ccw"
|
|
|
|
" address: cssid='%s' ssid='%s' devno='%s'"),
|
|
|
|
cssid, ssid, devno);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
addr->assigned = true;
|
|
|
|
} else if (cssid || ssid || devno) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Invalid partial specification for virtio ccw"
|
|
|
|
" address"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(cssid);
|
|
|
|
VIR_FREE(ssid);
|
|
|
|
VIR_FREE(devno);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
virDomainDeviceDriveAddressParseXML(xmlNodePtr node,
|
|
|
|
virDomainDeviceDriveAddressPtr addr)
|
|
|
|
{
|
|
|
|
char *bus, *unit, *controller, *target;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
memset(addr, 0, sizeof(*addr));
|
|
|
|
|
|
|
|
controller = virXMLPropString(node, "controller");
|
|
|
|
bus = virXMLPropString(node, "bus");
|
|
|
|
target = virXMLPropString(node, "target");
|
|
|
|
unit = virXMLPropString(node, "unit");
|
|
|
|
|
|
|
|
if (controller &&
|
|
|
|
virStrToLong_uip(controller, NULL, 10, &addr->controller) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Cannot parse <address> 'controller' attribute"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bus &&
|
|
|
|
virStrToLong_uip(bus, NULL, 10, &addr->bus) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Cannot parse <address> 'bus' attribute"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (target &&
|
|
|
|
virStrToLong_uip(target, NULL, 10, &addr->target) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Cannot parse <address> 'target' attribute"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (unit &&
|
|
|
|
virStrToLong_uip(unit, NULL, 10, &addr->unit) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Cannot parse <address> 'unit' attribute"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(controller);
|
|
|
|
VIR_FREE(bus);
|
|
|
|
VIR_FREE(target);
|
|
|
|
VIR_FREE(unit);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
virDomainDeviceVirtioSerialAddressParseXML(xmlNodePtr node,
|
|
|
|
virDomainDeviceVirtioSerialAddressPtr addr)
|
|
|
|
{
|
|
|
|
char *controller, *bus, *port;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
memset(addr, 0, sizeof(*addr));
|
|
|
|
|
|
|
|
controller = virXMLPropString(node, "controller");
|
|
|
|
bus = virXMLPropString(node, "bus");
|
|
|
|
port = virXMLPropString(node, "port");
|
|
|
|
|
|
|
|
if (controller &&
|
|
|
|
virStrToLong_uip(controller, NULL, 10, &addr->controller) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Cannot parse <address> 'controller' attribute"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bus &&
|
|
|
|
virStrToLong_uip(bus, NULL, 10, &addr->bus) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Cannot parse <address> 'bus' attribute"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (port &&
|
|
|
|
virStrToLong_uip(port, NULL, 10, &addr->port) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Cannot parse <address> 'port' attribute"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(controller);
|
|
|
|
VIR_FREE(bus);
|
|
|
|
VIR_FREE(port);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
virDomainDeviceCcidAddressParseXML(xmlNodePtr node,
|
|
|
|
virDomainDeviceCcidAddressPtr addr)
|
|
|
|
{
|
|
|
|
char *controller, *slot;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
memset(addr, 0, sizeof(*addr));
|
|
|
|
|
|
|
|
controller = virXMLPropString(node, "controller");
|
|
|
|
slot = virXMLPropString(node, "slot");
|
|
|
|
|
|
|
|
if (controller &&
|
|
|
|
virStrToLong_uip(controller, NULL, 10, &addr->controller) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Cannot parse <address> 'controller' attribute"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (slot &&
|
|
|
|
virStrToLong_uip(slot, NULL, 10, &addr->slot) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Cannot parse <address> 'slot' attribute"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(controller);
|
|
|
|
VIR_FREE(slot);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
virDomainDeviceUSBAddressParsePort(virDomainDeviceUSBAddressPtr 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,
|
|
|
|
virDomainDeviceUSBAddressPtr addr)
|
|
|
|
{
|
|
|
|
char *port, *bus;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
memset(addr, 0, sizeof(*addr));
|
|
|
|
|
|
|
|
port = virXMLPropString(node, "port");
|
|
|
|
bus = virXMLPropString(node, "bus");
|
|
|
|
|
|
|
|
if (port && virDomainDeviceUSBAddressParsePort(addr, port) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (bus &&
|
|
|
|
virStrToLong_uip(bus, NULL, 10, &addr->bus) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Cannot parse <address> 'bus' attribute"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(bus);
|
|
|
|
VIR_FREE(port);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
virDomainDeviceSpaprVioAddressParseXML(xmlNodePtr node,
|
|
|
|
virDomainDeviceSpaprVioAddressPtr addr)
|
|
|
|
{
|
|
|
|
char *reg;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
memset(addr, 0, sizeof(*addr));
|
|
|
|
|
|
|
|
reg = virXMLPropString(node, "reg");
|
|
|
|
if (reg) {
|
|
|
|
if (virStrToLong_ull(reg, NULL, 16, &addr->reg) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Cannot parse <address> 'reg' attribute"));
|
|
|
|
ret = -1;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
addr->has_reg = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(reg);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-08-29 16:29:41 +00:00
|
|
|
bool
|
2018-08-29 16:19:15 +00:00
|
|
|
virDomainDeviceAddressIsValid(virDomainDeviceInfoPtr info,
|
|
|
|
int type)
|
|
|
|
{
|
|
|
|
if (info->type != type)
|
2018-08-29 16:29:41 +00:00
|
|
|
return false;
|
2018-08-29 16:19:15 +00:00
|
|
|
|
|
|
|
switch (info->type) {
|
|
|
|
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI:
|
|
|
|
return virPCIDeviceAddressIsValid(&info->addr.pci, false);
|
|
|
|
|
|
|
|
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE:
|
2018-08-29 16:29:41 +00:00
|
|
|
return true;
|
2018-08-29 16:19:15 +00:00
|
|
|
|
|
|
|
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_S390:
|
|
|
|
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_MMIO:
|
2018-08-29 16:29:41 +00:00
|
|
|
return true;
|
2018-08-29 16:19:15 +00:00
|
|
|
|
|
|
|
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW:
|
|
|
|
return virDomainDeviceCCWAddressIsValid(&info->addr.ccw);
|
|
|
|
|
|
|
|
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB:
|
2018-08-29 16:29:41 +00:00
|
|
|
return true;
|
2018-08-29 16:19:15 +00:00
|
|
|
}
|
|
|
|
|
2018-08-29 16:29:41 +00:00
|
|
|
return false;
|
2018-08-29 16:19:15 +00:00
|
|
|
}
|
|
|
|
|
2014-05-07 12:21:35 +00:00
|
|
|
int
|
|
|
|
virInterfaceLinkParseXML(xmlNodePtr node,
|
2016-06-13 17:06:15 +00:00
|
|
|
virNetDevIfLinkPtr lnk)
|
2014-05-07 12:21:35 +00:00
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
char *stateStr, *speedStr;
|
|
|
|
int state;
|
|
|
|
|
|
|
|
stateStr = virXMLPropString(node, "state");
|
|
|
|
speedStr = virXMLPropString(node, "speed");
|
|
|
|
|
|
|
|
if (stateStr) {
|
2016-06-13 17:06:15 +00:00
|
|
|
if ((state = virNetDevIfStateTypeFromString(stateStr)) < 0) {
|
2014-05-07 12:21:35 +00:00
|
|
|
virReportError(VIR_ERR_XML_ERROR,
|
|
|
|
_("unknown link state: %s"),
|
|
|
|
stateStr);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
lnk->state = state;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (speedStr &&
|
|
|
|
virStrToLong_ui(speedStr, NULL, 10, &lnk->speed) < 0) {
|
|
|
|
virReportError(VIR_ERR_XML_ERROR,
|
|
|
|
_("Unable to parse link speed: %s"),
|
|
|
|
speedStr);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(stateStr);
|
|
|
|
VIR_FREE(speedStr);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
virInterfaceLinkFormat(virBufferPtr buf,
|
2016-06-13 17:06:15 +00:00
|
|
|
const virNetDevIfLink *lnk)
|
2014-05-07 12:21:35 +00:00
|
|
|
{
|
|
|
|
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'",
|
2016-06-13 17:06:15 +00:00
|
|
|
virNetDevIfStateTypeToString(lnk->state));
|
2014-05-07 12:21:35 +00:00
|
|
|
virBufferAddLit(buf, "/>\n");
|
|
|
|
return 0;
|
|
|
|
}
|