/*
* qemu_command.c: QEMU command generation
*
* Copyright (C) 2006-2015 Red Hat, Inc.
* Copyright (C) 2006 Daniel P. Berrange
*
* 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
* .
*
* Author: Daniel P. Berrange
*/
#include
#include "qemu_command.h"
#include "qemu_hostdev.h"
#include "qemu_capabilities.h"
#include "cpu/cpu.h"
#include "dirname.h"
#include "passfd.h"
#include "viralloc.h"
#include "virlog.h"
#include "virarch.h"
#include "virerror.h"
#include "virfile.h"
#include "virnetdev.h"
#include "virnetdevbridge.h"
#include "virstring.h"
#include "virtime.h"
#include "viruuid.h"
#include "c-ctype.h"
#include "domain_nwfilter.h"
#include "domain_addr.h"
#include "domain_audit.h"
#include "domain_conf.h"
#include "netdev_bandwidth_conf.h"
#include "snapshot_conf.h"
#include "storage_conf.h"
#include "secret_conf.h"
#include "network/bridge_driver.h"
#include "virnetdevtap.h"
#include "base64.h"
#include "device_conf.h"
#include "virstoragefile.h"
#include "virtpm.h"
#include "virscsi.h"
#include "virnuma.h"
#if defined(__linux__)
# include
#endif
#include
#include
#define VIR_FROM_THIS VIR_FROM_QEMU
VIR_LOG_INIT("qemu.qemu_command");
#define VIO_ADDR_NET 0x1000ul
#define VIO_ADDR_SCSI 0x2000ul
#define VIO_ADDR_SERIAL 0x30000000ul
#define VIO_ADDR_NVRAM 0x3000ul
VIR_ENUM_DECL(virDomainDiskQEMUBus)
VIR_ENUM_IMPL(virDomainDiskQEMUBus, VIR_DOMAIN_DISK_BUS_LAST,
"ide",
"floppy",
"scsi",
"virtio",
"xen",
"usb",
"uml",
"sata",
"sd")
VIR_ENUM_DECL(qemuDiskCacheV1)
VIR_ENUM_DECL(qemuDiskCacheV2)
VIR_ENUM_IMPL(qemuDiskCacheV1, VIR_DOMAIN_DISK_CACHE_LAST,
"default",
"off",
"off", /* writethrough not supported, so for safety, disable */
"on", /* Old 'on' was equivalent to 'writeback' */
"off", /* directsync not supported, for safety, disable */
"off"); /* unsafe not supported, for safety, disable */
VIR_ENUM_IMPL(qemuDiskCacheV2, VIR_DOMAIN_DISK_CACHE_LAST,
"default",
"none",
"writethrough",
"writeback",
"directsync",
"unsafe");
VIR_ENUM_DECL(qemuVideo)
VIR_ENUM_IMPL(qemuVideo, VIR_DOMAIN_VIDEO_TYPE_LAST,
"std",
"cirrus",
"vmware",
"", /* no arg needed for xen */
"", /* don't support vbox */
"qxl",
"" /* don't support parallels */);
VIR_ENUM_DECL(qemuDeviceVideo)
VIR_ENUM_IMPL(qemuDeviceVideo, VIR_DOMAIN_VIDEO_TYPE_LAST,
"VGA",
"cirrus-vga",
"vmware-svga",
"", /* no device for xen */
"", /* don't support vbox */
"qxl-vga",
"" /* don't support parallels */);
VIR_ENUM_DECL(qemuSoundCodec)
VIR_ENUM_IMPL(qemuSoundCodec, VIR_DOMAIN_SOUND_CODEC_TYPE_LAST,
"hda-duplex",
"hda-micro");
VIR_ENUM_DECL(qemuControllerModelUSB)
VIR_ENUM_IMPL(qemuControllerModelUSB, VIR_DOMAIN_CONTROLLER_MODEL_USB_LAST,
"piix3-usb-uhci",
"piix4-usb-uhci",
"usb-ehci",
"ich9-usb-ehci1",
"ich9-usb-uhci1",
"ich9-usb-uhci2",
"ich9-usb-uhci3",
"vt82c686b-usb-uhci",
"pci-ohci",
"nec-usb-xhci",
"none");
VIR_ENUM_DECL(qemuDomainFSDriver)
VIR_ENUM_IMPL(qemuDomainFSDriver, VIR_DOMAIN_FS_DRIVER_TYPE_LAST,
"local",
"local",
"handle",
NULL,
NULL,
NULL);
VIR_ENUM_DECL(qemuNumaPolicy)
VIR_ENUM_IMPL(qemuNumaPolicy, VIR_DOMAIN_NUMATUNE_MEM_LAST,
"bind",
"preferred",
"interleave");
/**
* qemuVirCommandGetFDSet:
* @cmd: the command to modify
* @fd: fd to reassign to the child
*
* Get the parameters for the QEMU -add-fd command line option
* for the given file descriptor. The file descriptor must previously
* have been 'transferred' in a virCommandPassFD() call.
* This function for example returns "set=10,fd=20".
*/
static char *
qemuVirCommandGetFDSet(virCommandPtr cmd, int fd)
{
char *result = NULL;
int idx = virCommandPassFDGetFDIndex(cmd, fd);
if (idx >= 0) {
ignore_value(virAsprintf(&result, "set=%d,fd=%d", idx, fd));
} else {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("file descriptor %d has not been transferred"), fd);
}
return result;
}
/**
* qemuVirCommandGetDevSet:
* @cmd: the command to modify
* @fd: fd to reassign to the child
*
* Get the parameters for the QEMU path= parameter where a file
* descriptor is accessed via a file descriptor set, for example
* /dev/fdset/10. The file descriptor must previously have been
* 'transferred' in a virCommandPassFD() call.
*/
static char *
qemuVirCommandGetDevSet(virCommandPtr cmd, int fd)
{
char *result = NULL;
int idx = virCommandPassFDGetFDIndex(cmd, fd);
if (idx >= 0) {
ignore_value(virAsprintf(&result, "/dev/fdset/%d", idx));
} else {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("file descriptor %d has not been transferred"), fd);
}
return result;
}
/**
* qemuPhysIfaceConnect:
* @def: the definition of the VM (needed by 802.1Qbh and audit)
* @driver: pointer to the driver instance
* @net: pointer to the VM's interface description with direct device type
* @qemuCaps: flags for qemu
* @vmop: VM operation type
*
* Returns a filedescriptor on success or -1 in case of error.
*/
int
qemuPhysIfaceConnect(virDomainDefPtr def,
virQEMUDriverPtr driver,
virDomainNetDefPtr net,
virQEMUCapsPtr qemuCaps,
virNetDevVPortProfileOp vmop)
{
int rc;
char *res_ifname = NULL;
int vnet_hdr = 0;
virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver);
unsigned int macvlan_create_flags = VIR_NETDEV_MACVLAN_CREATE_WITH_TAP;
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_VNET_HDR) &&
net->model && STREQ(net->model, "virtio"))
vnet_hdr = 1;
rc = virNetDevMacVLanCreateWithVPortProfile(
net->ifname, &net->mac,
virDomainNetGetActualDirectDev(net),
virDomainNetGetActualDirectMode(net),
vnet_hdr, def->uuid,
virDomainNetGetActualVirtPortProfile(net),
&res_ifname,
vmop, cfg->stateDir,
macvlan_create_flags);
if (rc >= 0) {
virDomainAuditNetDevice(def, net, res_ifname, true);
VIR_FREE(net->ifname);
net->ifname = res_ifname;
}
virObjectUnref(cfg);
return rc;
}
/**
* qemuCreateInBridgePortWithHelper:
* @cfg: the configuration object in which the helper name is looked up
* @brname: the bridge name
* @ifname: the returned interface name
* @macaddr: the returned MAC address
* @tapfd: file descriptor return value for the new tap device
* @flags: OR of virNetDevTapCreateFlags:
* VIR_NETDEV_TAP_CREATE_VNET_HDR
* - Enable IFF_VNET_HDR on the tap device
*
* This function creates a new tap device on a bridge using an external
* helper. The final name for the bridge will be stored in @ifname.
*
* Returns 0 in case of success or -1 on failure
*/
static int qemuCreateInBridgePortWithHelper(virQEMUDriverConfigPtr cfg,
const char *brname,
char **ifname,
int *tapfd,
unsigned int flags)
{
virCommandPtr cmd;
int pair[2] = { -1, -1 };
if ((flags & ~VIR_NETDEV_TAP_CREATE_VNET_HDR) != VIR_NETDEV_TAP_CREATE_IFUP)
return -1;
if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) < 0) {
virReportSystemError(errno, "%s", _("failed to create socket"));
return -1;
}
if (!virFileIsExecutable(cfg->bridgeHelperName)) {
virReportSystemError(errno, _("'%s' is not a suitable bridge helper"),
cfg->bridgeHelperName);
return -1;
}
cmd = virCommandNew(cfg->bridgeHelperName);
if (flags & VIR_NETDEV_TAP_CREATE_VNET_HDR)
virCommandAddArgFormat(cmd, "--use-vnet");
virCommandAddArgFormat(cmd, "--br=%s", brname);
virCommandAddArgFormat(cmd, "--fd=%d", pair[1]);
virCommandPassFD(cmd, pair[1],
VIR_COMMAND_PASS_FD_CLOSE_PARENT);
virCommandClearCaps(cmd);
#ifdef CAP_NET_ADMIN
virCommandAllowCap(cmd, CAP_NET_ADMIN);
#endif
if (virCommandRunAsync(cmd, NULL) < 0) {
*tapfd = -1;
goto cleanup;
}
do {
*tapfd = recvfd(pair[0], 0);
} while (*tapfd < 0 && errno == EINTR);
if (*tapfd < 0) {
virReportSystemError(errno, "%s",
_("failed to retrieve file descriptor for interface"));
goto cleanup;
}
if (virNetDevTapGetName(*tapfd, ifname) < 0 ||
virCommandWait(cmd, NULL) < 0) {
VIR_FORCE_CLOSE(*tapfd);
*tapfd = -1;
}
cleanup:
virCommandFree(cmd);
VIR_FORCE_CLOSE(pair[0]);
return *tapfd < 0 ? -1 : 0;
}
/* qemuNetworkIfaceConnect - *only* called if actualType is
* VIR_DOMAIN_NET_TYPE_NETWORK or VIR_DOMAIN_NET_TYPE_BRIDGE (i.e. if
* the connection is made with a tap device connecting to a bridge
* device)
*/
int
qemuNetworkIfaceConnect(virDomainDefPtr def,
virQEMUDriverPtr driver,
virDomainNetDefPtr net,
virQEMUCapsPtr qemuCaps,
int *tapfd,
size_t *tapfdSize)
{
const char *brname;
int ret = -1;
unsigned int tap_create_flags = VIR_NETDEV_TAP_CREATE_IFUP;
bool template_ifname = false;
virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver);
const char *tunpath = "/dev/net/tun";
if (net->backend.tap) {
tunpath = net->backend.tap;
if (!(virQEMUDriverIsPrivileged(driver))) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("cannot use custom tap device in session mode"));
goto cleanup;
}
}
if (!(brname = virDomainNetGetActualBridgeName(net))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing bridge name"));
goto cleanup;
}
if (!net->ifname ||
STRPREFIX(net->ifname, VIR_NET_GENERATED_PREFIX) ||
strchr(net->ifname, '%')) {
VIR_FREE(net->ifname);
if (VIR_STRDUP(net->ifname, VIR_NET_GENERATED_PREFIX "%d") < 0)
goto cleanup;
/* avoid exposing vnet%d in getXMLDesc or error outputs */
template_ifname = true;
}
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_VNET_HDR) &&
net->model && STREQ(net->model, "virtio")) {
tap_create_flags |= VIR_NETDEV_TAP_CREATE_VNET_HDR;
}
if (virQEMUDriverIsPrivileged(driver)) {
if (virNetDevTapCreateInBridgePort(brname, &net->ifname, &net->mac,
def->uuid, tunpath, tapfd, *tapfdSize,
virDomainNetGetActualVirtPortProfile(net),
virDomainNetGetActualVlan(net),
tap_create_flags) < 0) {
virDomainAuditNetDevice(def, net, tunpath, false);
goto cleanup;
}
if (virDomainNetGetActualBridgeMACTableManager(net)
== VIR_NETWORK_BRIDGE_MAC_TABLE_MANAGER_LIBVIRT) {
/* libvirt is managing the FDB of the bridge this device
* is attaching to, so we need to turn off learning and
* unicast_flood on the device to prevent the kernel from
* adding any FDB entries for it. We will add add an fdb
* entry ourselves (during qemuInterfaceStartDevices(),
* using the MAC address from the interface config.
*/
if (virNetDevBridgePortSetLearning(brname, net->ifname, false) < 0)
goto cleanup;
if (virNetDevBridgePortSetUnicastFlood(brname, net->ifname, false) < 0)
goto cleanup;
}
} else {
if (qemuCreateInBridgePortWithHelper(cfg, brname,
&net->ifname,
tapfd, tap_create_flags) < 0) {
virDomainAuditNetDevice(def, net, tunpath, false);
goto cleanup;
}
/* qemuCreateInBridgePortWithHelper can only create a single FD */
if (*tapfdSize > 1) {
VIR_WARN("Ignoring multiqueue network request");
*tapfdSize = 1;
}
}
virDomainAuditNetDevice(def, net, tunpath, true);
if (cfg->macFilter &&
ebtablesAddForwardAllowIn(driver->ebtables,
net->ifname,
&net->mac) < 0)
goto cleanup;
if (net->filter &&
virDomainConfNWFilterInstantiate(def->uuid, net) < 0) {
goto cleanup;
}
ret = 0;
cleanup:
if (ret < 0) {
size_t i;
for (i = 0; i < *tapfdSize && tapfd[i] >= 0; i++)
VIR_FORCE_CLOSE(tapfd[i]);
if (template_ifname)
VIR_FREE(net->ifname);
}
virObjectUnref(cfg);
return ret;
}
static bool
qemuDomainSupportsNicdev(virDomainDefPtr def,
virQEMUCapsPtr qemuCaps,
virDomainNetDefPtr net)
{
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE))
return false;
/* non-virtio ARM nics require legacy -net nic */
if (((def->os.arch == VIR_ARCH_ARMV7L) ||
(def->os.arch == VIR_ARCH_AARCH64)) &&
net->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_MMIO &&
net->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI)
return false;
return true;
}
static bool
qemuDomainSupportsNetdev(virDomainDefPtr def,
virQEMUCapsPtr qemuCaps,
virDomainNetDefPtr net)
{
if (!qemuDomainSupportsNicdev(def, qemuCaps, net))
return false;
return virQEMUCapsGet(qemuCaps, QEMU_CAPS_NETDEV);
}
static int
qemuBuildObjectCommandLinePropsInternal(const char *key,
const virJSONValue *value,
virBufferPtr buf,
bool nested)
{
virJSONValuePtr elem;
virBitmapPtr bitmap = NULL;
ssize_t pos = -1;
ssize_t end;
size_t i;
switch ((virJSONType) value->type) {
case VIR_JSON_TYPE_STRING:
virBufferAsprintf(buf, ",%s=%s", key, value->data.string);
break;
case VIR_JSON_TYPE_NUMBER:
virBufferAsprintf(buf, ",%s=%s", key, value->data.number);
break;
case VIR_JSON_TYPE_BOOLEAN:
if (value->data.boolean)
virBufferAsprintf(buf, ",%s=yes", key);
else
virBufferAsprintf(buf, ",%s=no", key);
break;
case VIR_JSON_TYPE_ARRAY:
if (nested) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("nested -object property arrays are not supported"));
return -1;
}
if (virJSONValueGetArrayAsBitmap(value, &bitmap) == 0) {
while ((pos = virBitmapNextSetBit(bitmap, pos)) > -1) {
if ((end = virBitmapNextClearBit(bitmap, pos)) < 0)
end = virBitmapLastSetBit(bitmap) + 1;
if (end - 1 > pos) {
virBufferAsprintf(buf, ",%s=%zd-%zd", key, pos, end - 1);
pos = end;
} else {
virBufferAsprintf(buf, ",%s=%zd", key, pos);
}
}
} else {
/* fallback, treat the array as a non-bitmap, adding the key
* for each member */
for (i = 0; i < virJSONValueArraySize(value); i++) {
elem = virJSONValueArrayGet((virJSONValuePtr)value, i);
/* recurse to avoid duplicating code */
if (qemuBuildObjectCommandLinePropsInternal(key, elem, buf,
true) < 0)
return -1;
}
}
break;
case VIR_JSON_TYPE_OBJECT:
case VIR_JSON_TYPE_NULL:
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("NULL and OBJECT JSON types can't be converted to "
"commandline string"));
return -1;
}
virBitmapFree(bitmap);
return 0;
}
static int
qemuBuildObjectCommandLineProps(const char *key,
const virJSONValue *value,
void *opaque)
{
return qemuBuildObjectCommandLinePropsInternal(key, value, opaque, false);
}
char *
qemuBuildObjectCommandlineFromJSON(const char *type,
const char *alias,
virJSONValuePtr props)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
char *ret = NULL;
virBufferAsprintf(&buf, "%s,id=%s", type, alias);
if (virJSONValueObjectForeachKeyValue(props,
qemuBuildObjectCommandLineProps,
&buf) < 0)
goto cleanup;
if (virBufferCheckError(&buf) < 0)
goto cleanup;
ret = virBufferContentAndReset(&buf);
cleanup:
virBufferFreeAndReset(&buf);
return ret;
}
/**
* qemuOpenVhostNet:
* @def: domain definition
* @net: network definition
* @qemuCaps: qemu binary capabilities
* @vhostfd: array of opened vhost-net device
* @vhostfdSize: number of file descriptors in @vhostfd array
*
* Open vhost-net, multiple times - if requested.
* In case, no vhost-net is needed, @vhostfdSize is set to 0
* and 0 is returned.
*
* Returns: 0 on success
* -1 on failure
*/
int
qemuOpenVhostNet(virDomainDefPtr def,
virDomainNetDefPtr net,
virQEMUCapsPtr qemuCaps,
int *vhostfd,
size_t *vhostfdSize)
{
size_t i;
const char *vhostnet_path = net->backend.vhost;
if (!vhostnet_path)
vhostnet_path = "/dev/vhost-net";
/* If running a plain QEMU guest, or
* if the config says explicitly to not use vhost, return now*/
if (def->virtType != VIR_DOMAIN_VIRT_KVM ||
net->driver.virtio.name == VIR_DOMAIN_NET_BACKEND_TYPE_QEMU) {
*vhostfdSize = 0;
return 0;
}
/* If qemu doesn't support vhost-net mode (including the -netdev command
* option), don't try to open the device.
*/
if (!(virQEMUCapsGet(qemuCaps, QEMU_CAPS_VHOST_NET) &&
qemuDomainSupportsNetdev(def, qemuCaps, net))) {
if (net->driver.virtio.name == VIR_DOMAIN_NET_BACKEND_TYPE_VHOST) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
"%s", _("vhost-net is not supported with "
"this QEMU binary"));
return -1;
}
*vhostfdSize = 0;
return 0;
}
/* If the nic model isn't virtio, don't try to open. */
if (!(net->model && STREQ(net->model, "virtio"))) {
if (net->driver.virtio.name == VIR_DOMAIN_NET_BACKEND_TYPE_VHOST) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
"%s", _("vhost-net is only supported for "
"virtio network interfaces"));
return -1;
}
*vhostfdSize = 0;
return 0;
}
for (i = 0; i < *vhostfdSize; i++) {
vhostfd[i] = open(vhostnet_path, O_RDWR);
/* If the config says explicitly to use vhost and we couldn't open it,
* report an error.
*/
if (vhostfd[i] < 0) {
virDomainAuditNetDevice(def, net, vhostnet_path, false);
if (net->driver.virtio.name == VIR_DOMAIN_NET_BACKEND_TYPE_VHOST) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
"%s", _("vhost-net was requested for an interface, "
"but is unavailable"));
goto error;
}
VIR_WARN("Unable to open vhost-net. Opened so far %zu, requested %zu",
i, *vhostfdSize);
*vhostfdSize = i;
break;
}
}
virDomainAuditNetDevice(def, net, vhostnet_path, *vhostfdSize);
return 0;
error:
while (i--)
VIR_FORCE_CLOSE(vhostfd[i]);
return -1;
}
int
qemuNetworkPrepareDevices(virDomainDefPtr def)
{
int ret = -1;
size_t i;
for (i = 0; i < def->nnets; i++) {
virDomainNetDefPtr net = def->nets[i];
int actualType;
/* If appropriate, grab a physical device from the configured
* network's pool of devices, or resolve bridge device name
* to the one defined in the network definition.
*/
if (networkAllocateActualDevice(def, net) < 0)
goto cleanup;
actualType = virDomainNetGetActualType(net);
if (actualType == VIR_DOMAIN_NET_TYPE_HOSTDEV &&
net->type == VIR_DOMAIN_NET_TYPE_NETWORK) {
/* Each type='hostdev' network device must also have a
* corresponding entry in the hostdevs array. For netdevs
* that are hardcoded as type='hostdev', this is already
* done by the parser, but for those allocated from a
* network / determined at runtime, we need to do it
* separately.
*/
virDomainHostdevDefPtr hostdev = virDomainNetGetActualHostdev(net);
virDomainHostdevSubsysPCIPtr pcisrc = &hostdev->source.subsys.u.pci;
if (virDomainHostdevFind(def, hostdev, NULL) >= 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("PCI device %04x:%02x:%02x.%x "
"allocated from network %s is already "
"in use by domain %s"),
pcisrc->addr.domain, pcisrc->addr.bus,
pcisrc->addr.slot, pcisrc->addr.function,
net->data.network.name, def->name);
goto cleanup;
}
if (virDomainHostdevInsert(def, hostdev) < 0)
goto cleanup;
}
}
ret = 0;
cleanup:
return ret;
}
static int qemuDomainDeviceAliasIndex(const virDomainDeviceInfo *info,
const char *prefix)
{
int idx;
if (!info->alias)
return -1;
if (!STRPREFIX(info->alias, prefix))
return -1;
if (virStrToLong_i(info->alias + strlen(prefix), NULL, 10, &idx) < 0)
return -1;
return idx;
}
int qemuDomainNetVLAN(virDomainNetDefPtr def)
{
return qemuDomainDeviceAliasIndex(&def->info, "net");
}
/* Names used before -drive existed */
static int qemuAssignDeviceDiskAliasLegacy(virDomainDiskDefPtr disk)
{
char *dev_name;
if (VIR_STRDUP(dev_name,
disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM &&
STREQ(disk->dst, "hdc") ? "cdrom" : disk->dst) < 0)
return -1;
disk->info.alias = dev_name;
return 0;
}
char *qemuDeviceDriveHostAlias(virDomainDiskDefPtr disk,
virQEMUCapsPtr qemuCaps)
{
char *ret;
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) {
ignore_value(virAsprintf(&ret, "%s%s", QEMU_DRIVE_HOST_PREFIX,
disk->info.alias));
} else {
ignore_value(VIR_STRDUP(ret, disk->info.alias));
}
return ret;
}
/* Names used before -drive supported the id= option */
static int qemuAssignDeviceDiskAliasFixed(virDomainDiskDefPtr disk)
{
int busid, devid;
int ret;
char *dev_name;
if (virDiskNameToBusDeviceIndex(disk, &busid, &devid) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot convert disk '%s' to bus/device index"),
disk->dst);
return -1;
}
switch (disk->bus) {
case VIR_DOMAIN_DISK_BUS_IDE:
if (disk->device == VIR_DOMAIN_DISK_DEVICE_DISK)
ret = virAsprintf(&dev_name, "ide%d-hd%d", busid, devid);
else
ret = virAsprintf(&dev_name, "ide%d-cd%d", busid, devid);
break;
case VIR_DOMAIN_DISK_BUS_SCSI:
if (disk->device == VIR_DOMAIN_DISK_DEVICE_DISK)
ret = virAsprintf(&dev_name, "scsi%d-hd%d", busid, devid);
else
ret = virAsprintf(&dev_name, "scsi%d-cd%d", busid, devid);
break;
case VIR_DOMAIN_DISK_BUS_FDC:
ret = virAsprintf(&dev_name, "floppy%d", devid);
break;
case VIR_DOMAIN_DISK_BUS_VIRTIO:
ret = virAsprintf(&dev_name, "virtio%d", devid);
break;
case VIR_DOMAIN_DISK_BUS_XEN:
ret = virAsprintf(&dev_name, "xenblk%d", devid);
break;
case VIR_DOMAIN_DISK_BUS_SD:
ret = virAsprintf(&dev_name, "sd%d", devid);
break;
default:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("Unsupported disk name mapping for bus '%s'"),
virDomainDiskBusTypeToString(disk->bus));
return -1;
}
if (ret == -1)
return -1;
disk->info.alias = dev_name;
return 0;
}
static int
qemuSetSCSIControllerModel(virDomainDefPtr def,
virQEMUCapsPtr qemuCaps,
int *model)
{
if (*model > 0) {
switch (*model) {
case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSILOGIC:
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SCSI_LSI)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("This QEMU doesn't support "
"the LSI 53C895A SCSI controller"));
return -1;
}
break;
case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_SCSI:
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_SCSI)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("This QEMU doesn't support "
"virtio scsi controller"));
return -1;
}
break;
case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_IBMVSCSI:
/*TODO: need checking work here if necessary */
break;
case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSISAS1078:
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SCSI_MEGASAS)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("This QEMU doesn't support "
"the LSI SAS1078 controller"));
return -1;
}
break;
default:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("Unsupported controller model: %s"),
virDomainControllerModelSCSITypeToString(*model));
return -1;
}
} else {
if (ARCH_IS_PPC64(def->os.arch) &&
STRPREFIX(def->os.machine, "pseries")) {
*model = VIR_DOMAIN_CONTROLLER_MODEL_SCSI_IBMVSCSI;
} else if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_SCSI_LSI)) {
*model = VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSILOGIC;
} else if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_SCSI)) {
*model = VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_SCSI;
} else {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Unable to determine model for scsi controller"));
return -1;
}
}
return 0;
}
/* Our custom -drive naming scheme used with id= */
static int
qemuAssignDeviceDiskAliasCustom(virDomainDefPtr def,
virDomainDiskDefPtr disk,
virQEMUCapsPtr qemuCaps)
{
const char *prefix = virDomainDiskBusTypeToString(disk->bus);
int controllerModel = -1;
if (disk->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) {
if (disk->bus == VIR_DOMAIN_DISK_BUS_SCSI) {
controllerModel =
virDomainDeviceFindControllerModel(def, &disk->info,
VIR_DOMAIN_CONTROLLER_TYPE_SCSI);
if ((qemuSetSCSIControllerModel(def, qemuCaps, &controllerModel)) < 0)
return -1;
}
if (disk->bus != VIR_DOMAIN_DISK_BUS_SCSI ||
controllerModel == VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSILOGIC) {
if (virAsprintf(&disk->info.alias, "%s%d-%d-%d", prefix,
disk->info.addr.drive.controller,
disk->info.addr.drive.bus,
disk->info.addr.drive.unit) < 0)
return -1;
} else {
if (virAsprintf(&disk->info.alias, "%s%d-%d-%d-%d", prefix,
disk->info.addr.drive.controller,
disk->info.addr.drive.bus,
disk->info.addr.drive.target,
disk->info.addr.drive.unit) < 0)
return -1;
}
} else {
int idx = virDiskNameToIndex(disk->dst);
if (virAsprintf(&disk->info.alias, "%s-disk%d", prefix, idx) < 0)
return -1;
}
return 0;
}
int
qemuAssignDeviceDiskAlias(virDomainDefPtr vmdef,
virDomainDiskDefPtr def,
virQEMUCapsPtr qemuCaps)
{
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE)) {
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE))
return qemuAssignDeviceDiskAliasCustom(vmdef, def, qemuCaps);
else
return qemuAssignDeviceDiskAliasFixed(def);
} else {
return qemuAssignDeviceDiskAliasLegacy(def);
}
}
int
qemuAssignDeviceNetAlias(virDomainDefPtr def, virDomainNetDefPtr net, int idx)
{
if (idx == -1) {
size_t i;
idx = 0;
for (i = 0; i < def->nnets; i++) {
int thisidx;
if (virDomainNetGetActualType(def->nets[i])
== VIR_DOMAIN_NET_TYPE_HOSTDEV) {
/* type='hostdev' interfaces have a hostdev%d alias */
continue;
}
if ((thisidx = qemuDomainDeviceAliasIndex(&def->nets[i]->info, "net")) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Unable to determine device index for network device"));
return -1;
}
if (thisidx >= idx)
idx = thisidx + 1;
}
}
if (virAsprintf(&net->info.alias, "net%d", idx) < 0)
return -1;
return 0;
}
int
qemuAssignDeviceHostdevAlias(virDomainDefPtr def, virDomainHostdevDefPtr hostdev, int idx)
{
if (idx == -1) {
size_t i;
idx = 0;
for (i = 0; i < def->nhostdevs; i++) {
int thisidx;
if ((thisidx = qemuDomainDeviceAliasIndex(def->hostdevs[i]->info, "hostdev")) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Unable to determine device index for hostdev device"));
return -1;
}
if (thisidx >= idx)
idx = thisidx + 1;
}
}
if (virAsprintf(&hostdev->info->alias, "hostdev%d", idx) < 0)
return -1;
return 0;
}
int
qemuAssignDeviceRedirdevAlias(virDomainDefPtr def, virDomainRedirdevDefPtr redirdev, int idx)
{
if (idx == -1) {
size_t i;
idx = 0;
for (i = 0; i < def->nredirdevs; i++) {
int thisidx;
if ((thisidx = qemuDomainDeviceAliasIndex(&def->redirdevs[i]->info, "redir")) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Unable to determine device index for redirected device"));
return -1;
}
if (thisidx >= idx)
idx = thisidx + 1;
}
}
if (virAsprintf(&redirdev->info.alias, "redir%d", idx) < 0)
return -1;
return 0;
}
int
qemuAssignDeviceControllerAlias(virDomainDefPtr domainDef,
virQEMUCapsPtr qemuCaps,
virDomainControllerDefPtr controller)
{
const char *prefix = virDomainControllerTypeToString(controller->type);
if (controller->type == VIR_DOMAIN_CONTROLLER_TYPE_PCI) {
if (!virQEMUCapsHasPCIMultiBus(qemuCaps, domainDef)) {
/* qemus that don't support multiple PCI buses have
* hardcoded the name of their single PCI controller as
* "pci".
*/
return VIR_STRDUP(controller->info.alias, "pci");
} else if (controller->model == VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT) {
/* The pcie-root controller on Q35 machinetypes uses a
* different naming convention ("pcie.0"), because it is
* hardcoded that way in qemu.
*/
return virAsprintf(&controller->info.alias, "pcie.%d", controller->idx);
}
/* All other PCI controllers use the consistent "pci.%u"
* (including the hardcoded pci-root controller on
* multibus-capable qemus).
*/
return virAsprintf(&controller->info.alias, "pci.%d", controller->idx);
} else if (controller->type == VIR_DOMAIN_CONTROLLER_TYPE_IDE) {
/* for any machine based on I440FX, the first (and currently
* only) IDE controller is an integrated controller hardcoded
* with id "ide"
*/
if (qemuDomainMachineIsI440FX(domainDef) && controller->idx == 0)
return VIR_STRDUP(controller->info.alias, "ide");
} else if (controller->type == VIR_DOMAIN_CONTROLLER_TYPE_SATA) {
/* for any Q35 machine, the first SATA controller is the
* integrated one, and it too is hardcoded with id "ide"
*/
if (qemuDomainMachineIsQ35(domainDef) && controller->idx == 0)
return VIR_STRDUP(controller->info.alias, "ide");
} else if (controller->type == VIR_DOMAIN_CONTROLLER_TYPE_USB) {
/* first USB device is "usb", others are normal "usb%d" */
if (controller->idx == 0)
return VIR_STRDUP(controller->info.alias, "usb");
}
/* all other controllers use the default ${type}${index} naming
* scheme for alias/id.
*/
return virAsprintf(&controller->info.alias, "%s%d", prefix, controller->idx);
}
static ssize_t
qemuGetNextChrDevIndex(virDomainDefPtr def,
virDomainChrDefPtr chr,
const char *prefix)
{
const virDomainChrDef **arrPtr;
size_t cnt;
size_t i;
ssize_t idx = 0;
const char *prefix2 = NULL;
if (chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE)
prefix2 = "serial";
virDomainChrGetDomainPtrs(def, chr->deviceType, &arrPtr, &cnt);
for (i = 0; i < cnt; i++) {
ssize_t thisidx;
if (((thisidx = qemuDomainDeviceAliasIndex(&arrPtr[i]->info, prefix)) < 0) &&
(prefix2 &&
(thisidx = qemuDomainDeviceAliasIndex(&arrPtr[i]->info, prefix2)) < 0)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Unable to determine device index for character device"));
return -1;
}
if (thisidx >= idx)
idx = thisidx + 1;
}
return idx;
}
int
qemuAssignDeviceRNGAlias(virDomainRNGDefPtr rng,
size_t idx)
{
if (virAsprintf(&rng->info.alias, "rng%zu", idx) < 0)
return -1;
return 0;
}
int
qemuAssignDeviceChrAlias(virDomainDefPtr def,
virDomainChrDefPtr chr,
ssize_t idx)
{
const char *prefix = NULL;
switch ((virDomainChrDeviceType) chr->deviceType) {
case VIR_DOMAIN_CHR_DEVICE_TYPE_PARALLEL:
prefix = "parallel";
break;
case VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL:
prefix = "serial";
break;
case VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE:
prefix = "console";
break;
case VIR_DOMAIN_CHR_DEVICE_TYPE_CHANNEL:
prefix = "channel";
break;
case VIR_DOMAIN_CHR_DEVICE_TYPE_LAST:
return -1;
}
if (idx == -1 && (idx = qemuGetNextChrDevIndex(def, chr, prefix)) < 0)
return -1;
return virAsprintf(&chr->info.alias, "%s%zd", prefix, idx);
}
int
qemuAssignDeviceAliases(virDomainDefPtr def, virQEMUCapsPtr qemuCaps)
{
size_t i;
for (i = 0; i < def->ndisks; i++) {
if (qemuAssignDeviceDiskAlias(def, def->disks[i], qemuCaps) < 0)
return -1;
}
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_NET_NAME) ||
virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) {
for (i = 0; i < def->nnets; i++) {
/* type='hostdev' interfaces are also on the hostdevs list,
* and will have their alias assigned with other hostdevs.
*/
if (virDomainNetGetActualType(def->nets[i])
!= VIR_DOMAIN_NET_TYPE_HOSTDEV &&
qemuAssignDeviceNetAlias(def, def->nets[i], i) < 0) {
return -1;
}
}
}
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE))
return 0;
for (i = 0; i < def->nfss; i++) {
if (virAsprintf(&def->fss[i]->info.alias, "fs%zu", i) < 0)
return -1;
}
for (i = 0; i < def->nsounds; i++) {
if (virAsprintf(&def->sounds[i]->info.alias, "sound%zu", i) < 0)
return -1;
}
for (i = 0; i < def->nhostdevs; i++) {
if (qemuAssignDeviceHostdevAlias(def, def->hostdevs[i], i) < 0)
return -1;
}
for (i = 0; i < def->nredirdevs; i++) {
if (qemuAssignDeviceRedirdevAlias(def, def->redirdevs[i], i) < 0)
return -1;
}
for (i = 0; i < def->nvideos; i++) {
if (virAsprintf(&def->videos[i]->info.alias, "video%zu", i) < 0)
return -1;
}
for (i = 0; i < def->ncontrollers; i++) {
if (qemuAssignDeviceControllerAlias(def, qemuCaps, def->controllers[i]) < 0)
return -1;
}
for (i = 0; i < def->ninputs; i++) {
if (virAsprintf(&def->inputs[i]->info.alias, "input%zu", i) < 0)
return -1;
}
for (i = 0; i < def->nparallels; i++) {
if (qemuAssignDeviceChrAlias(def, def->parallels[i], i) < 0)
return -1;
}
for (i = 0; i < def->nserials; i++) {
if (qemuAssignDeviceChrAlias(def, def->serials[i], i) < 0)
return -1;
}
for (i = 0; i < def->nchannels; i++) {
if (qemuAssignDeviceChrAlias(def, def->channels[i], i) < 0)
return -1;
}
for (i = 0; i < def->nconsoles; i++) {
if (qemuAssignDeviceChrAlias(def, def->consoles[i], i) < 0)
return -1;
}
for (i = 0; i < def->nhubs; i++) {
if (virAsprintf(&def->hubs[i]->info.alias, "hub%zu", i) < 0)
return -1;
}
for (i = 0; i < def->nshmems; i++) {
if (virAsprintf(&def->shmems[i]->info.alias, "shmem%zu", i) < 0)
return -1;
}
for (i = 0; i < def->nsmartcards; i++) {
if (virAsprintf(&def->smartcards[i]->info.alias, "smartcard%zu", i) < 0)
return -1;
}
if (def->watchdog) {
if (virAsprintf(&def->watchdog->info.alias, "watchdog%d", 0) < 0)
return -1;
}
if (def->memballoon) {
if (virAsprintf(&def->memballoon->info.alias, "balloon%d", 0) < 0)
return -1;
}
for (i = 0; i < def->nrngs; i++) {
if (qemuAssignDeviceRNGAlias(def->rngs[i], i) < 0)
return -1;
}
if (def->tpm) {
if (virAsprintf(&def->tpm->info.alias, "tpm%d", 0) < 0)
return -1;
}
for (i = 0; i < def->nmems; i++) {
if (virAsprintf(&def->mems[i]->info.alias, "dimm%zu", i) < 0)
return -1;
}
return 0;
}
static void
qemuDomainPrimeVirtioDeviceAddresses(virDomainDefPtr def,
virDomainDeviceAddressType type)
{
/*
declare address-less virtio devices to be of address type 'type'
disks, networks, consoles, controllers, memballoon and rng in this
order
if type is ccw filesystem devices are declared to be of address type ccw
*/
size_t i;
for (i = 0; i < def->ndisks; i++) {
if (def->disks[i]->bus == VIR_DOMAIN_DISK_BUS_VIRTIO &&
def->disks[i]->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE)
def->disks[i]->info.type = type;
}
for (i = 0; i < def->nnets; i++) {
if (STREQ(def->nets[i]->model, "virtio") &&
def->nets[i]->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) {
def->nets[i]->info.type = type;
}
}
for (i = 0; i < def->ncontrollers; i++) {
if ((def->controllers[i]->type ==
VIR_DOMAIN_CONTROLLER_TYPE_VIRTIO_SERIAL ||
def->controllers[i]->type ==
VIR_DOMAIN_CONTROLLER_TYPE_SCSI) &&
def->controllers[i]->info.type ==
VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE)
def->controllers[i]->info.type = type;
}
if (def->memballoon &&
def->memballoon->model == VIR_DOMAIN_MEMBALLOON_MODEL_VIRTIO &&
def->memballoon->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE)
def->memballoon->info.type = type;
for (i = 0; i < def->nrngs; i++) {
if (def->rngs[i]->model == VIR_DOMAIN_RNG_MODEL_VIRTIO &&
def->rngs[i]->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE)
def->rngs[i]->info.type = type;
}
if (type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW) {
for (i = 0; i < def->nfss; i++) {
if (def->fss[i]->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE)
def->fss[i]->info.type = type;
}
}
}
/*
* Three steps populating CCW devnos
* 1. Allocate empty address set
* 2. Gather addresses with explicit devno
* 3. Assign defaults to the rest
*/
static int
qemuDomainAssignS390Addresses(virDomainDefPtr def,
virQEMUCapsPtr qemuCaps,
virDomainObjPtr obj)
{
int ret = -1;
virDomainCCWAddressSetPtr addrs = NULL;
qemuDomainObjPrivatePtr priv = NULL;
if (qemuDomainMachineIsS390CCW(def) &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_CCW)) {
qemuDomainPrimeVirtioDeviceAddresses(
def, VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW);
if (!(addrs = virDomainCCWAddressSetCreate()))
goto cleanup;
if (virDomainDeviceInfoIterate(def, virDomainCCWAddressValidate,
addrs) < 0)
goto cleanup;
if (virDomainDeviceInfoIterate(def, virDomainCCWAddressAllocate,
addrs) < 0)
goto cleanup;
} else if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_S390)) {
/* deal with legacy virtio-s390 */
qemuDomainPrimeVirtioDeviceAddresses(
def, VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_S390);
}
if (obj && obj->privateData) {
priv = obj->privateData;
if (addrs) {
/* if this is the live domain object, we persist the CCW addresses*/
virDomainCCWAddressSetFree(priv->ccwaddrs);
priv->persistentAddrs = 1;
priv->ccwaddrs = addrs;
addrs = NULL;
} else {
priv->persistentAddrs = 0;
}
}
ret = 0;
cleanup:
virDomainCCWAddressSetFree(addrs);
return ret;
}
static int
qemuDomainAssignARMVirtioMMIOAddresses(virDomainDefPtr def,
virQEMUCapsPtr qemuCaps)
{
if (((def->os.arch == VIR_ARCH_ARMV7L) ||
(def->os.arch == VIR_ARCH_AARCH64)) &&
(STRPREFIX(def->os.machine, "vexpress-") ||
STREQ(def->os.machine, "virt") ||
STRPREFIX(def->os.machine, "virt-")) &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VIRTIO_MMIO)) {
qemuDomainPrimeVirtioDeviceAddresses(
def, VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_MMIO);
}
return 0;
}
static int
qemuSpaprVIOFindByReg(virDomainDefPtr def ATTRIBUTE_UNUSED,
virDomainDeviceDefPtr device ATTRIBUTE_UNUSED,
virDomainDeviceInfoPtr info, void *opaque)
{
virDomainDeviceInfoPtr target = opaque;
if (info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_SPAPRVIO)
return 0;
/* Match a dev that has a reg, is not us, and has a matching reg */
if (info->addr.spaprvio.has_reg && info != target &&
info->addr.spaprvio.reg == target->addr.spaprvio.reg)
/* Has to be < 0 so virDomainDeviceInfoIterate() will exit */
return -1;
return 0;
}
static int
qemuAssignSpaprVIOAddress(virDomainDefPtr def, virDomainDeviceInfoPtr info,
unsigned long long default_reg)
{
bool user_reg;
int ret;
if (info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_SPAPRVIO)
return 0;
/* Check if the user has assigned the reg already, if so use it */
user_reg = info->addr.spaprvio.has_reg;
if (!user_reg) {
info->addr.spaprvio.reg = default_reg;
info->addr.spaprvio.has_reg = true;
}
ret = virDomainDeviceInfoIterate(def, qemuSpaprVIOFindByReg, info);
while (ret != 0) {
if (user_reg) {
virReportError(VIR_ERR_XML_ERROR,
_("spapr-vio address %#llx already in use"),
info->addr.spaprvio.reg);
return -EEXIST;
}
/* We assigned the reg, so try a new value */
info->addr.spaprvio.reg += 0x1000;
ret = virDomainDeviceInfoIterate(def, qemuSpaprVIOFindByReg, info);
}
return 0;
}
static int
qemuDomainAssignVirtioSerialAddresses(virDomainDefPtr def,
virDomainObjPtr obj)
{
int ret = -1;
size_t i;
virDomainVirtioSerialAddrSetPtr addrs = NULL;
qemuDomainObjPrivatePtr priv = NULL;
if (!(addrs = virDomainVirtioSerialAddrSetCreate()))
goto cleanup;
if (virDomainVirtioSerialAddrSetAddControllers(addrs, def) < 0)
goto cleanup;
if (virDomainDeviceInfoIterate(def, virDomainVirtioSerialAddrReserve,
addrs) < 0)
goto cleanup;
VIR_DEBUG("Finished reserving existing ports");
for (i = 0; i < def->nconsoles; i++) {
virDomainChrDefPtr chr = def->consoles[i];
if (chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE &&
chr->targetType == VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_VIRTIO &&
!virDomainVirtioSerialAddrIsComplete(&chr->info) &&
virDomainVirtioSerialAddrAutoAssign(def, addrs, &chr->info, true) < 0)
goto cleanup;
}
for (i = 0; i < def->nchannels; i++) {
virDomainChrDefPtr chr = def->channels[i];
if (chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_CHANNEL &&
chr->targetType == VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO &&
!virDomainVirtioSerialAddrIsComplete(&chr->info) &&
virDomainVirtioSerialAddrAutoAssign(def, addrs, &chr->info, false) < 0)
goto cleanup;
}
if (obj && obj->privateData) {
priv = obj->privateData;
/* if this is the live domain object, we persist the addresses */
virDomainVirtioSerialAddrSetFree(priv->vioserialaddrs);
priv->persistentAddrs = 1;
priv->vioserialaddrs = addrs;
addrs = NULL;
}
ret = 0;
cleanup:
virDomainVirtioSerialAddrSetFree(addrs);
return ret;
}
int qemuDomainAssignSpaprVIOAddresses(virDomainDefPtr def,
virQEMUCapsPtr qemuCaps)
{
size_t i;
int ret = -1;
int model;
/* Default values match QEMU. See spapr_(llan|vscsi|vty).c */
for (i = 0; i < def->nnets; i++) {
if (def->nets[i]->model &&
STREQ(def->nets[i]->model, "spapr-vlan"))
def->nets[i]->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_SPAPRVIO;
if (qemuAssignSpaprVIOAddress(def, &def->nets[i]->info,
VIO_ADDR_NET) < 0)
goto cleanup;
}
for (i = 0; i < def->ncontrollers; i++) {
model = def->controllers[i]->model;
if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_SCSI) {
if (qemuSetSCSIControllerModel(def, qemuCaps, &model) < 0)
goto cleanup;
}
if (model == VIR_DOMAIN_CONTROLLER_MODEL_SCSI_IBMVSCSI &&
def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_SCSI)
def->controllers[i]->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_SPAPRVIO;
if (qemuAssignSpaprVIOAddress(def, &def->controllers[i]->info,
VIO_ADDR_SCSI) < 0)
goto cleanup;
}
for (i = 0; i < def->nserials; i++) {
if (def->serials[i]->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL &&
ARCH_IS_PPC64(def->os.arch) &&
STRPREFIX(def->os.machine, "pseries"))
def->serials[i]->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_SPAPRVIO;
if (qemuAssignSpaprVIOAddress(def, &def->serials[i]->info,
VIO_ADDR_SERIAL) < 0)
goto cleanup;
}
if (def->nvram) {
if (ARCH_IS_PPC64(def->os.arch) &&
STRPREFIX(def->os.machine, "pseries"))
def->nvram->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_SPAPRVIO;
if (qemuAssignSpaprVIOAddress(def, &def->nvram->info,
VIO_ADDR_NVRAM) < 0)
goto cleanup;
}
/* No other devices are currently supported on spapr-vio */
ret = 0;
cleanup:
return ret;
}
static int
qemuCollectPCIAddress(virDomainDefPtr def ATTRIBUTE_UNUSED,
virDomainDeviceDefPtr device,
virDomainDeviceInfoPtr info,
void *opaque)
{
virDomainPCIAddressSetPtr addrs = opaque;
int ret = -1;
virDevicePCIAddressPtr addr = &info->addr.pci;
bool entireSlot;
/* flags may be changed from default below */
virDomainPCIConnectFlags flags = (VIR_PCI_CONNECT_HOTPLUGGABLE |
VIR_PCI_CONNECT_TYPE_PCI);
if ((info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI)
|| ((device->type == VIR_DOMAIN_DEVICE_HOSTDEV) &&
(device->data.hostdev->parent.type != VIR_DOMAIN_DEVICE_NONE))) {
/* If a hostdev has a parent, its info will be a part of the
* parent, and will have its address collected during the scan
* of the parent's device type.
*/
return 0;
}
/* Change flags according to differing requirements of different
* devices.
*/
switch (device->type) {
case VIR_DOMAIN_DEVICE_CONTROLLER:
switch (device->data.controller->type) {
case VIR_DOMAIN_CONTROLLER_TYPE_PCI:
switch (device->data.controller->model) {
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_BRIDGE:
/* pci-bridge needs a PCI slot, but it isn't
* hot-pluggable, so it doesn't need a hot-pluggable slot.
*/
flags = VIR_PCI_CONNECT_TYPE_PCI;
break;
case VIR_DOMAIN_CONTROLLER_MODEL_DMI_TO_PCI_BRIDGE:
/* pci-bridge needs a PCIe slot, but it isn't
* hot-pluggable, so it doesn't need a hot-pluggable slot.
*/
flags = VIR_PCI_CONNECT_TYPE_PCIE;
break;
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT_PORT:
/* pcie-root-port can only connect to pcie-root, isn't
* hot-pluggable
*/
flags = VIR_PCI_CONNECT_TYPE_PCIE_ROOT;
break;
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_UPSTREAM_PORT:
/* pcie-switch can only connect to a true
* pcie bus, and can't be hot-plugged.
*/
flags = VIR_PCI_CONNECT_TYPE_PCIE_PORT;
break;
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_DOWNSTREAM_PORT:
/* pcie-switch-downstream-port can only connect to a
* pcie-switch-upstream-port, and can't be hot-plugged.
*/
flags = VIR_PCI_CONNECT_TYPE_PCIE_SWITCH;
break;
default:
break;
}
break;
case VIR_DOMAIN_CONTROLLER_TYPE_SATA:
/* SATA controllers aren't hot-plugged, and can be put in
* either a PCI or PCIe slot
*/
flags = VIR_PCI_CONNECT_TYPE_PCI | VIR_PCI_CONNECT_TYPE_PCIE;
break;
case VIR_DOMAIN_CONTROLLER_TYPE_USB:
/* allow UHCI and EHCI controllers to be manually placed on
* the PCIe bus (but don't put them there automatically)
*/
switch (device->data.controller->model) {
case VIR_DOMAIN_CONTROLLER_MODEL_USB_EHCI:
case VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_EHCI1:
case VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_UHCI1:
case VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_UHCI2:
case VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_UHCI3:
case VIR_DOMAIN_CONTROLLER_MODEL_USB_VT82C686B_UHCI:
flags = VIR_PCI_CONNECT_TYPE_PCI;
break;
case VIR_DOMAIN_CONTROLLER_MODEL_USB_NEC_XHCI:
/* should this be PCIE-only? Or do we need to allow PCI
* for backward compatibility?
*/
flags = VIR_PCI_CONNECT_TYPE_PCI | VIR_PCI_CONNECT_TYPE_PCIE;
break;
case VIR_DOMAIN_CONTROLLER_MODEL_USB_PCI_OHCI:
case VIR_DOMAIN_CONTROLLER_MODEL_USB_PIIX3_UHCI:
case VIR_DOMAIN_CONTROLLER_MODEL_USB_PIIX4_UHCI:
/* Allow these for PCI only */
break;
}
}
break;
case VIR_DOMAIN_DEVICE_SOUND:
switch (device->data.sound->model) {
case VIR_DOMAIN_SOUND_MODEL_ICH6:
case VIR_DOMAIN_SOUND_MODEL_ICH9:
flags = VIR_PCI_CONNECT_TYPE_PCI;
break;
}
break;
case VIR_DOMAIN_DEVICE_VIDEO:
/* video cards aren't hot-plugged, and can be put in either a
* PCI or PCIe slot
*/
flags = VIR_PCI_CONNECT_TYPE_PCI | VIR_PCI_CONNECT_TYPE_PCIE;
break;
}
/* Ignore implicit controllers on slot 0:0:1.0:
* implicit IDE controller on 0:0:1.1 (no qemu command line)
* implicit USB controller on 0:0:1.2 (-usb)
*
* If the machine does have a PCI bus, they will get reserved
* in qemuAssignDevicePCISlots().
*/
/* These are the IDE and USB controllers in the PIIX3, hardcoded
* to bus 0 slot 1. They cannot be attached to a PCIe slot, only
* PCI.
*/
if (device->type == VIR_DOMAIN_DEVICE_CONTROLLER && addr->domain == 0 &&
addr->bus == 0 && addr->slot == 1) {
virDomainControllerDefPtr cont = device->data.controller;
if ((cont->type == VIR_DOMAIN_CONTROLLER_TYPE_IDE && cont->idx == 0 &&
addr->function == 1) ||
(cont->type == VIR_DOMAIN_CONTROLLER_TYPE_USB && cont->idx == 0 &&
(cont->model == VIR_DOMAIN_CONTROLLER_MODEL_USB_PIIX3_UHCI ||
cont->model == -1) && addr->function == 2)) {
/* Note the check for nbuses > 0 - if there are no PCI
* buses, we skip this check. This is a quirk required for
* some machinetypes such as s390, which pretend to have a
* PCI bus for long enough to generate the "-usb" on the
* commandline, but that don't really care if a PCI bus
* actually exists. */
if (addrs->nbuses > 0 &&
!(addrs->buses[0].flags & VIR_PCI_CONNECT_TYPE_PCI)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Bus 0 must be PCI for integrated PIIX3 "
"USB or IDE controllers"));
return -1;
} else {
return 0;
}
}
}
entireSlot = (addr->function == 0 &&
addr->multi != VIR_TRISTATE_SWITCH_ON);
if (virDomainPCIAddressReserveAddr(addrs, addr, flags,
entireSlot, true) < 0)
goto cleanup;
ret = 0;
cleanup:
return ret;
}
static bool
qemuDomainSupportsPCI(virDomainDefPtr def, virQEMUCapsPtr qemuCaps)
{
if ((def->os.arch != VIR_ARCH_ARMV7L) && (def->os.arch != VIR_ARCH_AARCH64))
return true;
if (STREQ(def->os.machine, "versatilepb"))
return true;
if ((STREQ(def->os.machine, "virt") ||
STRPREFIX(def->os.machine, "virt-")) &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_GPEX))
return true;
return false;
}
static bool
qemuDomainPCIBusFullyReserved(virDomainPCIAddressBusPtr bus)
{
size_t i;
for (i = bus->minSlot; i <= bus->maxSlot; i++)
if (!bus->slots[i])
return false;
return true;
}
int qemuDomainAssignAddresses(virDomainDefPtr def,
virQEMUCapsPtr qemuCaps,
virDomainObjPtr obj)
{
int rc;
rc = qemuDomainAssignVirtioSerialAddresses(def, obj);
if (rc)
return rc;
rc = qemuDomainAssignSpaprVIOAddresses(def, qemuCaps);
if (rc)
return rc;
rc = qemuDomainAssignS390Addresses(def, qemuCaps, obj);
if (rc)
return rc;
rc = qemuDomainAssignARMVirtioMMIOAddresses(def, qemuCaps);
if (rc)
return rc;
return qemuDomainAssignPCIAddresses(def, qemuCaps, obj);
}
virDomainPCIAddressSetPtr
qemuDomainPCIAddressSetCreate(virDomainDefPtr def,
unsigned int nbuses,
bool dryRun)
{
virDomainPCIAddressSetPtr addrs;
size_t i;
if ((addrs = virDomainPCIAddressSetAlloc(nbuses)) == NULL)
return NULL;
addrs->nbuses = nbuses;
addrs->dryRun = dryRun;
/* As a safety measure, set default model='pci-root' for first pci
* controller and 'pci-bridge' for all subsequent. After setting
* those defaults, then scan the config and set the actual model
* for all addrs[idx]->bus that already have a corresponding
* controller in the config.
*
*/
if (nbuses > 0)
virDomainPCIAddressBusSetModel(&addrs->buses[0],
VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT);
for (i = 1; i < nbuses; i++) {
virDomainPCIAddressBusSetModel(&addrs->buses[i],
VIR_DOMAIN_CONTROLLER_MODEL_PCI_BRIDGE);
}
for (i = 0; i < def->ncontrollers; i++) {
size_t idx = def->controllers[i]->idx;
if (def->controllers[i]->type != VIR_DOMAIN_CONTROLLER_TYPE_PCI)
continue;
if (idx >= addrs->nbuses) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Inappropriate new pci controller index %zu "
"not found in addrs"), idx);
goto error;
}
if (virDomainPCIAddressBusSetModel(&addrs->buses[idx],
def->controllers[i]->model) < 0)
goto error;
}
if (virDomainDeviceInfoIterate(def, qemuCollectPCIAddress, addrs) < 0)
goto error;
return addrs;
error:
virDomainPCIAddressSetFree(addrs);
return NULL;
}
void
qemuDomainReleaseDeviceAddress(virDomainObjPtr vm,
virDomainDeviceInfoPtr info,
const char *devstr)
{
qemuDomainObjPrivatePtr priv = vm->privateData;
if (!devstr)
devstr = info->alias;
if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW &&
qemuDomainMachineIsS390CCW(vm->def) &&
virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_VIRTIO_CCW) &&
virDomainCCWAddressReleaseAddr(priv->ccwaddrs, info) < 0)
VIR_WARN("Unable to release CCW address on %s",
NULLSTR(devstr));
else if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI &&
virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_DEVICE) &&
virDomainPCIAddressReleaseSlot(priv->pciaddrs,
&info->addr.pci) < 0)
VIR_WARN("Unable to release PCI address on %s",
NULLSTR(devstr));
if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_SERIAL &&
virDomainVirtioSerialAddrRelease(priv->vioserialaddrs, info) < 0)
VIR_WARN("Unable to release virtio-serial address on %s",
NULLSTR(devstr));
}
#define IS_USB2_CONTROLLER(ctrl) \
(((ctrl)->type == VIR_DOMAIN_CONTROLLER_TYPE_USB) && \
((ctrl)->model == VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_EHCI1 || \
(ctrl)->model == VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_UHCI1 || \
(ctrl)->model == VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_UHCI2 || \
(ctrl)->model == VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_UHCI3))
static int
qemuValidateDevicePCISlotsPIIX3(virDomainDefPtr def,
virQEMUCapsPtr qemuCaps,
virDomainPCIAddressSetPtr addrs)
{
int ret = -1;
size_t i;
virDevicePCIAddress tmp_addr;
bool qemuDeviceVideoUsable = virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VIDEO_PRIMARY);
char *addrStr = NULL;
virDomainPCIConnectFlags flags = VIR_PCI_CONNECT_HOTPLUGGABLE | VIR_PCI_CONNECT_TYPE_PCI;
/* Verify that first IDE and USB controllers (if any) is on the PIIX3, fn 1 */
for (i = 0; i < def->ncontrollers; i++) {
/* First IDE controller lives on the PIIX3 at slot=1, function=1 */
if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_IDE &&
def->controllers[i]->idx == 0) {
if (def->controllers[i]->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) {
if (def->controllers[i]->info.addr.pci.domain != 0 ||
def->controllers[i]->info.addr.pci.bus != 0 ||
def->controllers[i]->info.addr.pci.slot != 1 ||
def->controllers[i]->info.addr.pci.function != 1) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Primary IDE controller must have PCI address 0:0:1.1"));
goto cleanup;
}
} else {
def->controllers[i]->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI;
def->controllers[i]->info.addr.pci.domain = 0;
def->controllers[i]->info.addr.pci.bus = 0;
def->controllers[i]->info.addr.pci.slot = 1;
def->controllers[i]->info.addr.pci.function = 1;
}
} else if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_USB &&
def->controllers[i]->idx == 0 &&
(def->controllers[i]->model == VIR_DOMAIN_CONTROLLER_MODEL_USB_PIIX3_UHCI ||
def->controllers[i]->model == -1)) {
if (def->controllers[i]->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) {
if (def->controllers[i]->info.addr.pci.domain != 0 ||
def->controllers[i]->info.addr.pci.bus != 0 ||
def->controllers[i]->info.addr.pci.slot != 1 ||
def->controllers[i]->info.addr.pci.function != 2) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("PIIX3 USB controller must have PCI address 0:0:1.2"));
goto cleanup;
}
} else {
def->controllers[i]->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI;
def->controllers[i]->info.addr.pci.domain = 0;
def->controllers[i]->info.addr.pci.bus = 0;
def->controllers[i]->info.addr.pci.slot = 1;
def->controllers[i]->info.addr.pci.function = 2;
}
}
}
/* PIIX3 (ISA bridge, IDE controller, something else unknown, USB controller)
* hardcoded slot=1, multifunction device
*/
if (addrs->nbuses) {
memset(&tmp_addr, 0, sizeof(tmp_addr));
tmp_addr.slot = 1;
if (virDomainPCIAddressReserveSlot(addrs, &tmp_addr, flags) < 0)
goto cleanup;
}
if (def->nvideos > 0) {
/* Because the PIIX3 integrated IDE/USB controllers are
* already at slot 1, when qemu looks for the first free slot
* to place the VGA controller (which is always the first
* device added after integrated devices), it *always* ends up
* at slot 2.
*/
virDomainVideoDefPtr primaryVideo = def->videos[0];
if (primaryVideo->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) {
memset(&tmp_addr, 0, sizeof(tmp_addr));
tmp_addr.slot = 2;
if (!(addrStr = virDomainPCIAddressAsString(&tmp_addr)))
goto cleanup;
if (!virDomainPCIAddressValidate(addrs, &tmp_addr,
addrStr, flags, false))
goto cleanup;
if (virDomainPCIAddressSlotInUse(addrs, &tmp_addr)) {
if (qemuDeviceVideoUsable) {
if (virDomainPCIAddressReserveNextSlot(addrs,
&primaryVideo->info,
flags) < 0)
goto cleanup;
} else {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("PCI address 0:0:2.0 is in use, "
"QEMU needs it for primary video"));
goto cleanup;
}
} else {
if (virDomainPCIAddressReserveSlot(addrs, &tmp_addr, flags) < 0)
goto cleanup;
primaryVideo->info.addr.pci = tmp_addr;
primaryVideo->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI;
}
} else if (!qemuDeviceVideoUsable) {
if (primaryVideo->info.addr.pci.domain != 0 ||
primaryVideo->info.addr.pci.bus != 0 ||
primaryVideo->info.addr.pci.slot != 2 ||
primaryVideo->info.addr.pci.function != 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Primary video card must have PCI address 0:0:2.0"));
goto cleanup;
}
/* If TYPE == PCI, then qemuCollectPCIAddress() function
* has already reserved the address, so we must skip */
}
} else if (addrs->nbuses && !qemuDeviceVideoUsable) {
memset(&tmp_addr, 0, sizeof(tmp_addr));
tmp_addr.slot = 2;
if (virDomainPCIAddressSlotInUse(addrs, &tmp_addr)) {
VIR_DEBUG("PCI address 0:0:2.0 in use, future addition of a video"
" device will not be possible without manual"
" intervention");
} else if (virDomainPCIAddressReserveSlot(addrs, &tmp_addr, flags) < 0) {
goto cleanup;
}
}
ret = 0;
cleanup:
VIR_FREE(addrStr);
return ret;
}
static int
qemuDomainValidateDevicePCISlotsQ35(virDomainDefPtr def,
virQEMUCapsPtr qemuCaps,
virDomainPCIAddressSetPtr addrs)
{
int ret = -1;
size_t i;
virDevicePCIAddress tmp_addr;
bool qemuDeviceVideoUsable = virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VIDEO_PRIMARY);
char *addrStr = NULL;
virDomainPCIConnectFlags flags = VIR_PCI_CONNECT_TYPE_PCIE;
for (i = 0; i < def->ncontrollers; i++) {
switch (def->controllers[i]->type) {
case VIR_DOMAIN_CONTROLLER_TYPE_SATA:
/* Verify that the first SATA controller is at 00:1F.2 the
* q35 machine type *always* has a SATA controller at this
* address.
*/
if (def->controllers[i]->idx == 0) {
if (def->controllers[i]->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) {
if (def->controllers[i]->info.addr.pci.domain != 0 ||
def->controllers[i]->info.addr.pci.bus != 0 ||
def->controllers[i]->info.addr.pci.slot != 0x1F ||
def->controllers[i]->info.addr.pci.function != 2) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Primary SATA controller must have PCI address 0:0:1f.2"));
goto cleanup;
}
} else {
def->controllers[i]->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI;
def->controllers[i]->info.addr.pci.domain = 0;
def->controllers[i]->info.addr.pci.bus = 0;
def->controllers[i]->info.addr.pci.slot = 0x1F;
def->controllers[i]->info.addr.pci.function = 2;
}
}
break;
case VIR_DOMAIN_CONTROLLER_TYPE_PCI:
if (def->controllers[i]->model == VIR_DOMAIN_CONTROLLER_MODEL_DMI_TO_PCI_BRIDGE &&
def->controllers[i]->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) {
/* Try to assign this bridge to 00:1E.0 (because that
* is its standard location on real hardware) unless
* it's already taken, but don't insist on it.
*/
memset(&tmp_addr, 0, sizeof(tmp_addr));
tmp_addr.slot = 0x1E;
if (!virDomainPCIAddressSlotInUse(addrs, &tmp_addr)) {
if (virDomainPCIAddressReserveAddr(addrs, &tmp_addr,
flags, true, false) < 0)
goto cleanup;
def->controllers[i]->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI;
def->controllers[i]->info.addr.pci.domain = 0;
def->controllers[i]->info.addr.pci.bus = 0;
def->controllers[i]->info.addr.pci.slot = 0x1E;
def->controllers[i]->info.addr.pci.function = 0;
}
}
break;
}
}
/* Reserve slot 0x1F function 0 (ISA bridge, not in config model)
* and function 3 (SMBus, also not (yet) in config model). As with
* the SATA controller, these devices are always present in a q35
* machine; there is no way to not have them.
*/
if (addrs->nbuses) {
memset(&tmp_addr, 0, sizeof(tmp_addr));
tmp_addr.slot = 0x1F;
tmp_addr.function = 0;
tmp_addr.multi = 1;
if (virDomainPCIAddressReserveAddr(addrs, &tmp_addr, flags,
false, false) < 0)
goto cleanup;
tmp_addr.function = 3;
tmp_addr.multi = 0;
if (virDomainPCIAddressReserveAddr(addrs, &tmp_addr, flags,
false, false) < 0)
goto cleanup;
}
if (def->nvideos > 0) {
/* NB: unlike the pc machinetypes, on q35 machinetypes the
* integrated devices are at slot 0x1f, so when qemu looks for
* the first free lot for the first VGA, it will always be at
* slot 1 (which was used up by the integrated PIIX3 devices
* on pc machinetypes).
*/
virDomainVideoDefPtr primaryVideo = def->videos[0];
if (primaryVideo->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) {
memset(&tmp_addr, 0, sizeof(tmp_addr));
tmp_addr.slot = 1;
if (!(addrStr = virDomainPCIAddressAsString(&tmp_addr)))
goto cleanup;
if (!virDomainPCIAddressValidate(addrs, &tmp_addr,
addrStr, flags, false))
goto cleanup;
if (virDomainPCIAddressSlotInUse(addrs, &tmp_addr)) {
if (qemuDeviceVideoUsable) {
if (virDomainPCIAddressReserveNextSlot(addrs,
&primaryVideo->info,
flags) < 0)
goto cleanup;
} else {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("PCI address 0:0:1.0 is in use, "
"QEMU needs it for primary video"));
goto cleanup;
}
} else {
if (virDomainPCIAddressReserveSlot(addrs, &tmp_addr, flags) < 0)
goto cleanup;
primaryVideo->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI;
primaryVideo->info.addr.pci = tmp_addr;
}
} else if (!qemuDeviceVideoUsable) {
if (primaryVideo->info.addr.pci.domain != 0 ||
primaryVideo->info.addr.pci.bus != 0 ||
primaryVideo->info.addr.pci.slot != 1 ||
primaryVideo->info.addr.pci.function != 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Primary video card must have PCI address 0:0:1.0"));
goto cleanup;
}
/* If TYPE == PCI, then qemuCollectPCIAddress() function
* has already reserved the address, so we must skip */
}
} else if (addrs->nbuses && !qemuDeviceVideoUsable) {
memset(&tmp_addr, 0, sizeof(tmp_addr));
tmp_addr.slot = 1;
if (virDomainPCIAddressSlotInUse(addrs, &tmp_addr)) {
VIR_DEBUG("PCI address 0:0:1.0 in use, future addition of a video"
" device will not be possible without manual"
" intervention");
virResetLastError();
} else if (virDomainPCIAddressReserveSlot(addrs, &tmp_addr, flags) < 0) {
goto cleanup;
}
}
ret = 0;
cleanup:
VIR_FREE(addrStr);
return ret;
}
static int
qemuValidateDevicePCISlotsChipsets(virDomainDefPtr def,
virQEMUCapsPtr qemuCaps,
virDomainPCIAddressSetPtr addrs)
{
if (qemuDomainMachineIsI440FX(def) &&
qemuValidateDevicePCISlotsPIIX3(def, qemuCaps, addrs) < 0) {
return -1;
}
if (qemuDomainMachineIsQ35(def) &&
qemuDomainValidateDevicePCISlotsQ35(def, qemuCaps, addrs) < 0) {
return -1;
}
return 0;
}
int
qemuDomainAssignPCIAddresses(virDomainDefPtr def,
virQEMUCapsPtr qemuCaps,
virDomainObjPtr obj)
{
int ret = -1;
virDomainPCIAddressSetPtr addrs = NULL;
qemuDomainObjPrivatePtr priv = NULL;
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) {
int max_idx = -1;
int nbuses = 0;
size_t i;
int rv;
bool buses_reserved = true;
virDomainPCIConnectFlags flags = VIR_PCI_CONNECT_TYPE_PCI;
for (i = 0; i < def->ncontrollers; i++) {
if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_PCI) {
if ((int) def->controllers[i]->idx > max_idx)
max_idx = def->controllers[i]->idx;
}
}
nbuses = max_idx + 1;
if (nbuses > 0 &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_PCI_BRIDGE)) {
virDomainDeviceInfo info;
/* 1st pass to figure out how many PCI bridges we need */
if (!(addrs = qemuDomainPCIAddressSetCreate(def, nbuses, true)))
goto cleanup;
if (qemuValidateDevicePCISlotsChipsets(def, qemuCaps, addrs) < 0)
goto cleanup;
for (i = 0; i < addrs->nbuses; i++) {
if (!qemuDomainPCIBusFullyReserved(&addrs->buses[i]))
buses_reserved = false;
}
/* Reserve 1 extra slot for a (potential) bridge only if buses
* are not fully reserved yet
*/
if (!buses_reserved &&
virDomainPCIAddressReserveNextSlot(addrs, &info, flags) < 0)
goto cleanup;
if (qemuAssignDevicePCISlots(def, addrs) < 0)
goto cleanup;
for (i = 1; i < addrs->nbuses; i++) {
virDomainPCIAddressBusPtr bus = &addrs->buses[i];
if ((rv = virDomainDefMaybeAddController(
def, VIR_DOMAIN_CONTROLLER_TYPE_PCI,
i, bus->model)) < 0)
goto cleanup;
/* If we added a new bridge, we will need one more address */
if (rv > 0 &&
virDomainPCIAddressReserveNextSlot(addrs, &info, flags) < 0)
goto cleanup;
}
nbuses = addrs->nbuses;
virDomainPCIAddressSetFree(addrs);
addrs = NULL;
} else if (max_idx > 0) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("PCI bridges are not supported "
"by this QEMU binary"));
goto cleanup;
}
if (!(addrs = qemuDomainPCIAddressSetCreate(def, nbuses, false)))
goto cleanup;
if (qemuDomainSupportsPCI(def, qemuCaps)) {
if (qemuValidateDevicePCISlotsChipsets(def, qemuCaps, addrs) < 0)
goto cleanup;
if (qemuAssignDevicePCISlots(def, addrs) < 0)
goto cleanup;
for (i = 0; i < def->ncontrollers; i++) {
virDomainControllerDefPtr cont = def->controllers[i];
int idx = cont->idx;
virDevicePCIAddressPtr addr;
virDomainPCIControllerOptsPtr options;
if (cont->type != VIR_DOMAIN_CONTROLLER_TYPE_PCI)
continue;
addr = &cont->info.addr.pci;
options = &cont->opts.pciopts;
/* set defaults for any other auto-generated config
* options for this controller that haven't been
* specified in config.
*/
switch ((virDomainControllerModelPCI)cont->model) {
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_BRIDGE:
if (options->modelName == VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_NONE)
options->modelName = VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_PCI_BRIDGE;
if (options->chassisNr == -1)
options->chassisNr = cont->idx;
break;
case VIR_DOMAIN_CONTROLLER_MODEL_DMI_TO_PCI_BRIDGE:
if (options->modelName == VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_NONE)
options->modelName = VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_I82801B11_BRIDGE;
break;
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT_PORT:
if (options->modelName == VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_NONE)
options->modelName = VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_IOH3420;
if (options->chassis == -1)
options->chassis = cont->idx;
if (options->port == -1)
options->port = (addr->slot << 3) + addr->function;
break;
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_UPSTREAM_PORT:
if (options->modelName == VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_NONE)
options->modelName = VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_X3130_UPSTREAM;
break;
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_DOWNSTREAM_PORT:
if (options->modelName == VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_NONE)
options->modelName = VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_XIO3130_DOWNSTREAM;
if (options->chassis == -1)
options->chassis = cont->idx;
if (options->port == -1)
options->port = addr->slot;
break;
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT:
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT:
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_LAST:
break;
}
/* check if every PCI bridge controller's ID is greater than
* the bus it is placed onto
*/
if (cont->model == VIR_DOMAIN_CONTROLLER_MODEL_PCI_BRIDGE &&
idx <= addr->bus) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("failed to create PCI bridge "
"on bus %d: too many devices with fixed "
"addresses"),
addr->bus);
goto cleanup;
}
}
}
}
if (obj && obj->privateData) {
priv = obj->privateData;
if (addrs) {
/* if this is the live domain object, we persist the PCI addresses*/
virDomainPCIAddressSetFree(priv->pciaddrs);
priv->persistentAddrs = 1;
priv->pciaddrs = addrs;
addrs = NULL;
} else {
priv->persistentAddrs = 0;
}
}
ret = 0;
cleanup:
virDomainPCIAddressSetFree(addrs);
return ret;
}
/*
* This assigns static PCI slots to all configured devices.
* The ordering here is chosen to match the ordering used
* with old QEMU < 0.12, so that if a user updates a QEMU
* host from old QEMU to QEMU >= 0.12, their guests should
* get PCI addresses in the same order as before.
*
* NB, if they previously hotplugged devices then all bets
* are off. Hotplug for old QEMU was unfixably broken wrt
* to stable PCI addressing.
*
* Order is:
*
* - Host bridge (slot 0)
* - PIIX3 ISA bridge, IDE controller, something else unknown, USB controller (slot 1)
* - Video (slot 2)
*
* - These integrated devices were already added by
* qemuValidateDevicePCISlotsChipsets invoked right before this function
*
* Incrementally assign slots from 3 onwards:
*
* - Net
* - Sound
* - SCSI controllers
* - VirtIO block
* - VirtIO balloon
* - Host device passthrough
* - Watchdog
* - pci serial devices
*
* Prior to this function being invoked, qemuCollectPCIAddress() will have
* added all existing PCI addresses from the 'def' to 'addrs'. Thus this
* function must only try to reserve addresses if info.type == NONE and
* skip over info.type == PCI
*/
int
qemuAssignDevicePCISlots(virDomainDefPtr def,
virDomainPCIAddressSetPtr addrs)
{
size_t i, j;
virDomainPCIConnectFlags flags;
virDevicePCIAddress tmp_addr;
/* PCI controllers */
for (i = 0; i < def->ncontrollers; i++) {
if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_PCI) {
if (def->controllers[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE)
continue;
switch (def->controllers[i]->model) {
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT:
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT:
/* pci-root and pcie-root are implicit in the machine,
* and needs no address */
continue;
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_BRIDGE:
/* pci-bridge doesn't require hot-plug
* (although it does provide hot-plug in its slots)
*/
flags = VIR_PCI_CONNECT_TYPE_PCI;
break;
case VIR_DOMAIN_CONTROLLER_MODEL_DMI_TO_PCI_BRIDGE:
/* dmi-to-pci-bridge requires a non-hotplug PCIe
* slot
*/
flags = VIR_PCI_CONNECT_TYPE_PCIE;
break;
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT_PORT:
/* pcie-root-port can only plug into pcie-root */
flags = VIR_PCI_CONNECT_TYPE_PCIE_ROOT;
break;
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_UPSTREAM_PORT:
/* pcie-switch really does need a real PCIe
* port, but it doesn't need to be pcie-root
*/
flags = VIR_PCI_CONNECT_TYPE_PCIE_PORT;
break;
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_DOWNSTREAM_PORT:
/* pcie-switch-port can only plug into pcie-switch */
flags = VIR_PCI_CONNECT_TYPE_PCIE_SWITCH;
break;
default:
flags = VIR_PCI_CONNECT_HOTPLUGGABLE | VIR_PCI_CONNECT_TYPE_PCI;
break;
}
if (virDomainPCIAddressReserveNextSlot(addrs,
&def->controllers[i]->info,
flags) < 0)
goto error;
}
}
flags = VIR_PCI_CONNECT_HOTPLUGGABLE | VIR_PCI_CONNECT_TYPE_PCI;
for (i = 0; i < def->nfss; i++) {
if (def->fss[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE)
continue;
/* Only support VirtIO-9p-pci so far. If that changes,
* we might need to skip devices here */
if (virDomainPCIAddressReserveNextSlot(addrs, &def->fss[i]->info,
flags) < 0)
goto error;
}
/* Network interfaces */
for (i = 0; i < def->nnets; i++) {
/* type='hostdev' network devices might be USB, and are also
* in hostdevs list anyway, so handle them with other hostdevs
* instead of here.
*/
if ((def->nets[i]->type == VIR_DOMAIN_NET_TYPE_HOSTDEV) ||
(def->nets[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE)) {
continue;
}
if (virDomainPCIAddressReserveNextSlot(addrs, &def->nets[i]->info,
flags) < 0)
goto error;
}
/* Sound cards */
for (i = 0; i < def->nsounds; i++) {
if (def->sounds[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE)
continue;
/* Skip ISA sound card, PCSPK and usb-audio */
if (def->sounds[i]->model == VIR_DOMAIN_SOUND_MODEL_SB16 ||
def->sounds[i]->model == VIR_DOMAIN_SOUND_MODEL_PCSPK ||
def->sounds[i]->model == VIR_DOMAIN_SOUND_MODEL_USB)
continue;
if (virDomainPCIAddressReserveNextSlot(addrs, &def->sounds[i]->info,
flags) < 0)
goto error;
}
/* Device controllers (SCSI, USB, but not IDE, FDC or CCID) */
for (i = 0; i < def->ncontrollers; i++) {
/* PCI controllers have been dealt with earlier */
if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_PCI)
continue;
/* USB controller model 'none' doesn't need a PCI address */
if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_USB &&
def->controllers[i]->model == VIR_DOMAIN_CONTROLLER_MODEL_USB_NONE)
continue;
/* FDC lives behind the ISA bridge; CCID is a usb device */
if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_FDC ||
def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_CCID)
continue;
/* First IDE controller lives on the PIIX3 at slot=1, function=1,
dealt with earlier on*/
if (def->controllers[i]->type == VIR_DOMAIN_CONTROLLER_TYPE_IDE &&
def->controllers[i]->idx == 0)
continue;
if (def->controllers[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE)
continue;
/* USB2 needs special handling to put all companions in the same slot */
if (IS_USB2_CONTROLLER(def->controllers[i])) {
virDevicePCIAddress addr = { 0, 0, 0, 0, false };
memset(&tmp_addr, 0, sizeof(tmp_addr));
for (j = 0; j < i; j++) {
if (IS_USB2_CONTROLLER(def->controllers[j]) &&
def->controllers[j]->idx == def->controllers[i]->idx) {
addr = def->controllers[j]->info.addr.pci;
break;
}
}
switch (def->controllers[i]->model) {
case VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_EHCI1:
addr.function = 7;
break;
case VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_UHCI1:
addr.function = 0;
addr.multi = VIR_TRISTATE_SWITCH_ON;
break;
case VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_UHCI2:
addr.function = 1;
break;
case VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_UHCI3:
addr.function = 2;
break;
}
if (addr.slot == 0) {
/* This is the first part of the controller, so need
* to find a free slot & then reserve a function */
if (virDomainPCIAddressGetNextSlot(addrs, &tmp_addr, flags) < 0)
goto error;
addr.bus = tmp_addr.bus;
addr.slot = tmp_addr.slot;
addrs->lastaddr = addr;
addrs->lastaddr.function = 0;
addrs->lastaddr.multi = 0;
}
/* Finally we can reserve the slot+function */
if (virDomainPCIAddressReserveAddr(addrs, &addr, flags,
false, false) < 0)
goto error;
def->controllers[i]->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI;
def->controllers[i]->info.addr.pci = addr;
} else {
if (virDomainPCIAddressReserveNextSlot(addrs,
&def->controllers[i]->info,
flags) < 0)
goto error;
}
}
/* Disks (VirtIO only for now) */
for (i = 0; i < def->ndisks; i++) {
/* Only VirtIO disks use PCI addrs */
if (def->disks[i]->bus != VIR_DOMAIN_DISK_BUS_VIRTIO)
continue;
/* don't touch s390 devices */
if (def->disks[i]->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI ||
def->disks[i]->info.type ==
VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_S390 ||
def->disks[i]->info.type ==
VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW)
continue;
if (def->disks[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("virtio disk cannot have an address of type '%s'"),
virDomainDeviceAddressTypeToString(def->disks[i]->info.type));
goto error;
}
if (virDomainPCIAddressReserveNextSlot(addrs, &def->disks[i]->info,
flags) < 0)
goto error;
}
/* Host PCI devices */
for (i = 0; i < def->nhostdevs; i++) {
if (def->hostdevs[i]->info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE)
continue;
if (def->hostdevs[i]->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS ||
def->hostdevs[i]->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI)
continue;
if (virDomainPCIAddressReserveNextSlot(addrs,
def->hostdevs[i]->info,
flags) < 0)
goto error;
}
/* VirtIO balloon */
if (def->memballoon &&
def->memballoon->model == VIR_DOMAIN_MEMBALLOON_MODEL_VIRTIO &&
def->memballoon->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) {
if (virDomainPCIAddressReserveNextSlot(addrs,
&def->memballoon->info,
flags) < 0)
goto error;
}
/* VirtIO RNG */
for (i = 0; i < def->nrngs; i++) {
if (def->rngs[i]->model != VIR_DOMAIN_RNG_MODEL_VIRTIO ||
def->rngs[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE)
continue;
if (virDomainPCIAddressReserveNextSlot(addrs,
&def->rngs[i]->info, flags) < 0)
goto error;
}
/* A watchdog - check if it is a PCI device */
if (def->watchdog &&
def->watchdog->model == VIR_DOMAIN_WATCHDOG_MODEL_I6300ESB &&
def->watchdog->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) {
if (virDomainPCIAddressReserveNextSlot(addrs, &def->watchdog->info,
flags) < 0)
goto error;
}
/* Assign a PCI slot to the primary video card if there is not an
* assigned address. */
if (def->nvideos > 0 &&
def->videos[0]->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) {
if (virDomainPCIAddressReserveNextSlot(addrs, &def->videos[0]->info,
flags) < 0)
goto error;
}
/* Further non-primary video cards which have to be qxl type */
for (i = 1; i < def->nvideos; i++) {
if (def->videos[i]->type != VIR_DOMAIN_VIDEO_TYPE_QXL) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("non-primary video device must be type of 'qxl'"));
goto error;
}
if (def->videos[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE)
continue;
if (virDomainPCIAddressReserveNextSlot(addrs, &def->videos[i]->info,
flags) < 0)
goto error;
}
/* Shared Memory */
for (i = 0; i < def->nshmems; i++) {
if (def->shmems[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE)
continue;
if (virDomainPCIAddressReserveNextSlot(addrs,
&def->shmems[i]->info, flags) < 0)
goto error;
}
for (i = 0; i < def->ninputs; i++) {
/* Nada - none are PCI based (yet) */
}
for (i = 0; i < def->nparallels; i++) {
/* Nada - none are PCI based (yet) */
}
for (i = 0; i < def->nserials; i++) {
virDomainChrDefPtr chr = def->serials[i];
if (chr->targetType != VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_PCI)
continue;
if (chr->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE)
continue;
if (virDomainPCIAddressReserveNextSlot(addrs, &chr->info, flags) < 0)
goto error;
}
for (i = 0; i < def->nchannels; i++) {
/* Nada - none are PCI based (yet) */
}
for (i = 0; i < def->nhubs; i++) {
/* Nada - none are PCI based (yet) */
}
return 0;
error:
return -1;
}
static int
qemuBuildDeviceAddressStr(virBufferPtr buf,
virDomainDefPtr domainDef,
virDomainDeviceInfoPtr info,
virQEMUCapsPtr qemuCaps)
{
int ret = -1;
char *devStr = NULL;
const char *contAlias = NULL;
if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) {
size_t i;
if (!(devStr = virDomainPCIAddressAsString(&info->addr.pci)))
goto cleanup;
for (i = 0; i < domainDef->ncontrollers; i++) {
virDomainControllerDefPtr cont = domainDef->controllers[i];
if (cont->type == VIR_DOMAIN_CONTROLLER_TYPE_PCI &&
cont->idx == info->addr.pci.bus) {
contAlias = cont->info.alias;
if (!contAlias) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Device alias was not set for PCI "
"controller with index %u required "
"for device at address %s"),
info->addr.pci.bus, devStr);
goto cleanup;
}
break;
}
}
if (!contAlias) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Could not find PCI "
"controller with index %u required "
"for device at address %s"),
info->addr.pci.bus, devStr);
goto cleanup;
}
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_PCI_MULTIFUNCTION)) {
if (info->addr.pci.function != 0) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Only PCI device addresses with function=0 "
"are supported with this QEMU binary"));
goto cleanup;
}
if (info->addr.pci.multi == VIR_TRISTATE_SWITCH_ON) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("'multifunction=on' is not supported with "
"this QEMU binary"));
goto cleanup;
}
}
if (info->addr.pci.bus != 0 &&
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_PCI_BRIDGE)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Multiple PCI buses are not supported "
"with this QEMU binary"));
goto cleanup;
}
virBufferAsprintf(buf, ",bus=%s", contAlias);
if (info->addr.pci.multi == VIR_TRISTATE_SWITCH_ON)
virBufferAddLit(buf, ",multifunction=on");
else if (info->addr.pci.multi == VIR_TRISTATE_SWITCH_OFF)
virBufferAddLit(buf, ",multifunction=off");
virBufferAsprintf(buf, ",addr=0x%x", info->addr.pci.slot);
if (info->addr.pci.function != 0)
virBufferAsprintf(buf, ".0x%x", info->addr.pci.function);
} else if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB) {
if (!(contAlias = virDomainControllerAliasFind(domainDef,
VIR_DOMAIN_CONTROLLER_TYPE_USB,
info->addr.usb.bus)))
goto cleanup;
virBufferAsprintf(buf, ",bus=%s.0,port=%s", contAlias, info->addr.usb.port);
} else if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_SPAPRVIO) {
if (info->addr.spaprvio.has_reg)
virBufferAsprintf(buf, ",reg=0x%llx", info->addr.spaprvio.reg);
} else if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW) {
if (info->addr.ccw.assigned)
virBufferAsprintf(buf, ",devno=%x.%x.%04x",
info->addr.ccw.cssid,
info->addr.ccw.ssid,
info->addr.ccw.devno);
} else if (info->type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_ISA) {
virBufferAsprintf(buf, ",iobase=0x%x,irq=0x%x",
info->addr.isa.iobase,
info->addr.isa.irq);
}
ret = 0;
cleanup:
VIR_FREE(devStr);
return ret;
}
static int
qemuBuildRomStr(virBufferPtr buf,
virDomainDeviceInfoPtr info,
virQEMUCapsPtr qemuCaps)
{
if (info->rombar || info->romfile) {
if (info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
"%s", _("rombar and romfile are supported only for PCI devices"));
return -1;
}
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_PCI_ROMBAR)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
"%s", _("rombar and romfile not supported in this QEMU binary"));
return -1;
}
switch (info->rombar) {
case VIR_TRISTATE_SWITCH_OFF:
virBufferAddLit(buf, ",rombar=0");
break;
case VIR_TRISTATE_SWITCH_ON:
virBufferAddLit(buf, ",rombar=1");
break;
default:
break;
}
if (info->romfile)
virBufferAsprintf(buf, ",romfile=%s", info->romfile);
}
return 0;
}
static int
qemuBuildIoEventFdStr(virBufferPtr buf,
virTristateSwitch use,
virQEMUCapsPtr qemuCaps)
{
if (use && virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_IOEVENTFD))
virBufferAsprintf(buf, ",ioeventfd=%s",
virTristateSwitchTypeToString(use));
return 0;
}
#define QEMU_SERIAL_PARAM_ACCEPTED_CHARS \
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_ "
static int
qemuSafeSerialParamValue(const char *value)
{
if (strspn(value, QEMU_SERIAL_PARAM_ACCEPTED_CHARS) != strlen(value)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("driver serial '%s' contains unsafe characters"),
value);
return -1;
}
return 0;
}
static char *
qemuGetSecretString(virConnectPtr conn,
const char *scheme,
bool encoded,
virStorageAuthDefPtr authdef,
virSecretUsageType secretUsageType)
{
size_t secret_size;
virSecretPtr sec = NULL;
char *secret = NULL;
char uuidStr[VIR_UUID_STRING_BUFLEN];
/* look up secret */
switch (authdef->secretType) {
case VIR_STORAGE_SECRET_TYPE_UUID:
sec = virSecretLookupByUUID(conn, authdef->secret.uuid);
virUUIDFormat(authdef->secret.uuid, uuidStr);
break;
case VIR_STORAGE_SECRET_TYPE_USAGE:
sec = virSecretLookupByUsage(conn, secretUsageType,
authdef->secret.usage);
break;
}
if (!sec) {
if (authdef->secretType == VIR_STORAGE_SECRET_TYPE_UUID) {
virReportError(VIR_ERR_NO_SECRET,
_("%s no secret matches uuid '%s'"),
scheme, uuidStr);
} else {
virReportError(VIR_ERR_NO_SECRET,
_("%s no secret matches usage value '%s'"),
scheme, authdef->secret.usage);
}
goto cleanup;
}
secret = (char *)conn->secretDriver->secretGetValue(sec, &secret_size, 0,
VIR_SECRET_GET_VALUE_INTERNAL_CALL);
if (!secret) {
if (authdef->secretType == VIR_STORAGE_SECRET_TYPE_UUID) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("could not get value of the secret for "
"username '%s' using uuid '%s'"),
authdef->username, uuidStr);
} else {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("could not get value of the secret for "
"username '%s' using usage value '%s'"),
authdef->username, authdef->secret.usage);
}
goto cleanup;
}
if (encoded) {
char *base64 = NULL;
base64_encode_alloc(secret, secret_size, &base64);
VIR_FREE(secret);
if (!base64) {
virReportOOMError();
goto cleanup;
}
secret = base64;
}
cleanup:
virObjectUnref(sec);
return secret;
}
static int
qemuParseRBDString(virDomainDiskDefPtr disk)
{
char *source = disk->src->path;
int ret;
disk->src->path = NULL;
ret = virStorageSourceParseRBDColonString(source, disk->src);
VIR_FREE(source);
return ret;
}
static int
qemuParseDriveURIString(virDomainDiskDefPtr def, virURIPtr uri,
const char *scheme)
{
int ret = -1;
char *transp = NULL;
char *sock = NULL;
char *volimg = NULL;
char *secret = NULL;
virStorageAuthDefPtr authdef = NULL;
if (VIR_ALLOC(def->src->hosts) < 0)
goto error;
transp = strchr(uri->scheme, '+');
if (transp)
*transp++ = 0;
if (!STREQ(uri->scheme, scheme)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Invalid transport/scheme '%s'"), uri->scheme);
goto error;
}
if (!transp) {
def->src->hosts->transport = VIR_STORAGE_NET_HOST_TRANS_TCP;
} else {
def->src->hosts->transport = virStorageNetHostTransportTypeFromString(transp);
if (def->src->hosts->transport < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Invalid %s transport type '%s'"), scheme, transp);
goto error;
}
}
def->src->nhosts = 0; /* set to 1 once everything succeeds */
if (def->src->hosts->transport != VIR_STORAGE_NET_HOST_TRANS_UNIX) {
if (VIR_STRDUP(def->src->hosts->name, uri->server) < 0)
goto error;
if (virAsprintf(&def->src->hosts->port, "%d", uri->port) < 0)
goto error;
} else {
def->src->hosts->name = NULL;
def->src->hosts->port = 0;
if (uri->query) {
if (STRPREFIX(uri->query, "socket=")) {
sock = strchr(uri->query, '=') + 1;
if (VIR_STRDUP(def->src->hosts->socket, sock) < 0)
goto error;
} else {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Invalid query parameter '%s'"), uri->query);
goto error;
}
}
}
if (uri->path) {
volimg = uri->path + 1; /* skip the prefix slash */
VIR_FREE(def->src->path);
if (VIR_STRDUP(def->src->path, volimg) < 0)
goto error;
} else {
VIR_FREE(def->src->path);
}
if (uri->user) {
const char *secrettype;
/* formulate authdef for disk->src->auth */
if (VIR_ALLOC(authdef) < 0)
goto error;
secret = strchr(uri->user, ':');
if (secret)
*secret = '\0';
if (VIR_STRDUP(authdef->username, uri->user) < 0)
goto error;
if (STREQ(scheme, "iscsi")) {
secrettype =
virSecretUsageTypeToString(VIR_SECRET_USAGE_TYPE_ISCSI);
if (VIR_STRDUP(authdef->secrettype, secrettype) < 0)
goto error;
}
def->src->auth = authdef;
authdef = NULL;
/* Cannot formulate a secretType (eg, usage or uuid) given
* what is provided.
*/
}
def->src->nhosts = 1;
ret = 0;
cleanup:
virURIFree(uri);
return ret;
error:
virStorageNetHostDefClear(def->src->hosts);
VIR_FREE(def->src->hosts);
virStorageAuthDefFree(authdef);
goto cleanup;
}
static int
qemuParseGlusterString(virDomainDiskDefPtr def)
{
virURIPtr uri = NULL;
if (!(uri = virURIParse(def->src->path)))
return -1;
return qemuParseDriveURIString(def, uri, "gluster");
}
static int
qemuParseISCSIString(virDomainDiskDefPtr def)
{
virURIPtr uri = NULL;
char *slash;
unsigned lun;
if (!(uri = virURIParse(def->src->path)))
return -1;
if (uri->path &&
(slash = strchr(uri->path + 1, '/')) != NULL) {
if (slash[1] == '\0') {
*slash = '\0';
} else if (virStrToLong_ui(slash + 1, NULL, 10, &lun) == -1) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("invalid name '%s' for iSCSI disk"),
def->src->path);
virURIFree(uri);
return -1;
}
}
return qemuParseDriveURIString(def, uri, "iscsi");
}
static int
qemuParseNBDString(virDomainDiskDefPtr disk)
{
virStorageNetHostDefPtr h = NULL;
char *host, *port;
char *src;
virURIPtr uri = NULL;
if (strstr(disk->src->path, "://")) {
if (!(uri = virURIParse(disk->src->path)))
return -1;
return qemuParseDriveURIString(disk, uri, "nbd");
}
if (VIR_ALLOC(h) < 0)
goto error;
host = disk->src->path + strlen("nbd:");
if (STRPREFIX(host, "unix:/")) {
src = strchr(host + strlen("unix:"), ':');
if (src)
*src++ = '\0';
h->transport = VIR_STORAGE_NET_HOST_TRANS_UNIX;
if (VIR_STRDUP(h->socket, host + strlen("unix:")) < 0)
goto error;
} else {
port = strchr(host, ':');
if (!port) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot parse nbd filename '%s'"), disk->src->path);
goto error;
}
*port++ = '\0';
if (VIR_STRDUP(h->name, host) < 0)
goto error;
src = strchr(port, ':');
if (src)
*src++ = '\0';
if (VIR_STRDUP(h->port, port) < 0)
goto error;
}
if (src && STRPREFIX(src, "exportname=")) {
if (VIR_STRDUP(src, strchr(src, '=') + 1) < 0)
goto error;
} else {
src = NULL;
}
VIR_FREE(disk->src->path);
disk->src->path = src;
disk->src->nhosts = 1;
disk->src->hosts = h;
return 0;
error:
virStorageNetHostDefClear(h);
VIR_FREE(h);
return -1;
}
static int
qemuNetworkDriveGetPort(int protocol,
const char *port)
{
int ret = 0;
if (port) {
if (virStrToLong_i(port, NULL, 10, &ret) < 0 || ret < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("failed to parse port number '%s'"),
port);
return -1;
}
return ret;
}
switch ((virStorageNetProtocol) protocol) {
case VIR_STORAGE_NET_PROTOCOL_HTTP:
return 80;
case VIR_STORAGE_NET_PROTOCOL_HTTPS:
return 443;
case VIR_STORAGE_NET_PROTOCOL_FTP:
return 21;
case VIR_STORAGE_NET_PROTOCOL_FTPS:
return 990;
case VIR_STORAGE_NET_PROTOCOL_TFTP:
return 69;
case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
return 7000;
case VIR_STORAGE_NET_PROTOCOL_NBD:
return 10809;
case VIR_STORAGE_NET_PROTOCOL_ISCSI:
case VIR_STORAGE_NET_PROTOCOL_GLUSTER:
/* no default port specified */
return 0;
case VIR_STORAGE_NET_PROTOCOL_RBD:
case VIR_STORAGE_NET_PROTOCOL_LAST:
case VIR_STORAGE_NET_PROTOCOL_NONE:
/* not applicable */
return -1;
}
return -1;
}
#define QEMU_DEFAULT_NBD_PORT "10809"
static char *
qemuBuildNetworkDriveURI(virStorageSourcePtr src,
const char *username,
const char *secret)
{
char *ret = NULL;
virBuffer buf = VIR_BUFFER_INITIALIZER;
virURIPtr uri = NULL;
size_t i;
switch ((virStorageNetProtocol) src->protocol) {
case VIR_STORAGE_NET_PROTOCOL_NBD:
if (src->nhosts != 1) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("protocol '%s' accepts only one host"),
virStorageNetProtocolTypeToString(src->protocol));
goto cleanup;
}
if (!((src->hosts->name && strchr(src->hosts->name, ':')) ||
(src->hosts->transport == VIR_STORAGE_NET_HOST_TRANS_TCP &&
!src->hosts->name) ||
(src->hosts->transport == VIR_STORAGE_NET_HOST_TRANS_UNIX &&
src->hosts->socket &&
src->hosts->socket[0] != '/'))) {
virBufferAddLit(&buf, "nbd:");
switch (src->hosts->transport) {
case VIR_STORAGE_NET_HOST_TRANS_TCP:
virBufferStrcat(&buf, src->hosts->name, NULL);
virBufferAsprintf(&buf, ":%s",
src->hosts->port ? src->hosts->port :
QEMU_DEFAULT_NBD_PORT);
break;
case VIR_STORAGE_NET_HOST_TRANS_UNIX:
if (!src->hosts->socket) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("socket attribute required for "
"unix transport"));
goto cleanup;
}
virBufferAsprintf(&buf, "unix:%s", src->hosts->socket);
break;
default:
virReportError(VIR_ERR_INTERNAL_ERROR,
_("nbd does not support transport '%s'"),
virStorageNetHostTransportTypeToString(src->hosts->transport));
goto cleanup;
}
if (src->path)
virBufferAsprintf(&buf, ":exportname=%s", src->path);
if (virBufferCheckError(&buf) < 0)
goto cleanup;
ret = virBufferContentAndReset(&buf);
goto cleanup;
}
/* fallthrough */
/* NBD code uses same formatting scheme as others in some cases */
case VIR_STORAGE_NET_PROTOCOL_HTTP:
case VIR_STORAGE_NET_PROTOCOL_HTTPS:
case VIR_STORAGE_NET_PROTOCOL_FTP:
case VIR_STORAGE_NET_PROTOCOL_FTPS:
case VIR_STORAGE_NET_PROTOCOL_TFTP:
case VIR_STORAGE_NET_PROTOCOL_ISCSI:
case VIR_STORAGE_NET_PROTOCOL_GLUSTER:
if (src->nhosts != 1) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("protocol '%s' accepts only one host"),
virStorageNetProtocolTypeToString(src->protocol));
goto cleanup;
}
if (VIR_ALLOC(uri) < 0)
goto cleanup;
if (src->hosts->transport == VIR_STORAGE_NET_HOST_TRANS_TCP) {
if (VIR_STRDUP(uri->scheme,
virStorageNetProtocolTypeToString(src->protocol)) < 0)
goto cleanup;
} else {
if (virAsprintf(&uri->scheme, "%s+%s",
virStorageNetProtocolTypeToString(src->protocol),
virStorageNetHostTransportTypeToString(src->hosts->transport)) < 0)
goto cleanup;
}
if ((uri->port = qemuNetworkDriveGetPort(src->protocol, src->hosts->port)) < 0)
goto cleanup;
if (src->path) {
if (src->volume) {
if (virAsprintf(&uri->path, "/%s%s",
src->volume, src->path) < 0)
goto cleanup;
} else {
if (virAsprintf(&uri->path, "%s%s",
src->path[0] == '/' ? "" : "/",
src->path) < 0)
goto cleanup;
}
}
if (src->hosts->socket &&
virAsprintf(&uri->query, "socket=%s", src->hosts->socket) < 0)
goto cleanup;
if (username) {
if (secret) {
if (virAsprintf(&uri->user, "%s:%s", username, secret) < 0)
goto cleanup;
} else {
if (VIR_STRDUP(uri->user, username) < 0)
goto cleanup;
}
}
if (VIR_STRDUP(uri->server, src->hosts->name) < 0)
goto cleanup;
ret = virURIFormat(uri);
break;
case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
if (!src->path) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("missing disk source for 'sheepdog' protocol"));
goto cleanup;
}
if (src->nhosts == 0) {
if (virAsprintf(&ret, "sheepdog:%s", src->path) < 0)
goto cleanup;
} else if (src->nhosts == 1) {
if (virAsprintf(&ret, "sheepdog:%s:%s:%s",
src->hosts->name,
src->hosts->port ? src->hosts->port : "7000",
src->path) < 0)
goto cleanup;
} else {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("protocol 'sheepdog' accepts up to one host"));
goto cleanup;
}
break;
case VIR_STORAGE_NET_PROTOCOL_RBD:
if (strchr(src->path, ':')) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("':' not allowed in RBD source volume name '%s'"),
src->path);
goto cleanup;
}
virBufferStrcat(&buf, "rbd:", src->path, NULL);
if (src->snapshot)
virBufferEscape(&buf, '\\', ":", "@%s", src->snapshot);
if (username) {
virBufferEscape(&buf, '\\', ":", ":id=%s", username);
virBufferEscape(&buf, '\\', ":",
":key=%s:auth_supported=cephx\\;none",
secret);
} else {
virBufferAddLit(&buf, ":auth_supported=none");
}
if (src->nhosts > 0) {
virBufferAddLit(&buf, ":mon_host=");
for (i = 0; i < src->nhosts; i++) {
if (i)
virBufferAddLit(&buf, "\\;");
/* assume host containing : is ipv6 */
if (strchr(src->hosts[i].name, ':'))
virBufferEscape(&buf, '\\', ":", "[%s]",
src->hosts[i].name);
else
virBufferAsprintf(&buf, "%s", src->hosts[i].name);
if (src->hosts[i].port)
virBufferAsprintf(&buf, "\\:%s", src->hosts[i].port);
}
}
if (src->configFile)
virBufferEscape(&buf, '\\', ":", ":conf=%s", src->configFile);
if (virBufferCheckError(&buf) < 0)
goto cleanup;
ret = virBufferContentAndReset(&buf);
break;
case VIR_STORAGE_NET_PROTOCOL_LAST:
case VIR_STORAGE_NET_PROTOCOL_NONE:
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unexpected network protocol '%s'"),
virStorageNetProtocolTypeToString(src->protocol));
goto cleanup;
}
cleanup:
virBufferFreeAndReset(&buf);
virURIFree(uri);
return ret;
}
int
qemuGetDriveSourceString(virStorageSourcePtr src,
virConnectPtr conn,
char **source)
{
int actualType = virStorageSourceGetActualType(src);
char *secret = NULL;
char *username = NULL;
int ret = -1;
*source = NULL;
/* return 1 for empty sources */
if (virStorageSourceIsEmpty(src))
return 1;
if (conn) {
if (actualType == VIR_STORAGE_TYPE_NETWORK &&
src->auth &&
(src->protocol == VIR_STORAGE_NET_PROTOCOL_ISCSI ||
src->protocol == VIR_STORAGE_NET_PROTOCOL_RBD)) {
bool encode = false;
int secretType = VIR_SECRET_USAGE_TYPE_ISCSI;
const char *protocol = virStorageNetProtocolTypeToString(src->protocol);
username = src->auth->username;
if (src->protocol == VIR_STORAGE_NET_PROTOCOL_RBD) {
/* qemu requires the secret to be encoded for RBD */
encode = true;
secretType = VIR_SECRET_USAGE_TYPE_CEPH;
}
if (!(secret = qemuGetSecretString(conn,
protocol,
encode,
src->auth,
secretType)))
goto cleanup;
}
}
switch ((virStorageType) actualType) {
case VIR_STORAGE_TYPE_BLOCK:
case VIR_STORAGE_TYPE_FILE:
case VIR_STORAGE_TYPE_DIR:
if (VIR_STRDUP(*source, src->path) < 0)
goto cleanup;
break;
case VIR_STORAGE_TYPE_NETWORK:
if (!(*source = qemuBuildNetworkDriveURI(src, username, secret)))
goto cleanup;
break;
case VIR_STORAGE_TYPE_VOLUME:
case VIR_STORAGE_TYPE_NONE:
case VIR_STORAGE_TYPE_LAST:
break;
}
ret = 0;
cleanup:
VIR_FREE(secret);
return ret;
}
/* Perform disk definition config validity checks */
int
qemuCheckDiskConfig(virDomainDiskDefPtr disk)
{
if (virDiskNameToIndex(disk->dst) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unsupported disk type '%s'"), disk->dst);
goto error;
}
if (disk->wwn) {
if ((disk->bus != VIR_DOMAIN_DISK_BUS_IDE) &&
(disk->bus != VIR_DOMAIN_DISK_BUS_SCSI)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Only ide and scsi disk support wwn"));
goto error;
}
}
if ((disk->vendor || disk->product) &&
disk->bus != VIR_DOMAIN_DISK_BUS_SCSI) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Only scsi disk supports vendor and product"));
goto error;
}
if (disk->device == VIR_DOMAIN_DISK_DEVICE_LUN) {
/* make sure that both the bus supports type='lun' (SG_IO). */
if (disk->bus != VIR_DOMAIN_DISK_BUS_VIRTIO &&
disk->bus != VIR_DOMAIN_DISK_BUS_SCSI) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("disk device='lun' is not supported for bus='%s'"),
virDomainDiskQEMUBusTypeToString(disk->bus));
goto error;
}
if (disk->src->type == VIR_STORAGE_TYPE_NETWORK) {
if (disk->src->protocol != VIR_STORAGE_NET_PROTOCOL_ISCSI) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("disk device='lun' is not supported "
"for protocol='%s'"),
virStorageNetProtocolTypeToString(disk->src->protocol));
goto error;
}
} else if (!virDomainDiskSourceIsBlockType(disk->src, true)) {
goto error;
}
if (disk->wwn) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Setting wwn is not supported for lun device"));
goto error;
}
if (disk->vendor || disk->product) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Setting vendor or product is not supported "
"for lun device"));
goto error;
}
}
return 0;
error:
return -1;
}
/* Check whether the device address is using either 'ccw' or default s390
* address format and whether that's "legal" for the current qemu and/or
* guest os.machine type. This is the corollary to the code which doesn't
* find the address type set using an emulator that supports either 'ccw'
* or s390 and sets the address type based on the capabilities.
*
* If the address is using 'ccw' or s390 and it's not supported, generate
* an error and return false; otherwise, return true.
*/
bool
qemuCheckCCWS390AddressSupport(virDomainDefPtr def,
virDomainDeviceInfo info,
virQEMUCapsPtr qemuCaps,
const char *devicename)
{
if (info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW) {
if (!qemuDomainMachineIsS390CCW(def)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("cannot use CCW address type for device "
"'%s' using machine type '%s'"),
devicename, def->os.machine);
return false;
} else if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_CCW)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("CCW address type is not supported by "
"this QEMU"));
return false;
}
} else if (info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_S390) {
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_S390)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("virtio S390 address type is not supported by "
"this QEMU"));
return false;
}
}
return true;
}
/* Qemu 1.2 and later have a binary flag -enable-fips that must be
* used for VNC auth to obey FIPS settings; but the flag only
* exists on Linux, and with no way to probe for it via QMP. Our
* solution: if FIPS mode is required, then unconditionally use
* the flag, regardless of qemu version, for the following matrix:
*
* old QEMU new QEMU
* FIPS enabled doesn't start VNC auth disabled
* FIPS disabled/missing VNC auth enabled VNC auth enabled
*/
bool
qemuCheckFips(void)
{
bool ret = false;
if (virFileExists("/proc/sys/crypto/fips_enabled")) {
char *buf = NULL;
if (virFileReadAll("/proc/sys/crypto/fips_enabled", 10, &buf) < 0)
return ret;
if (STREQ(buf, "1\n"))
ret = true;
VIR_FREE(buf);
}
return ret;
}
char *
qemuBuildDriveStr(virConnectPtr conn,
virDomainDiskDefPtr disk,
bool bootable,
virQEMUCapsPtr qemuCaps)
{
virBuffer opt = VIR_BUFFER_INITIALIZER;
const char *bus = virDomainDiskQEMUBusTypeToString(disk->bus);
const char *trans =
virDomainDiskGeometryTransTypeToString(disk->geometry.trans);
int idx = virDiskNameToIndex(disk->dst);
int busid = -1, unitid = -1;
char *source = NULL;
int actualType = virStorageSourceGetActualType(disk->src);
if (idx < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unsupported disk type '%s'"), disk->dst);
goto error;
}
switch (disk->bus) {
case VIR_DOMAIN_DISK_BUS_SCSI:
if (disk->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("unexpected address type for scsi disk"));
goto error;
}
/* Setting bus= attr for SCSI drives, causes a controller
* to be created. Yes this is slightly odd. It is not possible
* to have > 1 bus on a SCSI controller (yet). */
if (disk->info.addr.drive.bus != 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("SCSI controller only supports 1 bus"));
goto error;
}
busid = disk->info.addr.drive.controller;
unitid = disk->info.addr.drive.unit;
break;
case VIR_DOMAIN_DISK_BUS_IDE:
if (disk->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("unexpected address type for ide disk"));
goto error;
}
/* We can only have 1 IDE controller (currently) */
if (disk->info.addr.drive.controller != 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Only 1 %s controller is supported"), bus);
goto error;
}
busid = disk->info.addr.drive.bus;
unitid = disk->info.addr.drive.unit;
break;
case VIR_DOMAIN_DISK_BUS_FDC:
if (disk->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("unexpected address type for fdc disk"));
goto error;
}
/* We can only have 1 FDC controller (currently) */
if (disk->info.addr.drive.controller != 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Only 1 %s controller is supported"), bus);
goto error;
}
/* We can only have 1 FDC bus (currently) */
if (disk->info.addr.drive.bus != 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Only 1 %s bus is supported"), bus);
goto error;
}
if (disk->info.addr.drive.target != 0) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("target must be 0 for controller fdc"));
goto error;
}
unitid = disk->info.addr.drive.unit;
break;
case VIR_DOMAIN_DISK_BUS_VIRTIO:
idx = -1;
break;
case VIR_DOMAIN_DISK_BUS_XEN:
case VIR_DOMAIN_DISK_BUS_SD:
/* Xen and SD have no address type currently, so assign
* based on index */
break;
}
if (qemuGetDriveSourceString(disk->src, conn, &source) < 0)
goto error;
if (source &&
!((disk->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY ||
disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM) &&
disk->tray_status == VIR_DOMAIN_DISK_TRAY_OPEN)) {
virBufferAddLit(&opt, "file=");
switch (actualType) {
case VIR_STORAGE_TYPE_DIR:
/* QEMU only supports magic FAT format for now */
if (disk->src->format > 0 &&
disk->src->format != VIR_STORAGE_FILE_FAT) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unsupported disk driver type for '%s'"),
virStorageFileFormatTypeToString(disk->src->format));
goto error;
}
if (!disk->src->readonly) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("cannot create virtual FAT disks in read-write mode"));
goto error;
}
virBufferAddLit(&opt, "fat:");
if (disk->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY)
virBufferAddLit(&opt, "floppy:");
break;
case VIR_STORAGE_TYPE_BLOCK:
if (disk->tray_status == VIR_DOMAIN_DISK_TRAY_OPEN) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
disk->src->type == VIR_STORAGE_TYPE_VOLUME ?
_("tray status 'open' is invalid for block type volume") :
_("tray status 'open' is invalid for block type disk"));
goto error;
}
break;
default:
break;
}
virBufferEscape(&opt, ',', ",", "%s,", source);
}
VIR_FREE(source);
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE))
virBufferAddLit(&opt, "if=none");
else
virBufferAsprintf(&opt, "if=%s", bus);
if (disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM) {
if (disk->bus == VIR_DOMAIN_DISK_BUS_SCSI) {
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SCSI_CD))
virBufferAddLit(&opt, ",media=cdrom");
} else if (disk->bus == VIR_DOMAIN_DISK_BUS_IDE) {
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_IDE_CD))
virBufferAddLit(&opt, ",media=cdrom");
} else {
virBufferAddLit(&opt, ",media=cdrom");
}
}
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) {
virBufferAsprintf(&opt, ",id=%s%s", QEMU_DRIVE_HOST_PREFIX, disk->info.alias);
} else {
if (busid == -1 && unitid == -1) {
if (idx != -1)
virBufferAsprintf(&opt, ",index=%d", idx);
} else {
if (busid != -1)
virBufferAsprintf(&opt, ",bus=%d", busid);
if (unitid != -1)
virBufferAsprintf(&opt, ",unit=%d", unitid);
}
}
if (bootable &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_BOOT) &&
(disk->device == VIR_DOMAIN_DISK_DEVICE_DISK ||
disk->device == VIR_DOMAIN_DISK_DEVICE_LUN) &&
disk->bus != VIR_DOMAIN_DISK_BUS_IDE)
virBufferAddLit(&opt, ",boot=on");
if (disk->src->readonly &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_READONLY)) {
if (disk->device == VIR_DOMAIN_DISK_DEVICE_DISK) {
if (disk->bus == VIR_DOMAIN_DISK_BUS_IDE) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("readonly ide disks are not supported"));
goto error;
}
if (disk->bus == VIR_DOMAIN_DISK_BUS_SATA) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("readonly sata disks are not supported"));
goto error;
}
}
virBufferAddLit(&opt, ",readonly=on");
}
if (disk->transient) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("transient disks not supported yet"));
goto error;
}
if (disk->src->format > 0 &&
disk->src->type != VIR_STORAGE_TYPE_DIR &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_FORMAT))
virBufferAsprintf(&opt, ",format=%s",
virStorageFileFormatTypeToString(disk->src->format));
/* generate geometry command string */
if (disk->geometry.cylinders > 0 &&
disk->geometry.heads > 0 &&
disk->geometry.sectors > 0) {
virBufferAsprintf(&opt, ",cyls=%u,heads=%u,secs=%u",
disk->geometry.cylinders,
disk->geometry.heads,
disk->geometry.sectors);
if (disk->geometry.trans != VIR_DOMAIN_DISK_TRANS_DEFAULT)
virBufferAsprintf(&opt, ",trans=%s", trans);
}
if (disk->serial &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_SERIAL)) {
if (qemuSafeSerialParamValue(disk->serial) < 0)
goto error;
if (disk->bus == VIR_DOMAIN_DISK_BUS_SCSI &&
disk->device == VIR_DOMAIN_DISK_DEVICE_LUN) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("scsi-block 'lun' devices do not support the "
"serial property"));
goto error;
}
virBufferAddLit(&opt, ",serial=");
virBufferEscape(&opt, '\\', " ", "%s", disk->serial);
}
if (disk->cachemode) {
const char *mode = NULL;
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_CACHE_V2)) {
mode = qemuDiskCacheV2TypeToString(disk->cachemode);
if (disk->cachemode == VIR_DOMAIN_DISK_CACHE_DIRECTSYNC &&
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_CACHE_DIRECTSYNC)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("disk cache mode 'directsync' is not "
"supported by this QEMU"));
goto error;
} else if (disk->cachemode == VIR_DOMAIN_DISK_CACHE_UNSAFE &&
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_CACHE_UNSAFE)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("disk cache mode 'unsafe' is not "
"supported by this QEMU"));
goto error;
}
} else {
mode = qemuDiskCacheV1TypeToString(disk->cachemode);
}
if (disk->iomode == VIR_DOMAIN_DISK_IO_NATIVE &&
disk->cachemode != VIR_DOMAIN_DISK_CACHE_DIRECTSYNC &&
disk->cachemode != VIR_DOMAIN_DISK_CACHE_DISABLE) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("native I/O needs either no disk cache "
"or directsync cache mode, QEMU will fallback "
"to aio=threads"));
goto error;
}
virBufferAsprintf(&opt, ",cache=%s", mode);
} else if (disk->src->shared && !disk->src->readonly) {
virBufferAddLit(&opt, ",cache=off");
}
if (disk->copy_on_read) {
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_COPY_ON_READ)) {
virBufferAsprintf(&opt, ",copy-on-read=%s",
virTristateSwitchTypeToString(disk->copy_on_read));
} else {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("copy_on_read is not supported by this QEMU binary"));
goto error;
}
}
if (disk->discard) {
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_DISCARD)) {
virBufferAsprintf(&opt, ",discard=%s",
virDomainDiskDiscardTypeToString(disk->discard));
} else {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("discard is not supported by this QEMU binary"));
goto error;
}
}
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_MONITOR_JSON)) {
const char *wpolicy = NULL, *rpolicy = NULL;
if (disk->error_policy)
wpolicy = virDomainDiskErrorPolicyTypeToString(disk->error_policy);
if (disk->rerror_policy)
rpolicy = virDomainDiskErrorPolicyTypeToString(disk->rerror_policy);
if (disk->error_policy == VIR_DOMAIN_DISK_ERROR_POLICY_ENOSPACE) {
/* in the case of enospace, the option is spelled
* differently in qemu, and it's only valid for werror,
* not for rerror, so leave leave rerror NULL.
*/
wpolicy = "enospc";
} else if (!rpolicy) {
/* for other policies, rpolicy can match wpolicy */
rpolicy = wpolicy;
}
if (wpolicy)
virBufferAsprintf(&opt, ",werror=%s", wpolicy);
if (rpolicy)
virBufferAsprintf(&opt, ",rerror=%s", rpolicy);
}
if (disk->iomode) {
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_AIO)) {
virBufferAsprintf(&opt, ",aio=%s",
virDomainDiskIoTypeToString(disk->iomode));
} else {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("disk aio mode not supported with this "
"QEMU binary"));
goto error;
}
}
/* block I/O throttling */
if ((disk->blkdeviotune.total_bytes_sec ||
disk->blkdeviotune.read_bytes_sec ||
disk->blkdeviotune.write_bytes_sec ||
disk->blkdeviotune.total_iops_sec ||
disk->blkdeviotune.read_iops_sec ||
disk->blkdeviotune.write_iops_sec) &&
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_IOTUNE)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("block I/O throttling not supported with this "
"QEMU binary"));
goto error;
}
/* block I/O throttling 1.7 */
if ((disk->blkdeviotune.total_bytes_sec_max ||
disk->blkdeviotune.read_bytes_sec_max ||
disk->blkdeviotune.write_bytes_sec_max ||
disk->blkdeviotune.total_iops_sec_max ||
disk->blkdeviotune.read_iops_sec_max ||
disk->blkdeviotune.write_iops_sec_max ||
disk->blkdeviotune.size_iops_sec) &&
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_IOTUNE_MAX)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("there are some block I/O throttling parameters "
"that are not supported with this QEMU binary"));
goto error;
}
if (disk->blkdeviotune.total_bytes_sec > LLONG_MAX ||
disk->blkdeviotune.read_bytes_sec > LLONG_MAX ||
disk->blkdeviotune.write_bytes_sec > LLONG_MAX ||
disk->blkdeviotune.total_iops_sec > LLONG_MAX ||
disk->blkdeviotune.read_iops_sec > LLONG_MAX ||
disk->blkdeviotune.write_iops_sec > LLONG_MAX ||
disk->blkdeviotune.total_bytes_sec_max > LLONG_MAX ||
disk->blkdeviotune.read_bytes_sec_max > LLONG_MAX ||
disk->blkdeviotune.write_bytes_sec_max > LLONG_MAX ||
disk->blkdeviotune.total_iops_sec_max > LLONG_MAX ||
disk->blkdeviotune.read_iops_sec_max > LLONG_MAX ||
disk->blkdeviotune.write_iops_sec_max > LLONG_MAX ||
disk->blkdeviotune.size_iops_sec > LLONG_MAX) {
virReportError(VIR_ERR_OVERFLOW,
_("block I/O throttle limit must "
"be less than %llu using QEMU"), LLONG_MAX);
goto error;
}
if (disk->blkdeviotune.total_bytes_sec) {
virBufferAsprintf(&opt, ",bps=%llu",
disk->blkdeviotune.total_bytes_sec);
}
if (disk->blkdeviotune.read_bytes_sec) {
virBufferAsprintf(&opt, ",bps_rd=%llu",
disk->blkdeviotune.read_bytes_sec);
}
if (disk->blkdeviotune.write_bytes_sec) {
virBufferAsprintf(&opt, ",bps_wr=%llu",
disk->blkdeviotune.write_bytes_sec);
}
if (disk->blkdeviotune.total_iops_sec) {
virBufferAsprintf(&opt, ",iops=%llu",
disk->blkdeviotune.total_iops_sec);
}
if (disk->blkdeviotune.read_iops_sec) {
virBufferAsprintf(&opt, ",iops_rd=%llu",
disk->blkdeviotune.read_iops_sec);
}
if (disk->blkdeviotune.write_iops_sec) {
virBufferAsprintf(&opt, ",iops_wr=%llu",
disk->blkdeviotune.write_iops_sec);
}
if (disk->blkdeviotune.total_bytes_sec_max) {
virBufferAsprintf(&opt, ",bps_max=%llu",
disk->blkdeviotune.total_bytes_sec_max);
}
if (disk->blkdeviotune.read_bytes_sec_max) {
virBufferAsprintf(&opt, ",bps_rd_max=%llu",
disk->blkdeviotune.read_bytes_sec_max);
}
if (disk->blkdeviotune.write_bytes_sec_max) {
virBufferAsprintf(&opt, ",bps_wr_max=%llu",
disk->blkdeviotune.write_bytes_sec_max);
}
if (disk->blkdeviotune.total_iops_sec_max) {
virBufferAsprintf(&opt, ",iops_max=%llu",
disk->blkdeviotune.total_iops_sec_max);
}
if (disk->blkdeviotune.read_iops_sec_max) {
virBufferAsprintf(&opt, ",iops_rd_max=%llu",
disk->blkdeviotune.read_iops_sec_max);
}
if (disk->blkdeviotune.write_iops_sec_max) {
virBufferAsprintf(&opt, ",iops_wr_max=%llu",
disk->blkdeviotune.write_iops_sec_max);
}
if (disk->blkdeviotune.size_iops_sec) {
virBufferAsprintf(&opt, ",iops_size=%llu",
disk->blkdeviotune.size_iops_sec);
}
if (virBufferCheckError(&opt) < 0)
goto error;
return virBufferContentAndReset(&opt);
error:
VIR_FREE(source);
virBufferFreeAndReset(&opt);
return NULL;
}
static bool
qemuCheckIOThreads(virDomainDefPtr def,
virQEMUCapsPtr qemuCaps,
virDomainDiskDefPtr disk)
{
/* Have capability */
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_IOTHREAD)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("IOThreads not supported for this QEMU"));
return false;
}
/* Right "type" of disk" */
if (disk->bus != VIR_DOMAIN_DISK_BUS_VIRTIO ||
(disk->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI &&
disk->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("IOThreads only available for virtio pci and "
"virtio ccw disk"));
return false;
}
/* Can we find the disk iothread in the iothreadid list? */
if (!virDomainIOThreadIDFind(def, disk->iothread)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("Disk iothread '%u' not defined in iothreadid"),
disk->iothread);
return false;
}
return true;
}
char *
qemuBuildDriveDevStr(virDomainDefPtr def,
virDomainDiskDefPtr disk,
int bootindex,
virQEMUCapsPtr qemuCaps)
{
virBuffer opt = VIR_BUFFER_INITIALIZER;
const char *bus = virDomainDiskQEMUBusTypeToString(disk->bus);
const char *contAlias;
int controllerModel;
if (virDomainDiskDefDstDuplicates(def))
goto error;
if (qemuCheckDiskConfig(disk) < 0)
goto error;
/* Live only checks */
if (disk->device == VIR_DOMAIN_DISK_DEVICE_LUN) {
/* make sure that the qemu binary supports type='lun' (SG_IO). */
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_BLK_SG_IO)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("disk device='lun' is not supported by "
"this QEMU"));
goto error;
}
}
if (!qemuCheckCCWS390AddressSupport(def, disk->info, qemuCaps, disk->dst))
goto error;
if (disk->iothread && !qemuCheckIOThreads(def, qemuCaps, disk))
goto error;
switch (disk->bus) {
case VIR_DOMAIN_DISK_BUS_IDE:
if (disk->info.addr.drive.target != 0) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("target must be 0 for ide controller"));
goto error;
}
if (disk->wwn &&
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_IDE_DRIVE_WWN)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Setting wwn for ide disk is not supported "
"by this QEMU"));
goto error;
}
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_IDE_CD)) {
if (disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM)
virBufferAddLit(&opt, "ide-cd");
else
virBufferAddLit(&opt, "ide-hd");
} else {
virBufferAddLit(&opt, "ide-drive");
}
if (!(contAlias = virDomainControllerAliasFind(def, VIR_DOMAIN_CONTROLLER_TYPE_IDE,
disk->info.addr.drive.controller)))
goto error;
virBufferAsprintf(&opt, ",bus=%s.%d,unit=%d",
contAlias,
disk->info.addr.drive.bus,
disk->info.addr.drive.unit);
break;
case VIR_DOMAIN_DISK_BUS_SCSI:
if (disk->device == VIR_DOMAIN_DISK_DEVICE_LUN) {
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SCSI_BLOCK)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("This QEMU doesn't support scsi-block for "
"lun passthrough"));
goto error;
}
}
if (disk->wwn &&
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SCSI_DISK_WWN)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Setting wwn for scsi disk is not supported "
"by this QEMU"));
goto error;
}
/* Properties wwn, vendor and product were introduced in the
* same QEMU release (1.2.0).
*/
if ((disk->vendor || disk->product) &&
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SCSI_DISK_WWN)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Setting vendor or product for scsi disk is not "
"supported by this QEMU"));
goto error;
}
controllerModel =
virDomainDeviceFindControllerModel(def, &disk->info,
VIR_DOMAIN_CONTROLLER_TYPE_SCSI);
if ((qemuSetSCSIControllerModel(def, qemuCaps, &controllerModel)) < 0)
goto error;
if (disk->device == VIR_DOMAIN_DISK_DEVICE_LUN) {
virBufferAddLit(&opt, "scsi-block");
} else {
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_SCSI_CD)) {
if (disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM)
virBufferAddLit(&opt, "scsi-cd");
else
virBufferAddLit(&opt, "scsi-hd");
} else {
virBufferAddLit(&opt, "scsi-disk");
}
}
if (!(contAlias = virDomainControllerAliasFind(def, VIR_DOMAIN_CONTROLLER_TYPE_SCSI,
disk->info.addr.drive.controller)))
goto error;
if (controllerModel == VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSILOGIC) {
if (disk->info.addr.drive.target != 0) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("target must be 0 for controller "
"model 'lsilogic'"));
goto error;
}
virBufferAsprintf(&opt, ",bus=%s.%d,scsi-id=%d",
contAlias,
disk->info.addr.drive.bus,
disk->info.addr.drive.unit);
} else {
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SCSI_DISK_CHANNEL)) {
if (disk->info.addr.drive.target > 7) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("This QEMU doesn't support target "
"greater than 7"));
goto error;
}
if (disk->info.addr.drive.bus != 0 &&
disk->info.addr.drive.unit != 0) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("This QEMU only supports both bus and "
"unit equal to 0"));
goto error;
}
}
virBufferAsprintf(&opt, ",bus=%s.0,channel=%d,scsi-id=%d,lun=%d",
contAlias,
disk->info.addr.drive.bus,
disk->info.addr.drive.target,
disk->info.addr.drive.unit);
}
break;
case VIR_DOMAIN_DISK_BUS_SATA:
if (disk->info.addr.drive.bus != 0) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("bus must be 0 for ide controller"));
goto error;
}
if (disk->info.addr.drive.target != 0) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("target must be 0 for ide controller"));
goto error;
}
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_IDE_CD)) {
if (disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM)
virBufferAddLit(&opt, "ide-cd");
else
virBufferAddLit(&opt, "ide-hd");
} else {
virBufferAddLit(&opt, "ide-drive");
}
if (!(contAlias = virDomainControllerAliasFind(def, VIR_DOMAIN_CONTROLLER_TYPE_SATA,
disk->info.addr.drive.controller)))
goto error;
virBufferAsprintf(&opt, ",bus=%s.%d",
contAlias,
disk->info.addr.drive.unit);
break;
case VIR_DOMAIN_DISK_BUS_VIRTIO:
if (disk->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW) {
virBufferAddLit(&opt, "virtio-blk-ccw");
if (disk->iothread)
virBufferAsprintf(&opt, ",iothread=iothread%u", disk->iothread);
} else if (disk->info.type ==
VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_S390) {
virBufferAddLit(&opt, "virtio-blk-s390");
} else if (disk->info.type ==
VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_MMIO) {
virBufferAddLit(&opt, "virtio-blk-device");
} else {
virBufferAddLit(&opt, "virtio-blk-pci");
if (disk->iothread)
virBufferAsprintf(&opt, ",iothread=iothread%u", disk->iothread);
}
qemuBuildIoEventFdStr(&opt, disk->ioeventfd, qemuCaps);
if (disk->event_idx &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_BLK_EVENT_IDX)) {
virBufferAsprintf(&opt, ",event_idx=%s",
virTristateSwitchTypeToString(disk->event_idx));
}
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_BLK_SCSI)) {
/* if sg_io is true but the scsi option isn't supported,
* that means it's just always on in this version of qemu.
*/
virBufferAsprintf(&opt, ",scsi=%s",
(disk->device == VIR_DOMAIN_DISK_DEVICE_LUN)
? "on" : "off");
}
if (qemuBuildDeviceAddressStr(&opt, def, &disk->info, qemuCaps) < 0)
goto error;
break;
case VIR_DOMAIN_DISK_BUS_USB:
if (disk->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE &&
disk->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("unexpected address type for usb disk"));
goto error;
}
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_USB_STORAGE)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("This QEMU doesn't support '-device "
"usb-storage'"));
goto error;
}
virBufferAddLit(&opt, "usb-storage");
if (qemuBuildDeviceAddressStr(&opt, def, &disk->info, qemuCaps) < 0)
goto error;
break;
default:
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unsupported disk bus '%s' with device setup"), bus);
goto error;
}
virBufferAsprintf(&opt, ",drive=%s%s", QEMU_DRIVE_HOST_PREFIX, disk->info.alias);
virBufferAsprintf(&opt, ",id=%s", disk->info.alias);
if (bootindex && virQEMUCapsGet(qemuCaps, QEMU_CAPS_BOOTINDEX))
virBufferAsprintf(&opt, ",bootindex=%d", bootindex);
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_BLOCKIO)) {
if (disk->blockio.logical_block_size > 0)
virBufferAsprintf(&opt, ",logical_block_size=%u",
disk->blockio.logical_block_size);
if (disk->blockio.physical_block_size > 0)
virBufferAsprintf(&opt, ",physical_block_size=%u",
disk->blockio.physical_block_size);
}
if (disk->wwn) {
if (STRPREFIX(disk->wwn, "0x"))
virBufferAsprintf(&opt, ",wwn=%s", disk->wwn);
else
virBufferAsprintf(&opt, ",wwn=0x%s", disk->wwn);
}
if (disk->vendor)
virBufferAsprintf(&opt, ",vendor=%s", disk->vendor);
if (disk->product)
virBufferAsprintf(&opt, ",product=%s", disk->product);
if (disk->bus == VIR_DOMAIN_DISK_BUS_USB) {
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_USB_STORAGE_REMOVABLE)) {
if (disk->removable == VIR_TRISTATE_SWITCH_ON)
virBufferAddLit(&opt, ",removable=on");
else
virBufferAddLit(&opt, ",removable=off");
} else {
if (disk->removable != VIR_TRISTATE_SWITCH_ABSENT) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("This QEMU doesn't support setting the "
"removable flag of USB storage devices"));
goto error;
}
}
}
if (virBufferCheckError(&opt) < 0)
goto error;
return virBufferContentAndReset(&opt);
error:
virBufferFreeAndReset(&opt);
return NULL;
}
char *qemuBuildFSStr(virDomainFSDefPtr fs,
virQEMUCapsPtr qemuCaps ATTRIBUTE_UNUSED)
{
virBuffer opt = VIR_BUFFER_INITIALIZER;
const char *driver = qemuDomainFSDriverTypeToString(fs->fsdriver);
const char *wrpolicy = virDomainFSWrpolicyTypeToString(fs->wrpolicy);
if (fs->type != VIR_DOMAIN_FS_TYPE_MOUNT) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("only supports mount filesystem type"));
goto error;
}
if (!driver) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Filesystem driver type not supported"));
goto error;
}
virBufferAdd(&opt, driver, -1);
if (fs->fsdriver == VIR_DOMAIN_FS_DRIVER_TYPE_PATH ||
fs->fsdriver == VIR_DOMAIN_FS_DRIVER_TYPE_DEFAULT) {
if (fs->accessmode == VIR_DOMAIN_FS_ACCESSMODE_MAPPED) {
virBufferAddLit(&opt, ",security_model=mapped");
} else if (fs->accessmode == VIR_DOMAIN_FS_ACCESSMODE_PASSTHROUGH) {
virBufferAddLit(&opt, ",security_model=passthrough");
} else if (fs->accessmode == VIR_DOMAIN_FS_ACCESSMODE_SQUASH) {
virBufferAddLit(&opt, ",security_model=none");
}
} else {
/* For other fs drivers, default(passthru) should always
* be supported */
if (fs->accessmode != VIR_DOMAIN_FS_ACCESSMODE_PASSTHROUGH) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("only supports passthrough accessmode"));
goto error;
}
}
if (fs->wrpolicy) {
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_FSDEV_WRITEOUT)) {
virBufferAsprintf(&opt, ",writeout=%s", wrpolicy);
} else {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("filesystem writeout not supported"));
goto error;
}
}
virBufferAsprintf(&opt, ",id=%s%s", QEMU_FSDEV_HOST_PREFIX, fs->info.alias);
virBufferAsprintf(&opt, ",path=%s", fs->src);
if (fs->readonly) {
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_FSDEV_READONLY)) {
virBufferAddLit(&opt, ",readonly");
} else {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("readonly filesystem is not supported by this "
"QEMU binary"));
goto error;
}
}
if (virBufferCheckError(&opt) < 0)
goto error;
return virBufferContentAndReset(&opt);
error:
virBufferFreeAndReset(&opt);
return NULL;
}
char *
qemuBuildFSDevStr(virDomainDefPtr def,
virDomainFSDefPtr fs,
virQEMUCapsPtr qemuCaps)
{
virBuffer opt = VIR_BUFFER_INITIALIZER;
if (fs->type != VIR_DOMAIN_FS_TYPE_MOUNT) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("can only passthrough directories"));
goto error;
}
if (fs->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW)
virBufferAddLit(&opt, "virtio-9p-ccw");
else
virBufferAddLit(&opt, "virtio-9p-pci");
virBufferAsprintf(&opt, ",id=%s", fs->info.alias);
virBufferAsprintf(&opt, ",fsdev=%s%s", QEMU_FSDEV_HOST_PREFIX, fs->info.alias);
virBufferAsprintf(&opt, ",mount_tag=%s", fs->dst);
if (qemuBuildDeviceAddressStr(&opt, def, &fs->info, qemuCaps) < 0)
goto error;
if (virBufferCheckError(&opt) < 0)
goto error;
return virBufferContentAndReset(&opt);
error:
virBufferFreeAndReset(&opt);
return NULL;
}
static int
qemuControllerModelUSBToCaps(int model)
{
switch (model) {
case VIR_DOMAIN_CONTROLLER_MODEL_USB_PIIX3_UHCI:
return QEMU_CAPS_PIIX3_USB_UHCI;
case VIR_DOMAIN_CONTROLLER_MODEL_USB_PIIX4_UHCI:
return QEMU_CAPS_PIIX4_USB_UHCI;
case VIR_DOMAIN_CONTROLLER_MODEL_USB_EHCI:
return QEMU_CAPS_USB_EHCI;
case VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_EHCI1:
case VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_UHCI1:
case VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_UHCI2:
case VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_UHCI3:
return QEMU_CAPS_ICH9_USB_EHCI1;
case VIR_DOMAIN_CONTROLLER_MODEL_USB_VT82C686B_UHCI:
return QEMU_CAPS_VT82C686B_USB_UHCI;
case VIR_DOMAIN_CONTROLLER_MODEL_USB_PCI_OHCI:
return QEMU_CAPS_PCI_OHCI;
case VIR_DOMAIN_CONTROLLER_MODEL_USB_NEC_XHCI:
return QEMU_CAPS_NEC_USB_XHCI;
default:
return -1;
}
}
static int
qemuBuildUSBControllerDevStr(virDomainDefPtr domainDef,
virDomainControllerDefPtr def,
virQEMUCapsPtr qemuCaps,
virBuffer *buf)
{
const char *smodel;
int model, flags;
model = def->model;
if (model == -1) {
if ARCH_IS_PPC64(domainDef->os.arch)
model = VIR_DOMAIN_CONTROLLER_MODEL_USB_PCI_OHCI;
else
model = VIR_DOMAIN_CONTROLLER_MODEL_USB_PIIX3_UHCI;
}
smodel = qemuControllerModelUSBTypeToString(model);
flags = qemuControllerModelUSBToCaps(model);
if (flags == -1 || !virQEMUCapsGet(qemuCaps, flags)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("%s not supported in this QEMU binary"), smodel);
return -1;
}
virBufferAsprintf(buf, "%s", smodel);
if (def->info.mastertype == VIR_DOMAIN_CONTROLLER_MASTER_USB)
virBufferAsprintf(buf, ",masterbus=%s.0,firstport=%d",
def->info.alias, def->info.master.usb.startport);
else
virBufferAsprintf(buf, ",id=%s", def->info.alias);
return 0;
}
char *
qemuBuildControllerDevStr(virDomainDefPtr domainDef,
virDomainControllerDefPtr def,
virQEMUCapsPtr qemuCaps,
int *nusbcontroller)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
int model = def->model;
const char *modelName = NULL;
if (!qemuCheckCCWS390AddressSupport(domainDef, def->info, qemuCaps,
"controller"))
return NULL;
if (def->type == VIR_DOMAIN_CONTROLLER_TYPE_SCSI) {
if ((qemuSetSCSIControllerModel(domainDef, qemuCaps, &model)) < 0)
return NULL;
}
if (!(def->type == VIR_DOMAIN_CONTROLLER_TYPE_SCSI &&
model == VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_SCSI)) {
if (def->queues) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("'queues' is only supported by virtio-scsi controller"));
return NULL;
}
if (def->cmd_per_lun) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("'cmd_per_lun' is only supported by virtio-scsi controller"));
return NULL;
}
if (def->max_sectors) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("'max_sectors' is only supported by virtio-scsi controller"));
return NULL;
}
if (def->ioeventfd) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("'ioeventfd' is only supported by virtio-scsi controller"));
return NULL;
}
}
switch (def->type) {
case VIR_DOMAIN_CONTROLLER_TYPE_SCSI:
switch (model) {
case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_SCSI:
if (def->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW)
virBufferAddLit(&buf, "virtio-scsi-ccw");
else if (def->info.type ==
VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_S390)
virBufferAddLit(&buf, "virtio-scsi-s390");
else if (def->info.type ==
VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_MMIO)
virBufferAddLit(&buf, "virtio-scsi-device");
else
virBufferAddLit(&buf, "virtio-scsi-pci");
break;
case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSILOGIC:
virBufferAddLit(&buf, "lsi");
break;
case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_IBMVSCSI:
virBufferAddLit(&buf, "spapr-vscsi");
break;
case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSISAS1078:
virBufferAddLit(&buf, "megasas");
break;
default:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("Unsupported controller model: %s"),
virDomainControllerModelSCSITypeToString(def->model));
}
virBufferAsprintf(&buf, ",id=%s", def->info.alias);
break;
case VIR_DOMAIN_CONTROLLER_TYPE_VIRTIO_SERIAL:
if (def->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) {
virBufferAddLit(&buf, "virtio-serial-pci");
} else if (def->info.type ==
VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW) {
virBufferAddLit(&buf, "virtio-serial-ccw");
} else if (def->info.type ==
VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_S390) {
virBufferAddLit(&buf, "virtio-serial-s390");
} else if (def->info.type ==
VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_MMIO) {
virBufferAddLit(&buf, "virtio-serial-device");
} else {
virBufferAddLit(&buf, "virtio-serial");
}
virBufferAsprintf(&buf, ",id=%s", def->info.alias);
if (def->opts.vioserial.ports != -1) {
virBufferAsprintf(&buf, ",max_ports=%d",
def->opts.vioserial.ports);
}
if (def->opts.vioserial.vectors != -1) {
virBufferAsprintf(&buf, ",vectors=%d",
def->opts.vioserial.vectors);
}
break;
case VIR_DOMAIN_CONTROLLER_TYPE_CCID:
virBufferAsprintf(&buf, "usb-ccid,id=%s", def->info.alias);
break;
case VIR_DOMAIN_CONTROLLER_TYPE_SATA:
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_ICH9_AHCI)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("SATA is not supported with this "
"QEMU binary"));
goto error;
}
virBufferAsprintf(&buf, "ahci,id=%s", def->info.alias);
break;
case VIR_DOMAIN_CONTROLLER_TYPE_USB:
if (qemuBuildUSBControllerDevStr(domainDef, def, qemuCaps, &buf) == -1)
goto error;
if (nusbcontroller)
*nusbcontroller += 1;
break;
case VIR_DOMAIN_CONTROLLER_TYPE_PCI:
if (def->model == VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT ||
def->model == VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("wrong function called for pci-root/pcie-root"));
return NULL;
}
if (def->idx == 0) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("index for pci controllers of model '%s' must be > 0"),
virDomainControllerModelPCITypeToString(def->model));
goto error;
}
switch (def->model) {
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_BRIDGE:
if (def->opts.pciopts.modelName
== VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_NONE ||
def->opts.pciopts.chassisNr == -1) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("autogenerated pci-bridge options not set"));
goto error;
}
modelName = virDomainControllerPCIModelNameTypeToString(def->opts.pciopts.modelName);
if (!modelName) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unknown pci-bridge model name value %d"),
def->opts.pciopts.modelName);
goto error;
}
if (def->opts.pciopts.modelName
!= VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_PCI_BRIDGE) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("PCI controller model name '%s' "
"is not valid for a pci-bridge"),
modelName);
goto error;
}
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_PCI_BRIDGE)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("the pci-bridge controller "
"is not supported in this QEMU binary"));
goto error;
}
virBufferAsprintf(&buf, "%s,chassis_nr=%d,id=%s",
modelName, def->opts.pciopts.chassisNr,
def->info.alias);
break;
case VIR_DOMAIN_CONTROLLER_MODEL_DMI_TO_PCI_BRIDGE:
if (def->opts.pciopts.modelName
== VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_NONE) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("autogenerated dmi-to-pci-bridge options not set"));
goto error;
}
modelName = virDomainControllerPCIModelNameTypeToString(def->opts.pciopts.modelName);
if (!modelName) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unknown dmi-to-pci-bridge model name value %d"),
def->opts.pciopts.modelName);
goto error;
}
if (def->opts.pciopts.modelName
!= VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_I82801B11_BRIDGE) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("PCI controller model name '%s' "
"is not valid for a dmi-to-pci-bridge"),
modelName);
goto error;
}
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_DMI_TO_PCI_BRIDGE)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("the dmi-to-pci-bridge (i82801b11-bridge) "
"controller is not supported in this QEMU binary"));
goto error;
}
virBufferAsprintf(&buf, "%s,id=%s", modelName, def->info.alias);
break;
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT_PORT:
if (def->opts.pciopts.modelName
== VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_NONE) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("autogenerated pcie-root-port options not set"));
goto error;
}
modelName = virDomainControllerPCIModelNameTypeToString(def->opts.pciopts.modelName);
if (!modelName) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unknown pcie-root-port model name value %d"),
def->opts.pciopts.modelName);
goto error;
}
if (def->opts.pciopts.modelName
!= VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_IOH3420) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("PCI controller model name '%s' "
"is not valid for a pcie-root-port"),
modelName);
goto error;
}
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_IOH3420)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("the pcie-root-port (ioh3420) "
"controller is not supported in this QEMU binary"));
goto error;
}
virBufferAsprintf(&buf, "%s,port=0x%x,chassis=%d,id=%s",
modelName, def->opts.pciopts.port,
def->opts.pciopts.chassis, def->info.alias);
break;
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_UPSTREAM_PORT:
if (def->opts.pciopts.modelName
== VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_NONE) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("autogenerated pcie-switch-upstream-port options not set"));
goto error;
}
modelName = virDomainControllerPCIModelNameTypeToString(def->opts.pciopts.modelName);
if (!modelName) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unknown pcie-switch-upstream-port model name value %d"),
def->opts.pciopts.modelName);
goto error;
}
if (def->opts.pciopts.modelName
!= VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_X3130_UPSTREAM) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("PCI controller model name '%s' "
"is not valid for a pcie-switch-upstream-port"),
modelName);
goto error;
}
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_X3130_UPSTREAM)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("the pcie-switch-upstream-port (x3130-upstream) "
"controller is not supported in this QEMU binary"));
goto error;
}
virBufferAsprintf(&buf, "%s,id=%s", modelName, def->info.alias);
break;
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_DOWNSTREAM_PORT:
if (def->opts.pciopts.modelName
== VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_NONE ||
def->opts.pciopts.chassis == -1 ||
def->opts.pciopts.port == -1) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("autogenerated pcie-switch-downstream-port "
"options not set"));
goto error;
}
modelName = virDomainControllerPCIModelNameTypeToString(def->opts.pciopts.modelName);
if (!modelName) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unknown pcie-switch-downstream-port model name value %d"),
def->opts.pciopts.modelName);
goto error;
}
if (def->opts.pciopts.modelName
!= VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_XIO3130_DOWNSTREAM) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("PCI controller model name '%s' "
"is not valid for a pcie-switch-downstream-port"),
modelName);
goto error;
}
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_XIO3130_DOWNSTREAM)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("The pcie-switch-downstream-port "
"(xio3130-downstream) controller "
"is not supported in this QEMU binary"));
goto error;
}
virBufferAsprintf(&buf, "%s,port=0x%x,chassis=%d,id=%s",
modelName, def->opts.pciopts.port,
def->opts.pciopts.chassis, def->info.alias);
break;
}
break;
case VIR_DOMAIN_CONTROLLER_TYPE_IDE:
/* Since we currently only support the integrated IDE controller
* on 440fx, if we ever get to here, it's because some other
* machinetype had an IDE controller specified, or a 440fx had
* multiple ide controllers.
*/
if (qemuDomainMachineIsI440FX(domainDef))
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Only a single IDE controller is unsupported "
"for this machine type"));
else
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("IDE controllers are unsupported for "
"this QEMU binary or machine type"));
goto error;
default:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("Unsupported controller type: %s"),
virDomainControllerTypeToString(def->type));
goto error;
}
if (def->queues)
virBufferAsprintf(&buf, ",num_queues=%u", def->queues);
if (def->cmd_per_lun)
virBufferAsprintf(&buf, ",cmd_per_lun=%u", def->cmd_per_lun);
if (def->max_sectors)
virBufferAsprintf(&buf, ",max_sectors=%u", def->max_sectors);
qemuBuildIoEventFdStr(&buf, def->ioeventfd, qemuCaps);
if (qemuBuildDeviceAddressStr(&buf, domainDef, &def->info, qemuCaps) < 0)
goto error;
if (virBufferCheckError(&buf) < 0)
goto error;
return virBufferContentAndReset(&buf);
error:
virBufferFreeAndReset(&buf);
return NULL;
}
/**
* qemuBuildMemoryBackendStr:
* @size: size of the memory device in kibibytes
* @pagesize: size of the requested memory page in KiB, 0 for default
* @guestNode: NUMA node in the guest that the memory object will be attached to
* @hostNodes: map of host nodes to alloc the memory in, NULL for default
* @autoNodeset: fallback nodeset in case of automatic numa placement
* @def: domain definition object
* @qemuCaps: qemu capabilities object
* @cfg: qemu driver config object
* @aliasPrefix: prefix string of the alias (to allow for multiple frontents)
* @id: index of the device (to construct the alias)
* @backendStr: returns the object string
*
* Formats the configuration string for the memory device backend according
* to the configuration. @pagesize and @hostNodes can be used to override the
* default source configuration, both are optional.
*
* Returns 0 on success, 1 if only the implicit memory-device-ram with no
* other configuration was used (to detect legacy configurations). Returns
* -1 in case of an error.
*/
int
qemuBuildMemoryBackendStr(unsigned long long size,
unsigned long long pagesize,
int guestNode,
virBitmapPtr userNodeset,
virBitmapPtr autoNodeset,
virDomainDefPtr def,
virQEMUCapsPtr qemuCaps,
virQEMUDriverConfigPtr cfg,
const char **backendType,
virJSONValuePtr *backendProps,
bool force)
{
virDomainHugePagePtr master_hugepage = NULL;
virDomainHugePagePtr hugepage = NULL;
virDomainNumatuneMemMode mode;
const long system_page_size = virGetSystemPageSizeKB();
virNumaMemAccess memAccess = VIR_NUMA_MEM_ACCESS_DEFAULT;
size_t i;
char *mem_path = NULL;
virBitmapPtr nodemask = NULL;
int ret = -1;
virJSONValuePtr props = NULL;
*backendProps = NULL;
*backendType = NULL;
/* memory devices could provide a invalid guest node */
if (guestNode >= virDomainNumaGetNodeCount(def->numa)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("can't add memory backend for guest node '%d' as "
"the guest has only '%zu' NUMA nodes configured"),
guestNode, virDomainNumaGetNodeCount(def->numa));
return -1;
}
if (!(props = virJSONValueNewObject()))
return -1;
memAccess = virDomainNumaGetNodeMemoryAccessMode(def->numa, guestNode);
if (virDomainNumatuneGetMode(def->numa, guestNode, &mode) < 0 &&
virDomainNumatuneGetMode(def->numa, -1, &mode) < 0)
mode = VIR_DOMAIN_NUMATUNE_MEM_STRICT;
if (pagesize == 0) {
/* Find the huge page size we want to use */
for (i = 0; i < def->mem.nhugepages; i++) {
bool thisHugepage = false;
hugepage = &def->mem.hugepages[i];
if (!hugepage->nodemask) {
master_hugepage = hugepage;
continue;
}
if (virBitmapGetBit(hugepage->nodemask, guestNode,
&thisHugepage) < 0) {
/* Ignore this error. It's not an error after all. Well,
* the nodemask for this can contain lower NUMA
* nodes than we are querying in here. */
continue;
}
if (thisHugepage) {
/* Hooray, we've found the page size */
break;
}
}
if (i == def->mem.nhugepages) {
/* We have not found specific huge page to be used with this
* NUMA node. Use the generic setting then ( without any
* @nodemask) if possible. */
hugepage = master_hugepage;
}
if (hugepage)
pagesize = hugepage->size;
}
if (pagesize == system_page_size) {
/* However, if user specified to use "huge" page
* of regular system page size, it's as if they
* hasn't specified any huge pages at all. */
pagesize = 0;
hugepage = NULL;
}
if (pagesize || hugepage) {
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_FILE)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("this qemu doesn't support hugepage memory backing"));
goto cleanup;
}
if (pagesize) {
/* Now lets see, if the huge page we want to use is even mounted
* and ready to use */
for (i = 0; i < cfg->nhugetlbfs; i++) {
if (cfg->hugetlbfs[i].size == pagesize)
break;
}
if (i == cfg->nhugetlbfs) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to find any usable hugetlbfs mount for %llu KiB"),
pagesize);
goto cleanup;
}
if (!(mem_path = qemuGetHugepagePath(&cfg->hugetlbfs[i])))
goto cleanup;
} else {
if (!(mem_path = qemuGetDefaultHugepath(cfg->hugetlbfs,
cfg->nhugetlbfs)))
goto cleanup;
}
*backendType = "memory-backend-file";
if (virJSONValueObjectAdd(props,
"b:prealloc", true,
"s:mem-path", mem_path,
NULL) < 0)
goto cleanup;
switch (memAccess) {
case VIR_NUMA_MEM_ACCESS_SHARED:
if (virJSONValueObjectAdd(props, "b:share", true, NULL) < 0)
goto cleanup;
break;
case VIR_NUMA_MEM_ACCESS_PRIVATE:
if (virJSONValueObjectAdd(props, "b:share", false, NULL) < 0)
goto cleanup;
break;
case VIR_NUMA_MEM_ACCESS_DEFAULT:
case VIR_NUMA_MEM_ACCESS_LAST:
break;
}
} else {
if (memAccess) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Shared memory mapping is supported "
"only with hugepages"));
goto cleanup;
}
*backendType = "memory-backend-ram";
}
if (virJSONValueObjectAdd(props, "U:size", size * 1024, NULL) < 0)
goto cleanup;
if (userNodeset) {
nodemask = userNodeset;
} else {
if (virDomainNumatuneMaybeGetNodeset(def->numa, autoNodeset,
&nodemask, guestNode) < 0)
goto cleanup;
}
if (nodemask) {
if (!virNumaNodesetIsAvailable(nodemask))
goto cleanup;
if (virJSONValueObjectAdd(props,
"m:host-nodes", nodemask,
"S:policy", qemuNumaPolicyTypeToString(mode),
NULL) < 0)
goto cleanup;
}
if (!hugepage && !pagesize) {
bool nodeSpecified = virDomainNumatuneNodeSpecified(def->numa, guestNode);
if ((userNodeset || nodeSpecified || force) &&
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_RAM)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("this qemu doesn't support the "
"memory-backend-ram object"));
goto cleanup;
}
/* report back that using the new backend is not necessary to achieve
* the desired configuration */
if (!userNodeset && !nodeSpecified) {
*backendProps = props;
props = NULL;
ret = 1;
goto cleanup;
}
}
*backendProps = props;
props = NULL;
ret = 0;
cleanup:
virJSONValueFree(props);
VIR_FREE(mem_path);
return ret;
}
static int
qemuBuildMemoryCellBackendStr(virDomainDefPtr def,
virQEMUCapsPtr qemuCaps,
virQEMUDriverConfigPtr cfg,
size_t cell,
virBitmapPtr auto_nodeset,
char **backendStr)
{
virJSONValuePtr props = NULL;
char *alias = NULL;
const char *backendType;
int ret = -1;
int rc;
*backendStr = NULL;
if (virAsprintf(&alias, "ram-node%zu", cell) < 0)
goto cleanup;
if ((rc = qemuBuildMemoryBackendStr(virDomainNumaGetNodeMemorySize(def->numa, cell),
0, cell,
NULL, auto_nodeset,
def, qemuCaps, cfg,
&backendType, &props, false)) < 0)
goto cleanup;
if (!(*backendStr = qemuBuildObjectCommandlineFromJSON(backendType,
alias,
props)))
goto cleanup;
ret = rc;
cleanup:
VIR_FREE(alias);
virJSONValueFree(props);
return ret;
}
static char *
qemuBuildMemoryDimmBackendStr(virDomainMemoryDefPtr mem,
virDomainDefPtr def,
virQEMUCapsPtr qemuCaps,
virQEMUDriverConfigPtr cfg)
{
virJSONValuePtr props = NULL;
char *alias = NULL;
const char *backendType;
char *ret = NULL;
if (!mem->info.alias) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("memory device alias is not assigned"));
return NULL;
}
if (virAsprintf(&alias, "mem%s", mem->info.alias) < 0)
goto cleanup;
if (qemuBuildMemoryBackendStr(mem->size, mem->pagesize,
mem->targetNode, mem->sourceNodes, NULL,
def, qemuCaps, cfg,
&backendType, &props, true) < 0)
goto cleanup;
ret = qemuBuildObjectCommandlineFromJSON(backendType, alias, props);
cleanup:
VIR_FREE(alias);
virJSONValueFree(props);
return ret;
}
static bool
qemuCheckMemoryDimmConflict(virDomainDefPtr def,
virDomainMemoryDefPtr mem)
{
size_t i;
for (i = 0; i < def->nmems; i++) {
virDomainMemoryDefPtr tmp = def->mems[i];
if (tmp == mem ||
tmp->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DIMM)
continue;
if (mem->info.addr.dimm.slot == tmp->info.addr.dimm.slot) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("memory device slot '%u' is already being "
"used by another memory device"),
mem->info.addr.dimm.slot);
return true;
}
if (mem->info.addr.dimm.base != 0 &&
mem->info.addr.dimm.base == tmp->info.addr.dimm.base) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("memory device base '0x%llx' is already being "
"used by another memory device"),
mem->info.addr.dimm.base);
return true;
}
}
return false;
}
char *
qemuBuildMemoryDeviceStr(virDomainMemoryDefPtr mem,
virDomainDefPtr def,
virQEMUCapsPtr qemuCaps)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
if (!mem->info.alias) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("missing alias for memory device"));
return NULL;
}
switch ((virDomainMemoryModel) mem->model) {
case VIR_DOMAIN_MEMORY_MODEL_DIMM:
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_PC_DIMM)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("this qemu doesn't support the pc-dimm device"));
return NULL;
}
if (mem->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DIMM &&
mem->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("only 'dimm' addresses are supported for the "
"pc-dimm device"));
return NULL;
}
if (mem->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DIMM &&
mem->info.addr.dimm.slot >= def->mem.memory_slots) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("memory device slot '%u' exceeds slots count '%u'"),
mem->info.addr.dimm.slot, def->mem.memory_slots);
return NULL;
}
virBufferAsprintf(&buf, "pc-dimm,node=%d,memdev=mem%s,id=%s",
mem->targetNode, mem->info.alias, mem->info.alias);
if (mem->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DIMM) {
if (qemuCheckMemoryDimmConflict(def, mem))
return NULL;
virBufferAsprintf(&buf, ",slot=%d", mem->info.addr.dimm.slot);
virBufferAsprintf(&buf, ",addr=%llu", mem->info.addr.dimm.base);
}
break;
case VIR_DOMAIN_MEMORY_MODEL_NONE:
case VIR_DOMAIN_MEMORY_MODEL_LAST:
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("invalid memory device type"));
break;
}
if (virBufferCheckError(&buf) < 0)
return NULL;
return virBufferContentAndReset(&buf);
}
char *
qemuBuildNicStr(virDomainNetDefPtr net,
const char *prefix,
int vlan)
{
char *str;
char macaddr[VIR_MAC_STRING_BUFLEN];
ignore_value(virAsprintf(&str,
"%smacaddr=%s,vlan=%d%s%s%s%s",
prefix ? prefix : "",
virMacAddrFormat(&net->mac, macaddr),
vlan,
(net->model ? ",model=" : ""),
(net->model ? net->model : ""),
(net->info.alias ? ",name=" : ""),
(net->info.alias ? net->info.alias : "")));
return str;
}
char *
qemuBuildNicDevStr(virDomainDefPtr def,
virDomainNetDefPtr net,
int vlan,
int bootindex,
size_t vhostfdSize,
virQEMUCapsPtr qemuCaps)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
const char *nic = net->model;
bool usingVirtio = false;
char macaddr[VIR_MAC_STRING_BUFLEN];
if (STREQ(net->model, "virtio")) {
if (net->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW)
nic = "virtio-net-ccw";
else if (net->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_S390)
nic = "virtio-net-s390";
else if (net->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_MMIO)
nic = "virtio-net-device";
else
nic = "virtio-net-pci";
usingVirtio = true;
}
virBufferAdd(&buf, nic, -1);
if (usingVirtio && net->driver.virtio.txmode) {
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_TX_ALG)) {
virBufferAddLit(&buf, ",tx=");
switch (net->driver.virtio.txmode) {
case VIR_DOMAIN_NET_VIRTIO_TX_MODE_IOTHREAD:
virBufferAddLit(&buf, "bh");
break;
case VIR_DOMAIN_NET_VIRTIO_TX_MODE_TIMER:
virBufferAddLit(&buf, "timer");
break;
default:
/* this should never happen, if it does, we need
* to add another case to this switch.
*/
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("unrecognized virtio-net-pci 'tx' option"));
goto error;
}
} else {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("virtio-net-pci 'tx' option not supported in this QEMU binary"));
goto error;
}
}
if (usingVirtio) {
qemuBuildIoEventFdStr(&buf, net->driver.virtio.ioeventfd, qemuCaps);
if (net->driver.virtio.event_idx &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_NET_EVENT_IDX)) {
virBufferAsprintf(&buf, ",event_idx=%s",
virTristateSwitchTypeToString(net->driver.virtio.event_idx));
}
if (net->driver.virtio.host.csum) {
virBufferAsprintf(&buf, ",csum=%s",
virTristateSwitchTypeToString(net->driver.virtio.host.csum));
}
if (net->driver.virtio.host.gso) {
virBufferAsprintf(&buf, ",gso=%s",
virTristateSwitchTypeToString(net->driver.virtio.host.gso));
}
if (net->driver.virtio.host.tso4) {
virBufferAsprintf(&buf, ",host_tso4=%s",
virTristateSwitchTypeToString(net->driver.virtio.host.tso4));
}
if (net->driver.virtio.host.tso6) {
virBufferAsprintf(&buf, ",host_tso6=%s",
virTristateSwitchTypeToString(net->driver.virtio.host.tso6));
}
if (net->driver.virtio.host.ecn) {
virBufferAsprintf(&buf, ",host_ecn=%s",
virTristateSwitchTypeToString(net->driver.virtio.host.ecn));
}
if (net->driver.virtio.host.ufo) {
virBufferAsprintf(&buf, ",host_ufo=%s",
virTristateSwitchTypeToString(net->driver.virtio.host.ufo));
}
if (net->driver.virtio.host.mrg_rxbuf) {
virBufferAsprintf(&buf, ",mrg_rxbuf=%s",
virTristateSwitchTypeToString(net->driver.virtio.host.mrg_rxbuf));
}
if (net->driver.virtio.guest.csum) {
virBufferAsprintf(&buf, ",guest_csum=%s",
virTristateSwitchTypeToString(net->driver.virtio.guest.csum));
}
if (net->driver.virtio.guest.tso4) {
virBufferAsprintf(&buf, ",guest_tso4=%s",
virTristateSwitchTypeToString(net->driver.virtio.guest.tso4));
}
if (net->driver.virtio.guest.tso6) {
virBufferAsprintf(&buf, ",guest_tso6=%s",
virTristateSwitchTypeToString(net->driver.virtio.guest.tso6));
}
if (net->driver.virtio.guest.ecn) {
virBufferAsprintf(&buf, ",guest_ecn=%s",
virTristateSwitchTypeToString(net->driver.virtio.guest.ecn));
}
if (net->driver.virtio.guest.ufo) {
virBufferAsprintf(&buf, ",guest_ufo=%s",
virTristateSwitchTypeToString(net->driver.virtio.guest.ufo));
}
}
if (usingVirtio && vhostfdSize > 1) {
if (net->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW) {
/* ccw provides a one to one relation of fds to queues and
* does not support the vectors option
*/
virBufferAddLit(&buf, ",mq=on");
} else {
/* As advised at http://www.linux-kvm.org/page/Multiqueue
* we should add vectors=2*N+2 where N is the vhostfdSize
*/
virBufferAsprintf(&buf, ",mq=on,vectors=%zu", 2 * vhostfdSize + 2);
}
}
if (vlan == -1)
virBufferAsprintf(&buf, ",netdev=host%s", net->info.alias);
else
virBufferAsprintf(&buf, ",vlan=%d", vlan);
virBufferAsprintf(&buf, ",id=%s", net->info.alias);
virBufferAsprintf(&buf, ",mac=%s",
virMacAddrFormat(&net->mac, macaddr));
if (qemuBuildDeviceAddressStr(&buf, def, &net->info, qemuCaps) < 0)
goto error;
if (qemuBuildRomStr(&buf, &net->info, qemuCaps) < 0)
goto error;
if (bootindex && virQEMUCapsGet(qemuCaps, QEMU_CAPS_BOOTINDEX))
virBufferAsprintf(&buf, ",bootindex=%d", bootindex);
if (virBufferCheckError(&buf) < 0)
goto error;
return virBufferContentAndReset(&buf);
error:
virBufferFreeAndReset(&buf);
return NULL;
}
char *
qemuBuildHostNetStr(virDomainNetDefPtr net,
virQEMUDriverPtr driver,
char type_sep,
int vlan,
char **tapfd,
size_t tapfdSize,
char **vhostfd,
size_t vhostfdSize)
{
bool is_tap = false;
virBuffer buf = VIR_BUFFER_INITIALIZER;
virDomainNetType netType = virDomainNetGetActualType(net);
virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver);
size_t i;
if (net->script && netType != VIR_DOMAIN_NET_TYPE_ETHERNET) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("scripts are not supported on interfaces of type %s"),
virDomainNetTypeToString(netType));
virObjectUnref(cfg);
return NULL;
}
switch (netType) {
/*
* If type='bridge', and we're running as privileged user
* or -netdev bridge is not supported then it will fall
* through, -net tap,fd
*/
case VIR_DOMAIN_NET_TYPE_BRIDGE:
case VIR_DOMAIN_NET_TYPE_NETWORK:
case VIR_DOMAIN_NET_TYPE_DIRECT:
virBufferAsprintf(&buf, "tap%c", type_sep);
/* for one tapfd 'fd=' shall be used,
* for more than one 'fds=' is the right choice */
if (tapfdSize == 1) {
virBufferAsprintf(&buf, "fd=%s", tapfd[0]);
} else {
virBufferAddLit(&buf, "fds=");
for (i = 0; i < tapfdSize; i++) {
if (i)
virBufferAddChar(&buf, ':');
virBufferAdd(&buf, tapfd[i], -1);
}
}
type_sep = ',';
is_tap = true;
break;
case VIR_DOMAIN_NET_TYPE_ETHERNET:
virBufferAddLit(&buf, "tap");
if (net->ifname) {
virBufferAsprintf(&buf, "%cifname=%s", type_sep, net->ifname);
type_sep = ',';
}
if (net->script) {
virBufferAsprintf(&buf, "%cscript=%s", type_sep,
net->script);
type_sep = ',';
}
is_tap = true;
break;
case VIR_DOMAIN_NET_TYPE_CLIENT:
virBufferAsprintf(&buf, "socket%cconnect=%s:%d",
type_sep,
net->data.socket.address,
net->data.socket.port);
type_sep = ',';
break;
case VIR_DOMAIN_NET_TYPE_SERVER:
virBufferAsprintf(&buf, "socket%clisten=%s:%d",
type_sep,
net->data.socket.address ? net->data.socket.address
: "",
net->data.socket.port);
type_sep = ',';
break;
case VIR_DOMAIN_NET_TYPE_MCAST:
virBufferAsprintf(&buf, "socket%cmcast=%s:%d",
type_sep,
net->data.socket.address,
net->data.socket.port);
type_sep = ',';
break;
case VIR_DOMAIN_NET_TYPE_UDP:
virBufferAsprintf(&buf, "socket%cudp=%s:%d,localaddr=%s:%d",
type_sep,
net->data.socket.address,
net->data.socket.port,
net->data.socket.localaddr,
net->data.socket.localport);
type_sep = ',';
break;
case VIR_DOMAIN_NET_TYPE_USER:
default:
virBufferAddLit(&buf, "user");
break;
}
if (vlan >= 0) {
virBufferAsprintf(&buf, "%cvlan=%d", type_sep, vlan);
if (net->info.alias)
virBufferAsprintf(&buf, ",name=host%s",
net->info.alias);
} else {
virBufferAsprintf(&buf, "%cid=host%s",
type_sep, net->info.alias);
}
if (is_tap) {
if (vhostfdSize) {
virBufferAddLit(&buf, ",vhost=on,");
if (vhostfdSize == 1) {
virBufferAsprintf(&buf, "vhostfd=%s", vhostfd[0]);
} else {
virBufferAddLit(&buf, "vhostfds=");
for (i = 0; i < vhostfdSize; i++) {
if (i)
virBufferAddChar(&buf, ':');
virBufferAdd(&buf, vhostfd[i], -1);
}
}
}
if (net->tune.sndbuf_specified)
virBufferAsprintf(&buf, ",sndbuf=%lu", net->tune.sndbuf);
}
virObjectUnref(cfg);
if (virBufferCheckError(&buf) < 0)
return NULL;
return virBufferContentAndReset(&buf);
}
char *
qemuBuildWatchdogDevStr(virDomainDefPtr def,
virDomainWatchdogDefPtr dev,
virQEMUCapsPtr qemuCaps)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
const char *model = virDomainWatchdogModelTypeToString(dev->model);
if (!model) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("missing watchdog model"));
goto error;
}
virBufferAsprintf(&buf, "%s,id=%s", model, dev->info.alias);
if (qemuBuildDeviceAddressStr(&buf, def, &dev->info, qemuCaps) < 0)
goto error;
if (virBufferCheckError(&buf) < 0)
goto error;
return virBufferContentAndReset(&buf);
error:
virBufferFreeAndReset(&buf);
return NULL;
}
char *
qemuBuildMemballoonDevStr(virDomainDefPtr def,
virDomainMemballoonDefPtr dev,
virQEMUCapsPtr qemuCaps)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
switch (dev->info.type) {
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI:
virBufferAddLit(&buf, "virtio-balloon-pci");
break;
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW:
virBufferAddLit(&buf, "virtio-balloon-ccw");
break;
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_MMIO:
virBufferAddLit(&buf, "virtio-balloon-device");
break;
default:
virReportError(VIR_ERR_XML_ERROR,
_("memballoon unsupported with address type '%s'"),
virDomainDeviceAddressTypeToString(dev->info.type));
goto error;
}
virBufferAsprintf(&buf, ",id=%s", dev->info.alias);
if (qemuBuildDeviceAddressStr(&buf, def, &dev->info, qemuCaps) < 0)
goto error;
if (virBufferCheckError(&buf) < 0)
goto error;
return virBufferContentAndReset(&buf);
error:
virBufferFreeAndReset(&buf);
return NULL;
}
static char *
qemuBuildNVRAMDevStr(virDomainNVRAMDefPtr dev)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
if (dev->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_SPAPRVIO &&
dev->info.addr.spaprvio.has_reg) {
virBufferAsprintf(&buf, "spapr-nvram.reg=0x%llx",
dev->info.addr.spaprvio.reg);
} else {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("nvram address type must be spaprvio"));
goto error;
}
if (virBufferCheckError(&buf) < 0)
goto error;
return virBufferContentAndReset(&buf);
error:
virBufferFreeAndReset(&buf);
return NULL;
}
char *
qemuBuildUSBInputDevStr(virDomainDefPtr def,
virDomainInputDefPtr dev,
virQEMUCapsPtr qemuCaps)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
switch (dev->type) {
case VIR_DOMAIN_INPUT_TYPE_MOUSE:
virBufferAsprintf(&buf, "usb-mouse,id=%s", dev->info.alias);
break;
case VIR_DOMAIN_INPUT_TYPE_TABLET:
virBufferAsprintf(&buf, "usb-tablet,id=%s", dev->info.alias);
break;
case VIR_DOMAIN_INPUT_TYPE_KBD:
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_USB_KBD))
goto error;
virBufferAsprintf(&buf, "usb-kbd,id=%s", dev->info.alias);
break;
}
if (qemuBuildDeviceAddressStr(&buf, def, &dev->info, qemuCaps) < 0)
goto error;
if (virBufferCheckError(&buf) < 0)
goto error;
return virBufferContentAndReset(&buf);
error:
virBufferFreeAndReset(&buf);
return NULL;
}
char *
qemuBuildSoundDevStr(virDomainDefPtr def,
virDomainSoundDefPtr sound,
virQEMUCapsPtr qemuCaps)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
const char *model = NULL;
/* Hack for devices with different names in QEMU and libvirt */
switch ((virDomainSoundModel) sound->model) {
case VIR_DOMAIN_SOUND_MODEL_ES1370:
model = "ES1370";
break;
case VIR_DOMAIN_SOUND_MODEL_AC97:
model = "AC97";
break;
case VIR_DOMAIN_SOUND_MODEL_ICH6:
model = "intel-hda";
break;
case VIR_DOMAIN_SOUND_MODEL_USB:
model = "usb-audio";
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_USB_AUDIO)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("usb-audio controller is not supported "
"by this QEMU binary"));
goto error;
}
break;
case VIR_DOMAIN_SOUND_MODEL_ICH9:
model = "ich9-intel-hda";
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_ICH9_INTEL_HDA)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("The ich9-intel-hda audio controller "
"is not supported in this QEMU binary"));
goto error;
}
break;
case VIR_DOMAIN_SOUND_MODEL_SB16:
model = "sb16";
break;
case VIR_DOMAIN_SOUND_MODEL_PCSPK: /* pc-speaker is handled separately */
case VIR_DOMAIN_SOUND_MODEL_LAST:
virReportError(VIR_ERR_INTERNAL_ERROR,
_("sound card model '%s' is not supported by qemu"),
virDomainSoundModelTypeToString(sound->model));
goto error;
}
virBufferAsprintf(&buf, "%s,id=%s", model, sound->info.alias);
if (qemuBuildDeviceAddressStr(&buf, def, &sound->info, qemuCaps) < 0)
goto error;
if (virBufferCheckError(&buf) < 0)
goto error;
return virBufferContentAndReset(&buf);
error:
virBufferFreeAndReset(&buf);
return NULL;
}
static int
qemuSoundCodecTypeToCaps(int type)
{
switch (type) {
case VIR_DOMAIN_SOUND_CODEC_TYPE_DUPLEX:
return QEMU_CAPS_HDA_DUPLEX;
case VIR_DOMAIN_SOUND_CODEC_TYPE_MICRO:
return QEMU_CAPS_HDA_MICRO;
default:
return -1;
}
}
static char *
qemuBuildSoundCodecStr(virDomainSoundDefPtr sound,
virDomainSoundCodecDefPtr codec,
virQEMUCapsPtr qemuCaps)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
const char *stype;
int type, flags;
type = codec->type;
stype = qemuSoundCodecTypeToString(type);
flags = qemuSoundCodecTypeToCaps(type);
if (flags == -1 || !virQEMUCapsGet(qemuCaps, flags)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("%s not supported in this QEMU binary"), stype);
goto error;
}
virBufferAsprintf(&buf, "%s,id=%s-codec%d,bus=%s.0,cad=%d",
stype, sound->info.alias, codec->cad, sound->info.alias, codec->cad);
return virBufferContentAndReset(&buf);
error:
virBufferFreeAndReset(&buf);
return NULL;
}
static char *
qemuBuildDeviceVideoStr(virDomainDefPtr def,
virDomainVideoDefPtr video,
virQEMUCapsPtr qemuCaps,
bool primary)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
const char *model;
if (primary) {
model = qemuDeviceVideoTypeToString(video->type);
if (!model || STREQ(model, "")) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("video type %s is not supported with QEMU"),
virDomainVideoTypeToString(video->type));
goto error;
}
} else {
if (video->type != VIR_DOMAIN_VIDEO_TYPE_QXL) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
"%s", _("non-primary video device must be type of 'qxl'"));
goto error;
}
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_QXL)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
"%s", _("only one video card is currently supported"));
goto error;
}
model = "qxl";
}
virBufferAsprintf(&buf, "%s,id=%s", model, video->info.alias);
if (video->type == VIR_DOMAIN_VIDEO_TYPE_QXL) {
if (video->vram > (UINT_MAX / 1024)) {
virReportError(VIR_ERR_OVERFLOW,
_("value for 'vram' must be less than '%u'"),
UINT_MAX / 1024);
goto error;
}
if (video->ram > (UINT_MAX / 1024)) {
virReportError(VIR_ERR_OVERFLOW,
_("value for 'ram' must be less than '%u'"),
UINT_MAX / 1024);
goto error;
}
if (video->ram) {
/* QEMU accepts bytes for ram_size. */
virBufferAsprintf(&buf, ",ram_size=%u", video->ram * 1024);
}
if (video->vram) {
/* QEMU accepts bytes for vram_size. */
virBufferAsprintf(&buf, ",vram_size=%u", video->vram * 1024);
}
if ((primary && virQEMUCapsGet(qemuCaps, QEMU_CAPS_QXL_VGA_VGAMEM)) ||
(!primary && virQEMUCapsGet(qemuCaps, QEMU_CAPS_QXL_VGAMEM))) {
/* QEMU accepts mebibytes for vgamem_mb. */
virBufferAsprintf(&buf, ",vgamem_mb=%u", video->vgamem / 1024);
}
} else if (video->vram &&
((video->type == VIR_DOMAIN_VIDEO_TYPE_VGA &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_VGA_VGAMEM)) ||
(video->type == VIR_DOMAIN_VIDEO_TYPE_VMVGA &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_VMWARE_SVGA_VGAMEM)))) {
if (video->vram < 1024) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
"%s", _("value for 'vram' must be at least 1 MiB "
"(1024 KiB)"));
goto error;
}
virBufferAsprintf(&buf, ",vgamem_mb=%u", video->vram / 1024);
}
if (qemuBuildDeviceAddressStr(&buf, def, &video->info, qemuCaps) < 0)
goto error;
if (virBufferCheckError(&buf) < 0)
goto error;
return virBufferContentAndReset(&buf);
error:
virBufferFreeAndReset(&buf);
return NULL;
}
int
qemuOpenPCIConfig(virDomainHostdevDefPtr dev)
{
virDomainHostdevSubsysPCIPtr pcisrc = &dev->source.subsys.u.pci;
char *path = NULL;
int configfd = -1;
if (virAsprintf(&path, "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/config",
pcisrc->addr.domain, pcisrc->addr.bus,
pcisrc->addr.slot, pcisrc->addr.function) < 0)
return -1;
configfd = open(path, O_RDWR, 0);
if (configfd < 0)
virReportSystemError(errno, _("Failed opening %s"), path);
VIR_FREE(path);
return configfd;
}
char *
qemuBuildPCIHostdevDevStr(virDomainDefPtr def,
virDomainHostdevDefPtr dev,
const char *configfd,
virQEMUCapsPtr qemuCaps)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
virDomainHostdevSubsysPCIPtr pcisrc = &dev->source.subsys.u.pci;
int backend = pcisrc->backend;
/* caller has to assign proper passthrough backend type */
switch ((virDomainHostdevSubsysPCIBackendType) backend) {
case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_KVM:
virBufferAddLit(&buf, "pci-assign");
if (configfd && *configfd)
virBufferAsprintf(&buf, ",configfd=%s", configfd);
break;
case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO:
virBufferAddLit(&buf, "vfio-pci");
break;
default:
virReportError(VIR_ERR_INTERNAL_ERROR,
_("invalid PCI passthrough type '%s'"),
virDomainHostdevSubsysPCIBackendTypeToString(backend));
goto error;
}
virBufferAddLit(&buf, ",host=");
if (pcisrc->addr.domain) {
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_HOST_PCI_MULTIDOMAIN)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("non-zero domain='%.4x' in host device PCI address "
"not supported in this QEMU binary"),
pcisrc->addr.domain);
goto error;
}
virBufferAsprintf(&buf, "%.4x:", pcisrc->addr.domain);
}
virBufferAsprintf(&buf, "%.2x:%.2x.%.1x",
pcisrc->addr.bus, pcisrc->addr.slot,
pcisrc->addr.function);
virBufferAsprintf(&buf, ",id=%s", dev->info->alias);
if (dev->info->bootIndex)
virBufferAsprintf(&buf, ",bootindex=%d", dev->info->bootIndex);
if (qemuBuildDeviceAddressStr(&buf, def, dev->info, qemuCaps) < 0)
goto error;
if (qemuBuildRomStr(&buf, dev->info, qemuCaps) < 0)
goto error;
if (virBufferCheckError(&buf) < 0)
goto error;
return virBufferContentAndReset(&buf);
error:
virBufferFreeAndReset(&buf);
return NULL;
}
char *
qemuBuildPCIHostdevPCIDevStr(virDomainHostdevDefPtr dev,
virQEMUCapsPtr qemuCaps)
{
char *ret = NULL;
virDomainHostdevSubsysPCIPtr pcisrc = &dev->source.subsys.u.pci;
if (pcisrc->addr.domain) {
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_HOST_PCI_MULTIDOMAIN)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("non-zero domain='%.4x' in host device PCI address "
"not supported in this QEMU binary"),
pcisrc->addr.domain);
goto cleanup;
}
ignore_value(virAsprintf(&ret, "host=%.4x:%.2x:%.2x.%.1x",
pcisrc->addr.domain, pcisrc->addr.bus,
pcisrc->addr.slot, pcisrc->addr.function));
} else {
ignore_value(virAsprintf(&ret, "host=%.2x:%.2x.%.1x",
pcisrc->addr.bus, pcisrc->addr.slot,
pcisrc->addr.function));
}
cleanup:
return ret;
}
char *
qemuBuildRedirdevDevStr(virDomainDefPtr def,
virDomainRedirdevDefPtr dev,
virQEMUCapsPtr qemuCaps)
{
size_t i;
virBuffer buf = VIR_BUFFER_INITIALIZER;
virDomainRedirFilterDefPtr redirfilter = def->redirfilter;
if (dev->bus != VIR_DOMAIN_REDIRDEV_BUS_USB) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("Redirection bus %s is not supported by QEMU"),
virDomainRedirdevBusTypeToString(dev->bus));
goto error;
}
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_USB_REDIR)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("USB redirection is not supported "
"by this version of QEMU"));
goto error;
}
virBufferAsprintf(&buf, "usb-redir,chardev=char%s,id=%s",
dev->info.alias, dev->info.alias);
if (redirfilter && redirfilter->nusbdevs) {
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_USB_REDIR_FILTER)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("USB redirection filter is not "
"supported by this version of QEMU"));
goto error;
}
virBufferAddLit(&buf, ",filter=");
for (i = 0; i < redirfilter->nusbdevs; i++) {
virDomainRedirFilterUSBDevDefPtr usbdev = redirfilter->usbdevs[i];
if (usbdev->usbClass >= 0)
virBufferAsprintf(&buf, "0x%02X:", usbdev->usbClass);
else
virBufferAddLit(&buf, "-1:");
if (usbdev->vendor >= 0)
virBufferAsprintf(&buf, "0x%04X:", usbdev->vendor);
else
virBufferAddLit(&buf, "-1:");
if (usbdev->product >= 0)
virBufferAsprintf(&buf, "0x%04X:", usbdev->product);
else
virBufferAddLit(&buf, "-1:");
if (usbdev->version >= 0)
virBufferAsprintf(&buf, "0x%04X:", usbdev->version);
else
virBufferAddLit(&buf, "-1:");
virBufferAsprintf(&buf, "%u", usbdev->allow);
if (i < redirfilter->nusbdevs -1)
virBufferAddLit(&buf, "|");
}
}
if (dev->info.bootIndex) {
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_USB_REDIR_BOOTINDEX)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("USB redirection booting is not "
"supported by this version of QEMU"));
goto error;
}
virBufferAsprintf(&buf, ",bootindex=%d", dev->info.bootIndex);
}
if (qemuBuildDeviceAddressStr(&buf, def, &dev->info, qemuCaps) < 0)
goto error;
if (virBufferCheckError(&buf) < 0)
goto error;
return virBufferContentAndReset(&buf);
error:
virBufferFreeAndReset(&buf);
return NULL;
}
char *
qemuBuildUSBHostdevDevStr(virDomainDefPtr def,
virDomainHostdevDefPtr dev,
virQEMUCapsPtr qemuCaps)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
virDomainHostdevSubsysUSBPtr usbsrc = &dev->source.subsys.u.usb;
if (!dev->missing && !usbsrc->bus && !usbsrc->device) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("USB host device is missing bus/device information"));
return NULL;
}
virBufferAddLit(&buf, "usb-host");
if (!dev->missing) {
virBufferAsprintf(&buf, ",hostbus=%d,hostaddr=%d",
usbsrc->bus, usbsrc->device);
}
virBufferAsprintf(&buf, ",id=%s", dev->info->alias);
if (dev->info->bootIndex)
virBufferAsprintf(&buf, ",bootindex=%d", dev->info->bootIndex);
if (qemuBuildDeviceAddressStr(&buf, def, dev->info, qemuCaps) < 0)
goto error;
if (virBufferCheckError(&buf) < 0)
goto error;
return virBufferContentAndReset(&buf);
error:
virBufferFreeAndReset(&buf);
return NULL;
}
char *
qemuBuildHubDevStr(virDomainDefPtr def,
virDomainHubDefPtr dev,
virQEMUCapsPtr qemuCaps)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
if (dev->type != VIR_DOMAIN_HUB_TYPE_USB) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("hub type %s not supported"),
virDomainHubTypeToString(dev->type));
goto error;
}
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_USB_HUB)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("usb-hub not supported by QEMU binary"));
goto error;
}
virBufferAddLit(&buf, "usb-hub");
virBufferAsprintf(&buf, ",id=%s", dev->info.alias);
if (qemuBuildDeviceAddressStr(&buf, def, &dev->info, qemuCaps) < 0)
goto error;
if (virBufferCheckError(&buf) < 0)
goto error;
return virBufferContentAndReset(&buf);
error:
virBufferFreeAndReset(&buf);
return NULL;
}
char *
qemuBuildUSBHostdevUSBDevStr(virDomainHostdevDefPtr dev)
{
char *ret = NULL;
virDomainHostdevSubsysUSBPtr usbsrc = &dev->source.subsys.u.usb;
if (dev->missing) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("This QEMU doesn't not support missing USB devices"));
return NULL;
}
if (!usbsrc->bus && !usbsrc->device) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("USB host device is missing bus/device information"));
return NULL;
}
ignore_value(virAsprintf(&ret, "host:%d.%d", usbsrc->bus, usbsrc->device));
return ret;
}
static char *
qemuBuildSCSIHostHostdevDrvStr(virDomainHostdevDefPtr dev,
virQEMUCapsPtr qemuCaps ATTRIBUTE_UNUSED,
qemuBuildCommandLineCallbacksPtr callbacks)
{
virDomainHostdevSubsysSCSIPtr scsisrc = &dev->source.subsys.u.scsi;
virDomainHostdevSubsysSCSIHostPtr scsihostsrc = &scsisrc->u.host;
char *sg = NULL;
sg = (callbacks->qemuGetSCSIDeviceSgName)(NULL,
scsihostsrc->adapter,
scsihostsrc->bus,
scsihostsrc->target,
scsihostsrc->unit);
return sg;
}
static char *
qemuBuildSCSIiSCSIHostdevDrvStr(virConnectPtr conn,
virDomainHostdevDefPtr dev)
{
char *source = NULL;
char *secret = NULL;
char *username = NULL;
virStorageSource src;
memset(&src, 0, sizeof(src));
virDomainHostdevSubsysSCSIPtr scsisrc = &dev->source.subsys.u.scsi;
virDomainHostdevSubsysSCSIiSCSIPtr iscsisrc = &scsisrc->u.iscsi;
if (conn && iscsisrc->auth) {
const char *protocol =
virStorageNetProtocolTypeToString(VIR_STORAGE_NET_PROTOCOL_ISCSI);
bool encode = false;
int secretType = VIR_SECRET_USAGE_TYPE_ISCSI;
username = iscsisrc->auth->username;
if (!(secret = qemuGetSecretString(conn, protocol, encode,
iscsisrc->auth, secretType)))
goto cleanup;
}
src.protocol = VIR_STORAGE_NET_PROTOCOL_ISCSI;
src.path = iscsisrc->path;
src.hosts = iscsisrc->hosts;
src.nhosts = iscsisrc->nhosts;
/* Rather than pull what we think we want - use the network disk code */
source = qemuBuildNetworkDriveURI(&src, username, secret);
cleanup:
VIR_FREE(secret);
return source;
}
char *
qemuBuildSCSIHostdevDrvStr(virConnectPtr conn,
virDomainHostdevDefPtr dev,
virQEMUCapsPtr qemuCaps,
qemuBuildCommandLineCallbacksPtr callbacks)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
char *source = NULL;
virDomainHostdevSubsysSCSIPtr scsisrc = &dev->source.subsys.u.scsi;
if (scsisrc->protocol == VIR_DOMAIN_HOSTDEV_SCSI_PROTOCOL_TYPE_ISCSI) {
if (!(source = qemuBuildSCSIiSCSIHostdevDrvStr(conn, dev)))
goto error;
virBufferAsprintf(&buf, "file=%s,if=none,format=raw", source);
} else {
if (!(source = qemuBuildSCSIHostHostdevDrvStr(dev, qemuCaps,
callbacks)))
goto error;
virBufferAsprintf(&buf, "file=/dev/%s,if=none", source);
}
virBufferAsprintf(&buf, ",id=%s-%s",
virDomainDeviceAddressTypeToString(dev->info->type),
dev->info->alias);
if (dev->readonly) {
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_READONLY)) {
virBufferAddLit(&buf, ",readonly=on");
} else {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("this qemu doesn't support 'readonly' "
"for -drive"));
goto error;
}
}
if (virBufferCheckError(&buf) < 0)
goto error;
VIR_FREE(source);
return virBufferContentAndReset(&buf);
error:
VIR_FREE(source);
virBufferFreeAndReset(&buf);
return NULL;
}
char *
qemuBuildSCSIHostdevDevStr(virDomainDefPtr def,
virDomainHostdevDefPtr dev,
virQEMUCapsPtr qemuCaps)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
int model = -1;
const char *contAlias;
model = virDomainDeviceFindControllerModel(def, dev->info,
VIR_DOMAIN_CONTROLLER_TYPE_SCSI);
if (qemuSetSCSIControllerModel(def, qemuCaps, &model) < 0)
goto error;
if (model == VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSILOGIC) {
if (dev->info->addr.drive.target != 0) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("target must be 0 for scsi host device "
"if its controller model is 'lsilogic'"));
goto error;
}
if (dev->info->addr.drive.unit > 7) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("unit must be not more than 7 for scsi host "
"device if its controller model is 'lsilogic'"));
goto error;
}
}
virBufferAddLit(&buf, "scsi-generic");
if (!(contAlias = virDomainControllerAliasFind(def, VIR_DOMAIN_CONTROLLER_TYPE_SCSI,
dev->info->addr.drive.controller)))
goto error;
if (model == VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSILOGIC) {
virBufferAsprintf(&buf, ",bus=%s.%d,scsi-id=%d",
contAlias,
dev->info->addr.drive.bus,
dev->info->addr.drive.unit);
} else {
virBufferAsprintf(&buf, ",bus=%s.0,channel=%d,scsi-id=%d,lun=%d",
contAlias,
dev->info->addr.drive.bus,
dev->info->addr.drive.target,
dev->info->addr.drive.unit);
}
virBufferAsprintf(&buf, ",drive=%s-%s,id=%s",
virDomainDeviceAddressTypeToString(dev->info->type),
dev->info->alias, dev->info->alias);
if (dev->info->bootIndex)
virBufferAsprintf(&buf, ",bootindex=%d", dev->info->bootIndex);
if (virBufferCheckError(&buf) < 0)
goto error;
return virBufferContentAndReset(&buf);
error:
virBufferFreeAndReset(&buf);
return NULL;
}
/* This function outputs a -chardev command line option which describes only the
* host side of the character device */
static char *
qemuBuildChrChardevStr(virDomainChrSourceDefPtr dev, const char *alias,
virQEMUCapsPtr qemuCaps)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
bool telnet;
switch (dev->type) {
case VIR_DOMAIN_CHR_TYPE_NULL:
virBufferAsprintf(&buf, "null,id=char%s", alias);
break;
case VIR_DOMAIN_CHR_TYPE_VC:
virBufferAsprintf(&buf, "vc,id=char%s", alias);
break;
case VIR_DOMAIN_CHR_TYPE_PTY:
virBufferAsprintf(&buf, "pty,id=char%s", alias);
break;
case VIR_DOMAIN_CHR_TYPE_DEV:
virBufferAsprintf(&buf, "%s,id=char%s,path=%s",
STRPREFIX(alias, "parallel") ? "parport" : "tty",
alias, dev->data.file.path);
break;
case VIR_DOMAIN_CHR_TYPE_FILE:
virBufferAsprintf(&buf, "file,id=char%s,path=%s", alias,
dev->data.file.path);
break;
case VIR_DOMAIN_CHR_TYPE_PIPE:
virBufferAsprintf(&buf, "pipe,id=char%s,path=%s", alias,
dev->data.file.path);
break;
case VIR_DOMAIN_CHR_TYPE_STDIO:
virBufferAsprintf(&buf, "stdio,id=char%s", alias);
break;
case VIR_DOMAIN_CHR_TYPE_UDP: {
const char *connectHost = dev->data.udp.connectHost;
const char *bindHost = dev->data.udp.bindHost;
const char *bindService = dev->data.udp.bindService;
if (connectHost == NULL)
connectHost = "";
if (bindHost == NULL)
bindHost = "";
if (bindService == NULL)
bindService = "0";
virBufferAsprintf(&buf,
"udp,id=char%s,host=%s,port=%s,localaddr=%s,"
"localport=%s",
alias,
connectHost,
dev->data.udp.connectService,
bindHost, bindService);
break;
}
case VIR_DOMAIN_CHR_TYPE_TCP:
telnet = dev->data.tcp.protocol == VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNET;
virBufferAsprintf(&buf,
"socket,id=char%s,host=%s,port=%s%s%s",
alias,
dev->data.tcp.host,
dev->data.tcp.service,
telnet ? ",telnet" : "",
dev->data.tcp.listen ? ",server,nowait" : "");
break;
case VIR_DOMAIN_CHR_TYPE_UNIX:
virBufferAsprintf(&buf,
"socket,id=char%s,path=%s%s",
alias,
dev->data.nix.path,
dev->data.nix.listen ? ",server,nowait" : "");
break;
case VIR_DOMAIN_CHR_TYPE_SPICEVMC:
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_CHARDEV_SPICEVMC)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("spicevmc not supported in this QEMU binary"));
goto error;
}
virBufferAsprintf(&buf, "spicevmc,id=char%s,name=%s", alias,
virDomainChrSpicevmcTypeToString(dev->data.spicevmc));
break;
case VIR_DOMAIN_CHR_TYPE_SPICEPORT:
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_CHARDEV_SPICEPORT)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("spiceport not supported in this QEMU binary"));
goto error;
}
virBufferAsprintf(&buf, "spiceport,id=char%s,name=%s", alias,
dev->data.spiceport.channel);
break;
default:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("unsupported chardev '%s'"),
virDomainChrTypeToString(dev->type));
goto error;
}
if (virBufferCheckError(&buf) < 0)
goto error;
return virBufferContentAndReset(&buf);
error:
virBufferFreeAndReset(&buf);
return NULL;
}
static char *
qemuBuildChrArgStr(virDomainChrSourceDefPtr dev, const char *prefix)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
if (prefix)
virBufferAdd(&buf, prefix, strlen(prefix));
switch ((virDomainChrType)dev->type) {
case VIR_DOMAIN_CHR_TYPE_NULL:
virBufferAddLit(&buf, "null");
break;
case VIR_DOMAIN_CHR_TYPE_VC:
virBufferAddLit(&buf, "vc");
break;
case VIR_DOMAIN_CHR_TYPE_PTY:
virBufferAddLit(&buf, "pty");
break;
case VIR_DOMAIN_CHR_TYPE_DEV:
virBufferStrcat(&buf, dev->data.file.path, NULL);
break;
case VIR_DOMAIN_CHR_TYPE_FILE:
virBufferAsprintf(&buf, "file:%s", dev->data.file.path);
break;
case VIR_DOMAIN_CHR_TYPE_PIPE:
virBufferAsprintf(&buf, "pipe:%s", dev->data.file.path);
break;
case VIR_DOMAIN_CHR_TYPE_STDIO:
virBufferAddLit(&buf, "stdio");
break;
case VIR_DOMAIN_CHR_TYPE_UDP: {
const char *connectHost = dev->data.udp.connectHost;
const char *bindHost = dev->data.udp.bindHost;
const char *bindService = dev->data.udp.bindService;
if (connectHost == NULL)
connectHost = "";
if (bindHost == NULL)
bindHost = "";
if (bindService == NULL)
bindService = "0";
virBufferAsprintf(&buf, "udp:%s:%s@%s:%s",
connectHost,
dev->data.udp.connectService,
bindHost,
bindService);
break;
}
case VIR_DOMAIN_CHR_TYPE_TCP:
if (dev->data.tcp.protocol == VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNET) {
virBufferAsprintf(&buf, "telnet:%s:%s%s",
dev->data.tcp.host,
dev->data.tcp.service,
dev->data.tcp.listen ? ",server,nowait" : "");
} else {
virBufferAsprintf(&buf, "tcp:%s:%s%s",
dev->data.tcp.host,
dev->data.tcp.service,
dev->data.tcp.listen ? ",server,nowait" : "");
}
break;
case VIR_DOMAIN_CHR_TYPE_UNIX:
virBufferAsprintf(&buf, "unix:%s%s",
dev->data.nix.path,
dev->data.nix.listen ? ",server,nowait" : "");
break;
case VIR_DOMAIN_CHR_TYPE_SPICEVMC:
case VIR_DOMAIN_CHR_TYPE_SPICEPORT:
case VIR_DOMAIN_CHR_TYPE_NMDM:
case VIR_DOMAIN_CHR_TYPE_LAST:
break;
}
if (virBufferCheckError(&buf) < 0)
goto error;
return virBufferContentAndReset(&buf);
error:
virBufferFreeAndReset(&buf);
return NULL;
}
static char *
qemuBuildVirtioSerialPortDevStr(virDomainDefPtr def,
virDomainChrDefPtr dev,
virQEMUCapsPtr qemuCaps)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
const char *contAlias;
switch (dev->deviceType) {
case VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE:
virBufferAddLit(&buf, "virtconsole");
break;
case VIR_DOMAIN_CHR_DEVICE_TYPE_CHANNEL:
/* Legacy syntax '-device spicevmc' */
if (dev->source.type == VIR_DOMAIN_CHR_TYPE_SPICEVMC &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_SPICEVMC)) {
virBufferAddLit(&buf, "spicevmc");
} else {
virBufferAddLit(&buf, "virtserialport");
}
break;
default:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Cannot use virtio serial for parallel/serial devices"));
return NULL;
}
if (dev->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE &&
dev->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW &&
dev->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_S390) {
/* Check it's a virtio-serial address */
if (dev->info.type !=
VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_SERIAL)
{
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("virtio serial device has invalid address type"));
goto error;
}
contAlias = virDomainControllerAliasFind(def, VIR_DOMAIN_CONTROLLER_TYPE_VIRTIO_SERIAL,
dev->info.addr.vioserial.controller);
if (!contAlias)
goto error;
virBufferAsprintf(&buf, ",bus=%s.%d,nr=%d", contAlias,
dev->info.addr.vioserial.bus,
dev->info.addr.vioserial.port);
}
if (dev->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_CHANNEL &&
dev->source.type == VIR_DOMAIN_CHR_TYPE_SPICEVMC &&
dev->target.name &&
STRNEQ(dev->target.name, "com.redhat.spice.0")) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("Unsupported spicevmc target name '%s'"),
dev->target.name);
goto error;
}
if (!(dev->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_CHANNEL &&
dev->source.type == VIR_DOMAIN_CHR_TYPE_SPICEVMC &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_SPICEVMC))) {
virBufferAsprintf(&buf, ",chardev=char%s,id=%s",
dev->info.alias, dev->info.alias);
if (dev->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_CHANNEL &&
(dev->source.type == VIR_DOMAIN_CHR_TYPE_SPICEVMC ||
dev->target.name)) {
virBufferAsprintf(&buf, ",name=%s", dev->target.name
? dev->target.name : "com.redhat.spice.0");
}
} else {
virBufferAsprintf(&buf, ",id=%s", dev->info.alias);
}
if (virBufferCheckError(&buf) < 0)
goto error;
return virBufferContentAndReset(&buf);
error:
virBufferFreeAndReset(&buf);
return NULL;
}
static char *
qemuBuildSclpDevStr(virDomainChrDefPtr dev)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
if (dev->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE) {
switch (dev->targetType) {
case VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SCLP:
virBufferAddLit(&buf, "sclpconsole");
break;
case VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SCLPLM:
virBufferAddLit(&buf, "sclplmconsole");
break;
}
} else {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Cannot use slcp with devices other than console"));
goto error;
}
virBufferAsprintf(&buf, ",chardev=char%s,id=%s",
dev->info.alias, dev->info.alias);
if (virBufferCheckError(&buf) < 0)
goto error;
return virBufferContentAndReset(&buf);
error:
virBufferFreeAndReset(&buf);
return NULL;
}
static int
qemuBuildRNGBackendChrdevStr(virDomainRNGDefPtr rng,
virQEMUCapsPtr qemuCaps,
char **chr)
{
*chr = NULL;
switch ((virDomainRNGBackend) rng->backend) {
case VIR_DOMAIN_RNG_BACKEND_RANDOM:
case VIR_DOMAIN_RNG_BACKEND_LAST:
/* no chardev backend is needed */
return 0;
case VIR_DOMAIN_RNG_BACKEND_EGD:
if (!(*chr = qemuBuildChrChardevStr(rng->source.chardev,
rng->info.alias, qemuCaps)))
return -1;
}
return 0;
}
int
qemuBuildRNGBackendProps(virDomainRNGDefPtr rng,
virQEMUCapsPtr qemuCaps,
const char **type,
virJSONValuePtr *props)
{
char *charBackendAlias = NULL;
int ret = -1;
switch ((virDomainRNGBackend) rng->backend) {
case VIR_DOMAIN_RNG_BACKEND_RANDOM:
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_RNG_RANDOM)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("this qemu doesn't support the rng-random "
"backend"));
goto cleanup;
}
*type = "rng-random";
if (virJSONValueObjectCreate(props, "s:filename", rng->source.file,
NULL) < 0)
goto cleanup;
break;
case VIR_DOMAIN_RNG_BACKEND_EGD:
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_RNG_EGD)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("this qemu doesn't support the rng-egd "
"backend"));
goto cleanup;
}
*type = "rng-egd";
if (virAsprintf(&charBackendAlias, "char%s", rng->info.alias) < 0)
goto cleanup;
if (virJSONValueObjectCreate(props, "s:chardev", charBackendAlias,
NULL) < 0)
goto cleanup;
break;
case VIR_DOMAIN_RNG_BACKEND_LAST:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("unknown rng-random backend"));
goto cleanup;
}
ret = 0;
cleanup:
VIR_FREE(charBackendAlias);
return ret;
}
static char *
qemuBuildRNGBackendStr(virDomainRNGDefPtr rng,
virQEMUCapsPtr qemuCaps)
{
const char *type = NULL;
char *alias = NULL;
virJSONValuePtr props = NULL;
char *ret = NULL;
if (virAsprintf(&alias, "obj%s", rng->info.alias) < 0)
goto cleanup;
if (qemuBuildRNGBackendProps(rng, qemuCaps, &type, &props) < 0)
goto cleanup;
ret = qemuBuildObjectCommandlineFromJSON(type, alias, props);
cleanup:
VIR_FREE(alias);
virJSONValueFree(props);
return ret;
}
char *
qemuBuildRNGDevStr(virDomainDefPtr def,
virDomainRNGDefPtr dev,
virQEMUCapsPtr qemuCaps)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
if (dev->model != VIR_DOMAIN_RNG_MODEL_VIRTIO ||
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VIRTIO_RNG)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("this qemu doesn't support RNG device type '%s'"),
virDomainRNGModelTypeToString(dev->model));
goto error;
}
if (!qemuCheckCCWS390AddressSupport(def, dev->info, qemuCaps,
dev->source.file))
goto error;
if (dev->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW)
virBufferAsprintf(&buf, "virtio-rng-ccw,rng=obj%s,id=%s",
dev->info.alias, dev->info.alias);
else if (dev->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_S390)
virBufferAsprintf(&buf, "virtio-rng-s390,rng=obj%s,id=%s",
dev->info.alias, dev->info.alias);
else if (dev->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_MMIO)
virBufferAsprintf(&buf, "virtio-rng-device,rng=obj%s,id=%s",
dev->info.alias, dev->info.alias);
else
virBufferAsprintf(&buf, "virtio-rng-pci,rng=obj%s,id=%s",
dev->info.alias, dev->info.alias);
if (dev->rate > 0) {
virBufferAsprintf(&buf, ",max-bytes=%u", dev->rate);
if (dev->period)
virBufferAsprintf(&buf, ",period=%u", dev->period);
else
virBufferAddLit(&buf, ",period=1000");
}
if (qemuBuildDeviceAddressStr(&buf, def, &dev->info, qemuCaps) < 0)
goto error;
if (virBufferCheckError(&buf) < 0)
goto error;
return virBufferContentAndReset(&buf);
error:
virBufferFreeAndReset(&buf);
return NULL;
}
static char *qemuBuildTPMBackendStr(const virDomainDef *def,
virCommandPtr cmd,
virQEMUCapsPtr qemuCaps,
const char *emulator,
int *tpmfd, int *cancelfd)
{
const virDomainTPMDef *tpm = def->tpm;
virBuffer buf = VIR_BUFFER_INITIALIZER;
const char *type = virDomainTPMBackendTypeToString(tpm->type);
char *cancel_path = NULL, *devset = NULL;
const char *tpmdev;
*tpmfd = -1;
*cancelfd = -1;
virBufferAsprintf(&buf, "%s,id=tpm-%s", type, tpm->info.alias);
switch (tpm->type) {
case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH:
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_TPM_PASSTHROUGH))
goto no_support;
tpmdev = tpm->data.passthrough.source.data.file.path;
if (!(cancel_path = virTPMCreateCancelPath(tpmdev)))
goto error;
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_ADD_FD)) {
*tpmfd = open(tpmdev, O_RDWR);
if (*tpmfd < 0) {
virReportSystemError(errno, _("Could not open TPM device %s"),
tpmdev);
goto error;
}
virCommandPassFD(cmd, *tpmfd,
VIR_COMMAND_PASS_FD_CLOSE_PARENT);
devset = qemuVirCommandGetDevSet(cmd, *tpmfd);
if (devset == NULL)
goto error;
*cancelfd = open(cancel_path, O_WRONLY);
if (*cancelfd < 0) {
virReportSystemError(errno,
_("Could not open TPM device's cancel "
"path %s"), cancel_path);
goto error;
}
VIR_FREE(cancel_path);
virCommandPassFD(cmd, *cancelfd,
VIR_COMMAND_PASS_FD_CLOSE_PARENT);
cancel_path = qemuVirCommandGetDevSet(cmd, *cancelfd);
if (cancel_path == NULL)
goto error;
}
virBufferAddLit(&buf, ",path=");
virBufferEscape(&buf, ',', ",", "%s", devset ? devset : tpmdev);
virBufferAddLit(&buf, ",cancel-path=");
virBufferEscape(&buf, ',', ",", "%s", cancel_path);
VIR_FREE(devset);
VIR_FREE(cancel_path);
break;
case VIR_DOMAIN_TPM_TYPE_LAST:
goto error;
}
if (virBufferCheckError(&buf) < 0)
goto error;
return virBufferContentAndReset(&buf);
no_support:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("The QEMU executable %s does not support TPM "
"backend type %s"),
emulator, type);
error:
VIR_FREE(devset);
VIR_FREE(cancel_path);
virBufferFreeAndReset(&buf);
return NULL;
}
static char *qemuBuildTPMDevStr(const virDomainDef *def,
virQEMUCapsPtr qemuCaps,
const char *emulator)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
const virDomainTPMDef *tpm = def->tpm;
const char *model = virDomainTPMModelTypeToString(tpm->model);
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_TPM_TIS)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("The QEMU executable %s does not support TPM "
"model %s"),
emulator, model);
goto error;
}
virBufferAsprintf(&buf, "%s,tpmdev=tpm-%s,id=%s",
model, tpm->info.alias, tpm->info.alias);
if (virBufferCheckError(&buf) < 0)
goto error;
return virBufferContentAndReset(&buf);
error:
virBufferFreeAndReset(&buf);
return NULL;
}
static char *qemuBuildSmbiosBiosStr(virSysinfoBIOSDefPtr def)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
if (!def)
return NULL;
virBufferAddLit(&buf, "type=0");
/* 0:Vendor */
if (def->vendor)
virBufferAsprintf(&buf, ",vendor=%s", def->vendor);
/* 0:BIOS Version */
if (def->version)
virBufferAsprintf(&buf, ",version=%s", def->version);
/* 0:BIOS Release Date */
if (def->date)
virBufferAsprintf(&buf, ",date=%s", def->date);
/* 0:System BIOS Major Release and 0:System BIOS Minor Release */
if (def->release)
virBufferAsprintf(&buf, ",release=%s", def->release);
if (virBufferCheckError(&buf) < 0)
goto error;
return virBufferContentAndReset(&buf);
error:
virBufferFreeAndReset(&buf);
return NULL;
}
static char *qemuBuildSmbiosSystemStr(virSysinfoSystemDefPtr def,
bool skip_uuid)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
if (!def ||
(!def->manufacturer && !def->product && !def->version &&
!def->serial && (!def->uuid || skip_uuid) &&
def->sku && !def->family))
return NULL;
virBufferAddLit(&buf, "type=1");
/* 1:Manufacturer */
if (def->manufacturer)
virBufferAsprintf(&buf, ",manufacturer=%s",
def->manufacturer);
/* 1:Product Name */
if (def->product)
virBufferAsprintf(&buf, ",product=%s", def->product);
/* 1:Version */
if (def->version)
virBufferAsprintf(&buf, ",version=%s", def->version);
/* 1:Serial Number */
if (def->serial)
virBufferAsprintf(&buf, ",serial=%s", def->serial);
/* 1:UUID */
if (def->uuid && !skip_uuid)
virBufferAsprintf(&buf, ",uuid=%s", def->uuid);
/* 1:SKU Number */
if (def->sku)
virBufferAsprintf(&buf, ",sku=%s", def->sku);
/* 1:Family */
if (def->family)
virBufferAsprintf(&buf, ",family=%s", def->family);
if (virBufferCheckError(&buf) < 0)
goto error;
return virBufferContentAndReset(&buf);
error:
virBufferFreeAndReset(&buf);
return NULL;
}
static char *qemuBuildSmbiosBaseBoardStr(virSysinfoBaseBoardDefPtr def)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
if (!def)
return NULL;
virBufferAddLit(&buf, "type=2");
/* 2:Manufacturer */
if (def->manufacturer)
virBufferAsprintf(&buf, ",manufacturer=%s",
def->manufacturer);
/* 2:Product Name */
if (def->product)
virBufferAsprintf(&buf, ",product=%s", def->product);
/* 2:Version */
if (def->version)
virBufferAsprintf(&buf, ",version=%s", def->version);
/* 2:Serial Number */
if (def->serial)
virBufferAsprintf(&buf, ",serial=%s", def->serial);
/* 2:Asset Tag */
if (def->asset)
virBufferAsprintf(&buf, ",asset=%s", def->asset);
/* 2:Location */
if (def->location)
virBufferAsprintf(&buf, ",location=%s", def->location);
if (virBufferCheckError(&buf) < 0)
goto error;
return virBufferContentAndReset(&buf);
error:
virBufferFreeAndReset(&buf);
return NULL;
}
static char *
qemuBuildClockArgStr(virDomainClockDefPtr def)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
switch (def->offset) {
case VIR_DOMAIN_CLOCK_OFFSET_UTC:
virBufferAddLit(&buf, "base=utc");
break;
case VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME:
case VIR_DOMAIN_CLOCK_OFFSET_TIMEZONE:
virBufferAddLit(&buf, "base=localtime");
break;
case VIR_DOMAIN_CLOCK_OFFSET_VARIABLE: {
time_t now = time(NULL);
struct tm nowbits;
if (def->data.variable.basis == VIR_DOMAIN_CLOCK_BASIS_LOCALTIME) {
long localOffset;
/* in the case of basis='localtime', rather than trying to
* keep that basis (and associated offset from UTC) in the
* status and deal with adding in the difference each time
* there is an RTC_CHANGE event, it is simpler and less
* error prone to just convert the adjustment an offset
* from UTC right now (and change the status to
* "basis='utc' to reflect this). This eliminates
* potential errors in both RTC_CHANGE events and in
* migration (in the case that the status of DST, or the
* timezone of the destination host, changed relative to
* startup).
*/
if (virTimeLocalOffsetFromUTC(&localOffset) < 0)
goto error;
def->data.variable.adjustment += localOffset;
def->data.variable.basis = VIR_DOMAIN_CLOCK_BASIS_UTC;
}
now += def->data.variable.adjustment;
gmtime_r(&now, &nowbits);
/* when an RTC_CHANGE event is received from qemu, we need to
* have the adjustment used at domain start time available to
* compute the new offset from UTC. As this new value is
* itself stored in def->data.variable.adjustment, we need to
* save a copy of it now.
*/
def->data.variable.adjustment0 = def->data.variable.adjustment;
virBufferAsprintf(&buf, "base=%d-%02d-%02dT%02d:%02d:%02d",
nowbits.tm_year + 1900,
nowbits.tm_mon + 1,
nowbits.tm_mday,
nowbits.tm_hour,
nowbits.tm_min,
nowbits.tm_sec);
} break;
default:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("unsupported clock offset '%s'"),
virDomainClockOffsetTypeToString(def->offset));
goto error;
}
/* Look for an 'rtc' timer element, and add in appropriate clock= and driftfix= */
size_t i;
for (i = 0; i < def->ntimers; i++) {
if (def->timers[i]->name == VIR_DOMAIN_TIMER_NAME_RTC) {
switch (def->timers[i]->track) {
case -1: /* unspecified - use hypervisor default */
break;
case VIR_DOMAIN_TIMER_TRACK_BOOT:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("unsupported rtc timer track '%s'"),
virDomainTimerTrackTypeToString(def->timers[i]->track));
goto error;
case VIR_DOMAIN_TIMER_TRACK_GUEST:
virBufferAddLit(&buf, ",clock=vm");
break;
case VIR_DOMAIN_TIMER_TRACK_WALL:
virBufferAddLit(&buf, ",clock=host");
break;
}
switch (def->timers[i]->tickpolicy) {
case -1:
case VIR_DOMAIN_TIMER_TICKPOLICY_DELAY:
/* This is the default - missed ticks delivered when
next scheduled, at normal rate */
break;
case VIR_DOMAIN_TIMER_TICKPOLICY_CATCHUP:
/* deliver ticks at a faster rate until caught up */
virBufferAddLit(&buf, ",driftfix=slew");
break;
case VIR_DOMAIN_TIMER_TICKPOLICY_MERGE:
case VIR_DOMAIN_TIMER_TICKPOLICY_DISCARD:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("unsupported rtc timer tickpolicy '%s'"),
virDomainTimerTickpolicyTypeToString(def->timers[i]->tickpolicy));
goto error;
}
break; /* no need to check other timers - there is only one rtc */
}
}
if (virBufferCheckError(&buf) < 0)
goto error;
return virBufferContentAndReset(&buf);
error:
virBufferFreeAndReset(&buf);
return NULL;
}
static int
qemuBuildCpuModelArgStr(virQEMUDriverPtr driver,
const virDomainDef *def,
virBufferPtr buf,
virQEMUCapsPtr qemuCaps,
bool *hasHwVirt,
bool migrating)
{
int ret = -1;
size_t i;
virCPUDefPtr host = NULL;
virCPUDefPtr guest = NULL;
virCPUDefPtr cpu = NULL;
virCPUDefPtr featCpu = NULL;
size_t ncpus = 0;
char **cpus = NULL;
virCPUDataPtr data = NULL;
virCPUDataPtr hostData = NULL;
char *compare_msg = NULL;
virCPUCompareResult cmp;
const char *preferred;
virCapsPtr caps = NULL;
bool compareAgainstHost = ((def->virtType == VIR_DOMAIN_VIRT_KVM ||
def->cpu->mode != VIR_CPU_MODE_CUSTOM) &&
def->cpu->mode != VIR_CPU_MODE_HOST_PASSTHROUGH);
if (!(caps = virQEMUDriverGetCapabilities(driver, false)))
goto cleanup;
host = caps->host.cpu;
if (!host ||
!host->model ||
(ncpus = virQEMUCapsGetCPUDefinitions(qemuCaps, &cpus)) == 0) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("CPU specification not supported by hypervisor"));
goto cleanup;
}
if (!(cpu = virCPUDefCopy(def->cpu)))
goto cleanup;
if (cpu->mode == VIR_CPU_MODE_HOST_MODEL &&
!migrating &&
cpuUpdate(cpu, host) < 0)
goto cleanup;
/* For non-KVM, CPU features are emulated, so host compat doesn't matter */
if (compareAgainstHost) {
bool noTSX = false;
cmp = cpuGuestData(host, cpu, &data, &compare_msg);
switch (cmp) {
case VIR_CPU_COMPARE_INCOMPATIBLE:
if (cpuEncode(host->arch, host, NULL, &hostData,
NULL, NULL, NULL, NULL) == 0 &&
(!cpuHasFeature(hostData, "hle") ||
!cpuHasFeature(hostData, "rtm")) &&
(STREQ_NULLABLE(cpu->model, "Haswell") ||
STREQ_NULLABLE(cpu->model, "Broadwell")))
noTSX = true;
if (compare_msg) {
if (noTSX) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("guest and host CPU are not compatible: "
"%s; try using '%s-noTSX' CPU model"),
compare_msg, cpu->model);
} else {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("guest and host CPU are not compatible: "
"%s"),
compare_msg);
}
} else {
if (noTSX) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("guest CPU is not compatible with host "
"CPU; try using '%s-noTSX' CPU model"),
cpu->model);
} else {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("guest CPU is not compatible with host "
"CPU"));
}
}
/* fall through */
case VIR_CPU_COMPARE_ERROR:
goto cleanup;
default:
break;
}
}
/* Only 'svm' requires --enable-nesting. The nested
* 'vmx' patches now simply hook off the CPU features
*/
if ((def->os.arch == VIR_ARCH_X86_64 || def->os.arch == VIR_ARCH_I686) &&
compareAgainstHost) {
int hasSVM = cpuHasFeature(data, "svm");
if (hasSVM < 0)
goto cleanup;
*hasHwVirt = hasSVM > 0 ? true : false;
}
if ((cpu->mode == VIR_CPU_MODE_HOST_PASSTHROUGH) ||
((cpu->mode == VIR_CPU_MODE_HOST_MODEL) &&
ARCH_IS_PPC64(def->os.arch))) {
const char *mode = virCPUModeTypeToString(cpu->mode);
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_CPU_HOST)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("CPU mode '%s' is not supported by QEMU"
" binary"), mode);
goto cleanup;
}
if (def->virtType != VIR_DOMAIN_VIRT_KVM) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("CPU mode '%s' is only supported with kvm"),
mode);
goto cleanup;
}
virBufferAddLit(buf, "host");
if (def->os.arch == VIR_ARCH_ARMV7L &&
host->arch == VIR_ARCH_AARCH64) {
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_CPU_AARCH64_OFF)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("QEMU binary does not support CPU "
"host-passthrough for armv7l on "
"aarch64 host"));
goto cleanup;
}
virBufferAddLit(buf, ",aarch64=off");
}
if (ARCH_IS_PPC64(def->os.arch) &&
cpu->mode == VIR_CPU_MODE_HOST_MODEL &&
def->cpu->model != NULL) {
virBufferAsprintf(buf, ",compat=%s", def->cpu->model);
} else {
featCpu = cpu;
}
} else {
if (VIR_ALLOC(guest) < 0)
goto cleanup;
if (VIR_STRDUP(guest->vendor_id, cpu->vendor_id) < 0)
goto cleanup;
if (compareAgainstHost) {
guest->arch = host->arch;
if (cpu->match == VIR_CPU_MATCH_MINIMUM)
preferred = host->model;
else
preferred = cpu->model;
guest->type = VIR_CPU_TYPE_GUEST;
guest->fallback = cpu->fallback;
if (cpuDecode(guest, data,
(const char **)cpus, ncpus, preferred) < 0)
goto cleanup;
} else {
guest->arch = def->os.arch;
if (VIR_STRDUP(guest->model, cpu->model) < 0)
goto cleanup;
}
virBufferAdd(buf, guest->model, -1);
if (guest->vendor_id)
virBufferAsprintf(buf, ",vendor=%s", guest->vendor_id);
featCpu = guest;
}
if (featCpu) {
for (i = 0; i < featCpu->nfeatures; i++) {
char sign;
if (featCpu->features[i].policy == VIR_CPU_FEATURE_DISABLE)
sign = '-';
else
sign = '+';
virBufferAsprintf(buf, ",%c%s", sign, featCpu->features[i].name);
}
}
ret = 0;
cleanup:
virObjectUnref(caps);
VIR_FREE(compare_msg);
cpuDataFree(data);
cpuDataFree(hostData);
virCPUDefFree(guest);
virCPUDefFree(cpu);
return ret;
}
static int
qemuBuildCpuArgStr(virQEMUDriverPtr driver,
const virDomainDef *def,
const char *emulator,
virQEMUCapsPtr qemuCaps,
virArch hostarch,
char **opt,
bool *hasHwVirt,
bool migrating)
{
const char *default_model;
bool have_cpu = false;
int ret = -1;
virBuffer buf = VIR_BUFFER_INITIALIZER;
size_t i;
*hasHwVirt = false;
if (def->os.arch == VIR_ARCH_I686)
default_model = "qemu32";
else
default_model = "qemu64";
if (def->cpu &&
(def->cpu->mode != VIR_CPU_MODE_CUSTOM || def->cpu->model)) {
if (qemuBuildCpuModelArgStr(driver, def, &buf, qemuCaps,
hasHwVirt, migrating) < 0)
goto cleanup;
have_cpu = true;
} else {
/*
* Need to force a 32-bit guest CPU type if
*
* 1. guest OS is i686
* 2. host OS is x86_64
* 3. emulator is qemu-kvm or kvm
*
* Or
*
* 1. guest OS is i686
* 2. emulator is qemu-system-x86_64
*/
if (def->os.arch == VIR_ARCH_I686 &&
((hostarch == VIR_ARCH_X86_64 &&
strstr(emulator, "kvm")) ||
strstr(emulator, "x86_64"))) {
virBufferAdd(&buf, default_model, -1);
have_cpu = true;
}
}
/* Handle paravirtual timers */
for (i = 0; i < def->clock.ntimers; i++) {
virDomainTimerDefPtr timer = def->clock.timers[i];
if (timer->present == -1)
continue;
if (timer->name == VIR_DOMAIN_TIMER_NAME_KVMCLOCK) {
virBufferAsprintf(&buf, "%s,%ckvmclock",
have_cpu ? "" : default_model,
timer->present ? '+' : '-');
have_cpu = true;
} else if (timer->name == VIR_DOMAIN_TIMER_NAME_HYPERVCLOCK &&
timer->present) {
virBufferAsprintf(&buf, "%s,hv_time",
have_cpu ? "" : default_model);
have_cpu = true;
}
}
if (def->apic_eoi) {
char sign;
if (def->apic_eoi == VIR_TRISTATE_SWITCH_ON)
sign = '+';
else
sign = '-';
virBufferAsprintf(&buf, "%s,%ckvm_pv_eoi",
have_cpu ? "" : default_model,
sign);
have_cpu = true;
}
if (def->features[VIR_DOMAIN_FEATURE_PVSPINLOCK]) {
char sign;
if (def->features[VIR_DOMAIN_FEATURE_PVSPINLOCK] == VIR_TRISTATE_SWITCH_ON)
sign = '+';
else
sign = '-';
virBufferAsprintf(&buf, "%s,%ckvm_pv_unhalt",
have_cpu ? "" : default_model,
sign);
have_cpu = true;
}
if (def->features[VIR_DOMAIN_FEATURE_HYPERV] == VIR_TRISTATE_SWITCH_ON) {
if (!have_cpu) {
virBufferAdd(&buf, default_model, -1);
have_cpu = true;
}
for (i = 0; i < VIR_DOMAIN_HYPERV_LAST; i++) {
switch ((virDomainHyperv) i) {
case VIR_DOMAIN_HYPERV_RELAXED:
case VIR_DOMAIN_HYPERV_VAPIC:
if (def->hyperv_features[i] == VIR_TRISTATE_SWITCH_ON)
virBufferAsprintf(&buf, ",hv_%s",
virDomainHypervTypeToString(i));
break;
case VIR_DOMAIN_HYPERV_SPINLOCKS:
if (def->hyperv_features[i] == VIR_TRISTATE_SWITCH_ON)
virBufferAsprintf(&buf, ",hv_spinlocks=0x%x",
def->hyperv_spinlocks);
break;
/* coverity[dead_error_begin] */
case VIR_DOMAIN_HYPERV_LAST:
break;
}
}
}
if (def->features[VIR_DOMAIN_FEATURE_KVM] == VIR_TRISTATE_SWITCH_ON) {
if (!have_cpu) {
virBufferAdd(&buf, default_model, -1);
have_cpu = true;
}
for (i = 0; i < VIR_DOMAIN_KVM_LAST; i++) {
switch ((virDomainKVM) i) {
case VIR_DOMAIN_KVM_HIDDEN:
if (def->kvm_features[i] == VIR_TRISTATE_SWITCH_ON)
virBufferAddLit(&buf, ",kvm=off");
break;
/* coverity[dead_error_begin] */
case VIR_DOMAIN_KVM_LAST:
break;
}
}
}
if (def->features[VIR_DOMAIN_FEATURE_PMU]) {
virTristateSwitch pmu = def->features[VIR_DOMAIN_FEATURE_PMU];
if (!have_cpu)
virBufferAdd(&buf, default_model, -1);
virBufferAsprintf(&buf, ",pmu=%s",
virTristateSwitchTypeToString(pmu));
have_cpu = true;
}
if (def->features[VIR_DOMAIN_FEATURE_GIC] == VIR_TRISTATE_SWITCH_ON) {
if (def->gic_version && def->gic_version != 2) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("gic version '%u' is not supported"),
def->gic_version);
goto cleanup;
}
/* There's no command line argument currently to turn on/off GIC. It's
* done automatically by qemu-system-aarch64. But if this changes, lets
* put the code here. */
}
if (virBufferCheckError(&buf) < 0)
goto cleanup;
*opt = virBufferContentAndReset(&buf);
ret = 0;
cleanup:
return ret;
}
static int
qemuBuildObsoleteAccelArg(virCommandPtr cmd,
const virDomainDef *def,
virQEMUCapsPtr qemuCaps)
{
bool disableKQEMU = false;
bool enableKQEMU = false;
bool disableKVM = false;
bool enableKVM = false;
switch (def->virtType) {
case VIR_DOMAIN_VIRT_QEMU:
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_KQEMU))
disableKQEMU = true;
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_KVM))
disableKVM = true;
break;
case VIR_DOMAIN_VIRT_KQEMU:
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_KVM))
disableKVM = true;
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_ENABLE_KQEMU)) {
enableKQEMU = true;
} else if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_KQEMU)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("the QEMU binary does not support kqemu"));
return -1;
}
break;
case VIR_DOMAIN_VIRT_KVM:
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_KQEMU))
disableKQEMU = true;
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_ENABLE_KVM)) {
enableKVM = true;
} else if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_KVM)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("the QEMU binary does not support kvm"));
return -1;
}
break;
case VIR_DOMAIN_VIRT_XEN:
/* XXX better check for xenner */
break;
default:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("the QEMU binary does not support %s"),
virDomainVirtTypeToString(def->virtType));
return -1;
}
if (disableKQEMU)
virCommandAddArg(cmd, "-no-kqemu");
else if (enableKQEMU)
virCommandAddArgList(cmd, "-enable-kqemu", "-kernel-kqemu", NULL);
if (disableKVM)
virCommandAddArg(cmd, "-no-kvm");
if (enableKVM)
virCommandAddArg(cmd, "-enable-kvm");
return 0;
}
static bool
qemuAppendKeyWrapMachineParm(virBuffer *buf, virQEMUCapsPtr qemuCaps,
int flag, const char *pname, int pstate)
{
if (pstate != VIR_TRISTATE_SWITCH_ABSENT) {
if (!virQEMUCapsGet(qemuCaps, flag)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("%s is not available with this QEMU binary"), pname);
return false;
}
virBufferAsprintf(buf, ",%s=%s", pname,
virTristateSwitchTypeToString(pstate));
}
return true;
}
static bool
qemuAppendKeyWrapMachineParms(virBuffer *buf, virQEMUCapsPtr qemuCaps,
const virDomainKeyWrapDef *keywrap)
{
if (!qemuAppendKeyWrapMachineParm(buf, qemuCaps, QEMU_CAPS_AES_KEY_WRAP,
"aes-key-wrap", keywrap->aes))
return false;
if (!qemuAppendKeyWrapMachineParm(buf, qemuCaps, QEMU_CAPS_DEA_KEY_WRAP,
"dea-key-wrap", keywrap->dea))
return false;
return true;
}
static int
qemuBuildMachineArgStr(virCommandPtr cmd,
const virDomainDef *def,
virQEMUCapsPtr qemuCaps)
{
bool obsoleteAccel = false;
/* This should *never* be NULL, since we always provide
* a machine in the capabilities data for QEMU. So this
* check is just here as a safety in case the unexpected
* happens */
if (!def->os.machine)
return 0;
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_MACHINE_OPT)) {
/* if no parameter to the machine type is needed, we still use
* '-M' to keep the most of the compatibility with older versions.
*/
virCommandAddArgList(cmd, "-M", def->os.machine, NULL);
if (def->mem.dump_core) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("dump-guest-core is not available "
"with this QEMU binary"));
return -1;
}
if (def->mem.nosharepages) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("disable shared memory is not available "
"with this QEMU binary"));
return -1;
}
obsoleteAccel = true;
if (def->keywrap) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("key wrap support is not available "
"with this QEMU binary"));
return -1;
}
} else {
virBuffer buf = VIR_BUFFER_INITIALIZER;
virTristateSwitch vmport = def->features[VIR_DOMAIN_FEATURE_VMPORT];
virCommandAddArg(cmd, "-machine");
virBufferAdd(&buf, def->os.machine, -1);
if (def->virtType == VIR_DOMAIN_VIRT_QEMU)
virBufferAddLit(&buf, ",accel=tcg");
else if (def->virtType == VIR_DOMAIN_VIRT_KVM)
virBufferAddLit(&buf, ",accel=kvm");
else
obsoleteAccel = true;
/* To avoid the collision of creating USB controllers when calling
* machine->init in QEMU, it needs to set usb=off
*/
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_MACHINE_USB_OPT))
virBufferAddLit(&buf, ",usb=off");
if (vmport) {
if (!virQEMUCapsSupportsVmport(qemuCaps, def)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("vmport is not available "
"with this QEMU binary"));
virBufferFreeAndReset(&buf);
return -1;
}
virBufferAsprintf(&buf, ",vmport=%s",
virTristateSwitchTypeToString(vmport));
}
if (def->mem.dump_core) {
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DUMP_GUEST_CORE)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("dump-guest-core is not available "
"with this QEMU binary"));
virBufferFreeAndReset(&buf);
return -1;
}
virBufferAsprintf(&buf, ",dump-guest-core=%s",
virTristateSwitchTypeToString(def->mem.dump_core));
}
if (def->mem.nosharepages) {
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_MEM_MERGE)) {
virBufferAddLit(&buf, ",mem-merge=off");
} else {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("disable shared memory is not available "
"with this QEMU binary"));
virBufferFreeAndReset(&buf);
return -1;
}
}
if (def->keywrap &&
!qemuAppendKeyWrapMachineParms(&buf, qemuCaps, def->keywrap)) {
virBufferFreeAndReset(&buf);
return -1;
}
virCommandAddArgBuffer(cmd, &buf);
}
if (obsoleteAccel &&
qemuBuildObsoleteAccelArg(cmd, def, qemuCaps) < 0)
return -1;
return 0;
}
static char *
qemuBuildSmpArgStr(const virDomainDef *def,
virQEMUCapsPtr qemuCaps)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
virBufferAsprintf(&buf, "%u", def->vcpus);
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_SMP_TOPOLOGY)) {
if (def->vcpus != def->maxvcpus)
virBufferAsprintf(&buf, ",maxcpus=%u", def->maxvcpus);
/* sockets, cores, and threads are either all zero
* or all non-zero, thus checking one of them is enough */
if (def->cpu && def->cpu->sockets) {
virBufferAsprintf(&buf, ",sockets=%u", def->cpu->sockets);
virBufferAsprintf(&buf, ",cores=%u", def->cpu->cores);
virBufferAsprintf(&buf, ",threads=%u", def->cpu->threads);
} else {
virBufferAsprintf(&buf, ",sockets=%u", def->maxvcpus);
virBufferAsprintf(&buf, ",cores=%u", 1);
virBufferAsprintf(&buf, ",threads=%u", 1);
}
} else if (def->vcpus != def->maxvcpus) {
virBufferFreeAndReset(&buf);
/* FIXME - consider hot-unplugging cpus after boot for older qemu */
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("setting current vcpu count less than maximum is "
"not supported with this QEMU binary"));
return NULL;
}
if (virBufferCheckError(&buf) < 0)
return NULL;
return virBufferContentAndReset(&buf);
}
static int
qemuBuildNumaArgStr(virQEMUDriverConfigPtr cfg,
virDomainDefPtr def,
virCommandPtr cmd,
virQEMUCapsPtr qemuCaps,
virBitmapPtr auto_nodeset)
{
size_t i;
virBuffer buf = VIR_BUFFER_INITIALIZER;
char *cpumask = NULL, *tmpmask = NULL, *next = NULL;
char **nodeBackends = NULL;
bool needBackend = false;
int rc;
int ret = -1;
size_t ncells = virDomainNumaGetNodeCount(def->numa);
const long system_page_size = virGetSystemPageSizeKB();
if (virDomainNumatuneHasPerNodeBinding(def->numa) &&
!(virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_RAM) ||
virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_FILE))) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Per-node memory binding is not supported "
"with this QEMU"));
goto cleanup;
}
if (def->mem.nhugepages &&
def->mem.hugepages[0].size != system_page_size &&
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_FILE)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("huge pages per NUMA node are not "
"supported with this QEMU"));
goto cleanup;
}
if (!virDomainNumatuneNodesetIsAvailable(def->numa, auto_nodeset))
goto cleanup;
for (i = 0; i < def->mem.nhugepages; i++) {
ssize_t next_bit, pos = 0;
if (!def->mem.hugepages[i].nodemask) {
/* This is the master hugepage to use. Skip it as it has no
* nodemask anyway. */
continue;
}
if (ncells) {
/* Fortunately, we allow only guest NUMA nodes to be continuous
* starting from zero. */
pos = ncells - 1;
}
next_bit = virBitmapNextSetBit(def->mem.hugepages[i].nodemask, pos);
if (next_bit >= 0) {
virReportError(VIR_ERR_XML_DETAIL,
_("hugepages: node %zd not found"),
next_bit);
goto cleanup;
}
}
if (VIR_ALLOC_N(nodeBackends, ncells) < 0)
goto cleanup;
/* using of -numa memdev= cannot be combined with -numa mem=, thus we
* need to check which approach to use */
for (i = 0; i < ncells; i++) {
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_RAM) ||
virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_FILE)) {
if ((rc = qemuBuildMemoryCellBackendStr(def, qemuCaps, cfg, i,
auto_nodeset,
&nodeBackends[i])) < 0)
goto cleanup;
if (rc == 0)
needBackend = true;
} else {
if (virDomainNumaGetNodeMemoryAccessMode(def->numa, i)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Shared memory mapping is not supported "
"with this QEMU"));
goto cleanup;
}
}
}
for (i = 0; i < ncells; i++) {
VIR_FREE(cpumask);
if (!(cpumask = virBitmapFormat(virDomainNumaGetNodeCpumask(def->numa, i))))
goto cleanup;
if (strchr(cpumask, ',') &&
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_NUMA)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("disjoint NUMA cpu ranges are not supported "
"with this QEMU"));
goto cleanup;
}
if (needBackend)
virCommandAddArgList(cmd, "-object", nodeBackends[i], NULL);
virCommandAddArg(cmd, "-numa");
virBufferAsprintf(&buf, "node,nodeid=%zu", i);
for (tmpmask = cpumask; tmpmask; tmpmask = next) {
if ((next = strchr(tmpmask, ',')))
*(next++) = '\0';
virBufferAddLit(&buf, ",cpus=");
virBufferAdd(&buf, tmpmask, -1);
}
if (needBackend)
virBufferAsprintf(&buf, ",memdev=ram-node%zu", i);
else
virBufferAsprintf(&buf, ",mem=%llu",
virDomainNumaGetNodeMemorySize(def->numa, i) / 1024);
virCommandAddArgBuffer(cmd, &buf);
}
ret = 0;
cleanup:
VIR_FREE(cpumask);
if (nodeBackends) {
for (i = 0; i < ncells; i++)
VIR_FREE(nodeBackends[i]);
VIR_FREE(nodeBackends);
}
virBufferFreeAndReset(&buf);
return ret;
}
static int
qemuBuildGraphicsVNCCommandLine(virQEMUDriverConfigPtr cfg,
virCommandPtr cmd,
virDomainDefPtr def,
virQEMUCapsPtr qemuCaps,
virDomainGraphicsDefPtr graphics)
{
virBuffer opt = VIR_BUFFER_INITIALIZER;
const char *listenNetwork;
const char *listenAddr = NULL;
char *netAddr = NULL;
bool escapeAddr;
int ret;
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_VNC)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("vnc graphics are not supported with this QEMU"));
goto error;
}
if (graphics->data.vnc.socket || cfg->vncAutoUnixSocket) {
if (!graphics->data.vnc.socket &&
virAsprintf(&graphics->data.vnc.socket,
"%s/domain-%s/vnc.sock", cfg->libDir, def->name) == -1)
goto error;
virBufferAsprintf(&opt, "unix:%s", graphics->data.vnc.socket);
} else {
if (!graphics->data.vnc.autoport &&
(graphics->data.vnc.port < 5900 ||
graphics->data.vnc.port > 65535)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("vnc port must be in range [5900,65535]"));
goto error;
}
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_VNC_COLON)) {
switch (virDomainGraphicsListenGetType(graphics, 0)) {
case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS:
listenAddr = virDomainGraphicsListenGetAddress(graphics, 0);
break;
case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK:
listenNetwork = virDomainGraphicsListenGetNetwork(graphics, 0);
if (!listenNetwork)
break;
ret = networkGetNetworkAddress(listenNetwork, &netAddr);
if (ret <= -2) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
"%s", _("network-based listen not possible, "
"network driver not present"));
goto error;
}
if (ret < 0)
goto error;
listenAddr = netAddr;
/* store the address we found in the element so it
* will show up in status. */
if (virDomainGraphicsListenSetAddress(graphics, 0,
listenAddr, -1, false) < 0)
goto error;
break;
}
if (!listenAddr)
listenAddr = cfg->vncListen;
escapeAddr = strchr(listenAddr, ':') != NULL;
if (escapeAddr)
virBufferAsprintf(&opt, "[%s]", listenAddr);
else
virBufferAdd(&opt, listenAddr, -1);
virBufferAsprintf(&opt, ":%d",
graphics->data.vnc.port - 5900);
VIR_FREE(netAddr);
} else {
virBufferAsprintf(&opt, "%d",
graphics->data.vnc.port - 5900);
}
}
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_VNC_COLON)) {
if (!graphics->data.vnc.socket &&
graphics->data.vnc.websocket) {
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_VNC_WEBSOCKET)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("VNC WebSockets are not supported "
"with this QEMU binary"));
goto error;
}
virBufferAsprintf(&opt, ",websocket=%d", graphics->data.vnc.websocket);
}
if (graphics->data.vnc.sharePolicy) {
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_VNC_SHARE_POLICY)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("vnc display sharing policy is not "
"supported with this QEMU"));
goto error;
}
virBufferAsprintf(&opt, ",share=%s",
virDomainGraphicsVNCSharePolicyTypeToString(
graphics->data.vnc.sharePolicy));
}
if (graphics->data.vnc.auth.passwd || cfg->vncPassword)
virBufferAddLit(&opt, ",password");
if (cfg->vncTLS) {
virBufferAddLit(&opt, ",tls");
if (cfg->vncTLSx509verify)
virBufferAsprintf(&opt, ",x509verify=%s", cfg->vncTLSx509certdir);
else
virBufferAsprintf(&opt, ",x509=%s", cfg->vncTLSx509certdir);
}
if (cfg->vncSASL) {
virBufferAddLit(&opt, ",sasl");
if (cfg->vncSASLdir)
virCommandAddEnvPair(cmd, "SASL_CONF_PATH", cfg->vncSASLdir);
/* TODO: Support ACLs later */
}
}
virCommandAddArg(cmd, "-vnc");
virCommandAddArgBuffer(cmd, &opt);
if (graphics->data.vnc.keymap)
virCommandAddArgList(cmd, "-k", graphics->data.vnc.keymap, NULL);
/* Unless user requested it, set the audio backend to none, to
* prevent it opening the host OS audio devices, since that causes
* security issues and might not work when using VNC.
*/
if (cfg->vncAllowHostAudio)
virCommandAddEnvPassBlockSUID(cmd, "QEMU_AUDIO_DRV", NULL);
else
virCommandAddEnvString(cmd, "QEMU_AUDIO_DRV=none");
return 0;
error:
VIR_FREE(netAddr);
virBufferFreeAndReset(&opt);
return -1;
}
static int
qemuBuildGraphicsSPICECommandLine(virQEMUDriverConfigPtr cfg,
virCommandPtr cmd,
virQEMUCapsPtr qemuCaps,
virDomainGraphicsDefPtr graphics)
{
virBuffer opt = VIR_BUFFER_INITIALIZER;
const char *listenNetwork;
const char *listenAddr = NULL;
char *netAddr = NULL;
int ret;
int defaultMode = graphics->data.spice.defaultMode;
int port = graphics->data.spice.port;
int tlsPort = graphics->data.spice.tlsPort;
size_t i;
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SPICE)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("spice graphics are not supported with this QEMU"));
goto error;
}
if (port > 0 || tlsPort <= 0)
virBufferAsprintf(&opt, "port=%u", port);
if (tlsPort > 0) {
if (!cfg->spiceTLS) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("spice TLS port set in XML configuration,"
" but TLS is disabled in qemu.conf"));
goto error;
}
if (port > 0)
virBufferAddChar(&opt, ',');
virBufferAsprintf(&opt, "tls-port=%u", tlsPort);
}
if (cfg->spiceSASL) {
virBufferAddLit(&opt, ",sasl");
if (cfg->spiceSASLdir)
virCommandAddEnvPair(cmd, "SASL_CONF_PATH",
cfg->spiceSASLdir);
/* TODO: Support ACLs later */
}
switch (virDomainGraphicsListenGetType(graphics, 0)) {
case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS:
listenAddr = virDomainGraphicsListenGetAddress(graphics, 0);
break;
case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK:
listenNetwork = virDomainGraphicsListenGetNetwork(graphics, 0);
if (!listenNetwork)
break;
ret = networkGetNetworkAddress(listenNetwork, &netAddr);
if (ret <= -2) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
"%s", _("network-based listen not possible, "
"network driver not present"));
goto error;
}
if (ret < 0)
goto error;
listenAddr = netAddr;
/* store the address we found in the element so it will
* show up in status. */
if (virDomainGraphicsListenSetAddress(graphics, 0,
listenAddr, -1, false) < 0)
goto error;
break;
}
if (!listenAddr)
listenAddr = cfg->spiceListen;
if (listenAddr)
virBufferAsprintf(&opt, ",addr=%s", listenAddr);
VIR_FREE(netAddr);
if (graphics->data.spice.mousemode) {
switch (graphics->data.spice.mousemode) {
case VIR_DOMAIN_GRAPHICS_SPICE_MOUSE_MODE_SERVER:
virBufferAddLit(&opt, ",agent-mouse=off");
break;
case VIR_DOMAIN_GRAPHICS_SPICE_MOUSE_MODE_CLIENT:
virBufferAddLit(&opt, ",agent-mouse=on");
break;
default:
break;
}
}
/* In the password case we set it via monitor command, to avoid
* making it visible on CLI, so there's no use of password=XXX
* in this bit of the code */
if (!graphics->data.spice.auth.passwd &&
!cfg->spicePassword)
virBufferAddLit(&opt, ",disable-ticketing");
if (tlsPort > 0)
virBufferAsprintf(&opt, ",x509-dir=%s", cfg->spiceTLSx509certdir);
switch (defaultMode) {
case VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_SECURE:
virBufferAddLit(&opt, ",tls-channel=default");
break;
case VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_INSECURE:
virBufferAddLit(&opt, ",plaintext-channel=default");
break;
case VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_ANY:
/* nothing */
break;
}
for (i = 0; i < VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_LAST; i++) {
switch (graphics->data.spice.channels[i]) {
case VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_SECURE:
if (tlsPort <= 0) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("spice secure channels set in XML configuration, "
"but TLS port is not provided"));
goto error;
}
virBufferAsprintf(&opt, ",tls-channel=%s",
virDomainGraphicsSpiceChannelNameTypeToString(i));
break;
case VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_INSECURE:
if (port <= 0) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("spice insecure channels set in XML "
"configuration, but plain port is not provided"));
goto error;
}
virBufferAsprintf(&opt, ",plaintext-channel=%s",
virDomainGraphicsSpiceChannelNameTypeToString(i));
break;
case VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_ANY:
switch (defaultMode) {
case VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_SECURE:
if (tlsPort <= 0) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("spice defaultMode secure requested in XML "
"configuration but TLS port not provided"));
goto error;
}
break;
case VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_INSECURE:
if (port <= 0) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("spice defaultMode insecure requested in XML "
"configuration but plain port not provided"));
goto error;
}
break;
case VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_ANY:
/* don't care */
break;
}
}
}
if (graphics->data.spice.image)
virBufferAsprintf(&opt, ",image-compression=%s",
virDomainGraphicsSpiceImageCompressionTypeToString(graphics->data.spice.image));
if (graphics->data.spice.jpeg)
virBufferAsprintf(&opt, ",jpeg-wan-compression=%s",
virDomainGraphicsSpiceJpegCompressionTypeToString(graphics->data.spice.jpeg));
if (graphics->data.spice.zlib)
virBufferAsprintf(&opt, ",zlib-glz-wan-compression=%s",
virDomainGraphicsSpiceZlibCompressionTypeToString(graphics->data.spice.zlib));
if (graphics->data.spice.playback)
virBufferAsprintf(&opt, ",playback-compression=%s",
virTristateSwitchTypeToString(graphics->data.spice.playback));
if (graphics->data.spice.streaming)
virBufferAsprintf(&opt, ",streaming-video=%s",
virDomainGraphicsSpiceStreamingModeTypeToString(graphics->data.spice.streaming));
if (graphics->data.spice.copypaste == VIR_TRISTATE_BOOL_NO)
virBufferAddLit(&opt, ",disable-copy-paste");
if (graphics->data.spice.filetransfer == VIR_TRISTATE_BOOL_NO) {
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SPICE_FILE_XFER_DISABLE)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("This QEMU can't disable file transfers through spice"));
goto error;
} else {
virBufferAddLit(&opt, ",disable-agent-file-xfer");
}
}
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_SEAMLESS_MIGRATION)) {
/* If qemu supports seamless migration turn it
* unconditionally on. If migration destination
* doesn't support it, it fallbacks to previous
* migration algorithm silently. */
virBufferAddLit(&opt, ",seamless-migration=on");
}
virCommandAddArg(cmd, "-spice");
virCommandAddArgBuffer(cmd, &opt);
if (graphics->data.spice.keymap)
virCommandAddArgList(cmd, "-k",
graphics->data.spice.keymap, NULL);
/* SPICE includes native support for tunnelling audio, so we
* set the audio backend to point at SPICE's own driver
*/
virCommandAddEnvString(cmd, "QEMU_AUDIO_DRV=spice");
return 0;
error:
VIR_FREE(netAddr);
virBufferFreeAndReset(&opt);
return -1;
}
static int
qemuBuildGraphicsCommandLine(virQEMUDriverConfigPtr cfg,
virCommandPtr cmd,
virDomainDefPtr def,
virQEMUCapsPtr qemuCaps,
virDomainGraphicsDefPtr graphics)
{
switch ((virDomainGraphicsType) graphics->type) {
case VIR_DOMAIN_GRAPHICS_TYPE_SDL:
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_0_10) &&
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SDL)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("sdl not supported by '%s'"), def->emulator);
return -1;
}
if (graphics->data.sdl.xauth)
virCommandAddEnvPair(cmd, "XAUTHORITY", graphics->data.sdl.xauth);
if (graphics->data.sdl.display)
virCommandAddEnvPair(cmd, "DISPLAY", graphics->data.sdl.display);
if (graphics->data.sdl.fullscreen)
virCommandAddArg(cmd, "-full-screen");
/* If using SDL for video, then we should just let it
* use QEMU's host audio drivers, possibly SDL too
* User can set these two before starting libvirtd
*/
virCommandAddEnvPassBlockSUID(cmd, "QEMU_AUDIO_DRV", NULL);
virCommandAddEnvPassBlockSUID(cmd, "SDL_AUDIODRIVER", NULL);
/* New QEMU has this flag to let us explicitly ask for
* SDL graphics. This is better than relying on the
* default, since the default changes :-( */
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_SDL))
virCommandAddArg(cmd, "-sdl");
break;
case VIR_DOMAIN_GRAPHICS_TYPE_VNC:
return qemuBuildGraphicsVNCCommandLine(cfg, cmd, def, qemuCaps, graphics);
case VIR_DOMAIN_GRAPHICS_TYPE_SPICE:
return qemuBuildGraphicsSPICECommandLine(cfg, cmd, qemuCaps, graphics);
case VIR_DOMAIN_GRAPHICS_TYPE_RDP:
case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP:
case VIR_DOMAIN_GRAPHICS_TYPE_LAST:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("unsupported graphics type '%s'"),
virDomainGraphicsTypeToString(graphics->type));
return -1;
}
return 0;
}
static int
qemuBuildVhostuserCommandLine(virCommandPtr cmd,
virDomainDefPtr def,
virDomainNetDefPtr net,
virQEMUCapsPtr qemuCaps,
int bootindex)
{
virBuffer chardev_buf = VIR_BUFFER_INITIALIZER;
virBuffer netdev_buf = VIR_BUFFER_INITIALIZER;
unsigned int queues = net->driver.virtio.queues;
char *nic = NULL;
if (!qemuDomainSupportsNetdev(def, qemuCaps, net)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("Netdev support unavailable"));
goto error;
}
switch ((virDomainChrType) net->data.vhostuser->type) {
case VIR_DOMAIN_CHR_TYPE_UNIX:
virBufferAsprintf(&chardev_buf, "socket,id=char%s,path=%s%s",
net->info.alias, net->data.vhostuser->data.nix.path,
net->data.vhostuser->data.nix.listen ? ",server" : "");
break;
case VIR_DOMAIN_CHR_TYPE_NULL:
case VIR_DOMAIN_CHR_TYPE_VC:
case VIR_DOMAIN_CHR_TYPE_PTY:
case VIR_DOMAIN_CHR_TYPE_DEV:
case VIR_DOMAIN_CHR_TYPE_FILE:
case VIR_DOMAIN_CHR_TYPE_PIPE:
case VIR_DOMAIN_CHR_TYPE_STDIO:
case VIR_DOMAIN_CHR_TYPE_UDP:
case VIR_DOMAIN_CHR_TYPE_TCP:
case VIR_DOMAIN_CHR_TYPE_SPICEVMC:
case VIR_DOMAIN_CHR_TYPE_SPICEPORT:
case VIR_DOMAIN_CHR_TYPE_NMDM:
case VIR_DOMAIN_CHR_TYPE_LAST:
virReportError(VIR_ERR_INTERNAL_ERROR,
_("vhost-user type '%s' not supported"),
virDomainChrTypeToString(net->data.vhostuser->type));
goto error;
}
virBufferAsprintf(&netdev_buf, "type=vhost-user,id=host%s,chardev=char%s",
net->info.alias, net->info.alias);
if (queues > 1) {
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_VHOSTUSER_MULTIQUEUE)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("multi-queue is not supported for vhost-user "
"with this QEMU binary"));
goto error;
}
virBufferAsprintf(&netdev_buf, ",queues=%u", queues);
}
virCommandAddArg(cmd, "-chardev");
virCommandAddArgBuffer(cmd, &chardev_buf);
virCommandAddArg(cmd, "-netdev");
virCommandAddArgBuffer(cmd, &netdev_buf);
if (!(nic = qemuBuildNicDevStr(def, net, -1, bootindex,
queues, qemuCaps))) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("Error generating NIC -device string"));
goto error;
}
virCommandAddArgList(cmd, "-device", nic, NULL);
VIR_FREE(nic);
return 0;
error:
virBufferFreeAndReset(&chardev_buf);
virBufferFreeAndReset(&netdev_buf);
VIR_FREE(nic);
return -1;
}
static int
qemuBuildInterfaceCommandLine(virCommandPtr cmd,
virQEMUDriverPtr driver,
virDomainDefPtr def,
virDomainNetDefPtr net,
virQEMUCapsPtr qemuCaps,
int vlan,
int bootindex,
virNetDevVPortProfileOp vmop,
bool standalone,
size_t *nnicindexes,
int **nicindexes)
{
int ret = -1;
char *nic = NULL, *host = NULL;
int *tapfd = NULL;
size_t tapfdSize = 0;
int *vhostfd = NULL;
size_t vhostfdSize = 0;
char **tapfdName = NULL;
char **vhostfdName = NULL;
int actualType = virDomainNetGetActualType(net);
virQEMUDriverConfigPtr cfg = NULL;
virNetDevBandwidthPtr actualBandwidth;
size_t i;
if (!bootindex)
bootindex = net->info.bootIndex;
if (actualType == VIR_DOMAIN_NET_TYPE_VHOSTUSER)
return qemuBuildVhostuserCommandLine(cmd, def, net, qemuCaps, bootindex);
if (actualType == VIR_DOMAIN_NET_TYPE_HOSTDEV) {
/* NET_TYPE_HOSTDEV devices are really hostdev devices, so
* their commandlines are constructed with other hostdevs.
*/
return 0;
}
/* Currently nothing besides TAP devices supports multiqueue. */
if (net->driver.virtio.queues > 0 &&
!(actualType == VIR_DOMAIN_NET_TYPE_NETWORK ||
actualType == VIR_DOMAIN_NET_TYPE_BRIDGE)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("Multiqueue network is not supported for: %s"),
virDomainNetTypeToString(actualType));
return -1;
}
/* and only TAP devices support nwfilter rules */
if (net->filter &&
!(actualType == VIR_DOMAIN_NET_TYPE_NETWORK ||
actualType == VIR_DOMAIN_NET_TYPE_BRIDGE)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("filterref is not supported for "
"network interfaces of type %s"),
virDomainNetTypeToString(actualType));
return -1;
}
if (net->backend.tap &&
!(actualType == VIR_DOMAIN_NET_TYPE_NETWORK ||
actualType == VIR_DOMAIN_NET_TYPE_BRIDGE)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("Custom tap device path is not supported for: %s"),
virDomainNetTypeToString(actualType));
return -1;
}
cfg = virQEMUDriverGetConfig(driver);
if (actualType == VIR_DOMAIN_NET_TYPE_NETWORK ||
actualType == VIR_DOMAIN_NET_TYPE_BRIDGE) {
tapfdSize = net->driver.virtio.queues;
if (!tapfdSize)
tapfdSize = 1;
if (VIR_ALLOC_N(tapfd, tapfdSize) < 0 ||
VIR_ALLOC_N(tapfdName, tapfdSize) < 0)
goto cleanup;
memset(tapfd, -1, tapfdSize * sizeof(tapfd[0]));
if (qemuNetworkIfaceConnect(def, driver, net,
qemuCaps, tapfd,
&tapfdSize) < 0)
goto cleanup;
} else if (actualType == VIR_DOMAIN_NET_TYPE_DIRECT) {
if (VIR_ALLOC(tapfd) < 0 || VIR_ALLOC(tapfdName) < 0)
goto cleanup;
tapfdSize = 1;
tapfd[0] = qemuPhysIfaceConnect(def, driver, net,
qemuCaps, vmop);
if (tapfd[0] < 0)
goto cleanup;
}
/* For types whose implementations use a netdev on the host, add
* an entry to nicindexes for passing on to systemd.
*/
switch ((virDomainNetType)actualType) {
case VIR_DOMAIN_NET_TYPE_ETHERNET:
case VIR_DOMAIN_NET_TYPE_NETWORK:
case VIR_DOMAIN_NET_TYPE_BRIDGE:
case VIR_DOMAIN_NET_TYPE_DIRECT:
{
int nicindex;
/* network and bridge use a tap device, and direct uses a
* macvtap device
*/
if (virQEMUDriverIsPrivileged(driver) && nicindexes && nnicindexes &&
net->ifname) {
if (virNetDevGetIndex(net->ifname, &nicindex) < 0 ||
VIR_APPEND_ELEMENT(*nicindexes, *nnicindexes, nicindex) < 0)
goto cleanup;
}
break;
}
case VIR_DOMAIN_NET_TYPE_USER:
case VIR_DOMAIN_NET_TYPE_VHOSTUSER:
case VIR_DOMAIN_NET_TYPE_SERVER:
case VIR_DOMAIN_NET_TYPE_CLIENT:
case VIR_DOMAIN_NET_TYPE_MCAST:
case VIR_DOMAIN_NET_TYPE_UDP:
case VIR_DOMAIN_NET_TYPE_INTERNAL:
case VIR_DOMAIN_NET_TYPE_HOSTDEV:
case VIR_DOMAIN_NET_TYPE_LAST:
/* These types don't use a network device on the host, but
* instead use some other type of connection to the emulated
* device in the qemu process.
*
* (Note that hostdev can't be considered as "using a network
* device", because by the time it is being used, it has been
* detached from the hostside network driver so it doesn't show
* up in the list of interfaces on the host - it's just some
* PCI device.)
*/
break;
}
/* Set bandwidth or warn if requested and not supported. */
actualBandwidth = virDomainNetGetActualBandwidth(net);
if (actualBandwidth) {
if (virNetDevSupportBandwidth(actualType)) {
if (virNetDevBandwidthSet(net->ifname, actualBandwidth, false) < 0)
goto cleanup;
} else {
VIR_WARN("setting bandwidth on interfaces of "
"type '%s' is not implemented yet",
virDomainNetTypeToString(actualType));
}
}
if ((actualType == VIR_DOMAIN_NET_TYPE_NETWORK ||
actualType == VIR_DOMAIN_NET_TYPE_BRIDGE ||
actualType == VIR_DOMAIN_NET_TYPE_ETHERNET ||
actualType == VIR_DOMAIN_NET_TYPE_DIRECT) &&
!standalone) {
/* Attempt to use vhost-net mode for these types of
network device */
vhostfdSize = net->driver.virtio.queues;
if (!vhostfdSize)
vhostfdSize = 1;
if (VIR_ALLOC_N(vhostfd, vhostfdSize) < 0 ||
VIR_ALLOC_N(vhostfdName, vhostfdSize))
goto cleanup;
memset(vhostfd, -1, vhostfdSize * sizeof(vhostfd[0]));
if (qemuOpenVhostNet(def, net, qemuCaps, vhostfd, &vhostfdSize) < 0)
goto cleanup;
}
for (i = 0; i < tapfdSize; i++) {
if (virSecurityManagerSetTapFDLabel(driver->securityManager,
def, tapfd[i]) < 0)
goto cleanup;
virCommandPassFD(cmd, tapfd[i],
VIR_COMMAND_PASS_FD_CLOSE_PARENT);
if (virAsprintf(&tapfdName[i], "%d", tapfd[i]) < 0)
goto cleanup;
}
for (i = 0; i < vhostfdSize; i++) {
virCommandPassFD(cmd, vhostfd[i],
VIR_COMMAND_PASS_FD_CLOSE_PARENT);
if (virAsprintf(&vhostfdName[i], "%d", vhostfd[i]) < 0)
goto cleanup;
}
/* Possible combinations:
*
* 1. Old way: -net nic,model=e1000,vlan=1 -net tap,vlan=1
* 2. Semi-new: -device e1000,vlan=1 -net tap,vlan=1
* 3. Best way: -netdev type=tap,id=netdev1 -device e1000,id=netdev1
*
* NB, no support for -netdev without use of -device
*/
if (qemuDomainSupportsNetdev(def, qemuCaps, net)) {
if (!(host = qemuBuildHostNetStr(net, driver,
',', vlan,
tapfdName, tapfdSize,
vhostfdName, vhostfdSize)))
goto cleanup;
virCommandAddArgList(cmd, "-netdev", host, NULL);
}
if (qemuDomainSupportsNicdev(def, qemuCaps, net)) {
if (!(nic = qemuBuildNicDevStr(def, net, vlan, bootindex,
vhostfdSize, qemuCaps)))
goto cleanup;
virCommandAddArgList(cmd, "-device", nic, NULL);
} else {
if (!(nic = qemuBuildNicStr(net, "nic,", vlan)))
goto cleanup;
virCommandAddArgList(cmd, "-net", nic, NULL);
}
if (!qemuDomainSupportsNetdev(def, qemuCaps, net)) {
if (!(host = qemuBuildHostNetStr(net, driver,
',', vlan,
tapfdName, tapfdSize,
vhostfdName, vhostfdSize)))
goto cleanup;
virCommandAddArgList(cmd, "-net", host, NULL);
}
ret = 0;
cleanup:
if (ret < 0) {
virErrorPtr saved_err = virSaveLastError();
virDomainConfNWFilterTeardown(net);
virSetError(saved_err);
virFreeError(saved_err);
}
for (i = 0; tapfd && i < tapfdSize && tapfd[i] >= 0; i++) {
if (ret < 0)
VIR_FORCE_CLOSE(tapfd[i]);
if (tapfdName)
VIR_FREE(tapfdName[i]);
}
for (i = 0; vhostfd && i < vhostfdSize && vhostfd[i] >= 0; i++) {
if (ret < 0)
VIR_FORCE_CLOSE(vhostfd[i]);
if (vhostfdName)
VIR_FREE(vhostfdName[i]);
}
VIR_FREE(tapfd);
VIR_FREE(vhostfd);
VIR_FREE(nic);
VIR_FREE(host);
VIR_FREE(tapfdName);
VIR_FREE(vhostfdName);
virObjectUnref(cfg);
return ret;
}
char *
qemuBuildShmemDevStr(virDomainDefPtr def,
virDomainShmemDefPtr shmem,
virQEMUCapsPtr qemuCaps)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_IVSHMEM)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("ivshmem device is not supported "
"with this QEMU binary"));
goto error;
}
virBufferAddLit(&buf, "ivshmem");
if (shmem->size) {
/*
* Thanks to our parsing code, we have a guarantee that the
* size is power of two and is at least a mebibyte in size.
* But because it may change in the future, the checks are
* doubled in here.
*/
if (shmem->size & (shmem->size - 1)) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("shmem size must be a power of two"));
goto error;
}
if (shmem->size < 1024 * 1024) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("shmem size must be at least 1 MiB (1024 KiB)"));
goto error;
}
virBufferAsprintf(&buf, ",size=%llum", shmem->size >> 20);
}
if (!shmem->server.enabled) {
virBufferAsprintf(&buf, ",shm=%s,id=%s", shmem->name, shmem->info.alias);
} else {
virBufferAsprintf(&buf, ",chardev=char%s,id=%s", shmem->info.alias, shmem->info.alias);
if (shmem->msi.enabled) {
virBufferAddLit(&buf, ",msi=on");
if (shmem->msi.vectors)
virBufferAsprintf(&buf, ",vectors=%u", shmem->msi.vectors);
if (shmem->msi.ioeventfd)
virBufferAsprintf(&buf, ",ioeventfd=%s",
virTristateSwitchTypeToString(shmem->msi.ioeventfd));
}
}
if (shmem->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("only 'pci' addresses are supported for the "
"shared memory device"));
goto error;
}
if (qemuBuildDeviceAddressStr(&buf, def, &shmem->info, qemuCaps) < 0)
goto error;
if (virBufferCheckError(&buf) < 0)
goto error;
return virBufferContentAndReset(&buf);
error:
virBufferFreeAndReset(&buf);
return NULL;
}
char *
qemuBuildShmemBackendStr(virDomainShmemDefPtr shmem,
virQEMUCapsPtr qemuCaps)
{
char *devstr = NULL;
if (!shmem->server.chr.data.nix.path &&
virAsprintf(&shmem->server.chr.data.nix.path,
"/var/lib/libvirt/shmem-%s-sock",
shmem->name) < 0)
return NULL;
devstr = qemuBuildChrChardevStr(&shmem->server.chr, shmem->info.alias, qemuCaps);
return devstr;
}
static int
qemuBuildShmemCommandLine(virCommandPtr cmd,
virDomainDefPtr def,
virDomainShmemDefPtr shmem,
virQEMUCapsPtr qemuCaps)
{
char *devstr = NULL;
if (!(devstr = qemuBuildShmemDevStr(def, shmem, qemuCaps)))
return -1;
virCommandAddArgList(cmd, "-device", devstr, NULL);
VIR_FREE(devstr);
if (shmem->server.enabled) {
if (!(devstr = qemuBuildShmemBackendStr(shmem, qemuCaps)))
return -1;
virCommandAddArgList(cmd, "-chardev", devstr, NULL);
VIR_FREE(devstr);
}
return 0;
}
static int
qemuBuildChrDeviceCommandLine(virCommandPtr cmd,
virDomainDefPtr def,
virDomainChrDefPtr chr,
virQEMUCapsPtr qemuCaps)
{
char *devstr = NULL;
if (qemuBuildChrDeviceStr(&devstr, def, chr, qemuCaps) < 0)
return -1;
virCommandAddArgList(cmd, "-device", devstr, NULL);
VIR_FREE(devstr);
return 0;
}
static int
qemuBuildDomainLoaderCommandLine(virCommandPtr cmd,
virDomainDefPtr def,
virQEMUCapsPtr qemuCaps)
{
int ret = -1;
virDomainLoaderDefPtr loader = def->os.loader;
virBuffer buf = VIR_BUFFER_INITIALIZER;
int unit = 0;
if (!loader)
return 0;
switch ((virDomainLoader) loader->type) {
case VIR_DOMAIN_LOADER_TYPE_ROM:
virCommandAddArg(cmd, "-bios");
virCommandAddArg(cmd, loader->path);
break;
case VIR_DOMAIN_LOADER_TYPE_PFLASH:
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("this QEMU binary doesn't support -drive"));
goto cleanup;
}
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_FORMAT)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("this QEMU binary doesn't support passing "
"drive format"));
goto cleanup;
}
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_NO_ACPI) &&
def->features[VIR_DOMAIN_FEATURE_ACPI] != VIR_TRISTATE_SWITCH_ON) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("ACPI must be enabled in order to use UEFI"));
goto cleanup;
}
virBufferAsprintf(&buf,
"file=%s,if=pflash,format=raw,unit=%d",
loader->path, unit);
unit++;
if (loader->readonly) {
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_READONLY)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("this qemu doesn't support passing "
"readonly attribute"));
goto cleanup;
}
virBufferAsprintf(&buf, ",readonly=%s",
virTristateSwitchTypeToString(loader->readonly));
}
virCommandAddArg(cmd, "-drive");
virCommandAddArgBuffer(cmd, &buf);
if (loader->nvram) {
virBufferFreeAndReset(&buf);
virBufferAsprintf(&buf,
"file=%s,if=pflash,format=raw,unit=%d",
loader->nvram, unit);
virCommandAddArg(cmd, "-drive");
virCommandAddArgBuffer(cmd, &buf);
}
break;
case VIR_DOMAIN_LOADER_TYPE_LAST:
/* nada */
break;
}
ret = 0;
cleanup:
virBufferFreeAndReset(&buf);
return ret;
}
static int
qemuBuildTPMCommandLine(virDomainDefPtr def,
virCommandPtr cmd,
virQEMUCapsPtr qemuCaps,
const char *emulator)
{
char *optstr;
int tpmfd = -1;
int cancelfd = -1;
char *fdset;
if (!(optstr = qemuBuildTPMBackendStr(def, cmd, qemuCaps, emulator,
&tpmfd, &cancelfd)))
return -1;
virCommandAddArgList(cmd, "-tpmdev", optstr, NULL);
VIR_FREE(optstr);
if (tpmfd >= 0) {
fdset = qemuVirCommandGetFDSet(cmd, tpmfd);
if (!fdset)
return -1;
virCommandAddArgList(cmd, "-add-fd", fdset, NULL);
VIR_FREE(fdset);
}
if (cancelfd >= 0) {
fdset = qemuVirCommandGetFDSet(cmd, cancelfd);
if (!fdset)
return -1;
virCommandAddArgList(cmd, "-add-fd", fdset, NULL);
VIR_FREE(fdset);
}
if (!(optstr = qemuBuildTPMDevStr(def, qemuCaps, emulator)))
return -1;
virCommandAddArgList(cmd, "-device", optstr, NULL);
VIR_FREE(optstr);
return 0;
}
qemuBuildCommandLineCallbacks buildCommandLineCallbacks = {
.qemuGetSCSIDeviceSgName = virSCSIDeviceGetSgName,
};
/*
* Constructs a argv suitable for launching qemu with config defined
* for a given virtual machine.
*
* XXX 'conn' is only required to resolve network -> bridge name
* figure out how to remove this requirement some day
*/
virCommandPtr
qemuBuildCommandLine(virConnectPtr conn,
virQEMUDriverPtr driver,
virDomainDefPtr def,
virDomainChrSourceDefPtr monitor_chr,
bool monitor_json,
virQEMUCapsPtr qemuCaps,
const char *migrateFrom,
int migrateFd,
virDomainSnapshotObjPtr snapshot,
virNetDevVPortProfileOp vmop,
qemuBuildCommandLineCallbacksPtr callbacks,
bool standalone,
bool enableFips,
virBitmapPtr nodeset,
size_t *nnicindexes,
int **nicindexes)
{
virErrorPtr originalError = NULL;
size_t i, j;
const char *emulator;
char uuid[VIR_UUID_STRING_BUFLEN];
char *cpu;
char *smp;
int last_good_net = -1;
bool hasHwVirt = false;
virCommandPtr cmd = NULL;
bool allowReboot = true;
bool emitBootindex = false;
int sdl = 0;
int vnc = 0;
int spice = 0;
int usbcontroller = 0;
int actualSerials = 0;
bool usblegacy = false;
bool mlock = false;
int contOrder[] = {
/*
* List of controller types that we add commandline args for,
* *in the order we want to add them*.
*
* The floppy controller is implicit on PIIX4 and older Q35
* machines. For newer Q35 machines it is added out of the
* controllers loop, after the floppy drives.
*
* We don't add PCI/PCIe root controller either, because it's
* implicit, but we do add PCI bridges and other PCI
* controllers, so we leave that in to check each
* one. Likewise, we don't do anything for the primary IDE
* controller on an i440fx machine or primary SATA on q35, but
* we do add those beyond these two exceptions.
*/
VIR_DOMAIN_CONTROLLER_TYPE_PCI,
VIR_DOMAIN_CONTROLLER_TYPE_USB,
VIR_DOMAIN_CONTROLLER_TYPE_SCSI,
VIR_DOMAIN_CONTROLLER_TYPE_IDE,
VIR_DOMAIN_CONTROLLER_TYPE_SATA,
VIR_DOMAIN_CONTROLLER_TYPE_VIRTIO_SERIAL,
VIR_DOMAIN_CONTROLLER_TYPE_CCID,
};
virArch hostarch = virArchFromHost();
virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver);
virBuffer boot_buf = VIR_BUFFER_INITIALIZER;
char *boot_order_str = NULL, *boot_opts_str = NULL;
virBuffer fdc_opts = VIR_BUFFER_INITIALIZER;
char *fdc_opts_str = NULL;
VIR_DEBUG("conn=%p driver=%p def=%p mon=%p json=%d "
"qemuCaps=%p migrateFrom=%s migrateFD=%d "
"snapshot=%p vmop=%d",
conn, driver, def, monitor_chr, monitor_json,
qemuCaps, migrateFrom, migrateFd, snapshot, vmop);
virUUIDFormat(def->uuid, uuid);
emulator = def->emulator;
if (!virQEMUDriverIsPrivileged(driver)) {
/* If we have no cgroups then we can have no tunings that
* require them */
if (virMemoryLimitIsSet(def->mem.hard_limit) ||
virMemoryLimitIsSet(def->mem.soft_limit) ||
def->mem.min_guarantee ||
virMemoryLimitIsSet(def->mem.swap_hard_limit)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Memory tuning is not available in session mode"));
goto error;
}
if (def->blkio.weight) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Block I/O tuning is not available in session mode"));
goto error;
}
if (def->cputune.sharesSpecified || def->cputune.period ||
def->cputune.quota || def->cputune.emulator_period ||
def->cputune.emulator_quota) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("CPU tuning is not available in session mode"));
goto error;
}
}
for (i = 0; i < def->ngraphics; ++i) {
switch (def->graphics[i]->type) {
case VIR_DOMAIN_GRAPHICS_TYPE_SDL:
++sdl;
break;
case VIR_DOMAIN_GRAPHICS_TYPE_VNC:
++vnc;
break;
case VIR_DOMAIN_GRAPHICS_TYPE_SPICE:
++spice;
break;
}
}
/*
* do not use boot=on for drives when not using KVM since this
* is not supported at all in upstream QEmu.
*/
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_KVM) &&
(def->virtType == VIR_DOMAIN_VIRT_QEMU))
virQEMUCapsClear(qemuCaps, QEMU_CAPS_DRIVE_BOOT);
cmd = virCommandNew(emulator);
virCommandAddEnvPassCommon(cmd);
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_NAME)) {
virCommandAddArg(cmd, "-name");
if (cfg->setProcessName &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_NAME_PROCESS)) {
virCommandAddArgFormat(cmd, "%s,process=qemu:%s",
def->name, def->name);
} else {
virCommandAddArg(cmd, def->name);
}
}
if (!standalone)
virCommandAddArg(cmd, "-S"); /* freeze CPU */
if (enableFips)
virCommandAddArg(cmd, "-enable-fips");
if (qemuBuildMachineArgStr(cmd, def, qemuCaps) < 0)
goto error;
if (qemuBuildCpuArgStr(driver, def, emulator, qemuCaps,
hostarch, &cpu, &hasHwVirt, !!migrateFrom) < 0)
goto error;
if (cpu) {
virCommandAddArgList(cmd, "-cpu", cpu, NULL);
VIR_FREE(cpu);
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_NESTING) &&
hasHwVirt)
virCommandAddArg(cmd, "-enable-nesting");
}
if (qemuBuildDomainLoaderCommandLine(cmd, def, qemuCaps) < 0)
goto error;
if (qemuDomainAlignMemorySizes(def) < 0)
goto error;
virCommandAddArg(cmd, "-m");
if (def->mem.max_memory) {
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_PC_DIMM)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("memory hotplug isn't supported by this QEMU binary"));
goto error;
}
/* due to guest support, qemu would silently enable NUMA with one node
* once the memory hotplug backend is enabled. To avoid possible
* confusion we will enforce user originated numa configuration along
* with memory hotplug. */
if (virDomainNumaGetNodeCount(def->numa) == 0) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("At least one numa node has to be configured when "
"enabling memory hotplug"));
goto error;
}
/* Use the 'k' suffix to let qemu handle the units */
virCommandAddArgFormat(cmd, "size=%lluk,slots=%u,maxmem=%lluk",
virDomainDefGetMemoryInitial(def),
def->mem.memory_slots,
def->mem.max_memory);
} else {
virCommandAddArgFormat(cmd, "%llu", virDomainDefGetMemoryInitial(def) / 1024);
}
if (def->mem.nhugepages && !virDomainNumaGetNodeCount(def->numa)) {
const long system_page_size = virGetSystemPageSizeKB();
char *mem_path = NULL;
if (def->mem.hugepages[0].size == system_page_size) {
/* There is one special case: if user specified "huge"
* pages of regular system pages size. */
} else {
if (!cfg->nhugetlbfs) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("hugetlbfs filesystem is not mounted "
"or disabled by administrator config"));
goto error;
}
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_MEM_PATH)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("hugepage backing not supported by '%s'"),
def->emulator);
goto error;
}
if (def->mem.hugepages[0].size) {
for (j = 0; j < cfg->nhugetlbfs; j++) {
if (cfg->hugetlbfs[j].size == def->mem.hugepages[0].size)
break;
}
if (j == cfg->nhugetlbfs) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to find any usable hugetlbfs mount for %llu KiB"),
def->mem.hugepages[0].size);
goto error;
}
if (!(mem_path = qemuGetHugepagePath(&cfg->hugetlbfs[j])))
goto error;
} else {
if (!(mem_path = qemuGetDefaultHugepath(cfg->hugetlbfs,
cfg->nhugetlbfs)))
goto error;
}
}
virCommandAddArg(cmd, "-mem-prealloc");
if (mem_path)
virCommandAddArgList(cmd, "-mem-path", mem_path, NULL);
VIR_FREE(mem_path);
}
if (def->mem.locked && !virQEMUCapsGet(qemuCaps, QEMU_CAPS_MLOCK)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("memory locking not supported by QEMU binary"));
goto error;
}
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_MLOCK)) {
virCommandAddArg(cmd, "-realtime");
virCommandAddArgFormat(cmd, "mlock=%s",
def->mem.locked ? "on" : "off");
}
mlock = def->mem.locked;
virCommandAddArg(cmd, "-smp");
if (!(smp = qemuBuildSmpArgStr(def, qemuCaps)))
goto error;
virCommandAddArg(cmd, smp);
VIR_FREE(smp);
if (def->iothreads > 0 &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_IOTHREAD)) {
/* Create iothread objects using the defined iothreadids list
* and the defined id and name from the list. These may be used
* by a disk definition which will associate to an iothread by
* supplying a value of an id from the list
*/
for (i = 0; i < def->niothreadids; i++) {
virCommandAddArg(cmd, "-object");
virCommandAddArgFormat(cmd, "iothread,id=iothread%u",
def->iothreadids[i]->iothread_id);
}
}
if (virDomainNumaGetNodeCount(def->numa)) {
if (qemuBuildNumaArgStr(cfg, def, cmd, qemuCaps, nodeset) < 0)
goto error;
/* memory hotplug requires NUMA to be enabled - we already checked
* that memory devices are present only when NUMA is */
if (def->nmems > def->mem.memory_slots) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("memory device count '%zu' exceeds slots count '%u'"),
def->nmems, def->mem.memory_slots);
goto error;
}
for (i = 0; i < def->nmems; i++) {
char *backStr;
char *dimmStr;
if (!(backStr = qemuBuildMemoryDimmBackendStr(def->mems[i], def,
qemuCaps, cfg)))
goto error;
if (!(dimmStr = qemuBuildMemoryDeviceStr(def->mems[i], def, qemuCaps))) {
VIR_FREE(backStr);
goto error;
}
virCommandAddArgList(cmd, "-object", backStr, "-device", dimmStr, NULL);
VIR_FREE(backStr);
VIR_FREE(dimmStr);
}
}
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_UUID))
virCommandAddArgList(cmd, "-uuid", uuid, NULL);
if (def->virtType == VIR_DOMAIN_VIRT_XEN ||
def->os.type == VIR_DOMAIN_OSTYPE_XEN ||
def->os.type == VIR_DOMAIN_OSTYPE_LINUX) {
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_XEN_DOMID)) {
virCommandAddArg(cmd, "-xen-attach");
virCommandAddArg(cmd, "-xen-domid");
virCommandAddArgFormat(cmd, "%d", def->id);
} else {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("qemu emulator '%s' does not support xen"),
def->emulator);
goto error;
}
}
if ((def->os.smbios_mode != VIR_DOMAIN_SMBIOS_NONE) &&
(def->os.smbios_mode != VIR_DOMAIN_SMBIOS_EMULATE)) {
virSysinfoDefPtr source = NULL;
bool skip_uuid = false;
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SMBIOS_TYPE)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("the QEMU binary %s does not support smbios settings"),
emulator);
goto error;
}
/* should we really error out or just warn in those cases ? */
if (def->os.smbios_mode == VIR_DOMAIN_SMBIOS_HOST) {
if (driver->hostsysinfo == NULL) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Host SMBIOS information is not available"));
goto error;
}
source = driver->hostsysinfo;
/* Host and guest uuid must differ, by definition of UUID. */
skip_uuid = true;
} else if (def->os.smbios_mode == VIR_DOMAIN_SMBIOS_SYSINFO) {
if (def->sysinfo == NULL) {
virReportError(VIR_ERR_XML_ERROR,
_("Domain '%s' sysinfo are not available"),
def->name);
goto error;
}
source = def->sysinfo;
/* domain_conf guaranteed that system_uuid matches guest uuid. */
}
if (source != NULL) {
char *smbioscmd;
smbioscmd = qemuBuildSmbiosBiosStr(source->bios);
if (smbioscmd != NULL) {
virCommandAddArgList(cmd, "-smbios", smbioscmd, NULL);
VIR_FREE(smbioscmd);
}
smbioscmd = qemuBuildSmbiosSystemStr(source->system, skip_uuid);
if (smbioscmd != NULL) {
virCommandAddArgList(cmd, "-smbios", smbioscmd, NULL);
VIR_FREE(smbioscmd);
}
if (source->nbaseBoard > 1) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("qemu does not support more than "
"one entry to Type 2 in SMBIOS table"));
goto error;
}
for (i = 0; i < source->nbaseBoard; i++) {
if (!(smbioscmd = qemuBuildSmbiosBaseBoardStr(source->baseBoard + i)))
goto error;
virCommandAddArgList(cmd, "-smbios", smbioscmd, NULL);
VIR_FREE(smbioscmd);
}
}
}
/*
* NB, -nographic *MUST* come before any serial, or monitor
* or parallel port flags due to QEMU craziness, where it
* decides to change the serial port & monitor to be on stdout
* if you ask for nographic. So we have to make sure we override
* these defaults ourselves...
*/
if (!def->graphics) {
virCommandAddArg(cmd, "-nographic");
if (cfg->nogfxAllowHostAudio)
virCommandAddEnvPassBlockSUID(cmd, "QEMU_AUDIO_DRV", NULL);
else
virCommandAddEnvString(cmd, "QEMU_AUDIO_DRV=none");
}
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) {
/* Disable global config files and default devices */
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_NO_USER_CONFIG))
virCommandAddArg(cmd, "-no-user-config");
else if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_NODEFCONFIG))
virCommandAddArg(cmd, "-nodefconfig");
virCommandAddArg(cmd, "-nodefaults");
}
/* Serial graphics adapter */
if (def->os.bios.useserial == VIR_TRISTATE_BOOL_YES) {
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("qemu does not support -device"));
goto error;
}
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SGA)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("qemu does not support SGA"));
goto error;
}
if (!def->nserials) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("need at least one serial port to use SGA"));
goto error;
}
virCommandAddArgList(cmd, "-device", "sga", NULL);
}
if (monitor_chr) {
char *chrdev;
/* Use -chardev if it's available */
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_CHARDEV)) {
virCommandAddArg(cmd, "-chardev");
if (!(chrdev = qemuBuildChrChardevStr(monitor_chr, "monitor",
qemuCaps)))
goto error;
virCommandAddArg(cmd, chrdev);
VIR_FREE(chrdev);
virCommandAddArg(cmd, "-mon");
virCommandAddArgFormat(cmd,
"chardev=charmonitor,id=monitor,mode=%s",
monitor_json ? "control" : "readline");
} else {
const char *prefix = NULL;
if (monitor_json)
prefix = "control,";
virCommandAddArg(cmd, "-monitor");
if (!(chrdev = qemuBuildChrArgStr(monitor_chr, prefix)))
goto error;
virCommandAddArg(cmd, chrdev);
VIR_FREE(chrdev);
}
}
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_RTC)) {
char *rtcopt;
virCommandAddArg(cmd, "-rtc");
if (!(rtcopt = qemuBuildClockArgStr(&def->clock)))
goto error;
virCommandAddArg(cmd, rtcopt);
VIR_FREE(rtcopt);
} else {
switch (def->clock.offset) {
case VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME:
case VIR_DOMAIN_CLOCK_OFFSET_TIMEZONE:
virCommandAddArg(cmd, "-localtime");
break;
case VIR_DOMAIN_CLOCK_OFFSET_UTC:
/* Nothing, its the default */
break;
default:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("unsupported clock offset '%s'"),
virDomainClockOffsetTypeToString(def->clock.offset));
goto error;
}
}
if (def->clock.offset == VIR_DOMAIN_CLOCK_OFFSET_TIMEZONE &&
def->clock.data.timezone) {
virCommandAddEnvPair(cmd, "TZ", def->clock.data.timezone);
}
for (i = 0; i < def->clock.ntimers; i++) {
switch ((virDomainTimerNameType) def->clock.timers[i]->name) {
case VIR_DOMAIN_TIMER_NAME_PLATFORM:
case VIR_DOMAIN_TIMER_NAME_TSC:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("unsupported timer type (name) '%s'"),
virDomainTimerNameTypeToString(def->clock.timers[i]->name));
goto error;
case VIR_DOMAIN_TIMER_NAME_KVMCLOCK:
case VIR_DOMAIN_TIMER_NAME_HYPERVCLOCK:
/* Timers above are handled when building -cpu. */
case VIR_DOMAIN_TIMER_NAME_LAST:
break;
case VIR_DOMAIN_TIMER_NAME_RTC:
/* This has already been taken care of (in qemuBuildClockArgStr)
if QEMU_CAPS_RTC is set (mutually exclusive with
QEMUD_FLAG_RTC_TD_HACK) */
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_RTC_TD_HACK)) {
switch (def->clock.timers[i]->tickpolicy) {
case -1:
case VIR_DOMAIN_TIMER_TICKPOLICY_DELAY:
/* the default - do nothing */
break;
case VIR_DOMAIN_TIMER_TICKPOLICY_CATCHUP:
virCommandAddArg(cmd, "-rtc-td-hack");
break;
case VIR_DOMAIN_TIMER_TICKPOLICY_MERGE:
case VIR_DOMAIN_TIMER_TICKPOLICY_DISCARD:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("unsupported rtc tickpolicy '%s'"),
virDomainTimerTickpolicyTypeToString(def->clock.timers[i]->tickpolicy));
goto error;
}
} else if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_RTC) &&
(def->clock.timers[i]->tickpolicy
!= VIR_DOMAIN_TIMER_TICKPOLICY_DELAY) &&
(def->clock.timers[i]->tickpolicy != -1)) {
/* a non-default rtc policy was given, but there is no
way to implement it in this version of qemu */
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("unsupported rtc tickpolicy '%s'"),
virDomainTimerTickpolicyTypeToString(def->clock.timers[i]->tickpolicy));
goto error;
}
break;
case VIR_DOMAIN_TIMER_NAME_PIT:
switch (def->clock.timers[i]->tickpolicy) {
case -1:
case VIR_DOMAIN_TIMER_TICKPOLICY_DELAY:
/* delay is the default if we don't have kernel
(-no-kvm-pit), otherwise, the default is catchup. */
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_KVM_PIT_TICK_POLICY))
virCommandAddArgList(cmd, "-global",
"kvm-pit.lost_tick_policy=discard", NULL);
else if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_NO_KVM_PIT))
virCommandAddArg(cmd, "-no-kvm-pit-reinjection");
break;
case VIR_DOMAIN_TIMER_TICKPOLICY_CATCHUP:
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_NO_KVM_PIT) ||
virQEMUCapsGet(qemuCaps, QEMU_CAPS_KVM_PIT_TICK_POLICY)) {
/* do nothing - this is default for kvm-pit */
} else if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_TDF)) {
/* -tdf switches to 'catchup' with userspace pit. */
virCommandAddArg(cmd, "-tdf");
} else {
/* can't catchup if we have neither pit mode */
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("unsupported pit tickpolicy '%s'"),
virDomainTimerTickpolicyTypeToString(def->clock.timers[i]->tickpolicy));
goto error;
}
break;
case VIR_DOMAIN_TIMER_TICKPOLICY_MERGE:
case VIR_DOMAIN_TIMER_TICKPOLICY_DISCARD:
/* no way to support these modes for pit in qemu */
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("unsupported pit tickpolicy '%s'"),
virDomainTimerTickpolicyTypeToString(def->clock.timers[i]->tickpolicy));
goto error;
}
break;
case VIR_DOMAIN_TIMER_NAME_HPET:
/* the only meaningful attribute for hpet is "present". If
* present is -1, that means it wasn't specified, and
* should be left at the default for the
* hypervisor. "default" when -no-hpet exists is "yes",
* and when -no-hpet doesn't exist is "no". "confusing"?
* "yes"! */
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_NO_HPET)) {
if (def->clock.timers[i]->present == 0)
virCommandAddArg(cmd, "-no-hpet");
} else {
/* no hpet timer available. The only possible action
is to raise an error if present="yes" */
if (def->clock.timers[i]->present == 1) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
"%s", _("hpet timer is not supported"));
goto error;
}
}
break;
}
}
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_NO_REBOOT)) {
/* Only add -no-reboot option if each event destroys domain */
if (def->onReboot == VIR_DOMAIN_LIFECYCLE_DESTROY &&
def->onPoweroff == VIR_DOMAIN_LIFECYCLE_DESTROY &&
(def->onCrash == VIR_DOMAIN_LIFECYCLE_CRASH_DESTROY ||
def->onCrash == VIR_DOMAIN_LIFECYCLE_CRASH_COREDUMP_DESTROY)) {
allowReboot = false;
virCommandAddArg(cmd, "-no-reboot");
}
}
/* If JSON monitor is enabled, we can receive an event
* when QEMU stops. If we use no-shutdown, then we can
* watch for this event and do a soft/warm reboot.
*/
if (monitor_json && allowReboot &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_NO_SHUTDOWN)) {
virCommandAddArg(cmd, "-no-shutdown");
}
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_NO_ACPI)) {
if (def->features[VIR_DOMAIN_FEATURE_ACPI] != VIR_TRISTATE_SWITCH_ON)
virCommandAddArg(cmd, "-no-acpi");
}
if (def->pm.s3) {
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DISABLE_S3)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
"%s", _("setting ACPI S3 not supported"));
goto error;
}
virCommandAddArg(cmd, "-global");
virCommandAddArgFormat(cmd, "PIIX4_PM.disable_s3=%d",
def->pm.s3 == VIR_TRISTATE_BOOL_NO);
}
if (def->pm.s4) {
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DISABLE_S4)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
"%s", _("setting ACPI S4 not supported"));
goto error;
}
virCommandAddArg(cmd, "-global");
virCommandAddArgFormat(cmd, "PIIX4_PM.disable_s4=%d",
def->pm.s4 == VIR_TRISTATE_BOOL_NO);
}
/*
* We prefer using explicit bootindex=N parameters for predictable
* results even though domain XML doesn't use per device boot elements.
* However, we can't use bootindex if boot menu was requested.
*/
if (!def->os.nBootDevs) {
/* def->os.nBootDevs is guaranteed to be > 0 unless per-device boot
* configuration is used
*/
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_BOOTINDEX)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("hypervisor lacks deviceboot feature"));
goto error;
}
emitBootindex = true;
} else if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_BOOTINDEX) &&
(def->os.bootmenu != VIR_TRISTATE_BOOL_YES ||
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_BOOT_MENU))) {
emitBootindex = true;
}
if (!emitBootindex) {
char boot[VIR_DOMAIN_BOOT_LAST+1];
for (i = 0; i < def->os.nBootDevs; i++) {
switch (def->os.bootDevs[i]) {
case VIR_DOMAIN_BOOT_CDROM:
boot[i] = 'd';
break;
case VIR_DOMAIN_BOOT_FLOPPY:
boot[i] = 'a';
break;
case VIR_DOMAIN_BOOT_DISK:
boot[i] = 'c';
break;
case VIR_DOMAIN_BOOT_NET:
boot[i] = 'n';
break;
default:
boot[i] = 'c';
break;
}
}
boot[def->os.nBootDevs] = '\0';
virBufferAsprintf(&boot_buf, "%s", boot);
if (virBufferCheckError(&boot_buf) < 0)
goto error;
boot_order_str = virBufferContentAndReset(&boot_buf);
}
if (def->os.bootmenu) {
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_BOOT_MENU)) {
if (def->os.bootmenu == VIR_TRISTATE_BOOL_YES)
virBufferAddLit(&boot_buf, "menu=on,");
else
virBufferAddLit(&boot_buf, "menu=off,");
} else {
/* We cannot emit an error when bootmenu is enabled but
* unsupported because of backward compatibility */
VIR_WARN("bootmenu is enabled but not "
"supported by this QEMU binary");
}
}
if (def->os.bios.rt_set) {
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_REBOOT_TIMEOUT)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("reboot timeout is not supported "
"by this QEMU binary"));
goto error;
}
virBufferAsprintf(&boot_buf,
"reboot-timeout=%d,",
def->os.bios.rt_delay);
}
if (def->os.bm_timeout_set) {
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SPLASH_TIMEOUT)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("splash timeout is not supported "
"by this QEMU binary"));
goto error;
}
virBufferAsprintf(&boot_buf, "splash-time=%u,", def->os.bm_timeout);
}
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_BOOT_STRICT))
virBufferAddLit(&boot_buf, "strict=on,");
virBufferTrim(&boot_buf, ",", -1);
if (virBufferCheckError(&boot_buf) < 0)
goto error;
boot_opts_str = virBufferContentAndReset(&boot_buf);
if (boot_order_str || boot_opts_str) {
virCommandAddArg(cmd, "-boot");
if (boot_order_str && boot_opts_str) {
virCommandAddArgFormat(cmd, "order=%s,%s",
boot_order_str, boot_opts_str);
} else if (boot_order_str) {
virCommandAddArg(cmd, boot_order_str);
} else if (boot_opts_str) {
virCommandAddArg(cmd, boot_opts_str);
}
}
VIR_FREE(boot_opts_str);
VIR_FREE(boot_order_str);
if (def->os.kernel)
virCommandAddArgList(cmd, "-kernel", def->os.kernel, NULL);
if (def->os.initrd)
virCommandAddArgList(cmd, "-initrd", def->os.initrd, NULL);
if (def->os.cmdline)
virCommandAddArgList(cmd, "-append", def->os.cmdline, NULL);
if (def->os.dtb) {
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DTB)) {
virCommandAddArgList(cmd, "-dtb", def->os.dtb, NULL);
} else {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("dtb is not supported with this QEMU binary"));
goto error;
}
}
for (i = 0; i < def->ncontrollers; i++) {
virDomainControllerDefPtr cont = def->controllers[i];
if (cont->type == VIR_DOMAIN_CONTROLLER_TYPE_PCI &&
cont->opts.pciopts.pcihole64) {
const char *hoststr = NULL;
bool cap = false;
bool machine = false;
switch (cont->model) {
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT:
hoststr = "i440FX-pcihost";
cap = virQEMUCapsGet(qemuCaps, QEMU_CAPS_I440FX_PCI_HOLE64_SIZE);
machine = qemuDomainMachineIsI440FX(def);
break;
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT:
hoststr = "q35-pcihost";
cap = virQEMUCapsGet(qemuCaps, QEMU_CAPS_Q35_PCI_HOLE64_SIZE);
machine = qemuDomainMachineIsQ35(def);
break;
default:
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("64-bit PCI hole setting is only for root"
" PCI controllers"));
goto error;
}
if (!machine) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("Setting the 64-bit PCI hole size is not "
"supported for machine '%s'"), def->os.machine);
goto error;
}
if (!cap) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("64-bit PCI hole size setting is not supported "
"with this QEMU binary"));
goto error;
}
virCommandAddArg(cmd, "-global");
virCommandAddArgFormat(cmd, "%s.pci-hole64-size=%luK", hoststr,
cont->opts.pciopts.pcihole64size);
}
}
for (i = 0; i < def->ndisks; i++) {
virDomainDiskDefPtr disk = def->disks[i];
if (disk->src->driverName != NULL &&
!STREQ(disk->src->driverName, "qemu")) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("unsupported driver name '%s' for disk '%s'"),
disk->src->driverName, disk->src->path);
goto error;
}
}
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) {
for (j = 0; j < ARRAY_CARDINALITY(contOrder); j++) {
for (i = 0; i < def->ncontrollers; i++) {
virDomainControllerDefPtr cont = def->controllers[i];
char *devstr;
if (cont->type != contOrder[j])
continue;
/* skip USB controllers with type none.*/
if (cont->type == VIR_DOMAIN_CONTROLLER_TYPE_USB &&
cont->model == VIR_DOMAIN_CONTROLLER_MODEL_USB_NONE) {
usbcontroller = -1; /* mark we don't want a controller */
continue;
}
/* skip pci-root/pcie-root */
if (cont->type == VIR_DOMAIN_CONTROLLER_TYPE_PCI &&
(cont->model == VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT ||
cont->model == VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT))
continue;
/* first SATA controller on Q35 machines is implicit */
if (cont->type == VIR_DOMAIN_CONTROLLER_TYPE_SATA &&
cont->idx == 0 && qemuDomainMachineIsQ35(def))
continue;
/* first IDE controller on i440fx machines is implicit */
if (cont->type == VIR_DOMAIN_CONTROLLER_TYPE_IDE &&
cont->idx == 0 && qemuDomainMachineIsI440FX(def))
continue;
if (cont->type == VIR_DOMAIN_CONTROLLER_TYPE_USB &&
cont->model == -1 &&
!qemuDomainMachineIsQ35(def) &&
(!virQEMUCapsGet(qemuCaps, QEMU_CAPS_PIIX3_USB_UHCI) ||
(!virQEMUCapsGet(qemuCaps, QEMU_CAPS_PCI_OHCI) &&
ARCH_IS_PPC64(def->os.arch)))) {
if (usblegacy) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Multiple legacy USB controllers are "
"not supported"));
goto error;
}
usblegacy = true;
continue;
}
virCommandAddArg(cmd, "-device");
if (!(devstr = qemuBuildControllerDevStr(def, cont, qemuCaps,
&usbcontroller)))
goto error;
virCommandAddArg(cmd, devstr);
VIR_FREE(devstr);
}
}
}
if (usbcontroller == 0 &&
!qemuDomainMachineIsQ35(def) &&
!ARCH_IS_S390(def->os.arch))
virCommandAddArg(cmd, "-usb");
for (i = 0; i < def->nhubs; i++) {
virDomainHubDefPtr hub = def->hubs[i];
char *optstr;
virCommandAddArg(cmd, "-device");
if (!(optstr = qemuBuildHubDevStr(def, hub, qemuCaps)))
goto error;
virCommandAddArg(cmd, optstr);
VIR_FREE(optstr);
}
/* If QEMU supports -drive param instead of old -hda, -hdb, -cdrom .. */
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE)) {
int bootCD = 0, bootFloppy = 0, bootDisk = 0;
if ((virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_BOOT) || emitBootindex)) {
/* bootDevs will get translated into either bootindex=N or boot=on
* depending on what qemu supports */
for (i = 0; i < def->os.nBootDevs; i++) {
switch (def->os.bootDevs[i]) {
case VIR_DOMAIN_BOOT_CDROM:
bootCD = i + 1;
break;
case VIR_DOMAIN_BOOT_FLOPPY:
bootFloppy = i + 1;
break;
case VIR_DOMAIN_BOOT_DISK:
bootDisk = i + 1;
break;
}
}
}
for (i = 0; i < def->ndisks; i++) {
char *optstr;
int bootindex = 0;
virDomainDiskDefPtr disk = def->disks[i];
bool withDeviceArg = false;
bool deviceFlagMasked = false;
/* Unless we have -device, then USB disks need special
handling */
if ((disk->bus == VIR_DOMAIN_DISK_BUS_USB) &&
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) {
if (disk->device == VIR_DOMAIN_DISK_DEVICE_DISK) {
virCommandAddArg(cmd, "-usbdevice");
virCommandAddArgFormat(cmd, "disk:%s", disk->src->path);
} else {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unsupported usb disk type for '%s'"),
disk->src->path);
goto error;
}
continue;
}
/* PowerPC pseries based VMs do not support floppy device */
if ((disk->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY) &&
ARCH_IS_PPC64(def->os.arch) && STRPREFIX(def->os.machine, "pseries")) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("PowerPC pseries machines do not support floppy device"));
goto error;
}
switch (disk->device) {
case VIR_DOMAIN_DISK_DEVICE_CDROM:
bootindex = bootCD;
bootCD = 0;
break;
case VIR_DOMAIN_DISK_DEVICE_FLOPPY:
bootindex = bootFloppy;
bootFloppy = 0;
break;
case VIR_DOMAIN_DISK_DEVICE_DISK:
case VIR_DOMAIN_DISK_DEVICE_LUN:
bootindex = bootDisk;
bootDisk = 0;
break;
}
virCommandAddArg(cmd, "-drive");
/* Unfortunately it is not possible to use
-device for floppies, xen PV, or SD
devices. Fortunately, those don't need
static PCI addresses, so we don't really
care that we can't use -device */
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) {
if (disk->bus != VIR_DOMAIN_DISK_BUS_XEN &&
disk->bus != VIR_DOMAIN_DISK_BUS_SD) {
withDeviceArg = true;
} else {
virQEMUCapsClear(qemuCaps, QEMU_CAPS_DEVICE);
deviceFlagMasked = true;
}
}
optstr = qemuBuildDriveStr(conn, disk,
emitBootindex ? false : !!bootindex,
qemuCaps);
if (deviceFlagMasked)
virQEMUCapsSet(qemuCaps, QEMU_CAPS_DEVICE);
if (!optstr)
goto error;
virCommandAddArg(cmd, optstr);
VIR_FREE(optstr);
if (!emitBootindex)
bootindex = 0;
else if (disk->info.bootIndex)
bootindex = disk->info.bootIndex;
if (withDeviceArg) {
if (disk->bus == VIR_DOMAIN_DISK_BUS_FDC) {
if (virAsprintf(&optstr, "drive%c=drive-%s",
disk->info.addr.drive.unit ? 'B' : 'A',
disk->info.alias) < 0)
goto error;
if (!qemuDomainMachineNeedsFDC(def)) {
virCommandAddArg(cmd, "-global");
virCommandAddArgFormat(cmd, "isa-fdc.%s", optstr);
} else {
virBufferAsprintf(&fdc_opts, "%s,", optstr);
}
VIR_FREE(optstr);
if (bootindex) {
if (virAsprintf(&optstr, "bootindex%c=%d",
disk->info.addr.drive.unit
? 'B' : 'A',
bootindex) < 0)
goto error;
if (!qemuDomainMachineNeedsFDC(def)) {
virCommandAddArg(cmd, "-global");
virCommandAddArgFormat(cmd, "isa-fdc.%s", optstr);
} else {
virBufferAsprintf(&fdc_opts, "%s,", optstr);
}
VIR_FREE(optstr);
}
} else {
virCommandAddArg(cmd, "-device");
if (!(optstr = qemuBuildDriveDevStr(def, disk, bootindex,
qemuCaps)))
goto error;
virCommandAddArg(cmd, optstr);
VIR_FREE(optstr);
}
}
}
/* Newer Q35 machine types require an explicit FDC controller */
virBufferTrim(&fdc_opts, ",", -1);
if ((fdc_opts_str = virBufferContentAndReset(&fdc_opts))) {
virCommandAddArg(cmd, "-device");
virCommandAddArgFormat(cmd, "isa-fdc,%s", fdc_opts_str);
VIR_FREE(fdc_opts_str);
}
} else {
for (i = 0; i < def->ndisks; i++) {
char dev[NAME_MAX];
char *file;
const char *fmt;
virDomainDiskDefPtr disk = def->disks[i];
if ((disk->src->type == VIR_STORAGE_TYPE_BLOCK) &&
(disk->tray_status == VIR_DOMAIN_DISK_TRAY_OPEN)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("tray status 'open' is invalid for "
"block type disk"));
goto error;
}
if (disk->bus == VIR_DOMAIN_DISK_BUS_USB) {
if (disk->device == VIR_DOMAIN_DISK_DEVICE_DISK) {
virCommandAddArg(cmd, "-usbdevice");
virCommandAddArgFormat(cmd, "disk:%s", disk->src->path);
} else {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unsupported usb disk type for '%s'"),
disk->src->path);
goto error;
}
continue;
}
if (STREQ(disk->dst, "hdc") &&
disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM) {
if (disk->src->path) {
snprintf(dev, NAME_MAX, "-%s", "cdrom");
} else {
continue;
}
} else {
if (STRPREFIX(disk->dst, "hd") ||
STRPREFIX(disk->dst, "fd")) {
snprintf(dev, NAME_MAX, "-%s", disk->dst);
} else {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unsupported disk type '%s'"), disk->dst);
goto error;
}
}
if (disk->src->type == VIR_STORAGE_TYPE_DIR) {
/* QEMU only supports magic FAT format for now */
if (disk->src->format > 0 &&
disk->src->format != VIR_STORAGE_FILE_FAT) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unsupported disk driver type for '%s'"),
virStorageFileFormatTypeToString(disk->src->format));
goto error;
}
if (!disk->src->readonly) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("cannot create virtual FAT disks in read-write mode"));
goto error;
}
if (disk->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY)
fmt = "fat:floppy:%s";
else
fmt = "fat:%s";
if (virAsprintf(&file, fmt, disk->src) < 0)
goto error;
} else if (disk->src->type == VIR_STORAGE_TYPE_NETWORK) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("network disks are only supported with -drive"));
goto error;
} else {
if (VIR_STRDUP(file, disk->src->path) < 0)
goto error;
}
/* Don't start with source if the tray is open for
* CDROM and Floppy device.
*/
if (!((disk->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY ||
disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM) &&
disk->tray_status == VIR_DOMAIN_DISK_TRAY_OPEN))
virCommandAddArgList(cmd, dev, file, NULL);
VIR_FREE(file);
}
}
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_FSDEV)) {
for (i = 0; i < def->nfss; i++) {
char *optstr;
virDomainFSDefPtr fs = def->fss[i];
virCommandAddArg(cmd, "-fsdev");
if (!(optstr = qemuBuildFSStr(fs, qemuCaps)))
goto error;
virCommandAddArg(cmd, optstr);
VIR_FREE(optstr);
virCommandAddArg(cmd, "-device");
if (!(optstr = qemuBuildFSDevStr(def, fs, qemuCaps)))
goto error;
virCommandAddArg(cmd, optstr);
VIR_FREE(optstr);
}
} else {
if (def->nfss) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("filesystem passthrough not supported by this QEMU"));
goto error;
}
}
if (!def->nnets) {
/* If we have -device, then we set -nodefault already */
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE))
virCommandAddArgList(cmd, "-net", "none", NULL);
} else {
int bootNet = 0;
if (emitBootindex) {
/* convert to bootindex since we didn't emit
* -boot n
*/
for (i = 0; i < def->os.nBootDevs; i++) {
if (def->os.bootDevs[i] == VIR_DOMAIN_BOOT_NET) {
bootNet = i + 1;
break;
}
}
}
for (i = 0; i < def->nnets; i++) {
virDomainNetDefPtr net = def->nets[i];
int vlan;
/* VLANs are not used with -netdev, so don't record them */
if (qemuDomainSupportsNetdev(def, qemuCaps, net))
vlan = -1;
else
vlan = i;
if (qemuBuildInterfaceCommandLine(cmd, driver, def, net,
qemuCaps, vlan, bootNet, vmop,
standalone, nnicindexes, nicindexes) < 0)
goto error;
last_good_net = i;
bootNet = 0;
}
}
if (def->nsmartcards) {
/* -device usb-ccid was already emitted along with other
* controllers. For now, qemu handles only one smartcard. */
virDomainSmartcardDefPtr smartcard = def->smartcards[0];
char *devstr;
virBuffer opt = VIR_BUFFER_INITIALIZER;
const char *database;
if (def->nsmartcards > 1 ||
smartcard->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCID ||
smartcard->info.addr.ccid.controller != 0 ||
smartcard->info.addr.ccid.slot != 0) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("this QEMU binary lacks multiple smartcard "
"support"));
virBufferFreeAndReset(&opt);
goto error;
}
switch (smartcard->type) {
case VIR_DOMAIN_SMARTCARD_TYPE_HOST:
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_CHARDEV) ||
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_CCID_EMULATED)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("this QEMU binary lacks smartcard host "
"mode support"));
goto error;
}
virBufferAddLit(&opt, "ccid-card-emulated,backend=nss-emulated");
break;
case VIR_DOMAIN_SMARTCARD_TYPE_HOST_CERTIFICATES:
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_CHARDEV) ||
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_CCID_EMULATED)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("this QEMU binary lacks smartcard host "
"mode support"));
goto error;
}
virBufferAddLit(&opt, "ccid-card-emulated,backend=certificates");
for (j = 0; j < VIR_DOMAIN_SMARTCARD_NUM_CERTIFICATES; j++) {
if (strchr(smartcard->data.cert.file[j], ',')) {
virBufferFreeAndReset(&opt);
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("invalid certificate name: %s"),
smartcard->data.cert.file[j]);
goto error;
}
virBufferAsprintf(&opt, ",cert%zu=%s", j + 1,
smartcard->data.cert.file[j]);
}
if (smartcard->data.cert.database) {
if (strchr(smartcard->data.cert.database, ',')) {
virBufferFreeAndReset(&opt);
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("invalid database name: %s"),
smartcard->data.cert.database);
goto error;
}
database = smartcard->data.cert.database;
} else {
database = VIR_DOMAIN_SMARTCARD_DEFAULT_DATABASE;
}
virBufferAsprintf(&opt, ",db=%s", database);
break;
case VIR_DOMAIN_SMARTCARD_TYPE_PASSTHROUGH:
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_CHARDEV) ||
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_CCID_PASSTHRU)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("this QEMU binary lacks smartcard "
"passthrough mode support"));
goto error;
}
virCommandAddArg(cmd, "-chardev");
if (!(devstr = qemuBuildChrChardevStr(&smartcard->data.passthru,
smartcard->info.alias,
qemuCaps))) {
virBufferFreeAndReset(&opt);
goto error;
}
virCommandAddArg(cmd, devstr);
VIR_FREE(devstr);
virBufferAsprintf(&opt, "ccid-card-passthru,chardev=char%s",
smartcard->info.alias);
break;
default:
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unexpected smartcard type %d"),
smartcard->type);
virBufferFreeAndReset(&opt);
goto error;
}
virCommandAddArg(cmd, "-device");
virBufferAsprintf(&opt, ",id=%s,bus=ccid0.0", smartcard->info.alias);
virCommandAddArgBuffer(cmd, &opt);
}
for (i = 0; i < def->nserials; i++) {
virDomainChrDefPtr serial = def->serials[i];
char *devstr;
if (serial->source.type == VIR_DOMAIN_CHR_TYPE_SPICEPORT && !spice)
continue;
/* Use -chardev with -device if they are available */
if (virQEMUCapsSupportsChardev(def, qemuCaps, serial)) {
virCommandAddArg(cmd, "-chardev");
if (!(devstr = qemuBuildChrChardevStr(&serial->source,
serial->info.alias,
qemuCaps)))
goto error;
virCommandAddArg(cmd, devstr);
VIR_FREE(devstr);
if (qemuBuildChrDeviceCommandLine(cmd, def, serial, qemuCaps) < 0)
goto error;
} else {
virCommandAddArg(cmd, "-serial");
if (!(devstr = qemuBuildChrArgStr(&serial->source, NULL)))
goto error;
virCommandAddArg(cmd, devstr);
VIR_FREE(devstr);
}
actualSerials++;
}
/* If we have -device, then we set -nodefault already */
if (!actualSerials && !virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE))
virCommandAddArgList(cmd, "-serial", "none", NULL);
if (!def->nparallels) {
/* If we have -device, then we set -nodefault already */
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE))
virCommandAddArgList(cmd, "-parallel", "none", NULL);
} else {
for (i = 0; i < def->nparallels; i++) {
virDomainChrDefPtr parallel = def->parallels[i];
char *devstr;
/* Use -chardev with -device if they are available */
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_CHARDEV) &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) {
virCommandAddArg(cmd, "-chardev");
if (!(devstr = qemuBuildChrChardevStr(¶llel->source,
parallel->info.alias,
qemuCaps)))
goto error;
virCommandAddArg(cmd, devstr);
VIR_FREE(devstr);
if (qemuBuildChrDeviceCommandLine(cmd, def, parallel, qemuCaps) < 0)
goto error;
} else {
virCommandAddArg(cmd, "-parallel");
if (!(devstr = qemuBuildChrArgStr(¶llel->source, NULL)))
goto error;
virCommandAddArg(cmd, devstr);
VIR_FREE(devstr);
}
}
}
for (i = 0; i < def->nchannels; i++) {
virDomainChrDefPtr channel = def->channels[i];
char *devstr;
switch (channel->targetType) {
case VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_GUESTFWD:
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_CHARDEV) ||
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
"%s", _("guestfwd requires QEMU to support -chardev & -device"));
goto error;
}
virCommandAddArg(cmd, "-chardev");
if (!(devstr = qemuBuildChrChardevStr(&channel->source,
channel->info.alias,
qemuCaps)))
goto error;
virCommandAddArg(cmd, devstr);
VIR_FREE(devstr);
if (qemuBuildChrDeviceStr(&devstr, def, channel, qemuCaps) < 0)
goto error;
virCommandAddArgList(cmd, "-netdev", devstr, NULL);
VIR_FREE(devstr);
break;
case VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO:
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("virtio channel requires QEMU to support -device"));
goto error;
}
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_SPICEVMC) &&
channel->source.type == VIR_DOMAIN_CHR_TYPE_SPICEVMC) {
/* spicevmc was originally introduced via a -device
* with a backend internal to qemu; although we prefer
* the newer -chardev interface. */
;
} else {
virCommandAddArg(cmd, "-chardev");
if (!(devstr = qemuBuildChrChardevStr(&channel->source,
channel->info.alias,
qemuCaps)))
goto error;
virCommandAddArg(cmd, devstr);
VIR_FREE(devstr);
}
if (qemuBuildChrDeviceCommandLine(cmd, def, channel, qemuCaps) < 0)
goto error;
break;
}
}
/* Explicit console devices */
for (i = 0; i < def->nconsoles; i++) {
virDomainChrDefPtr console = def->consoles[i];
char *devstr;
switch (console->targetType) {
case VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SCLP:
case VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SCLPLM:
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("sclp console requires QEMU to support -device"));
goto error;
}
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SCLP_S390)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("sclp console requires QEMU to support s390-sclp"));
goto error;
}
virCommandAddArg(cmd, "-chardev");
if (!(devstr = qemuBuildChrChardevStr(&console->source,
console->info.alias,
qemuCaps)))
goto error;
virCommandAddArg(cmd, devstr);
VIR_FREE(devstr);
if (qemuBuildChrDeviceCommandLine(cmd, def, console, qemuCaps) < 0)
goto error;
break;
case VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_VIRTIO:
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("virtio channel requires QEMU to support -device"));
goto error;
}
virCommandAddArg(cmd, "-chardev");
if (!(devstr = qemuBuildChrChardevStr(&console->source,
console->info.alias,
qemuCaps)))
goto error;
virCommandAddArg(cmd, devstr);
VIR_FREE(devstr);
if (qemuBuildChrDeviceCommandLine(cmd, def, console, qemuCaps) < 0)
goto error;
break;
case VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SERIAL:
break;
default:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("unsupported console target type %s"),
NULLSTR(virDomainChrConsoleTargetTypeToString(console->targetType)));
goto error;
}
}
if (def->tpm) {
if (qemuBuildTPMCommandLine(def, cmd, qemuCaps, emulator) < 0)
goto error;
}
for (i = 0; i < def->ninputs; i++) {
virDomainInputDefPtr input = def->inputs[i];
if (input->bus == VIR_DOMAIN_INPUT_BUS_USB) {
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) {
char *optstr;
virCommandAddArg(cmd, "-device");
if (!(optstr = qemuBuildUSBInputDevStr(def, input, qemuCaps)))
goto error;
virCommandAddArg(cmd, optstr);
VIR_FREE(optstr);
} else {
switch (input->type) {
case VIR_DOMAIN_INPUT_TYPE_MOUSE:
virCommandAddArgList(cmd, "-usbdevice", "mouse", NULL);
break;
case VIR_DOMAIN_INPUT_TYPE_TABLET:
virCommandAddArgList(cmd, "-usbdevice", "tablet", NULL);
break;
case VIR_DOMAIN_INPUT_TYPE_KBD:
virCommandAddArgList(cmd, "-usbdevice", "keyboard", NULL);
break;
}
}
}
}
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_0_10) && sdl + vnc + spice > 1) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("only 1 graphics device is supported"));
goto error;
}
if (sdl > 1 || vnc > 1 || spice > 1) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("only 1 graphics device of each type "
"(sdl, vnc, spice) is supported"));
goto error;
}
for (i = 0; i < def->ngraphics; ++i) {
if (qemuBuildGraphicsCommandLine(cfg, cmd, def, qemuCaps,
def->graphics[i]) < 0)
goto error;
}
if (def->nvideos > 0) {
int primaryVideoType = def->videos[0]->type;
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VIDEO_PRIMARY) &&
((primaryVideoType == VIR_DOMAIN_VIDEO_TYPE_VGA &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VGA)) ||
(primaryVideoType == VIR_DOMAIN_VIDEO_TYPE_CIRRUS &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_CIRRUS_VGA)) ||
(primaryVideoType == VIR_DOMAIN_VIDEO_TYPE_VMVGA &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VMWARE_SVGA)) ||
(primaryVideoType == VIR_DOMAIN_VIDEO_TYPE_QXL &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_QXL_VGA)))
) {
for (i = 0; i < def->nvideos; i++) {
char *str;
virCommandAddArg(cmd, "-device");
if (!(str = qemuBuildDeviceVideoStr(def, def->videos[i], qemuCaps, !i)))
goto error;
virCommandAddArg(cmd, str);
VIR_FREE(str);
}
} else if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_VGA)) {
if (primaryVideoType == VIR_DOMAIN_VIDEO_TYPE_XEN) {
/* nothing - vga has no effect on Xen pvfb */
} else {
if ((primaryVideoType == VIR_DOMAIN_VIDEO_TYPE_QXL) &&
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_VGA_QXL)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("This QEMU does not support QXL graphics adapters"));
goto error;
}
const char *vgastr = qemuVideoTypeToString(primaryVideoType);
if (!vgastr || STREQ(vgastr, "")) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("video type %s is not supported with QEMU"),
virDomainVideoTypeToString(primaryVideoType));
goto error;
}
virCommandAddArgList(cmd, "-vga", vgastr, NULL);
/* If we cannot use --device option to specify the video device
* in QEMU we will fallback to the old --vga option. To get the
* correct device name for the --vga option the 'qemuVideo' is
* used, but to set some device attributes we need to use the
* --global option and for that we need to specify the device
* name the same as for --device option and for that we need to
* use 'qemuDeviceVideo'.
*
* See 'Graphics Devices' section in docs/qdev-device-use.txt in
* QEMU repository.
*/
const char *dev = qemuDeviceVideoTypeToString(primaryVideoType);
if (def->videos[0]->type == VIR_DOMAIN_VIDEO_TYPE_QXL &&
(def->videos[0]->vram || def->videos[0]->ram) &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) {
unsigned int ram = def->videos[0]->ram;
unsigned int vram = def->videos[0]->vram;
unsigned int vgamem = def->videos[0]->vgamem;
if (vram > (UINT_MAX / 1024)) {
virReportError(VIR_ERR_OVERFLOW,
_("value for 'vram' must be less than '%u'"),
UINT_MAX / 1024);
goto error;
}
if (ram > (UINT_MAX / 1024)) {
virReportError(VIR_ERR_OVERFLOW,
_("value for 'ram' must be less than '%u'"),
UINT_MAX / 1024);
goto error;
}
if (ram) {
virCommandAddArg(cmd, "-global");
virCommandAddArgFormat(cmd, "%s.ram_size=%u",
dev, ram * 1024);
}
if (vram) {
virCommandAddArg(cmd, "-global");
virCommandAddArgFormat(cmd, "%s.vram_size=%u",
dev, vram * 1024);
}
if (vgamem &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_QXL_VGA_VGAMEM)) {
virCommandAddArg(cmd, "-global");
virCommandAddArgFormat(cmd, "%s.vgamem_mb=%u",
dev, vgamem / 1024);
}
}
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE) &&
def->videos[0]->vram &&
((primaryVideoType == VIR_DOMAIN_VIDEO_TYPE_VGA &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_VGA_VGAMEM)) ||
(primaryVideoType == VIR_DOMAIN_VIDEO_TYPE_VMVGA &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_VMWARE_SVGA_VGAMEM)))) {
unsigned int vram = def->videos[0]->vram;
if (vram < 1024) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
"%s", _("value for 'vgamem' must be at "
"least 1 MiB (1024 KiB)"));
goto error;
}
virCommandAddArg(cmd, "-global");
virCommandAddArgFormat(cmd, "%s.vgamem_mb=%u",
dev, vram / 1024);
}
}
if (def->nvideos > 1) {
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) {
for (i = 1; i < def->nvideos; i++) {
char *str;
if (def->videos[i]->type != VIR_DOMAIN_VIDEO_TYPE_QXL) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("video type %s is only valid as primary video card"),
virDomainVideoTypeToString(def->videos[0]->type));
goto error;
}
virCommandAddArg(cmd, "-device");
if (!(str = qemuBuildDeviceVideoStr(def, def->videos[i], qemuCaps, false)))
goto error;
virCommandAddArg(cmd, str);
VIR_FREE(str);
}
} else {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
"%s", _("only one video card is currently supported"));
goto error;
}
}
} else {
switch (def->videos[0]->type) {
case VIR_DOMAIN_VIDEO_TYPE_VGA:
virCommandAddArg(cmd, "-std-vga");
break;
case VIR_DOMAIN_VIDEO_TYPE_VMVGA:
virCommandAddArg(cmd, "-vmwarevga");
break;
case VIR_DOMAIN_VIDEO_TYPE_XEN:
case VIR_DOMAIN_VIDEO_TYPE_CIRRUS:
/* No special args - this is the default */
break;
default:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("video type %s is not supported with this QEMU"),
virDomainVideoTypeToString(def->videos[0]->type));
goto error;
}
if (def->nvideos > 1) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
"%s", _("only one video card is currently supported"));
goto error;
}
}
} else {
/* If we have -device, then we set -nodefault already */
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE) &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_VGA) &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_VGA_NONE))
virCommandAddArgList(cmd, "-vga", "none", NULL);
}
/* Add sound hardware */
if (def->nsounds) {
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) {
for (i = 0; i < def->nsounds; i++) {
virDomainSoundDefPtr sound = def->sounds[i];
char *str = NULL;
/* Sadly pcspk device doesn't use -device syntax. Fortunately
* we don't need to set any PCI address on it, so we don't
* mind too much */
if (sound->model == VIR_DOMAIN_SOUND_MODEL_PCSPK) {
virCommandAddArgList(cmd, "-soundhw", "pcspk", NULL);
} else {
virCommandAddArg(cmd, "-device");
if (!(str = qemuBuildSoundDevStr(def, sound, qemuCaps)))
goto error;
virCommandAddArg(cmd, str);
VIR_FREE(str);
if (sound->model == VIR_DOMAIN_SOUND_MODEL_ICH6 ||
sound->model == VIR_DOMAIN_SOUND_MODEL_ICH9) {
char *codecstr = NULL;
for (j = 0; j < sound->ncodecs; j++) {
virCommandAddArg(cmd, "-device");
if (!(codecstr = qemuBuildSoundCodecStr(sound, sound->codecs[j], qemuCaps))) {
goto error;
}
virCommandAddArg(cmd, codecstr);
VIR_FREE(codecstr);
}
if (j == 0) {
virDomainSoundCodecDef codec = {
VIR_DOMAIN_SOUND_CODEC_TYPE_DUPLEX,
0
};
virCommandAddArg(cmd, "-device");
if (!(codecstr = qemuBuildSoundCodecStr(sound, &codec, qemuCaps))) {
goto error;
}
virCommandAddArg(cmd, codecstr);
VIR_FREE(codecstr);
}
}
}
}
} else {
int size = 100;
char *modstr;
if (VIR_ALLOC_N(modstr, size+1) < 0)
goto error;
for (i = 0; i < def->nsounds && size > 0; i++) {
virDomainSoundDefPtr sound = def->sounds[i];
const char *model = virDomainSoundModelTypeToString(sound->model);
if (!model) {
VIR_FREE(modstr);
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("invalid sound model"));
goto error;
}
if (sound->model == VIR_DOMAIN_SOUND_MODEL_ICH6 ||
sound->model == VIR_DOMAIN_SOUND_MODEL_ICH9) {
VIR_FREE(modstr);
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("this QEMU binary lacks hda support"));
goto error;
}
strncat(modstr, model, size);
size -= strlen(model);
if (i < (def->nsounds - 1))
strncat(modstr, ",", size--);
}
virCommandAddArgList(cmd, "-soundhw", modstr, NULL);
VIR_FREE(modstr);
}
}
/* Add watchdog hardware */
if (def->watchdog) {
virDomainWatchdogDefPtr watchdog = def->watchdog;
char *optstr;
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) {
virCommandAddArg(cmd, "-device");
optstr = qemuBuildWatchdogDevStr(def, watchdog, qemuCaps);
if (!optstr)
goto error;
} else {
virCommandAddArg(cmd, "-watchdog");
const char *model = virDomainWatchdogModelTypeToString(watchdog->model);
if (!model) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("missing watchdog model"));
goto error;
}
if (VIR_STRDUP(optstr, model) < 0)
goto error;
}
virCommandAddArg(cmd, optstr);
VIR_FREE(optstr);
int act = watchdog->action;
if (act == VIR_DOMAIN_WATCHDOG_ACTION_DUMP)
act = VIR_DOMAIN_WATCHDOG_ACTION_PAUSE;
const char *action = virDomainWatchdogActionTypeToString(act);
if (!action) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("invalid watchdog action"));
goto error;
}
virCommandAddArgList(cmd, "-watchdog-action", action, NULL);
}
/* Add redirected devices */
for (i = 0; i < def->nredirdevs; i++) {
virDomainRedirdevDefPtr redirdev = def->redirdevs[i];
char *devstr;
virCommandAddArg(cmd, "-chardev");
if (!(devstr = qemuBuildChrChardevStr(&redirdev->source.chr,
redirdev->info.alias,
qemuCaps))) {
goto error;
}
virCommandAddArg(cmd, devstr);
VIR_FREE(devstr);
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("redirected devices are not supported by this QEMU"));
goto error;
}
virCommandAddArg(cmd, "-device");
if (!(devstr = qemuBuildRedirdevDevStr(def, redirdev, qemuCaps)))
goto error;
virCommandAddArg(cmd, devstr);
VIR_FREE(devstr);
}
/* Add host passthrough hardware */
for (i = 0; i < def->nhostdevs; i++) {
virDomainHostdevDefPtr hostdev = def->hostdevs[i];
char *devstr;
if (hostdev->info->bootIndex) {
if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS ||
(hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI &&
hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB &&
hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("booting from assigned devices is only "
"supported for PCI, USB and SCSI devices"));
goto error;
} else {
if (hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) {
if (hostdev->source.subsys.u.pci.backend
== VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO) {
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_VFIO_PCI_BOOTINDEX)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("booting from PCI devices assigned with VFIO "
"is not supported with this version of qemu"));
goto error;
}
} else {
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_PCI_BOOTINDEX)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("booting from assigned PCI devices is not "
"supported with this version of qemu"));
goto error;
}
}
}
if (hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB &&
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_USB_HOST_BOOTINDEX)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("booting from assigned USB devices is not "
"supported with this version of qemu"));
goto error;
}
if (hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI &&
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_SCSI_GENERIC_BOOTINDEX)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("booting from assigned SCSI devices is not"
" supported with this version of qemu"));
goto error;
}
}
}
/* USB */
if (hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS &&
hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) {
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) {
virCommandAddArg(cmd, "-device");
if (!(devstr = qemuBuildUSBHostdevDevStr(def, hostdev, qemuCaps)))
goto error;
virCommandAddArg(cmd, devstr);
VIR_FREE(devstr);
} else {
virCommandAddArg(cmd, "-usbdevice");
if (!(devstr = qemuBuildUSBHostdevUSBDevStr(hostdev)))
goto error;
virCommandAddArg(cmd, devstr);
VIR_FREE(devstr);
}
}
/* PCI */
if (hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS &&
hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) {
int backend = hostdev->source.subsys.u.pci.backend;
if (backend == VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO) {
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VFIO_PCI)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("VFIO PCI device assignment is not "
"supported by this version of qemu"));
goto error;
}
/* VFIO requires all of the guest's memory to be locked
* resident */
mlock = true;
}
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) {
char *configfd_name = NULL;
if ((backend != VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO) &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_PCI_CONFIGFD)) {
int configfd = qemuOpenPCIConfig(hostdev);
if (configfd >= 0) {
if (virAsprintf(&configfd_name, "%d", configfd) < 0) {
VIR_FORCE_CLOSE(configfd);
goto error;
}
virCommandPassFD(cmd, configfd,
VIR_COMMAND_PASS_FD_CLOSE_PARENT);
}
}
virCommandAddArg(cmd, "-device");
devstr = qemuBuildPCIHostdevDevStr(def, hostdev, configfd_name, qemuCaps);
VIR_FREE(configfd_name);
if (!devstr)
goto error;
virCommandAddArg(cmd, devstr);
VIR_FREE(devstr);
} else if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_PCIDEVICE)) {
virCommandAddArg(cmd, "-pcidevice");
if (!(devstr = qemuBuildPCIHostdevPCIDevStr(hostdev, qemuCaps)))
goto error;
virCommandAddArg(cmd, devstr);
VIR_FREE(devstr);
} else {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("PCI device assignment is not supported by this version of qemu"));
goto error;
}
}
/* SCSI */
if (hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS &&
hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI) {
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE) &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE) &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_SCSI_GENERIC)) {
char *drvstr;
virCommandAddArg(cmd, "-drive");
if (!(drvstr = qemuBuildSCSIHostdevDrvStr(conn, hostdev, qemuCaps, callbacks)))
goto error;
virCommandAddArg(cmd, drvstr);
VIR_FREE(drvstr);
virCommandAddArg(cmd, "-device");
if (!(devstr = qemuBuildSCSIHostdevDevStr(def, hostdev, qemuCaps)))
goto error;
virCommandAddArg(cmd, devstr);
VIR_FREE(devstr);
} else {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("SCSI passthrough is not supported by this version of qemu"));
goto error;
}
}
}
/* Migration is very annoying due to wildly varying syntax &
* capabilities over time of KVM / QEMU codebases.
*/
if (migrateFrom) {
virCommandAddArg(cmd, "-incoming");
if (STRPREFIX(migrateFrom, "tcp")) {
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_MIGRATE_QEMU_TCP)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
"%s", _("TCP migration is not supported with "
"this QEMU binary"));
goto error;
}
virCommandAddArg(cmd, migrateFrom);
} else if (STRPREFIX(migrateFrom, "rdma")) {
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_MIGRATE_RDMA)) {
virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
_("incoming RDMA migration is not supported "
"with this QEMU binary"));
goto error;
}
virCommandAddArg(cmd, migrateFrom);
} else if (STREQ(migrateFrom, "stdio")) {
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_MIGRATE_QEMU_FD)) {
virCommandAddArgFormat(cmd, "fd:%d", migrateFd);
virCommandPassFD(cmd, migrateFd, 0);
} else if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_MIGRATE_QEMU_EXEC)) {
virCommandAddArg(cmd, "exec:cat");
virCommandSetInputFD(cmd, migrateFd);
} else if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_MIGRATE_KVM_STDIO)) {
virCommandAddArg(cmd, migrateFrom);
virCommandSetInputFD(cmd, migrateFd);
} else {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
"%s", _("STDIO migration is not supported "
"with this QEMU binary"));
goto error;
}
} else if (STRPREFIX(migrateFrom, "exec")) {
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_MIGRATE_QEMU_EXEC)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
"%s", _("EXEC migration is not supported "
"with this QEMU binary"));
goto error;
}
virCommandAddArg(cmd, migrateFrom);
} else if (STRPREFIX(migrateFrom, "fd")) {
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_MIGRATE_QEMU_FD)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
"%s", _("FD migration is not supported "
"with this QEMU binary"));
goto error;
}
virCommandAddArg(cmd, migrateFrom);
virCommandPassFD(cmd, migrateFd, 0);
} else if (STRPREFIX(migrateFrom, "unix")) {
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_MIGRATE_QEMU_UNIX)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
"%s", _("UNIX migration is not supported "
"with this QEMU binary"));
goto error;
}
virCommandAddArg(cmd, migrateFrom);
} else {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("unknown migration protocol"));
goto error;
}
}
/* QEMU changed its default behavior to not include the virtio balloon
* device. Explicitly request it to ensure it will be present.
*
* NB: Earlier we declared that VirtIO balloon will always be in
* slot 0x3 on bus 0x0
*/
if (STREQLEN(def->os.machine, "s390-virtio", 10) &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_S390) && def->memballoon)
def->memballoon->model = VIR_DOMAIN_MEMBALLOON_MODEL_NONE;
if (def->memballoon &&
def->memballoon->model != VIR_DOMAIN_MEMBALLOON_MODEL_NONE) {
if (def->memballoon->model != VIR_DOMAIN_MEMBALLOON_MODEL_VIRTIO) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("Memory balloon device type '%s' is not supported by this version of qemu"),
virDomainMemballoonModelTypeToString(def->memballoon->model));
goto error;
}
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE)) {
char *optstr;
virCommandAddArg(cmd, "-device");
optstr = qemuBuildMemballoonDevStr(def, def->memballoon, qemuCaps);
if (!optstr)
goto error;
virCommandAddArg(cmd, optstr);
VIR_FREE(optstr);
} else if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_BALLOON)) {
virCommandAddArgList(cmd, "-balloon", "virtio", NULL);
}
}
for (i = 0; i < def->nrngs; i++) {
virDomainRNGDefPtr rng = def->rngs[i];
char *tmp;
if (!rng->info.alias) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("RNG device is missing alias"));
goto error;
}
/* possibly add character device for backend */
if (qemuBuildRNGBackendChrdevStr(rng, qemuCaps, &tmp) < 0)
goto error;
if (tmp) {
virCommandAddArgList(cmd, "-chardev", tmp, NULL);
VIR_FREE(tmp);
}
/* add the RNG source backend */
if (!(tmp = qemuBuildRNGBackendStr(rng, qemuCaps)))
goto error;
virCommandAddArgList(cmd, "-object", tmp, NULL);
VIR_FREE(tmp);
/* add the device */
if (!(tmp = qemuBuildRNGDevStr(def, rng, qemuCaps)))
goto error;
virCommandAddArgList(cmd, "-device", tmp, NULL);
VIR_FREE(tmp);
}
if (def->nvram) {
if (ARCH_IS_PPC64(def->os.arch) &&
STRPREFIX(def->os.machine, "pseries")) {
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_NVRAM)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("nvram device is not supported by "
"this QEMU binary"));
goto error;
}
char *optstr;
virCommandAddArg(cmd, "-global");
optstr = qemuBuildNVRAMDevStr(def->nvram);
if (!optstr)
goto error;
if (optstr)
virCommandAddArg(cmd, optstr);
VIR_FREE(optstr);
} else {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("nvram device is only supported for PPC64"));
goto error;
}
}
if (snapshot)
virCommandAddArgList(cmd, "-loadvm", snapshot->def->name, NULL);
if (def->namespaceData) {
qemuDomainCmdlineDefPtr qemucmd;
qemucmd = def->namespaceData;
for (i = 0; i < qemucmd->num_args; i++)
virCommandAddArg(cmd, qemucmd->args[i]);
for (i = 0; i < qemucmd->num_env; i++)
virCommandAddEnvPair(cmd, qemucmd->env_name[i],
qemucmd->env_value[i]
? qemucmd->env_value[i] : "");
}
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_SECCOMP_SANDBOX)) {
if (cfg->seccompSandbox == 0)
virCommandAddArgList(cmd, "-sandbox", "off", NULL);
else if (cfg->seccompSandbox > 0)
virCommandAddArgList(cmd, "-sandbox", "on", NULL);
} else if (cfg->seccompSandbox > 0) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("QEMU does not support seccomp sandboxes"));
goto error;
}
if (def->panic) {
if (ARCH_IS_PPC64(def->os.arch) && STRPREFIX(def->os.machine, "pseries")) {
/* For pSeries guests, the firmware provides the same
* functionality as the pvpanic device. The address
* cannot be configured by the user */
if (def->panic->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("setting the panic device address is not "
"supported for pSeries guests"));
goto error;
}
} else {
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_PANIC)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("the QEMU binary does not support the "
"panic device"));
goto error;
}
switch (def->panic->info.type) {
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_ISA:
virCommandAddArg(cmd, "-device");
virCommandAddArgFormat(cmd, "pvpanic,ioport=%d",
def->panic->info.addr.isa.iobase);
break;
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE:
virCommandAddArgList(cmd, "-device", "pvpanic", NULL);
break;
default:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("panic is supported only "
"with ISA address type"));
goto error;
}
}
}
for (i = 0; i < def->nshmems; i++) {
if (qemuBuildShmemCommandLine(cmd, def, def->shmems[i], qemuCaps))
goto error;
}
if (mlock) {
unsigned long long memKB;
/* VFIO requires all of the guest's memory to be
* locked resident, plus some amount for IO
* space. Alex Williamson suggested adding 1GiB for IO
* space just to be safe (some finer tuning might be
* nice, though).
*/
if (virMemoryLimitIsSet(def->mem.hard_limit))
memKB = def->mem.hard_limit;
else
memKB = virDomainDefGetMemoryActual(def) + 1024 * 1024;
virCommandSetMaxMemLock(cmd, memKB * 1024);
}
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_MSG_TIMESTAMP) &&
cfg->logTimestamp)
virCommandAddArgList(cmd, "-msg", "timestamp=on", NULL);
virObjectUnref(cfg);
return cmd;
error:
VIR_FREE(boot_order_str);
VIR_FREE(boot_opts_str);
virBufferFreeAndReset(&boot_buf);
virObjectUnref(cfg);
/* free up any resources in the network driver
* but don't overwrite the original error */
originalError = virSaveLastError();
for (i = 0; last_good_net != -1 && i <= last_good_net; i++)
virDomainConfNWFilterTeardown(def->nets[i]);
virSetError(originalError);
virFreeError(originalError);
virCommandFree(cmd);
return NULL;
}
/* This function generates the correct '-device' string for character
* devices of each architecture.
*/
static int
qemuBuildSerialChrDeviceStr(char **deviceStr,
virDomainDefPtr def,
virDomainChrDefPtr serial,
virQEMUCapsPtr qemuCaps,
virArch arch,
char *machine)
{
virBuffer cmd = VIR_BUFFER_INITIALIZER;
if (ARCH_IS_PPC64(arch) && STRPREFIX(machine, "pseries")) {
if (serial->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL &&
serial->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_SPAPRVIO) {
virBufferAsprintf(&cmd, "spapr-vty,chardev=char%s",
serial->info.alias);
if (qemuBuildDeviceAddressStr(&cmd, def, &serial->info, qemuCaps) < 0)
goto error;
}
} else {
virBufferAsprintf(&cmd, "%s,chardev=char%s,id=%s",
virDomainChrSerialTargetTypeToString(serial->targetType),
serial->info.alias, serial->info.alias);
switch (serial->targetType) {
case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_USB:
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_USB_SERIAL)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("usb-serial is not supported in this QEMU binary"));
goto error;
}
if (serial->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE &&
serial->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("usb-serial requires address of usb type"));
goto error;
}
if (qemuBuildDeviceAddressStr(&cmd, def, &serial->info, qemuCaps) < 0)
goto error;
break;
case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_ISA:
if (serial->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE &&
serial->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_ISA) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("isa-serial requires address of isa type"));
goto error;
}
if (qemuBuildDeviceAddressStr(&cmd, def, &serial->info, qemuCaps) < 0)
goto error;
break;
case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_PCI:
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_PCI_SERIAL)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("pci-serial is not supported with this QEMU binary"));
goto error;
}
if (serial->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE &&
serial->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("pci-serial requires address of pci type"));
goto error;
}
if (qemuBuildDeviceAddressStr(&cmd, def, &serial->info, qemuCaps) < 0)
goto error;
break;
}
}
if (virBufferCheckError(&cmd) < 0)
goto error;
*deviceStr = virBufferContentAndReset(&cmd);
return 0;
error:
virBufferFreeAndReset(&cmd);
return -1;
}
static int
qemuBuildParallelChrDeviceStr(char **deviceStr,
virDomainChrDefPtr chr)
{
if (virAsprintf(deviceStr, "isa-parallel,chardev=char%s,id=%s",
chr->info.alias, chr->info.alias) < 0)
return -1;
return 0;
}
static int
qemuBuildChannelChrDeviceStr(char **deviceStr,
virDomainDefPtr def,
virDomainChrDefPtr chr,
virQEMUCapsPtr qemuCaps)
{
int ret = -1;
char *addr = NULL;
int port;
switch ((virDomainChrChannelTargetType) chr->targetType) {
case VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_GUESTFWD:
addr = virSocketAddrFormat(chr->target.addr);
if (!addr)
return ret;
port = virSocketAddrGetPort(chr->target.addr);
if (virAsprintf(deviceStr,
"user,guestfwd=tcp:%s:%i-chardev:char%s,id=user-%s",
addr, port, chr->info.alias, chr->info.alias) < 0)
goto cleanup;
break;
case VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO:
if (!(*deviceStr = qemuBuildVirtioSerialPortDevStr(def, chr, qemuCaps)))
goto cleanup;
break;
case VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_NONE:
case VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_LAST:
return ret;
}
ret = 0;
cleanup:
VIR_FREE(addr);
return ret;
}
static int
qemuBuildConsoleChrDeviceStr(char **deviceStr,
virDomainDefPtr def,
virDomainChrDefPtr chr,
virQEMUCapsPtr qemuCaps)
{
int ret = -1;
switch ((virDomainChrConsoleTargetType) chr->targetType) {
case VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SCLP:
case VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SCLPLM:
if (!(*deviceStr = qemuBuildSclpDevStr(chr)))
goto cleanup;
break;
case VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_VIRTIO:
if (!(*deviceStr = qemuBuildVirtioSerialPortDevStr(def, chr, qemuCaps)))
goto cleanup;
break;
case VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SERIAL:
break;
case VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_NONE:
case VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_XEN:
case VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_UML:
case VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_LXC:
case VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_OPENVZ:
case VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_LAST:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("unsupported console target type %s"),
NULLSTR(virDomainChrConsoleTargetTypeToString(chr->targetType)));
goto cleanup;
}
ret = 0;
cleanup:
return ret;
}
int
qemuBuildChrDeviceStr(char **deviceStr,
virDomainDefPtr vmdef,
virDomainChrDefPtr chr,
virQEMUCapsPtr qemuCaps)
{
int ret = -1;
switch ((virDomainChrDeviceType) chr->deviceType) {
case VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL:
ret = qemuBuildSerialChrDeviceStr(deviceStr, vmdef, chr, qemuCaps,
vmdef->os.arch,
vmdef->os.machine);
break;
case VIR_DOMAIN_CHR_DEVICE_TYPE_PARALLEL:
ret = qemuBuildParallelChrDeviceStr(deviceStr, chr);
break;
case VIR_DOMAIN_CHR_DEVICE_TYPE_CHANNEL:
ret = qemuBuildChannelChrDeviceStr(deviceStr, vmdef, chr, qemuCaps);
break;
case VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE:
ret = qemuBuildConsoleChrDeviceStr(deviceStr, vmdef, chr, qemuCaps);
break;
case VIR_DOMAIN_CHR_DEVICE_TYPE_LAST:
return ret;
}
return ret;
}
/*
* This method takes a string representing a QEMU command line ARGV set
* optionally prefixed by a list of environment variables. It then tries
* to split it up into a NULL terminated list of env & argv, splitting
* on space
*/
static int qemuStringToArgvEnv(const char *args,
char ***retenv,
char ***retargv)
{
char **arglist = NULL;
size_t argcount = 0;
size_t argalloc = 0;
size_t envend;
size_t i;
const char *curr = args;
const char *start;
char **progenv = NULL;
char **progargv = NULL;
/* Iterate over string, splitting on sequences of ' ' */
while (curr && *curr != '\0') {
char *arg;
const char *next;
start = curr;
/* accept a space in CEPH_ARGS */
if (STRPREFIX(curr, "CEPH_ARGS=-m "))
start += strlen("CEPH_ARGS=-m ");
if (*start == '\'') {
if (start == curr)
curr++;
next = strchr(start + 1, '\'');
} else if (*start == '"') {
if (start == curr)
curr++;
next = strchr(start + 1, '"');
} else {
next = strchr(start, ' ');
}
if (!next)
next = strchr(curr, '\n');
if (VIR_STRNDUP(arg, curr, next ? next - curr : -1) < 0)
goto error;
if (next && (*next == '\'' || *next == '"'))
next++;
if (VIR_RESIZE_N(arglist, argalloc, argcount, 2) < 0) {
VIR_FREE(arg);
goto error;
}
arglist[argcount++] = arg;
arglist[argcount] = NULL;
while (next && c_isspace(*next))
next++;
curr = next;
}
/* Iterate over list of args, finding first arg not containing
* the '=' character (eg, skip over env vars FOO=bar) */
for (envend = 0; ((envend < argcount) &&
(strchr(arglist[envend], '=') != NULL));
envend++)
; /* nada */
/* Copy the list of env vars */
if (envend > 0) {
if (VIR_REALLOC_N(progenv, envend+1) < 0)
goto error;
for (i = 0; i < envend; i++)
progenv[i] = arglist[i];
progenv[i] = NULL;
}
/* Copy the list of argv */
if (VIR_REALLOC_N(progargv, argcount-envend + 1) < 0)
goto error;
for (i = envend; i < argcount; i++)
progargv[i-envend] = arglist[i];
progargv[i-envend] = NULL;
VIR_FREE(arglist);
*retenv = progenv;
*retargv = progargv;
return 0;
error:
VIR_FREE(progenv);
VIR_FREE(progargv);
virStringFreeList(arglist);
return -1;
}
/*
* Search for a named env variable, and return the value part
*/
static const char *qemuFindEnv(char **progenv,
const char *name)
{
size_t i;
int len = strlen(name);
for (i = 0; progenv && progenv[i]; i++) {
if (STREQLEN(progenv[i], name, len) &&
progenv[i][len] == '=')
return progenv[i] + len + 1;
}
return NULL;
}
/*
* Takes a string containing a set of key=value,key=value,key...
* parameters and splits them up, returning two arrays with
* the individual keys and values. If allowEmptyValue is nonzero,
* the "=value" part is optional and if a key with no value is found,
* NULL is be placed into corresponding place in retvalues.
*/
int
qemuParseKeywords(const char *str,
char ***retkeywords,
char ***retvalues,
int *retnkeywords,
int allowEmptyValue)
{
int keywordCount = 0;
int keywordAlloc = 0;
char **keywords = NULL;
char **values = NULL;
const char *start = str;
const char *end;
size_t i;
*retkeywords = NULL;
*retvalues = NULL;
*retnkeywords = 0;
end = start + strlen(str);
while (start) {
const char *separator;
const char *endmark;
char *keyword;
char *value = NULL;
endmark = start;
do {
/* Qemu accepts ',,' as an escape for a literal comma;
* skip past those here while searching for the end of the
* value, then strip them down below */
endmark = strchr(endmark, ',');
} while (endmark && endmark[1] == ',' && (endmark += 2));
if (!endmark)
endmark = end;
if (!(separator = strchr(start, '=')))
separator = end;
if (separator >= endmark) {
if (!allowEmptyValue) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("malformed keyword arguments in '%s'"), str);
goto error;
}
separator = endmark;
}
if (VIR_STRNDUP(keyword, start, separator - start) < 0)
goto error;
if (separator < endmark) {
separator++;
if (VIR_STRNDUP(value, separator, endmark - separator) < 0) {
VIR_FREE(keyword);
goto error;
}
if (strchr(value, ',')) {
char *p = strchr(value, ',') + 1;
char *q = p + 1;
while (*q) {
if (*q == ',')
q++;
*p++ = *q++;
}
*p = '\0';
}
}
if (keywordAlloc == keywordCount) {
if (VIR_REALLOC_N(keywords, keywordAlloc + 10) < 0 ||
VIR_REALLOC_N(values, keywordAlloc + 10) < 0) {
VIR_FREE(keyword);
VIR_FREE(value);
goto error;
}
keywordAlloc += 10;
}
keywords[keywordCount] = keyword;
values[keywordCount] = value;
keywordCount++;
start = endmark < end ? endmark + 1 : NULL;
}
*retkeywords = keywords;
*retvalues = values;
*retnkeywords = keywordCount;
return 0;
error:
for (i = 0; i < keywordCount; i++) {
VIR_FREE(keywords[i]);
VIR_FREE(values[i]);
}
VIR_FREE(keywords);
VIR_FREE(values);
return -1;
}
/*
* Tries to parse new style QEMU -drive args.
*
* eg -drive file=/dev/HostVG/VirtData1,if=ide,index=1
*
* Will fail if not using the 'index' keyword
*/
static virDomainDiskDefPtr
qemuParseCommandLineDisk(virDomainXMLOptionPtr xmlopt,
const char *val,
virDomainDefPtr dom,
int nvirtiodisk,
bool old_style_ceph_args)
{
virDomainDiskDefPtr def = NULL;
char **keywords;
char **values;
int nkeywords;
size_t i;
int idx = -1;
int busid = -1;
int unitid = -1;
if (qemuParseKeywords(val,
&keywords,
&values,
&nkeywords,
0) < 0)
return NULL;
if (VIR_ALLOC(def) < 0)
goto cleanup;
if (VIR_ALLOC(def->src) < 0)
goto error;
if ((ARCH_IS_PPC64(dom->os.arch) &&
dom->os.machine && STRPREFIX(dom->os.machine, "pseries")))
def->bus = VIR_DOMAIN_DISK_BUS_SCSI;
else
def->bus = VIR_DOMAIN_DISK_BUS_IDE;
def->device = VIR_DOMAIN_DISK_DEVICE_DISK;
def->src->type = VIR_STORAGE_TYPE_FILE;
for (i = 0; i < nkeywords; i++) {
if (STREQ(keywords[i], "file")) {
if (values[i] && STRNEQ(values[i], "")) {
def->src->path = values[i];
values[i] = NULL;
if (STRPREFIX(def->src->path, "/dev/"))
def->src->type = VIR_STORAGE_TYPE_BLOCK;
else if (STRPREFIX(def->src->path, "nbd:") ||
STRPREFIX(def->src->path, "nbd+")) {
def->src->type = VIR_STORAGE_TYPE_NETWORK;
def->src->protocol = VIR_STORAGE_NET_PROTOCOL_NBD;
if (qemuParseNBDString(def) < 0)
goto error;
} else if (STRPREFIX(def->src->path, "rbd:")) {
char *p = def->src->path;
def->src->type = VIR_STORAGE_TYPE_NETWORK;
def->src->protocol = VIR_STORAGE_NET_PROTOCOL_RBD;
if (VIR_STRDUP(def->src->path, p + strlen("rbd:")) < 0)
goto error;
/* old-style CEPH_ARGS env variable is parsed later */
if (!old_style_ceph_args && qemuParseRBDString(def) < 0) {
VIR_FREE(p);
goto error;
}
VIR_FREE(p);
} else if (STRPREFIX(def->src->path, "gluster:") ||
STRPREFIX(def->src->path, "gluster+")) {
def->src->type = VIR_STORAGE_TYPE_NETWORK;
def->src->protocol = VIR_STORAGE_NET_PROTOCOL_GLUSTER;
if (qemuParseGlusterString(def) < 0)
goto error;
} else if (STRPREFIX(def->src->path, "iscsi:")) {
def->src->type = VIR_STORAGE_TYPE_NETWORK;
def->src->protocol = VIR_STORAGE_NET_PROTOCOL_ISCSI;
if (qemuParseISCSIString(def) < 0)
goto error;
} else if (STRPREFIX(def->src->path, "sheepdog:")) {
char *p = def->src->path;
char *port, *vdi;
def->src->type = VIR_STORAGE_TYPE_NETWORK;
def->src->protocol = VIR_STORAGE_NET_PROTOCOL_SHEEPDOG;
if (VIR_STRDUP(def->src->path, p + strlen("sheepdog:")) < 0)
goto error;
VIR_FREE(p);
/* def->src->path must be [vdiname] or [host]:[port]:[vdiname] */
port = strchr(def->src->path, ':');
if (port) {
*port = '\0';
vdi = strchr(port + 1, ':');
if (!vdi) {
*port = ':';
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot parse sheepdog filename '%s'"),
def->src->path);
goto error;
}
port++;
*vdi++ = '\0';
if (VIR_ALLOC(def->src->hosts) < 0)
goto error;
def->src->nhosts = 1;
def->src->hosts->name = def->src->path;
if (VIR_STRDUP(def->src->hosts->port, port) < 0)
goto error;
def->src->hosts->transport = VIR_STORAGE_NET_HOST_TRANS_TCP;
def->src->hosts->socket = NULL;
if (VIR_STRDUP(def->src->path, vdi) < 0)
goto error;
}
} else {
def->src->type = VIR_STORAGE_TYPE_FILE;
}
} else {
def->src->type = VIR_STORAGE_TYPE_FILE;
}
} else if (STREQ(keywords[i], "if")) {
if (STREQ(values[i], "ide")) {
def->bus = VIR_DOMAIN_DISK_BUS_IDE;
if ((ARCH_IS_PPC64(dom->os.arch) &&
dom->os.machine && STRPREFIX(dom->os.machine, "pseries"))) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("pseries systems do not support ide devices '%s'"), val);
goto error;
}
} else if (STREQ(values[i], "scsi")) {
def->bus = VIR_DOMAIN_DISK_BUS_SCSI;
} else if (STREQ(values[i], "virtio")) {
def->bus = VIR_DOMAIN_DISK_BUS_VIRTIO;
} else if (STREQ(values[i], "xen")) {
def->bus = VIR_DOMAIN_DISK_BUS_XEN;
} else if (STREQ(values[i], "sd")) {
def->bus = VIR_DOMAIN_DISK_BUS_SD;
}
} else if (STREQ(keywords[i], "media")) {
if (STREQ(values[i], "cdrom")) {
def->device = VIR_DOMAIN_DISK_DEVICE_CDROM;
def->src->readonly = true;
} else if (STREQ(values[i], "floppy")) {
def->device = VIR_DOMAIN_DISK_DEVICE_FLOPPY;
}
} else if (STREQ(keywords[i], "format")) {
if (VIR_STRDUP(def->src->driverName, "qemu") < 0)
goto error;
def->src->format = virStorageFileFormatTypeFromString(values[i]);
} else if (STREQ(keywords[i], "cache")) {
if (STREQ(values[i], "off") ||
STREQ(values[i], "none"))
def->cachemode = VIR_DOMAIN_DISK_CACHE_DISABLE;
else if (STREQ(values[i], "writeback") ||
STREQ(values[i], "on"))
def->cachemode = VIR_DOMAIN_DISK_CACHE_WRITEBACK;
else if (STREQ(values[i], "writethrough"))
def->cachemode = VIR_DOMAIN_DISK_CACHE_WRITETHRU;
else if (STREQ(values[i], "directsync"))
def->cachemode = VIR_DOMAIN_DISK_CACHE_DIRECTSYNC;
else if (STREQ(values[i], "unsafe"))
def->cachemode = VIR_DOMAIN_DISK_CACHE_UNSAFE;
} else if (STREQ(keywords[i], "werror")) {
if (STREQ(values[i], "stop"))
def->error_policy = VIR_DOMAIN_DISK_ERROR_POLICY_STOP;
else if (STREQ(values[i], "report"))
def->error_policy = VIR_DOMAIN_DISK_ERROR_POLICY_REPORT;
else if (STREQ(values[i], "ignore"))
def->error_policy = VIR_DOMAIN_DISK_ERROR_POLICY_IGNORE;
else if (STREQ(values[i], "enospc"))
def->error_policy = VIR_DOMAIN_DISK_ERROR_POLICY_ENOSPACE;
} else if (STREQ(keywords[i], "rerror")) {
if (STREQ(values[i], "stop"))
def->rerror_policy = VIR_DOMAIN_DISK_ERROR_POLICY_STOP;
else if (STREQ(values[i], "report"))
def->rerror_policy = VIR_DOMAIN_DISK_ERROR_POLICY_REPORT;
else if (STREQ(values[i], "ignore"))
def->rerror_policy = VIR_DOMAIN_DISK_ERROR_POLICY_IGNORE;
} else if (STREQ(keywords[i], "index")) {
if (virStrToLong_i(values[i], NULL, 10, &idx) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot parse drive index '%s'"), val);
goto error;
}
} else if (STREQ(keywords[i], "bus")) {
if (virStrToLong_i(values[i], NULL, 10, &busid) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot parse drive bus '%s'"), val);
goto error;
}
} else if (STREQ(keywords[i], "unit")) {
if (virStrToLong_i(values[i], NULL, 10, &unitid) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot parse drive unit '%s'"), val);
goto error;
}
} else if (STREQ(keywords[i], "readonly")) {
if ((values[i] == NULL) || STREQ(values[i], "on"))
def->src->readonly = true;
} else if (STREQ(keywords[i], "aio")) {
if ((def->iomode = virDomainDiskIoTypeFromString(values[i])) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot parse io mode '%s'"), values[i]);
goto error;
}
} else if (STREQ(keywords[i], "cyls")) {
if (virStrToLong_ui(values[i], NULL, 10,
&(def->geometry.cylinders)) < 0) {
virDomainDiskDefFree(def);
def = NULL;
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot parse cylinders value'%s'"),
values[i]);
goto error;
}
} else if (STREQ(keywords[i], "heads")) {
if (virStrToLong_ui(values[i], NULL, 10,
&(def->geometry.heads)) < 0) {
virDomainDiskDefFree(def);
def = NULL;
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot parse heads value'%s'"),
values[i]);
goto error;
}
} else if (STREQ(keywords[i], "secs")) {
if (virStrToLong_ui(values[i], NULL, 10,
&(def->geometry.sectors)) < 0) {
virDomainDiskDefFree(def);
def = NULL;
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot parse sectors value'%s'"),
values[i]);
goto error;
}
} else if (STREQ(keywords[i], "trans")) {
def->geometry.trans =
virDomainDiskGeometryTransTypeFromString(values[i]);
if ((def->geometry.trans < VIR_DOMAIN_DISK_TRANS_DEFAULT) ||
(def->geometry.trans >= VIR_DOMAIN_DISK_TRANS_LAST)) {
virDomainDiskDefFree(def);
def = NULL;
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot parse translation value '%s'"),
values[i]);
goto error;
}
}
}
if (def->rerror_policy == def->error_policy)
def->rerror_policy = 0;
if (!def->src->path &&
def->device == VIR_DOMAIN_DISK_DEVICE_DISK &&
def->src->type != VIR_STORAGE_TYPE_NETWORK) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("missing file parameter in drive '%s'"), val);
goto error;
}
if (idx == -1 &&
def->bus == VIR_DOMAIN_DISK_BUS_VIRTIO)
idx = nvirtiodisk;
if (idx == -1 &&
unitid == -1 &&
busid == -1) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("missing index/unit/bus parameter in drive '%s'"),
val);
goto error;
}
if (idx == -1) {
if (unitid == -1)
unitid = 0;
if (busid == -1)
busid = 0;
switch (def->bus) {
case VIR_DOMAIN_DISK_BUS_IDE:
idx = (busid * 2) + unitid;
break;
case VIR_DOMAIN_DISK_BUS_SCSI:
idx = (busid * 7) + unitid;
break;
default:
idx = unitid;
break;
}
}
if (def->bus == VIR_DOMAIN_DISK_BUS_IDE) {
ignore_value(VIR_STRDUP(def->dst, "hda"));
} else if (def->bus == VIR_DOMAIN_DISK_BUS_SCSI ||
def->bus == VIR_DOMAIN_DISK_BUS_SD) {
ignore_value(VIR_STRDUP(def->dst, "sda"));
} else if (def->bus == VIR_DOMAIN_DISK_BUS_VIRTIO) {
ignore_value(VIR_STRDUP(def->dst, "vda"));
} else if (def->bus == VIR_DOMAIN_DISK_BUS_XEN) {
ignore_value(VIR_STRDUP(def->dst, "xvda"));
} else {
ignore_value(VIR_STRDUP(def->dst, "hda"));
}
if (!def->dst)
goto error;
if (STREQ(def->dst, "xvda"))
def->dst[3] = 'a' + idx;
else
def->dst[2] = 'a' + idx;
if (virDomainDiskDefAssignAddress(xmlopt, def, dom) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("invalid device name '%s'"), def->dst);
virDomainDiskDefFree(def);
def = NULL;
goto cleanup;
}
cleanup:
for (i = 0; i < nkeywords; i++) {
VIR_FREE(keywords[i]);
VIR_FREE(values[i]);
}
VIR_FREE(keywords);
VIR_FREE(values);
return def;
error:
virDomainDiskDefFree(def);
def = NULL;
goto cleanup;
}
/*
* Tries to find a NIC definition matching a vlan we want
*/
static const char *
qemuFindNICForVLAN(int nnics,
const char **nics,
int wantvlan)
{
size_t i;
for (i = 0; i < nnics; i++) {
int gotvlan;
const char *tmp = strstr(nics[i], "vlan=");
char *end;
if (!tmp)
continue;
tmp += strlen("vlan=");
if (virStrToLong_i(tmp, &end, 10, &gotvlan) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot parse NIC vlan in '%s'"), nics[i]);
return NULL;
}
if (gotvlan == wantvlan)
return nics[i];
}
if (wantvlan == 0 && nnics > 0)
return nics[0];
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot find NIC definition for vlan %d"), wantvlan);
return NULL;
}
/*
* Tries to parse a QEMU -net backend argument. Gets given
* a list of all known -net frontend arguments to try and
* match up against. Horribly complicated stuff
*/
static virDomainNetDefPtr
qemuParseCommandLineNet(virDomainXMLOptionPtr xmlopt,
const char *val,
int nnics,
const char **nics)
{
virDomainNetDefPtr def = NULL;
char **keywords = NULL;
char **values = NULL;
int nkeywords;
const char *nic;
int wantvlan = 0;
const char *tmp;
bool genmac = true;
size_t i;
tmp = strchr(val, ',');
if (tmp) {
if (qemuParseKeywords(tmp+1,
&keywords,
&values,
&nkeywords,
0) < 0)
return NULL;
} else {
nkeywords = 0;
}
if (VIR_ALLOC(def) < 0)
goto cleanup;
/* 'tap' could turn into libvirt type=ethernet, type=bridge or
* type=network, but we can't tell, so use the generic config */
if (STRPREFIX(val, "tap,"))
def->type = VIR_DOMAIN_NET_TYPE_ETHERNET;
else if (STRPREFIX(val, "socket"))
def->type = VIR_DOMAIN_NET_TYPE_CLIENT;
else if (STRPREFIX(val, "user"))
def->type = VIR_DOMAIN_NET_TYPE_USER;
else
def->type = VIR_DOMAIN_NET_TYPE_ETHERNET;
for (i = 0; i < nkeywords; i++) {
if (STREQ(keywords[i], "vlan")) {
if (virStrToLong_i(values[i], NULL, 10, &wantvlan) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot parse vlan in '%s'"), val);
virDomainNetDefFree(def);
def = NULL;
goto cleanup;
}
} else if (def->type == VIR_DOMAIN_NET_TYPE_ETHERNET &&
STREQ(keywords[i], "script") && STRNEQ(values[i], "")) {
def->script = values[i];
values[i] = NULL;
} else if (def->type == VIR_DOMAIN_NET_TYPE_ETHERNET &&
STREQ(keywords[i], "ifname")) {
def->ifname = values[i];
values[i] = NULL;
}
}
/* Done parsing the nic backend. Now to try and find corresponding
* frontend, based off vlan number. NB this assumes a 1-1 mapping
*/
nic = qemuFindNICForVLAN(nnics, nics, wantvlan);
if (!nic) {
virDomainNetDefFree(def);
def = NULL;
goto cleanup;
}
if (!STRPREFIX(nic, "nic")) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot parse NIC definition '%s'"), nic);
virDomainNetDefFree(def);
def = NULL;
goto cleanup;
}
for (i = 0; i < nkeywords; i++) {
VIR_FREE(keywords[i]);
VIR_FREE(values[i]);
}
VIR_FREE(keywords);
VIR_FREE(values);
if (STRPREFIX(nic, "nic,")) {
if (qemuParseKeywords(nic + strlen("nic,"),
&keywords,
&values,
&nkeywords,
0) < 0) {
virDomainNetDefFree(def);
def = NULL;
goto cleanup;
}
} else {
nkeywords = 0;
}
for (i = 0; i < nkeywords; i++) {
if (STREQ(keywords[i], "macaddr")) {
genmac = false;
if (virMacAddrParse(values[i], &def->mac) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unable to parse mac address '%s'"),
values[i]);
virDomainNetDefFree(def);
def = NULL;
goto cleanup;
}
} else if (STREQ(keywords[i], "model")) {
def->model = values[i];
values[i] = NULL;
} else if (STREQ(keywords[i], "vhost")) {
if ((values[i] == NULL) || STREQ(values[i], "on")) {
def->driver.virtio.name = VIR_DOMAIN_NET_BACKEND_TYPE_VHOST;
} else if (STREQ(keywords[i], "off")) {
def->driver.virtio.name = VIR_DOMAIN_NET_BACKEND_TYPE_QEMU;
}
} else if (STREQ(keywords[i], "sndbuf") && values[i]) {
if (virStrToLong_ul(values[i], NULL, 10, &def->tune.sndbuf) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot parse sndbuf size in '%s'"), val);
virDomainNetDefFree(def);
def = NULL;
goto cleanup;
}
def->tune.sndbuf_specified = true;
}
}
if (genmac)
virDomainNetGenerateMAC(xmlopt, &def->mac);
cleanup:
for (i = 0; i < nkeywords; i++) {
VIR_FREE(keywords[i]);
VIR_FREE(values[i]);
}
VIR_FREE(keywords);
VIR_FREE(values);
return def;
}
/*
* Tries to parse a QEMU PCI device
*/
static virDomainHostdevDefPtr
qemuParseCommandLinePCI(const char *val)
{
int bus = 0, slot = 0, func = 0;
const char *start;
char *end;
virDomainHostdevDefPtr def = virDomainHostdevDefAlloc();
if (!def)
goto error;
if (!STRPREFIX(val, "host=")) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unknown PCI device syntax '%s'"), val);
goto error;
}
start = val + strlen("host=");
if (virStrToLong_i(start, &end, 16, &bus) < 0 || *end != ':') {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot extract PCI device bus '%s'"), val);
goto error;
}
start = end + 1;
if (virStrToLong_i(start, &end, 16, &slot) < 0 || *end != '.') {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot extract PCI device slot '%s'"), val);
goto error;
}
start = end + 1;
if (virStrToLong_i(start, NULL, 16, &func) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot extract PCI device function '%s'"), val);
goto error;
}
def->mode = VIR_DOMAIN_HOSTDEV_MODE_SUBSYS;
def->managed = true;
def->source.subsys.type = VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI;
def->source.subsys.u.pci.addr.bus = bus;
def->source.subsys.u.pci.addr.slot = slot;
def->source.subsys.u.pci.addr.function = func;
return def;
error:
virDomainHostdevDefFree(def);
return NULL;
}
/*
* Tries to parse a QEMU USB device
*/
static virDomainHostdevDefPtr
qemuParseCommandLineUSB(const char *val)
{
virDomainHostdevDefPtr def = virDomainHostdevDefAlloc();
virDomainHostdevSubsysUSBPtr usbsrc;
int first = 0, second = 0;
const char *start;
char *end;
if (!def)
goto error;
usbsrc = &def->source.subsys.u.usb;
if (!STRPREFIX(val, "host:")) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unknown USB device syntax '%s'"), val);
goto error;
}
start = val + strlen("host:");
if (strchr(start, ':')) {
if (virStrToLong_i(start, &end, 16, &first) < 0 || *end != ':') {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot extract USB device vendor '%s'"), val);
goto error;
}
start = end + 1;
if (virStrToLong_i(start, NULL, 16, &second) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot extract USB device product '%s'"), val);
goto error;
}
} else {
if (virStrToLong_i(start, &end, 10, &first) < 0 || *end != '.') {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot extract USB device bus '%s'"), val);
goto error;
}
start = end + 1;
if (virStrToLong_i(start, NULL, 10, &second) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot extract USB device address '%s'"), val);
goto error;
}
}
def->mode = VIR_DOMAIN_HOSTDEV_MODE_SUBSYS;
def->managed = false;
def->source.subsys.type = VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB;
if (*end == '.') {
usbsrc->bus = first;
usbsrc->device = second;
} else {
usbsrc->vendor = first;
usbsrc->product = second;
}
return def;
error:
virDomainHostdevDefFree(def);
return NULL;
}
/*
* Tries to parse a QEMU serial/parallel device
*/
static int
qemuParseCommandLineChr(virDomainChrSourceDefPtr source,
const char *val)
{
if (STREQ(val, "null")) {
source->type = VIR_DOMAIN_CHR_TYPE_NULL;
} else if (STREQ(val, "vc")) {
source->type = VIR_DOMAIN_CHR_TYPE_VC;
} else if (STREQ(val, "pty")) {
source->type = VIR_DOMAIN_CHR_TYPE_PTY;
} else if (STRPREFIX(val, "file:")) {
source->type = VIR_DOMAIN_CHR_TYPE_FILE;
if (VIR_STRDUP(source->data.file.path, val + strlen("file:")) < 0)
goto error;
} else if (STRPREFIX(val, "pipe:")) {
source->type = VIR_DOMAIN_CHR_TYPE_PIPE;
if (VIR_STRDUP(source->data.file.path, val + strlen("pipe:")) < 0)
goto error;
} else if (STREQ(val, "stdio")) {
source->type = VIR_DOMAIN_CHR_TYPE_STDIO;
} else if (STRPREFIX(val, "udp:")) {
const char *svc1, *host2, *svc2;
source->type = VIR_DOMAIN_CHR_TYPE_UDP;
val += strlen("udp:");
svc1 = strchr(val, ':');
host2 = svc1 ? strchr(svc1, '@') : NULL;
svc2 = host2 ? strchr(host2, ':') : NULL;
if (svc1 && svc1 != val &&
VIR_STRNDUP(source->data.udp.connectHost, val, svc1 - val) < 0)
goto error;
if (svc1) {
svc1++;
if (VIR_STRNDUP(source->data.udp.connectService, svc1,
host2 ? host2 - svc1 : strlen(svc1)) < 0)
goto error;
}
if (host2) {
host2++;
if (svc2 && svc2 != host2 &&
VIR_STRNDUP(source->data.udp.bindHost, host2, svc2 - host2) < 0)
goto error;
}
if (svc2) {
svc2++;
if (STRNEQ(svc2, "0")) {
if (VIR_STRDUP(source->data.udp.bindService, svc2) < 0)
goto error;
}
}
} else if (STRPREFIX(val, "tcp:") ||
STRPREFIX(val, "telnet:")) {
const char *opt, *svc;
source->type = VIR_DOMAIN_CHR_TYPE_TCP;
if (STRPREFIX(val, "tcp:")) {
val += strlen("tcp:");
} else {
val += strlen("telnet:");
source->data.tcp.protocol = VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNET;
}
svc = strchr(val, ':');
if (!svc) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot find port number in character device %s"), val);
goto error;
}
opt = strchr(svc, ',');
if (opt && strstr(opt, "server"))
source->data.tcp.listen = true;
if (VIR_STRNDUP(source->data.tcp.host, val, svc - val) < 0)
goto error;
svc++;
if (VIR_STRNDUP(source->data.tcp.service, svc, opt ? opt - svc : -1) < 0)
goto error;
} else if (STRPREFIX(val, "unix:")) {
const char *opt;
val += strlen("unix:");
opt = strchr(val, ',');
source->type = VIR_DOMAIN_CHR_TYPE_UNIX;
if (VIR_STRNDUP(source->data.nix.path, val, opt ? opt - val : -1) < 0)
goto error;
} else if (STRPREFIX(val, "/dev")) {
source->type = VIR_DOMAIN_CHR_TYPE_DEV;
if (VIR_STRDUP(source->data.file.path, val) < 0)
goto error;
} else {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unknown character device syntax %s"), val);
goto error;
}
return 0;
error:
return -1;
}
static virCPUDefPtr
qemuInitGuestCPU(virDomainDefPtr dom)
{
if (!dom->cpu) {
virCPUDefPtr cpu;
if (VIR_ALLOC(cpu) < 0)
return NULL;
cpu->type = VIR_CPU_TYPE_GUEST;
cpu->match = VIR_CPU_MATCH_EXACT;
dom->cpu = cpu;
}
return dom->cpu;
}
static int
qemuParseCommandLineCPU(virDomainDefPtr dom,
const char *val)
{
virCPUDefPtr cpu = NULL;
char **tokens;
char **hv_tokens = NULL;
char *model = NULL;
int ret = -1;
size_t i;
if (!(tokens = virStringSplit(val, ",", 0)))
goto cleanup;
if (tokens[0] == NULL)
goto syntax;
for (i = 0; tokens[i] != NULL; i++) {
if (*tokens[i] == '\0')
goto syntax;
if (i == 0) {
if (VIR_STRDUP(model, tokens[i]) < 0)
goto cleanup;
if (!STREQ(model, "qemu32") && !STREQ(model, "qemu64")) {
if (!(cpu = qemuInitGuestCPU(dom)))
goto cleanup;
cpu->model = model;
model = NULL;
}
} else if (*tokens[i] == '+' || *tokens[i] == '-') {
const char *feature = tokens[i] + 1; /* '+' or '-' */
int policy;
if (*tokens[i] == '+')
policy = VIR_CPU_FEATURE_REQUIRE;
else
policy = VIR_CPU_FEATURE_DISABLE;
if (*feature == '\0')
goto syntax;
if (dom->os.arch != VIR_ARCH_X86_64 &&
dom->os.arch != VIR_ARCH_I686) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("%s platform doesn't support CPU features'"),
virArchToString(dom->os.arch));
goto cleanup;
}
if (STREQ(feature, "kvmclock")) {
bool present = (policy == VIR_CPU_FEATURE_REQUIRE);
size_t j;
for (j = 0; j < dom->clock.ntimers; j++) {
if (dom->clock.timers[j]->name == VIR_DOMAIN_TIMER_NAME_KVMCLOCK)
break;
}
if (j == dom->clock.ntimers) {
virDomainTimerDefPtr timer;
if (VIR_ALLOC(timer) < 0 ||
VIR_APPEND_ELEMENT_COPY(dom->clock.timers,
dom->clock.ntimers, timer) < 0) {
VIR_FREE(timer);
goto cleanup;
}
timer->name = VIR_DOMAIN_TIMER_NAME_KVMCLOCK;
timer->present = present;
timer->tickpolicy = -1;
timer->track = -1;
timer->mode = -1;
} else if (dom->clock.timers[j]->present != -1 &&
dom->clock.timers[j]->present != present) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("conflicting occurrences of kvmclock feature"));
goto cleanup;
}
} else if (STREQ(feature, "kvm_pv_eoi")) {
if (policy == VIR_CPU_FEATURE_REQUIRE)
dom->apic_eoi = VIR_TRISTATE_SWITCH_ON;
else
dom->apic_eoi = VIR_TRISTATE_SWITCH_OFF;
} else {
if (!cpu) {
if (!(cpu = qemuInitGuestCPU(dom)))
goto cleanup;
cpu->model = model;
model = NULL;
}
if (virCPUDefAddFeature(cpu, feature, policy) < 0)
goto cleanup;
}
} else if (STRPREFIX(tokens[i], "hv_")) {
const char *token = tokens[i] + 3; /* "hv_" */
const char *feature, *value;
int f;
if (*token == '\0')
goto syntax;
if (!(hv_tokens = virStringSplit(token, "=", 2)))
goto cleanup;
feature = hv_tokens[0];
value = hv_tokens[1];
if (*feature == '\0')
goto syntax;
dom->features[VIR_DOMAIN_FEATURE_HYPERV] = VIR_TRISTATE_SWITCH_ON;
if ((f = virDomainHypervTypeFromString(feature)) < 0) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("unsupported HyperV Enlightenment feature "
"'%s'"), feature);
goto cleanup;
}
switch ((virDomainHyperv) f) {
case VIR_DOMAIN_HYPERV_RELAXED:
case VIR_DOMAIN_HYPERV_VAPIC:
if (value) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("HyperV feature '%s' should not "
"have a value"), feature);
goto cleanup;
}
dom->hyperv_features[f] = VIR_TRISTATE_SWITCH_ON;
break;
case VIR_DOMAIN_HYPERV_SPINLOCKS:
dom->hyperv_features[f] = VIR_TRISTATE_SWITCH_ON;
if (!value) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("missing HyperV spinlock retry count"));
goto cleanup;
}
if (virStrToLong_ui(value, NULL, 0, &dom->hyperv_spinlocks) < 0) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("cannot parse HyperV spinlock retry count"));
goto cleanup;
}
if (dom->hyperv_spinlocks < 0xFFF)
dom->hyperv_spinlocks = 0xFFF;
break;
case VIR_DOMAIN_HYPERV_LAST:
break;
}
virStringFreeList(hv_tokens);
hv_tokens = NULL;
} else if (STREQ(tokens[i], "kvm=off")) {
dom->features[VIR_DOMAIN_FEATURE_KVM] = VIR_TRISTATE_SWITCH_ON;
dom->kvm_features[VIR_DOMAIN_KVM_HIDDEN] = VIR_TRISTATE_SWITCH_ON;
}
}
if (dom->os.arch == VIR_ARCH_X86_64) {
bool is_32bit = false;
if (cpu) {
virCPUDataPtr cpuData = NULL;
if (cpuEncode(VIR_ARCH_X86_64, cpu, NULL, &cpuData,
NULL, NULL, NULL, NULL) < 0)
goto cleanup;
is_32bit = (cpuHasFeature(cpuData, "lm") != 1);
cpuDataFree(cpuData);
} else if (model) {
is_32bit = STREQ(model, "qemu32");
}
if (is_32bit)
dom->os.arch = VIR_ARCH_I686;
}
ret = 0;
cleanup:
VIR_FREE(model);
virStringFreeList(tokens);
virStringFreeList(hv_tokens);
return ret;
syntax:
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unknown CPU syntax '%s'"), val);
goto cleanup;
}
static int
qemuParseCommandLineSmp(virDomainDefPtr dom,
const char *val)
{
unsigned int sockets = 0;
unsigned int cores = 0;
unsigned int threads = 0;
unsigned int maxcpus = 0;
size_t i;
int nkws;
char **kws;
char **vals;
int n;
char *end;
int ret;
if (qemuParseKeywords(val, &kws, &vals, &nkws, 1) < 0)
return -1;
for (i = 0; i < nkws; i++) {
if (vals[i] == NULL) {
if (i > 0 ||
virStrToLong_i(kws[i], &end, 10, &n) < 0 || *end != '\0')
goto syntax;
dom->vcpus = n;
} else {
if (virStrToLong_i(vals[i], &end, 10, &n) < 0 || *end != '\0')
goto syntax;
if (STREQ(kws[i], "sockets"))
sockets = n;
else if (STREQ(kws[i], "cores"))
cores = n;
else if (STREQ(kws[i], "threads"))
threads = n;
else if (STREQ(kws[i], "maxcpus"))
maxcpus = n;
else
goto syntax;
}
}
dom->maxvcpus = maxcpus ? maxcpus : dom->vcpus;
if (sockets && cores && threads) {
virCPUDefPtr cpu;
if (!(cpu = qemuInitGuestCPU(dom)))
goto error;
cpu->sockets = sockets;
cpu->cores = cores;
cpu->threads = threads;
} else if (sockets || cores || threads) {
goto syntax;
}
ret = 0;
cleanup:
for (i = 0; i < nkws; i++) {
VIR_FREE(kws[i]);
VIR_FREE(vals[i]);
}
VIR_FREE(kws);
VIR_FREE(vals);
return ret;
syntax:
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot parse CPU topology '%s'"), val);
error:
ret = -1;
goto cleanup;
}
static void
qemuParseCommandLineBootDevs(virDomainDefPtr def, const char *str)
{
int n, b = 0;
for (n = 0; str[n] && b < VIR_DOMAIN_BOOT_LAST; n++) {
if (str[n] == 'a')
def->os.bootDevs[b++] = VIR_DOMAIN_BOOT_FLOPPY;
else if (str[n] == 'c')
def->os.bootDevs[b++] = VIR_DOMAIN_BOOT_DISK;
else if (str[n] == 'd')
def->os.bootDevs[b++] = VIR_DOMAIN_BOOT_CDROM;
else if (str[n] == 'n')
def->os.bootDevs[b++] = VIR_DOMAIN_BOOT_NET;
else if (str[n] == ',')
break;
}
def->os.nBootDevs = b;
}
/*
* Analyse the env and argv settings and reconstruct a
* virDomainDefPtr representing these settings as closely
* as is practical. This is not an exact science....
*/
static virDomainDefPtr
qemuParseCommandLine(virCapsPtr qemuCaps,
virDomainXMLOptionPtr xmlopt,
char **progenv,
char **progargv,
char **pidfile,
virDomainChrSourceDefPtr *monConfig,
bool *monJSON)
{
virDomainDefPtr def;
size_t i;
bool nographics = false;
bool fullscreen = false;
char **list = NULL;
char *path;
size_t nnics = 0;
const char **nics = NULL;
int video = VIR_DOMAIN_VIDEO_TYPE_CIRRUS;
int nvirtiodisk = 0;
qemuDomainCmdlineDefPtr cmd = NULL;
virDomainDiskDefPtr disk = NULL;
const char *ceph_args = qemuFindEnv(progenv, "CEPH_ARGS");
if (pidfile)
*pidfile = NULL;
if (monConfig)
*monConfig = NULL;
if (monJSON)
*monJSON = false;
if (!progargv[0]) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("no emulator path found"));
return NULL;
}
if (!(def = virDomainDefNew()))
goto error;
/* allocate the cmdlinedef up-front; if it's unused, we'll free it later */
if (VIR_ALLOC(cmd) < 0)
goto error;
if (virUUIDGenerate(def->uuid) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("failed to generate uuid"));
goto error;
}
def->id = -1;
def->mem.cur_balloon = 64 * 1024;
virDomainDefSetMemoryInitial(def, def->mem.cur_balloon);
def->maxvcpus = 1;
def->vcpus = 1;
def->clock.offset = VIR_DOMAIN_CLOCK_OFFSET_UTC;
def->onReboot = VIR_DOMAIN_LIFECYCLE_RESTART;
def->onCrash = VIR_DOMAIN_LIFECYCLE_CRASH_DESTROY;
def->onPoweroff = VIR_DOMAIN_LIFECYCLE_DESTROY;
def->virtType = VIR_DOMAIN_VIRT_QEMU;
if (VIR_STRDUP(def->emulator, progargv[0]) < 0)
goto error;
if (!(path = last_component(def->emulator)))
goto error;
if (strstr(path, "xenner")) {
def->virtType = VIR_DOMAIN_VIRT_KVM;
def->os.type = VIR_DOMAIN_OSTYPE_XEN;
} else {
def->os.type = VIR_DOMAIN_OSTYPE_HVM;
if (strstr(path, "kvm")) {
def->virtType = VIR_DOMAIN_VIRT_KVM;
def->features[VIR_DOMAIN_FEATURE_PAE] = VIR_TRISTATE_SWITCH_ON;
}
}
if (def->virtType == VIR_DOMAIN_VIRT_KVM)
def->os.arch = qemuCaps->host.arch;
else if (STRPREFIX(path, "qemu-system-"))
def->os.arch = virArchFromString(path + strlen("qemu-system-"));
else
def->os.arch = VIR_ARCH_I686;
if ((def->os.arch == VIR_ARCH_I686) ||
(def->os.arch == VIR_ARCH_X86_64))
def->features[VIR_DOMAIN_FEATURE_ACPI] = VIR_TRISTATE_SWITCH_ON;
#define WANT_VALUE() \
const char *val = progargv[++i]; \
if (!val) { \
virReportError(VIR_ERR_INTERNAL_ERROR, \
_("missing value for %s argument"), arg); \
goto error; \
}
/* One initial loop to get list of NICs, so we
* can correlate them later */
for (i = 1; progargv[i]; i++) {
const char *arg = progargv[i];
/* Make sure we have a single - for all options to
simplify next logic */
if (STRPREFIX(arg, "--"))
arg++;
if (STREQ(arg, "-net")) {
WANT_VALUE();
if (STRPREFIX(val, "nic") &&
VIR_APPEND_ELEMENT(nics, nnics, val) < 0)
goto error;
}
}
/* Now the real processing loop */
for (i = 1; progargv[i]; i++) {
const char *arg = progargv[i];
bool argRecognized = true;
/* Make sure we have a single - for all options to
simplify next logic */
if (STRPREFIX(arg, "--"))
arg++;
if (STREQ(arg, "-vnc")) {
virDomainGraphicsDefPtr vnc;
char *tmp;
WANT_VALUE();
if (VIR_ALLOC(vnc) < 0)
goto error;
vnc->type = VIR_DOMAIN_GRAPHICS_TYPE_VNC;
if (STRPREFIX(val, "unix:")) {
/* -vnc unix:/some/big/path */
if (VIR_STRDUP(vnc->data.vnc.socket, val + 5) < 0) {
virDomainGraphicsDefFree(vnc);
goto error;
}
} else {
/*
* -vnc 127.0.0.1:4
* -vnc [2001:1:2:3:4:5:1234:1234]:4
* -vnc some.host.name:4
*/
char *opts;
char *port;
const char *sep = ":";
if (val[0] == '[')
sep = "]:";
tmp = strstr(val, sep);
if (!tmp) {
virDomainGraphicsDefFree(vnc);
virReportError(VIR_ERR_INTERNAL_ERROR,
_("missing VNC port number in '%s'"), val);
goto error;
}
port = tmp + strlen(sep);
if (virStrToLong_i(port, &opts, 10,
&vnc->data.vnc.port) < 0) {
virDomainGraphicsDefFree(vnc);
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot parse VNC port '%s'"), port);
goto error;
}
if (val[0] == '[')
virDomainGraphicsListenSetAddress(vnc, 0,
val+1, tmp-(val+1), true);
else
virDomainGraphicsListenSetAddress(vnc, 0,
val, tmp-val, true);
if (!virDomainGraphicsListenGetAddress(vnc, 0)) {
virDomainGraphicsDefFree(vnc);
goto error;
}
if (*opts == ',') {
char *orig_opts;
if (VIR_STRDUP(orig_opts, opts + 1) < 0) {
virDomainGraphicsDefFree(vnc);
goto error;
}
opts = orig_opts;
while (opts && *opts) {
char *nextopt = strchr(opts, ',');
if (nextopt)
*(nextopt++) = '\0';
if (STRPREFIX(opts, "websocket")) {
char *websocket = opts + strlen("websocket");
if (*(websocket++) == '=' &&
*websocket) {
/* If the websocket continues with
* '=', we'll parse it */
if (virStrToLong_i(websocket,
NULL, 0,
&vnc->data.vnc.websocket) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot parse VNC "
"WebSocket port '%s'"),
websocket);
virDomainGraphicsDefFree(vnc);
VIR_FREE(orig_opts);
goto error;
}
} else {
/* Otherwise, we'll compute the port the same
* way QEMU does, by adding a 5700 to the
* display value. */
vnc->data.vnc.websocket =
vnc->data.vnc.port + 5700;
}
} else if (STRPREFIX(opts, "share=")) {
char *sharePolicy = opts + strlen("share=");
if (sharePolicy && *sharePolicy) {
int policy =
virDomainGraphicsVNCSharePolicyTypeFromString(sharePolicy);
if (policy < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unknown vnc display sharing policy '%s'"),
sharePolicy);
virDomainGraphicsDefFree(vnc);
VIR_FREE(orig_opts);
goto error;
} else {
vnc->data.vnc.sharePolicy = policy;
}
} else {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("missing vnc sharing policy"));
virDomainGraphicsDefFree(vnc);
VIR_FREE(orig_opts);
goto error;
}
}
opts = nextopt;
}
VIR_FREE(orig_opts);
}
vnc->data.vnc.port += 5900;
vnc->data.vnc.autoport = false;
}
if (VIR_APPEND_ELEMENT(def->graphics, def->ngraphics, vnc) < 0) {
virDomainGraphicsDefFree(vnc);
goto error;
}
} else if (STREQ(arg, "-m")) {
int mem;
WANT_VALUE();
if (virStrToLong_i(val, NULL, 10, &mem) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, \
_("cannot parse memory level '%s'"), val);
goto error;
}
virDomainDefSetMemoryInitial(def, mem * 1024);
def->mem.cur_balloon = mem * 1024;
} else if (STREQ(arg, "-smp")) {
WANT_VALUE();
if (qemuParseCommandLineSmp(def, val) < 0)
goto error;
} else if (STREQ(arg, "-uuid")) {
WANT_VALUE();
if (virUUIDParse(val, def->uuid) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, \
_("cannot parse UUID '%s'"), val);
goto error;
}
} else if (STRPREFIX(arg, "-hd") ||
STRPREFIX(arg, "-sd") ||
STRPREFIX(arg, "-fd") ||
STREQ(arg, "-cdrom")) {
WANT_VALUE();
if (!(disk = virDomainDiskDefNew(xmlopt)))
goto error;
if (STRPREFIX(val, "/dev/")) {
disk->src->type = VIR_STORAGE_TYPE_BLOCK;
} else if (STRPREFIX(val, "nbd:")) {
disk->src->type = VIR_STORAGE_TYPE_NETWORK;
disk->src->protocol = VIR_STORAGE_NET_PROTOCOL_NBD;
} else if (STRPREFIX(val, "rbd:")) {
disk->src->type = VIR_STORAGE_TYPE_NETWORK;
disk->src->protocol = VIR_STORAGE_NET_PROTOCOL_RBD;
val += strlen("rbd:");
} else if (STRPREFIX(val, "gluster")) {
disk->src->type = VIR_STORAGE_TYPE_NETWORK;
disk->src->protocol = VIR_STORAGE_NET_PROTOCOL_GLUSTER;
} else if (STRPREFIX(val, "sheepdog:")) {
disk->src->type = VIR_STORAGE_TYPE_NETWORK;
disk->src->protocol = VIR_STORAGE_NET_PROTOCOL_SHEEPDOG;
val += strlen("sheepdog:");
} else {
disk->src->type = VIR_STORAGE_TYPE_FILE;
}
if (STREQ(arg, "-cdrom")) {
disk->device = VIR_DOMAIN_DISK_DEVICE_CDROM;
if ((ARCH_IS_PPC64(def->os.arch) &&
def->os.machine && STRPREFIX(def->os.machine, "pseries")))
disk->bus = VIR_DOMAIN_DISK_BUS_SCSI;
if (VIR_STRDUP(disk->dst, "hdc") < 0)
goto error;
disk->src->readonly = true;
} else {
if (STRPREFIX(arg, "-fd")) {
disk->device = VIR_DOMAIN_DISK_DEVICE_FLOPPY;
disk->bus = VIR_DOMAIN_DISK_BUS_FDC;
} else {
disk->device = VIR_DOMAIN_DISK_DEVICE_DISK;
if (STRPREFIX(arg, "-hd"))
disk->bus = VIR_DOMAIN_DISK_BUS_IDE;
else
disk->bus = VIR_DOMAIN_DISK_BUS_SCSI;
if ((ARCH_IS_PPC64(def->os.arch) &&
def->os.machine && STRPREFIX(def->os.machine, "pseries")))
disk->bus = VIR_DOMAIN_DISK_BUS_SCSI;
}
if (VIR_STRDUP(disk->dst, arg + 1) < 0)
goto error;
}
if (VIR_STRDUP(disk->src->path, val) < 0)
goto error;
if (disk->src->type == VIR_STORAGE_TYPE_NETWORK) {
char *port;
switch ((virStorageNetProtocol) disk->src->protocol) {
case VIR_STORAGE_NET_PROTOCOL_NBD:
if (qemuParseNBDString(disk) < 0)
goto error;
break;
case VIR_STORAGE_NET_PROTOCOL_RBD:
/* old-style CEPH_ARGS env variable is parsed later */
if (!ceph_args && qemuParseRBDString(disk) < 0)
goto error;
break;
case VIR_STORAGE_NET_PROTOCOL_SHEEPDOG:
/* disk->src must be [vdiname] or [host]:[port]:[vdiname] */
port = strchr(disk->src->path, ':');
if (port) {
char *vdi;
*port++ = '\0';
vdi = strchr(port, ':');
if (!vdi) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot parse sheepdog filename '%s'"), val);
goto error;
}
*vdi++ = '\0';
if (VIR_ALLOC(disk->src->hosts) < 0)
goto error;
disk->src->nhosts = 1;
disk->src->hosts->name = disk->src->path;
if (VIR_STRDUP(disk->src->hosts->port, port) < 0)
goto error;
if (VIR_STRDUP(disk->src->path, vdi) < 0)
goto error;
}
break;
case VIR_STORAGE_NET_PROTOCOL_GLUSTER:
if (qemuParseGlusterString(disk) < 0)
goto error;
break;
case VIR_STORAGE_NET_PROTOCOL_ISCSI:
if (qemuParseISCSIString(disk) < 0)
goto error;
break;
case VIR_STORAGE_NET_PROTOCOL_HTTP:
case VIR_STORAGE_NET_PROTOCOL_HTTPS:
case VIR_STORAGE_NET_PROTOCOL_FTP:
case VIR_STORAGE_NET_PROTOCOL_FTPS:
case VIR_STORAGE_NET_PROTOCOL_TFTP:
case VIR_STORAGE_NET_PROTOCOL_LAST:
case VIR_STORAGE_NET_PROTOCOL_NONE:
/* ignored for now */
break;
}
}
if (virDomainDiskDefAssignAddress(xmlopt, disk, def) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Cannot assign address for device name '%s'"),
disk->dst);
goto error;
}
if (VIR_APPEND_ELEMENT(def->disks, def->ndisks, disk) < 0)
goto error;
} else if (STREQ(arg, "-no-acpi")) {
def->features[VIR_DOMAIN_FEATURE_ACPI] = VIR_TRISTATE_SWITCH_ABSENT;
} else if (STREQ(arg, "-no-reboot")) {
def->onReboot = VIR_DOMAIN_LIFECYCLE_DESTROY;
} else if (STREQ(arg, "-no-kvm")) {
def->virtType = VIR_DOMAIN_VIRT_QEMU;
} else if (STREQ(arg, "-enable-kvm")) {
def->virtType = VIR_DOMAIN_VIRT_KVM;
} else if (STREQ(arg, "-nographic")) {
nographics = true;
} else if (STREQ(arg, "-full-screen")) {
fullscreen = true;
} else if (STREQ(arg, "-localtime")) {
def->clock.offset = VIR_DOMAIN_CLOCK_OFFSET_LOCALTIME;
} else if (STREQ(arg, "-kernel")) {
WANT_VALUE();
if (VIR_STRDUP(def->os.kernel, val) < 0)
goto error;
} else if (STREQ(arg, "-bios")) {
WANT_VALUE();
if (VIR_ALLOC(def->os.loader) < 0 ||
VIR_STRDUP(def->os.loader->path, val) < 0)
goto error;
} else if (STREQ(arg, "-initrd")) {
WANT_VALUE();
if (VIR_STRDUP(def->os.initrd, val) < 0)
goto error;
} else if (STREQ(arg, "-append")) {
WANT_VALUE();
if (VIR_STRDUP(def->os.cmdline, val) < 0)
goto error;
} else if (STREQ(arg, "-dtb")) {
WANT_VALUE();
if (VIR_STRDUP(def->os.dtb, val) < 0)
goto error;
} else if (STREQ(arg, "-boot")) {
const char *token = NULL;
WANT_VALUE();
if (!strchr(val, ',')) {
qemuParseCommandLineBootDevs(def, val);
} else {
token = val;
while (token && *token) {
if (STRPREFIX(token, "order=")) {
token += strlen("order=");
qemuParseCommandLineBootDevs(def, token);
} else if (STRPREFIX(token, "menu=on")) {
def->os.bootmenu = 1;
} else if (STRPREFIX(token, "reboot-timeout=")) {
int num;
char *endptr;
if (virStrToLong_i(token + strlen("reboot-timeout="),
&endptr, 10, &num) < 0 ||
(*endptr != '\0' && endptr != strchr(token, ','))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("cannot parse reboot-timeout value"));
goto error;
}
if (num > 65535)
num = 65535;
else if (num < -1)
num = -1;
def->os.bios.rt_delay = num;
def->os.bios.rt_set = true;
}
token = strchr(token, ',');
/* This incrementation has to be done here in order to make it
* possible to pass the token pointer properly into the loop */
if (token)
token++;
}
}
} else if (STREQ(arg, "-name")) {
char *process;
WANT_VALUE();
process = strstr(val, ",process=");
if (process == NULL) {
if (VIR_STRDUP(def->name, val) < 0)
goto error;
} else {
if (VIR_STRNDUP(def->name, val, process - val) < 0)
goto error;
}
if (STREQ(def->name, ""))
VIR_FREE(def->name);
} else if (STREQ(arg, "-M") ||
STREQ(arg, "-machine")) {
char *param;
size_t j = 0;
/* -machine [type=]name[,prop[=value][,...]]
* Set os.machine only if first parameter lacks '=' or
* contains explicit type='...' */
WANT_VALUE();
if (!(list = virStringSplit(val, ",", 0)))
goto error;
param = list[0];
if (STRPREFIX(param, "type="))
param += strlen("type=");
if (!strchr(param, '=')) {
if (VIR_STRDUP(def->os.machine, param) < 0)
goto error;
j++;
}
/* handle all remaining "-machine" parameters */
while ((param = list[j++])) {
if (STRPREFIX(param, "dump-guest-core=")) {
param += strlen("dump-guest-core=");
def->mem.dump_core = virTristateSwitchTypeFromString(param);
if (def->mem.dump_core <= 0)
def->mem.dump_core = VIR_TRISTATE_SWITCH_ABSENT;
} else if (STRPREFIX(param, "mem-merge=off")) {
def->mem.nosharepages = true;
} else if (STRPREFIX(param, "accel=kvm")) {
def->virtType = VIR_DOMAIN_VIRT_KVM;
def->features[VIR_DOMAIN_FEATURE_PAE] = VIR_TRISTATE_SWITCH_ON;
} else if (STRPREFIX(param, "aes-key-wrap=")) {
if (STREQ(arg, "-M")) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("aes-key-wrap is not supported with "
"this QEMU binary"));
goto error;
}
param += strlen("aes-key-wrap=");
if (!def->keywrap && VIR_ALLOC(def->keywrap) < 0)
goto error;
def->keywrap->aes = virTristateSwitchTypeFromString(param);
if (def->keywrap->aes < 0)
def->keywrap->aes = VIR_TRISTATE_SWITCH_ABSENT;
} else if (STRPREFIX(param, "dea-key-wrap=")) {
if (STREQ(arg, "-M")) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("dea-key-wrap is not supported with "
"this QEMU binary"));
goto error;
}
param += strlen("dea-key-wrap=");
if (!def->keywrap && VIR_ALLOC(def->keywrap) < 0)
goto error;
def->keywrap->dea = virTristateSwitchTypeFromString(param);
if (def->keywrap->dea < 0)
def->keywrap->dea = VIR_TRISTATE_SWITCH_ABSENT;
}
}
virStringFreeList(list);
list = NULL;
} else if (STREQ(arg, "-serial")) {
WANT_VALUE();
if (STRNEQ(val, "none")) {
virDomainChrDefPtr chr;
if (!(chr = virDomainChrDefNew()))
goto error;
if (qemuParseCommandLineChr(&chr->source, val) < 0) {
virDomainChrDefFree(chr);
goto error;
}
chr->deviceType = VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL;
chr->target.port = def->nserials;
if (VIR_APPEND_ELEMENT(def->serials, def->nserials, chr) < 0) {
virDomainChrDefFree(chr);
goto error;
}
}
} else if (STREQ(arg, "-parallel")) {
WANT_VALUE();
if (STRNEQ(val, "none")) {
virDomainChrDefPtr chr;
if (!(chr = virDomainChrDefNew()))
goto error;
if (qemuParseCommandLineChr(&chr->source, val) < 0) {
virDomainChrDefFree(chr);
goto error;
}
chr->deviceType = VIR_DOMAIN_CHR_DEVICE_TYPE_PARALLEL;
chr->target.port = def->nparallels;
if (VIR_APPEND_ELEMENT(def->parallels, def->nparallels, chr) < 0) {
virDomainChrDefFree(chr);
goto error;
}
}
} else if (STREQ(arg, "-usbdevice")) {
WANT_VALUE();
if (STREQ(val, "tablet") ||
STREQ(val, "mouse") ||
STREQ(val, "keyboard")) {
virDomainInputDefPtr input;
if (VIR_ALLOC(input) < 0)
goto error;
input->bus = VIR_DOMAIN_INPUT_BUS_USB;
if (STREQ(val, "tablet"))
input->type = VIR_DOMAIN_INPUT_TYPE_TABLET;
else if (STREQ(val, "mouse"))
input->type = VIR_DOMAIN_INPUT_TYPE_MOUSE;
else
input->type = VIR_DOMAIN_INPUT_TYPE_KBD;
if (VIR_APPEND_ELEMENT(def->inputs, def->ninputs, input) < 0) {
virDomainInputDefFree(input);
goto error;
}
} else if (STRPREFIX(val, "disk:")) {
if (!(disk = virDomainDiskDefNew(xmlopt)))
goto error;
if (VIR_STRDUP(disk->src->path, val + strlen("disk:")) < 0)
goto error;
if (STRPREFIX(disk->src->path, "/dev/"))
disk->src->type = VIR_STORAGE_TYPE_BLOCK;
else
disk->src->type = VIR_STORAGE_TYPE_FILE;
disk->device = VIR_DOMAIN_DISK_DEVICE_DISK;
disk->bus = VIR_DOMAIN_DISK_BUS_USB;
disk->removable = VIR_TRISTATE_SWITCH_ABSENT;
if (VIR_STRDUP(disk->dst, "sda") < 0)
goto error;
if (VIR_APPEND_ELEMENT(def->disks, def->ndisks, disk) < 0)
goto error;
} else {
virDomainHostdevDefPtr hostdev;
if (!(hostdev = qemuParseCommandLineUSB(val)))
goto error;
if (VIR_APPEND_ELEMENT(def->hostdevs, def->nhostdevs, hostdev) < 0) {
virDomainHostdevDefFree(hostdev);
goto error;
}
}
} else if (STREQ(arg, "-net")) {
WANT_VALUE();
if (!STRPREFIX(val, "nic") && STRNEQ(val, "none")) {
virDomainNetDefPtr net;
if (!(net = qemuParseCommandLineNet(xmlopt, val, nnics, nics)))
goto error;
if (VIR_APPEND_ELEMENT(def->nets, def->nnets, net) < 0) {
virDomainNetDefFree(net);
goto error;
}
}
} else if (STREQ(arg, "-drive")) {
WANT_VALUE();
if (!(disk = qemuParseCommandLineDisk(xmlopt, val, def,
nvirtiodisk,
ceph_args != NULL)))
goto error;
if (disk->bus == VIR_DOMAIN_DISK_BUS_VIRTIO)
nvirtiodisk++;
if (VIR_APPEND_ELEMENT(def->disks, def->ndisks, disk) < 0)
goto error;
} else if (STREQ(arg, "-pcidevice")) {
virDomainHostdevDefPtr hostdev;
WANT_VALUE();
if (!(hostdev = qemuParseCommandLinePCI(val)))
goto error;
if (VIR_APPEND_ELEMENT(def->hostdevs, def->nhostdevs, hostdev) < 0) {
virDomainHostdevDefFree(hostdev);
goto error;
}
} else if (STREQ(arg, "-soundhw")) {
const char *start;
WANT_VALUE();
start = val;
while (start) {
const char *tmp = strchr(start, ',');
int type = -1;
if (STRPREFIX(start, "pcspk")) {
type = VIR_DOMAIN_SOUND_MODEL_PCSPK;
} else if (STRPREFIX(start, "sb16")) {
type = VIR_DOMAIN_SOUND_MODEL_SB16;
} else if (STRPREFIX(start, "es1370")) {
type = VIR_DOMAIN_SOUND_MODEL_ES1370;
} else if (STRPREFIX(start, "ac97")) {
type = VIR_DOMAIN_SOUND_MODEL_AC97;
} else if (STRPREFIX(start, "hda")) {
type = VIR_DOMAIN_SOUND_MODEL_ICH6;
}
if (type != -1) {
virDomainSoundDefPtr snd;
if (VIR_ALLOC(snd) < 0)
goto error;
snd->model = type;
if (VIR_APPEND_ELEMENT(def->sounds, def->nsounds, snd) < 0) {
VIR_FREE(snd);
goto error;
}
}
start = tmp ? tmp + 1 : NULL;
}
} else if (STREQ(arg, "-watchdog")) {
WANT_VALUE();
int model = virDomainWatchdogModelTypeFromString(val);
if (model != -1) {
virDomainWatchdogDefPtr wd;
if (VIR_ALLOC(wd) < 0)
goto error;
wd->model = model;
wd->action = VIR_DOMAIN_WATCHDOG_ACTION_RESET;
def->watchdog = wd;
}
} else if (STREQ(arg, "-watchdog-action") && def->watchdog) {
WANT_VALUE();
int action = virDomainWatchdogActionTypeFromString(val);
if (action != -1)
def->watchdog->action = action;
} else if (STREQ(arg, "-bootloader")) {
WANT_VALUE();
if (VIR_STRDUP(def->os.bootloader, val) < 0)
goto error;
} else if (STREQ(arg, "-vmwarevga")) {
video = VIR_DOMAIN_VIDEO_TYPE_VMVGA;
} else if (STREQ(arg, "-std-vga")) {
video = VIR_DOMAIN_VIDEO_TYPE_VGA;
} else if (STREQ(arg, "-vga")) {
WANT_VALUE();
if (STRNEQ(val, "none")) {
video = qemuVideoTypeFromString(val);
if (video < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unknown video adapter type '%s'"), val);
goto error;
}
}
} else if (STREQ(arg, "-cpu")) {
WANT_VALUE();
if (qemuParseCommandLineCPU(def, val) < 0)
goto error;
} else if (STREQ(arg, "-domid")) {
WANT_VALUE();
/* ignore, generted on the fly */
} else if (STREQ(arg, "-usb")) {
virDomainControllerDefPtr ctldef;
if (VIR_ALLOC(ctldef) < 0)
goto error;
ctldef->type = VIR_DOMAIN_CONTROLLER_TYPE_USB;
ctldef->idx = 0;
ctldef->model = -1;
if (virDomainControllerInsert(def, ctldef) < 0) {
VIR_FREE(ctldef);
goto error;
}
} else if (STREQ(arg, "-pidfile")) {
WANT_VALUE();
if (pidfile)
if (VIR_STRDUP(*pidfile, val) < 0)
goto error;
} else if (STREQ(arg, "-incoming")) {
WANT_VALUE();
/* ignore, used via restore/migrate APIs */
} else if (STREQ(arg, "-monitor")) {
WANT_VALUE();
if (monConfig) {
virDomainChrSourceDefPtr chr;
if (VIR_ALLOC(chr) < 0)
goto error;
if (qemuParseCommandLineChr(chr, val) < 0) {
virDomainChrSourceDefFree(chr);
goto error;
}
*monConfig = chr;
}
} else if (STREQ(arg, "-global") &&
STRPREFIX(progargv[i + 1], "PIIX4_PM.disable_s3=")) {
/* We want to parse only the known "-global" parameters,
* so the ones that we don't know are still added to the
* namespace */
WANT_VALUE();
val += strlen("PIIX4_PM.disable_s3=");
if (STREQ(val, "0")) {
def->pm.s3 = VIR_TRISTATE_BOOL_YES;
} else if (STREQ(val, "1")) {
def->pm.s3 = VIR_TRISTATE_BOOL_NO;
} else {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("invalid value for disable_s3 parameter: "
"'%s'"), val);
goto error;
}
} else if (STREQ(arg, "-global") &&
STRPREFIX(progargv[i + 1], "PIIX4_PM.disable_s4=")) {
WANT_VALUE();
val += strlen("PIIX4_PM.disable_s4=");
if (STREQ(val, "0")) {
def->pm.s4 = VIR_TRISTATE_BOOL_YES;
} else if (STREQ(val, "1")) {
def->pm.s4 = VIR_TRISTATE_BOOL_NO;
} else {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("invalid value for disable_s4 parameter: "
"'%s'"), val);
goto error;
}
} else if (STREQ(arg, "-global") &&
STRPREFIX(progargv[i + 1], "spapr-nvram.reg=")) {
WANT_VALUE();
if (VIR_ALLOC(def->nvram) < 0)
goto error;
def->nvram->info.type = VIR_DOMAIN_DEVICE_ADDRESS_TYPE_SPAPRVIO;
def->nvram->info.addr.spaprvio.has_reg = true;
val += strlen("spapr-nvram.reg=");
if (virStrToLong_ull(val, NULL, 16,
&def->nvram->info.addr.spaprvio.reg) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot parse nvram's address '%s'"), val);
goto error;
}
} else if (STREQ(arg, "-S") ||
STREQ(arg, "-nodefaults") ||
STREQ(arg, "-nodefconfig")) {
/* ignore, always added by libvirt */
} else if (STREQ(arg, "-device") && progargv[1 + 1]) {
const char *opts = progargv[i + 1];
/* NB: we can't do WANT_VALUE until we're sure that we
* recognize the device, otherwise the !argRecognized
* logic below will be messed up
*/
if (STRPREFIX(opts, "virtio-balloon")) {
WANT_VALUE();
if (VIR_ALLOC(def->memballoon) < 0)
goto error;
def->memballoon->model = VIR_DOMAIN_MEMBALLOON_MODEL_VIRTIO;
} else {
/* add in new -device's here */
argRecognized = false;
}
} else {
argRecognized = false;
}
if (!argRecognized) {
char *tmp = NULL;
/* something we can't yet parse. Add it to the qemu namespace
* cmdline/environment advanced options and hope for the best
*/
VIR_WARN("unknown QEMU argument '%s', adding to the qemu namespace",
arg);
if (VIR_STRDUP(tmp, arg) < 0 ||
VIR_APPEND_ELEMENT(cmd->args, cmd->num_args, tmp) < 0) {
VIR_FREE(tmp);
goto error;
}
}
}
#undef WANT_VALUE
if (def->ndisks > 0 && ceph_args) {
char *hosts, *port, *saveptr = NULL, *token;
virDomainDiskDefPtr first_rbd_disk = NULL;
for (i = 0; i < def->ndisks; i++) {
if (def->disks[i]->src->type == VIR_STORAGE_TYPE_NETWORK &&
def->disks[i]->src->protocol == VIR_STORAGE_NET_PROTOCOL_RBD) {
first_rbd_disk = def->disks[i];
break;
}
}
if (!first_rbd_disk) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("CEPH_ARGS was set without an rbd disk"));
goto error;
}
/* CEPH_ARGS should be: -m host1[:port1][,host2[:port2]]... */
if (!STRPREFIX(ceph_args, "-m ")) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("could not parse CEPH_ARGS '%s'"), ceph_args);
goto error;
}
if (VIR_STRDUP(hosts, strchr(ceph_args, ' ') + 1) < 0)
goto error;
first_rbd_disk->src->nhosts = 0;
token = strtok_r(hosts, ",", &saveptr);
while (token != NULL) {
if (VIR_REALLOC_N(first_rbd_disk->src->hosts,
first_rbd_disk->src->nhosts + 1) < 0) {
VIR_FREE(hosts);
goto error;
}
port = strchr(token, ':');
if (port) {
*port++ = '\0';
if (VIR_STRDUP(port, port) < 0) {
VIR_FREE(hosts);
goto error;
}
}
first_rbd_disk->src->hosts[first_rbd_disk->src->nhosts].port = port;
if (VIR_STRDUP(first_rbd_disk->src->hosts[first_rbd_disk->src->nhosts].name,
token) < 0) {
VIR_FREE(hosts);
goto error;
}
first_rbd_disk->src->hosts[first_rbd_disk->src->nhosts].transport = VIR_STORAGE_NET_HOST_TRANS_TCP;
first_rbd_disk->src->hosts[first_rbd_disk->src->nhosts].socket = NULL;
first_rbd_disk->src->nhosts++;
token = strtok_r(NULL, ",", &saveptr);
}
VIR_FREE(hosts);
if (first_rbd_disk->src->nhosts == 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("found no rbd hosts in CEPH_ARGS '%s'"), ceph_args);
goto error;
}
}
if (!def->os.machine) {
virCapsDomainDataPtr capsdata;
if (!(capsdata = virCapabilitiesDomainDataLookup(qemuCaps, def->os.type,
def->os.arch, def->virtType, NULL, NULL)))
goto error;
if (VIR_STRDUP(def->os.machine, capsdata->machinetype) < 0) {
VIR_FREE(capsdata);
goto error;
}
VIR_FREE(capsdata);
}
if (!nographics && def->ngraphics == 0) {
virDomainGraphicsDefPtr sdl;
const char *display = qemuFindEnv(progenv, "DISPLAY");
const char *xauth = qemuFindEnv(progenv, "XAUTHORITY");
if (VIR_ALLOC(sdl) < 0)
goto error;
sdl->type = VIR_DOMAIN_GRAPHICS_TYPE_SDL;
sdl->data.sdl.fullscreen = fullscreen;
if (VIR_STRDUP(sdl->data.sdl.display, display) < 0) {
VIR_FREE(sdl);
goto error;
}
if (VIR_STRDUP(sdl->data.sdl.xauth, xauth) < 0) {
VIR_FREE(sdl);
goto error;
}
if (VIR_APPEND_ELEMENT(def->graphics, def->ngraphics, sdl) < 0) {
virDomainGraphicsDefFree(sdl);
goto error;
}
}
if (def->ngraphics) {
virDomainVideoDefPtr vid;
if (VIR_ALLOC(vid) < 0)
goto error;
if (def->virtType == VIR_DOMAIN_VIRT_XEN)
vid->type = VIR_DOMAIN_VIDEO_TYPE_XEN;
else
vid->type = video;
vid->vram = virDomainVideoDefaultRAM(def, vid->type);
if (vid->type == VIR_DOMAIN_VIDEO_TYPE_QXL) {
vid->ram = virDomainVideoDefaultRAM(def, vid->type);
vid->vgamem = QEMU_QXL_VGAMEM_DEFAULT;
} else {
vid->ram = 0;
vid->vgamem = 0;
}
vid->heads = 1;
if (VIR_APPEND_ELEMENT(def->videos, def->nvideos, vid) < 0) {
virDomainVideoDefFree(vid);
goto error;
}
}
/*
* having a balloon is the default, define one with type="none" to avoid it
*/
if (!def->memballoon) {
virDomainMemballoonDefPtr memballoon;
if (VIR_ALLOC(memballoon) < 0)
goto error;
memballoon->model = VIR_DOMAIN_MEMBALLOON_MODEL_NONE;
def->memballoon = memballoon;
}
VIR_FREE(nics);
if (virDomainDefAddImplicitControllers(def) < 0)
goto error;
if (virDomainDefPostParse(def, qemuCaps, xmlopt) < 0)
goto error;
if (cmd->num_args || cmd->num_env) {
def->ns = *virDomainXMLOptionGetNamespace(xmlopt);
def->namespaceData = cmd;
}
else
qemuDomainCmdlineDefFree(cmd);
return def;
error:
virDomainDiskDefFree(disk);
qemuDomainCmdlineDefFree(cmd);
virDomainDefFree(def);
virStringFreeList(list);
VIR_FREE(nics);
if (monConfig) {
virDomainChrSourceDefFree(*monConfig);
*monConfig = NULL;
}
if (pidfile)
VIR_FREE(*pidfile);
return NULL;
}
virDomainDefPtr qemuParseCommandLineString(virCapsPtr qemuCaps,
virDomainXMLOptionPtr xmlopt,
const char *args,
char **pidfile,
virDomainChrSourceDefPtr *monConfig,
bool *monJSON)
{
char **progenv = NULL;
char **progargv = NULL;
virDomainDefPtr def = NULL;
if (qemuStringToArgvEnv(args, &progenv, &progargv) < 0)
goto cleanup;
def = qemuParseCommandLine(qemuCaps, xmlopt, progenv, progargv,
pidfile, monConfig, monJSON);
cleanup:
virStringFreeList(progargv);
virStringFreeList(progenv);
return def;
}
static int qemuParseProcFileStrings(int pid_value,
const char *name,
char ***list)
{
char *path = NULL;
int ret = -1;
char *data = NULL;
ssize_t len;
char *tmp;
size_t nstr = 0;
char **str = NULL;
if (virAsprintf(&path, "/proc/%d/%s", pid_value, name) < 0)
goto cleanup;
if ((len = virFileReadAll(path, 1024*128, &data)) < 0)
goto cleanup;
tmp = data;
while (tmp < (data + len)) {
if (VIR_EXPAND_N(str, nstr, 1) < 0)
goto cleanup;
if (VIR_STRDUP(str[nstr-1], tmp) < 0)
goto cleanup;
/* Skip arg */
tmp += strlen(tmp);
/* Skip \0 separator */
tmp++;
}
if (VIR_EXPAND_N(str, nstr, 1) < 0)
goto cleanup;
str[nstr-1] = NULL;
ret = nstr-1;
*list = str;
cleanup:
if (ret < 0)
virStringFreeList(str);
VIR_FREE(data);
VIR_FREE(path);
return ret;
}
virDomainDefPtr qemuParseCommandLinePid(virCapsPtr qemuCaps,
virDomainXMLOptionPtr xmlopt,
pid_t pid,
char **pidfile,
virDomainChrSourceDefPtr *monConfig,
bool *monJSON)
{
virDomainDefPtr def = NULL;
char **progargv = NULL;
char **progenv = NULL;
char *exepath = NULL;
char *emulator;
/* The parser requires /proc/pid, which only exists on platforms
* like Linux where pid_t fits in int. */
if ((int) pid != pid ||
qemuParseProcFileStrings(pid, "cmdline", &progargv) < 0 ||
qemuParseProcFileStrings(pid, "environ", &progenv) < 0)
goto cleanup;
if (!(def = qemuParseCommandLine(qemuCaps, xmlopt, progenv, progargv,
pidfile, monConfig, monJSON)))
goto cleanup;
if (virAsprintf(&exepath, "/proc/%d/exe", (int) pid) < 0)
goto cleanup;
if (virFileResolveLink(exepath, &emulator) < 0) {
virReportSystemError(errno,
_("Unable to resolve %s for pid %u"),
exepath, (int) pid);
goto cleanup;
}
VIR_FREE(def->emulator);
def->emulator = emulator;
cleanup:
VIR_FREE(exepath);
virStringFreeList(progargv);
virStringFreeList(progenv);
return def;
}